Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/mod/workshop/locallib.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00032 defined('MOODLE_INTERNAL') || die();
00033 
00034 require_once(dirname(__FILE__).'/lib.php');     // we extend this library here
00035 require_once($CFG->libdir . '/gradelib.php');   // we use some rounding and comparing routines here
00036 require_once($CFG->libdir . '/filelib.php');
00037 
00045 class workshop {
00046 
00048     const ALLOCATION_EXISTS             = -9999;
00049     const ALLOCATION_ERROR              = -9998;
00050 
00052     const PHASE_SETUP                   = 10;
00053     const PHASE_SUBMISSION              = 20;
00054     const PHASE_ASSESSMENT              = 30;
00055     const PHASE_EVALUATION              = 40;
00056     const PHASE_CLOSED                  = 50;
00057 
00059     const EXAMPLES_VOLUNTARY            = 0;
00060     const EXAMPLES_BEFORE_SUBMISSION    = 1;
00061     const EXAMPLES_BEFORE_ASSESSMENT    = 2;
00062 
00064     public $cm;
00065 
00067     public $course;
00068 
00070     public $context;
00071 
00073     public $id;
00074 
00076     public $name;
00077 
00079     public $intro;
00080 
00082     public $introformat;
00083 
00085     public $instructauthors;
00086 
00088     public $instructauthorsformat;
00089 
00091     public $instructreviewers;
00092 
00094     public $instructreviewersformat;
00095 
00097     public $timemodified;
00098 
00100     public $phase;
00101 
00103     public $useexamples;
00104 
00106     public $usepeerassessment;
00107 
00109     public $useselfassessment;
00110 
00112     public $grade;
00113 
00115     public $gradinggrade;
00116 
00118     public $strategy;
00119 
00121     public $evaluation;
00122 
00124     public $gradedecimals;
00125 
00127     public $nattachments;
00128 
00130     public $latesubmissions;
00131 
00133     public $maxbytes;
00134 
00136     public $examplesmode;
00137 
00139     public $submissionstart;
00140 
00142     public $submissionend;
00143 
00145     public $assessmentstart;
00146 
00148     public $assessmentend;
00149 
00154     protected $strategyinstance = null;
00155 
00160     protected $evaluationinstance = null;
00161 
00173     public function __construct(stdclass $dbrecord, stdclass $cm, stdclass $course, stdclass $context=null) {
00174         foreach ($dbrecord as $field => $value) {
00175             if (property_exists('workshop', $field)) {
00176                 $this->{$field} = $value;
00177             }
00178         }
00179         $this->cm           = $cm;
00180         $this->course       = $course;
00181         if (is_null($context)) {
00182             $this->context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
00183         } else {
00184             $this->context = $context;
00185         }
00186         $this->evaluation   = 'best';   // todo make this configurable although we have no alternatives yet
00187     }
00188 
00190     // Static methods                                                             //
00192 
00198     public static function installed_allocators() {
00199         $installed = get_plugin_list('workshopallocation');
00200         $forms = array();
00201         foreach ($installed as $allocation => $allocationpath) {
00202             if (file_exists($allocationpath . '/lib.php')) {
00203                 $forms[$allocation] = get_string('pluginname', 'workshopallocation_' . $allocation);
00204             }
00205         }
00206         // usability - make sure that manual allocation appears the first
00207         if (isset($forms['manual'])) {
00208             $m = array('manual' => $forms['manual']);
00209             unset($forms['manual']);
00210             $forms = array_merge($m, $forms);
00211         }
00212         return $forms;
00213     }
00214 
00222     public static function instruction_editors_options(stdclass $context) {
00223         return array('subdirs' => 1, 'maxbytes' => 0, 'maxfiles' => -1,
00224                      'changeformat' => 1, 'context' => $context, 'noclean' => 1, 'trusttext' => 0);
00225     }
00226 
00234     public static function percent_to_value($percent, $total) {
00235         if ($percent < 0 or $percent > 100) {
00236             throw new coding_exception('The percent can not be less than 0 or higher than 100');
00237         }
00238 
00239         return $total * $percent / 100;
00240     }
00241 
00247     public static function available_maxgrades_list() {
00248         $grades = array();
00249         for ($i=100; $i>=0; $i--) {
00250             $grades[$i] = $i;
00251         }
00252         return $grades;
00253     }
00254 
00260     public static function available_example_modes_list() {
00261         $options = array();
00262         $options[self::EXAMPLES_VOLUNTARY]         = get_string('examplesvoluntary', 'workshop');
00263         $options[self::EXAMPLES_BEFORE_SUBMISSION] = get_string('examplesbeforesubmission', 'workshop');
00264         $options[self::EXAMPLES_BEFORE_ASSESSMENT] = get_string('examplesbeforeassessment', 'workshop');
00265         return $options;
00266     }
00267 
00273     public static function available_strategies_list() {
00274         $installed = get_plugin_list('workshopform');
00275         $forms = array();
00276         foreach ($installed as $strategy => $strategypath) {
00277             if (file_exists($strategypath . '/lib.php')) {
00278                 $forms[$strategy] = get_string('pluginname', 'workshopform_' . $strategy);
00279             }
00280         }
00281         return $forms;
00282     }
00283 
00289     public static function available_dimension_weights_list() {
00290         $weights = array();
00291         for ($i=16; $i>=0; $i--) {
00292             $weights[$i] = $i;
00293         }
00294         return $weights;
00295     }
00296 
00308     public static function available_assessment_weights_list() {
00309         $weights = array();
00310         for ($i=16; $i>=0; $i--) {
00311             $weights[$i] = $i;
00312         }
00313         return $weights;
00314     }
00315 
00323     public static function gcd($a, $b) {
00324         return ($b == 0) ? ($a):(self::gcd($b, $a % $b));
00325     }
00326 
00334     public static function lcm($a, $b) {
00335         return ($a / self::gcd($a,$b)) * $b;
00336     }
00337 
00349     public static function timestamp_formats($timestamp) {
00350         $formats = array('date', 'datefullshort', 'dateshort', 'datetime',
00351                 'datetimeshort', 'daydate', 'daydatetime', 'dayshort', 'daytime',
00352                 'monthyear', 'recent', 'recentfull', 'time');
00353         $a = new stdclass();
00354         foreach ($formats as $format) {
00355             $a->{$format} = userdate($timestamp, get_string('strftime'.$format, 'langconfig'));
00356         }
00357         $day = userdate($timestamp, '%Y%m%d', 99, false);
00358         $today = userdate(time(), '%Y%m%d', 99, false);
00359         $tomorrow = userdate(time() + DAYSECS, '%Y%m%d', 99, false);
00360         $yesterday = userdate(time() - DAYSECS, '%Y%m%d', 99, false);
00361         $distance = (int)round(abs(time() - $timestamp) / DAYSECS);
00362         if ($day == $today) {
00363             $a->distanceday = get_string('daystoday', 'workshop');
00364         } elseif ($day == $yesterday) {
00365             $a->distanceday = get_string('daysyesterday', 'workshop');
00366         } elseif ($day < $today) {
00367             $a->distanceday = get_string('daysago', 'workshop', $distance);
00368         } elseif ($day == $tomorrow) {
00369             $a->distanceday = get_string('daystomorrow', 'workshop');
00370         } elseif ($day > $today) {
00371             $a->distanceday = get_string('daysleft', 'workshop', $distance);
00372         }
00373         return $a;
00374     }
00375 
00377     // Workshop API                                                               //
00379 
00389     public function get_potential_authors($musthavesubmission=true) {
00390         $users = get_users_by_capability($this->context, 'mod/workshop:submit',
00391                     'u.id,u.lastname,u.firstname', 'u.lastname,u.firstname,u.id', '', '', '', '', false, false, true);
00392         if ($musthavesubmission) {
00393             $users = array_intersect_key($users, $this->users_with_submission(array_keys($users)));
00394         }
00395         return $users;
00396     }
00397 
00407     public function get_potential_reviewers($musthavesubmission=false) {
00408         $users = get_users_by_capability($this->context, 'mod/workshop:peerassess',
00409                     'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname,u.id', '', '', '', '', false, false, true);
00410         if ($musthavesubmission) {
00411             // users without their own submission can not be reviewers
00412             $users = array_intersect_key($users, $this->users_with_submission(array_keys($users)));
00413         }
00414         return $users;
00415     }
00416 
00427     public function get_grouped($users) {
00428         global $DB;
00429         global $CFG;
00430 
00431         $grouped = array();  // grouped users to be returned
00432         if (empty($users)) {
00433             return $grouped;
00434         }
00435         if (!empty($CFG->enablegroupmembersonly) and $this->cm->groupmembersonly) {
00436             // Available for group members only - the workshop is available only
00437             // to users assigned to groups within the selected grouping, or to
00438             // any group if no grouping is selected.
00439             $groupingid = $this->cm->groupingid;
00440             // All users that are members of at least one group will be
00441             // added into a virtual group id 0
00442             $grouped[0] = array();
00443         } else {
00444             $groupingid = 0;
00445             // there is no need to be member of a group so $grouped[0] will contain
00446             // all users
00447             $grouped[0] = $users;
00448         }
00449         $gmemberships = groups_get_all_groups($this->cm->course, array_keys($users), $groupingid,
00450                             'gm.id,gm.groupid,gm.userid');
00451         foreach ($gmemberships as $gmembership) {
00452             if (!isset($grouped[$gmembership->groupid])) {
00453                 $grouped[$gmembership->groupid] = array();
00454             }
00455             $grouped[$gmembership->groupid][$gmembership->userid] = $users[$gmembership->userid];
00456             $grouped[0][$gmembership->userid] = $users[$gmembership->userid];
00457         }
00458         return $grouped;
00459     }
00460 
00468     public function get_allocations() {
00469         global $DB;
00470 
00471         $sql = 'SELECT a.id, a.submissionid, a.reviewerid, s.authorid
00472                   FROM {workshop_assessments} a
00473             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
00474                  WHERE s.example = 0 AND s.workshopid = :workshopid';
00475         $params = array('workshopid' => $this->id);
00476 
00477         return $DB->get_records_sql($sql, $params);
00478     }
00479 
00489     public function get_submissions($authorid='all') {
00490         global $DB;
00491 
00492         $authorfields      = user_picture::fields('u', null, 'authoridx', 'author');
00493         $gradeoverbyfields = user_picture::fields('t', null, 'gradeoverbyx', 'over');
00494         $sql = "SELECT s.id, s.workshopid, s.example, s.authorid, s.timecreated, s.timemodified,
00495                        s.title, s.grade, s.gradeover, s.gradeoverby, s.published,
00496                        $authorfields, $gradeoverbyfields
00497                   FROM {workshop_submissions} s
00498             INNER JOIN {user} u ON (s.authorid = u.id)
00499              LEFT JOIN {user} t ON (s.gradeoverby = t.id)
00500                  WHERE s.example = 0 AND s.workshopid = :workshopid";
00501         $params = array('workshopid' => $this->id);
00502 
00503         if ('all' === $authorid) {
00504             // no additional conditions
00505         } elseif (!empty($authorid)) {
00506             list($usql, $uparams) = $DB->get_in_or_equal($authorid, SQL_PARAMS_NAMED);
00507             $sql .= " AND authorid $usql";
00508             $params = array_merge($params, $uparams);
00509         } else {
00510             // $authorid is empty
00511             return array();
00512         }
00513         $sql .= " ORDER BY u.lastname, u.firstname";
00514 
00515         return $DB->get_records_sql($sql, $params);
00516     }
00517 
00524     public function get_submission_by_id($id) {
00525         global $DB;
00526 
00527         // we intentionally check the workshopid here, too, so the workshop can't touch submissions
00528         // from other instances
00529         $authorfields      = user_picture::fields('u', null, 'authoridx', 'author');
00530         $gradeoverbyfields = user_picture::fields('g', null, 'gradeoverbyx', 'gradeoverby');
00531         $sql = "SELECT s.*, $authorfields, $gradeoverbyfields
00532                   FROM {workshop_submissions} s
00533             INNER JOIN {user} u ON (s.authorid = u.id)
00534              LEFT JOIN {user} g ON (s.gradeoverby = g.id)
00535                  WHERE s.example = 0 AND s.workshopid = :workshopid AND s.id = :id";
00536         $params = array('workshopid' => $this->id, 'id' => $id);
00537         return $DB->get_record_sql($sql, $params, MUST_EXIST);
00538     }
00539 
00546     public function get_submission_by_author($authorid) {
00547         global $DB;
00548 
00549         if (empty($authorid)) {
00550             return false;
00551         }
00552         $authorfields      = user_picture::fields('u', null, 'authoridx', 'author');
00553         $gradeoverbyfields = user_picture::fields('g', null, 'gradeoverbyx', 'gradeoverby');
00554         $sql = "SELECT s.*, $authorfields, $gradeoverbyfields
00555                   FROM {workshop_submissions} s
00556             INNER JOIN {user} u ON (s.authorid = u.id)
00557              LEFT JOIN {user} g ON (s.gradeoverby = g.id)
00558                  WHERE s.example = 0 AND s.workshopid = :workshopid AND s.authorid = :authorid";
00559         $params = array('workshopid' => $this->id, 'authorid' => $authorid);
00560         return $DB->get_record_sql($sql, $params);
00561     }
00562 
00568     public function get_published_submissions($orderby='finalgrade DESC') {
00569         global $DB;
00570 
00571         $authorfields = user_picture::fields('u', null, 'authoridx', 'author');
00572         $sql = "SELECT s.id, s.authorid, s.timecreated, s.timemodified,
00573                        s.title, s.grade, s.gradeover, COALESCE(s.gradeover,s.grade) AS finalgrade,
00574                        $authorfields
00575                   FROM {workshop_submissions} s
00576             INNER JOIN {user} u ON (s.authorid = u.id)
00577                  WHERE s.example = 0 AND s.workshopid = :workshopid AND s.published = 1
00578               ORDER BY $orderby";
00579         $params = array('workshopid' => $this->id);
00580         return $DB->get_records_sql($sql, $params);
00581     }
00582 
00589     public function get_example_by_id($id) {
00590         global $DB;
00591         return $DB->get_record('workshop_submissions',
00592                 array('id' => $id, 'workshopid' => $this->id, 'example' => 1), '*', MUST_EXIST);
00593     }
00594 
00601     public function get_examples_for_manager() {
00602         global $DB;
00603 
00604         $sql = 'SELECT s.id, s.title,
00605                        a.id AS assessmentid, a.grade, a.gradinggrade
00606                   FROM {workshop_submissions} s
00607              LEFT JOIN {workshop_assessments} a ON (a.submissionid = s.id AND a.weight = 1)
00608                  WHERE s.example = 1 AND s.workshopid = :workshopid
00609               ORDER BY s.title';
00610         return $DB->get_records_sql($sql, array('workshopid' => $this->id));
00611     }
00612 
00620     public function get_examples_for_reviewer($reviewerid) {
00621         global $DB;
00622 
00623         if (empty($reviewerid)) {
00624             return false;
00625         }
00626         $sql = 'SELECT s.id, s.title,
00627                        a.id AS assessmentid, a.grade, a.gradinggrade
00628                   FROM {workshop_submissions} s
00629              LEFT JOIN {workshop_assessments} a ON (a.submissionid = s.id AND a.reviewerid = :reviewerid AND a.weight = 0)
00630                  WHERE s.example = 1 AND s.workshopid = :workshopid
00631               ORDER BY s.title';
00632         return $DB->get_records_sql($sql, array('workshopid' => $this->id, 'reviewerid' => $reviewerid));
00633     }
00634 
00642     public function prepare_submission(stdClass $record, $showauthor = false) {
00643 
00644         $submission         = new workshop_submission($record, $showauthor);
00645         $submission->url    = $this->submission_url($record->id);
00646 
00647         return $submission;
00648     }
00649 
00657     public function prepare_submission_summary(stdClass $record, $showauthor = false) {
00658 
00659         $summary        = new workshop_submission_summary($record, $showauthor);
00660         $summary->url   = $this->submission_url($record->id);
00661 
00662         return $summary;
00663     }
00664 
00671     public function prepare_example_submission(stdClass $record) {
00672 
00673         $example = new workshop_example_submission($record);
00674 
00675         return $example;
00676     }
00677 
00686     public function prepare_example_summary(stdClass $example) {
00687 
00688         $summary = new workshop_example_submission_summary($example);
00689 
00690         if (is_null($example->grade)) {
00691             $summary->status = 'notgraded';
00692             $summary->assesslabel = get_string('assess', 'workshop');
00693         } else {
00694             $summary->status = 'graded';
00695             $summary->assesslabel = get_string('reassess', 'workshop');
00696         }
00697 
00698         $summary->gradeinfo           = new stdclass();
00699         $summary->gradeinfo->received = $this->real_grade($example->grade);
00700         $summary->gradeinfo->max      = $this->real_grade(100);
00701 
00702         $summary->url       = new moodle_url($this->exsubmission_url($example->id));
00703         $summary->editurl   = new moodle_url($this->exsubmission_url($example->id), array('edit' => 'on'));
00704         $summary->assessurl = new moodle_url($this->exsubmission_url($example->id), array('assess' => 'on', 'sesskey' => sesskey()));
00705 
00706         return $summary;
00707     }
00708 
00723     public function prepare_assessment(stdClass $record, $form, array $options = array()) {
00724 
00725         $assessment             = new workshop_assessment($record, $options);
00726         $assessment->url        = $this->assess_url($record->id);
00727         $assessment->maxgrade   = $this->real_grade(100);
00728 
00729         if (!empty($options['showform']) and !($form instanceof workshop_assessment_form)) {
00730             debugging('Not a valid instance of workshop_assessment_form supplied', DEBUG_DEVELOPER);
00731         }
00732 
00733         if (!empty($options['showform']) and ($form instanceof workshop_assessment_form)) {
00734             $assessment->form = $form;
00735         }
00736 
00737         if (empty($options['showweight'])) {
00738             $assessment->weight = null;
00739         }
00740 
00741         if (!is_null($record->grade)) {
00742             $assessment->realgrade = $this->real_grade($record->grade);
00743         }
00744 
00745         return $assessment;
00746     }
00747 
00761     public function prepare_example_assessment(stdClass $record, $form = null, array $options = array()) {
00762 
00763         $assessment             = new workshop_example_assessment($record, $options);
00764         $assessment->url        = $this->exassess_url($record->id);
00765         $assessment->maxgrade   = $this->real_grade(100);
00766 
00767         if (!empty($options['showform']) and !($form instanceof workshop_assessment_form)) {
00768             debugging('Not a valid instance of workshop_assessment_form supplied', DEBUG_DEVELOPER);
00769         }
00770 
00771         if (!empty($options['showform']) and ($form instanceof workshop_assessment_form)) {
00772             $assessment->form = $form;
00773         }
00774 
00775         if (!is_null($record->grade)) {
00776             $assessment->realgrade = $this->real_grade($record->grade);
00777         }
00778 
00779         $assessment->weight = null;
00780 
00781         return $assessment;
00782     }
00783 
00797     public function prepare_example_reference_assessment(stdClass $record, $form = null, array $options = array()) {
00798 
00799         $assessment             = new workshop_example_reference_assessment($record, $options);
00800         $assessment->maxgrade   = $this->real_grade(100);
00801 
00802         if (!empty($options['showform']) and !($form instanceof workshop_assessment_form)) {
00803             debugging('Not a valid instance of workshop_assessment_form supplied', DEBUG_DEVELOPER);
00804         }
00805 
00806         if (!empty($options['showform']) and ($form instanceof workshop_assessment_form)) {
00807             $assessment->form = $form;
00808         }
00809 
00810         if (!is_null($record->grade)) {
00811             $assessment->realgrade = $this->real_grade($record->grade);
00812         }
00813 
00814         $assessment->weight = null;
00815 
00816         return $assessment;
00817     }
00818 
00825     public function delete_submission(stdclass $submission) {
00826         global $DB;
00827         $assessments = $DB->get_records('workshop_assessments', array('submissionid' => $submission->id), '', 'id');
00828         $this->delete_assessment(array_keys($assessments));
00829         $DB->delete_records('workshop_submissions', array('id' => $submission->id));
00830     }
00831 
00841     public function get_all_assessments() {
00842         global $DB;
00843 
00844         $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
00845         $authorfields   = user_picture::fields('author', null, 'authorid', 'author');
00846         $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
00847         $sql = "SELECT a.id, a.submissionid, a.reviewerid, a.timecreated, a.timemodified,
00848                        a.grade, a.gradinggrade, a.gradinggradeover, a.gradinggradeoverby,
00849                        $reviewerfields, $authorfields, $overbyfields,
00850                        s.title
00851                   FROM {workshop_assessments} a
00852             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
00853             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
00854             INNER JOIN {user} author ON (s.authorid = author.id)
00855              LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
00856                  WHERE s.workshopid = :workshopid AND s.example = 0
00857               ORDER BY reviewer.lastname, reviewer.firstname";
00858         $params = array('workshopid' => $this->id);
00859 
00860         return $DB->get_records_sql($sql, $params);
00861     }
00862 
00869     public function get_assessment_by_id($id) {
00870         global $DB;
00871 
00872         $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
00873         $authorfields   = user_picture::fields('author', null, 'authorid', 'author');
00874         $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
00875         $sql = "SELECT a.*, s.title, $reviewerfields, $authorfields, $overbyfields
00876                   FROM {workshop_assessments} a
00877             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
00878             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
00879             INNER JOIN {user} author ON (s.authorid = author.id)
00880              LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
00881                  WHERE a.id = :id AND s.workshopid = :workshopid";
00882         $params = array('id' => $id, 'workshopid' => $this->id);
00883 
00884         return $DB->get_record_sql($sql, $params, MUST_EXIST);
00885     }
00886 
00894     public function get_assessment_of_submission_by_user($submissionid, $reviewerid) {
00895         global $DB;
00896 
00897         $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
00898         $authorfields   = user_picture::fields('author', null, 'authorid', 'author');
00899         $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
00900         $sql = "SELECT a.*, s.title, $reviewerfields, $authorfields, $overbyfields
00901                   FROM {workshop_assessments} a
00902             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
00903             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id AND s.example = 0)
00904             INNER JOIN {user} author ON (s.authorid = author.id)
00905              LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
00906                  WHERE s.id = :sid AND reviewer.id = :rid AND s.workshopid = :workshopid";
00907         $params = array('sid' => $submissionid, 'rid' => $reviewerid, 'workshopid' => $this->id);
00908 
00909         return $DB->get_record_sql($sql, $params, IGNORE_MISSING);
00910     }
00911 
00918     public function get_assessments_of_submission($submissionid) {
00919         global $DB;
00920 
00921         $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
00922         $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
00923         $sql = "SELECT a.*, s.title, $reviewerfields, $overbyfields
00924                   FROM {workshop_assessments} a
00925             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
00926             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
00927              LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
00928                  WHERE s.example = 0 AND s.id = :submissionid AND s.workshopid = :workshopid
00929               ORDER BY reviewer.lastname, reviewer.firstname, reviewer.id";
00930         $params = array('submissionid' => $submissionid, 'workshopid' => $this->id);
00931 
00932         return $DB->get_records_sql($sql, $params);
00933     }
00934 
00941     public function get_assessments_by_reviewer($reviewerid) {
00942         global $DB;
00943 
00944         $reviewerfields = user_picture::fields('reviewer', null, 'revieweridx', 'reviewer');
00945         $authorfields   = user_picture::fields('author', null, 'authorid', 'author');
00946         $overbyfields   = user_picture::fields('overby', null, 'gradinggradeoverbyx', 'overby');
00947         $sql = "SELECT a.*, $reviewerfields, $authorfields, $overbyfields,
00948                        s.id AS submissionid, s.title AS submissiontitle, s.timecreated AS submissioncreated,
00949                        s.timemodified AS submissionmodified
00950                   FROM {workshop_assessments} a
00951             INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
00952             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
00953             INNER JOIN {user} author ON (s.authorid = author.id)
00954              LEFT JOIN {user} overby ON (a.gradinggradeoverby = overby.id)
00955                  WHERE s.example = 0 AND reviewer.id = :reviewerid AND s.workshopid = :workshopid";
00956         $params = array('reviewerid' => $reviewerid, 'workshopid' => $this->id);
00957 
00958         return $DB->get_records_sql($sql, $params);
00959     }
00960 
00970     public function add_allocation(stdclass $submission, $reviewerid, $weight=1, $bulk=false) {
00971         global $DB;
00972 
00973         if ($DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'reviewerid' => $reviewerid))) {
00974             return self::ALLOCATION_EXISTS;
00975         }
00976 
00977         $weight = (int)$weight;
00978         if ($weight < 0) {
00979             $weight = 0;
00980         }
00981         if ($weight > 16) {
00982             $weight = 16;
00983         }
00984 
00985         $now = time();
00986         $assessment = new stdclass();
00987         $assessment->submissionid           = $submission->id;
00988         $assessment->reviewerid             = $reviewerid;
00989         $assessment->timecreated            = $now;         // do not set timemodified here
00990         $assessment->weight                 = $weight;
00991         $assessment->generalcommentformat   = editors_get_preferred_format();
00992         $assessment->feedbackreviewerformat = editors_get_preferred_format();
00993 
00994         return $DB->insert_record('workshop_assessments', $assessment, true, $bulk);
00995     }
00996 
01003     public function delete_assessment($id) {
01004         global $DB;
01005 
01006         // todo remove all given grades from workshop_grades;
01007 
01008         if (is_array($id)) {
01009             return $DB->delete_records_list('workshop_assessments', 'id', $id);
01010         } else {
01011             return $DB->delete_records('workshop_assessments', array('id' => $id));
01012         }
01013     }
01014 
01020     public function grading_strategy_instance() {
01021         global $CFG;    // because we require other libs here
01022 
01023         if (is_null($this->strategyinstance)) {
01024             $strategylib = dirname(__FILE__) . '/form/' . $this->strategy . '/lib.php';
01025             if (is_readable($strategylib)) {
01026                 require_once($strategylib);
01027             } else {
01028                 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
01029             }
01030             $classname = 'workshop_' . $this->strategy . '_strategy';
01031             $this->strategyinstance = new $classname($this);
01032             if (!in_array('workshop_strategy', class_implements($this->strategyinstance))) {
01033                 throw new coding_exception($classname . ' does not implement workshop_strategy interface');
01034             }
01035         }
01036         return $this->strategyinstance;
01037     }
01038 
01044     public function grading_evaluation_instance() {
01045         global $CFG;    // because we require other libs here
01046 
01047         if (is_null($this->evaluationinstance)) {
01048             $evaluationlib = dirname(__FILE__) . '/eval/' . $this->evaluation . '/lib.php';
01049             if (is_readable($evaluationlib)) {
01050                 require_once($evaluationlib);
01051             } else {
01052                 throw new coding_exception('the grading evaluation subplugin must contain library ' . $evaluationlib);
01053             }
01054             $classname = 'workshop_' . $this->evaluation . '_evaluation';
01055             $this->evaluationinstance = new $classname($this);
01056             if (!in_array('workshop_evaluation', class_implements($this->evaluationinstance))) {
01057                 throw new coding_exception($classname . ' does not implement workshop_evaluation interface');
01058             }
01059         }
01060         return $this->evaluationinstance;
01061     }
01062 
01069     public function allocator_instance($method) {
01070         global $CFG;    // because we require other libs here
01071 
01072         $allocationlib = dirname(__FILE__) . '/allocation/' . $method . '/lib.php';
01073         if (is_readable($allocationlib)) {
01074             require_once($allocationlib);
01075         } else {
01076             throw new coding_exception('Unable to find the allocation library ' . $allocationlib);
01077         }
01078         $classname = 'workshop_' . $method . '_allocator';
01079         return new $classname($this);
01080     }
01081 
01085     public function view_url() {
01086         global $CFG;
01087         return new moodle_url('/mod/workshop/view.php', array('id' => $this->cm->id));
01088     }
01089 
01093     public function editform_url() {
01094         global $CFG;
01095         return new moodle_url('/mod/workshop/editform.php', array('cmid' => $this->cm->id));
01096     }
01097 
01101     public function previewform_url() {
01102         global $CFG;
01103         return new moodle_url('/mod/workshop/editformpreview.php', array('cmid' => $this->cm->id));
01104     }
01105 
01110     public function assess_url($assessmentid) {
01111         global $CFG;
01112         $assessmentid = clean_param($assessmentid, PARAM_INT);
01113         return new moodle_url('/mod/workshop/assessment.php', array('asid' => $assessmentid));
01114     }
01115 
01120     public function exassess_url($assessmentid) {
01121         global $CFG;
01122         $assessmentid = clean_param($assessmentid, PARAM_INT);
01123         return new moodle_url('/mod/workshop/exassessment.php', array('asid' => $assessmentid));
01124     }
01125 
01129     public function submission_url($id=null) {
01130         global $CFG;
01131         return new moodle_url('/mod/workshop/submission.php', array('cmid' => $this->cm->id, 'id' => $id));
01132     }
01133 
01138     public function exsubmission_url($id) {
01139         global $CFG;
01140         return new moodle_url('/mod/workshop/exsubmission.php', array('cmid' => $this->cm->id, 'id' => $id));
01141     }
01142 
01148     public function compare_url($sid, array $aids) {
01149         global $CFG;
01150 
01151         $url = new moodle_url('/mod/workshop/compare.php', array('cmid' => $this->cm->id, 'sid' => $sid));
01152         $i = 0;
01153         foreach ($aids as $aid) {
01154             $url->param("aid{$i}", $aid);
01155             $i++;
01156         }
01157         return $url;
01158     }
01159 
01165     public function excompare_url($sid, $aid) {
01166         global $CFG;
01167         return new moodle_url('/mod/workshop/excompare.php', array('cmid' => $this->cm->id, 'sid' => $sid, 'aid' => $aid));
01168     }
01169 
01173     public function updatemod_url() {
01174         global $CFG;
01175         return new moodle_url('/course/modedit.php', array('update' => $this->cm->id, 'return' => 1));
01176     }
01177 
01182     public function allocation_url($method=null) {
01183         global $CFG;
01184         $params = array('cmid' => $this->cm->id);
01185         if (!empty($method)) {
01186             $params['method'] = $method;
01187         }
01188         return new moodle_url('/mod/workshop/allocation.php', $params);
01189     }
01190 
01195     public function switchphase_url($phasecode) {
01196         global $CFG;
01197         $phasecode = clean_param($phasecode, PARAM_INT);
01198         return new moodle_url('/mod/workshop/switchphase.php', array('cmid' => $this->cm->id, 'phase' => $phasecode));
01199     }
01200 
01204     public function aggregate_url() {
01205         global $CFG;
01206         return new moodle_url('/mod/workshop/aggregate.php', array('cmid' => $this->cm->id));
01207     }
01208 
01212     public function toolbox_url($tool) {
01213         global $CFG;
01214         return new moodle_url('/mod/workshop/toolbox.php', array('id' => $this->cm->id, 'tool' => $tool));
01215     }
01216 
01224     public function log($action, moodle_url $url = null, $info = null) {
01225 
01226         if (is_null($url)) {
01227             $url = $this->view_url();
01228         }
01229 
01230         if (is_null($info)) {
01231             $info = $this->id;
01232         }
01233 
01234         $logurl = $this->log_convert_url($url);
01235         add_to_log($this->course->id, 'workshop', $action, $logurl, $info, $this->cm->id);
01236     }
01237 
01244     public function creating_submission_allowed($userid) {
01245 
01246         $now = time();
01247         $ignoredeadlines = has_capability('mod/workshop:ignoredeadlines', $this->context, $userid);
01248 
01249         if ($this->latesubmissions) {
01250             if ($this->phase != self::PHASE_SUBMISSION and $this->phase != self::PHASE_ASSESSMENT) {
01251                 // late submissions are allowed in the submission and assessment phase only
01252                 return false;
01253             }
01254             if (!$ignoredeadlines and !empty($this->submissionstart) and $this->submissionstart > $now) {
01255                 // late submissions are not allowed before the submission start
01256                 return false;
01257             }
01258             return true;
01259 
01260         } else {
01261             if ($this->phase != self::PHASE_SUBMISSION) {
01262                 // submissions are allowed during the submission phase only
01263                 return false;
01264             }
01265             if (!$ignoredeadlines and !empty($this->submissionstart) and $this->submissionstart > $now) {
01266                 // if enabled, submitting is not allowed before the date/time defined in the mod_form
01267                 return false;
01268             }
01269             if (!$ignoredeadlines and !empty($this->submissionend) and $now > $this->submissionend ) {
01270                 // if enabled, submitting is not allowed after the date/time defined in the mod_form unless late submission is allowed
01271                 return false;
01272             }
01273             return true;
01274         }
01275     }
01276 
01283     public function modifying_submission_allowed($userid) {
01284 
01285         $now = time();
01286         $ignoredeadlines = has_capability('mod/workshop:ignoredeadlines', $this->context, $userid);
01287 
01288         if ($this->phase != self::PHASE_SUBMISSION) {
01289             // submissions can be edited during the submission phase only
01290             return false;
01291         }
01292         if (!$ignoredeadlines and !empty($this->submissionstart) and $this->submissionstart > $now) {
01293             // if enabled, re-submitting is not allowed before the date/time defined in the mod_form
01294             return false;
01295         }
01296         if (!$ignoredeadlines and !empty($this->submissionend) and $now > $this->submissionend) {
01297             // if enabled, re-submitting is not allowed after the date/time defined in the mod_form even if late submission is allowed
01298             return false;
01299         }
01300         return true;
01301     }
01302 
01309     public function assessing_allowed($userid) {
01310 
01311         if ($this->phase != self::PHASE_ASSESSMENT) {
01312             // assessing is not allowed but in the assessment phase
01313             return false;
01314         }
01315 
01316         $now = time();
01317         $ignoredeadlines = has_capability('mod/workshop:ignoredeadlines', $this->context, $userid);
01318 
01319         if (!$ignoredeadlines and !empty($this->assessmentstart) and $this->assessmentstart > $now) {
01320             // if enabled, assessing is not allowed before the date/time defined in the mod_form
01321             return false;
01322         }
01323         if (!$ignoredeadlines and !empty($this->assessmentend) and $now > $this->assessmentend) {
01324             // if enabled, assessing is not allowed after the date/time defined in the mod_form
01325             return false;
01326         }
01327         // here we go, assessing is allowed
01328         return true;
01329     }
01330 
01340     public function assessing_examples_allowed() {
01341         if (empty($this->useexamples)) {
01342             return null;
01343         }
01344         if (self::EXAMPLES_VOLUNTARY == $this->examplesmode) {
01345             return true;
01346         }
01347         if (self::EXAMPLES_BEFORE_SUBMISSION == $this->examplesmode and self::PHASE_SUBMISSION == $this->phase) {
01348             return true;
01349         }
01350         if (self::EXAMPLES_BEFORE_ASSESSMENT == $this->examplesmode and self::PHASE_ASSESSMENT == $this->phase) {
01351             return true;
01352         }
01353         return false;
01354     }
01355 
01361     public function assessments_available() {
01362         return $this->phase == self::PHASE_CLOSED;
01363     }
01364 
01373     public function switch_phase($newphase) {
01374         global $DB;
01375 
01376         $known = $this->available_phases_list();
01377         if (!isset($known[$newphase])) {
01378             return false;
01379         }
01380 
01381         if (self::PHASE_CLOSED == $newphase) {
01382             // push the grades into the gradebook
01383             $workshop = new stdclass();
01384             foreach ($this as $property => $value) {
01385                 $workshop->{$property} = $value;
01386             }
01387             $workshop->course     = $this->course->id;
01388             $workshop->cmidnumber = $this->cm->id;
01389             $workshop->modname    = 'workshop';
01390             workshop_update_grades($workshop);
01391         }
01392 
01393         $DB->set_field('workshop', 'phase', $newphase, array('id' => $this->id));
01394         return true;
01395     }
01396 
01404     public function set_peer_grade($assessmentid, $grade) {
01405         global $DB;
01406 
01407         if (is_null($grade)) {
01408             return false;
01409         }
01410         $data = new stdclass();
01411         $data->id = $assessmentid;
01412         $data->grade = $grade;
01413         $data->timemodified = time();
01414         $DB->update_record('workshop_assessments', $data);
01415         return $grade;
01416     }
01417 
01429     public function prepare_grading_report_data($userid, $groups, $page, $perpage, $sortby, $sorthow) {
01430         global $DB;
01431 
01432         $canviewall     = has_capability('mod/workshop:viewallassessments', $this->context, $userid);
01433         $isparticipant  = has_any_capability(array('mod/workshop:submit', 'mod/workshop:peerassess'), $this->context, $userid);
01434 
01435         if (!$canviewall and !$isparticipant) {
01436             // who the hell is this?
01437             return array();
01438         }
01439 
01440         if (!in_array($sortby, array('lastname','firstname','submissiontitle','submissiongrade','gradinggrade'))) {
01441             $sortby = 'lastname';
01442         }
01443 
01444         if (!($sorthow === 'ASC' or $sorthow === 'DESC')) {
01445             $sorthow = 'ASC';
01446         }
01447 
01448         // get the list of user ids to be displayed
01449         if ($canviewall) {
01450             // fetch the list of ids of all workshop participants - this may get really long so fetch just id
01451             $participants = get_users_by_capability($this->context, array('mod/workshop:submit', 'mod/workshop:peerassess'),
01452                     'u.id', '', '', '', $groups, '', false, false, true);
01453         } else {
01454             // this is an ordinary workshop participant (aka student) - display the report just for him/her
01455             $participants = array($userid => (object)array('id' => $userid));
01456         }
01457 
01458         // we will need to know the number of all records later for the pagination purposes
01459         $numofparticipants = count($participants);
01460 
01461         if ($numofparticipants > 0) {
01462             // load all fields which can be used for sorting and paginate the records
01463             list($participantids, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED);
01464             $params['workshopid1'] = $this->id;
01465             $params['workshopid2'] = $this->id;
01466             $sqlsort = array();
01467             $sqlsortfields = array($sortby => $sorthow) + array('lastname' => 'ASC', 'firstname' => 'ASC', 'u.id' => 'ASC');
01468             foreach ($sqlsortfields as $sqlsortfieldname => $sqlsortfieldhow) {
01469                 $sqlsort[] = $sqlsortfieldname . ' ' . $sqlsortfieldhow;
01470             }
01471             $sqlsort = implode(',', $sqlsort);
01472             $sql = "SELECT u.id AS userid,u.firstname,u.lastname,u.picture,u.imagealt,u.email,
01473                            s.title AS submissiontitle, s.grade AS submissiongrade, ag.gradinggrade
01474                       FROM {user} u
01475                  LEFT JOIN {workshop_submissions} s ON (s.authorid = u.id AND s.workshopid = :workshopid1 AND s.example = 0)
01476                  LEFT JOIN {workshop_aggregations} ag ON (ag.userid = u.id AND ag.workshopid = :workshopid2)
01477                      WHERE u.id $participantids
01478                   ORDER BY $sqlsort";
01479             $participants = $DB->get_records_sql($sql, $params, $page * $perpage, $perpage);
01480         } else {
01481             $participants = array();
01482         }
01483 
01484         // this will hold the information needed to display user names and pictures
01485         $userinfo = array();
01486 
01487         // get the user details for all participants to display
01488         foreach ($participants as $participant) {
01489             if (!isset($userinfo[$participant->userid])) {
01490                 $userinfo[$participant->userid]            = new stdclass();
01491                 $userinfo[$participant->userid]->id        = $participant->userid;
01492                 $userinfo[$participant->userid]->firstname = $participant->firstname;
01493                 $userinfo[$participant->userid]->lastname  = $participant->lastname;
01494                 $userinfo[$participant->userid]->picture   = $participant->picture;
01495                 $userinfo[$participant->userid]->imagealt  = $participant->imagealt;
01496                 $userinfo[$participant->userid]->email     = $participant->email;
01497             }
01498         }
01499 
01500         // load the submissions details
01501         $submissions = $this->get_submissions(array_keys($participants));
01502 
01503         // get the user details for all moderators (teachers) that have overridden a submission grade
01504         foreach ($submissions as $submission) {
01505             if (!isset($userinfo[$submission->gradeoverby])) {
01506                 $userinfo[$submission->gradeoverby]            = new stdclass();
01507                 $userinfo[$submission->gradeoverby]->id        = $submission->gradeoverby;
01508                 $userinfo[$submission->gradeoverby]->firstname = $submission->overfirstname;
01509                 $userinfo[$submission->gradeoverby]->lastname  = $submission->overlastname;
01510                 $userinfo[$submission->gradeoverby]->picture   = $submission->overpicture;
01511                 $userinfo[$submission->gradeoverby]->imagealt  = $submission->overimagealt;
01512                 $userinfo[$submission->gradeoverby]->email     = $submission->overemail;
01513             }
01514         }
01515 
01516         // get the user details for all reviewers of the displayed participants
01517         $reviewers = array();
01518         if ($submissions) {
01519             list($submissionids, $params) = $DB->get_in_or_equal(array_keys($submissions), SQL_PARAMS_NAMED);
01520             $sql = "SELECT a.id AS assessmentid, a.submissionid, a.grade, a.gradinggrade, a.gradinggradeover, a.weight,
01521                            r.id AS reviewerid, r.lastname, r.firstname, r.picture, r.imagealt, r.email,
01522                            s.id AS submissionid, s.authorid
01523                       FROM {workshop_assessments} a
01524                       JOIN {user} r ON (a.reviewerid = r.id)
01525                       JOIN {workshop_submissions} s ON (a.submissionid = s.id AND s.example = 0)
01526                      WHERE a.submissionid $submissionids
01527                   ORDER BY a.weight DESC, r.lastname, r.firstname";
01528             $reviewers = $DB->get_records_sql($sql, $params);
01529             foreach ($reviewers as $reviewer) {
01530                 if (!isset($userinfo[$reviewer->reviewerid])) {
01531                     $userinfo[$reviewer->reviewerid]            = new stdclass();
01532                     $userinfo[$reviewer->reviewerid]->id        = $reviewer->reviewerid;
01533                     $userinfo[$reviewer->reviewerid]->firstname = $reviewer->firstname;
01534                     $userinfo[$reviewer->reviewerid]->lastname  = $reviewer->lastname;
01535                     $userinfo[$reviewer->reviewerid]->picture   = $reviewer->picture;
01536                     $userinfo[$reviewer->reviewerid]->imagealt  = $reviewer->imagealt;
01537                     $userinfo[$reviewer->reviewerid]->email     = $reviewer->email;
01538                 }
01539             }
01540         }
01541 
01542         // get the user details for all reviewees of the displayed participants
01543         $reviewees = array();
01544         if ($participants) {
01545             list($participantids, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED);
01546             $params['workshopid'] = $this->id;
01547             $sql = "SELECT a.id AS assessmentid, a.submissionid, a.grade, a.gradinggrade, a.gradinggradeover, a.reviewerid, a.weight,
01548                            s.id AS submissionid,
01549                            e.id AS authorid, e.lastname, e.firstname, e.picture, e.imagealt, e.email
01550                       FROM {user} u
01551                       JOIN {workshop_assessments} a ON (a.reviewerid = u.id)
01552                       JOIN {workshop_submissions} s ON (a.submissionid = s.id AND s.example = 0)
01553                       JOIN {user} e ON (s.authorid = e.id)
01554                      WHERE u.id $participantids AND s.workshopid = :workshopid
01555                   ORDER BY a.weight DESC, e.lastname, e.firstname";
01556             $reviewees = $DB->get_records_sql($sql, $params);
01557             foreach ($reviewees as $reviewee) {
01558                 if (!isset($userinfo[$reviewee->authorid])) {
01559                     $userinfo[$reviewee->authorid]            = new stdclass();
01560                     $userinfo[$reviewee->authorid]->id        = $reviewee->authorid;
01561                     $userinfo[$reviewee->authorid]->firstname = $reviewee->firstname;
01562                     $userinfo[$reviewee->authorid]->lastname  = $reviewee->lastname;
01563                     $userinfo[$reviewee->authorid]->picture   = $reviewee->picture;
01564                     $userinfo[$reviewee->authorid]->imagealt  = $reviewee->imagealt;
01565                     $userinfo[$reviewee->authorid]->email     = $reviewee->email;
01566                 }
01567             }
01568         }
01569 
01570         // finally populate the object to be rendered
01571         $grades = $participants;
01572 
01573         foreach ($participants as $participant) {
01574             // set up default (null) values
01575             $grades[$participant->userid]->submissionid = null;
01576             $grades[$participant->userid]->submissiontitle = null;
01577             $grades[$participant->userid]->submissiongrade = null;
01578             $grades[$participant->userid]->submissiongradeover = null;
01579             $grades[$participant->userid]->submissiongradeoverby = null;
01580             $grades[$participant->userid]->submissionpublished = null;
01581             $grades[$participant->userid]->reviewedby = array();
01582             $grades[$participant->userid]->reviewerof = array();
01583         }
01584         unset($participants);
01585         unset($participant);
01586 
01587         foreach ($submissions as $submission) {
01588             $grades[$submission->authorid]->submissionid = $submission->id;
01589             $grades[$submission->authorid]->submissiontitle = $submission->title;
01590             $grades[$submission->authorid]->submissiongrade = $this->real_grade($submission->grade);
01591             $grades[$submission->authorid]->submissiongradeover = $this->real_grade($submission->gradeover);
01592             $grades[$submission->authorid]->submissiongradeoverby = $submission->gradeoverby;
01593             $grades[$submission->authorid]->submissionpublished = $submission->published;
01594         }
01595         unset($submissions);
01596         unset($submission);
01597 
01598         foreach($reviewers as $reviewer) {
01599             $info = new stdclass();
01600             $info->userid = $reviewer->reviewerid;
01601             $info->assessmentid = $reviewer->assessmentid;
01602             $info->submissionid = $reviewer->submissionid;
01603             $info->grade = $this->real_grade($reviewer->grade);
01604             $info->gradinggrade = $this->real_grading_grade($reviewer->gradinggrade);
01605             $info->gradinggradeover = $this->real_grading_grade($reviewer->gradinggradeover);
01606             $info->weight = $reviewer->weight;
01607             $grades[$reviewer->authorid]->reviewedby[$reviewer->reviewerid] = $info;
01608         }
01609         unset($reviewers);
01610         unset($reviewer);
01611 
01612         foreach($reviewees as $reviewee) {
01613             $info = new stdclass();
01614             $info->userid = $reviewee->authorid;
01615             $info->assessmentid = $reviewee->assessmentid;
01616             $info->submissionid = $reviewee->submissionid;
01617             $info->grade = $this->real_grade($reviewee->grade);
01618             $info->gradinggrade = $this->real_grading_grade($reviewee->gradinggrade);
01619             $info->gradinggradeover = $this->real_grading_grade($reviewee->gradinggradeover);
01620             $info->weight = $reviewee->weight;
01621             $grades[$reviewee->reviewerid]->reviewerof[$reviewee->authorid] = $info;
01622         }
01623         unset($reviewees);
01624         unset($reviewee);
01625 
01626         foreach ($grades as $grade) {
01627             $grade->gradinggrade = $this->real_grading_grade($grade->gradinggrade);
01628         }
01629 
01630         $data = new stdclass();
01631         $data->grades = $grades;
01632         $data->userinfo = $userinfo;
01633         $data->totalcount = $numofparticipants;
01634         $data->maxgrade = $this->real_grade(100);
01635         $data->maxgradinggrade = $this->real_grading_grade(100);
01636         return $data;
01637     }
01638 
01646     public function real_grade_value($value, $max) {
01647         $localized = true;
01648         if (is_null($value) or $value === '') {
01649             return null;
01650         } elseif ($max == 0) {
01651             return 0;
01652         } else {
01653             return format_float($max * $value / 100, $this->gradedecimals, $localized);
01654         }
01655     }
01656 
01666     public function raw_grade_value($value, $max) {
01667         if (is_null($value) or $value === '') {
01668             return null;
01669         }
01670         if ($max == 0 or $value < 0) {
01671             return 0;
01672         }
01673         $p = $value / $max * 100;
01674         if ($p > 100) {
01675             return $max;
01676         }
01677         return grade_floatval($p);
01678     }
01679 
01686     public function real_grade($value) {
01687         return $this->real_grade_value($value, $this->grade);
01688     }
01689 
01696     public function real_grading_grade($value) {
01697         return $this->real_grade_value($value, $this->gradinggrade);
01698     }
01699 
01709     public function clear_assessments() {
01710         global $DB;
01711 
01712         $submissions = $this->get_submissions();
01713         if (empty($submissions)) {
01714             // no money, no love
01715             return;
01716         }
01717         $submissions = array_keys($submissions);
01718         list($sql, $params) = $DB->get_in_or_equal($submissions, SQL_PARAMS_NAMED);
01719         $sql = "submissionid $sql";
01720         $DB->set_field_select('workshop_assessments', 'grade', null, $sql, $params);
01721         $DB->set_field_select('workshop_assessments', 'gradinggrade', null, $sql, $params);
01722     }
01723 
01730     public function clear_submission_grades($restrict=null) {
01731         global $DB;
01732 
01733         $sql = "workshopid = :workshopid AND example = 0";
01734         $params = array('workshopid' => $this->id);
01735 
01736         if (is_null($restrict)) {
01737             // update all users - no more conditions
01738         } elseif (!empty($restrict)) {
01739             list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
01740             $sql .= " AND authorid $usql";
01741             $params = array_merge($params, $uparams);
01742         } else {
01743             throw new coding_exception('Empty value is not a valid parameter here');
01744         }
01745 
01746         $DB->set_field_select('workshop_submissions', 'grade', null, $sql, $params);
01747     }
01748 
01755     public function aggregate_submission_grades($restrict=null) {
01756         global $DB;
01757 
01758         // fetch a recordset with all assessments to process
01759         $sql = 'SELECT s.id AS submissionid, s.grade AS submissiongrade,
01760                        a.weight, a.grade
01761                   FROM {workshop_submissions} s
01762              LEFT JOIN {workshop_assessments} a ON (a.submissionid = s.id)
01763                  WHERE s.example=0 AND s.workshopid=:workshopid'; // to be cont.
01764         $params = array('workshopid' => $this->id);
01765 
01766         if (is_null($restrict)) {
01767             // update all users - no more conditions
01768         } elseif (!empty($restrict)) {
01769             list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
01770             $sql .= " AND s.authorid $usql";
01771             $params = array_merge($params, $uparams);
01772         } else {
01773             throw new coding_exception('Empty value is not a valid parameter here');
01774         }
01775 
01776         $sql .= ' ORDER BY s.id'; // this is important for bulk processing
01777 
01778         $rs         = $DB->get_recordset_sql($sql, $params);
01779         $batch      = array();    // will contain a set of all assessments of a single submission
01780         $previous   = null;       // a previous record in the recordset
01781 
01782         foreach ($rs as $current) {
01783             if (is_null($previous)) {
01784                 // we are processing the very first record in the recordset
01785                 $previous   = $current;
01786             }
01787             if ($current->submissionid == $previous->submissionid) {
01788                 // we are still processing the current submission
01789                 $batch[] = $current;
01790             } else {
01791                 // process all the assessments of a sigle submission
01792                 $this->aggregate_submission_grades_process($batch);
01793                 // and then start to process another submission
01794                 $batch      = array($current);
01795                 $previous   = $current;
01796             }
01797         }
01798         // do not forget to process the last batch!
01799         $this->aggregate_submission_grades_process($batch);
01800         $rs->close();
01801     }
01802 
01809     public function clear_grading_grades($restrict=null) {
01810         global $DB;
01811 
01812         $sql = "workshopid = :workshopid";
01813         $params = array('workshopid' => $this->id);
01814 
01815         if (is_null($restrict)) {
01816             // update all users - no more conditions
01817         } elseif (!empty($restrict)) {
01818             list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
01819             $sql .= " AND userid $usql";
01820             $params = array_merge($params, $uparams);
01821         } else {
01822             throw new coding_exception('Empty value is not a valid parameter here');
01823         }
01824 
01825         $DB->set_field_select('workshop_aggregations', 'gradinggrade', null, $sql, $params);
01826     }
01827 
01837     public function aggregate_grading_grades($restrict=null) {
01838         global $DB;
01839 
01840         // fetch a recordset with all assessments to process
01841         $sql = 'SELECT a.reviewerid, a.gradinggrade, a.gradinggradeover,
01842                        ag.id AS aggregationid, ag.gradinggrade AS aggregatedgrade
01843                   FROM {workshop_assessments} a
01844             INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
01845              LEFT JOIN {workshop_aggregations} ag ON (ag.userid = a.reviewerid AND ag.workshopid = s.workshopid)
01846                  WHERE s.example=0 AND s.workshopid=:workshopid'; // to be cont.
01847         $params = array('workshopid' => $this->id);
01848 
01849         if (is_null($restrict)) {
01850             // update all users - no more conditions
01851         } elseif (!empty($restrict)) {
01852             list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
01853             $sql .= " AND a.reviewerid $usql";
01854             $params = array_merge($params, $uparams);
01855         } else {
01856             throw new coding_exception('Empty value is not a valid parameter here');
01857         }
01858 
01859         $sql .= ' ORDER BY a.reviewerid'; // this is important for bulk processing
01860 
01861         $rs         = $DB->get_recordset_sql($sql, $params);
01862         $batch      = array();    // will contain a set of all assessments of a single submission
01863         $previous   = null;       // a previous record in the recordset
01864 
01865         foreach ($rs as $current) {
01866             if (is_null($previous)) {
01867                 // we are processing the very first record in the recordset
01868                 $previous   = $current;
01869             }
01870             if ($current->reviewerid == $previous->reviewerid) {
01871                 // we are still processing the current reviewer
01872                 $batch[] = $current;
01873             } else {
01874                 // process all the assessments of a sigle submission
01875                 $this->aggregate_grading_grades_process($batch);
01876                 // and then start to process another reviewer
01877                 $batch      = array($current);
01878                 $previous   = $current;
01879             }
01880         }
01881         // do not forget to process the last batch!
01882         $this->aggregate_grading_grades_process($batch);
01883         $rs->close();
01884     }
01885 
01894     public function get_feedbackreviewer_form(moodle_url $actionurl, stdclass $assessment, $options=array()) {
01895         global $CFG;
01896         require_once(dirname(__FILE__) . '/feedbackreviewer_form.php');
01897 
01898         $current = new stdclass();
01899         $current->asid                      = $assessment->id;
01900         $current->weight                    = $assessment->weight;
01901         $current->gradinggrade              = $this->real_grading_grade($assessment->gradinggrade);
01902         $current->gradinggradeover          = $this->real_grading_grade($assessment->gradinggradeover);
01903         $current->feedbackreviewer          = $assessment->feedbackreviewer;
01904         $current->feedbackreviewerformat    = $assessment->feedbackreviewerformat;
01905         if (is_null($current->gradinggrade)) {
01906             $current->gradinggrade = get_string('nullgrade', 'workshop');
01907         }
01908         if (!isset($options['editable'])) {
01909             $editable = true;   // by default
01910         } else {
01911             $editable = (bool)$options['editable'];
01912         }
01913 
01914         // prepare wysiwyg editor
01915         $current = file_prepare_standard_editor($current, 'feedbackreviewer', array());
01916 
01917         return new workshop_feedbackreviewer_form($actionurl,
01918                 array('workshop' => $this, 'current' => $current, 'editoropts' => array(), 'options' => $options),
01919                 'post', '', null, $editable);
01920     }
01921 
01930     public function get_feedbackauthor_form(moodle_url $actionurl, stdclass $submission, $options=array()) {
01931         global $CFG;
01932         require_once(dirname(__FILE__) . '/feedbackauthor_form.php');
01933 
01934         $current = new stdclass();
01935         $current->submissionid          = $submission->id;
01936         $current->published             = $submission->published;
01937         $current->grade                 = $this->real_grade($submission->grade);
01938         $current->gradeover             = $this->real_grade($submission->gradeover);
01939         $current->feedbackauthor        = $submission->feedbackauthor;
01940         $current->feedbackauthorformat  = $submission->feedbackauthorformat;
01941         if (is_null($current->grade)) {
01942             $current->grade = get_string('nullgrade', 'workshop');
01943         }
01944         if (!isset($options['editable'])) {
01945             $editable = true;   // by default
01946         } else {
01947             $editable = (bool)$options['editable'];
01948         }
01949 
01950         // prepare wysiwyg editor
01951         $current = file_prepare_standard_editor($current, 'feedbackauthor', array());
01952 
01953         return new workshop_feedbackauthor_form($actionurl,
01954                 array('workshop' => $this, 'current' => $current, 'editoropts' => array(), 'options' => $options),
01955                 'post', '', null, $editable);
01956     }
01957 
01959     // Internal methods (implementation details)                                  //
01961 
01971     protected function aggregate_submission_grades_process(array $assessments) {
01972         global $DB;
01973 
01974         $submissionid   = null; // the id of the submission being processed
01975         $current        = null; // the grade currently saved in database
01976         $finalgrade     = null; // the new grade to be calculated
01977         $sumgrades      = 0;
01978         $sumweights     = 0;
01979 
01980         foreach ($assessments as $assessment) {
01981             if (is_null($submissionid)) {
01982                 // the id is the same in all records, fetch it during the first loop cycle
01983                 $submissionid = $assessment->submissionid;
01984             }
01985             if (is_null($current)) {
01986                 // the currently saved grade is the same in all records, fetch it during the first loop cycle
01987                 $current = $assessment->submissiongrade;
01988             }
01989             if (is_null($assessment->grade)) {
01990                 // this was not assessed yet
01991                 continue;
01992             }
01993             if ($assessment->weight == 0) {
01994                 // this does not influence the calculation
01995                 continue;
01996             }
01997             $sumgrades  += $assessment->grade * $assessment->weight;
01998             $sumweights += $assessment->weight;
01999         }
02000         if ($sumweights > 0 and is_null($finalgrade)) {
02001             $finalgrade = grade_floatval($sumgrades / $sumweights);
02002         }
02003         // check if the new final grade differs from the one stored in the database
02004         if (grade_floats_different($finalgrade, $current)) {
02005             // we need to save new calculation into the database
02006             $record = new stdclass();
02007             $record->id = $submissionid;
02008             $record->grade = $finalgrade;
02009             $record->timegraded = time();
02010             $DB->update_record('workshop_submissions', $record);
02011         }
02012     }
02013 
02024     protected function aggregate_grading_grades_process(array $assessments, $timegraded = null) {
02025         global $DB;
02026 
02027         $reviewerid = null; // the id of the reviewer being processed
02028         $current    = null; // the gradinggrade currently saved in database
02029         $finalgrade = null; // the new grade to be calculated
02030         $agid       = null; // aggregation id
02031         $sumgrades  = 0;
02032         $count      = 0;
02033 
02034         if (is_null($timegraded)) {
02035             $timegraded = time();
02036         }
02037 
02038         foreach ($assessments as $assessment) {
02039             if (is_null($reviewerid)) {
02040                 // the id is the same in all records, fetch it during the first loop cycle
02041                 $reviewerid = $assessment->reviewerid;
02042             }
02043             if (is_null($agid)) {
02044                 // the id is the same in all records, fetch it during the first loop cycle
02045                 $agid = $assessment->aggregationid;
02046             }
02047             if (is_null($current)) {
02048                 // the currently saved grade is the same in all records, fetch it during the first loop cycle
02049                 $current = $assessment->aggregatedgrade;
02050             }
02051             if (!is_null($assessment->gradinggradeover)) {
02052                 // the grading grade for this assessment is overridden by a teacher
02053                 $sumgrades += $assessment->gradinggradeover;
02054                 $count++;
02055             } else {
02056                 if (!is_null($assessment->gradinggrade)) {
02057                     $sumgrades += $assessment->gradinggrade;
02058                     $count++;
02059                 }
02060             }
02061         }
02062         if ($count > 0) {
02063             $finalgrade = grade_floatval($sumgrades / $count);
02064         }
02065         // check if the new final grade differs from the one stored in the database
02066         if (grade_floats_different($finalgrade, $current)) {
02067             // we need to save new calculation into the database
02068             if (is_null($agid)) {
02069                 // no aggregation record yet
02070                 $record = new stdclass();
02071                 $record->workshopid = $this->id;
02072                 $record->userid = $reviewerid;
02073                 $record->gradinggrade = $finalgrade;
02074                 $record->timegraded = $timegraded;
02075                 $DB->insert_record('workshop_aggregations', $record);
02076             } else {
02077                 $record = new stdclass();
02078                 $record->id = $agid;
02079                 $record->gradinggrade = $finalgrade;
02080                 $record->timegraded = $timegraded;
02081                 $DB->update_record('workshop_aggregations', $record);
02082             }
02083         }
02084     }
02085 
02094     protected function users_with_submission(array $userids) {
02095         global $DB;
02096 
02097         if (empty($userids)) {
02098             return array();
02099         }
02100         $userswithsubmission = array();
02101         list($usql, $uparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
02102         $sql = "SELECT id,authorid
02103                   FROM {workshop_submissions}
02104                  WHERE example = 0 AND workshopid = :workshopid AND authorid $usql";
02105         $params = array('workshopid' => $this->id);
02106         $params = array_merge($params, $uparams);
02107         $submissions = $DB->get_records_sql($sql, $params);
02108         foreach ($submissions as $submission) {
02109             $userswithsubmission[$submission->authorid] = true;
02110         }
02111 
02112         return $userswithsubmission;
02113     }
02114 
02118     protected function available_phases_list() {
02119         return array(
02120             self::PHASE_SETUP       => true,
02121             self::PHASE_SUBMISSION  => true,
02122             self::PHASE_ASSESSMENT  => true,
02123             self::PHASE_EVALUATION  => true,
02124             self::PHASE_CLOSED      => true,
02125         );
02126     }
02127 
02134     protected function log_convert_url(moodle_url $fullurl) {
02135         static $baseurl;
02136 
02137         if (!isset($baseurl)) {
02138             $baseurl = new moodle_url('/mod/workshop/');
02139             $baseurl = $baseurl->out();
02140         }
02141 
02142         return substr($fullurl->out(), strlen($baseurl));
02143     }
02144 }
02145 
02147 // Renderable components
02149 
02156 class workshop_user_plan implements renderable {
02157 
02159     public $userid;
02161     public $workshop;
02163     public $phases = array();
02165     protected $examples = null;
02166 
02173     public function __construct(workshop $workshop, $userid) {
02174         global $DB;
02175 
02176         $this->workshop = $workshop;
02177         $this->userid   = $userid;
02178 
02179         //---------------------------------------------------------
02180         // * SETUP | submission | assessment | evaluation | closed
02181         //---------------------------------------------------------
02182         $phase = new stdclass();
02183         $phase->title = get_string('phasesetup', 'workshop');
02184         $phase->tasks = array();
02185         if (has_capability('moodle/course:manageactivities', $workshop->context, $userid)) {
02186             $task = new stdclass();
02187             $task->title = get_string('taskintro', 'workshop');
02188             $task->link = $workshop->updatemod_url();
02189             $task->completed = !(trim($workshop->intro) == '');
02190             $phase->tasks['intro'] = $task;
02191         }
02192         if (has_capability('moodle/course:manageactivities', $workshop->context, $userid)) {
02193             $task = new stdclass();
02194             $task->title = get_string('taskinstructauthors', 'workshop');
02195             $task->link = $workshop->updatemod_url();
02196             $task->completed = !(trim($workshop->instructauthors) == '');
02197             $phase->tasks['instructauthors'] = $task;
02198         }
02199         if (has_capability('mod/workshop:editdimensions', $workshop->context, $userid)) {
02200             $task = new stdclass();
02201             $task->title = get_string('editassessmentform', 'workshop');
02202             $task->link = $workshop->editform_url();
02203             if ($workshop->grading_strategy_instance()->form_ready()) {
02204                 $task->completed = true;
02205             } elseif ($workshop->phase > workshop::PHASE_SETUP) {
02206                 $task->completed = false;
02207             }
02208             $phase->tasks['editform'] = $task;
02209         }
02210         if ($workshop->useexamples and has_capability('mod/workshop:manageexamples', $workshop->context, $userid)) {
02211             $task = new stdclass();
02212             $task->title = get_string('prepareexamples', 'workshop');
02213             if ($DB->count_records('workshop_submissions', array('example' => 1, 'workshopid' => $workshop->id)) > 0) {
02214                 $task->completed = true;
02215             } elseif ($workshop->phase > workshop::PHASE_SETUP) {
02216                 $task->completed = false;
02217             }
02218             $phase->tasks['prepareexamples'] = $task;
02219         }
02220         if (empty($phase->tasks) and $workshop->phase == workshop::PHASE_SETUP) {
02221             // if we are in the setup phase and there is no task (typical for students), let us
02222             // display some explanation what is going on
02223             $task = new stdclass();
02224             $task->title = get_string('undersetup', 'workshop');
02225             $task->completed = 'info';
02226             $phase->tasks['setupinfo'] = $task;
02227         }
02228         $this->phases[workshop::PHASE_SETUP] = $phase;
02229 
02230         //---------------------------------------------------------
02231         // setup | * SUBMISSION | assessment | evaluation | closed
02232         //---------------------------------------------------------
02233         $phase = new stdclass();
02234         $phase->title = get_string('phasesubmission', 'workshop');
02235         $phase->tasks = array();
02236         if (($workshop->usepeerassessment or $workshop->useselfassessment)
02237              and has_capability('moodle/course:manageactivities', $workshop->context, $userid)) {
02238             $task = new stdclass();
02239             $task->title = get_string('taskinstructreviewers', 'workshop');
02240             $task->link = $workshop->updatemod_url();
02241             if (trim($workshop->instructreviewers)) {
02242                 $task->completed = true;
02243             } elseif ($workshop->phase >= workshop::PHASE_ASSESSMENT) {
02244                 $task->completed = false;
02245             }
02246             $phase->tasks['instructreviewers'] = $task;
02247         }
02248         if ($workshop->useexamples and $workshop->examplesmode == workshop::EXAMPLES_BEFORE_SUBMISSION
02249                 and has_capability('mod/workshop:submit', $workshop->context, $userid, false)
02250                     and !has_capability('mod/workshop:manageexamples', $workshop->context, $userid)) {
02251             $task = new stdclass();
02252             $task->title = get_string('exampleassesstask', 'workshop');
02253             $examples = $this->get_examples();
02254             $a = new stdclass();
02255             $a->expected = count($examples);
02256             $a->assessed = 0;
02257             foreach ($examples as $exampleid => $example) {
02258                 if (!is_null($example->grade)) {
02259                     $a->assessed++;
02260                 }
02261             }
02262             $task->details = get_string('exampleassesstaskdetails', 'workshop', $a);
02263             if ($a->assessed == $a->expected) {
02264                 $task->completed = true;
02265             } elseif ($workshop->phase >= workshop::PHASE_ASSESSMENT) {
02266                 $task->completed = false;
02267             }
02268             $phase->tasks['examples'] = $task;
02269         }
02270         if (has_capability('mod/workshop:submit', $workshop->context, $userid, false)) {
02271             $task = new stdclass();
02272             $task->title = get_string('tasksubmit', 'workshop');
02273             $task->link = $workshop->submission_url();
02274             if ($DB->record_exists('workshop_submissions', array('workshopid'=>$workshop->id, 'example'=>0, 'authorid'=>$userid))) {
02275                 $task->completed = true;
02276             } elseif ($workshop->phase >= workshop::PHASE_ASSESSMENT) {
02277                 $task->completed = false;
02278             } else {
02279                 $task->completed = null;    // still has a chance to submit
02280             }
02281             $phase->tasks['submit'] = $task;
02282         }
02283         if (has_capability('mod/workshop:allocate', $workshop->context, $userid)) {
02284             $task = new stdclass();
02285             $task->title = get_string('allocate', 'workshop');
02286             $task->link = $workshop->allocation_url();
02287             $numofauthors = count(get_users_by_capability($workshop->context, 'mod/workshop:submit', 'u.id', '', '', '',
02288                     '', '', false, true));
02289             $numofsubmissions = $DB->count_records('workshop_submissions', array('workshopid'=>$workshop->id, 'example'=>0));
02290             $sql = 'SELECT COUNT(s.id) AS nonallocated
02291                       FROM {workshop_submissions} s
02292                  LEFT JOIN {workshop_assessments} a ON (a.submissionid=s.id)
02293                      WHERE s.workshopid = :workshopid AND s.example=0 AND a.submissionid IS NULL';
02294             $params['workshopid'] = $workshop->id;
02295             $numnonallocated = $DB->count_records_sql($sql, $params);
02296             if ($numofsubmissions == 0) {
02297                 $task->completed = null;
02298             } elseif ($numnonallocated == 0) {
02299                 $task->completed = true;
02300             } elseif ($workshop->phase > workshop::PHASE_SUBMISSION) {
02301                 $task->completed = false;
02302             } else {
02303                 $task->completed = null;    // still has a chance to allocate
02304             }
02305             $a = new stdclass();
02306             $a->expected    = $numofauthors;
02307             $a->submitted   = $numofsubmissions;
02308             $a->allocate    = $numnonallocated;
02309             $task->details  = get_string('allocatedetails', 'workshop', $a);
02310             unset($a);
02311             $phase->tasks['allocate'] = $task;
02312 
02313             if ($numofsubmissions < $numofauthors and $workshop->phase >= workshop::PHASE_SUBMISSION) {
02314                 $task = new stdclass();
02315                 $task->title = get_string('someuserswosubmission', 'workshop');
02316                 $task->completed = 'info';
02317                 $phase->tasks['allocateinfo'] = $task;
02318             }
02319         }
02320         if ($workshop->submissionstart) {
02321             $task = new stdclass();
02322             $task->title = get_string('submissionstartdatetime', 'workshop', workshop::timestamp_formats($workshop->submissionstart));
02323             $task->completed = 'info';
02324             $phase->tasks['submissionstartdatetime'] = $task;
02325         }
02326         if ($workshop->submissionend) {
02327             $task = new stdclass();
02328             $task->title = get_string('submissionenddatetime', 'workshop', workshop::timestamp_formats($workshop->submissionend));
02329             $task->completed = 'info';
02330             $phase->tasks['submissionenddatetime'] = $task;
02331         }
02332         if (($workshop->submissionstart < time()) and $workshop->latesubmissions) {
02333             $task = new stdclass();
02334             $task->title = get_string('latesubmissionsallowed', 'workshop');
02335             $task->completed = 'info';
02336             $phase->tasks['latesubmissionsallowed'] = $task;
02337         }
02338         if (isset($phase->tasks['submissionstartdatetime']) or isset($phase->tasks['submissionenddatetime'])) {
02339             if (has_capability('mod/workshop:ignoredeadlines', $workshop->context, $userid)) {
02340                 $task = new stdclass();
02341                 $task->title = get_string('deadlinesignored', 'workshop');
02342                 $task->completed = 'info';
02343                 $phase->tasks['deadlinesignored'] = $task;
02344             }
02345         }
02346         $this->phases[workshop::PHASE_SUBMISSION] = $phase;
02347 
02348         //---------------------------------------------------------
02349         // setup | submission | * ASSESSMENT | evaluation | closed
02350         //---------------------------------------------------------
02351         $phase = new stdclass();
02352         $phase->title = get_string('phaseassessment', 'workshop');
02353         $phase->tasks = array();
02354         $phase->isreviewer = has_capability('mod/workshop:peerassess', $workshop->context, $userid);
02355         if ($workshop->useexamples and $workshop->examplesmode == workshop::EXAMPLES_BEFORE_ASSESSMENT
02356                 and $phase->isreviewer and !has_capability('mod/workshop:manageexamples', $workshop->context, $userid)) {
02357             $task = new stdclass();
02358             $task->title = get_string('exampleassesstask', 'workshop');
02359             $examples = $workshop->get_examples_for_reviewer($userid);
02360             $a = new stdclass();
02361             $a->expected = count($examples);
02362             $a->assessed = 0;
02363             foreach ($examples as $exampleid => $example) {
02364                 if (!is_null($example->grade)) {
02365                     $a->assessed++;
02366                 }
02367             }
02368             $task->details = get_string('exampleassesstaskdetails', 'workshop', $a);
02369             if ($a->assessed == $a->expected) {
02370                 $task->completed = true;
02371             } elseif ($workshop->phase > workshop::PHASE_ASSESSMENT) {
02372                 $task->completed = false;
02373             }
02374             $phase->tasks['examples'] = $task;
02375         }
02376         if (empty($phase->tasks['examples']) or !empty($phase->tasks['examples']->completed)) {
02377             $phase->assessments = $workshop->get_assessments_by_reviewer($userid);
02378             $numofpeers     = 0;    // number of allocated peer-assessments
02379             $numofpeerstodo = 0;    // number of peer-assessments to do
02380             $numofself      = 0;    // number of allocated self-assessments - should be 0 or 1
02381             $numofselftodo  = 0;    // number of self-assessments to do - should be 0 or 1
02382             foreach ($phase->assessments as $a) {
02383                 if ($a->authorid == $userid) {
02384                     $numofself++;
02385                     if (is_null($a->grade)) {
02386                         $numofselftodo++;
02387                     }
02388                 } else {
02389                     $numofpeers++;
02390                     if (is_null($a->grade)) {
02391                         $numofpeerstodo++;
02392                     }
02393                 }
02394             }
02395             unset($a);
02396             if ($workshop->usepeerassessment and $numofpeers) {
02397                 $task = new stdclass();
02398                 if ($numofpeerstodo == 0) {
02399                     $task->completed = true;
02400                 } elseif ($workshop->phase > workshop::PHASE_ASSESSMENT) {
02401                     $task->completed = false;
02402                 }
02403                 $a = new stdclass();
02404                 $a->total = $numofpeers;
02405                 $a->todo  = $numofpeerstodo;
02406                 $task->title = get_string('taskassesspeers', 'workshop');
02407                 $task->details = get_string('taskassesspeersdetails', 'workshop', $a);
02408                 unset($a);
02409                 $phase->tasks['assesspeers'] = $task;
02410             }
02411             if ($workshop->useselfassessment and $numofself) {
02412                 $task = new stdclass();
02413                 if ($numofselftodo == 0) {
02414                     $task->completed = true;
02415                 } elseif ($workshop->phase > workshop::PHASE_ASSESSMENT) {
02416                     $task->completed = false;
02417                 }
02418                 $task->title = get_string('taskassessself', 'workshop');
02419                 $phase->tasks['assessself'] = $task;
02420             }
02421         }
02422         if ($workshop->assessmentstart) {
02423             $task = new stdclass();
02424             $task->title = get_string('assessmentstartdatetime', 'workshop', workshop::timestamp_formats($workshop->assessmentstart));
02425             $task->completed = 'info';
02426             $phase->tasks['assessmentstartdatetime'] = $task;
02427         }
02428         if ($workshop->assessmentend) {
02429             $task = new stdclass();
02430             $task->title = get_string('assessmentenddatetime', 'workshop', workshop::timestamp_formats($workshop->assessmentend));
02431             $task->completed = 'info';
02432             $phase->tasks['assessmentenddatetime'] = $task;
02433         }
02434         if (isset($phase->tasks['assessmentstartdatetime']) or isset($phase->tasks['assessmentenddatetime'])) {
02435             if (has_capability('mod/workshop:ignoredeadlines', $workshop->context, $userid)) {
02436                 $task = new stdclass();
02437                 $task->title = get_string('deadlinesignored', 'workshop');
02438                 $task->completed = 'info';
02439                 $phase->tasks['deadlinesignored'] = $task;
02440             }
02441         }
02442         $this->phases[workshop::PHASE_ASSESSMENT] = $phase;
02443 
02444         //---------------------------------------------------------
02445         // setup | submission | assessment | * EVALUATION | closed
02446         //---------------------------------------------------------
02447         $phase = new stdclass();
02448         $phase->title = get_string('phaseevaluation', 'workshop');
02449         $phase->tasks = array();
02450         if (has_capability('mod/workshop:overridegrades', $workshop->context)) {
02451             $expected = count($workshop->get_potential_authors(false));
02452             $calculated = $DB->count_records_select('workshop_submissions',
02453                     'workshopid = ? AND (grade IS NOT NULL OR gradeover IS NOT NULL)', array($workshop->id));
02454             $task = new stdclass();
02455             $task->title = get_string('calculatesubmissiongrades', 'workshop');
02456             $a = new stdclass();
02457             $a->expected    = $expected;
02458             $a->calculated  = $calculated;
02459             $task->details  = get_string('calculatesubmissiongradesdetails', 'workshop', $a);
02460             if ($calculated >= $expected) {
02461                 $task->completed = true;
02462             } elseif ($workshop->phase > workshop::PHASE_EVALUATION) {
02463                 $task->completed = false;
02464             }
02465             $phase->tasks['calculatesubmissiongrade'] = $task;
02466 
02467             $expected = count($workshop->get_potential_reviewers(false));
02468             $calculated = $DB->count_records_select('workshop_aggregations',
02469                     'workshopid = ? AND gradinggrade IS NOT NULL', array($workshop->id));
02470             $task = new stdclass();
02471             $task->title = get_string('calculategradinggrades', 'workshop');
02472             $a = new stdclass();
02473             $a->expected    = $expected;
02474             $a->calculated  = $calculated;
02475             $task->details  = get_string('calculategradinggradesdetails', 'workshop', $a);
02476             if ($calculated >= $expected) {
02477                 $task->completed = true;
02478             } elseif ($workshop->phase > workshop::PHASE_EVALUATION) {
02479                 $task->completed = false;
02480             }
02481             $phase->tasks['calculategradinggrade'] = $task;
02482 
02483         } elseif ($workshop->phase == workshop::PHASE_EVALUATION) {
02484             $task = new stdclass();
02485             $task->title = get_string('evaluategradeswait', 'workshop');
02486             $task->completed = 'info';
02487             $phase->tasks['evaluateinfo'] = $task;
02488         }
02489         $this->phases[workshop::PHASE_EVALUATION] = $phase;
02490 
02491         //---------------------------------------------------------
02492         // setup | submission | assessment | evaluation | * CLOSED
02493         //---------------------------------------------------------
02494         $phase = new stdclass();
02495         $phase->title = get_string('phaseclosed', 'workshop');
02496         $phase->tasks = array();
02497         $this->phases[workshop::PHASE_CLOSED] = $phase;
02498 
02499         // Polish data, set default values if not done explicitly
02500         foreach ($this->phases as $phasecode => $phase) {
02501             $phase->title       = isset($phase->title)      ? $phase->title     : '';
02502             $phase->tasks       = isset($phase->tasks)      ? $phase->tasks     : array();
02503             if ($phasecode == $workshop->phase) {
02504                 $phase->active = true;
02505             } else {
02506                 $phase->active = false;
02507             }
02508             if (!isset($phase->actions)) {
02509                 $phase->actions = array();
02510             }
02511 
02512             foreach ($phase->tasks as $taskcode => $task) {
02513                 $task->title        = isset($task->title)       ? $task->title      : '';
02514                 $task->link         = isset($task->link)        ? $task->link       : null;
02515                 $task->details      = isset($task->details)     ? $task->details    : '';
02516                 $task->completed    = isset($task->completed)   ? $task->completed  : null;
02517             }
02518         }
02519 
02520         // Add phase switching actions
02521         if (has_capability('mod/workshop:switchphase', $workshop->context, $userid)) {
02522             foreach ($this->phases as $phasecode => $phase) {
02523                 if (! $phase->active) {
02524                     $action = new stdclass();
02525                     $action->type = 'switchphase';
02526                     $action->url  = $workshop->switchphase_url($phasecode);
02527                     $phase->actions[] = $action;
02528                 }
02529             }
02530         }
02531     }
02532 
02541     public function get_examples() {
02542         if (is_null($this->examples)) {
02543             $this->examples = $this->workshop->get_examples_for_reviewer($this->userid);
02544         }
02545         return $this->examples;
02546     }
02547 }
02548 
02556 abstract class workshop_submission_base {
02557 
02559     protected $anonymous;
02560 
02561     /* @var array of columns from workshop_submissions that are assigned as properties */
02562     protected $fields = array();
02563 
02571     public function __construct(stdClass $submission, $showauthor = false) {
02572 
02573         foreach ($this->fields as $field) {
02574             if (!property_exists($submission, $field)) {
02575                 throw new coding_exception('Submission record must provide public property ' . $field);
02576             }
02577             if (!property_exists($this, $field)) {
02578                 throw new coding_exception('Renderable component must accept public property ' . $field);
02579             }
02580             $this->{$field} = $submission->{$field};
02581         }
02582 
02583         if ($showauthor) {
02584             $this->anonymous = false;
02585         } else {
02586             $this->anonymize();
02587         }
02588     }
02589 
02595     public function anonymize() {
02596         foreach (array('authorid', 'authorfirstname', 'authorlastname',
02597                'authorpicture', 'authorimagealt', 'authoremail') as $field) {
02598             unset($this->{$field});
02599         }
02600         $this->anonymous = true;
02601     }
02602 
02608     public function is_anonymous() {
02609         return $this->anonymous;
02610     }
02611 }
02612 
02618 class workshop_submission_summary extends workshop_submission_base implements renderable {
02619 
02621     public $id;
02623     public $title;
02625     public $status;
02627     public $timecreated;
02629     public $timemodified;
02631     public $authorid;
02633     public $authorfirstname;
02635     public $authorlastname;
02637     public $authorpicture;
02639     public $authorimagealt;
02641     public $authoremail;
02643     public $url;
02644 
02649     protected $fields = array(
02650         'id', 'title', 'timecreated', 'timemodified',
02651         'authorid', 'authorfirstname', 'authorlastname', 'authorpicture',
02652         'authorimagealt', 'authoremail');
02653 }
02654 
02660 class workshop_submission extends workshop_submission_summary implements renderable {
02661 
02663     public $content;
02665     public $contentformat;
02667     public $contenttrust;
02669     public $attachment;
02670 
02675     protected $fields = array(
02676         'id', 'title', 'timecreated', 'timemodified', 'content', 'contentformat', 'contenttrust',
02677         'attachment', 'authorid', 'authorfirstname', 'authorlastname', 'authorpicture',
02678         'authorimagealt', 'authoremail');
02679 }
02680 
02687 class workshop_example_submission_summary extends workshop_submission_base implements renderable {
02688 
02690     public $id;
02692     public $title;
02694     public $status;
02696     public $gradeinfo;
02698     public $url;
02700     public $editurl;
02702     public $assesslabel;
02704     public $assessurl;
02706     public $editable = false;
02707 
02712     protected $fields = array('id', 'title');
02713 
02719     public function is_anonymous() {
02720         return true;
02721     }
02722 }
02723 
02729 class workshop_example_submission extends workshop_example_submission_summary implements renderable {
02730 
02732     public $content;
02734     public $contentformat;
02736     public $contenttrust;
02738     public $attachment;
02739 
02744     protected $fields = array('id', 'title', 'content', 'contentformat', 'contenttrust', 'attachment');
02745 }
02746 
02747 
02755 abstract class workshop_assessment_base {
02756 
02758     public $title = '';
02759 
02761     public $form;
02762 
02764     public $url;
02765 
02767     public $realgrade = null;
02768 
02770     public $maxgrade;
02771 
02773     public $reviewer = null;
02774 
02776     public $author = null;
02777 
02779     public $actions = array();
02780 
02781     /* @var array of columns that are assigned as properties */
02782     protected $fields = array();
02783 
02791     public function __construct(stdClass $record, array $options = array()) {
02792 
02793         $this->validate_raw_record($record);
02794 
02795         foreach ($this->fields as $field) {
02796             if (!property_exists($record, $field)) {
02797                 throw new coding_exception('Assessment record must provide public property ' . $field);
02798             }
02799             if (!property_exists($this, $field)) {
02800                 throw new coding_exception('Renderable component must accept public property ' . $field);
02801             }
02802             $this->{$field} = $record->{$field};
02803         }
02804 
02805         if (!empty($options['showreviewer'])) {
02806             $this->reviewer = user_picture::unalias($record, null, 'revieweridx', 'reviewer');
02807         }
02808 
02809         if (!empty($options['showauthor'])) {
02810             $this->author = user_picture::unalias($record, null, 'authorid', 'author');
02811         }
02812     }
02813 
02821     public function add_action(moodle_url $url, $label, $method = 'get') {
02822 
02823         $action = new stdClass();
02824         $action->url = $url;
02825         $action->label = $label;
02826         $action->method = $method;
02827 
02828         $this->actions[] = $action;
02829     }
02830 
02837     protected function validate_raw_record(stdClass $record) {
02838         // nothing to do here
02839     }
02840 }
02841 
02842 
02846 class workshop_assessment extends workshop_assessment_base implements renderable {
02847 
02849     public $id;
02850 
02852     public $submissionid;
02853 
02855     public $weight;
02856 
02858     public $timecreated;
02859 
02861     public $timemodified;
02862 
02864     public $grade;
02865 
02867     public $gradinggrade;
02868 
02870     public $gradinggradeover;
02871 
02873     protected $fields = array('id', 'submissionid', 'weight', 'timecreated',
02874         'timemodified', 'grade', 'gradinggrade', 'gradinggradeover');
02875 }
02876 
02877 
02881 class workshop_example_assessment extends workshop_assessment implements renderable {
02882 
02886     protected function validate_raw_record(stdClass $record) {
02887         if ($record->weight != 0) {
02888             throw new coding_exception('Invalid weight of example submission assessment');
02889         }
02890         parent::validate_raw_record($record);
02891     }
02892 }
02893 
02894 
02898 class workshop_example_reference_assessment extends workshop_assessment implements renderable {
02899 
02903     protected function validate_raw_record(stdClass $record) {
02904         if ($record->weight != 1) {
02905             throw new coding_exception('Invalid weight of the reference example submission assessment');
02906         }
02907         parent::validate_raw_record($record);
02908     }
02909 }
02910 
02911 
02920 class workshop_message implements renderable {
02921 
02922     const TYPE_INFO     = 10;
02923     const TYPE_OK       = 20;
02924     const TYPE_ERROR    = 30;
02925 
02927     protected $text = '';
02929     protected $type = self::TYPE_INFO;
02931     protected $actionurl = null;
02933     protected $actionlabel = '';
02934 
02939     public function __construct($text = null, $type = self::TYPE_INFO) {
02940         $this->set_text($text);
02941         $this->set_type($type);
02942     }
02943 
02949     public function set_text($text) {
02950         $this->text = $text;
02951     }
02952 
02958     public function set_type($type = self::TYPE_INFO) {
02959         if (in_array($type, array(self::TYPE_OK, self::TYPE_ERROR, self::TYPE_INFO))) {
02960             $this->type = $type;
02961         } else {
02962             throw new coding_exception('Unknown message type.');
02963         }
02964     }
02965 
02972     public function set_action(moodle_url $url, $label) {
02973         $this->actionurl    = $url;
02974         $this->actionlabel  = $label;
02975     }
02976 
02982     public function get_message() {
02983         return s($this->text);
02984     }
02985 
02991     public function get_type() {
02992         return $this->type;
02993     }
02994 
03000     public function get_action_url() {
03001         return $this->actionurl;
03002     }
03003 
03009     public function get_action_label() {
03010         return $this->actionlabel;
03011     }
03012 }
03013 
03017 class workshop_allocation_init_result implements renderable {
03018 
03020     protected $message;
03022     protected $info = array();
03024     protected $continue;
03025 
03034     public function __construct($result, moodle_url $continue) {
03035 
03036         if ($result === workshop::ALLOCATION_ERROR) {
03037             $this->message = new workshop_message(get_string('allocationerror', 'workshop'), workshop_message::TYPE_ERROR);
03038         } else {
03039             $this->message = new workshop_message(get_string('allocationdone', 'workshop'), workshop_message::TYPE_OK);
03040             if (is_array($result)) {
03041                 $this->info = $result;
03042             }
03043         }
03044 
03045         $this->continue = $continue;
03046     }
03047 
03051     public function get_message() {
03052         return $this->message;
03053     }
03054 
03058     public function get_info() {
03059         return $this->info;
03060     }
03061 
03065     public function get_continue_url() {
03066         return $this->continue;
03067     }
03068 }
03069 
03073 class workshop_grading_report implements renderable {
03074 
03076     protected $data;
03078     protected $options;
03079 
03087     public function __construct(stdClass $data, stdClass $options) {
03088         $this->data     = $data;
03089         $this->options  = $options;
03090     }
03091 
03095     public function get_data() {
03096         return $this->data;
03097     }
03098 
03102     public function get_options() {
03103         return $this->options;
03104     }
03105 }
03106 
03107 
03111 abstract class workshop_feedback {
03112 
03114     protected $provider = null;
03115 
03117     protected $content = null;
03118 
03120     protected $format = null;
03121 
03125     public function get_provider() {
03126 
03127         if (is_null($this->provider)) {
03128             throw new coding_exception('Feedback provider not set');
03129         }
03130 
03131         return $this->provider;
03132     }
03133 
03137     public function get_content() {
03138 
03139         if (is_null($this->content)) {
03140             throw new coding_exception('Feedback content not set');
03141         }
03142 
03143         return $this->content;
03144     }
03145 
03149     public function get_format() {
03150 
03151         if (is_null($this->format)) {
03152             throw new coding_exception('Feedback text format not set');
03153         }
03154 
03155         return $this->format;
03156     }
03157 }
03158 
03159 
03163 class workshop_feedback_author extends workshop_feedback implements renderable {
03164 
03170     public function __construct(stdClass $submission) {
03171 
03172         $this->provider = user_picture::unalias($submission, null, 'gradeoverbyx', 'gradeoverby');
03173         $this->content  = $submission->feedbackauthor;
03174         $this->format   = $submission->feedbackauthorformat;
03175     }
03176 }
03177 
03178 
03182 class workshop_feedback_reviewer extends workshop_feedback implements renderable {
03183 
03189     public function __construct(stdClass $assessment) {
03190 
03191         $this->provider = user_picture::unalias($assessment, null, 'gradinggradeoverbyx', 'overby');
03192         $this->content  = $assessment->feedbackreviewer;
03193         $this->format   = $assessment->feedbackreviewerformat;
03194     }
03195 }
 All Data Structures Namespaces Files Functions Variables Enumerations