/** * Given the data of the historic users and the current ones, creates a matrix of association coefficients, with the * current users as rows, and the historic user as columns. * * @see cosine_similarity($vector1, $vector2). * @param array $currentdata A 2D array. * @param array $historicdata A 2D array. * @param \text_progress_trace $trace Text output trace. * @return array The association matrix; empty if no association could be made. */ public function create_associations_matrix($currentdata, $historicdata, $trace) { $db = new database_helper(); $trace->output("[mycourse " . date('d/m/Y H:i:s') . "]: Starting creation of associations matrix."); $trace->output("[mycourse " . date('d/m/Y H:i:s') . "]: Transforming receiving data to get a matrix of users x resources. " . " with the views as values."); $currenttransformeddata = $this->matrix->transform_queried_data($currentdata); $historictransformeddata = $this->matrix->transform_queried_data($historicdata); $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Both historic and current data have been transformed.'); $currentusers = array_keys($currenttransformeddata); $historicusers = array_keys($historictransformeddata); $matrix = array(); foreach ($currentusers as $currentuser) { $trace->output('[mycourse ' . date('d/m/Y H:i:s') . "]: Starting calculating associations for current user " . "{$currentuser}."); $currentviewsvector = $currenttransformeddata[$currentuser]; $similarities = null; foreach ($historicusers as $historicuser) { $historicviewsvector = $historictransformeddata[$historicuser]; $trace->output('[mycourse ' . date('d/m/Y H:i:s') . "]: Starting calculating cosine similarity between a vector " . "of " . count($currentviewsvector) . " elements with a vector of " . count($historicviewsvector) . " elements."); $similarity = $this->cosine_similarity($currentviewsvector, $historicviewsvector); $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Cosine similarity between those vectors has been ' . 'calculated.'); $similarity = round($similarity, 4); $similarities[$historicuser] = $similarity; $db->insert_similarity($currentuser, $historicuser, $similarity, $this->currentweek); } $matrix[$currentuser] = $similarities; $trace->output('[mycourse ' . date('d/m/Y H:i:s') . "]: Similarities for current user {$currentuser} have been " . "calculated."); } return $matrix; }
/** * Creates the instances of the components to use to calculate the recommendations. * * @param \text_progress_trace $trace Text output trace. */ protected function initialize($trace) { $this->db = new database_helper(); $this->matrix = new decimal_matrix(); $this->associator = new cosine_similarity_associator($this->matrix); $this->recommendator = new simple_recommendator($this->associator); $trace->output('[mycourse]: Components initializated.'); }
public function test_text_progres_trace() { $this->resetAfterTest(false); $trace = new text_progress_trace(); $trace->output('do'); $trace->output('re', 1); $trace->output('mi', 2); $trace->finished(); $this->expectOutputString("do\n re\n mi\n"); }
/** * Creates the associations between the current users and historic users. In this simple_recommendator implementation, * each current user is associated with a UNIQUE previous user. * If $currentweek is the week of the year after the course start, we have to add 52 to $currentweek, otherwise, the * query won't return results, because $currentweek will be lower than the start week. * * @see get_selected_users($courseid) in database_helper.php. * @see get_course_start_week_and_year($courseid) in database_helper.php. * @see query_data($courseid, $year, $startweek, $currentweek) in database_helper.php. * @see find_course_previous_teachings_ids($courseid, $year) in database_helper.php. * @see create_associations_matrix in abstract_associator.php. * @see insert_associations in database_helper.php. * @param int $courseid The current course id. * @param int $currentweek The current week of the current course. * @param \text_progress_trace $trace Text output trace. * @return boolean False if no association could be done; true if yes. */ public function create_associations($courseid, $currentweek, $trace = null) { $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Creating associations.'); $selectedusers = $this->db->get_selected_users($courseid); $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Selected users to receive recommendations:'); foreach ($selectedusers as $selecteduser) { $trace->output("[mycourse]: \t- {$selecteduser}"); } $coursedates = $this->db->get_course_start_week_and_year($courseid, false); $currentstartweek = $coursedates['week']; $currentyear = $coursedates['year']; $trace->output("[mycourse " . date('d/m/Y H:i:s') . "]: Course start year: {$currentyear}; start week: {$currentstartweek}"); $yearchange = $currentstartweek > $currentweek; $endweek = $currentweek; if ($yearchange) { $endweek = $currentweek + 52; } $endweek += parent::TIME_WINDOW; $previouscourses = $this->db->get_associated_courses($courseid); $previouscourse = max($previouscourses); $trace->output("[mycourse " . date('d/m/Y H:i:s') . "]: Current course: '{$courseid}' will use the historic course " . "'{$previouscourse}' for the associations."); $coursedates = $this->db->get_course_start_week_and_year($previouscourse, true); $startweek = $coursedates['week']; $year = $coursedates['year']; $previousresources = $this->db->query_historic_resources_info($previouscourse); $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Starting the association of common resources in both ' . 'years, to determine which resources of previous year correspond to current\'s, to decide which can be candidate ' . 'to be recommended.'); $currentresources = $this->db->query_current_resources_info($courseid); $associatedresources = $this->associate_resources($previousresources, $currentresources); $previousresources = $associatedresources['previous']; $currentresources = $associatedresources['current']; $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Resource association ended.'); $onlyrecommendable = true; $previousdata = $this->db->query_historic_course_data($previouscourse, $year, $startweek, $endweek, null, true, $onlyrecommendable, $previousresources); $trace->output("[mycourse " . date('d/m/Y H:i:s') . "]: Log data for course '{$courseid}' will be queried with the " . "followingparameters: year: {$year}; from start week: {$startweek}; to end week: {$endweek}"); $currentdata = $this->db->query_data($courseid, $currentyear, $currentstartweek, $endweek, null, false, false, $onlyrecommendable, $currentresources); $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Previous query has finished.'); // We keep only the users that are selected to receive the recommendations. $currentselecteddata = array(); foreach ($currentdata as $currentuserrow) { $isselecteduser = in_array($currentuserrow->get_userid(), $selectedusers); if ($isselecteduser) { array_push($currentselecteddata, $currentuserrow); } } // We get the association matrix, where the rows will be the current users id; the columns, the previous users; // and the values, the simmilarity coefficient. $this->associator->set_currentweek($currentweek); $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Starting association matrix creation, where each current user ' . ' will be associated with the most similar previous user.'); $associationmatrix = $this->associator->create_associations_matrix($currentselecteddata, $previousdata, $trace); $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Association matrix creation ended.'); if (!empty($associationmatrix)) { $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Associations between current and historic users were made ' . 'successfully.'); $number = count($associationmatrix); $currentusersids = array(); $historicusersids = array(); // We have to find the highest coefficient of the relation of each current user with each previous student. foreach ($associationmatrix as $currentuser => $similarities) { $highestsimilarityindex = array_keys($similarities, max($similarities)); $associatedhistoric = $similarities[$highestsimilarityindex[0]]; array_push($currentusersids, $currentuser); // The key, user id, of the highest similarity coefficient, will be the most similar user. array_push($historicusersids, intval($highestsimilarityindex[0])); $trace->output("[mycourse]: Current user '{$currentuser}' has been associated with historic user" . " '{$highestsimilarityindex['0']}'."); } // Finally, we call the function that will insert the associations into the database. $this->db->insert_associations($number, $currentusersids, $courseid, $historicusersids, $previouscourse, $currentweek); $trace->output('[mycourse ' . date('d/m/Y H:i:s') . ']: Users associations have been inserted into database.'); return true; } else { $trace->output("[mycourse " . date('d/m/Y H:i:s') . "]: No associations could be done because the current course " . "'{$courseid}' and the historic course '{$previouscourse}' do not share any resources."); return false; } }