--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Aggregates the grades for submission and grades for assessments and calculates the total grade for workshop
+ *
+ * @package mod-workshop
+ * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(dirname(dirname(__FILE__))).'/config.php');
+require_once(dirname(__FILE__).'/locallib.php');
+
+$cmid = required_param('cmid', PARAM_INT); // course module
+$confirm = optional_param('confirm', false, PARAM_BOOL); // confirmation
+
+$cm = get_coursemodule_from_id('workshop', $cmid, 0, false, MUST_EXIST);
+$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+$workshop = $DB->get_record('workshop', array('id' => $cm->instance), '*', MUST_EXIST);
+$workshop = new workshop($workshop, $cm, $course);
+
+$PAGE->set_url(new moodle_url($workshop->aggregate_url(), array('cmid' => $cmid)));
+
+require_login($course, false, $cm);
+require_capability('mod/workshop:overridegrades', $PAGE->context);
+
+if ($confirm) {
+ if (!confirm_sesskey()) {
+ throw new moodle_exception('confirmsesskeybad');
+ }
+ $workshop->update_submission_grades();
+ $workshop->update_grading_grades();
+ redirect($workshop->view_url());
+}
+
+$PAGE->set_title($workshop->name);
+$PAGE->set_heading($course->fullname);
+$PAGE->navbar->add(get_string('aggregation', 'workshop'));
+
+//
+// Output starts here
+//
+echo $OUTPUT->header();
+echo $OUTPUT->confirm(get_string('aggregationinfo', 'workshop'),
+ new moodle_url($PAGE->url, array('confirm' => 1)), $workshop->view_url());
+echo $OUTPUT->footer();
<KEY NAME="formfield_uk" TYPE="unique" FIELDS="assessmentid, strategy, dimensionid" COMMENT="The combination of assessmentid, strategy and dimensionid must be unique" PREVIOUS="assessment_fk"/>
</KEYS>
</TABLE>
- <TABLE NAME="workshop_aggregations" COMMENT="Aggregated grade for submission, grade assessments and total grade calculated for workshop participants are stored here" PREVIOUS="workshop_grades">
+ <TABLE NAME="workshop_aggregations" COMMENT="Aggregated grade for assessment and total grade calculated for workshop participants are stored here. The aggregated grade for submission is stored in workshop_submissions" PREVIOUS="workshop_grades">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="workshopid"/>
<FIELD NAME="workshopid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="the id of the workshop instance" PREVIOUS="id" NEXT="userid"/>
- <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="The id of the user which aggregated grades are calculated for" PREVIOUS="workshopid" NEXT="submissiongrade"/>
- <FIELD NAME="submissiongrade" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" DECIMALS="5" COMMENT="The grade for submission" PREVIOUS="userid" NEXT="gradinggrade"/>
- <FIELD NAME="gradinggrade" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" DECIMALS="5" COMMENT="The aggregated grade for all assessments made by this reviewer. The grade is a number from interval 0..100. If NULL then the grade for assessments has not been aggregated yet." PREVIOUS="submissiongrade" NEXT="totalgrade"/>
- <FIELD NAME="totalgrade" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" DECIMALS="5" COMMENT="The total grade for this workshop to be pushed into the gradebook" PREVIOUS="gradinggrade" NEXT="timeaggregated"/>
- <FIELD NAME="timeaggregated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="The timestamp of when the participant's grades were recently aggregated. If there are some modifications of the assessment after this time, the aggregation shall be marked as possibly out-dated" PREVIOUS="totalgrade"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="The id of the user which aggregated grades are calculated for" PREVIOUS="workshopid" NEXT="gradinggrade"/>
+ <FIELD NAME="gradinggrade" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" DECIMALS="5" COMMENT="The aggregated grade for all assessments made by this reviewer. The grade is a number from interval 0..100. If NULL then the grade for assessments has not been aggregated yet." PREVIOUS="userid" NEXT="totalgrade"/>
+ <FIELD NAME="totalgrade" TYPE="number" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" DECIMALS="5" COMMENT="The total grade for this workshop to be pushed into the gradebook" PREVIOUS="gradinggrade" NEXT="timetotalgraded"/>
+ <FIELD NAME="timetotalgraded" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="The timestamp of when the participant's totalgrade was recently aggregated. If there are some modifications of any assessment of his/her submission after this time, the aggregation shall be marked as possibly out-dated" PREVIOUS="totalgrade"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="workshop_fk"/>
$string[''] = '';
$string[''] = '';
$string[''] = '';
+$string[''] = '';
+$string[''] = '';
+$string['aggregationinfo'] = 'During the aggregation process, the grades for submission, grades for assessment and total grades are re-calculated and stored into the Workshop database. This does not modify any manual overrides nor does not push the total grade into the gradebook.';
+$string['aggregation'] = 'Grades aggregation';
+$string['aggregategrades'] = 'Re-calculate grades';
$string['nullgrade'] = '?';
$string['formatpeergradeover'] = '$a->grade (<del>$a->gradinggrade</del> / <ins>$a->gradinggradeover</ins>)';
$string['formatpeergrade'] = '$a->grade ($a->gradinggrade)';
defined('MOODLE_INTERNAL') || die();
-require_once(dirname(__FILE__).'/lib.php'); // we extend this library here
-require_once($CFG->libdir . '/gradelib.php');
+require_once(dirname(__FILE__).'/lib.php'); // we extend this library here
+require_once($CFG->libdir . '/gradelib.php'); // we use some rounding and comparing routines here
/**
* Full-featured workshop API
return new moodle_url($CFG->wwwroot . '/mod/workshop/switchphase.php', array('cmid' => $this->cm->id, 'phase' => $phasecode));
}
+ /**
+ * @return moodle_url to the aggregation page
+ */
+ public function aggregate_url() {
+ global $CFG;
+ return new moodle_url($CFG->wwwroot . '/mod/workshop/aggregate.php', array('cmid' => $this->cm->id));
+ }
+
/**
* Returns an object containing all data to display the user's full name and picture
*
$params['workshopid'] = $this->id;
$sqlsort = $sortby . ' ' . $sorthow . ',u.lastname,u.firstname,u.id';
$sql = "SELECT u.id AS userid,u.firstname,u.lastname,u.picture,u.imagealt,
- s.title AS submissiontitle, a.submissiongrade, a.gradinggrade, a.totalgrade
+ s.title AS submissiontitle, s.grade AS submissiongrade, ag.gradinggrade, ag.totalgrade
FROM {user} u
LEFT JOIN {workshop_submissions} s ON (s.authorid = u.id)
- LEFT JOIN {workshop_aggregations} a ON (a.userid = u.id)
+ LEFT JOIN {workshop_aggregations} ag ON (ag.userid = u.id AND ag.workshopid = s.workshopid)
WHERE s.workshopid = :workshopid AND s.example = 0 AND u.id $participantids
ORDER BY $sqlsort";
$participants = $DB->get_records_sql($sql, $params, $page * $perpage, $perpage);
return grade_floatval((float)$grade + (float)$gradinggrade);
}
+ /**
+ * Calculates grades for submission for the given participant(s)
+ *
+ * Grade for submission is calculated as a weighted mean of all given grades
+ *
+ * @param null|int|array $restrict If null, update all authors, otherwise update just grades for the given author(s)
+ * @return void
+ */
+ public function update_submission_grades($restrict=null) {
+ global $DB;
+
+ // fetch a recordset with all assessments to process
+ $sql = 'SELECT s.id AS submissionid, s.authorid, s.grade AS submissiongrade, s.gradeover, s.gradeoverby,
+ a.weight, a.grade
+ FROM {workshop_submissions} s
+ LEFT JOIN {workshop_assessments} a ON (a.submissionid = s.id)
+ WHERE s.example=0 AND s.workshopid=:workshopid'; // to be cont.
+ $params = array('workshopid' => $this->id);
+
+ if (is_null($restrict)) {
+ // update all users - no more conditions
+ } elseif (!empty($restrict)) {
+ list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
+ $sql .= " AND s.authorid $usql";
+ $params = array_merge($params, $uparams);
+ } else {
+ throw new coding_exception('Empty value is not a valid parameter here');
+ }
+
+ $sql .= ' ORDER BY s.id'; // this is important for bulk processing
+ $rs = $DB->get_recordset_sql($sql, $params);
+
+ $previous = null;
+ foreach ($rs as $current) {
+ if (is_null($previous)) {
+ // we are processing the very first record in the recordset
+ $previous = $current;
+ $sumgrades = 0;
+ $sumweights = 0;
+ }
+ if (is_null($current->grade)) {
+ // this was not assessed yet
+ continue;
+ }
+ if ($current->weight == 0) {
+ // this does not influence the calculation
+ continue;
+ }
+ if ($current->submissionid != $previous->submissionid) {
+ // firstly finish the calculation for the previous submission as we now have all its data
+ if ($sumweights > 0) {
+ // there is a chance that the aggregated grade has changed
+ $finalgrade = $sumgrades / $sumweights;
+ if (grade_floats_different($finalgrade, $previous->submissiongrade)) {
+ // we need to save new calculation into the database
+ $DB->set_field('workshop_submissions', 'grade', $finalgrade, array('id' => $previous->submissionid));
+ }
+ }
+ // and then start to process another submission
+ $previous = $current;
+ if (is_null($current->grade)) {
+ $sumgrades = 0;
+ } else {
+ $sumgrades = $current->grade;
+ }
+ $sumweights = $current->weight;
+ continue;
+ } else {
+ // we are still processing the current submission
+ $sumgrades += $current->grade * $current->weight;
+ $sumweights += $current->weight;
+ continue;
+ }
+ }
+ // finally we must calculate the last submission's grade as it was not done in the previous loop
+ if ($sumweights > 0) {
+ // there is a chance that the aggregated grade has changed
+ $finalgrade = $sumgrades / $sumweights;
+ if (grade_floats_different($finalgrade, $current->submissiongrade)) {
+ // we need to save new calculation into the database
+ $DB->set_field('workshop_submissions', 'grade', $finalgrade, array('id' => $current->submissionid));
+ }
+ }
+ $rs->close();
+ }
+
+ /**
+ * Calculates grades for assessment for the given participant(s)
+ *
+ * Grade for submission is calculated as a weighted mean of all given grades
+ *
+ * @param null|int|array $restrict If null, update all reviewers, otherwise update just grades for the given reviewer(s)
+ * @return void
+ */
+ public function update_grading_grades($restrict=null) {
+ global $DB;
+
+ // todo
+ }
+
////////////////////////////////////////////////////////////////////////////////
// Internal methods (implementation details) //
////////////////////////////////////////////////////////////////////////////////
defined('MOODLE_INTERNAL') || die();
-$module->version = 2009092900;
+$module->version = 2009092901;
$module->requires = 2009090400; // Requires this Moodle version
$module->cron = 60;
$page = optional_param($pagingvar, 0, PARAM_INT);
$perpage = 10; // todo let the user modify this
$groups = ''; // todo let the user choose the group
- $sortby = 'totalgrade'; // todo let the user choose the column to sort by
+ $sortby = 'submissiongrade'; // todo let the user choose the column to sort by
$sorthow = 'DESC'; // todo detto
$data = $workshop->prepare_grading_report($USER->id, $groups, $page, $perpage, $sortby, $sorthow);