/**
  * 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;
     }
 }