示例#1
0
 /**
  * overridden constructor keeps a reference to the assignment class that is displaying this table
  *
  * @param assign $assignment The assignment class
  * @param int $perpage how many per page
  * @param string $filter The current filter
  * @param int $rowoffset For showing a subsequent page of results
  * @param bool $quickgrading Is this table wrapped in a quickgrading form?
  */
 public function __construct(assign $assignment, $perpage, $filter, $rowoffset, $quickgrading, $downloadfilename = null)
 {
     global $CFG, $PAGE, $DB;
     parent::__construct('mod_assign_grading');
     $this->assignment = $assignment;
     foreach ($assignment->get_feedback_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled()) {
             foreach ($plugin->get_grading_batch_operations() as $action => $description) {
                 if (empty($this->plugingradingbatchoperations)) {
                     $this->plugingradingbatchoperations[$plugin->get_type()] = array();
                 }
                 $this->plugingradingbatchoperations[$plugin->get_type()][$action] = $description;
             }
         }
     }
     $this->perpage = $perpage;
     $this->quickgrading = $quickgrading;
     $this->output = $PAGE->get_renderer('mod_assign');
     $this->define_baseurl(new moodle_url($CFG->wwwroot . '/mod/assign/view.php', array('action' => 'grading', 'id' => $assignment->get_course_module()->id)));
     // do some business - then set the sql
     $currentgroup = groups_get_activity_group($assignment->get_course_module(), true);
     if ($rowoffset) {
         $this->rownum = $rowoffset - 1;
     }
     $users = array_keys($assignment->list_participants($currentgroup, true));
     if (count($users) == 0) {
         // insert a record that will never match to the sql is still valid.
         $users[] = -1;
     }
     $params = array();
     $params['assignmentid1'] = (int) $this->assignment->get_instance()->id;
     $params['assignmentid2'] = (int) $this->assignment->get_instance()->id;
     $fields = user_picture::fields('u') . ', ';
     $fields .= 'u.id as userid, ';
     $fields .= 's.status as status, ';
     $fields .= 's.id as submissionid, ';
     $fields .= 's.timecreated as firstsubmission, ';
     $fields .= 's.timemodified as timesubmitted, ';
     $fields .= 'g.id as gradeid, ';
     $fields .= 'g.grade as grade, ';
     $fields .= 'g.timemodified as timemarked, ';
     $fields .= 'g.timecreated as firstmarked, ';
     $fields .= 'g.mailed as mailed, ';
     $fields .= 'g.locked as locked, ';
     $fields .= 'g.extensionduedate as extensionduedate';
     $from = '{user} u LEFT JOIN {assign_submission} s ON u.id = s.userid AND s.assignment = :assignmentid1' . ' LEFT JOIN {assign_grades} g ON u.id = g.userid AND g.assignment = :assignmentid2';
     $userparams = array();
     $userindex = 0;
     list($userwhere, $userparams) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED, 'user');
     $where = 'u.id ' . $userwhere;
     $params = array_merge($params, $userparams);
     if ($filter == ASSIGN_FILTER_SUBMITTED) {
         $where .= ' AND s.timecreated > 0 ';
     }
     if ($filter == ASSIGN_FILTER_REQUIRE_GRADING) {
         $where .= ' AND (s.timemodified > g.timemodified OR (s.timemodified IS NOT NULL AND g.timemodified IS NULL))';
     }
     if (strpos($filter, ASSIGN_FILTER_SINGLE_USER) === 0) {
         $userfilter = (int) array_pop(explode('=', $filter));
         $where .= ' AND (u.id = :userid)';
         $params['userid'] = $userfilter;
     }
     $this->set_sql($fields, $from, $where, $params);
     if ($downloadfilename) {
         $this->is_downloading('csv', $downloadfilename);
     }
     $columns = array();
     $headers = array();
     // Select.
     if (!$this->is_downloading()) {
         $columns[] = 'select';
         $headers[] = get_string('select') . '<div class="selectall"><label class="accesshide" for="selectall">' . get_string('selectall') . '</label>
                 <input type="checkbox" id="selectall" name="selectall" title="' . get_string('selectall') . '"/></div>';
     }
     // User picture.
     if (!$this->assignment->is_blind_marking()) {
         if (!$this->is_downloading()) {
             $columns[] = 'picture';
             $headers[] = get_string('pictureofuser');
         } else {
             $columns[] = 'recordid';
             $headers[] = get_string('recordid', 'assign');
         }
         // Fullname.
         $columns[] = 'fullname';
         $headers[] = get_string('fullname');
     } else {
         // Record ID.
         $columns[] = 'recordid';
         $headers[] = get_string('recordid', 'assign');
     }
     // Submission status
     if ($assignment->is_any_submission_plugin_enabled()) {
         $columns[] = 'status';
         $headers[] = get_string('status');
     }
     // Team submission columns
     if ($assignment->get_instance()->teamsubmission) {
         $columns[] = 'team';
         $headers[] = get_string('submissionteam', 'assign');
         $columns[] = 'teamstatus';
         $headers[] = get_string('teamsubmissionstatus', 'assign');
     }
     // Grade
     $columns[] = 'grade';
     $headers[] = get_string('grade');
     if ($this->is_downloading()) {
         if ($this->assignment->get_instance()->grade >= 0) {
             $columns[] = 'grademax';
             $headers[] = get_string('maxgrade', 'assign');
         } else {
             // This is a custom scale.
             $columns[] = 'scale';
             $headers[] = get_string('scale', 'assign');
         }
     }
     if (!$this->is_downloading()) {
         // We have to call this column userid so we can use userid as a default sortable column.
         $columns[] = 'userid';
         $headers[] = get_string('edit');
     }
     // Submission plugins
     if ($assignment->is_any_submission_plugin_enabled()) {
         $columns[] = 'timesubmitted';
         $headers[] = get_string('lastmodifiedsubmission', 'assign');
         foreach ($this->assignment->get_submission_plugins() as $plugin) {
             if ($this->is_downloading()) {
                 if ($plugin->is_visible() && $plugin->is_enabled()) {
                     foreach ($plugin->get_editor_fields() as $field => $description) {
                         $index = 'plugin' . count($this->plugincache);
                         $this->plugincache[$index] = array($plugin, $field);
                         $columns[] = $index;
                         $headers[] = $plugin->get_name();
                     }
                 }
             } else {
                 if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
                     $index = 'plugin' . count($this->plugincache);
                     $this->plugincache[$index] = array($plugin);
                     $columns[] = $index;
                     $headers[] = $plugin->get_name();
                 }
             }
         }
     }
     // time marked
     $columns[] = 'timemarked';
     $headers[] = get_string('lastmodifiedgrade', 'assign');
     // Feedback plugins
     foreach ($this->assignment->get_feedback_plugins() as $plugin) {
         if ($this->is_downloading()) {
             if ($plugin->is_visible() && $plugin->is_enabled()) {
                 foreach ($plugin->get_editor_fields() as $field => $description) {
                     $index = 'plugin' . count($this->plugincache);
                     $this->plugincache[$index] = array($plugin, $field);
                     $columns[] = $index;
                     $headers[] = $description;
                 }
             }
         } else {
             if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
                 $index = 'plugin' . count($this->plugincache);
                 $this->plugincache[$index] = array($plugin);
                 $columns[] = $index;
                 $headers[] = $plugin->get_name();
             }
         }
     }
     // Exclude 'Final grade' column in downloaded grading worksheets.
     if (!$this->is_downloading()) {
         // Final grade.
         $columns[] = 'finalgrade';
         $headers[] = get_string('finalgrade', 'grades');
     }
     // load the grading info for all users
     $this->gradinginfo = grade_get_grades($this->assignment->get_course()->id, 'mod', 'assign', $this->assignment->get_instance()->id, $users);
     $this->hasgrantextension = has_capability('mod/assign:grantextension', $this->assignment->get_context());
     if (!empty($CFG->enableoutcomes) && !empty($this->gradinginfo->outcomes)) {
         $columns[] = 'outcomes';
         $headers[] = get_string('outcomes', 'grades');
     }
     // set the columns
     $this->define_columns($columns);
     $this->define_headers($headers);
     // We require at least one unique column for the sort.
     $this->sortable(true, 'userid');
     $this->no_sorting('recordid');
     $this->no_sorting('finalgrade');
     $this->no_sorting('userid');
     $this->no_sorting('select');
     $this->no_sorting('outcomes');
     if ($assignment->get_instance()->teamsubmission) {
         $this->no_sorting('team');
         $this->no_sorting('teamstatus');
     }
     $plugincolumnindex = 0;
     foreach ($this->assignment->get_submission_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
             $submissionpluginindex = 'plugin' . $plugincolumnindex++;
             $this->no_sorting($submissionpluginindex);
         }
     }
     foreach ($this->assignment->get_feedback_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
             $feedbackpluginindex = 'plugin' . $plugincolumnindex++;
             $this->no_sorting($feedbackpluginindex);
         }
     }
     // When there is no data we still want the column headers printed in the csv file.
     if ($this->is_downloading()) {
         $this->start_output();
     }
 }
 /**
  * overridden constructor keeps a reference to the assignment class that is displaying this table
  *
  * @param assign $assignment The assignment class
  * @param int $perpage how many per page
  * @param string $filter The current filter
  * @param int $rowoffset For showing a subsequent page of results
  * @param bool $quickgrading Is this table wrapped in a quickgrading form?
  * @param string $downloadfilename
  */
 public function __construct(assign $assignment, $perpage, $filter, $rowoffset, $quickgrading, $downloadfilename = null)
 {
     global $CFG, $PAGE, $DB, $USER;
     parent::__construct('mod_assign_grading');
     $this->is_persistent(true);
     $this->assignment = $assignment;
     // Check permissions up front.
     $this->hasgrantextension = has_capability('mod/assign:grantextension', $this->assignment->get_context());
     $this->hasgrade = $this->assignment->can_grade();
     // Check if we have the elevated view capablities to see the blind details.
     $this->hasviewblind = has_capability('mod/assign:viewblinddetails', $this->assignment->get_context());
     foreach ($assignment->get_feedback_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled()) {
             foreach ($plugin->get_grading_batch_operations() as $action => $description) {
                 if (empty($this->plugingradingbatchoperations)) {
                     $this->plugingradingbatchoperations[$plugin->get_type()] = array();
                 }
                 $this->plugingradingbatchoperations[$plugin->get_type()][$action] = $description;
             }
         }
     }
     $this->perpage = $perpage;
     $this->quickgrading = $quickgrading && $this->hasgrade;
     $this->output = $PAGE->get_renderer('mod_assign');
     $urlparams = array('action' => 'grading', 'id' => $assignment->get_course_module()->id);
     $url = new moodle_url($CFG->wwwroot . '/mod/assign/view.php', $urlparams);
     $this->define_baseurl($url);
     // Do some business - then set the sql.
     $currentgroup = groups_get_activity_group($assignment->get_course_module(), true);
     if ($rowoffset) {
         $this->rownum = $rowoffset - 1;
     }
     $users = array_keys($assignment->list_participants($currentgroup, true));
     if (count($users) == 0) {
         // Insert a record that will never match to the sql is still valid.
         $users[] = -1;
     }
     $params = array();
     $params['assignmentid1'] = (int) $this->assignment->get_instance()->id;
     $params['assignmentid2'] = (int) $this->assignment->get_instance()->id;
     $params['assignmentid3'] = (int) $this->assignment->get_instance()->id;
     $extrauserfields = get_extra_user_fields($this->assignment->get_context());
     $fields = user_picture::fields('u', $extrauserfields) . ', ';
     $fields .= 'u.id as userid, ';
     $fields .= 's.status as status, ';
     $fields .= 's.id as submissionid, ';
     $fields .= 's.timecreated as firstsubmission, ';
     $fields .= 's.timemodified as timesubmitted, ';
     $fields .= 's.attemptnumber as attemptnumber, ';
     $fields .= 'g.id as gradeid, ';
     $fields .= 'g.grade as grade, ';
     $fields .= 'g.timemodified as timemarked, ';
     $fields .= 'g.timecreated as firstmarked, ';
     $fields .= 'uf.mailed as mailed, ';
     $fields .= 'uf.locked as locked, ';
     $fields .= 'uf.extensionduedate as extensionduedate, ';
     $fields .= 'uf.workflowstate as workflowstate, ';
     $fields .= 'uf.allocatedmarker as allocatedmarker ';
     $from = '{user} u
                      LEFT JOIN {assign_submission} s
                             ON u.id = s.userid
                            AND s.assignment = :assignmentid1
                            AND s.latest = 1
                      LEFT JOIN {assign_grades} g
                             ON u.id = g.userid
                            AND g.assignment = :assignmentid2 ';
     // For group submissions we don't immediately create an entry in the assign_submission table for each user,
     // instead the userid is set to 0. In this case we use a different query to retrieve the grade for the user.
     if ($this->assignment->get_instance()->teamsubmission) {
         $params['assignmentid4'] = (int) $this->assignment->get_instance()->id;
         $grademaxattempt = 'SELECT mxg.userid, MAX(mxg.attemptnumber) AS maxattempt
                               FROM {assign_grades} mxg
                              WHERE mxg.assignment = :assignmentid4
                           GROUP BY mxg.userid';
         $from .= 'LEFT JOIN (' . $grademaxattempt . ') gmx
                          ON u.id = gmx.userid
                         AND g.attemptnumber = gmx.maxattempt ';
     } else {
         $from .= 'AND g.attemptnumber = s.attemptnumber ';
     }
     $from .= 'LEFT JOIN {assign_user_flags} uf
                      ON u.id = uf.userid
                     AND uf.assignment = :assignmentid3';
     $userparams = array();
     $userindex = 0;
     list($userwhere, $userparams) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED, 'user');
     $where = 'u.id ' . $userwhere;
     $params = array_merge($params, $userparams);
     // The filters do not make sense when there are no submissions, so do not apply them.
     if ($this->assignment->is_any_submission_plugin_enabled()) {
         if ($filter == ASSIGN_FILTER_SUBMITTED) {
             $where .= ' AND (s.timemodified IS NOT NULL AND
                              s.status = :submitted) ';
             $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
         } else {
             if ($filter == ASSIGN_FILTER_NOT_SUBMITTED) {
                 $where .= ' AND (s.timemodified IS NULL OR s.status != :submitted) ';
                 $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
             } else {
                 if ($filter == ASSIGN_FILTER_REQUIRE_GRADING) {
                     $where .= ' AND (s.timemodified IS NOT NULL AND
                              s.status = :submitted AND
                              (s.timemodified >= g.timemodified OR g.timemodified IS NULL OR g.grade IS NULL))';
                     $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
                 } else {
                     if (strpos($filter, ASSIGN_FILTER_SINGLE_USER) === 0) {
                         $userfilter = (int) array_pop(explode('=', $filter));
                         $where .= ' AND (u.id = :userid)';
                         $params['userid'] = $userfilter;
                     }
                 }
             }
         }
     }
     if ($this->assignment->get_instance()->markingworkflow && $this->assignment->get_instance()->markingallocation) {
         if (has_capability('mod/assign:manageallocations', $this->assignment->get_context())) {
             // Check to see if marker filter is set.
             $markerfilter = (int) get_user_preferences('assign_markerfilter', '');
             if (!empty($markerfilter)) {
                 if ($markerfilter == ASSIGN_MARKER_FILTER_NO_MARKER) {
                     $where .= ' AND (uf.allocatedmarker IS NULL OR uf.allocatedmarker = 0)';
                 } else {
                     $where .= ' AND uf.allocatedmarker = :markerid';
                     $params['markerid'] = $markerfilter;
                 }
             }
         } else {
             // Only show users allocated to this marker.
             $where .= ' AND uf.allocatedmarker = :markerid';
             $params['markerid'] = $USER->id;
         }
     }
     if ($this->assignment->get_instance()->markingworkflow) {
         $workflowstates = $this->assignment->get_marking_workflow_states_for_current_user();
         if (!empty($workflowstates)) {
             $workflowfilter = get_user_preferences('assign_workflowfilter', '');
             if ($workflowfilter == ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED) {
                 $where .= ' AND (uf.workflowstate = :workflowstate OR uf.workflowstate IS NULL OR ' . $DB->sql_isempty('assign_user_flags', 'workflowstate', true, true) . ')';
                 $params['workflowstate'] = $workflowfilter;
             } else {
                 if (array_key_exists($workflowfilter, $workflowstates)) {
                     $where .= ' AND uf.workflowstate = :workflowstate';
                     $params['workflowstate'] = $workflowfilter;
                 }
             }
         }
     }
     $this->set_sql($fields, $from, $where, $params);
     if ($downloadfilename) {
         $this->is_downloading('csv', $downloadfilename);
     }
     $columns = array();
     $headers = array();
     // Select.
     if (!$this->is_downloading() && $this->hasgrade) {
         $columns[] = 'select';
         $headers[] = get_string('select') . '<div class="selectall"><label class="accesshide" for="selectall">' . get_string('selectall') . '</label>
                 <input type="checkbox" id="selectall" name="selectall" title="' . get_string('selectall') . '"/></div>';
     }
     // User picture.
     if ($this->hasviewblind || !$this->assignment->is_blind_marking()) {
         if (!$this->is_downloading()) {
             $columns[] = 'picture';
             $headers[] = get_string('pictureofuser');
         } else {
             $columns[] = 'recordid';
             $headers[] = get_string('recordid', 'assign');
         }
         // Fullname.
         $columns[] = 'fullname';
         $headers[] = get_string('fullname');
         foreach ($extrauserfields as $extrafield) {
             $columns[] = $extrafield;
             $headers[] = get_user_field_name($extrafield);
         }
     } else {
         // Record ID.
         $columns[] = 'recordid';
         $headers[] = get_string('recordid', 'assign');
     }
     // Submission status.
     $columns[] = 'status';
     $headers[] = get_string('status', 'assign');
     // Team submission columns.
     if ($assignment->get_instance()->teamsubmission) {
         $columns[] = 'team';
         $headers[] = get_string('submissionteam', 'assign');
     }
     // Allocated marker.
     if ($this->assignment->get_instance()->markingworkflow && $this->assignment->get_instance()->markingallocation && has_capability('mod/assign:manageallocations', $this->assignment->get_context())) {
         // Add a column for the allocated marker.
         $columns[] = 'allocatedmarker';
         $headers[] = get_string('marker', 'assign');
     }
     // Grade.
     $columns[] = 'grade';
     $headers[] = get_string('grade');
     if ($this->is_downloading()) {
         if ($this->assignment->get_instance()->grade >= 0) {
             $columns[] = 'grademax';
             $headers[] = get_string('maxgrade', 'assign');
         } else {
             // This is a custom scale.
             $columns[] = 'scale';
             $headers[] = get_string('scale', 'assign');
         }
         if ($this->assignment->get_instance()->markingworkflow) {
             // Add a column for the marking workflow state.
             $columns[] = 'workflowstate';
             $headers[] = get_string('markingworkflowstate', 'assign');
         }
         // Add a column for the list of valid marking workflow states.
         $columns[] = 'gradecanbechanged';
         $headers[] = get_string('gradecanbechanged', 'assign');
     }
     if (!$this->is_downloading() && $this->hasgrade) {
         // We have to call this column userid so we can use userid as a default sortable column.
         $columns[] = 'userid';
         $headers[] = get_string('edit');
     }
     // Submission plugins.
     if ($assignment->is_any_submission_plugin_enabled()) {
         $columns[] = 'timesubmitted';
         $headers[] = get_string('lastmodifiedsubmission', 'assign');
         foreach ($this->assignment->get_submission_plugins() as $plugin) {
             if ($this->is_downloading()) {
                 if ($plugin->is_visible() && $plugin->is_enabled()) {
                     foreach ($plugin->get_editor_fields() as $field => $description) {
                         $index = 'plugin' . count($this->plugincache);
                         $this->plugincache[$index] = array($plugin, $field);
                         $columns[] = $index;
                         $headers[] = $plugin->get_name();
                     }
                 }
             } else {
                 if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
                     $index = 'plugin' . count($this->plugincache);
                     $this->plugincache[$index] = array($plugin);
                     $columns[] = $index;
                     $headers[] = $plugin->get_name();
                 }
             }
         }
     }
     // Time marked.
     $columns[] = 'timemarked';
     $headers[] = get_string('lastmodifiedgrade', 'assign');
     // Feedback plugins.
     foreach ($this->assignment->get_feedback_plugins() as $plugin) {
         if ($this->is_downloading()) {
             if ($plugin->is_visible() && $plugin->is_enabled()) {
                 foreach ($plugin->get_editor_fields() as $field => $description) {
                     $index = 'plugin' . count($this->plugincache);
                     $this->plugincache[$index] = array($plugin, $field);
                     $columns[] = $index;
                     $headers[] = $description;
                 }
             }
         } else {
             if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
                 $index = 'plugin' . count($this->plugincache);
                 $this->plugincache[$index] = array($plugin);
                 $columns[] = $index;
                 $headers[] = $plugin->get_name();
             }
         }
     }
     // Exclude 'Final grade' column in downloaded grading worksheets.
     if (!$this->is_downloading()) {
         // Final grade.
         $columns[] = 'finalgrade';
         $headers[] = get_string('finalgrade', 'grades');
     }
     // Load the grading info for all users.
     $this->gradinginfo = grade_get_grades($this->assignment->get_course()->id, 'mod', 'assign', $this->assignment->get_instance()->id, $users);
     if (!empty($CFG->enableoutcomes) && !empty($this->gradinginfo->outcomes)) {
         $columns[] = 'outcomes';
         $headers[] = get_string('outcomes', 'grades');
     }
     // Set the columns.
     $this->define_columns($columns);
     $this->define_headers($headers);
     foreach ($extrauserfields as $extrafield) {
         $this->column_class($extrafield, $extrafield);
     }
     $this->no_sorting('recordid');
     $this->no_sorting('finalgrade');
     $this->no_sorting('userid');
     $this->no_sorting('select');
     $this->no_sorting('outcomes');
     if ($assignment->get_instance()->teamsubmission) {
         $this->no_sorting('team');
     }
     $plugincolumnindex = 0;
     foreach ($this->assignment->get_submission_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
             $submissionpluginindex = 'plugin' . $plugincolumnindex++;
             $this->no_sorting($submissionpluginindex);
         }
     }
     foreach ($this->assignment->get_feedback_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
             $feedbackpluginindex = 'plugin' . $plugincolumnindex++;
             $this->no_sorting($feedbackpluginindex);
         }
     }
     // When there is no data we still want the column headers printed in the csv file.
     if ($this->is_downloading()) {
         $this->start_output();
     }
 }
