|
Moodle
2.2.1
http://www.collinsharper.com
|
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 }