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