示例#3
0
    /**
     * overridden constructor keeps a reference to the assignment class that is displaying this table
     *
     * @param assign $assignment The assignment class
     * @param int $perpage how many per page
     * @param string $filter The current filter
     * @param int $rowoffset For showing a subsequent page of results
     * @param bool $quickgrading Is this table wrapped in a quickgrading form?
     */
    function __construct(assign $assignment, $perpage, $filter, $rowoffset, $quickgrading) {
        global $CFG, $PAGE, $DB;
        parent::__construct('mod_assign_grading');
        $this->assignment = $assignment;
        $this->perpage = $perpage;
        $this->quickgrading = $quickgrading;
        $this->output = $PAGE->get_renderer('mod_assign');

        $this->define_baseurl(new moodle_url($CFG->wwwroot . '/mod/assign/view.php', array('action'=>'grading', 'id'=>$assignment->get_course_module()->id)));

        // do some business - then set the sql

        $currentgroup = groups_get_activity_group($assignment->get_course_module(), true);

        if ($rowoffset) {
            $this->rownum = $rowoffset - 1;
        }

        $users = array_keys( $assignment->list_participants($currentgroup, true));
        if (count($users) == 0) {
            // insert a record that will never match to the sql is still valid.
            $users[] = -1;
        }

        $params = array();
        $params['assignmentid1'] = (int)$this->assignment->get_instance()->id;
        $params['assignmentid2'] = (int)$this->assignment->get_instance()->id;

        $fields = user_picture::fields('u') . ', u.id as userid, u.firstname as firstname, u.lastname as lastname, ';
        $fields .= 's.status as status, s.id as submissionid, s.timecreated as firstsubmission, s.timemodified as timesubmitted, ';
        $fields .= 'g.id as gradeid, g.grade as grade, g.timemodified as timemarked, g.timecreated as firstmarked, g.mailed as mailed, g.locked as locked';
        $from = '{user} u LEFT JOIN {assign_submission} s ON u.id = s.userid AND s.assignment = :assignmentid1' .
                        ' LEFT JOIN {assign_grades} g ON u.id = g.userid AND g.assignment = :assignmentid2';

        $userparams = array();
        $userindex = 0;

        list($userwhere, $userparams) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED, 'user');
        $where = 'u.id ' . $userwhere;
        $params = array_merge($params, $userparams);

        if ($filter == ASSIGN_FILTER_SUBMITTED) {
            $where .= ' AND s.timecreated > 0 ';
        }
        if ($filter == ASSIGN_FILTER_REQUIRE_GRADING) {
            $where .= ' AND (s.timemodified > g.timemodified OR (s.timemodified IS NOT NULL AND g.timemodified IS NULL))';
        }
        if (strpos($filter, ASSIGN_FILTER_SINGLE_USER) === 0) {
            $userfilter = (int) array_pop(explode('=', $filter));
            $where .= ' AND (u.id = :userid)';
            $params['userid'] = $userfilter;
        }
        $this->set_sql($fields, $from, $where, $params);

        $columns = array();
        $headers = array();

        // Select
        $columns[] = 'select';
        $headers[] = get_string('select') . '<div class="selectall"><input type="checkbox" name="selectall" title="' . get_string('selectall') . '"/></div>';

        // Edit links
        if (!$this->is_downloading()) {
            $columns[] = 'edit';
            $headers[] = get_string('edit');
        }

        // User picture
        $columns[] = 'picture';
        $headers[] = get_string('pictureofuser');

        // Fullname
        $columns[] = 'fullname';
        $headers[] = get_string('fullname');

        // Submission status
        if ($assignment->is_any_submission_plugin_enabled()) {
            $columns[] = 'status';
            $headers[] = get_string('status');
        }


        // Grade
        $columns[] = 'grade';
        $headers[] = get_string('grade');

        // Submission plugins
        if ($assignment->is_any_submission_plugin_enabled()) {
            $columns[] = 'timesubmitted';
            $headers[] = get_string('lastmodifiedsubmission', 'assign');

            foreach ($this->assignment->get_submission_plugins() as $plugin) {
                if ($plugin->is_visible() && $plugin->is_enabled()) {
                    $columns[] = 'assignsubmission_' . $plugin->get_type();
                    $headers[] = $plugin->get_name();
                }
            }
        }

        // time marked
        $columns[] = 'timemarked';
        $headers[] = get_string('lastmodifiedgrade', 'assign');

        // Feedback plugins
        foreach ($this->assignment->get_feedback_plugins() as $plugin) {
            if ($plugin->is_visible() && $plugin->is_enabled()) {
                $columns[] = 'assignfeedback_' . $plugin->get_type();
                $headers[] = $plugin->get_name();
            }
        }

        // final grade
        $columns[] = 'finalgrade';
        $headers[] = get_string('finalgrade', 'grades');

        // load the grading info for all users
        $this->gradinginfo = grade_get_grades($this->assignment->get_course()->id, 'mod', 'assign', $this->assignment->get_instance()->id, $users);

        if (!empty($CFG->enableoutcomes) && !empty($this->gradinginfo->outcomes)) {
            $columns[] = 'outcomes';
            $headers[] = get_string('outcomes', 'grades');
        }


        // set the columns
        $this->define_columns($columns);
        $this->define_headers($headers);
        $this->no_sorting('finalgrade');
        $this->no_sorting('edit');
        $this->no_sorting('select');
        $this->no_sorting('outcomes');

        foreach ($this->assignment->get_submission_plugins() as $plugin) {
            if ($plugin->is_visible() && $plugin->is_enabled()) {
                $this->no_sorting('assignsubmission_' . $plugin->get_type());
            }
        }
        foreach ($this->assignment->get_feedback_plugins() as $plugin) {
            if ($plugin->is_visible() && $plugin->is_enabled()) {
                $this->no_sorting('assignfeedback_' . $plugin->get_type());
            }
        }

    }
