MDL-11363 minor doc improvement
[moodle.git] / lib / gradelib.php
CommitLineData
5834dcdb 1<?php // $Id$
2
3///////////////////////////////////////////////////////////////////////////
5834dcdb 4// NOTICE OF COPYRIGHT //
5// //
6// Moodle - Modular Object-Oriented Dynamic Learning Environment //
53461661 7// http://moodle.org //
5834dcdb 8// //
53461661 9// Copyright (C) 1999 onwards Martin Dougiamas http://moodle.com //
5834dcdb 10// //
11// This program is free software; you can redistribute it and/or modify //
12// it under the terms of the GNU General Public License as published by //
13// the Free Software Foundation; either version 2 of the License, or //
14// (at your option) any later version. //
15// //
16// This program is distributed in the hope that it will be useful, //
17// but WITHOUT ANY WARRANTY; without even the implied warranty of //
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
19// GNU General Public License for more details: //
20// //
21// http://www.gnu.org/copyleft/gpl.html //
22// //
23///////////////////////////////////////////////////////////////////////////
24
25/**
42bbccd7 26 * Library of functions for gradebook
5834dcdb 27 *
28 * @author Moodle HQ developers
29 * @version $Id$
30 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
31 * @package moodlecore
32 */
33
53461661 34require_once($CFG->libdir . '/grade/constants.php');
eea6690a 35
3058964f 36require_once($CFG->libdir . '/grade/grade_category.php');
37require_once($CFG->libdir . '/grade/grade_item.php');
3ee5c201 38require_once($CFG->libdir . '/grade/grade_grade.php');
d5bdb228 39require_once($CFG->libdir . '/grade/grade_scale.php');
5501446d 40require_once($CFG->libdir . '/grade/grade_outcome.php');
60cf7430 41
6b5c722d 42/***** PUBLIC GRADE API - only these functions should be used in modules *****/
612607bd 43
c5b5f18d 44/**
45 * Submit new or update grade; update/create grade_item definition. Grade must have userid specified,
ac9b0805 46 * rawgrade and feedback with format are optional. rawgrade NULL means 'Not graded', missing property
c5b5f18d 47 * or key means do not change existing.
4cf1b9be 48 *
c5b5f18d 49 * Only following grade item properties can be changed 'itemname', 'idnumber', 'gradetype', 'grademax',
e648f890 50 * 'grademin', 'scaleid', 'multfactor', 'plusfactor', 'deleted'.
4cf1b9be 51 *
9c8d38fa 52 * @param string $source source of the grade such as 'mod/assignment'
c5b5f18d 53 * @param int $courseid id of course
3a5ae660 54 * @param string $itemtype type of grade item - mod, block
c5b5f18d 55 * @param string $itemmodule more specific then $itemtype - assignment, forum, etc.; maybe NULL for some item types
56 * @param int $iteminstance instance it of graded subject
57 * @param int $itemnumber most probably 0, modules can use other numbers when having more than one grades for each user
b60b2ce6 58 * @param mixed $grades grade (object, array) or several grades (arrays of arrays or objects), NULL if updating grade_item definition only
c5b5f18d 59 * @param mixed $itemdetails object or array describing the grading item, NULL if no change
60 */
b67ec72f 61function grade_update($source, $courseid, $itemtype, $itemmodule, $iteminstance, $itemnumber, $grades=NULL, $itemdetails=NULL) {
aaff71da 62 global $USER;
612607bd 63
c5b5f18d 64 // only following grade_item properties can be changed in this function
e648f890 65 $allowed = array('itemname', 'idnumber', 'gradetype', 'grademax', 'grademin', 'scaleid', 'multfactor', 'plusfactor', 'deleted');
612607bd 66
c4e4068f 67 // grade item identification
68 $params = compact('courseid', 'itemtype', 'itemmodule', 'iteminstance', 'itemnumber');
69
612607bd 70 if (is_null($courseid) or is_null($itemtype)) {
71 debugging('Missing courseid or itemtype');
72 return GRADE_UPDATE_FAILED;
73 }
74
c4e4068f 75 if (!$grade_items = grade_item::fetch_all($params)) {
612607bd 76 // create a new one
77 $grade_item = false;
78
79 } else if (count($grade_items) == 1){
80 $grade_item = reset($grade_items);
81 unset($grade_items); //release memory
82
83 } else {
34e67f76 84 debugging('Found more than one grade item');
612607bd 85 return GRADE_UPDATE_MULTIPLE;
86 }
87
aaff71da 88 if (!empty($itemdetails['deleted'])) {
89 if ($grade_item) {
90 if ($grade_item->delete($source)) {
91 return GRADE_UPDATE_OK;
92 } else {
93 return GRADE_UPDATE_FAILED;
94 }
95 }
96 return GRADE_UPDATE_OK;
97 }
98
612607bd 99/// Create or update the grade_item if needed
b159da78 100
612607bd 101 if (!$grade_item) {
612607bd 102 if ($itemdetails) {
103 $itemdetails = (array)$itemdetails;
2e53372c 104
772ddfbf 105 // grademin and grademax ignored when scale specified
2e53372c 106 if (array_key_exists('scaleid', $itemdetails)) {
107 if ($itemdetails['scaleid']) {
108 unset($itemdetails['grademin']);
109 unset($itemdetails['grademax']);
110 }
111 }
112
612607bd 113 foreach ($itemdetails as $k=>$v) {
114 if (!in_array($k, $allowed)) {
115 // ignore it
116 continue;
117 }
118 if ($k == 'gradetype' and $v == GRADE_TYPE_NONE) {
119 // no grade item needed!
120 return GRADE_UPDATE_OK;
121 }
122 $params[$k] = $v;
123 }
124 }
f70152b7 125 $grade_item = new grade_item($params);
126 $grade_item->insert();
612607bd 127
128 } else {
2cc4b0f9 129 if ($grade_item->is_locked()) {
678e8898 130 $message = get_string('gradeitemislocked', 'grades', $grade_item->itemname);
131 notice($message);
132 return GRADE_UPDATE_ITEM_LOCKED;
612607bd 133 }
134
135 if ($itemdetails) {
136 $itemdetails = (array)$itemdetails;
137 $update = false;
138 foreach ($itemdetails as $k=>$v) {
139 if (!in_array($k, $allowed)) {
140 // ignore it
141 continue;
142 }
143 if ($grade_item->{$k} != $v) {
144 $grade_item->{$k} = $v;
145 $update = true;
146 }
147 }
148 if ($update) {
149 $grade_item->update();
150 }
151 }
152 }
153
154/// Some extra checks
155 // do we use grading?
156 if ($grade_item->gradetype == GRADE_TYPE_NONE) {
157 return GRADE_UPDATE_OK;
158 }
159
160 // no grade submitted
b67ec72f 161 if (empty($grades)) {
612607bd 162 return GRADE_UPDATE_OK;
163 }
164
612607bd 165/// Finally start processing of grades
b67ec72f 166 if (is_object($grades)) {
167 $grades = array($grades);
612607bd 168 } else {
b67ec72f 169 if (array_key_exists('userid', $grades)) {
170 $grades = array($grades);
612607bd 171 }
172 }
173
4cf1b9be 174 $failed = false;
612607bd 175 foreach ($grades as $grade) {
176 $grade = (array)$grade;
177 if (empty($grade['userid'])) {
4cf1b9be 178 $failed = true;
179 debugging('Invalid userid in grade submitted');
180 continue;
ac9b0805 181 } else {
182 $userid = $grade['userid'];
612607bd 183 }
184
2cc4b0f9 185 $rawgrade = false;
ac9b0805 186 $feedback = false;
187 $feedbackformat = FORMAT_MOODLE;
772ddfbf 188
ac9b0805 189 if (array_key_exists('rawgrade', $grade)) {
190 $rawgrade = $grade['rawgrade'];
191 }
612607bd 192
4cf1b9be 193 if (array_key_exists('feedback', $grade)) {
ac9b0805 194 $feedback = $grade['feedback'];
612607bd 195 }
196
ac9b0805 197 if (array_key_exists('feedbackformat', $grade)) {
198 $feedbackformat = $grade['feedbackformat'];
612607bd 199 }
200
aaff71da 201 if (array_key_exists('usermodified', $grade)) {
202 $usermodified = $grade['usermodified'];
203 } else {
204 $usermodified = $USER->id;
205 }
206
ac9b0805 207 // update or insert the grade
aaff71da 208 if (!$grade_item->update_raw_grade($userid, $rawgrade, $source, null, $feedback, $feedbackformat, $usermodified)) {
4cf1b9be 209 $failed = true;
4cf1b9be 210 }
612607bd 211 }
212
4cf1b9be 213 if (!$failed) {
214 return GRADE_UPDATE_OK;
215 } else {
216 return GRADE_UPDATE_FAILED;
217 }
612607bd 218}
219
b67ec72f 220
221/**
3a5ae660 222 * Tells a module whether a grade (or grade_item if $userid is not given) is currently locked or not.
bc430af2 223 * If it's locked to the current user then the module can print a nice message or prevent editing in the module.
3a5ae660 224 * If no $userid is given, the method will always return the grade_item's locked state.
225 * If a $userid is given, the method will first check the grade_item's locked state (the column). If it is locked,
226 * the method will return true no matter the locked state of the specific grade being checked. If unlocked, it will
227 * return the locked state of the specific grade.
228 *
229 * @param int $courseid id of course
230 * @param string $itemtype 'mod', 'block'
231 * @param string $itemmodule 'forum, 'quiz', etc.
232 * @param int $iteminstance id of the item module
233 * @param int $itemnumber most probably 0, modules can use other numbers when having more than one grades for each user
234 * @param int $userid ID of the graded user
235 * @return boolean Whether the grade is locked or not
236 */
34e67f76 237function grade_is_locked($courseid, $itemtype, $itemmodule, $iteminstance, $itemnumber, $userid=NULL) {
b67ec72f 238
f92dcad8 239 if (!$grade_items = grade_item::fetch_all(compact('courseid', 'itemtype', 'itemmodule', 'iteminstance', 'itemnumber'))) {
34e67f76 240 return false;
241
242 } else if (count($grade_items) == 1){
243 $grade_item = reset($grade_items);
244 return $grade_item->is_locked($userid);
245
246 } else {
247 debugging('Found more than one grade item');
248 foreach ($grade_items as $grade_item) {
249 if ($grade_item->is_locked($userid)) {
250 return true;
251 }
252 }
253 return false;
254 }
255}
b67ec72f 256
3a5ae660 257/**
6b5c722d 258 * Returns list of outcomes used in activity together with current outcomes for user
3a5ae660 259 * @param int $courseid id of course
260 * @param string $itemtype 'mod', 'block'
261 * @param string $itemmodule 'forum, 'quiz', etc.
262 * @param int $iteminstance id of the item module
6b5c722d 263 * @param int $userid optional id of the graded user
3a5ae660 264 * @return array of outcome information objects (scaleid, name, grade and locked status) indexed with itemnumbers
265 */
e46f4d11 266function grade_get_outcomes($courseid, $itemtype, $itemmodule, $iteminstance, $userid=0) {
3a5ae660 267 $result = array();
6b5c722d 268 $course_item = grade_item::fetch_course_item($courseid);
269 $needsupdate = $course_item->needsupdate;
270
3a5ae660 271 if ($items = grade_item::fetch_all(array('itemtype'=>$itemtype, 'itemmodule'=>$itemmodule, 'iteminstance'=>$iteminstance, 'courseid'=>$courseid))) {
272 foreach ($items as $item) {
273 if (empty($item->outcomeid)) {
274 continue;
275 }
276 if (!$outcome = grade_outcome::fetch(array('id'=>$item->outcomeid))) {
277 debugging('Incorect outcomeid found');
278 continue;
279 }
280 // prepare outcome info with user grade
281 $o = new object();
282 $o->scaleid = $outcome->scaleid;
6b5c722d 283 $o->name = $outcome->get_name();
3a5ae660 284
e46f4d11 285 if (empty($userid)) {
286 //no user info
287 } if ($grade = $item->get_grade($userid,false)) {
3a5ae660 288 $o->grade = $grade->finalgrade;
289 $o->locked = $grade->is_locked();
6b5c722d 290 $o->hidden = $grade->is_hidden();
291
3a5ae660 292 } else {
293 $o->grade = null;
294 $o->locked = $item->is_locked();
6b5c722d 295 $o->hidden = $grade->is_hidden();
296 }
297
298 if ($needsupdate) {
299 $o->grade = false;
300 } else {
301 $o->grade = intval($o->grade); // 0 means no grade, int for scales
3a5ae660 302 }
11a14999 303
3a5ae660 304 $result[$item->itemnumber] = $o;
305 }
306 }
307
308 return $result;
309}
310
311/**
312 * Updates outcomes of user
313 * @param int $courseid id of course
314 * @param string $itemtype 'mod', 'block'
315 * @param string $itemmodule 'forum, 'quiz', etc.
316 * @param int $iteminstance id of the item module
317 * @param int $userid ID of the graded user
318 */
319function grade_update_outcomes($source, $courseid, $itemtype, $itemmodule, $iteminstance, $userid, $data) {
320 if ($items = grade_item::fetch_all(array('itemtype'=>$itemtype, 'itemmodule'=>$itemmodule, 'iteminstance'=>$iteminstance, 'courseid'=>$courseid))) {
321 foreach ($items as $item) {
322 if (!array_key_exists($item->itemnumber, $data)) {
323 continue;
324 }
325 $grade = $data[$item->itemnumber] < 1 ? null : $data[$item->itemnumber];
326 $item->update_final_grade($userid, $grade, $source);
11a14999 327 }
3a5ae660 328 }
329}
330
6b5c722d 331/**
332 * Returns list of grades used in activity optionally with current grades of one user
333 * @param int $courseid id of course
334 * @param string $itemtype 'mod', 'block'
335 * @param string $itemmodule 'forum, 'quiz', etc.
336 * @param int $iteminstance id of the item module
337 * @param int $userid optional id of the graded user; if userid not used, returns only information about grade_item
338 * @return array of grade information objects (scaleid, name, grade and locked status, etc.) indexed with itemnumbers
339 */
340function grade_get_final_grades($courseid, $itemtype, $itemmodule, $iteminstance, $userid=0) {
341 $result = array();
342 $course_item = grade_item::fetch_course_item($courseid);
343 $needsupdate = $course_item->needsupdate;
344
345 if ($items = grade_item::fetch_all(array('itemtype'=>$itemtype, 'itemmodule'=>$itemmodule, 'iteminstance'=>$iteminstance, 'courseid'=>$courseid))) {
346 foreach ($items as $item) {
347 if (!empty($item->outcomeid)) {
348 continue;
349 }
350 // prepare grade info with user grade
351 $o = new object();
352 $o->scaleid = $item->scaleid;
353 $o->name = $item->get_name();
354 $o->grademin = $item->grademin;
355 $o->grademax = $item->grademax;
356 $o->gradepass = $item->gadepass;
357
358 if (empty($userid)) {
359 //no user info
360
361 } if ($grade = $item->get_grade($userid, false)) {
3f2b0c8a 362 $o->grade = $grade->finalgrade;
363 $o->locked = $grade->is_locked();
364 $o->hidden = $grade->is_hidden();
365 $o->overridden = $grade->overridden;
366 $o->feedback = $text->feedback;
367 $o->feedbackformat = $text->feedbackformat;
6b5c722d 368
369 } else {
370 $o->grade = null;
371 $o->locked = $item->is_locked();
372 $o->hidden = $grade->is_hidden();
373 $o->overridden = 0;
374 $o->feedback = null;
375 $o->feedbackformat = FORMAT_MOODLE;
376 }
377
378 // create text representation of grade
379 if ($needsupdate) {
380 $o->grade = false;
381 $o->str_grade = get_string('error');
382
383 } else if (is_null($o->grade)) {
384 $o->str_grade = get_string('nograde');
385
386 } else {
387 switch ($item->gradetype) {
388 case GRADE_TYPE_VALUE:
389 $o->str_grade = $o->grade;
390 break;
391
392 case GRADE_TYPE_SCALE:
393 $scale = $item->load_scale();
394 $o->str_grade = format_string($scale[$o->grade-1]);
395 break;
396
397 case GRADE_TYPE_NONE:
398 case GRADE_TYPE_TEXT:
399 default:
400 $o->str_grade = '';
401 }
402 }
403
404 // create html representation of feedback
405 if (is_null($o->feedback)) {
406 $o->str_feedback = '';
407 } else {
408 $o->str_feedback = format_text($o->feedback, $o->feedbackformat);
409 }
410
411 $result[$item->itemnumber] = $o;
412 }
413 }
414
415 return $result;
416}
417
612607bd 418/***** END OF PUBLIC API *****/
419
60243313 420
421/**
2c5e52e2 422 * Verify new value of idnumber - checks for uniqueness of new idnumbers, old are kept intact
60243313 423 * @param string idnumber string (with magic quotes)
424 * @param object $cm used for course module idnumbers and items attached to modules
425 * @param object $gradeitem is item idnumber
426 * @return boolean true means idnumber ok
427 */
428function grade_verify_idnumber($idnumber, $grade_item=null, $cm=null) {
429 if ($idnumber == '') {
430 //we allow empty idnumbers
431 return true;
432 }
433
434 // keep existing even when not unique
435 if ($cm and $cm->idnumber == $idnumber) {
436 return true;
437 } else if ($grade_item and $grade_item->idnumber == $idnumber) {
438 return true;
439 }
440
441 if (get_records('course_modules', 'idnumber', $idnumber)) {
442 return false;
443 }
444
445 if (get_records('grade_items', 'idnumber', $idnumber)) {
446 return false;
447 }
448
449 return true;
450}
451
452/**
453 * Force final grade recalculation in all course items
454 * @param int $courseid
455 */
f8e6e4db 456function grade_force_full_regrading($courseid) {
457 set_field('grade_items', 'needsupdate', 1, 'courseid', $courseid);
458}
34e67f76 459
5834dcdb 460/**
ac9b0805 461 * Updates all final grades in course.
a8995b34 462 *
463 * @param int $courseid
f8e6e4db 464 * @param int $userid if specified, try to do a quick regrading of grades of this user only
465 * @param object $updated_item the item in which
466 * @return boolean true if ok, array of errors if problems found (item id is used as key)
a8995b34 467 */
c86caae7 468function grade_regrade_final_grades($courseid, $userid=null, $updated_item=null) {
b8ff92b6 469
514a3467 470 $course_item = grade_item::fetch_course_item($courseid);
f04873a9 471
f8e6e4db 472 if ($userid) {
473 // one raw grade updated for one user
474 if (empty($updated_item)) {
475 error("updated_item_id can not be null!");
476 }
477 if ($course_item->needsupdate) {
478 $updated_item->force_regrading();
479 return 'Can not do fast regrading after updating of raw grades';
a8995b34 480 }
772ddfbf 481
f8e6e4db 482 } else {
483 if (!$course_item->needsupdate) {
484 // nothing to do :-)
b8ff92b6 485 return true;
b8ff92b6 486 }
a8995b34 487 }
488
f8e6e4db 489 $grade_items = grade_item::fetch_all(array('courseid'=>$courseid));
490 $depends_on = array();
491
492 // first mark all category and calculated items as needing regrading
fb0e3570 493 // this is slower, but 100% accurate
f8e6e4db 494 foreach ($grade_items as $gid=>$gitem) {
fb46b5b6 495 if (!empty($updated_item) and $updated_item->id == $gid) {
f8e6e4db 496 $grade_items[$gid]->needsupdate = 1;
497
eacd3700 498 } else if ($gitem->is_course_item() or $gitem->is_category_item() or $gitem->is_calculated()) {
f8e6e4db 499 $grade_items[$gid]->needsupdate = 1;
500 }
2e53372c 501
f8e6e4db 502 // construct depends_on lookup array
503 $depends_on[$gid] = $grade_items[$gid]->depends_on();
504 }
2e53372c 505
d14ae855 506 $errors = array();
b8ff92b6 507 $finalids = array();
d14ae855 508 $gids = array_keys($grade_items);
eacd3700 509 $failed = 0;
d14ae855 510
511 while (count($finalids) < count($gids)) { // work until all grades are final or error found
512 $count = 0;
513 foreach ($gids as $gid) {
514 if (in_array($gid, $finalids)) {
515 continue; // already final
516 }
517
518 if (!$grade_items[$gid]->needsupdate) {
519 $finalids[] = $gid; // we can make it final - does not need update
b8ff92b6 520 continue;
521 }
522
b8ff92b6 523 $doupdate = true;
f8e6e4db 524 foreach ($depends_on[$gid] as $did) {
b8ff92b6 525 if (!in_array($did, $finalids)) {
526 $doupdate = false;
d14ae855 527 continue; // this item depends on something that is not yet in finals array
b8ff92b6 528 }
529 }
530
531 //oki - let's update, calculate or aggregate :-)
532 if ($doupdate) {
d14ae855 533 $result = $grade_items[$gid]->regrade_final_grades($userid);
f8e6e4db 534
535 if ($result === true) {
d14ae855 536 $grade_items[$gid]->regrading_finished();
fb0e3570 537 $grade_items[$gid]->check_locktime(); // do the locktime item locking
f8e6e4db 538 $count++;
b8ff92b6 539 $finalids[] = $gid;
fb0e3570 540
f8e6e4db 541 } else {
d14ae855 542 $grade_items[$gid]->force_regrading();
f8e6e4db 543 $errors[$gid] = $result;
b8ff92b6 544 }
545 }
546 }
547
548 if ($count == 0) {
eacd3700 549 $failed++;
550 } else {
551 $failed = 0;
552 }
553
554 if ($failed > 1) {
d14ae855 555 foreach($gids as $gid) {
556 if (in_array($gid, $finalids)) {
557 continue; // this one is ok
558 }
559 $grade_items[$gid]->force_regrading();
560 $errors[$grade_items[$gid]->id] = 'Probably circular reference or broken calculation formula'; // TODO: localize
b8ff92b6 561 }
d14ae855 562 break; // oki, found error
b8ff92b6 563 }
564 }
565
566 if (count($errors) == 0) {
fb0e3570 567 if (empty($userid)) {
568 // do the locktime locking of grades, but only when doing full regrading
fed7cdc9 569 grade_grade::check_locktime_all($gids);
fb0e3570 570 }
b8ff92b6 571 return true;
572 } else {
573 return $errors;
574 }
a8995b34 575}
967f222f 576
de420c11 577/**
d185c3ee 578 * For backwards compatibility with old third-party modules, this function can
579 * be used to import all grades from activities with legacy grading.
739196ba 580 * @param int $courseid or null if all courses
967f222f 581 */
739196ba 582function grade_grab_legacy_grades($courseid=null) {
612607bd 583
ac9b0805 584 global $CFG;
967f222f 585
586 if (!$mods = get_list_of_plugins('mod') ) {
587 error('No modules installed!');
588 }
589
739196ba 590 if ($courseid) {
591 $course_sql = " AND cm.course=$courseid";
592 } else {
593 $course_sql = "";
594 }
595
967f222f 596 foreach ($mods as $mod) {
597
598 if ($mod == 'NEWMODULE') { // Someone has unzipped the template, ignore it
599 continue;
600 }
601
d185c3ee 602 if (!$module = get_record('modules', 'name', $mod)) {
603 //not installed
604 continue;
605 }
606
607 if (!$module->visible) {
608 //disabled module
609 continue;
610 }
611
612 $fullmod = $CFG->dirroot.'/mod/'.$mod;
967f222f 613
614 // include the module lib once
615 if (file_exists($fullmod.'/lib.php')) {
616 include_once($fullmod.'/lib.php');
de420c11 617 // look for modname_grades() function - old gradebook pulling function
618 // if present sync the grades with new grading system
967f222f 619 $gradefunc = $mod.'_grades';
de420c11 620 if (function_exists($gradefunc)) {
621
622 // get all instance of the activity
d185c3ee 623 $sql = "SELECT a.*, cm.idnumber as cmidnumber, m.name as modname
624 FROM {$CFG->prefix}$mod a, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
739196ba 625 WHERE m.name='$mod' AND m.id=cm.module AND cm.instance=a.id $course_sql";
de420c11 626
627 if ($modinstances = get_records_sql($sql)) {
967f222f 628 foreach ($modinstances as $modinstance) {
d185c3ee 629 grade_update_mod_grades($modinstance);
967f222f 630 }
631 }
632 }
633 }
634 }
635}
636
ac9b0805 637/**
638 * For testing purposes mainly, reloads grades from all non legacy modules into gradebook.
639 */
640function grade_grab_grades() {
641
642 global $CFG;
643
644 if (!$mods = get_list_of_plugins('mod') ) {
645 error('No modules installed!');
646 }
647
648 foreach ($mods as $mod) {
649
650 if ($mod == 'NEWMODULE') { // Someone has unzipped the template, ignore it
651 continue;
652 }
653
654 if (!$module = get_record('modules', 'name', $mod)) {
655 //not installed
656 continue;
657 }
658
659 if (!$module->visible) {
660 //disabled module
661 continue;
662 }
663
664 $fullmod = $CFG->dirroot.'/mod/'.$mod;
665
666 // include the module lib once
667 if (file_exists($fullmod.'/lib.php')) {
668 include_once($fullmod.'/lib.php');
669 // look for modname_grades() function - old gradebook pulling function
670 // if present sync the grades with new grading system
671 $gradefunc = $mod.'_update_grades';
672 if (function_exists($gradefunc)) {
673 $gradefunc();
674 }
675 }
676 }
677}
678
d185c3ee 679/**
680 * Force full update of module grades in central gradebook - works for both legacy and converted activities.
681 * @param object $modinstance object with extra cmidnumber and modname property
682 * @return boolean success
683 */
2b0f65e2 684function grade_update_mod_grades($modinstance, $userid=0) {
d185c3ee 685 global $CFG;
686
687 $fullmod = $CFG->dirroot.'/mod/'.$modinstance->modname;
688 if (!file_exists($fullmod.'/lib.php')) {
689 debugging('missing lib.php file in module');
690 return false;
691 }
692 include_once($fullmod.'/lib.php');
693
694 // does it use legacy grading?
695 $gradefunc = $modinstance->modname.'_grades';
696 $updategradesfunc = $modinstance->modname.'_update_grades';
697 $updateitemfunc = $modinstance->modname.'_grade_item_update';
698
699 if (function_exists($gradefunc)) {
2b0f65e2 700
701 // legacy module - not yet converted
d185c3ee 702 if ($oldgrades = $gradefunc($modinstance->id)) {
703
704 $grademax = $oldgrades->maxgrade;
705 $scaleid = NULL;
706 if (!is_numeric($grademax)) {
707 // scale name is provided as a string, try to find it
708 if (!$scale = get_record('scale', 'name', $grademax)) {
709 debugging('Incorrect scale name! name:'.$grademax);
710 return false;
711 }
712 $scaleid = $scale->id;
713 }
714
715 if (!$grade_item = grade_get_legacy_grade_item($modinstance, $grademax, $scaleid)) {
716 debugging('Can not get/create legacy grade item!');
717 return false;
718 }
719
720 $grades = array();
2b0f65e2 721 foreach ($oldgrades->grades as $uid=>$usergrade) {
722 if ($userid and $uid != $userid) {
723 continue;
724 }
d185c3ee 725 $grade = new object();
2b0f65e2 726 $grade->userid = $uid;
d185c3ee 727
728 if ($usergrade == '-') {
729 // no grade
ac9b0805 730 $grade->rawgrade = null;
d185c3ee 731
732 } else if ($scaleid) {
733 // scale in use, words used
734 $gradescale = explode(",", $scale->scale);
ac9b0805 735 $grade->rawgrade = array_search($usergrade, $gradescale) + 1;
d185c3ee 736
737 } else {
738 // good old numeric value
ac9b0805 739 $grade->rawgrade = $usergrade;
d185c3ee 740 }
741 $grades[] = $grade;
742 }
743
744 grade_update('legacygrab', $grade_item->courseid, $grade_item->itemtype, $grade_item->itemmodule,
745 $grade_item->iteminstance, $grade_item->itemnumber, $grades);
746 }
747
748 } else if (function_exists($updategradesfunc) and function_exists($updateitemfunc)) {
749 //new grading supported, force updating of grades
750 $updateitemfunc($modinstance);
2b0f65e2 751 $updategradesfunc($modinstance, $userid);
d185c3ee 752
753 } else {
2b0f65e2 754 // mudule does not support grading??
d185c3ee 755 }
756
757 return true;
758}
de420c11 759
760/**
d185c3ee 761 * Get and update/create grade item for legacy modules.
de420c11 762 */
763function grade_get_legacy_grade_item($modinstance, $grademax, $scaleid) {
764
765 // does it already exist?
42ff9ce6 766 if ($grade_items = grade_item::fetch_all(array('courseid'=>$modinstance->course, 'itemtype'=>'mod', 'itemmodule'=>$modinstance->modname, 'iteminstance'=>$modinstance->id, 'itemnumber'=>0))) {
de420c11 767 if (count($grade_items) > 1) {
d185c3ee 768 debugging('Multiple legacy grade_items found.');
de420c11 769 return false;
770 }
771
772 $grade_item = reset($grade_items);
de420c11 773
d185c3ee 774 if (is_null($grademax) and is_null($scaleid)) {
775 $grade_item->gradetype = GRADE_TYPE_NONE;
de420c11 776
d185c3ee 777 } else if ($scaleid) {
778 $grade_item->gradetype = GRADE_TYPE_SCALE;
779 $grade_item->scaleid = $scaleid;
97d608ba 780 $grade_item->grademin = 1;
de420c11 781
d185c3ee 782 } else {
97d608ba 783 $grade_item->gradetype = GRADE_TYPE_VALUE;
784 $grade_item->grademax = $grademax;
785 $grade_item->grademin = 0;
de420c11 786 }
787
d185c3ee 788 $grade_item->itemname = $modinstance->name;
789 $grade_item->idnumber = $modinstance->cmidnumber;
de420c11 790
d185c3ee 791 $grade_item->update();
de420c11 792
793 return $grade_item;
794 }
612607bd 795
de420c11 796 // create new one
d185c3ee 797 $params = array('courseid' =>$modinstance->course,
de420c11 798 'itemtype' =>'mod',
799 'itemmodule' =>$modinstance->modname,
800 'iteminstance'=>$modinstance->id,
d185c3ee 801 'itemnumber' =>0,
de420c11 802 'itemname' =>$modinstance->name,
803 'idnumber' =>$modinstance->cmidnumber);
804
d185c3ee 805 if (is_null($grademax) and is_null($scaleid)) {
806 $params['gradetype'] = GRADE_TYPE_NONE;
807
808 } else if ($scaleid) {
612607bd 809 $params['gradetype'] = GRADE_TYPE_SCALE;
de420c11 810 $params['scaleid'] = $scaleid;
b3ac6c3e 811 $grade_item->grademin = 1;
de420c11 812 } else {
612607bd 813 $params['gradetype'] = GRADE_TYPE_VALUE;
de420c11 814 $params['grademax'] = $grademax;
815 $params['grademin'] = 0;
816 }
817
f70152b7 818 $grade_item = new grade_item($params);
819 $grade_item->insert();
de420c11 820
f70152b7 821 return $grade_item;
de420c11 822}
823
f615fbab 824/**
825 * Remove all grade related course data - history is kept
826 * @param int $courseid
6b5c722d 827 * @param bool @showfeedback print feedback
f615fbab 828 */
829function remove_course_grades($courseid, $showfeedback) {
830 $strdeleted = get_string('deleted');
831
832 $course_category = grade_category::fetch_course_category($courseid);
833 $course_category->delete('coursedelete');
834 if ($showfeedback) {
835 notify($strdeleted.' - '.get_string('grades', 'grades').', '.get_string('items', 'grades').', '.get_string('categories', 'grades'));
836 }
837
838 if ($outcomes = grade_outcome::fetch_all(array('courseid'=>$courseid))) {
839 foreach ($outcomes as $outcome) {
840 $outcome->delete('coursedelete');
841 }
842 }
843 delete_records('grade_outcomes_courses', 'courseid', $courseid);
844 if ($showfeedback) {
845 notify($strdeleted.' - '.get_string('outcomes', 'grades'));
846 }
847
848 if ($scales = grade_scale::fetch_all(array('courseid'=>$courseid))) {
849 foreach ($scales as $scale) {
850 $scale->delete('coursedelete');
851 }
852 }
853 if ($showfeedback) {
854 notify($strdeleted.' - '.get_string('scales'));
855 }
f615fbab 856}
bfe7297e 857
32b97bb2 858/**
859 * Builds an array of percentages indexed by integers for the purpose of building a select drop-down element.
860 * @param int $steps The value between each level.
861 * @param string $order 'asc' for 0-100 and 'desc' for 100-0
862 * @param int $lowest The lowest value to include
863 * @param int $highest The highest value to include
864 */
865function build_percentages_array($steps=1, $order='desc', $lowest=0, $highest=100) {
866 // TODO reject or implement
867}
42ff9ce6 868
2650c51e 869/**
870 * Grading cron job
871 */
872function grade_cron() {
26101be8 873 global $CFG;
874
875 $now = time();
876
877 $sql = "SELECT i.*
878 FROM {$CFG->prefix}grade_items i
879 WHERE i.locked = 0 AND i.locktime > 0 AND i.locktime < $now AND EXISTS (
1f4a0320 880 SELECT 'x' FROM {$CFG->prefix}grade_items c WHERE c.itemtype='course' AND c.needsupdate=0 AND c.courseid=i.courseid)";
26101be8 881
2650c51e 882 // go through all courses that have proper final grades and lock them if needed
26101be8 883 if ($rs = get_recordset_sql($sql)) {
2650c51e 884 if ($rs->RecordCount() > 0) {
26101be8 885 while ($item = rs_fetch_next_record($rs)) {
886 $grade_item = new grade_item($item, false);
887 $grade_item->locked = $now;
888 $grade_item->update('locktime');
2650c51e 889 }
890 }
891 rs_close($rs);
892 }
26101be8 893
894 $sql = "SELECT g.*
895 FROM {$CFG->prefix}grade_grades g, {$CFG->prefix}grade_items i
896 WHERE g.locked = 0 AND g.locktime > 0 AND g.locktime < $now AND g.itemid=i.id AND EXISTS (
1f4a0320 897 SELECT 'x' FROM {$CFG->prefix}grade_items c WHERE c.itemtype='course' AND c.needsupdate=0 AND c.courseid=i.courseid)";
26101be8 898
899 // go through all courses that have proper final grades and lock them if needed
900 if ($rs = get_recordset_sql($sql)) {
901 if ($rs->RecordCount() > 0) {
902 while ($grade = rs_fetch_next_record($rs)) {
1f4a0320 903 $grade_grade = new grade_grade($grade, false);
26101be8 904 $grade_grade->locked = $now;
905 $grade_grade->update('locktime');
906 }
907 }
908 rs_close($rs);
909 }
910
2650c51e 911}
912
60cf7430 913?>