community MDL-23170 fix user_can_edit
[moodle.git] / mod / workshop / locallib.php
CommitLineData
de811c0c 1<?php
53fad4b9
DM
2
3// This file is part of Moodle - http://moodle.org/
4//
de811c0c
DM
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
53fad4b9 14//
de811c0c
DM
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
53fad4b9 17
de811c0c 18/**
6e309973 19 * Library of internal classes and functions for module workshop
de811c0c 20 *
53fad4b9 21 * All the workshop specific functions, needed to implement the module
6e309973 22 * logic, should go to here. Instead of having bunch of function named
53fad4b9 23 * workshop_something() taking the workshop instance as the first
a39d7d87 24 * parameter, we use a class workshop that provides all methods.
53fad4b9 25 *
de811c0c
DM
26 * @package mod-workshop
27 * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
28 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29 */
30
31defined('MOODLE_INTERNAL') || die();
32
89c1aa97
DM
33require_once(dirname(__FILE__).'/lib.php'); // we extend this library here
34require_once($CFG->libdir . '/gradelib.php'); // we use some rounding and comparing routines here
0968b1a3 35
6e309973
DM
36/**
37 * Full-featured workshop API
38 *
a39d7d87 39 * This wraps the workshop database record with a set of methods that are called
6e309973 40 * from the module itself. The class should be initialized right after you get
a39d7d87 41 * $workshop, $cm and $course records at the begining of the script.
6e309973 42 */
a39d7d87
DM
43class workshop {
44
b761e6d9 45 /** return statuses of {@link add_allocation} to be passed to a workshop renderer method */
cbf87967
DM
46 const ALLOCATION_EXISTS = -9999;
47 const ALLOCATION_ERROR = -9998;
b761e6d9
DM
48
49 /** the internal code of the workshop phases as are stored in the database */
f6e8b318
DM
50 const PHASE_SETUP = 10;
51 const PHASE_SUBMISSION = 20;
52 const PHASE_ASSESSMENT = 30;
53 const PHASE_EVALUATION = 40;
54 const PHASE_CLOSED = 50;
55
56 /** the internal code of the examples modes as are stored in the database */
57 const EXAMPLES_VOLUNTARY = 0;
58 const EXAMPLES_BEFORE_SUBMISSION = 1;
59 const EXAMPLES_BEFORE_ASSESSMENT = 2;
b761e6d9 60
7a789aa8 61 /** @var stdclass course module record */
e9ab520f 62 public $cm;
a39d7d87 63
7a789aa8 64 /** @var stdclass course record */
e9ab520f 65 public $course;
6e309973 66
7a789aa8 67 /** @var stdclass context object */
e9ab520f
DM
68 public $context;
69
70 /** @var int workshop instance identifier */
71 public $id;
72
73 /** @var string workshop activity name */
74 public $name;
75
76 /** @var string introduction or description of the activity */
77 public $intro;
78
79 /** @var int format of the {@link $intro} */
80 public $introformat;
81
82 /** @var string instructions for the submission phase */
83 public $instructauthors;
84
85 /** @var int format of the {@link $instructauthors} */
86 public $instructauthorsformat;
87
88 /** @var string instructions for the assessment phase */
89 public $instructreviewers;
90
91 /** @var int format of the {@link $instructreviewers} */
92 public $instructreviewersformat;
93
94 /** @var int timestamp of when the module was modified */
95 public $timemodified;
96
97 /** @var int current phase of workshop, for example {@link workshop::PHASE_SETUP} */
98 public $phase;
99
100 /** @var bool optional feature: students practise evaluating on example submissions from teacher */
101 public $useexamples;
102
103 /** @var bool optional feature: students perform peer assessment of others' work */
104 public $usepeerassessment;
105
106 /** @var bool optional feature: students perform self assessment of their own work */
107 public $useselfassessment;
108
109 /** @var float number (10, 5) unsigned, the maximum grade for submission */
110 public $grade;
111
112 /** @var float number (10, 5) unsigned, the maximum grade for assessment */
113 public $gradinggrade;
114
115 /** @var string type of the current grading strategy used in this workshop, for example 'accumulative' */
116 public $strategy;
117
c2d2eb6e
DM
118 /** @var string the name of the evaluation plugin to use for grading grades calculation */
119 public $evaluation;
120
e9ab520f
DM
121 /** @var int number of digits that should be shown after the decimal point when displaying grades */
122 public $gradedecimals;
123
124 /** @var int number of allowed submission attachments and the files embedded into submission */
125 public $nattachments;
126
127 /** @var bool allow submitting the work after the deadline */
128 public $latesubmissions;
129
130 /** @var int maximum size of the one attached file in bytes */
131 public $maxbytes;
132
133 /** @var int mode of example submissions support, for example {@link workshop::EXAMPLES_VOLUNTARY} */
134 public $examplesmode;
135
136 /** @var int if greater than 0 then the submission is not allowed before this timestamp */
137 public $submissionstart;
138
139 /** @var int if greater than 0 then the submission is not allowed after this timestamp */
140 public $submissionend;
141
142 /** @var int if greater than 0 then the peer assessment is not allowed before this timestamp */
143 public $assessmentstart;
144
145 /** @var int if greater than 0 then the peer assessment is not allowed after this timestamp */
146 public $assessmentend;
147
b761e6d9
DM
148 /**
149 * @var workshop_strategy grading strategy instance
150 * Do not use directly, get the instance using {@link workshop::grading_strategy_instance()}
151 */
b13142da
DM
152 protected $strategyinstance = null;
153
45d24d39
DM
154 /**
155 * @var workshop_evaluation grading evaluation instance
156 * Do not use directly, get the instance using {@link workshop::grading_evaluation_instance()}
157 */
158 protected $evaluationinstance = null;
159
6e309973 160 /**
65ba104c 161 * Initializes the workshop API instance using the data from DB
a39d7d87
DM
162 *
163 * Makes deep copy of all passed records properties. Replaces integer $course attribute
164 * with a full database record (course should not be stored in instances table anyway).
6e309973 165 *
7a789aa8
DM
166 * @param stdclass $dbrecord Workshop instance data from {workshop} table
167 * @param stdclass $cm Course module record as returned by {@link get_coursemodule_from_id()}
168 * @param stdclass $course Course record from {course} table
169 * @param stdclass $context The context of the workshop instance
0dc47fb9 170 */
7a789aa8 171 public function __construct(stdclass $dbrecord, stdclass $cm, stdclass $course, stdclass $context=null) {
f05c168d 172 foreach ($dbrecord as $field => $value) {
ac239eba
DM
173 if (property_exists('workshop', $field)) {
174 $this->{$field} = $value;
175 }
a39d7d87 176 }
45d24d39 177 $this->cm = $cm;
ac239eba 178 $this->course = $course;
4efd7b5d
DM
179 if (is_null($context)) {
180 $this->context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
181 } else {
182 $this->context = $context;
183 }
184 $this->evaluation = 'best'; // todo make this configurable although we have no alternatives yet
6e309973
DM
185 }
186
aa40adbf
DM
187 ////////////////////////////////////////////////////////////////////////////////
188 // Static methods //
189 ////////////////////////////////////////////////////////////////////////////////
190
da0b1f70 191 /**
aa40adbf 192 * Return list of available allocation methods
da0b1f70 193 *
aa40adbf 194 * @return array Array ['string' => 'string'] of localized allocation method names
da0b1f70 195 */
aa40adbf
DM
196 public static function installed_allocators() {
197 $installed = get_plugin_list('workshopallocation');
198 $forms = array();
199 foreach ($installed as $allocation => $allocationpath) {
200 if (file_exists($allocationpath . '/lib.php')) {
201 $forms[$allocation] = get_string('pluginname', 'workshopallocation_' . $allocation);
202 }
f05c168d 203 }
aa40adbf
DM
204 // usability - make sure that manual allocation appears the first
205 if (isset($forms['manual'])) {
206 $m = array('manual' => $forms['manual']);
207 unset($forms['manual']);
208 $forms = array_merge($m, $forms);
da0b1f70 209 }
aa40adbf
DM
210 return $forms;
211 }
da0b1f70 212
aa40adbf
DM
213 /**
214 * Returns an array of options for the editors that are used for submitting and assessing instructions
215 *
7a789aa8 216 * @param stdclass $context
aa40adbf
DM
217 * @return array
218 */
7a789aa8 219 public static function instruction_editors_options(stdclass $context) {
aa40adbf
DM
220 return array('subdirs' => 1, 'maxbytes' => 0, 'maxfiles' => EDITOR_UNLIMITED_FILES,
221 'changeformat' => 1, 'context' => $context, 'noclean' => 1, 'trusttext' => 0);
da0b1f70
DM
222 }
223
61b737a5
DM
224 /**
225 * Given the percent and the total, returns the number
226 *
227 * @param float $percent from 0 to 100
228 * @param float $total the 100% value
229 * @return float
230 */
231 public static function percent_to_value($percent, $total) {
232 if ($percent < 0 or $percent > 100) {
233 throw new coding_exception('The percent can not be less than 0 or higher than 100');
234 }
235
236 return $total * $percent / 100;
237 }
238
f6e8b318
DM
239 /**
240 * Returns an array of numeric values that can be used as maximum grades
241 *
242 * @return array Array of integers
243 */
244 public static function available_maxgrades_list() {
245 $grades = array();
246 for ($i=100; $i>=0; $i--) {
247 $grades[$i] = $i;
248 }
249 return $grades;
250 }
251
252 /**
253 * Returns the localized list of supported examples modes
254 *
255 * @return array
256 */
257 public static function available_example_modes_list() {
258 $options = array();
259 $options[self::EXAMPLES_VOLUNTARY] = get_string('examplesvoluntary', 'workshop');
260 $options[self::EXAMPLES_BEFORE_SUBMISSION] = get_string('examplesbeforesubmission', 'workshop');
261 $options[self::EXAMPLES_BEFORE_ASSESSMENT] = get_string('examplesbeforeassessment', 'workshop');
262 return $options;
263 }
264
265 /**
266 * Returns the list of available grading strategy methods
267 *
268 * @return array ['string' => 'string']
269 */
270 public static function available_strategies_list() {
271 $installed = get_plugin_list('workshopform');
272 $forms = array();
273 foreach ($installed as $strategy => $strategypath) {
274 if (file_exists($strategypath . '/lib.php')) {
275 $forms[$strategy] = get_string('pluginname', 'workshopform_' . $strategy);
276 }
277 }
278 return $forms;
279 }
280
281 /**
282 * Return an array of possible values of assessment dimension weight
283 *
284 * @return array of integers 0, 1, 2, ..., 16
285 */
286 public static function available_dimension_weights_list() {
287 $weights = array();
288 for ($i=16; $i>=0; $i--) {
289 $weights[$i] = $i;
290 }
291 return $weights;
292 }
293
c6b784f0
DM
294 /**
295 * Return an array of possible values of assessment weight
296 *
297 * Note there is no real reason why the maximum value here is 16. It used to be 10 in
298 * workshop 1.x and I just decided to use the same number as in the maximum weight of
299 * a single assessment dimension.
300 * The value looks reasonable, though. Teachers who would want to assign themselves
301 * higher weight probably do not want peer assessment really...
302 *
303 * @return array of integers 0, 1, 2, ..., 16
304 */
305 public static function available_assessment_weights_list() {
306 $weights = array();
307 for ($i=16; $i>=0; $i--) {
308 $weights[$i] = $i;
309 }
310 return $weights;
311 }
312
f6e8b318
DM
313 /**
314 * Helper function returning the greatest common divisor
315 *
316 * @param int $a
317 * @param int $b
318 * @return int
319 */
320 public static function gcd($a, $b) {
321 return ($b == 0) ? ($a):(self::gcd($b, $a % $b));
322 }
323
324 /**
325 * Helper function returning the least common multiple
326 *
327 * @param int $a
328 * @param int $b
329 * @return int
330 */
331 public static function lcm($a, $b) {
332 return ($a / self::gcd($a,$b)) * $b;
333 }
334
5bab64a3
DM
335 /**
336 * Returns an object suitable for strings containing dates/times
337 *
338 * The returned object contains properties date, datefullshort, datetime, ... containing the given
339 * timestamp formatted using strftimedate, strftimedatefullshort, strftimedatetime, ... from the
340 * current lang's langconfig.php
341 * This allows translators and administrators customize the date/time format.
342 *
343 * @param int $timestamp the timestamp in UTC
344 * @return stdclass
345 */
346 public static function timestamp_formats($timestamp) {
347 $formats = array('date', 'datefullshort', 'dateshort', 'datetime',
348 'datetimeshort', 'daydate', 'daydatetime', 'dayshort', 'daytime',
349 'monthyear', 'recent', 'recentfull', 'time');
350 $a = new stdclass();
351 foreach ($formats as $format) {
352 $a->{$format} = userdate($timestamp, get_string('strftime'.$format, 'langconfig'));
353 }
354 $day = userdate($timestamp, '%Y%m%d', 99, false);
355 $today = userdate(time(), '%Y%m%d', 99, false);
356 $tomorrow = userdate(time() + DAYSECS, '%Y%m%d', 99, false);
357 $yesterday = userdate(time() - DAYSECS, '%Y%m%d', 99, false);
358 $distance = (int)round(abs(time() - $timestamp) / DAYSECS);
359 if ($day == $today) {
360 $a->distanceday = get_string('daystoday', 'workshop');
361 } elseif ($day == $yesterday) {
362 $a->distanceday = get_string('daysyesterday', 'workshop');
363 } elseif ($day < $today) {
364 $a->distanceday = get_string('daysago', 'workshop', $distance);
365 } elseif ($day == $tomorrow) {
366 $a->distanceday = get_string('daystomorrow', 'workshop');
367 } elseif ($day > $today) {
368 $a->distanceday = get_string('daysleft', 'workshop', $distance);
369 }
370 return $a;
371 }
372
aa40adbf
DM
373 ////////////////////////////////////////////////////////////////////////////////
374 // Workshop API //
375 ////////////////////////////////////////////////////////////////////////////////
376
6e309973
DM
377 /**
378 * Fetches all users with the capability mod/workshop:submit in the current context
379 *
3d2924e9 380 * The returned objects contain id, lastname and firstname properties and are ordered by lastname,firstname
53fad4b9 381 *
aa40adbf 382 * @todo handle with limits and groups
53fad4b9 383 * @param bool $musthavesubmission If true, return only users who have already submitted. All possible authors otherwise.
7a789aa8 384 * @return array array[userid] => stdclass{->id ->lastname ->firstname}
6e309973 385 */
d895c6aa
DM
386 public function get_potential_authors($musthavesubmission=true) {
387 $users = get_users_by_capability($this->context, 'mod/workshop:submit',
1fed6ce3 388 'u.id,u.lastname,u.firstname', 'u.lastname,u.firstname,u.id', '', '', '', '', false, false, true);
3d2924e9 389 if ($musthavesubmission) {
da0b1f70 390 $users = array_intersect_key($users, $this->users_with_submission(array_keys($users)));
66c9894d 391 }
da0b1f70 392 return $users;
6e309973
DM
393 }
394
6e309973
DM
395 /**
396 * Fetches all users with the capability mod/workshop:peerassess in the current context
397 *
b13142da 398 * The returned objects contain id, lastname and firstname properties and are ordered by lastname,firstname
53fad4b9 399 *
aa40adbf 400 * @todo handle with limits and groups
53fad4b9 401 * @param bool $musthavesubmission If true, return only users who have already submitted. All possible users otherwise.
7a789aa8 402 * @return array array[userid] => stdclass{->id ->lastname ->firstname}
6e309973 403 */
d895c6aa
DM
404 public function get_potential_reviewers($musthavesubmission=false) {
405 $users = get_users_by_capability($this->context, 'mod/workshop:peerassess',
1fed6ce3 406 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname,u.id', '', '', '', '', false, false, true);
3d2924e9
DM
407 if ($musthavesubmission) {
408 // users without their own submission can not be reviewers
da0b1f70 409 $users = array_intersect_key($users, $this->users_with_submission(array_keys($users)));
0968b1a3 410 }
da0b1f70 411 return $users;
0968b1a3
DM
412 }
413
b8ead2e6
DM
414 /**
415 * Groups the given users by the group membership
416 *
417 * This takes the module grouping settings into account. If "Available for group members only"
418 * is set, returns only groups withing the course module grouping. Always returns group [0] with
419 * all the given users.
420 *
7a789aa8
DM
421 * @param array $users array[userid] => stdclass{->id ->lastname ->firstname}
422 * @return array array[groupid][userid] => stdclass{->id ->lastname ->firstname}
53fad4b9 423 */
3d2924e9 424 public function get_grouped($users) {
53fad4b9 425 global $DB;
3d2924e9 426 global $CFG;
53fad4b9 427
b8ead2e6
DM
428 $grouped = array(); // grouped users to be returned
429 if (empty($users)) {
430 return $grouped;
a7c5b918 431 }
98da6021 432 if (!empty($CFG->enablegroupmembersonly) and $this->cm->groupmembersonly) {
53fad4b9
DM
433 // Available for group members only - the workshop is available only
434 // to users assigned to groups within the selected grouping, or to
435 // any group if no grouping is selected.
436 $groupingid = $this->cm->groupingid;
b8ead2e6 437 // All users that are members of at least one group will be
53fad4b9 438 // added into a virtual group id 0
b8ead2e6 439 $grouped[0] = array();
53fad4b9
DM
440 } else {
441 $groupingid = 0;
b8ead2e6
DM
442 // there is no need to be member of a group so $grouped[0] will contain
443 // all users
444 $grouped[0] = $users;
53fad4b9 445 }
b8ead2e6 446 $gmemberships = groups_get_all_groups($this->cm->course, array_keys($users), $groupingid,
53fad4b9
DM
447 'gm.id,gm.groupid,gm.userid');
448 foreach ($gmemberships as $gmembership) {
b8ead2e6
DM
449 if (!isset($grouped[$gmembership->groupid])) {
450 $grouped[$gmembership->groupid] = array();
53fad4b9 451 }
b8ead2e6
DM
452 $grouped[$gmembership->groupid][$gmembership->userid] = $users[$gmembership->userid];
453 $grouped[0][$gmembership->userid] = $users[$gmembership->userid];
53fad4b9 454 }
b8ead2e6 455 return $grouped;
53fad4b9 456 }
6e309973 457
aa40adbf
DM
458 /**
459 * Returns the list of all allocations (it est assigned assessments) in the workshop
460 *
461 * Assessments of example submissions are ignored
462 *
463 * @return array
464 */
465 public function get_allocations() {
466 global $DB;
467
00aca3c1 468 $sql = 'SELECT a.id, a.submissionid, a.reviewerid, s.authorid
aa40adbf
DM
469 FROM {workshop_assessments} a
470 INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
471 WHERE s.example = 0 AND s.workshopid = :workshopid';
472 $params = array('workshopid' => $this->id);
473
474 return $DB->get_records_sql($sql, $params);
475 }
476
6e309973
DM
477 /**
478 * Returns submissions from this workshop
479 *
3dc78e5b
DM
480 * Fetches data from {workshop_submissions} and adds some useful information from other
481 * tables. Does not return textual fields to prevent possible memory lack issues.
53fad4b9 482 *
00aca3c1 483 * @param mixed $authorid int|array|'all' If set to [array of] integer, return submission[s] of the given user[s] only
934329e5 484 * @return array of records or an empty array
6e309973 485 */
29dc43e7 486 public function get_submissions($authorid='all') {
6e309973
DM
487 global $DB;
488
00aca3c1 489 $sql = 'SELECT s.id, s.workshopid, s.example, s.authorid, s.timecreated, s.timemodified,
232175e4 490 s.title, s.grade, s.gradeover, s.gradeoverby, s.published,
00aca3c1 491 u.lastname AS authorlastname, u.firstname AS authorfirstname,
3a11c09f 492 u.picture AS authorpicture, u.imagealt AS authorimagealt, u.email AS authoremail,
00aca3c1 493 t.lastname AS overlastname, t.firstname AS overfirstname,
3a11c09f 494 t.picture AS overpicture, t.imagealt AS overimagealt, t.email AS overemail
3d2924e9 495 FROM {workshop_submissions} s
00aca3c1 496 INNER JOIN {user} u ON (s.authorid = u.id)
29dc43e7
DM
497 LEFT JOIN {user} t ON (s.gradeoverby = t.id)
498 WHERE s.example = 0 AND s.workshopid = :workshopid';
3d2924e9 499 $params = array('workshopid' => $this->id);
6e309973 500
00aca3c1 501 if ('all' === $authorid) {
3d2924e9 502 // no additional conditions
934329e5 503 } elseif (!empty($authorid)) {
00aca3c1
DM
504 list($usql, $uparams) = $DB->get_in_or_equal($authorid, SQL_PARAMS_NAMED);
505 $sql .= " AND authorid $usql";
6e309973 506 $params = array_merge($params, $uparams);
3d2924e9 507 } else {
934329e5
DM
508 // $authorid is empty
509 return array();
6e309973 510 }
3dc78e5b 511 $sql .= ' ORDER BY u.lastname, u.firstname';
6e309973 512
3dc78e5b 513 return $DB->get_records_sql($sql, $params);
6e309973
DM
514 }
515
51508f25
DM
516 /**
517 * Returns a submission record with the author's data
518 *
519 * @param int $id submission id
7a789aa8 520 * @return stdclass
51508f25
DM
521 */
522 public function get_submission_by_id($id) {
523 global $DB;
524
29dc43e7
DM
525 // we intentionally check the workshopid here, too, so the workshop can't touch submissions
526 // from other instances
51508f25
DM
527 $sql = 'SELECT s.*,
528 u.lastname AS authorlastname, u.firstname AS authorfirstname, u.id AS authorid,
3a11c09f 529 u.picture AS authorpicture, u.imagealt AS authorimagealt, u.email AS authoremail
51508f25 530 FROM {workshop_submissions} s
00aca3c1 531 INNER JOIN {user} u ON (s.authorid = u.id)
81eccf0a 532 WHERE s.example = 0 AND s.workshopid = :workshopid AND s.id = :id';
51508f25
DM
533 $params = array('workshopid' => $this->id, 'id' => $id);
534 return $DB->get_record_sql($sql, $params, MUST_EXIST);
535 }
536
53fad4b9 537 /**
3dc78e5b 538 * Returns a submission submitted by the given author
53fad4b9 539 *
3dc78e5b 540 * @param int $id author id
7a789aa8 541 * @return stdclass|false
53fad4b9 542 */
00aca3c1 543 public function get_submission_by_author($authorid) {
e9b0f0ab
DM
544 global $DB;
545
00aca3c1 546 if (empty($authorid)) {
53fad4b9
DM
547 return false;
548 }
3dc78e5b
DM
549 $sql = 'SELECT s.*,
550 u.lastname AS authorlastname, u.firstname AS authorfirstname, u.id AS authorid,
3a11c09f 551 u.picture AS authorpicture, u.imagealt AS authorimagealt, u.email AS authoremail
3dc78e5b 552 FROM {workshop_submissions} s
00aca3c1
DM
553 INNER JOIN {user} u ON (s.authorid = u.id)
554 WHERE s.example = 0 AND s.workshopid = :workshopid AND s.authorid = :authorid';
555 $params = array('workshopid' => $this->id, 'authorid' => $authorid);
3dc78e5b 556 return $DB->get_record_sql($sql, $params);
53fad4b9 557 }
6e309973 558
00bc77ee
DM
559 /**
560 * Returns published submissions with their authors data
561 *
562 * @return array of stdclass
563 */
564 public function get_published_submissions($orderby='finalgrade DESC') {
565 global $DB;
566
567 $sql = "SELECT s.id, s.authorid, s.timecreated, s.timemodified,
568 s.title, s.grade, s.gradeover, COALESCE(s.gradeover,s.grade) AS finalgrade,
569 u.lastname AS authorlastname, u.firstname AS authorfirstname, u.id AS authorid,
3a11c09f 570 u.picture AS authorpicture, u.imagealt AS authorimagealt, u.email AS authoremail
00bc77ee
DM
571 FROM {workshop_submissions} s
572 INNER JOIN {user} u ON (s.authorid = u.id)
573 WHERE s.example = 0 AND s.workshopid = :workshopid AND s.published = 1
574 ORDER BY $orderby";
575 $params = array('workshopid' => $this->id);
576 return $DB->get_records_sql($sql, $params);
577 }
578
81eccf0a
DM
579 /**
580 * Returns full record of the given example submission
581 *
582 * @param int $id example submission od
583 * @return object
584 */
585 public function get_example_by_id($id) {
586 global $DB;
587 return $DB->get_record('workshop_submissions',
588 array('id' => $id, 'workshopid' => $this->id, 'example' => 1), '*', MUST_EXIST);
589 }
590
cbf87967
DM
591 /**
592 * Returns the list of example submissions in this workshop with reference assessments attached
593 *
594 * @return array of objects or an empty array
595 * @see workshop::prepare_example_summary()
596 */
597 public function get_examples_for_manager() {
598 global $DB;
599
600 $sql = 'SELECT s.id, s.title,
601 a.id AS assessmentid, a.weight, a.grade, a.gradinggrade
602 FROM {workshop_submissions} s
603 LEFT JOIN {workshop_assessments} a ON (a.submissionid = s.id AND a.weight = 1)
604 WHERE s.example = 1 AND s.workshopid = :workshopid
605 ORDER BY s.title';
606 return $DB->get_records_sql($sql, array('workshopid' => $this->id));
607 }
608
609 /**
610 * Returns the list of all example submissions in this workshop with the information of assessments done by the given user
611 *
612 * @param int $reviewerid user id
613 * @return array of objects, indexed by example submission id
614 * @see workshop::prepare_example_summary()
615 */
616 public function get_examples_for_reviewer($reviewerid) {
617 global $DB;
618
619 if (empty($reviewerid)) {
620 return false;
621 }
622 $sql = 'SELECT s.id, s.title,
623 a.id AS assessmentid, a.weight, a.grade, a.gradinggrade
624 FROM {workshop_submissions} s
625 LEFT JOIN {workshop_assessments} a ON (a.submissionid = s.id AND a.reviewerid = :reviewerid AND a.weight = 0)
626 WHERE s.example = 1 AND s.workshopid = :workshopid
627 ORDER BY s.title';
628 return $DB->get_records_sql($sql, array('workshopid' => $this->id, 'reviewerid' => $reviewerid));
629 }
630
631 /**
632 * Prepares component containing summary of given example to be rendered
633 *
7a789aa8
DM
634 * @param stdclass $example as returned by {@link workshop::get_examples_for_manager()} or {@link workshop::get_examples_for_reviewer()}
635 * @return stdclass component to be rendered
cbf87967 636 */
7a789aa8 637 public function prepare_example_summary(stdclass $example) {
cbf87967 638
7a789aa8 639 $summary = new stdclass();
cbf87967
DM
640 $summary->example = $example;
641 if (is_null($example->grade)) {
642 $summary->status = 'notgraded';
643 $buttontext = get_string('assess', 'workshop');
644 } else {
645 $summary->status = 'graded';
646 $buttontext = get_string('reassess', 'workshop');
647 }
648
7a789aa8 649 $summary->gradeinfo = new stdclass();
cbf87967
DM
650 $summary->gradeinfo->received = $this->real_grade($example->grade);
651 $summary->gradeinfo->max = $this->real_grade(100);
652
3ba60ee1
PS
653 $aurl = new moodle_url($this->exsubmission_url($example->id), array('assess' => 'on', 'sesskey' => sesskey()));
654 $summary->btnform = new single_button($aurl, $buttontext, 'get');
cbf87967
DM
655
656 return $summary;
657 }
658
81eccf0a
DM
659 /**
660 * Removes the submission and all relevant data
661 *
7a789aa8 662 * @param stdclass $submission record to delete
81eccf0a
DM
663 * @return void
664 */
7a789aa8 665 public function delete_submission(stdclass $submission) {
81eccf0a
DM
666 global $DB;
667 $assessments = $DB->get_records('workshop_assessments', array('submissionid' => $submission->id), '', 'id');
668 $this->delete_assessment(array_keys($assessments));
669 $DB->delete_records('workshop_submissions', array('id' => $submission->id));
670 }
671
6e309973 672 /**
3dc78e5b 673 * Returns the list of all assessments in the workshop with some data added
6e309973
DM
674 *
675 * Fetches data from {workshop_assessments} and adds some useful information from other
3dc78e5b
DM
676 * tables. The returned object does not contain textual fields (ie comments) to prevent memory
677 * lack issues.
678 *
7a789aa8 679 * @return array [assessmentid] => assessment stdclass
6e309973 680 */
3dc78e5b 681 public function get_all_assessments() {
6e309973 682 global $DB;
53fad4b9 683
f6e8b318 684 $sql = 'SELECT a.id, a.submissionid, a.reviewerid, a.timecreated, a.timemodified,
3dc78e5b 685 a.grade, a.gradinggrade, a.gradinggradeover, a.gradinggradeoverby,
3d2924e9
DM
686 reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname,
687 s.title,
ddb59c77 688 author.id AS authorid, author.firstname AS authorfirstname,author.lastname AS authorlastname
3d2924e9 689 FROM {workshop_assessments} a
00aca3c1 690 INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
3d2924e9 691 INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
00aca3c1 692 INNER JOIN {user} author ON (s.authorid = author.id)
3dc78e5b
DM
693 WHERE s.workshopid = :workshopid AND s.example = 0
694 ORDER BY reviewer.lastname, reviewer.firstname';
3d2924e9
DM
695 $params = array('workshopid' => $this->id);
696
3dc78e5b 697 return $DB->get_records_sql($sql, $params);
53fad4b9
DM
698 }
699
700 /**
3dc78e5b 701 * Get the complete information about the given assessment
53fad4b9
DM
702 *
703 * @param int $id Assessment ID
5a372494 704 * @return stdclass
53fad4b9
DM
705 */
706 public function get_assessment_by_id($id) {
3dc78e5b
DM
707 global $DB;
708
709 $sql = 'SELECT a.*,
710 reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname,
711 s.title,
712 author.id AS authorid, author.firstname AS authorfirstname,author.lastname as authorlastname
713 FROM {workshop_assessments} a
00aca3c1 714 INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
3dc78e5b 715 INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
00aca3c1 716 INNER JOIN {user} author ON (s.authorid = author.id)
3dc78e5b
DM
717 WHERE a.id = :id AND s.workshopid = :workshopid';
718 $params = array('id' => $id, 'workshopid' => $this->id);
719
720 return $DB->get_record_sql($sql, $params, MUST_EXIST);
53fad4b9
DM
721 }
722
5a372494
DM
723 /**
724 * Get the complete information about the user's assessment of the given submission
725 *
726 * @param int $sid submission ID
727 * @param int $uid user ID of the reviewer
728 * @return false|stdclass false if not found, stdclass otherwise
729 */
730 public function get_assessment_of_submission_by_user($submissionid, $reviewerid) {
731 global $DB;
732
733 $sql = 'SELECT a.*,
734 reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname,
735 s.title,
736 author.id AS authorid, author.firstname AS authorfirstname,author.lastname as authorlastname
737 FROM {workshop_assessments} a
738 INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
739 INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id AND s.example = 0)
740 INNER JOIN {user} author ON (s.authorid = author.id)
741 WHERE s.id = :sid AND reviewer.id = :rid AND s.workshopid = :workshopid';
742 $params = array('sid' => $submissionid, 'rid' => $reviewerid, 'workshopid' => $this->id);
743
744 return $DB->get_record_sql($sql, $params, IGNORE_MISSING);
745 }
746
747 /**
748 * Get the complete information about all assessments of the given submission
749 *
750 * @param int $submissionid
751 * @return array
752 */
753 public function get_assessments_of_submission($submissionid) {
754 global $DB;
755
756 $sql = 'SELECT a.*,
757 reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname AS reviewerlastname
758 FROM {workshop_assessments} a
759 INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
760 INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
761 WHERE s.example = 0 AND s.id = :submissionid AND s.workshopid = :workshopid';
762 $params = array('submissionid' => $submissionid, 'workshopid' => $this->id);
763
764 return $DB->get_records_sql($sql, $params);
765 }
766
53fad4b9 767 /**
3dc78e5b 768 * Get the complete information about all assessments allocated to the given reviewer
53fad4b9 769 *
00aca3c1 770 * @param int $reviewerid
3dc78e5b 771 * @return array
53fad4b9 772 */
00aca3c1 773 public function get_assessments_by_reviewer($reviewerid) {
3dc78e5b
DM
774 global $DB;
775
776 $sql = 'SELECT a.*,
ddb59c77
DM
777 reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname AS reviewerlastname,
778 s.id AS submissionid, s.title AS submissiontitle, s.timecreated AS submissioncreated,
779 s.timemodified AS submissionmodified,
780 author.id AS authorid, author.firstname AS authorfirstname,author.lastname AS authorlastname,
3a11c09f 781 author.picture AS authorpicture, author.imagealt AS authorimagealt, author.email AS authoremail
3dc78e5b 782 FROM {workshop_assessments} a
00aca3c1 783 INNER JOIN {user} reviewer ON (a.reviewerid = reviewer.id)
3dc78e5b 784 INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
00aca3c1
DM
785 INNER JOIN {user} author ON (s.authorid = author.id)
786 WHERE s.example = 0 AND reviewer.id = :reviewerid AND s.workshopid = :workshopid';
787 $params = array('reviewerid' => $reviewerid, 'workshopid' => $this->id);
3dc78e5b
DM
788
789 return $DB->get_records_sql($sql, $params);
53fad4b9 790 }
6e309973 791
6e309973
DM
792 /**
793 * Allocate a submission to a user for review
53fad4b9 794 *
7a789aa8 795 * @param stdclass $submission Submission object with at least id property
6e309973 796 * @param int $reviewerid User ID
becec954 797 * @param int $weight of the new assessment, from 0 to 16
67ae13d9 798 * @param bool $bulk repeated inserts into DB expected
6e309973
DM
799 * @return int ID of the new assessment or an error code
800 */
67ae13d9 801 public function add_allocation(stdclass $submission, $reviewerid, $weight=1, $bulk=false) {
6e309973
DM
802 global $DB;
803
00aca3c1 804 if ($DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'reviewerid' => $reviewerid))) {
b761e6d9 805 return self::ALLOCATION_EXISTS;
6e309973
DM
806 }
807
67ae13d9
DM
808 $weight = (int)$weight;
809 if ($weight < 0) {
810 $weight = 0;
811 }
812 if ($weight > 16) {
813 $weight = 16;
814 }
815
6e309973 816 $now = time();
7a789aa8 817 $assessment = new stdclass();
e554671d
DM
818 $assessment->submissionid = $submission->id;
819 $assessment->reviewerid = $reviewerid;
820 $assessment->timecreated = $now;
821 $assessment->timemodified = $now;
becec954 822 $assessment->weight = $weight;
e554671d
DM
823 $assessment->generalcommentformat = FORMAT_HTML; // todo better default handling
824 $assessment->feedbackreviewerformat = FORMAT_HTML; // todo better default handling
6e309973 825
235b31c8 826 return $DB->insert_record('workshop_assessments', $assessment, true, $bulk);
6e309973
DM
827 }
828
6e309973 829 /**
53fad4b9 830 * Delete assessment record or records
6e309973 831 *
53fad4b9
DM
832 * @param mixed $id int|array assessment id or array of assessments ids
833 * @return bool false if $id not a valid parameter, true otherwise
6e309973
DM
834 */
835 public function delete_assessment($id) {
836 global $DB;
837
838 // todo remove all given grades from workshop_grades;
6e309973 839
53fad4b9 840 if (is_array($id)) {
235b31c8 841 return $DB->delete_records_list('workshop_assessments', 'id', $id);
3d2924e9 842 } else {
235b31c8 843 return $DB->delete_records('workshop_assessments', array('id' => $id));
53fad4b9 844 }
53fad4b9 845 }
6e309973
DM
846
847 /**
848 * Returns instance of grading strategy class
53fad4b9 849 *
7a789aa8 850 * @return stdclass Instance of a grading strategy
6e309973
DM
851 */
852 public function grading_strategy_instance() {
3d2924e9
DM
853 global $CFG; // because we require other libs here
854
3fd2b0e1 855 if (is_null($this->strategyinstance)) {
f05c168d 856 $strategylib = dirname(__FILE__) . '/form/' . $this->strategy . '/lib.php';
6e309973
DM
857 if (is_readable($strategylib)) {
858 require_once($strategylib);
859 } else {
f05c168d 860 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
6e309973 861 }
0dc47fb9 862 $classname = 'workshop_' . $this->strategy . '_strategy';
3fd2b0e1
DM
863 $this->strategyinstance = new $classname($this);
864 if (!in_array('workshop_strategy', class_implements($this->strategyinstance))) {
b761e6d9 865 throw new coding_exception($classname . ' does not implement workshop_strategy interface');
6e309973
DM
866 }
867 }
3fd2b0e1 868 return $this->strategyinstance;
6e309973
DM
869 }
870
45d24d39
DM
871 /**
872 * Returns instance of grading evaluation class
873 *
7a789aa8 874 * @return stdclass Instance of a grading evaluation
45d24d39
DM
875 */
876 public function grading_evaluation_instance() {
877 global $CFG; // because we require other libs here
878
879 if (is_null($this->evaluationinstance)) {
880 $evaluationlib = dirname(__FILE__) . '/eval/' . $this->evaluation . '/lib.php';
881 if (is_readable($evaluationlib)) {
882 require_once($evaluationlib);
883 } else {
884 throw new coding_exception('the grading evaluation subplugin must contain library ' . $evaluationlib);
885 }
886 $classname = 'workshop_' . $this->evaluation . '_evaluation';
887 $this->evaluationinstance = new $classname($this);
888 if (!in_array('workshop_evaluation', class_implements($this->evaluationinstance))) {
889 throw new coding_exception($classname . ' does not implement workshop_evaluation interface');
890 }
891 }
892 return $this->evaluationinstance;
893 }
894
66c9894d
DM
895 /**
896 * Returns instance of submissions allocator
53fad4b9 897 *
130ae619 898 * @param string $method The name of the allocation method, must be PARAM_ALPHA
7a789aa8 899 * @return stdclass Instance of submissions allocator
66c9894d
DM
900 */
901 public function allocator_instance($method) {
3d2924e9
DM
902 global $CFG; // because we require other libs here
903
f05c168d 904 $allocationlib = dirname(__FILE__) . '/allocation/' . $method . '/lib.php';
66c9894d
DM
905 if (is_readable($allocationlib)) {
906 require_once($allocationlib);
907 } else {
f05c168d 908 throw new coding_exception('Unable to find the allocation library ' . $allocationlib);
66c9894d
DM
909 }
910 $classname = 'workshop_' . $method . '_allocator';
911 return new $classname($this);
912 }
913
b8ead2e6 914 /**
454e8dd9 915 * @return moodle_url of this workshop's view page
b8ead2e6
DM
916 */
917 public function view_url() {
918 global $CFG;
a6855934 919 return new moodle_url('/mod/workshop/view.php', array('id' => $this->cm->id));
b8ead2e6
DM
920 }
921
922 /**
454e8dd9 923 * @return moodle_url of the page for editing this workshop's grading form
b8ead2e6
DM
924 */
925 public function editform_url() {
926 global $CFG;
a6855934 927 return new moodle_url('/mod/workshop/editform.php', array('cmid' => $this->cm->id));
b8ead2e6
DM
928 }
929
930 /**
454e8dd9 931 * @return moodle_url of the page for previewing this workshop's grading form
b8ead2e6
DM
932 */
933 public function previewform_url() {
934 global $CFG;
a6855934 935 return new moodle_url('/mod/workshop/editformpreview.php', array('cmid' => $this->cm->id));
b8ead2e6
DM
936 }
937
938 /**
939 * @param int $assessmentid The ID of assessment record
454e8dd9 940 * @return moodle_url of the assessment page
b8ead2e6 941 */
a39d7d87 942 public function assess_url($assessmentid) {
b8ead2e6 943 global $CFG;
454e8dd9 944 $assessmentid = clean_param($assessmentid, PARAM_INT);
a6855934 945 return new moodle_url('/mod/workshop/assessment.php', array('asid' => $assessmentid));
b8ead2e6
DM
946 }
947
becec954
DM
948 /**
949 * @param int $assessmentid The ID of assessment record
950 * @return moodle_url of the example assessment page
951 */
952 public function exassess_url($assessmentid) {
953 global $CFG;
954 $assessmentid = clean_param($assessmentid, PARAM_INT);
a6855934 955 return new moodle_url('/mod/workshop/exassessment.php', array('asid' => $assessmentid));
becec954
DM
956 }
957
39861053 958 /**
67cd00ba 959 * @return moodle_url of the page to view a submission, defaults to the own one
39861053 960 */
67cd00ba 961 public function submission_url($id=null) {
39861053 962 global $CFG;
a6855934 963 return new moodle_url('/mod/workshop/submission.php', array('cmid' => $this->cm->id, 'id' => $id));
39861053
DM
964 }
965
81eccf0a
DM
966 /**
967 * @param int $id example submission id
968 * @return moodle_url of the page to view an example submission
969 */
becec954 970 public function exsubmission_url($id) {
81eccf0a 971 global $CFG;
a6855934 972 return new moodle_url('/mod/workshop/exsubmission.php', array('cmid' => $this->cm->id, 'id' => $id));
81eccf0a
DM
973 }
974
cbf87967
DM
975 /**
976 * @param int $sid submission id
977 * @param array $aid of int assessment ids
978 * @return moodle_url of the page to compare assessments of the given submission
979 */
980 public function compare_url($sid, array $aids) {
981 global $CFG;
982
a6855934 983 $url = new moodle_url('/mod/workshop/compare.php', array('cmid' => $this->cm->id, 'sid' => $sid));
cbf87967
DM
984 $i = 0;
985 foreach ($aids as $aid) {
986 $url->param("aid{$i}", $aid);
987 $i++;
988 }
989 return $url;
990 }
991
992 /**
993 * @param int $sid submission id
994 * @param int $aid assessment id
995 * @return moodle_url of the page to compare the reference assessments of the given example submission
996 */
997 public function excompare_url($sid, $aid) {
998 global $CFG;
a6855934 999 return new moodle_url('/mod/workshop/excompare.php', array('cmid' => $this->cm->id, 'sid' => $sid, 'aid' => $aid));
cbf87967
DM
1000 }
1001
da0b1f70 1002 /**
454e8dd9 1003 * @return moodle_url of the mod_edit form
da0b1f70
DM
1004 */
1005 public function updatemod_url() {
1006 global $CFG;
a6855934 1007 return new moodle_url('/course/modedit.php', array('update' => $this->cm->id, 'return' => 1));
da0b1f70
DM
1008 }
1009
454e8dd9 1010 /**
08af32af 1011 * @param string $method allocation method
454e8dd9
DM
1012 * @return moodle_url to the allocation page
1013 */
08af32af 1014 public function allocation_url($method=null) {
da0b1f70 1015 global $CFG;
08af32af
DM
1016 $params = array('cmid' => $this->cm->id);
1017 if (!empty($method)) {
1018 $params['method'] = $method;
1019 }
1020 return new moodle_url('/mod/workshop/allocation.php', $params);
da0b1f70
DM
1021 }
1022
454e8dd9
DM
1023 /**
1024 * @param int $phasecode The internal phase code
1025 * @return moodle_url of the script to change the current phase to $phasecode
1026 */
1027 public function switchphase_url($phasecode) {
1028 global $CFG;
1029 $phasecode = clean_param($phasecode, PARAM_INT);
a6855934 1030 return new moodle_url('/mod/workshop/switchphase.php', array('cmid' => $this->cm->id, 'phase' => $phasecode));
454e8dd9
DM
1031 }
1032
89c1aa97
DM
1033 /**
1034 * @return moodle_url to the aggregation page
1035 */
1036 public function aggregate_url() {
1037 global $CFG;
a6855934 1038 return new moodle_url('/mod/workshop/aggregate.php', array('cmid' => $this->cm->id));
89c1aa97
DM
1039 }
1040
b8ead2e6 1041 /**
407b1e91
DM
1042 * Are users allowed to create/edit their submissions?
1043 *
407b1e91 1044 * @return bool
b8ead2e6 1045 */
407b1e91 1046 public function submitting_allowed() {
74bf8a94
DM
1047 if ($this->phase != self::PHASE_SUBMISSION) {
1048 // submitting is not allowed but in the submission phase
1049 return false;
1050 }
1051 $now = time();
1052 if (!empty($this->submissionstart) and $this->submissionstart > $now) {
1053 // if enabled, submitting is not allowed before the date/time defined in the mod_form
1054 return false;
1055 }
1056 if (!empty($this->submissionend) and empty($this->latesubmissions) and $now > $this->submissionend ) {
1057 // if enabled, submitting is not allowed after the date/time defined in the mod_form unless late submission is allowed
1058 return false;
1059 }
1060 // here we go, submission is allowed
407b1e91 1061 return true;
b8ead2e6
DM
1062 }
1063
c1e883bb 1064 /**
407b1e91 1065 * Are reviewers allowed to create/edit their assessments?
c1e883bb 1066 *
c1e883bb
DM
1067 * @return bool
1068 */
407b1e91 1069 public function assessing_allowed() {
74bf8a94
DM
1070 if ($this->phase != self::PHASE_ASSESSMENT) {
1071 // assessing is not allowed but in the assessment phase
1072 return false;
1073 }
1074 $now = time();
1075 if (!empty($this->assessmentstart) and $this->assessmentstart > $now) {
1076 // if enabled, assessing is not allowed before the date/time defined in the mod_form
1077 return false;
1078 }
1079 if (!empty($this->assessmentend) and $now > $this->assessmentend ) {
1080 // if enabled, assessing is not allowed after the date/time defined in the mod_form
1081 return false;
1082 }
1083 // here we go, assessing is allowed
c1e883bb
DM
1084 return true;
1085 }
1086
becec954
DM
1087 /**
1088 * Are reviewers allowed to create/edit their assessments of the example submissions?
1089 *
514d8c22
DM
1090 * Returns null if example submissions are not enabled in this workshop. Otherwise returns
1091 * true or false. Note this does not check other conditions like the number of already
1092 * assessed examples, examples mode etc.
becec954 1093 *
74bf8a94 1094 * @return null|bool
becec954
DM
1095 */
1096 public function assessing_examples_allowed() {
74bf8a94
DM
1097 if (empty($this->useexamples)) {
1098 return null;
1099 }
1100 if (self::EXAMPLES_VOLUNTARY == $this->examplesmode) {
1101 return true;
1102 }
1103 if (self::EXAMPLES_BEFORE_SUBMISSION == $this->examplesmode and self::PHASE_SUBMISSION == $this->phase) {
1104 return true;
1105 }
1106 if (self::EXAMPLES_BEFORE_ASSESSMENT == $this->examplesmode and self::PHASE_ASSESSMENT == $this->phase) {
1107 return true;
1108 }
1109 return false;
becec954 1110 }
407b1e91 1111
3dc78e5b
DM
1112 /**
1113 * Are the peer-reviews available to the authors?
1114 *
3dc78e5b
DM
1115 * @return bool
1116 */
1117 public function assessments_available() {
5a372494 1118 return $this->phase == self::PHASE_CLOSED;
3dc78e5b
DM
1119 }
1120
454e8dd9
DM
1121 /**
1122 * Switch to a new workshop phase
1123 *
1124 * Modifies the underlying database record. You should terminate the script shortly after calling this.
1125 *
1126 * @param int $newphase new phase code
1127 * @return bool true if success, false otherwise
1128 */
1129 public function switch_phase($newphase) {
1130 global $DB;
1131
365c2cc2 1132 $known = $this->available_phases_list();
454e8dd9
DM
1133 if (!isset($known[$newphase])) {
1134 return false;
1135 }
f6e8b318
DM
1136
1137 if (self::PHASE_CLOSED == $newphase) {
f27b70fb 1138 // push the grades into the gradebook
7a789aa8 1139 $workshop = new stdclass();
10bc4bce
DM
1140 foreach ($this as $property => $value) {
1141 $workshop->{$property} = $value;
1142 }
1143 $workshop->course = $this->course->id;
1144 $workshop->cmidnumber = $this->cm->id;
1145 $workshop->modname = 'workshop';
1146 workshop_update_grades($workshop);
f6e8b318
DM
1147 }
1148
454e8dd9
DM
1149 $DB->set_field('workshop', 'phase', $newphase, array('id' => $this->id));
1150 return true;
1151 }
ddb59c77
DM
1152
1153 /**
1154 * Saves a raw grade for submission as calculated from the assessment form fields
1155 *
1156 * @param array $assessmentid assessment record id, must exists
00aca3c1 1157 * @param mixed $grade raw percentual grade from 0.00000 to 100.00000
ddb59c77
DM
1158 * @return false|float the saved grade
1159 */
1160 public function set_peer_grade($assessmentid, $grade) {
1161 global $DB;
1162
1163 if (is_null($grade)) {
1164 return false;
1165 }
7a789aa8 1166 $data = new stdclass();
ddb59c77
DM
1167 $data->id = $assessmentid;
1168 $data->grade = $grade;
1169 $DB->update_record('workshop_assessments', $data);
1170 return $grade;
1171 }
6516b9e9 1172
29dc43e7
DM
1173 /**
1174 * Prepares data object with all workshop grades to be rendered
1175 *
5e71cefb
DM
1176 * @param int $userid the user we are preparing the report for
1177 * @param mixed $groups single group or array of groups - only show users who are in one of these group(s). Defaults to all
29dc43e7 1178 * @param int $page the current page (for the pagination)
5e71cefb 1179 * @param int $perpage participants per page (for the pagination)
f27b70fb 1180 * @param string $sortby lastname|firstname|submissiontitle|submissiongrade|gradinggrade
5e71cefb 1181 * @param string $sorthow ASC|DESC
7a789aa8 1182 * @return stdclass data for the renderer
29dc43e7 1183 */
d895c6aa 1184 public function prepare_grading_report($userid, $groups, $page, $perpage, $sortby, $sorthow) {
29dc43e7
DM
1185 global $DB;
1186
d895c6aa
DM
1187 $canviewall = has_capability('mod/workshop:viewallassessments', $this->context, $userid);
1188 $isparticipant = has_any_capability(array('mod/workshop:submit', 'mod/workshop:peerassess'), $this->context, $userid);
29dc43e7
DM
1189
1190 if (!$canviewall and !$isparticipant) {
1191 // who the hell is this?
1192 return array();
1193 }
1194
f27b70fb 1195 if (!in_array($sortby, array('lastname','firstname','submissiontitle','submissiongrade','gradinggrade'))) {
5e71cefb
DM
1196 $sortby = 'lastname';
1197 }
1198
1199 if (!($sorthow === 'ASC' or $sorthow === 'DESC')) {
1200 $sorthow = 'ASC';
1201 }
1202
1203 // get the list of user ids to be displayed
29dc43e7
DM
1204 if ($canviewall) {
1205 // fetch the list of ids of all workshop participants - this may get really long so fetch just id
d895c6aa 1206 $participants = get_users_by_capability($this->context, array('mod/workshop:submit', 'mod/workshop:peerassess'),
5e71cefb 1207 'u.id', '', '', '', $groups, '', false, false, true);
29dc43e7
DM
1208 } else {
1209 // this is an ordinary workshop participant (aka student) - display the report just for him/her
1210 $participants = array($userid => (object)array('id' => $userid));
1211 }
1212
5e71cefb 1213 // we will need to know the number of all records later for the pagination purposes
29dc43e7
DM
1214 $numofparticipants = count($participants);
1215
deea6e7a
DM
1216 if ($numofparticipants > 0) {
1217 // load all fields which can be used for sorting and paginate the records
1218 list($participantids, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED);
1219 $params['workshopid1'] = $this->id;
1220 $params['workshopid2'] = $this->id;
1221 $sqlsort = $sortby . ' ' . $sorthow . ',u.lastname,u.firstname,u.id';
3a11c09f 1222 $sql = "SELECT u.id AS userid,u.firstname,u.lastname,u.picture,u.imagealt,u.email,
deea6e7a
DM
1223 s.title AS submissiontitle, s.grade AS submissiongrade, ag.gradinggrade
1224 FROM {user} u
1225 LEFT JOIN {workshop_submissions} s ON (s.authorid = u.id AND s.workshopid = :workshopid1 AND s.example = 0)
1226 LEFT JOIN {workshop_aggregations} ag ON (ag.userid = u.id AND ag.workshopid = :workshopid2)
1227 WHERE u.id $participantids
1228 ORDER BY $sqlsort";
1229 $participants = $DB->get_records_sql($sql, $params, $page * $perpage, $perpage);
1230 } else {
1231 $participants = array();
1232 }
29dc43e7
DM
1233
1234 // this will hold the information needed to display user names and pictures
5e71cefb
DM
1235 $userinfo = array();
1236
1237 // get the user details for all participants to display
1238 foreach ($participants as $participant) {
1239 if (!isset($userinfo[$participant->userid])) {
7a789aa8 1240 $userinfo[$participant->userid] = new stdclass();
5e71cefb
DM
1241 $userinfo[$participant->userid]->id = $participant->userid;
1242 $userinfo[$participant->userid]->firstname = $participant->firstname;
1243 $userinfo[$participant->userid]->lastname = $participant->lastname;
1244 $userinfo[$participant->userid]->picture = $participant->picture;
1245 $userinfo[$participant->userid]->imagealt = $participant->imagealt;
3a11c09f 1246 $userinfo[$participant->userid]->email = $participant->email;
5e71cefb
DM
1247 }
1248 }
29dc43e7 1249
5e71cefb 1250 // load the submissions details
29dc43e7 1251 $submissions = $this->get_submissions(array_keys($participants));
5e71cefb
DM
1252
1253 // get the user details for all moderators (teachers) that have overridden a submission grade
29dc43e7 1254 foreach ($submissions as $submission) {
29dc43e7 1255 if (!isset($userinfo[$submission->gradeoverby])) {
7a789aa8 1256 $userinfo[$submission->gradeoverby] = new stdclass();
29dc43e7
DM
1257 $userinfo[$submission->gradeoverby]->id = $submission->gradeoverby;
1258 $userinfo[$submission->gradeoverby]->firstname = $submission->overfirstname;
1259 $userinfo[$submission->gradeoverby]->lastname = $submission->overlastname;
1260 $userinfo[$submission->gradeoverby]->picture = $submission->overpicture;
1261 $userinfo[$submission->gradeoverby]->imagealt = $submission->overimagealt;
3a11c09f 1262 $userinfo[$submission->gradeoverby]->email = $submission->overemail;
29dc43e7
DM
1263 }
1264 }
1265
5e71cefb 1266 // get the user details for all reviewers of the displayed participants
29dc43e7
DM
1267 $reviewers = array();
1268 if ($submissions) {
1269 list($submissionids, $params) = $DB->get_in_or_equal(array_keys($submissions), SQL_PARAMS_NAMED);
581878b8 1270 $sql = "SELECT a.id AS assessmentid, a.submissionid, a.grade, a.gradinggrade, a.gradinggradeover, a.weight,
3a11c09f 1271 r.id AS reviewerid, r.lastname, r.firstname, r.picture, r.imagealt, r.email,
29dc43e7
DM
1272 s.id AS submissionid, s.authorid
1273 FROM {workshop_assessments} a
1274 JOIN {user} r ON (a.reviewerid = r.id)
0324b6f1 1275 JOIN {workshop_submissions} s ON (a.submissionid = s.id AND s.example = 0)
c6b784f0
DM
1276 WHERE a.submissionid $submissionids
1277 ORDER BY a.weight DESC, r.lastname, r.firstname";
29dc43e7
DM
1278 $reviewers = $DB->get_records_sql($sql, $params);
1279 foreach ($reviewers as $reviewer) {
1280 if (!isset($userinfo[$reviewer->reviewerid])) {
7a789aa8 1281 $userinfo[$reviewer->reviewerid] = new stdclass();
29dc43e7
DM
1282 $userinfo[$reviewer->reviewerid]->id = $reviewer->reviewerid;
1283 $userinfo[$reviewer->reviewerid]->firstname = $reviewer->firstname;
1284 $userinfo[$reviewer->reviewerid]->lastname = $reviewer->lastname;
1285 $userinfo[$reviewer->reviewerid]->picture = $reviewer->picture;
1286 $userinfo[$reviewer->reviewerid]->imagealt = $reviewer->imagealt;
3a11c09f 1287 $userinfo[$reviewer->reviewerid]->email = $reviewer->email;
29dc43e7
DM
1288 }
1289 }
1290 }
1291
5e71cefb 1292 // get the user details for all reviewees of the displayed participants
934329e5
DM
1293 $reviewees = array();
1294 if ($participants) {
1295 list($participantids, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED);
1296 $params['workshopid'] = $this->id;
581878b8 1297 $sql = "SELECT a.id AS assessmentid, a.submissionid, a.grade, a.gradinggrade, a.gradinggradeover, a.reviewerid, a.weight,
934329e5 1298 s.id AS submissionid,
3a11c09f 1299 e.id AS authorid, e.lastname, e.firstname, e.picture, e.imagealt, e.email
934329e5
DM
1300 FROM {user} u
1301 JOIN {workshop_assessments} a ON (a.reviewerid = u.id)
0324b6f1 1302 JOIN {workshop_submissions} s ON (a.submissionid = s.id AND s.example = 0)
934329e5 1303 JOIN {user} e ON (s.authorid = e.id)
c6b784f0
DM
1304 WHERE u.id $participantids AND s.workshopid = :workshopid
1305 ORDER BY a.weight DESC, e.lastname, e.firstname";
934329e5
DM
1306 $reviewees = $DB->get_records_sql($sql, $params);
1307 foreach ($reviewees as $reviewee) {
1308 if (!isset($userinfo[$reviewee->authorid])) {
7a789aa8 1309 $userinfo[$reviewee->authorid] = new stdclass();
934329e5
DM
1310 $userinfo[$reviewee->authorid]->id = $reviewee->authorid;
1311 $userinfo[$reviewee->authorid]->firstname = $reviewee->firstname;
1312 $userinfo[$reviewee->authorid]->lastname = $reviewee->lastname;
1313 $userinfo[$reviewee->authorid]->picture = $reviewee->picture;
1314 $userinfo[$reviewee->authorid]->imagealt = $reviewee->imagealt;
3a11c09f 1315 $userinfo[$reviewee->authorid]->email = $reviewee->email;
934329e5 1316 }
29dc43e7
DM
1317 }
1318 }
1319
5e71cefb
DM
1320 // finally populate the object to be rendered
1321 $grades = $participants;
29dc43e7
DM
1322
1323 foreach ($participants as $participant) {
1324 // set up default (null) values
d183140d
DM
1325 $grades[$participant->userid]->submissionid = null;
1326 $grades[$participant->userid]->submissiontitle = null;
1327 $grades[$participant->userid]->submissiongrade = null;
1328 $grades[$participant->userid]->submissiongradeover = null;
1329 $grades[$participant->userid]->submissiongradeoverby = null;
232175e4 1330 $grades[$participant->userid]->submissionpublished = null;
5e71cefb
DM
1331 $grades[$participant->userid]->reviewedby = array();
1332 $grades[$participant->userid]->reviewerof = array();
29dc43e7
DM
1333 }
1334 unset($participants);
1335 unset($participant);
1336
1337 foreach ($submissions as $submission) {
1338 $grades[$submission->authorid]->submissionid = $submission->id;
1339 $grades[$submission->authorid]->submissiontitle = $submission->title;
b4857acb
DM
1340 $grades[$submission->authorid]->submissiongrade = $this->real_grade($submission->grade);
1341 $grades[$submission->authorid]->submissiongradeover = $this->real_grade($submission->gradeover);
29dc43e7 1342 $grades[$submission->authorid]->submissiongradeoverby = $submission->gradeoverby;
232175e4 1343 $grades[$submission->authorid]->submissionpublished = $submission->published;
29dc43e7
DM
1344 }
1345 unset($submissions);
1346 unset($submission);
1347
1348 foreach($reviewers as $reviewer) {
7a789aa8 1349 $info = new stdclass();
29dc43e7
DM
1350 $info->userid = $reviewer->reviewerid;
1351 $info->assessmentid = $reviewer->assessmentid;
1352 $info->submissionid = $reviewer->submissionid;
b4857acb
DM
1353 $info->grade = $this->real_grade($reviewer->grade);
1354 $info->gradinggrade = $this->real_grading_grade($reviewer->gradinggrade);
1355 $info->gradinggradeover = $this->real_grading_grade($reviewer->gradinggradeover);
581878b8 1356 $info->weight = $reviewer->weight;
29dc43e7
DM
1357 $grades[$reviewer->authorid]->reviewedby[$reviewer->reviewerid] = $info;
1358 }
1359 unset($reviewers);
1360 unset($reviewer);
1361
1362 foreach($reviewees as $reviewee) {
7a789aa8 1363 $info = new stdclass();
29dc43e7
DM
1364 $info->userid = $reviewee->authorid;
1365 $info->assessmentid = $reviewee->assessmentid;
1366 $info->submissionid = $reviewee->submissionid;
b4857acb
DM
1367 $info->grade = $this->real_grade($reviewee->grade);
1368 $info->gradinggrade = $this->real_grading_grade($reviewee->gradinggrade);
1369 $info->gradinggradeover = $this->real_grading_grade($reviewee->gradinggradeover);
581878b8 1370 $info->weight = $reviewee->weight;
29dc43e7
DM
1371 $grades[$reviewee->reviewerid]->reviewerof[$reviewee->authorid] = $info;
1372 }
1373 unset($reviewees);
1374 unset($reviewee);
1375
b4857acb
DM
1376 foreach ($grades as $grade) {
1377 $grade->gradinggrade = $this->real_grading_grade($grade->gradinggrade);
b4857acb
DM
1378 }
1379
7a789aa8 1380 $data = new stdclass();
29dc43e7
DM
1381 $data->grades = $grades;
1382 $data->userinfo = $userinfo;
1383 $data->totalcount = $numofparticipants;
b4857acb
DM
1384 $data->maxgrade = $this->real_grade(100);
1385 $data->maxgradinggrade = $this->real_grading_grade(100);
29dc43e7
DM
1386 return $data;
1387 }
1388
29dc43e7 1389 /**
b4857acb 1390 * Calculates the real value of a grade
29dc43e7 1391 *
b4857acb
DM
1392 * @param float $value percentual value from 0 to 100
1393 * @param float $max the maximal grade
1394 * @return string
1395 */
1396 public function real_grade_value($value, $max) {
1397 $localized = true;
557a1100 1398 if (is_null($value) or $value === '') {
b4857acb
DM
1399 return null;
1400 } elseif ($max == 0) {
1401 return 0;
1402 } else {
1403 return format_float($max * $value / 100, $this->gradedecimals, $localized);
1404 }
1405 }
1406
e554671d
DM
1407 /**
1408 * Calculates the raw (percentual) value from a real grade
1409 *
1410 * This is used in cases when a user wants to give a grade such as 12 of 20 and we need to save
1411 * this value in a raw percentual form into DB
1412 * @param float $value given grade
1413 * @param float $max the maximal grade
1414 * @return float suitable to be stored as numeric(10,5)
1415 */
1416 public function raw_grade_value($value, $max) {
557a1100 1417 if (is_null($value) or $value === '') {
e554671d
DM
1418 return null;
1419 }
1420 if ($max == 0 or $value < 0) {
1421 return 0;
1422 }
1423 $p = $value / $max * 100;
1424 if ($p > 100) {
1425 return $max;
1426 }
1427 return grade_floatval($p);
1428 }
1429
b4857acb
DM
1430 /**
1431 * Calculates the real value of grade for submission
1432 *
1433 * @param float $value percentual value from 0 to 100
1434 * @return string
1435 */
1436 public function real_grade($value) {
1437 return $this->real_grade_value($value, $this->grade);
1438 }
1439
1440 /**
1441 * Calculates the real value of grade for assessment
1442 *
1443 * @param float $value percentual value from 0 to 100
1444 * @return string
1445 */
1446 public function real_grading_grade($value) {
1447 return $this->real_grade_value($value, $this->gradinggrade);
29dc43e7
DM
1448 }
1449
89c1aa97 1450 /**
e9a90e69 1451 * Calculates grades for submission for the given participant(s) and updates it in the database
89c1aa97
DM
1452 *
1453 * @param null|int|array $restrict If null, update all authors, otherwise update just grades for the given author(s)
1454 * @return void
1455 */
8a1ba8ac 1456 public function aggregate_submission_grades($restrict=null) {
89c1aa97
DM
1457 global $DB;
1458
1459 // fetch a recordset with all assessments to process
1696f36c 1460 $sql = 'SELECT s.id AS submissionid, s.grade AS submissiongrade,
89c1aa97
DM
1461 a.weight, a.grade
1462 FROM {workshop_submissions} s
1463 LEFT JOIN {workshop_assessments} a ON (a.submissionid = s.id)
1464 WHERE s.example=0 AND s.workshopid=:workshopid'; // to be cont.
1465 $params = array('workshopid' => $this->id);
1466
1467 if (is_null($restrict)) {
1468 // update all users - no more conditions
1469 } elseif (!empty($restrict)) {
1470 list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
1471 $sql .= " AND s.authorid $usql";
1472 $params = array_merge($params, $uparams);
1473 } else {
1474 throw new coding_exception('Empty value is not a valid parameter here');
1475 }
1476
1477 $sql .= ' ORDER BY s.id'; // this is important for bulk processing
89c1aa97 1478
e9a90e69
DM
1479 $rs = $DB->get_recordset_sql($sql, $params);
1480 $batch = array(); // will contain a set of all assessments of a single submission
1481 $previous = null; // a previous record in the recordset
1482
89c1aa97
DM
1483 foreach ($rs as $current) {
1484 if (is_null($previous)) {
1485 // we are processing the very first record in the recordset
1486 $previous = $current;
89c1aa97 1487 }
e9a90e69 1488 if ($current->submissionid == $previous->submissionid) {
89c1aa97 1489 // we are still processing the current submission
e9a90e69
DM
1490 $batch[] = $current;
1491 } else {
1492 // process all the assessments of a sigle submission
1493 $this->aggregate_submission_grades_process($batch);
1494 // and then start to process another submission
1495 $batch = array($current);
1496 $previous = $current;
89c1aa97
DM
1497 }
1498 }
e9a90e69
DM
1499 // do not forget to process the last batch!
1500 $this->aggregate_submission_grades_process($batch);
89c1aa97
DM
1501 $rs->close();
1502 }
1503
1504 /**
1505 * Calculates grades for assessment for the given participant(s)
1506 *
39411930
DM
1507 * Grade for assessment is calculated as a simple mean of all grading grades calculated by the grading evaluator.
1508 * The assessment weight is not taken into account here.
89c1aa97
DM
1509 *
1510 * @param null|int|array $restrict If null, update all reviewers, otherwise update just grades for the given reviewer(s)
1511 * @return void
1512 */
8a1ba8ac 1513 public function aggregate_grading_grades($restrict=null) {
89c1aa97
DM
1514 global $DB;
1515
39411930
DM
1516 // fetch a recordset with all assessments to process
1517 $sql = 'SELECT a.reviewerid, a.gradinggrade, a.gradinggradeover,
1518 ag.id AS aggregationid, ag.gradinggrade AS aggregatedgrade
1519 FROM {workshop_assessments} a
1520 INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
1521 LEFT JOIN {workshop_aggregations} ag ON (ag.userid = a.reviewerid AND ag.workshopid = s.workshopid)
1522 WHERE s.example=0 AND s.workshopid=:workshopid'; // to be cont.
1523 $params = array('workshopid' => $this->id);
1524
1525 if (is_null($restrict)) {
1526 // update all users - no more conditions
1527 } elseif (!empty($restrict)) {
1528 list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
1529 $sql .= " AND a.reviewerid $usql";
1530 $params = array_merge($params, $uparams);
1531 } else {
1532 throw new coding_exception('Empty value is not a valid parameter here');
1533 }
1534
1535 $sql .= ' ORDER BY a.reviewerid'; // this is important for bulk processing
1536
1537 $rs = $DB->get_recordset_sql($sql, $params);
1538 $batch = array(); // will contain a set of all assessments of a single submission
1539 $previous = null; // a previous record in the recordset
1540
1541 foreach ($rs as $current) {
1542 if (is_null($previous)) {
1543 // we are processing the very first record in the recordset
1544 $previous = $current;
1545 }
1546 if ($current->reviewerid == $previous->reviewerid) {
1547 // we are still processing the current reviewer
1548 $batch[] = $current;
1549 } else {
1550 // process all the assessments of a sigle submission
1551 $this->aggregate_grading_grades_process($batch);
1552 // and then start to process another reviewer
1553 $batch = array($current);
1554 $previous = $current;
1555 }
1556 }
1557 // do not forget to process the last batch!
1558 $this->aggregate_grading_grades_process($batch);
1559 $rs->close();
89c1aa97
DM
1560 }
1561
77f43e7d 1562 /**
f6e8b318 1563 * Returns the mform the teachers use to put a feedback for the reviewer
77f43e7d 1564 *
c6b784f0
DM
1565 * @param moodle_url $actionurl
1566 * @param stdclass $assessment
1567 * @param array $options editable, editableweight, overridablegradinggrade
f6e8b318 1568 * @return workshop_feedbackreviewer_form
77f43e7d 1569 */
c6b784f0 1570 public function get_feedbackreviewer_form(moodle_url $actionurl, stdclass $assessment, $options=array()) {
77f43e7d
DM
1571 global $CFG;
1572 require_once(dirname(__FILE__) . '/feedbackreviewer_form.php');
1573
7a789aa8 1574 $current = new stdclass();
e554671d 1575 $current->asid = $assessment->id;
c6b784f0 1576 $current->weight = $assessment->weight;
e554671d
DM
1577 $current->gradinggrade = $this->real_grading_grade($assessment->gradinggrade);
1578 $current->gradinggradeover = $this->real_grading_grade($assessment->gradinggradeover);
1579 $current->feedbackreviewer = $assessment->feedbackreviewer;
1580 $current->feedbackreviewerformat = $assessment->feedbackreviewerformat;
1581 if (is_null($current->gradinggrade)) {
1582 $current->gradinggrade = get_string('nullgrade', 'workshop');
1583 }
c6b784f0
DM
1584 if (!isset($options['editable'])) {
1585 $editable = true; // by default
1586 } else {
1587 $editable = (bool)$options['editable'];
1588 }
e554671d
DM
1589
1590 // prepare wysiwyg editor
1591 $current = file_prepare_standard_editor($current, 'feedbackreviewer', array());
1592
77f43e7d 1593 return new workshop_feedbackreviewer_form($actionurl,
c6b784f0 1594 array('workshop' => $this, 'current' => $current, 'editoropts' => array(), 'options' => $options),
77f43e7d
DM
1595 'post', '', null, $editable);
1596 }
1597
67cd00ba
DM
1598 /**
1599 * Returns the mform the teachers use to put a feedback for the author on their submission
1600 *
c6b784f0
DM
1601 * @param moodle_url $actionurl
1602 * @param stdclass $submission
1603 * @param array $options editable
67cd00ba
DM
1604 * @return workshop_feedbackauthor_form
1605 */
c6b784f0 1606 public function get_feedbackauthor_form(moodle_url $actionurl, stdclass $submission, $options=array()) {
67cd00ba
DM
1607 global $CFG;
1608 require_once(dirname(__FILE__) . '/feedbackauthor_form.php');
1609
7a789aa8 1610 $current = new stdclass();
67cd00ba 1611 $current->submissionid = $submission->id;
232175e4 1612 $current->published = $submission->published;
557a1100
DM
1613 $current->grade = $this->real_grade($submission->grade);
1614 $current->gradeover = $this->real_grade($submission->gradeover);
1615 $current->feedbackauthor = $submission->feedbackauthor;
1616 $current->feedbackauthorformat = $submission->feedbackauthorformat;
67cd00ba
DM
1617 if (is_null($current->grade)) {
1618 $current->grade = get_string('nullgrade', 'workshop');
1619 }
c6b784f0
DM
1620 if (!isset($options['editable'])) {
1621 $editable = true; // by default
1622 } else {
1623 $editable = (bool)$options['editable'];
1624 }
67cd00ba
DM
1625
1626 // prepare wysiwyg editor
1627 $current = file_prepare_standard_editor($current, 'feedbackauthor', array());
1628
1629 return new workshop_feedbackauthor_form($actionurl,
232175e4 1630 array('workshop' => $this, 'current' => $current, 'editoropts' => array(), 'options' => $options),
67cd00ba
DM
1631 'post', '', null, $editable);
1632 }
1633
aa40adbf
DM
1634 ////////////////////////////////////////////////////////////////////////////////
1635 // Internal methods (implementation details) //
1636 ////////////////////////////////////////////////////////////////////////////////
6516b9e9 1637
e9a90e69
DM
1638 /**
1639 * Given an array of all assessments of a single submission, calculates the final grade for this submission
1640 *
1641 * This calculates the weighted mean of the passed assessment grades. If, however, the submission grade
1642 * was overridden by a teacher, the gradeover value is returned and the rest of grades are ignored.
1643 *
7a789aa8 1644 * @param array $assessments of stdclass(->submissionid ->submissiongrade ->gradeover ->weight ->grade)
1fed6ce3 1645 * @return void
e9a90e69
DM
1646 */
1647 protected function aggregate_submission_grades_process(array $assessments) {
1648 global $DB;
1649
1650 $submissionid = null; // the id of the submission being processed
1651 $current = null; // the grade currently saved in database
1652 $finalgrade = null; // the new grade to be calculated
1653 $sumgrades = 0;
1654 $sumweights = 0;
1655
1656 foreach ($assessments as $assessment) {
1657 if (is_null($submissionid)) {
1658 // the id is the same in all records, fetch it during the first loop cycle
1659 $submissionid = $assessment->submissionid;
1660 }
1661 if (is_null($current)) {
1662 // the currently saved grade is the same in all records, fetch it during the first loop cycle
1663 $current = $assessment->submissiongrade;
1664 }
e9a90e69
DM
1665 if (is_null($assessment->grade)) {
1666 // this was not assessed yet
1667 continue;
1668 }
1669 if ($assessment->weight == 0) {
1670 // this does not influence the calculation
1671 continue;
1672 }
1673 $sumgrades += $assessment->grade * $assessment->weight;
1674 $sumweights += $assessment->weight;
1675 }
1676 if ($sumweights > 0 and is_null($finalgrade)) {
1677 $finalgrade = grade_floatval($sumgrades / $sumweights);
1678 }
1679 // check if the new final grade differs from the one stored in the database
1680 if (grade_floats_different($finalgrade, $current)) {
1681 // we need to save new calculation into the database
7a789aa8 1682 $record = new stdclass();
10bc4bce
DM
1683 $record->id = $submissionid;
1684 $record->grade = $finalgrade;
1685 $record->timegraded = time();
1686 $DB->update_record('workshop_submissions', $record);
e9a90e69
DM
1687 }
1688 }
1689
39411930
DM
1690 /**
1691 * Given an array of all assessments done by a single reviewer, calculates the final grading grade
1692 *
1693 * This calculates the simple mean of the passed grading grades. If, however, the grading grade
1694 * was overridden by a teacher, the gradinggradeover value is returned and the rest of grades are ignored.
1695 *
7a789aa8 1696 * @param array $assessments of stdclass(->reviewerid ->gradinggrade ->gradinggradeover ->aggregationid ->aggregatedgrade)
1fed6ce3 1697 * @return void
39411930
DM
1698 */
1699 protected function aggregate_grading_grades_process(array $assessments) {
1700 global $DB;
1701
1702 $reviewerid = null; // the id of the reviewer being processed
1703 $current = null; // the gradinggrade currently saved in database
1704 $finalgrade = null; // the new grade to be calculated
1705 $agid = null; // aggregation id
1706 $sumgrades = 0;
1707 $count = 0;
1708
1709 foreach ($assessments as $assessment) {
1710 if (is_null($reviewerid)) {
1711 // the id is the same in all records, fetch it during the first loop cycle
1712 $reviewerid = $assessment->reviewerid;
1713 }
1714 if (is_null($agid)) {
1715 // the id is the same in all records, fetch it during the first loop cycle
1716 $agid = $assessment->aggregationid;
1717 }
1718 if (is_null($current)) {
1719 // the currently saved grade is the same in all records, fetch it during the first loop cycle
1720 $current = $assessment->aggregatedgrade;
1721 }
1722 if (!is_null($assessment->gradinggradeover)) {
1723 // the grading grade for this assessment is overriden by a teacher
1724 $sumgrades += $assessment->gradinggradeover;
1725 $count++;
1726 } else {
1727 if (!is_null($assessment->gradinggrade)) {
1728 $sumgrades += $assessment->gradinggrade;
1729 $count++;
1730 }
1731 }
1732 }
1733 if ($count > 0) {
1734 $finalgrade = grade_floatval($sumgrades / $count);
1735 }
1736 // check if the new final grade differs from the one stored in the database
1737 if (grade_floats_different($finalgrade, $current)) {
1738 // we need to save new calculation into the database
1739 if (is_null($agid)) {
1740 // no aggregation record yet
7a789aa8 1741 $record = new stdclass();
39411930
DM
1742 $record->workshopid = $this->id;
1743 $record->userid = $reviewerid;
1744 $record->gradinggrade = $finalgrade;
10bc4bce 1745 $record->timegraded = time();
39411930
DM
1746 $DB->insert_record('workshop_aggregations', $record);
1747 } else {
7a789aa8 1748 $record = new stdclass();
10bc4bce
DM
1749 $record->id = $agid;
1750 $record->gradinggrade = $finalgrade;
1751 $record->timegraded = time();
1752 $DB->update_record('workshop_aggregations', $record);
39411930
DM
1753 }
1754 }
1755 }
1756
6516b9e9 1757 /**
aa40adbf 1758 * Given a list of user ids, returns the filtered one containing just ids of users with own submission
6516b9e9 1759 *
aa40adbf
DM
1760 * Example submissions are ignored.
1761 *
1762 * @param array $userids
6516b9e9
DM
1763 * @return array
1764 */
aa40adbf
DM
1765 protected function users_with_submission(array $userids) {
1766 global $DB;
1767
1768 if (empty($userids)) {
1769 return array();
1770 }
1771 $userswithsubmission = array();
1772 list($usql, $uparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
00aca3c1 1773 $sql = "SELECT id,authorid
aa40adbf 1774 FROM {workshop_submissions}
00aca3c1 1775 WHERE example = 0 AND workshopid = :workshopid AND authorid $usql";
aa40adbf
DM
1776 $params = array('workshopid' => $this->id);
1777 $params = array_merge($params, $uparams);
1778 $submissions = $DB->get_records_sql($sql, $params);
1779 foreach ($submissions as $submission) {
00aca3c1 1780 $userswithsubmission[$submission->authorid] = true;
aa40adbf
DM
1781 }
1782
1783 return $userswithsubmission;
6516b9e9
DM
1784 }
1785
aa40adbf
DM
1786 /**
1787 * @return array of available workshop phases
1788 */
365c2cc2 1789 protected function available_phases_list() {
aa40adbf
DM
1790 return array(
1791 self::PHASE_SETUP => true,
1792 self::PHASE_SUBMISSION => true,
1793 self::PHASE_ASSESSMENT => true,
1794 self::PHASE_EVALUATION => true,
1795 self::PHASE_CLOSED => true,
1796 );
1797 }
1798
66c9894d 1799}
55fc1e59
DM
1800
1801/**
1802 * Represents the user planner tool
1803 *
1804 * Planner contains list of phases. Each phase contains list of tasks. Task is a simple object with
1805 * title, link and completed (true/false/null logic).
1806 */
1807class workshop_user_plan implements renderable {
1808
cff28ef0
DM
1809 /** @var int id of the user this plan is for */
1810 public $userid;
55fc1e59
DM
1811 /** @var workshop */
1812 public $workshop;
1813 /** @var array of (stdclass)tasks */
1814 public $phases = array();
cff28ef0
DM
1815 /** @var null|array of example submissions to be assessed by the planner owner */
1816 protected $examples = null;
55fc1e59
DM
1817
1818 /**
1819 * Prepare an individual workshop plan for the given user.
1820 *
1821 * @param workshop $workshop instance
1822 * @param int $userid whom the plan is prepared for
1823 */
1824 public function __construct(workshop $workshop, $userid) {
1825 global $DB;
1826
1827 $this->workshop = $workshop;
cff28ef0 1828 $this->userid = $userid;
55fc1e59 1829
5bab64a3
DM
1830 //---------------------------------------------------------
1831 // * SETUP | submission | assessment | evaluation | closed
1832 //---------------------------------------------------------
55fc1e59
DM
1833 $phase = new stdclass();
1834 $phase->title = get_string('phasesetup', 'workshop');
1835 $phase->tasks = array();
cff28ef0 1836 if (has_capability('moodle/course:manageactivities', $workshop->context, $userid)) {
55fc1e59
DM
1837 $task = new stdclass();
1838 $task->title = get_string('taskintro', 'workshop');
cff28ef0
DM
1839 $task->link = $workshop->updatemod_url();
1840 $task->completed = !(trim(strip_tags($workshop->intro)) == '');
55fc1e59
DM
1841 $phase->tasks['intro'] = $task;
1842 }
cff28ef0 1843 if (has_capability('moodle/course:manageactivities', $workshop->context, $userid)) {
55fc1e59
DM
1844 $task = new stdclass();
1845 $task->title = get_string('taskinstructauthors', 'workshop');
cff28ef0
DM
1846 $task->link = $workshop->updatemod_url();
1847 $task->completed = !(trim(strip_tags($workshop->instructauthors)) == '');
55fc1e59
DM
1848 $phase->tasks['instructauthors'] = $task;
1849 }
cff28ef0 1850 if (has_capability('mod/workshop:editdimensions', $workshop->context, $userid)) {
55fc1e59
DM
1851 $task = new stdclass();
1852 $task->title = get_string('editassessmentform', 'workshop');
cff28ef0
DM
1853 $task->link = $workshop->editform_url();
1854 if ($workshop->grading_strategy_instance()->form_ready()) {
55fc1e59 1855 $task->completed = true;
cff28ef0 1856 } elseif ($workshop->phase > workshop::PHASE_SETUP) {
55fc1e59
DM
1857 $task->completed = false;
1858 }
1859 $phase->tasks['editform'] = $task;
1860 }
cff28ef0 1861 if ($workshop->useexamples and has_capability('mod/workshop:manageexamples', $workshop->context, $userid)) {
55fc1e59
DM
1862 $task = new stdclass();
1863 $task->title = get_string('prepareexamples', 'workshop');
cff28ef0 1864 if ($DB->count_records('workshop_submissions', array('example' => 1, 'workshopid' => $workshop->id)) > 0) {
55fc1e59 1865 $task->completed = true;
cff28ef0 1866 } elseif ($workshop->phase > workshop::PHASE_SETUP) {
55fc1e59
DM
1867 $task->completed = false;
1868 }
1869 $phase->tasks['prepareexamples'] = $task;
1870 }
cff28ef0 1871 if (empty($phase->tasks) and $workshop->phase == workshop::PHASE_SETUP) {
55fc1e59
DM
1872 // if we are in the setup phase and there is no task (typical for students), let us
1873 // display some explanation what is going on
1874 $task = new stdclass();
1875 $task->title = get_string('undersetup', 'workshop');
1876 $task->completed = 'info';
1877 $phase->tasks['setupinfo'] = $task;
1878 }
1879 $this->phases[workshop::PHASE_SETUP] = $phase;
1880
5bab64a3
DM
1881 //---------------------------------------------------------
1882 // setup | * SUBMISSION | assessment | evaluation | closed
1883 //---------------------------------------------------------
55fc1e59
DM
1884 $phase = new stdclass();
1885 $phase->title = get_string('phasesubmission', 'workshop');
1886 $phase->tasks = array();
cff28ef0
DM
1887 if (($workshop->usepeerassessment or $workshop->useselfassessment)
1888 and has_capability('moodle/course:manageactivities', $workshop->context, $userid)) {
55fc1e59
DM
1889 $task = new stdclass();
1890 $task->title = get_string('taskinstructreviewers', 'workshop');
cff28ef0
DM
1891 $task->link = $workshop->updatemod_url();
1892 if (trim(strip_tags($workshop->instructreviewers))) {
55fc1e59 1893 $task->completed = true;
cff28ef0 1894 } elseif ($workshop->phase >= workshop::PHASE_ASSESSMENT) {
55fc1e59
DM
1895 $task->completed = false;
1896 }
1897 $phase->tasks['instructreviewers'] = $task;
1898 }
cff28ef0 1899 if ($workshop->useexamples and $workshop->examplesmode == workshop::EXAMPLES_BEFORE_SUBMISSION
514d8c22 1900 and has_capability('mod/workshop:submit', $workshop->context, $userid, false)
cff28ef0 1901 and !has_capability('mod/workshop:manageexamples', $workshop->context, $userid)) {
514d8c22
DM
1902 $task = new stdclass();
1903 $task->title = get_string('exampleassesstask', 'workshop');
cff28ef0 1904 $examples = $this->get_examples();
514d8c22
DM
1905 $a = new stdclass();
1906 $a->expected = count($examples);
1907 $a->assessed = 0;
1908 foreach ($examples as $exampleid => $example) {
1909 if (!is_null($example->grade)) {
1910 $a->assessed++;
1911 }
1912 }
1913 $task->details = get_string('exampleassesstaskdetails', 'workshop', $a);
1914 if ($a->assessed == $a->expected) {
1915 $task->completed = true;
cff28ef0 1916 } elseif ($workshop->phase >= workshop::PHASE_ASSESSMENT) {
514d8c22
DM
1917 $task->completed = false;
1918 }
1919 $phase->tasks['examples'] = $task;
1920 }
cff28ef0 1921 if (has_capability('mod/workshop:submit', $workshop->context, $userid, false)) {
55fc1e59
DM
1922 $task = new stdclass();
1923 $task->title = get_string('tasksubmit', 'workshop');
cff28ef0
DM
1924 $task->link = $workshop->submission_url();
1925 if ($DB->record_exists('workshop_submissions', array('workshopid'=>$workshop->id, 'example'=>0, 'authorid'=>$userid))) {
55fc1e59 1926 $task->completed = true;
cff28ef0 1927 } elseif ($workshop->phase >= workshop::PHASE_ASSESSMENT) {
55fc1e59
DM
1928 $task->completed = false;
1929 } else {
1930 $task->completed = null; // still has a chance to submit
1931 }
1932 $phase->tasks['submit'] = $task;
1933 }
cff28ef0 1934 if (has_capability('mod/workshop:allocate', $workshop->context, $userid)) {
55fc1e59
DM
1935 $task = new stdclass();
1936 $task->title = get_string('allocate', 'workshop');
cff28ef0
DM
1937 $task->link = $workshop->allocation_url();
1938 $numofauthors = count(get_users_by_capability($workshop->context, 'mod/workshop:submit', 'u.id', '', '', '',
55fc1e59 1939 '', '', false, true));
cff28ef0 1940 $numofsubmissions = $DB->count_records('workshop_submissions', array('workshopid'=>$workshop->id, 'example'=>0));
55fc1e59
DM
1941 $sql = 'SELECT COUNT(s.id) AS nonallocated
1942 FROM {workshop_submissions} s
1943 LEFT JOIN {workshop_assessments} a ON (a.submissionid=s.id)
1944 WHERE s.workshopid = :workshopid AND s.example=0 AND a.submissionid IS NULL';
cff28ef0 1945 $params['workshopid'] = $workshop->id;
55fc1e59
DM
1946 $numnonallocated = $DB->count_records_sql($sql, $params);
1947 if ($numofsubmissions == 0) {
1948 $task->completed = null;
1949 } elseif ($numnonallocated == 0) {
1950 $task->completed = true;
cff28ef0 1951 } elseif ($workshop->phase > workshop::PHASE_SUBMISSION) {
55fc1e59
DM
1952 $task->completed = false;
1953 } else {
1954 $task->completed = null; // still has a chance to allocate
1955 }
1956 $a = new stdclass();
1957 $a->expected = $numofauthors;
1958 $a->submitted = $numofsubmissions;
1959 $a->allocate = $numnonallocated;
1960 $task->details = get_string('allocatedetails', 'workshop', $a);
1961 unset($a);
1962 $phase->tasks['allocate'] = $task;
1963
cff28ef0 1964 if ($numofsubmissions < $numofauthors and $workshop->phase >= workshop::PHASE_SUBMISSION) {
55fc1e59
DM
1965 $task = new stdclass();
1966 $task->title = get_string('someuserswosubmission', 'workshop');
1967 $task->completed = 'info';
1968 $phase->tasks['allocateinfo'] = $task;
1969 }
1970 }
cff28ef0 1971 if ($workshop->submissionstart) {
5bab64a3 1972 $task = new stdclass();
cff28ef0 1973 $task->title = get_string('submissionstartdatetime', 'workshop', workshop::timestamp_formats($workshop->submissionstart));
5bab64a3
DM
1974 $task->completed = 'info';
1975 $phase->tasks['submissionstartdatetime'] = $task;
1976 }
cff28ef0 1977 if ($workshop->submissionend) {
5bab64a3 1978 $task = new stdclass();
cff28ef0 1979 $task->title = get_string('submissionenddatetime', 'workshop', workshop::timestamp_formats($workshop->submissionend));
5bab64a3
DM
1980 $task->completed = 'info';
1981 $phase->tasks['submissionenddatetime'] = $task;
1982 }
55fc1e59
DM
1983 $this->phases[workshop::PHASE_SUBMISSION] = $phase;
1984
5bab64a3
DM
1985 //---------------------------------------------------------
1986 // setup | submission | * ASSESSMENT | evaluation | closed
1987 //---------------------------------------------------------
55fc1e59
DM
1988 $phase = new stdclass();
1989 $phase->title = get_string('phaseassessment', 'workshop');
1990 $phase->tasks = array();
cff28ef0
DM
1991 $phase->isreviewer = has_capability('mod/workshop:peerassess', $workshop->context, $userid);
1992 if ($workshop->useexamples and $workshop->examplesmode == workshop::EXAMPLES_BEFORE_ASSESSMENT
1993 and $phase->isreviewer and !has_capability('mod/workshop:manageexamples', $workshop->context, $userid)) {
1994 $task = new stdclass();
1995 $task->title = get_string('exampleassesstask', 'workshop');
1996 $examples = $workshop->get_examples_for_reviewer($userid);
1997 $a = new stdclass();
1998 $a->expected = count($examples);
1999 $a->assessed = 0;
2000 foreach ($examples as $exampleid => $example) {
2001 if (!is_null($example->grade)) {
2002 $a->assessed++;
55fc1e59
DM
2003 }
2004 }
cff28ef0
DM
2005 $task->details = get_string('exampleassesstaskdetails', 'workshop', $a);
2006 if ($a->assessed == $a->expected) {
55fc1e59 2007 $task->completed = true;
cff28ef0 2008 } elseif ($workshop->phase > workshop::PHASE_ASSESSMENT) {
55fc1e59
DM
2009 $task->completed = false;
2010 }
cff28ef0 2011 $phase->tasks['examples'] = $task;
55fc1e59 2012 }
cff28ef0
DM
2013 if (empty($phase->tasks['examples']) or !empty($phase->tasks['examples']->completed)) {
2014 $phase->assessments = $workshop->get_assessments_by_reviewer($userid);
2015 $numofpeers = 0; // number of allocated peer-assessments
2016 $numofpeerstodo = 0; // number of peer-assessments to do
2017 $numofself = 0; // number of allocated self-assessments - should be 0 or 1
2018 $numofselftodo = 0; // number of self-assessments to do - should be 0 or 1
2019 foreach ($phase->assessments as $a) {
2020 if ($a->authorid == $userid) {
2021 $numofself++;
2022 if (is_null($a->grade)) {
2023 $numofselftodo++;
2024 }
2025 } else {
2026 $numofpeers++;
2027 if (is_null($a->grade)) {
2028 $numofpeerstodo++;
2029 }
2030 }
2031 }
2032 unset($a);
2033 if ($workshop->usepeerassessment and $numofpeers) {
2034 $task = new stdclass();
2035 if ($numofpeerstodo == 0) {
2036 $task->completed = true;
2037 } elseif ($workshop->phase > workshop::PHASE_ASSESSMENT) {
2038 $task->completed = false;
2039 }
2040 $a = new stdclass();
2041 $a->total = $numofpeers;
2042 $a->todo = $numofpeerstodo;
2043 $task->title = get_string('taskassesspeers', 'workshop');
2044 $task->details = get_string('taskassesspeersdetails', 'workshop', $a);
2045 unset($a);
2046 $phase->tasks['assesspeers'] = $task;
2047 }
2048 if ($workshop->useselfassessment and $numofself) {
2049 $task = new stdclass();
2050 if ($numofselftodo == 0) {
2051 $task->completed = true;
2052 } elseif ($workshop->phase > workshop::PHASE_ASSESSMENT) {
2053 $task->completed = false;
2054 }
2055 $task->title = get_string('taskassessself', 'workshop');
2056 $phase->tasks['assessself'] = $task;
55fc1e59 2057 }
55fc1e59 2058 }
cff28ef0 2059 if ($workshop->assessmentstart) {
5bab64a3 2060 $task = new stdclass();
cff28ef0 2061 $task->title = get_string('assessmentstartdatetime', 'workshop', workshop::timestamp_formats($workshop->assessmentstart));
5bab64a3
DM
2062 $task->completed = 'info';
2063 $phase->tasks['assessmentstartdatetime'] = $task;
2064 }
cff28ef0 2065 if ($workshop->assessmentend) {
5bab64a3 2066 $task = new stdclass();
cff28ef0 2067 $task->title = get_string('assessmentenddatetime', 'workshop', workshop::timestamp_formats($workshop->assessmentend));
5bab64a3
DM
2068 $task->completed = 'info';
2069 $phase->tasks['assessmentenddatetime'] = $task;
2070 }
55fc1e59
DM
2071 $this->phases[workshop::PHASE_ASSESSMENT] = $phase;
2072
5bab64a3
DM
2073 //---------------------------------------------------------
2074 // setup | submission | assessment | * EVALUATION | closed
2075 //---------------------------------------------------------
55fc1e59
DM
2076 $phase = new stdclass();
2077 $phase->title = get_string('phaseevaluation', 'workshop');
2078 $phase->tasks = array();
cff28ef0
DM
2079 if (has_capability('mod/workshop:overridegrades', $workshop->context)) {
2080 $expected = count($workshop->get_potential_authors(false));
55fc1e59 2081 $calculated = $DB->count_records_select('workshop_submissions',
cff28ef0 2082 'workshopid = ? AND (grade IS NOT NULL OR gradeover IS NOT NULL)', array($workshop->id));
55fc1e59
DM
2083 $task = new stdclass();
2084 $task->title = get_string('calculatesubmissiongrades', 'workshop');
2085 $a = new stdclass();
2086 $a->expected = $expected;
2087 $a->calculated = $calculated;
2088 $task->details = get_string('calculatesubmissiongradesdetails', 'workshop', $a);
2089 if ($calculated >= $expected) {
2090 $task->completed = true;
cff28ef0 2091 } elseif ($workshop->phase > workshop::PHASE_EVALUATION) {
55fc1e59
DM
2092 $task->completed = false;
2093 }
2094 $phase->tasks['calculatesubmissiongrade'] = $task;
2095
cff28ef0 2096 $expected = count($workshop->get_potential_reviewers(false));
55fc1e59 2097 $calculated = $DB->count_records_select('workshop_aggregations',
cff28ef0 2098 'workshopid = ? AND gradinggrade IS NOT NULL', array($workshop->id));
55fc1e59
DM
2099 $task = new stdclass();
2100 $task->title = get_string('calculategradinggrades', 'workshop');
2101 $a = new stdclass();
2102 $a->expected = $expected;
2103 $a->calculated = $calculated;
2104 $task->details = get_string('calculategradinggradesdetails', 'workshop', $a);
2105 if ($calculated >= $expected) {
2106 $task->completed = true;
cff28ef0 2107 } elseif ($workshop->phase > workshop::PHASE_EVALUATION) {
55fc1e59
DM
2108 $task->completed = false;
2109 }
2110 $phase->tasks['calculategradinggrade'] = $task;
2111
cff28ef0 2112 } elseif ($workshop->phase == workshop::PHASE_EVALUATION) {
55fc1e59
DM
2113 $task = new stdclass();
2114 $task->title = get_string('evaluategradeswait', 'workshop');
2115 $task->completed = 'info';
2116 $phase->tasks['evaluateinfo'] = $task;
2117 }
2118 $this->phases[workshop::PHASE_EVALUATION] = $phase;
2119
5bab64a3
DM
2120 //---------------------------------------------------------
2121 // setup | submission | assessment | evaluation | * CLOSED
2122 //---------------------------------------------------------
55fc1e59
DM
2123 $phase = new stdclass();
2124 $phase->title = get_string('phaseclosed', 'workshop');
2125 $phase->tasks = array();
2126 $this->phases[workshop::PHASE_CLOSED] = $phase;
2127
2128 // Polish data, set default values if not done explicitly
2129 foreach ($this->phases as $phasecode => $phase) {
2130 $phase->title = isset($phase->title) ? $phase->title : '';
2131 $phase->tasks = isset($phase->tasks) ? $phase->tasks : array();
cff28ef0 2132 if ($phasecode == $workshop->phase) {
55fc1e59
DM
2133 $phase->active = true;
2134 } else {
2135 $phase->active = false;
2136 }
2137 if (!isset($phase->actions)) {
2138 $phase->actions = array();
2139 }
2140
2141 foreach ($phase->tasks as $taskcode => $task) {
2142 $task->title = isset($task->title) ? $task->title : '';
2143 $task->link = isset($task->link) ? $task->link : null;
2144 $task->details = isset($task->details) ? $task->details : '';
2145 $task->completed = isset($task->completed) ? $task->completed : null;
2146 }
2147 }
2148
2149 // Add phase swithing actions
cff28ef0 2150 if (has_capability('mod/workshop:switchphase', $workshop->context, $userid)) {
55fc1e59
DM
2151 foreach ($this->phases as $phasecode => $phase) {
2152 if (! $phase->active) {
2153 $action = new stdclass();
2154 $action->type = 'switchphase';
cff28ef0 2155 $action->url = $workshop->switchphase_url($phasecode);
55fc1e59
DM
2156 $phase->actions[] = $action;
2157 }
2158 }
2159 }
2160 }
cff28ef0
DM
2161
2162 /**
2163 * Returns example submissions to be assessed by the owner of the planner
2164 *
2165 * This is here to cache the DB query because the same list is needed later in view.php
2166 *
2167 * @see workshop::get_examples_for_reviewer() for the format of returned value
2168 * @return array
2169 */
2170 public function get_examples() {
2171 if (is_null($this->examples)) {
2172 $this->examples = $this->workshop->get_examples_for_reviewer($this->userid);
2173 }
2174 return $this->examples;
2175 }
55fc1e59 2176}