示例#4
0
    /**
     * Process an uploaded zip file
     *
     * @param assign $assignment - The assignment instance
     * @param assign_feedback_file $fileplugin - The file feedback plugin
     * @return string - The html response
     */
    public function import_zip_files($assignment, $fileplugin) {
        global $USER, $CFG, $PAGE, $DB;

        @set_time_limit(ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME);
        $packer = get_file_packer('application/zip');

        $feedbackfilesupdated = 0;
        $feedbackfilesadded = 0;
        $userswithnewfeedback = array();
        $contextid = $assignment->get_context()->id;

        $fs = get_file_storage();
        $files = $fs->get_directory_files($contextid,
                                          'assignfeedback_file',
                                          ASSIGNFEEDBACK_FILE_IMPORT_FILEAREA,
                                          $USER->id,
                                          '/import/');

        $currentgroup = groups_get_activity_group($assignment->get_course_module(), true);
        $allusers = $assignment->list_participants($currentgroup, false);
        $participants = array();
        foreach ($allusers as $user) {
            $participants[$assignment->get_uniqueid_for_user($user->id)] = $user;
        }

        foreach ($files as $unzippedfile) {
            // Set the timeout for unzipping each file.
            $user = null;
            $plugin = null;
            $filename = '';

            if ($this->is_valid_filename_for_import($assignment, $unzippedfile, $participants, $user, $plugin, $filename)) {
                if ($this->is_file_modified($assignment, $user, $plugin, $filename, $unzippedfile)) {
                    $grade = $assignment->get_user_grade($user->id, true);

                    if ($oldfile = $fs->get_file($contextid,
                                                 'assignfeedback_file',
                                                 ASSIGNFEEDBACK_FILE_FILEAREA,
                                                 $grade->id,
                                                 '/',
                                                 $filename)) {
                        // Update existing feedback file.
                        $oldfile->replace_content_with($unzippedfile);
                        $feedbackfilesupdated++;
                    } else {
                        // Create a new feedback file.
                        $newfilerecord = new stdClass();
                        $newfilerecord->contextid = $contextid;
                        $newfilerecord->component = 'assignfeedback_file';
                        $newfilerecord->filearea = ASSIGNFEEDBACK_FILE_FILEAREA;
                        $newfilerecord->filename = $filename;
                        $newfilerecord->filepath = '/';
                        $newfilerecord->itemid = $grade->id;
                        $fs->create_file_from_storedfile($newfilerecord, $unzippedfile);
                        $feedbackfilesadded++;
                    }
                    $userswithnewfeedback[$user->id] = 1;

                    // Update the number of feedback files for this user.
                    $fileplugin->update_file_count($grade);

                    // Update the last modified time on the grade which will trigger student notifications.
                    $assignment->notify_grade_modified($grade);
                }
            }
        }

        require_once($CFG->dirroot . '/mod/assign/feedback/file/renderable.php');
        $importsummary = new assignfeedback_file_import_summary($assignment->get_course_module()->id,
                                                            count($userswithnewfeedback),
                                                            $feedbackfilesadded,
                                                            $feedbackfilesupdated);

        $assignrenderer = $assignment->get_renderer();
        $renderer = $PAGE->get_renderer('assignfeedback_file');

        $o = '';

        $o .= $assignrenderer->render(new assign_header($assignment->get_instance(),
                                                        $assignment->get_context(),
                                                        false,
                                                        $assignment->get_course_module()->id,
                                                        get_string('uploadzipsummary', 'assignfeedback_file')));

        $o .= $renderer->render($importsummary);

        $o .= $assignrenderer->render_footer();
        return $o;
    }
示例#5
0
 /**
  * Process an uploaded zip file
  *
  * @param assign $assignment - The assignment instance
  * @param assign_feedback_file $fileplugin - The file feedback plugin
  * @return string - The html response
  */
 public function import_zip_files($assignment, $fileplugin)
 {
     global $CFG, $PAGE, $DB;
     core_php_time_limit::raise(ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME);
     $packer = get_file_packer('application/zip');
     $feedbackfilesupdated = 0;
     $feedbackfilesadded = 0;
     $userswithnewfeedback = array();
     $contextid = $assignment->get_context()->id;
     $fs = get_file_storage();
     $files = $this->get_import_files($contextid);
     $currentgroup = groups_get_activity_group($assignment->get_course_module(), true);
     $allusers = $assignment->list_participants($currentgroup, false);
     $participants = array();
     foreach ($allusers as $user) {
         $participants[$assignment->get_uniqueid_for_user($user->id)] = $user;
     }
     foreach ($files as $unzippedfile) {
         // Set the timeout for unzipping each file.
         $user = null;
         $plugin = null;
         $filename = '';
         if ($this->is_valid_filename_for_import($assignment, $unzippedfile, $participants, $user, $plugin, $filename)) {
             if ($this->is_file_modified($assignment, $user, $plugin, $filename, $unzippedfile)) {
                 $grade = $assignment->get_user_grade($user->id, true);
                 // In 3.1 the default download structure of the submission files changed so that each student had their own
                 // separate folder, the files were not renamed and the folder structure was kept. It is possible that
                 // a user downloaded the submission files in 3.0 (or earlier) and edited the zip to add feedback or
                 // changed the behavior back to the previous format, the following code means that we will still support the
                 // old file structure. For more information please see - MDL-52489 / MDL-56022.
                 $path = pathinfo($filename);
                 if ($path['dirname'] == '.') {
                     // Student submissions are not in separate folders.
                     $basename = $filename;
                     $dirname = "/";
                     $dirnamewslash = "/";
                 } else {
                     $basename = $path['basename'];
                     $dirname = $path['dirname'];
                     $dirnamewslash = $dirname . "/";
                 }
                 if ($oldfile = $fs->get_file($contextid, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id, $dirname, $basename)) {
                     // Update existing feedback file.
                     $oldfile->replace_file_with($unzippedfile);
                     $feedbackfilesupdated++;
                 } else {
                     // Create a new feedback file.
                     $newfilerecord = new stdClass();
                     $newfilerecord->contextid = $contextid;
                     $newfilerecord->component = 'assignfeedback_file';
                     $newfilerecord->filearea = ASSIGNFEEDBACK_FILE_FILEAREA;
                     $newfilerecord->filename = $basename;
                     $newfilerecord->filepath = $dirnamewslash;
                     $newfilerecord->itemid = $grade->id;
                     $fs->create_file_from_storedfile($newfilerecord, $unzippedfile);
                     $feedbackfilesadded++;
                 }
                 $userswithnewfeedback[$user->id] = 1;
                 // Update the number of feedback files for this user.
                 $fileplugin->update_file_count($grade);
                 // Update the last modified time on the grade which will trigger student notifications.
                 $assignment->notify_grade_modified($grade);
             }
         }
     }
     require_once $CFG->dirroot . '/mod/assign/feedback/file/renderable.php';
     $importsummary = new assignfeedback_file_import_summary($assignment->get_course_module()->id, count($userswithnewfeedback), $feedbackfilesadded, $feedbackfilesupdated);
     $assignrenderer = $assignment->get_renderer();
     $renderer = $PAGE->get_renderer('assignfeedback_file');
     $o = '';
     $o .= $assignrenderer->render(new assign_header($assignment->get_instance(), $assignment->get_context(), false, $assignment->get_course_module()->id, get_string('uploadzipsummary', 'assignfeedback_file')));
     $o .= $renderer->render($importsummary);
     $o .= $assignrenderer->render_footer();
     return $o;
 }
 /**
  * Automatic student evaluation
  *
  * This function automatically avaluates the student according to enrichment criteria
  * which are cross referenced with the data obtained through Learning Analytics data mining procedures.
  *
  * For the new logging system from Moodle 2.6 onwards, Learning Analytics will only be obtained from
  * Legacy log Table a.k.a. {log} or New Internal Log Table a.k.a {logstore_standard_log}.
  *
  * External logstores can't be used because their data structure and logging data are custom
  * and can not be predicted. Only a log store creator would be able to change the data queries bellow
  * according to his log store specifications.
  *
  *
  * @param array $criterion data about the rubric design
  * @param array $options enriched rubric options
  */
 protected function evaluate_enrichment(&$criterion, $options)
 {
     global $DB;
     global $PAGE;
     global $CFG;
     $moduletypename = null;
     $benchmarkstudent = null;
     $benchmarkstudents = null;
     $benchmarkcriterion = null;
     $untiltime = null;
     $fromtime = null;
     $selectallstudents = null;
     $participatingstudents = null;
     $iterations = 0;
     $sql = null;
     // Get the current assignment data.
     $curmoduleid = (int) $PAGE->cm->module;
     $curmodulename = $PAGE->cm->modname;
     $curcmid = $PAGE->cm->id;
     $gradingmoduleid = $PAGE->cm->instance;
     $courseid = $PAGE->cm->course;
     //**** For Moodle 2.6 onwards new logging system  ***//
     /* If the Legacy Log is used, we work with it. If not, we use the new Internal Log (Standard). The old log is more efficient for the queries used bellow.
      *  If en External Logstore is used and both Legacy and new Internal logs are deactivated, do nothing, as we can't know in advance how the new
      *  external logs are stored and how the store's log tables are structured.
      *
      */
     // Set the necessary variables
     $uselegacyreader = true;
     $useinternalreader = null;
     $minloginternalreader = null;
     $logtable = null;
     $moodle_2_6_0_version = '2013111800';
     // Get the necessary variables if needed
     if ($CFG->version >= $moodle_2_6_0_version) {
         require_once $CFG->dirroot . '/report/outline/locallib.php';
         list($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable) = report_outline_get_common_log_variables();
     }
     //**** For moodle 2.2 versions assignment modules ****//
     $studentid = optional_param('userid', '', PARAM_INT);
     //**** For moodle 2.3 onwards assignment module ****//
     if (!$studentid) {
         require_once $CFG->dirroot . '/mod/assign/locallib.php';
         $context = context_module::instance($PAGE->cm->id);
         $assignment = new assign($context, $PAGE->cm, $PAGE->cm->course);
         $useridlist = array_keys($assignment->list_participants(0, true));
         sort($useridlist);
         // The users list containing the IDs of students evaluated.
         $rownum = $_REQUEST['rownum'];
         // Check if next or previous buttons are pressed in the avaluation form.
         if (array_key_exists('saveandshownext', $_REQUEST) || array_key_exists('nosaveandnext', $_REQUEST)) {
             $rownum++;
         }
         if (array_key_exists('nosaveandprevious', $_REQUEST)) {
             $rownum--;
         }
         $studentid = $useridlist[$rownum];
     }
     // SQL for including all course enroled students.
     $selectindividual = "= {$studentid}";
     $selectallstudents = "IN (SELECT u.id\n                                    FROM {role_assignments} ra\n                                        JOIN {context} c ON ra.contextid = c.id AND c.contextlevel = 50\n                                        JOIN {user} u ON u.id = ra.userid\n                                        JOIN {course} crse ON c.instanceid = crse.id\n                                    WHERE ra.roleid = 5\n                                        AND crse.id = {$courseid})";
     // Timestamp enrichment calculations according to assignment module (old or new type assignments).
     // In case of old type assignment...
     if ($curmoduleid == $this->oldassignmoduleid) {
         // Get potential enrichment due date.
         if ($options['timestampenrichmentend']) {
             $sql = 'SELECT asmnt.timedue AS duedate FROM {assignment} asmnt WHERE asmnt.id = ' . $gradingmoduleid;
             $untiltime = $DB->get_field_sql($sql, null);
         }
         // Get potential availability start time.
         if ($options['timestampenrichmentstart']) {
             $sql = 'SELECT asmnt.timeavailable AS startdate FROM {assignment} asmnt WHERE asmnt.id = ' . $gradingmoduleid;
             $fromtime = $DB->get_field_sql($sql, null);
         }
         // In case of new type assignment...
     } else {
         if ($curmoduleid == $this->newassignmoduleid) {
             // Get potential enrichment due date.
             if ($options['timestampenrichmentend']) {
                 $sql = 'SELECT asmnt.duedate AS duedate FROM {assign} asmnt WHERE asmnt.id = ' . $gradingmoduleid;
                 $untiltime = $DB->get_field_sql($sql, null);
             }
             // Get potential availability start time.
             if ($options['timestampenrichmentstart']) {
                 $sql = 'SELECT asmnt.allowsubmissionsfromdate AS startdate FROM {assign} asmnt WHERE asmnt.id = ' . $gradingmoduleid;
                 $fromtime = $DB->get_field_sql($sql, null);
             }
         }
     }
     // Retrieve Learning Analytics data according to criterion type (collaboration - study - grade).
     switch ($criterion['criteriontype']) {
         // In case of checking student studying, perform data mining from study logs of selected course modules (resources).
         case gradingform_erubric_controller::INTERACTION_TYPE_STUDY:
             $moduletypename = 'resource';
             foreach ($criterion['coursemodules'][$moduletypename][0] as $mdlinstance) {
                 // Iterate through course modules.
                 $tempinstance = explode('->', $mdlinstance);
                 $moduleid = $tempinstance[0];
                 $instanceid = $tempinstance[1];
                 $timefield = 'time';
                 // Get log files according to curent log store
                 if ($uselegacyreader) {
                     // Old log
                     $sql = "SELECT COUNT(lg.id) AS TOTALS\n                                FROM {log} lg\n                                    INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n                                WHERE   lg.userid {$selectindividual}\n                                    AND lg.action = 'view'\n                                    AND cm.course = {$courseid}\n                                    AND cm.module = {$moduleid}\n                                    AND cm.instance = {$instanceid} ";
                 } elseif ($useinternalreader) {
                     // New log
                     $sql = "SELECT COUNT(lg.id) AS TOTALS\n                                FROM {" . $logtable . "} lg\n                                    INNER JOIN {modules} mdls ON (lg.objecttable = mdls.name)\n                                WHERE   lg.userid {$selectindividual}\n                                    AND lg.action = 'viewed'\n                                    AND lg.courseid = {$courseid}\n                                    AND mdls.id = {$moduleid}\n                                    AND lg.objectid = {$instanceid} ";
                     $timefield = 'timecreated';
                 }
                 $this->get_value_from_learning_analytics($benchmarkstudent, $sql, 'lg', $timefield, $fromtime, $untiltime, 1);
                 // If the criterion has a global reference according to all students participating.
                 if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) {
                     $sql = "SELECT COUNT(crse.id) AS Totals\n                                FROM {role_assignments} ra\n                                    JOIN {context} c ON ra.contextid = c.id AND c.contextlevel = 50\n                                    JOIN {user} u ON u.id = ra.userid\n                                    JOIN {course} crse ON c.instanceid = crse.id\n                                WHERE ra.roleid = 5\n                                    AND crse.id = {$courseid} ";
                     $count = $DB->get_field_sql($sql, null);
                     if ($count && $count > 0) {
                         // If there is at least one student.
                         $iterations++;
                         // Get log files according to curent log store
                         if ($uselegacyreader) {
                             // Old log
                             $sql = "SELECT COUNT(lg.id) AS TOTALS\n                                        FROM {log} lg\n                                            INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n                                        WHERE   lg.userid {$selectallstudents}\n                                            AND lg.action = 'view'\n                                            AND cm.course = {$courseid}\n                                            AND cm.module = {$moduleid}\n                                            AND cm.instance = {$instanceid} ";
                         } elseif ($useinternalreader) {
                             // New log
                             $sql = "SELECT COUNT(lg.id) AS TOTALS\n                                        FROM {" . $logtable . "} lg\n                                            INNER JOIN {modules} mdls ON (lg.objecttable = mdls.name)\n                                        WHERE   lg.userid {$selectallstudents}\n                                            AND lg.action = 'viewed'\n                                            AND lg.courseid = {$courseid}\n                                            AND mdls.id = {$moduleid}\n                                            AND lg.objectid = {$instanceid} ";
                             $timefield = 'timecreated';
                         }
                         $this->get_value_from_learning_analytics($benchmarkstudents, $sql, 'lg', $timefield, $fromtime, $untiltime, $count);
                     }
                 }
             }
             break;
             // In case of checking student grades, perform data mining from grade logs of selected course modules (assignments).
         // In case of checking student grades, perform data mining from grade logs of selected course modules (assignments).
         case gradingform_erubric_controller::INTERACTION_TYPE_GRADE:
             $moduletypename = 'assignment';
             foreach ($criterion['coursemodules'][$moduletypename][0] as $mdlinstance) {
                 // Iterate through course modules.
                 $tempinstance = explode('->', $mdlinstance);
                 $moduleid = $tempinstance[0];
                 $instanceid = $tempinstance[1];
                 $tempstudentcurrgrade = null;
                 $sql = "SELECT gr.finalgrade AS Grade\n                            FROM {grade_grades} gr\n                                INNER JOIN {grade_items} gi ON (gi.id = gr.itemid)\n                                INNER JOIN {modules} md ON (md.name = gi.itemmodule)\n                            WHERE   gr.userid {$selectindividual}\n                                AND gi.courseid = {$courseid}\n                                AND md.id = {$moduleid}\n                                AND gi.iteminstance = {$instanceid} ";
                 $this->get_value_from_learning_analytics($tempstudentcurrgrade, $sql, null, null, null, null, 1);
                 // If the student has not been graded for this course assignment,
                 // there is no point on calculating his or the others benchmark.
                 if (!is_null($tempstudentcurrgrade)) {
                     $iterations++;
                     // Get maximum grade score for current assignment module in order to define student performance according to 100 scale.
                     $sql = "SELECT assmnt.grade AS maxgrade ";
                     if ($moduleid == $this->newassignmoduleid) {
                         $sql .= "FROM {assign} assmnt ";
                         // New type assignment modules (Moodle 2.3+)
                     } else {
                         $sql .= "FROM {assignment} assmnt ";
                         // Old type assignment modules (Moodle 2.2)
                     }
                     $sql .= "WHERE   assmnt.id = {$instanceid}";
                     $tempassignmax = (double) $DB->get_field_sql($sql, null);
                     $benchmarkstudent += (double) round($tempstudentcurrgrade / $tempassignmax * 100, 2);
                     // If the criterion has a global reference according to all students grades.
                     if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) {
                         $sql = "SELECT AVG(gr.finalgrade) AS AvgGrades\n                                FROM {grade_grades} gr\n                                    INNER JOIN {grade_items} gi ON (gi.id = gr.itemid)\n                                    INNER JOIN {modules} md ON (md.name = gi.itemmodule)\n                                WHERE   gr.userid {$selectallstudents}\n                                    AND gi.courseid = {$courseid}\n                                    AND md.id = {$moduleid}\n                                    AND gi.iteminstance = {$instanceid} ";
                         $tempstudentsgrade = $DB->get_field_sql($sql, null);
                         if (!is_null($tempstudentsgrade)) {
                             // If the value is valid.
                             $benchmarkstudents += (double) round($tempstudentsgrade / $tempassignmax * 100, 2);
                         }
                     }
                 }
             }
             break;
             // In case of checking student collaboration, perform data mining according to collaboration type:
             // - Entries, for simple log occurences in selected course modules,
             // - File Adds, for summing number of file adds (not number of files!), from course modules of type forum.
             // - Replies, for summing number of replied posts, from course modules of type forum.
             // - Interactions, for total number of students the evaluated student interacted during using the selected course modules.
         // In case of checking student collaboration, perform data mining according to collaboration type:
         // - Entries, for simple log occurences in selected course modules,
         // - File Adds, for summing number of file adds (not number of files!), from course modules of type forum.
         // - Replies, for summing number of replied posts, from course modules of type forum.
         // - Interactions, for total number of students the evaluated student interacted during using the selected course modules.
         case gradingform_erubric_controller::INTERACTION_TYPE_COLLABORATION:
             $moduletypename = 'activity';
             // In case of students interactions, benchmarks will be calculated after all modules have been processed.
             if ($criterion['collaborationtype'] == gradingform_erubric_controller::COLLABORATION_TYPE_INTERACTIONS) {
                 $distinctUsersFound = array();
             }
             // In case of students file submissions, benchmarks will be initialised with zero values.
             if ($criterion['collaborationtype'] == gradingform_erubric_controller::COLLABORATION_TYPE_FILE_ADDS) {
                 $benchmarkstudent = 0;
                 if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) {
                     $benchmarkstudents = 0;
                 }
             }
             foreach ($criterion['coursemodules'][$moduletypename][0] as $mdlinstance) {
                 // Iterate through course modules.
                 $tempinstance = explode('->', $mdlinstance);
                 $moduleid = $tempinstance[0];
                 $instanceid = $tempinstance[1];
                 $logtablename = 'log';
                 $timefield = 'time';
                 switch ($criterion['collaborationtype']) {
                     // In case of checking simple entries in forums and chats,
                     // just check for 'add post' or 'talk' actions inside moodle log.
                     case gradingform_erubric_controller::COLLABORATION_TYPE_ENTRIES:
                         // Get log files according to curent log store
                         if ($uselegacyreader) {
                             // Old log
                             $sql = "SELECT COUNT(lg.id) AS TOTALS\n                                        FROM {log} lg\n                                            INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n                                        WHERE   lg.userid {$selectindividual}\n                                            AND (lg.action = 'add post' OR lg.action = 'talk')\n                                            AND cm.course = {$courseid}\n                                            AND cm.module = {$moduleid}\n                                            AND cm.instance = {$instanceid} ";
                         } elseif ($useinternalreader) {
                             // New log
                             // There is no direct way to get both posts and talks from the new log
                             // according to the module id (forum or chat) and instance id (forum id or chat id),
                             // so we must seperate the original log query with two different queries that don't have to refer to the new log anymore.
                             // Bye bye new log... Bummer :(
                             // If this is a forum course module
                             if ($moduleid == $this->forummoduleid) {
                                 $sql = "SELECT COUNT(fp.id) AS TOTALS\n                                            FROM {forum_posts} fp\n                                                INNER JOIN {forum_discussions} fdcs ON (fp.discussion = fdcs.id)\n                                            WHERE   fp.userid {$selectindividual}\n                                                AND fdcs.course = {$courseid}\n                                                AND fdcs.forum = {$instanceid} ";
                                 $logtablename = 'fp';
                                 $timefield = 'created';
                                 // Else only if this is a chat course module
                             } elseif ($moduleid == $this->chatmoduleid) {
                                 $sql = "SELECT COUNT(chms.id) AS TOTALS\n                                            FROM {chat_messages} chms\n                                            WHERE   chms.userid {$selectindividual}\n                                                AND chms.system = 0\n                                                AND chms.chatid = {$instanceid} ";
                                 $logtablename = 'chms';
                                 $timefield = 'timestamp';
                             }
                         }
                         $this->get_value_from_learning_analytics($benchmarkstudent, $sql, $logtablename, $timefield, $fromtime, $untiltime, 1);
                         // If the criterion has a global reference according to all students' collaborations,
                         // we hold accountable only participating students. If a student is absent (no log entries), he is not accounted for.
                         if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) {
                             $count = 0;
                             // Get log files according to curent log store
                             if ($uselegacyreader) {
                                 // Old log
                                 $sql = "SELECT DISTINCT (lg.userid) AS userids\n                                            FROM {log} lg\n                                                INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n                                            WHERE   lg.userid {$selectallstudents}\n                                                AND (lg.action = 'add post' OR lg.action = 'talk')\n                                                AND cm.course = {$courseid}\n                                                AND cm.module = {$moduleid}\n                                                AND cm.instance = {$instanceid} ";
                                 // Count participating students and return the sql query to count their entries next
                                 $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, 'lg', 'time', $fromtime, $untiltime);
                                 if ($count > 0) {
                                     // If there is at least one student.
                                     $iterations++;
                                     $sql = "SELECT COUNT(lg.id) AS TOTALS\n                                                FROM {log} lg\n                                                    INNER JOIN {course_modules} cm ON (lg.cmid = cm.id)\n                                                WHERE   lg.userid IN ({$participatingstudents})\n                                                    AND (lg.action = 'add post' OR lg.action = 'talk')\n                                                    AND cm.course = {$courseid}\n                                                    AND cm.module = {$moduleid}\n                                                    AND cm.instance = {$instanceid} ";
                                     $this->get_value_from_learning_analytics($benchmarkstudents, $sql, 'lg', 'time', $fromtime, $untiltime, $count);
                                 }
                             } elseif ($useinternalreader) {
                                 // New log
                                 // There is no direct way to get both posts and talks from the new log
                                 // according to the module id (forum or chat) and instance id (forum id or chat id),
                                 // so we must seperate the original log query with two different queries that don't have to refer to the new log anymore.
                                 // Bye bye new log... Bummer :(
                                 // If this is a forum course module
                                 if ($moduleid == $this->forummoduleid) {
                                     $logtablename = 'fp';
                                     $timefield = 'created';
                                     $sql = "SELECT DISTINCT (fp.userid) AS userids\n                                                FROM {forum_posts} fp\n                                                    INNER JOIN {forum_discussions} fdcs ON (fp.discussion = fdcs.id)\n                                                WHERE   fp.userid {$selectallstudents}\n                                                    AND fdcs.course = {$courseid}\n                                                    AND fdcs.forum = {$instanceid} ";
                                     // Count participating students and return the sql query to count their entries next
                                     $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, $logtablename, $timefield, $fromtime, $untiltime);
                                     if ($count > 0) {
                                         // If there is at least one student.
                                         $iterations++;
                                         $sql = "SELECT COUNT(fp.id) AS TOTALS\n                                                    FROM {forum_posts} fp\n                                                        INNER JOIN {forum_discussions} fdcs ON (fp.discussion = fdcs.id)\n                                                    WHERE   fp.userid IN ({$participatingstudents})\n                                                        AND fdcs.course = {$courseid}\n                                                        AND fdcs.forum = {$instanceid} ";
                                         $this->get_value_from_learning_analytics($benchmarkstudents, $sql, $logtablename, $timefield, $fromtime, $untiltime, $count);
                                     }
                                     // Else only if this is a chat course module
                                 } else {
                                     if ($moduleid == $this->chatmoduleid) {
                                         $logtablename = 'chms';
                                         $timefield = 'timestamp';
                                         $sql = "SELECT DISTINCT (chms.userid) AS userids\n                                                FROM {chat_messages} chms\n                                                WHERE   chms.userid {$selectallstudents}\n                                                    AND chms.system = 0\n                                                    AND chms.chatid = {$instanceid} ";
                                         // Count participating students and return the sql query to count their entries next
                                         $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, $logtablename, $timefield, $fromtime, $untiltime);
                                         if ($count > 0) {
                                             // If there is at least one student.
                                             $iterations++;
                                             $sql = "SELECT COUNT(chms.id) AS TOTALS\n                                                    FROM {chat_messages} chms\n                                                    WHERE   chms.userid IN ({$participatingstudents})\n                                                        AND chms.system = 0\n                                                        AND chms.chatid = {$instanceid} ";
                                             $this->get_value_from_learning_analytics($benchmarkstudents, $sql, $logtablename, $timefield, $fromtime, $untiltime, $count);
                                         }
                                     }
                                 }
                             }
                         }
                         break;
                         // In case of checking addition occurences of files in forums,
                         // just count the attachement occurences in forum posts.
                     // In case of checking addition occurences of files in forums,
                     // just count the attachement occurences in forum posts.
                     case gradingform_erubric_controller::COLLABORATION_TYPE_FILE_ADDS:
                         $sql = "SELECT COUNT(fls.id) AS TOTALS\n                                    FROM {forum_posts} fp\n                                        INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n                                        INNER JOIN {files} fls ON (fls.itemid = fp.id)\n                                    WHERE   fp.userid {$selectindividual}\n                                        AND fls.filesize > 0\n                                        AND fls.component = 'mod_forum'\n                                        AND fd.forum = {$instanceid} ";
                         $this->get_value_from_learning_analytics($benchmarkstudent, $sql, 'fp', 'created', $fromtime, $untiltime, 1);
                         // If the criterion has a global reference according to all students file submissions,
                         // check all students participated in the forum, even if they haven't submited anything.
                         // Leave out all those who didn't participated in the forum at all.
                         if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) {
                             $count = 0;
                             $sql = "SELECT DISTINCT (fp.userid) AS userids\n                                        FROM {forum_posts} fp\n                                            INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n                                        WHERE   fp.userid {$selectallstudents}\n                                            AND fd.forum = {$instanceid} ";
                             $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, 'fp', 'created', $fromtime, $untiltime);
                             if ($count > 0) {
                                 // If there is at least one student.
                                 $iterations++;
                                 $sql = "SELECT COUNT(fls.id) AS TOTALS\n                                            FROM {forum_posts} fp\n                                                INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n                                                INNER JOIN {files} fls ON (fls.itemid = fp.id)\n                                            WHERE   fp.userid IN ({$participatingstudents})\n                                                AND fls.filesize > 0\n                                                AND fls.component = 'mod_forum'\n                                                AND fd.forum = {$instanceid} ";
                                 $this->get_value_from_learning_analytics($benchmarkstudents, $sql, 'fp', 'created', $fromtime, $untiltime, $count);
                             }
                         }
                         break;
                         // In case of checking student replies in forums,
                         // just count all student posts except self-replies and the ones referring to root post a.k.a. discussion.
                     // In case of checking student replies in forums,
                     // just count all student posts except self-replies and the ones referring to root post a.k.a. discussion.
                     case gradingform_erubric_controller::COLLABORATION_TYPE_REPLIES:
                         $sql = "SELECT COUNT(fp.id) AS TOTALS\n                                    FROM {forum_posts} fp\n                                        INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n                                    WHERE fp.userid {$selectindividual}\n                                        AND fp.parent <> 0\n                                        AND fp.parent NOT IN (SELECT fp2.id AS tempids\n                                                              FROM {forum_posts} fp2\n                                                                  INNER JOIN {forum_discussions} fd2 ON (fd2.id = fp2.discussion)\n                                                              WHERE fp2.userid {$selectindividual}\n                                                                  AND fp2.parent <> 0\n                                                                  AND fd2.forum = {$instanceid})\n                                        AND fd.forum = {$instanceid} ";
                         $this->get_value_from_learning_analytics($benchmarkstudent, $sql, 'fp', 'created', $fromtime, $untiltime, 1);
                         // If the criterion has a global reference according to all students replies.
                         if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) {
                             $count = 0;
                             $sql = "SELECT DISTINCT (fp.userid) AS userids\n                                        FROM {forum_posts} fp\n                                            INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n                                        WHERE fp.userid {$selectallstudents}\n                                            AND fp.parent <> 0\n                                            AND fp.parent NOT IN (SELECT fp2.id AS tempids\n                                                                  FROM {forum_posts} fp2\n                                                                      INNER JOIN {forum_discussions} fd2 ON (fd2.id = fp2.discussion)\n                                                                  WHERE fp2.userid = fp.userid\n                                                                      AND fp2.parent <> 0\n                                                                      AND fd2.forum = {$instanceid})\n                                            AND fd.forum = {$instanceid} ";
                             $participatingstudents = $this->timestamp_and_count_active_studends_involved($count, $sql, 'fp', 'created', $fromtime, $untiltime);
                             if ($count > 0) {
                                 // If there is at least one student.
                                 $iterations++;
                                 $sql = "SELECT COUNT(fp.id) AS TOTALS\n                                            FROM {forum_posts} fp\n                                                INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n                                            WHERE   fp.userid IN ({$participatingstudents})\n                                                AND fp.parent <> 0\n                                                AND fp.parent NOT IN (SELECT fp2.id AS tempids\n                                                                  FROM {forum_posts} fp2\n                                                                      INNER JOIN {forum_discussions} fd2 ON (fd2.id = fp2.discussion)\n                                                                  WHERE fp2.userid = fp.userid\n                                                                      AND fp2.parent <> 0\n                                                                      AND fd2.forum = {$instanceid})\n                                                AND fd.forum = {$instanceid} ";
                                 $this->get_value_from_learning_analytics($benchmarkstudents, $sql, 'fp', 'created', $fromtime, $untiltime, $count);
                             }
                         }
                         break;
                         // In case of checking the number of distinct students, the evaluated student has interacted, in forums or chats...
                         // 1. Retrieve all student ids (including the evaluated) for each course module.
                         // 2. Check if current evaluated student's id exists in each module of interacted student's ids.
                         // 3. Update the pile of current evaluated student's, interacted user's ids.
                         // 4. Count the pile's size to retieve the number of all students' ids,
                         //    thus get the nubmer of all students the current one interacted.
                         // In case of checking all students do the above for every student interacted in each course module...
                         //    (!!!Run time possible delay or script timeout for checking many users!!!)
                     // In case of checking the number of distinct students, the evaluated student has interacted, in forums or chats...
                     // 1. Retrieve all student ids (including the evaluated) for each course module.
                     // 2. Check if current evaluated student's id exists in each module of interacted student's ids.
                     // 3. Update the pile of current evaluated student's, interacted user's ids.
                     // 4. Count the pile's size to retieve the number of all students' ids,
                     //    thus get the nubmer of all students the current one interacted.
                     // In case of checking all students do the above for every student interacted in each course module...
                     //    (!!!Run time possible delay or script timeout for checking many users!!!)
                     case gradingform_erubric_controller::COLLABORATION_TYPE_INTERACTIONS:
                         if ($moduleid == $this->forummoduleid) {
                             // Check forum modules.
                             // Get each forum discussion and check interactions.
                             $tempsql = "SELECT fd.id AS discussionid FROM {forum_discussions} fd WHERE fd.forum = {$instanceid}";
                             $discussions = $DB->get_records_sql($tempsql, null);
                             foreach ($discussions as $dscn) {
                                 $discussionid = $dscn->discussionid;
                                 $sql = "SELECT DISTINCT(fp.userid) AS usersid\n                                            FROM {forum_posts} fp\n                                                INNER JOIN {forum_discussions} fd ON (fd.id = fp.discussion)\n                                            WHERE fd.forum = {$instanceid}\n                                                AND fd.id = {$discussionid}\n                                                AND fp.userid {$selectallstudents} ";
                                 // This line added to ensure that only students are accounted.
                                 $tempusersarray = null;
                                 $this->get_value_from_learning_analytics($tempusersarray, $sql, 'fp', 'created', $fromtime, $untiltime, null);
                                 if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENT) {
                                     // Check only current student evaluated.
                                     if (!is_null($tempusersarray)) {
                                         $studentin = false;
                                         // Check all participating students to see if current student's id was in.
                                         foreach ($tempusersarray as $tempid) {
                                             if (isset($tempid->usersid) && $tempid->usersid == $studentid) {
                                                 $studentin = true;
                                                 break;
                                             }
                                         }
                                         if ($studentin) {
                                             // If student took part in that discussion.
                                             foreach ($tempusersarray as $tempid) {
                                                 // Check all participating students.
                                                 // If temp user id not the same with the student's and is unique, add to pile.
                                                 if ($tempid->usersid != $studentid && (!isset($distinctUsersFound[$studentid]) || !in_array($tempid->usersid, $distinctUsersFound[$studentid]))) {
                                                     $distinctUsersFound[$studentid][] = $tempid->usersid;
                                                 }
                                             }
                                         }
                                     }
                                     // Check all students along with the current student evaluated, if there are any.
                                 } else {
                                     if (!is_null($tempusersarray)) {
                                         // Check all participating students in order to create and update
                                         // each one's unique pile of interactions.
                                         foreach ($tempusersarray as $tempcurrentid) {
                                             // loop A
                                             // Check all participating students again, to update the pile of the user selected by previous loop.
                                             foreach ($tempusersarray as $tempid) {
                                                 // loop B
                                                 // If temp user's id (loop B) is unique and not the same with user's id (loop A),
                                                 // add to pile (of user A).
                                                 if (isset($tempid->usersid) && isset($tempcurrentid->usersid) && $tempid->usersid != $tempcurrentid->usersid && (!isset($distinctUsersFound[$tempcurrentid->usersid]) || !in_array($tempid->usersid, $distinctUsersFound[$tempcurrentid->usersid]))) {
                                                     $distinctUsersFound[$tempcurrentid->usersid][] = $tempid->usersid;
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                         } else {
                             // Check chat modules.
                             $tempusersarray = $this->get_interacted_users_according_to_chat_sessions($instanceid, $fromtime, $untiltime);
                             if (empty($tempusersarray)) {
                                 break;
                             }
                             if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENT) {
                                 // Check only current student evaluated.
                                 // Go through all chat sessions to distinct interacted students.
                                 foreach ($tempusersarray as $tempsession) {
                                     foreach ($tempsession as $tempuserid) {
                                         // If temp user id not the same with the student's and is unique, add to pile.
                                         if ($tempuserid != $studentid && (!isset($distinctUsersFound[$studentid]) || !in_array($tempuserid, $distinctUsersFound[$studentid]))) {
                                             $distinctUsersFound[$studentid][] = $tempuserid;
                                         }
                                     }
                                 }
                             } else {
                                 // Check all participating students in order to create and update
                                 // each one's unique pile of interactions.
                                 foreach ($tempusersarray as $tempsession) {
                                     // Iterate through all chat sessions.
                                     foreach ($tempsession as $tempuserid1) {
                                         // Get users list.
                                         foreach ($tempusersarray as $tempuserid2) {
                                             // Get users list again.
                                             // If temp2 user id not the same with temp1's and is unique, add to temp1's pile.
                                             if ($tempuserid1 != $tempuserid2 && (!isset($distinctUsersFound[$tempuserid1]) || !in_array($tempuserid2, $distinctUsersFound[$tempuserid1]))) {
                                                 $distinctUsersFound[$tempuserid1][] = $tempuserid2;
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                         break;
                 }
             }
             break;
     }
     // When user interactions are checked, benchmarks are calculated after all course modules are processed and only if there are interactions.
     if ($criterion['collaborationtype'] == gradingform_erubric_controller::COLLABORATION_TYPE_INTERACTIONS && !empty($distinctUsersFound)) {
         if (array_key_exists($studentid, $distinctUsersFound)) {
             // Check current student evaluated.
             $benchmarkstudent = count($distinctUsersFound[$studentid]);
         } else {
             $benchmarkstudent = 0;
         }
         if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) {
             // Check all students participated.
             $benchmarkstudents = 0;
             $iterations = count($distinctUsersFound);
             foreach ($distinctUsersFound as $tempstudentid => $otherstudents) {
                 $benchmarkstudents += count($distinctUsersFound[$tempstudentid]);
             }
         }
     }
     // If there is a benchmark for current student evaluated, calculate the criterion benchmark. Else do nothing, leave everything null.
     if (!is_null($benchmarkstudent)) {
         if ($criterion['collaborationtype'] != gradingform_erubric_controller::COLLABORATION_TYPE_INTERACTIONS && ($criterion['criteriontype'] == gradingform_erubric_controller::INTERACTION_TYPE_GRADE || $criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS)) {
             $benchmarkstudent = (double) round($benchmarkstudent / $iterations, 2);
         }
         if ($criterion['referencetype'] == gradingform_erubric_controller::REFERENCE_STUDENTS) {
             // Check students benchmark.
             if ($benchmarkstudents) {
                 // To avoid division by zero for small students' benchmark, make two point precision rounding.
                 $benchmarkstudents = (double) round($benchmarkstudents / $iterations, 2);
                 $benchmarkcriterion = (int) round($benchmarkstudent * 100 / $benchmarkstudents);
             } else {
                 $benchmarkcriterion = null;
             }
         } else {
             $benchmarkcriterion = (int) $benchmarkstudent;
             // Just convert student benchmark to integer.
         }
     }
     // Run enrichment procedure to return the appropriate selected level (if exists),
     // and store criterion benchmarks for future reference.
     $criterion['checkedenrich'] = $this->get_enriched_level_from_benchmark($criterion['levels'], $benchmarkcriterion, $criterion['operator'], $options['sortlevelsasc']);
     $criterion['enrichedbenchmark'] = $benchmarkcriterion;
     $criterion['enrichedbenchmarkstudent'] = $benchmarkstudent;
     $criterion['enrichedbenchmarkstudents'] = $benchmarkstudents;
 }
 public function test_list_participants_blind_marking()
 {
     global $DB;
     $this->resetAfterTest(true);
     $course = $this->getDataGenerator()->create_course();
     $roles = $DB->get_records('role', null, '', 'shortname, id');
     $teacher = $this->getDataGenerator()->create_user();
     $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $roles['teacher']->id);
     $this->setUser($teacher);
     // Enrol two students.
     $students = [];
     for ($i = 0; $i < 2; $i++) {
         $student = $this->getDataGenerator()->create_user();
         $this->getDataGenerator()->enrol_user($student->id, $course->id, $roles['student']->id);
         $students[$student->id] = $student;
     }
     $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     $instance = $generator->create_instance(['course' => $course->id, 'blindmarking' => 1]);
     $cm = get_coursemodule_from_instance('assign', $instance->id);
     $context = context_module::instance($cm->id);
     $assign = new assign($context, $cm, $course);
     // Allocate IDs now.
     // We're testing whether the IDs are correct after allocation.
     assign::allocate_unique_ids($assign->get_instance()->id);
     $participants = $assign->list_participants(null, false);
     // There should be exactly two participants and they should be the students.
     $this->assertCount(2, $participants);
     foreach ($participants as $participant) {
         $this->assertArrayHasKey($participant->id, $students);
     }
     $keys = array_keys($participants);
     // Create a grading table, and query the DB This should have the same order.
     $table = new assign_grading_table($assign, 10, '', 0, false);
     $table->setup();
     $table->query_db(10);
     $this->assertEquals($keys, array_keys($table->rawdata));
     // Submit a file for the second student.
     $data = new stdClass();
     $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(), 'text' => 'Submission text', 'format' => FORMAT_MOODLE);
     static::helper_add_submission($assign, $participants[$keys[1]], $data, 'onlinetext');
     // Assign has a private cache. The easiest way to clear this is to create a new instance.
     $assign = new assign($context, $cm, $course);
     $newparticipants = $assign->list_participants(null, false);
     // There should be exactly two participants and they should be the students.
     $this->assertCount(2, $newparticipants);
     foreach ($newparticipants as $participant) {
         $this->assertArrayHasKey($participant->id, $students);
     }
     $newkeys = array_keys($newparticipants);
     // The user who submitted first should now be listed first.
     $this->assertEquals($participants[$keys[1]]->id, $newparticipants[$newkeys[0]]->id);
     $this->assertEquals($participants[$keys[0]]->id, $newparticipants[$newkeys[1]]->id);
     // Submit for the other student.
     static::helper_add_submission($assign, $participants[$keys[0]], $data, 'onlinetext');
     $assign = new assign($context, $cm, $course);
     $newparticipants = $assign->list_participants(null, false);
     // The users should still be listed in order of the first submission
     $this->assertEquals($participants[$keys[1]]->id, $newparticipants[$newkeys[0]]->id);
     $this->assertEquals($participants[$keys[0]]->id, $newparticipants[$newkeys[1]]->id);
     // The updated grading table should have the same order as the updated participant list.
     $table->query_db(10);
     $this->assertEquals($newkeys, array_keys($table->rawdata));
 }