From 53fad4b90c949224a5d40bbc7d4f4df8ac151409 Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Mon, 4 Jan 2010 17:47:09 +0000 Subject: [PATCH] MDL-19870 Initial work on random allocator Initial sketches of random allocator. Refactoring of the rest of the module here and there. Also, this commit removes trailing whitespace and running empty lines. --- mod/workshop/allocation.php | 29 +- mod/workshop/allocation/lib.php | 30 +- mod/workshop/allocation/manual/allocator.php | 72 ++-- mod/workshop/allocation/manual/renderer.php | 179 +++++----- mod/workshop/allocation/random/allocator.php | 308 +++++++++++++++-- .../allocation/random/settings_form.php | 70 ++++ .../random/simpletest/testallocator.php | 181 ++++++++++ mod/workshop/assessment.php | 29 +- mod/workshop/db/access.php | 9 +- mod/workshop/editform.php | 27 +- .../grading/accumulative/assessment_form.php | 17 +- .../grading/accumulative/edit_form.php | 23 +- .../accumulative/simpletest/teststrategy.php | 22 +- .../grading/accumulative/strategy.php | 22 +- mod/workshop/grading/assessment_form.php | 27 +- mod/workshop/grading/edit_form.php | 27 +- mod/workshop/grading/noerrors/edit_form.php | 35 +- mod/workshop/grading/noerrors/strategy.php | 28 +- mod/workshop/grading/strategy.php | 42 +-- mod/workshop/index.php | 13 +- mod/workshop/lang/en_utf8/workshop.php | 33 +- mod/workshop/lib.php | 65 ++-- mod/workshop/locallib.php | 314 ++++++++++++++---- mod/workshop/mod_form.php | 20 +- mod/workshop/renderer.php | 81 ++++- mod/workshop/settings.php | 5 +- mod/workshop/simpletest/testlib.php | 19 +- mod/workshop/simpletest/testworkshopapi.php | 19 +- mod/workshop/styles.php | 118 ++++--- mod/workshop/submission.php | 13 +- mod/workshop/submission_form.php | 10 +- mod/workshop/version.php | 11 +- mod/workshop/view.php | 17 +- 33 files changed, 1297 insertions(+), 618 deletions(-) create mode 100644 mod/workshop/allocation/random/settings_form.php create mode 100644 mod/workshop/allocation/random/simpletest/testallocator.php diff --git a/mod/workshop/allocation.php b/mod/workshop/allocation.php index 1d055bdff9..049948c10e 100644 --- a/mod/workshop/allocation.php +++ b/mod/workshop/allocation.php @@ -1,7 +1,7 @@ . - - + /** * At this page, teachers allocate submissions to students for a review * - * The allocation logic itself is delegated to allocators - subplugins in ./allocation + * The allocation logic itself is delegated to allocators - subplugins in ./allocation * folder. * * @package mod-workshop @@ -48,11 +47,11 @@ require_capability('mod/workshop:allocate', $context); $PAGE->set_title($workshop->name); $PAGE->set_heading($course->fullname); // -// todo navigation will be changed yet for Moodle 2.0 +// TODO navigation will be changed yet for Moodle 2.0 $navigation = build_navigation(get_string('allocation', 'workshop'), $cm); -$allocator = $workshop->allocator_instance($method); -$allocator->init(); +$allocator = $workshop->allocator_instance($method); +$initresult = $allocator->init(); // // Output starts here @@ -67,5 +66,13 @@ foreach ($allocators as $methodid => $methodname) { } print_tabs(array($tabrow), $method); // todo use $output call -echo $OUTPUT->container($allocator->ui(), 'allocator-ui'); +if (!empty($initresult)) { + echo $OUTPUT->container_start('allocator-init-results'); + echo $wsoutput->allocation_init_result($initresult); + echo $OUTPUT->container_end(); +} else { + echo $OUTPUT->container_start('allocator-ui'); + $allocator->ui($wsoutput); + echo $OUTPUT->container_end(); +} echo $OUTPUT->footer(); diff --git a/mod/workshop/allocation/lib.php b/mod/workshop/allocation/lib.php index e7750824c8..45a2862cc8 100644 --- a/mod/workshop/allocation/lib.php +++ b/mod/workshop/allocation/lib.php @@ -1,7 +1,7 @@ . - /** * Code for the submissions allocation support is defined here * @@ -26,11 +25,10 @@ defined('MOODLE_INTERNAL') || die(); - /** * Allocators are responsible for assigning submissions to reviewers for assessments * - * The task of the allocator is to assign the correct number of submissions to reviewers + * The task of the allocator is to assign the correct number of submissions to reviewers * for assessment. Several allocation methods are expected and they can be combined. For * example, teacher can allocate several submissions manually (by 'manual' allocator) and * then let the other submissions being allocated randomly (by 'random' allocator). @@ -38,27 +36,29 @@ defined('MOODLE_INTERNAL') || die(); * workshop_assessments table. */ interface workshop_allocator { - + /** * Initialize the allocator and eventually process submitted data * - * This method is called soon after the allocator is constructed and before any output + * This method is called soon after the allocator is constructed and before any output * is generated. Therefore is may process any data submitted and do other tasks. * It should not generate any output * * @throws moodle_workshop_exception - * @return void + * @return mixed void or optional HTML string */ public function init(); - /** - * Returns HTML to be displayed as the user interface + * Print HTML to be displayed as the user interface * - * If a form is part of the UI, the caller should have call $PAGE->set_url(...) - * - * @return string HTML to be displayed + * If a form is part of the UI, the caller should have called $PAGE->set_url(...) + * The methods must produce output instead of just returning it so mform->display() can + * be used there. + * + * @param object $wsoutput workshop module renderer can be used + * @return void */ - public function ui(); + public function ui(moodle_mod_workshop_renderer $wsoutput); } diff --git a/mod/workshop/allocation/manual/allocator.php b/mod/workshop/allocation/manual/allocator.php index 0ed0fe33db..975851aad4 100644 --- a/mod/workshop/allocation/manual/allocator.php +++ b/mod/workshop/allocation/manual/allocator.php @@ -1,7 +1,7 @@ . - /** * Allows user to allocate the submissions manually - * + * * @package mod-workshop * @copyright 2009 David Mudrak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -29,17 +28,15 @@ defined('MOODLE_INTERNAL') || die(); require_once(dirname(dirname(__FILE__)) . '/lib.php'); // interface definition require_once(dirname(dirname(dirname(__FILE__))) . '/locallib.php'); // workshop internal API - /** * These constants are used to pass status messages between init() and ui() */ define('WORKSHOP_ALLOCATION_MANUAL_MSG_ADDED', 1); define('WORKSHOP_ALLOCATION_MANUAL_MSG_NOSUBMISSION', 2); define('WORKSHOP_ALLOCATION_MANUAL_MSG_EXISTS', 3); -define('WORKSHOP_ALLOCATION_MANUAL_MSG_WOSUBMISSION', 4); -define('WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL', 5); -define('WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED', 6); - +define('WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL', 4); +define('WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED', 5); +define('WORKSHOP_ALLOCATION_MANUAL_MSG_DELETE_ERROR', 6); /** * Allows users to allocate submissions for review manually @@ -49,16 +46,13 @@ class workshop_manual_allocator implements workshop_allocator { /** workshop instance */ protected $workshop; - /** * @param stdClass $workshop Workshop record */ public function __construct(workshop $workshop) { - $this->workshop = $workshop; } - /** * Allocate submissions as requested by user */ @@ -75,14 +69,12 @@ class workshop_manual_allocator implements workshop_allocator { $reviewerid = required_param('by', PARAM_INT); $authorid = required_param('of', PARAM_INT); $m = array(); // message object to be passed to the next page - $rs = $this->workshop->get_submissions_recordset($authorid); - $submission = $rs->current(); - $rs->close(); - if (empty($submission->id)) { + $submission = $this->workshop->get_submission_by_author($authorid); + if (!$submission) { // nothing submitted by the given user $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_NOSUBMISSION; $m[] = $authorid; - + } else { // ok, we have the submission $res = $this->workshop->add_allocation($submission, $reviewerid); @@ -90,10 +82,6 @@ class workshop_manual_allocator implements workshop_allocator { $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_EXISTS; $m[] = $submission->userid; $m[] = $reviewerid; - } elseif ($res == WORKSHOP_ALLOCATION_WOSUBMISSION) { - $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_WOSUBMISSION; - $m[] = $submission->userid; - $m[] = $reviewerid; } else { $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_ADDED; $m[] = $submission->userid; @@ -109,10 +97,8 @@ class workshop_manual_allocator implements workshop_allocator { } $assessmentid = required_param('what', PARAM_INT); $confirmed = optional_param('confirm', 0, PARAM_INT); - $rs = $this->workshop->get_assessments_recordset('all', $assessmentid); - $assessment = $rs->current(); - $rs->close(); - if (!empty($assessment)) { + $assessment = $this->workshop->get_assessment_by_id($assessmentid); + if ($assessment) { if (!$confirmed) { $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL; $m[] = $assessment->id; @@ -124,10 +110,15 @@ class workshop_manual_allocator implements workshop_allocator { $m[] = 1; } } else { - $res = $this->workshop->delete_assessment($assessment->id); - $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED; - $m[] = $assessment->authorid; - $m[] = $assessment->reviewerid; + if($this->workshop->delete_assessment($assessment->id)) { + $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_DELETED; + $m[] = $assessment->authorid; + $m[] = $assessment->reviewerid; + } else { + $m[] = WORKSHOP_ALLOCATION_MANUAL_MSG_DELETE_ERROR; + $m[] = $assessment->authorid; + $m[] = $assessment->reviewerid; + } } $m = implode('-', $m); // serialize message object to be passed via URL redirect($PAGE->url->out(false, array('m' => $m), false)); @@ -136,11 +127,10 @@ class workshop_manual_allocator implements workshop_allocator { } } - /** * Prints user interface - current allocation and a form to edit it */ - public function ui() { + public function ui(moodle_mod_workshop_renderer $wsoutput) { global $PAGE; $hlauthorid = -1; // highlight this author @@ -168,12 +158,6 @@ class workshop_manual_allocator implements workshop_allocator { $msg->text = get_string('nosubmissionfound', 'workshop'); $msg->sty = 'error'; break; - case WORKSHOP_ALLOCATION_MANUAL_MSG_WOSUBMISSION: - $hlauthorid = $m[1]; - $hlreviewerid = $m[2]; - $msg->text = get_string('cantassesswosubmission', 'workshop'); - $sty->sty = 'error'; - break; case WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL: $hlauthorid = $m[2]; $hlreviewerid = $m[3]; @@ -191,6 +175,12 @@ class workshop_manual_allocator implements workshop_allocator { $msg->text = get_string('assessmentdeleted', 'workshop'); $msg->sty = 'ok'; break; + case WORKSHOP_ALLOCATION_MANUAL_MSG_DELETE_ERROR: + $hlauthorid = $m[1]; + $hlreviewerid = $m[2]; + $msg->text = get_string('assessmentnotdeleted', 'workshop'); + $msg->sty = 'error'; + break; } if ($m[0] == WORKSHOP_ALLOCATION_MANUAL_MSG_CONFIRM_DEL) { $handler = $PAGE->url->out_action(); @@ -234,12 +224,12 @@ class workshop_manual_allocator implements workshop_allocator { } } - // ok, we have all data. Let it pass to the renderer and return the output + // We have all data. Let it pass to the renderer and return the output + // Here, we do not use neither the core renderer nor the workshop one but use an own one require_once(dirname(__FILE__) . '/renderer.php'); $uioutput = $PAGE->theme->get_renderer('mod_workshop', $PAGE, 'allocation_manual'); - return $uioutput->display_allocations($this->workshop, $peer, $hlauthorid, $hlreviewerid, $msg); + echo $uioutput->display_allocations($this->workshop, $peer, $hlauthorid, $hlreviewerid, $msg); } - } diff --git a/mod/workshop/allocation/manual/renderer.php b/mod/workshop/allocation/manual/renderer.php index 12244cfd91..5effbc7606 100644 --- a/mod/workshop/allocation/manual/renderer.php +++ b/mod/workshop/allocation/manual/renderer.php @@ -1,7 +1,7 @@ . - - + /** * Renderer class for the manual allocation UI is defined here * @@ -27,9 +26,9 @@ defined('MOODLE_INTERNAL') || die(); /** - * Manual allocation renderer class + * Manual allocation renderer class */ -class moodle_mod_workshop_allocation_manual_renderer { +class moodle_mod_workshop_allocation_manual_renderer extends moodle_renderer_base { /** the underlying renderer to use */ protected $output; @@ -39,7 +38,7 @@ class moodle_mod_workshop_allocation_manual_renderer { /** * Workshop renderer constructor - * + * * @param mixed $page the page we are doing output for * @param mixed $output lower-level renderer, typically moodle_core_renderer * @access public @@ -50,11 +49,10 @@ class moodle_mod_workshop_allocation_manual_renderer { $this->output = $output; } - /** * Display the table of all current allocations and widgets to modify them - * - * @param workshop $workshop workshop instance object + * + * @param workshop $workshop workshop instance object * @param array $peers prepared array of all allocations * @param int $hlauthorid highlight this author * @param int $hlreviewerid highlight this reviewer @@ -63,12 +61,13 @@ class moodle_mod_workshop_allocation_manual_renderer { */ public function display_allocations(workshop $workshop, &$peers, $hlauthorid=null, $hlreviewerid=null, $msg=null) { + $wsoutput = $this->page->theme->get_renderer('mod_workshop', $this->page); if (empty($peers)) { - return $this->status_message((object)array('text' => get_string('nosubmissions', 'workshop'))); + return $wsoutput->status_message((object)array('text' => get_string('nosubmissions', 'workshop'))); } $table = new html_table(); - $table->set_classes = array('allocations'); + $table->set_classes('allocations'); $table->head = array(get_string('participantreviewedby', 'workshop'), get_string('participant', 'workshop'), get_string('participantrevierof', 'workshop')); @@ -87,68 +86,53 @@ class moodle_mod_workshop_allocation_manual_renderer { if ($user->id == $hlreviewerid) { $thisrowclasses[] = 'highlightreviewerof'; } - $table->rowclass[] = implode(' ', $thisrowclasses); + $table->rowclasses[] = implode(' ', $thisrowclasses); $table->data[] = $row; } - return $this->output->container($this->status_message($msg) . $this->output->table($table), 'manual-allocator'); + return $this->output->container($wsoutput->status_message($msg) . $this->output->table($table), 'manual-allocator'); } - /** - * Returns html code for a status message - * - * @param string $message to display - * @return string html + * Returns information about the workshop participant + * + * @param stdClass $user participant data + * @return string HTML code */ - protected function status_message(stdClass $message) { - - if (empty($message->text)) { - return ''; - } - $sty = $message->sty ? $message->sty : 'info'; - - $o = '' . $message->text . ''; - $closer = '' . get_string('messageclose', 'workshop') . ''; - $o .= $this->output->container($closer, 'status-message-closer'); - if (isset($message->extra)) { - $o .= $message->extra; - } - return $this->output->container($o, array('status-message', $sty)); - } - - protected function participant(stdClass $user) { - $o = print_user_picture($user, $this->page->course->id, null, 35, true); $o .= fullname($user); - $o .= '
' . "\n"; + $o .= $this->output->container_start(array('submission')); if (is_null($user->submissionid)) { - $o .= '' . get_string('nosubmissionfound', 'workshop'); + $o .= $this->output->output_tag('span', array('class' => 'info'), get_string('nosubmissionfound', 'workshop')); } else { - $o .= ''; + $submlink = $this->output->output_tag('a', array('href' => '#'), s($user->submissiontitle)); + $o .= $this->output->container($submlink, array('title')); if (is_null($user->submissiongrade)) { - $o .= '
' . get_string('nogradeyet', 'workshop') . '
'; + $o .= $this->output->container(get_string('nogradeyet', 'workshop'), array('grade', 'missing')); } else { - $o .= '
' . s($user->submissiongrade) . '
'; // todo calculate + $o .= $this->output->container(s($user->submissiongrade), array('grade')); // TODO calculate grade } } - $o .= '
' . "\n"; - + $o .= $this->output->container_end(); return $o; } - - protected function reviewers_of_participant(stdClass $user, workshop $workshop, &$peers) { - + /** + * Returns information about the current reviewers of the given participant and a selector do add new one + * + * @param stdClass $user participant data + * @param workshop_api $workshop workshop record + * @param array $peers objects with properties to display picture and fullname + * @return string html code + */ + protected function reviewers_of_participant(stdClass $user, workshop_api $workshop, &$peers) { $o = ''; if (is_null($user->submissionid)) { - $o .= '' . "\n"; - $o .= get_string('nothingtoreview', 'workshop'); - $o .= '' . "\n"; + $o .= $this->output->output_tag('span', array('class' => 'info'), get_string('nothingtoreview', 'workshop')); } else { $options = array(); - foreach ($workshop->get_peer_reviewers() as $reviewer) { + foreach ($workshop->get_peer_reviewers(!$workshop->assesswosubmission) as $reviewer) { $options[$reviewer->id] = fullname($reviewer); } if (!$workshop->useselfassessment) { @@ -156,73 +140,66 @@ class moodle_mod_workshop_allocation_manual_renderer { if (isset($options[$user->id])) { unset($options[$user->id]); } - } + } $handler = $this->page->url->out_action() . '&mode=new&of=' . $user->id . '&by='; $o .= popup_form($handler, $options, 'addreviewof' . $user->id, '', get_string('chooseuser', 'workshop'), '', '', true, 'self', get_string('addreviewer', 'workshop')); } - $o .= '' . "\n"; - + $o .= $this->output->output_end_tag('ul'); return $o; } - - protected function reviewees_of_participant(stdClass $user, workshop $workshop, &$peers) { - + /** + * Returns information about the current reviewees of the given participant and a selector do add new one + * + * @param stdClass $user participant data + * @param workshop_api $workshop workshop record + * @param array $peers objects with properties to display picture and fullname + * @return string html code + */ + protected function reviewees_of_participant(stdClass $user, workshop_api $workshop, &$peers) { $o = ''; - if (!$workshop->assesswosubmission && is_null($user->submissionid)) { - $o .= '' . "\n"; - $o .= get_string('cantassesswosubmission', 'workshop'); - $o .= '' . "\n"; - } else { - $options = array(); - foreach ($workshop->get_peer_authors(true) as $author) { - $options[$author->id] = fullname($author); - } - if (!$workshop->useselfassessment) { - // students can not be reviewed by themselves in this workshop - if (isset($options[$user->id])) { - unset($options[$user->id]); - } - } - - $handler = $this->page->url->out_action() . '&mode=new&by=' . $user->id . '&of='; - $o .= popup_form($handler, $options, 'addreviewby' . $user->id, '', - get_string('chooseuser', 'workshop'), '', '', true, 'self', get_string('addreviewee', 'workshop')); - $o .= '' . "\n"; } + $handler = $this->page->url->out_action() . '&mode=new&by=' . $user->id . '&of='; + $o .= popup_form($handler, $options, 'addreviewby' . $user->id, '', + get_string('chooseuser', 'workshop'), '', '', true, 'self', get_string('addreviewee', 'workshop')); + $o .= $this->output->output_start_tag('ul', array()); + foreach ($user->reviewerof as $authorid => $assessmentid) { + $o .= $this->output->output_start_tag('li', array()); + $o .= print_user_picture($peers[$authorid], $this->page->course->id, null, 16, true); + $o .= fullname($peers[$authorid]); + + // delete + $handler = $this->page->url->out_action(array('mode' => 'del', 'what' => $assessmentid)); + $o .= $this->output->output_tag('a', array('href' => $handler), ' X '); + $o .= $this->output->output_end_tag('li'); + } + $o .= $this->output->output_end_tag('ul'); return $o; } } - - - - - - - diff --git a/mod/workshop/allocation/random/allocator.php b/mod/workshop/allocation/random/allocator.php index 228824d8b5..7ceca1c9b8 100644 --- a/mod/workshop/allocation/random/allocator.php +++ b/mod/workshop/allocation/random/allocator.php @@ -1,7 +1,7 @@ . - /** * Allocates the submissions randomly - * + * * @package mod-workshop * @copyright 2009 David Mudrak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -26,54 +25,301 @@ defined('MOODLE_INTERNAL') || die(); -require_once(dirname(dirname(__FILE__)) . '/lib.php'); // interface definition +global $CFG; // access to global variables during unit test + +require_once(dirname(dirname(__FILE__)) . '/lib.php'); // interface definition +require_once(dirname(dirname(dirname(__FILE__))) . '/locallib.php'); // workshop internal API +require_once(dirname(__FILE__) . '/settings_form.php'); // settings form + +/** + * Constants used to pass status messages between init() and ui() + */ +define('WORKSHOP_ALLOCATION_RANDOM_MSG_SUCCESS', 1); +/** + * Constants used in allocation settings form + */ +define('WORKSHOP_USERTYPE_AUTHOR', 1); +define('WORKSHOP_USERTYPE_REVIEWER', 2); +/** + * Allocates the submissions randomly + */ class workshop_random_allocator implements workshop_allocator { /** workshop instance */ protected $workshop; - /** array of allocations */ - protected $allocation = array(); - - public function __construct(stdClass $workshop) { - global $DB, $USER; + /** mform with settings */ + protected $mform; + /** + * @param stdClass $workshop Workshop record + */ + public function __construct(workshop $workshop) { $this->workshop = $workshop; + } - // submissions to be allocated - $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $this->workshop->id, 'example' => 0), - '', 'id,userid,title'); + /** + * Allocate submissions as requested by user + */ + public function init() { + global $PAGE; + $customdata = array(); + // $customdata['workshop'] = $this->workshop; + $this->mform = new workshop_random_allocator_form($PAGE->url, $customdata); + if ($this->mform->is_cancelled()) { + redirect($PAGE->url->out(false, array(), false)); + } else if ($settings = $this->mform->get_data()) { + // process validated data + if (!confirm_sesskey()) { + throw new moodle_workshop_exception($this->workshop, 'confirmsesskeybad'); + } + $o = array(); // list of output messages + $numofreviews = required_param('numofreviews', PARAM_INT); + $numper = required_param('numper', PARAM_INT); + $removecurrent = required_param('removecurrent', PARAM_INT); + $assesswosubmission = required_param('assesswosubmission', PARAM_INT); + $musthavesubmission = empty($assesswosubmission); + $addselfassessment = required_param('addselfassessment', PARAM_INT); - // dummy allocation - allocate all submissions to the current USER - foreach ($submissions as $submissionid => $submission) { - $this->allocation[$submissionid] = new stdClass; - $this->allocation[$submissionid]->submissionid = $submissionid; - $this->allocation[$submissionid]->title = $submission->title; - $this->allocation[$submissionid]->authorid = $submission->userid; - $this->allocation[$submissionid]->reviewerid = $USER->id; - $this->allocation[$submissionid]->assessmentid = NULL; + $authors = $this->workshop->get_peer_authors_by_group(); + $reviewers = $this->workshop->get_peer_reviewers_by_group($musthavesubmission); + $assessments = $this->workshop->get_assessments(); + + $newallocations = array(); // array of (reviewer,reviewee) tuples + + if ($numofreviews) { + $randomallocations = $this->random_allocation($authors, $reviewers, $assessments, $numofreviews, $numper); + $newallocations = array_merge($newallocations, $randomallocations); + $o[] = 'ok::' . get_string('numofrandomlyallocatedsubmissions', 'workshop', count($randomallocations)); + unset($randomallocations); + } + if ($addselfassessment) { + $selfallocations = $this->self_allocation($authors, $reviewers, $assessments); + $newallocations = array_merge($newallocations, $selfallocations); + $o[] = 'ok::' . get_string('numofselfallocatedsubmissions', 'workshop', count($selfallocations)); + unset($selfallocations); + } + if (empty($newallocations)) { + $o[] = 'info::' . get_string('noallocationtoadd', 'workshop'); + } else { + $this->add_new_allocations($newallocations, $authors, $reviewers); + foreach ($newallocations as $newallocation) { + list($reviewerid, $authorid) = each($newallocation); + $a = new stdClass(); + $a->reviewername = fullname($reviewers[0][$reviewerid]); + $a->authorname = fullname($authors[0][$authorid]); + $o[] = 'ok::ident::' . get_string('allocationaddeddetail', 'workshop', $a); + } + } + if ($removecurrent) { + $delassessments = $this->get_unkept_assessments($assessments, $newallocations, $addselfassessment); + // random allocator should not be able to delete assessments that have already been graded + // by reviewer + $o[] = 'info::' . get_string('numofdeallocatedassessment', 'workshop', count($delassessments)); + foreach ($delassessments as $delassessmentid) { + $a = new stdClass(); + $a->authorname = fullname((object)array( + 'lastname' => $assessments[$delassessmentid]->authorlastname, + 'firstname' => $assessments[$delassessmentid]->authorfirstname)); + $a->reviewername = fullname((object)array( + 'lastname' => $assessments[$delassessmentid]->reviewerlastname, + 'firstname' => $assessments[$delassessmentid]->reviewerfirstname)); + if (!is_null($assessments[$delassessmentid]->grade)) { + $o[] = 'error::ident::' . get_string('allocationdeallocategraded', 'workshop', $a); + unset($delassessments[$delassessmentid]); + } else { + $o[] = 'info::ident::' . get_string('assessmentdeleteddetail', 'workshop', $a); + } + } + $this->workshop->delete_assessment($delassessments); + } + return $o; + } else { + // this branch is executed if the form is submitted but the data + // doesn't validate and the form should be redisplayed + // or on the first display of the form. } + } - // already created assessments - $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), - '', 'id,submissionid,userid'); - - foreach ($assessments as $assessmentid => $assessment) { - $this->allocation[$assessment->submissionid]->assessmentid = $assessmentid; + /** + * Prints user interface + */ + public function ui(moodle_mod_workshop_renderer $wsoutput) { + global $OUTPUT; + + $m = optional_param('m', null, PARAM_INT); // status message code + $msg = new stdClass(); + if ($m == WORKSHOP_ALLOCATION_RANDOM_MSG_SUCCESS) { + $msg = (object)array('text' => get_string('randomallocationdone', 'workshop'), 'sty' => 'ok'); + } + + echo $OUTPUT->container_start('random-allocator'); + echo $wsoutput->status_message($msg); + $this->mform->display(); + echo $OUTPUT->container_end(); + } + + /** + * Allocates submissions to their authors for review + * + * If the submission has already been allocated, it is skipped. If the author is not found among + * reviewers, the submission is not assigned. + * + * @param array $authors as returned by {@see workshop_api::get_peer_authors_by_group()} + * @param array $reviewers as returned by {@see workshop_api::get_peer_reviewers_by_group()} + * @param array $assessments as returned by {@see workshop_api::get_assessments()} + * @return array of new allocations to be created, array of array(reviewerid => authorid) + */ + protected function self_allocation($authors=array(), $reviewers=array(), $assessments=array()) { + if (!isset($authors[0]) || !isset($reviewers[0])) { + // no authors or no reviewers + return array(); } + $alreadyallocated = array(); + foreach ($assessments as $assessment) { + if ($assessment->authorid == $assessment->reviewerid) { + $alreadyallocated[$assessment->authorid] = 1; + } + } + $add = array(); // list of new allocations to be created + foreach ($authors[0] as $authorid => $author) { + // for all authors in all groups + if (isset($reviewers[0][$authorid])) { + // if the author can be reviewer + if (!isset($alreadyallocated[$authorid])) { + // and the allocation does not exist yet, then + $add[] = array($authorid => $authorid); + } + } + } + return $add; } + /** + * Creates new assessment records + * + * @param array $newallocations pairs 'reviewerid' => 'authorid' + * @param array $dataauthors authors by group, group [0] contains all authors + * @param array $datareviewers reviewers by group, group [0] contains all reviewers + * @return bool + */ + protected function add_new_allocations($newallocations, $dataauthors, $datareviewers) { + global $DB; - public function init() { + $newallocations = $this->get_unique_allocations($newallocations); + $authorids = $this->get_author_ids($newallocations); + $submissions = $this->workshop->get_submission_by_author($authorids); + $submissions = $this->index_submissions_by_authors($submissions); + foreach ($newallocations as $newallocation) { + list($reviewerid, $authorid) = each($newallocation); + if (!isset($submissions[$authorid])) { + throw new moodle_workshop_exception($this->workshop, 'unabletoallocateauthorwithoutsubmission'); + } + $submission = $submissions[$authorid]; + $status = $this->workshop->add_allocation($submission, $reviewerid, true); + if (WORKSHOP_ALLOCATION_EXISTS == $status) { + debugging('newallocations array contains existing allocation, this should not happen'); + } + } } + /** + * Flips the structure of submission so it is indexed by userid attribute + * + * It is the caller's responsibility to make sure the submissions are not teacher + * examples so no user is the author of more submissions. + * + * @param string $submissions array indexed by submission id + * @return array indexed by author id + */ + protected function index_submissions_by_authors($submissions) { + $byauthor = array(); + if (is_array($submissions)) { + foreach ($submissions as $submissionid => $submission) { + if (isset($byauthor[$submission->userid])) { + throw new moodle_workshop_exception($this->workshop, 'moresubmissionsbyauthor'); + } + $byauthor[$submission->userid] = $submission; + } + } + return $byauthor; + } - public function ui() { - return 'TODO'; + /** + * Extracts unique list of authors' IDs from the structure of new allocations + * + * @param array $newallocations of pairs 'reviewerid' => 'authorid' + * @return array of authorids + */ + protected function get_author_ids($newallocations) { + $authors = array(); + foreach ($newallocations as $newallocation) { + $authorid = reset($newallocation); + if (!in_array($authorid, $authors)) { + $authors[] = $authorid; + } + } + return $authors; } + /** + * Removes duplicate allocations + * + * @param mixed $newallocations array of 'reviewerid' => 'authorid' pairs + * @return array + */ + protected function get_unique_allocations($newallocations) { + return array_merge(array_map('unserialize', array_unique(array_map('serialize', $newallocations)))); + } + + /** + * Returns the list of assessments to remove + * + * If user selects "removecurrentallocations", we should remove all current assessment records + * and insert new ones. But this would needlessly waste table ids. Instead, let us find only those + * assessments that have not been re-allocated in this run of allocation. So, the once-allocated + * submissions are kept with their original id. + * + * @param array $assessments list of current assessments + * @param mixed $newallocations array of 'reviewerid' => 'authorid' pairs + * @param bool $keepselfassessments do not remove already allocated self assessments + * @return array of assessments ids to be removed + */ + protected function get_unkept_assessments($assessments, $newallocations, $keepselfassessments) { + $keepids = array(); // keep these assessments + foreach ($assessments as $assessmentid => $assessment) { + $aaid = $assessment->authorid; + $arid = $assessment->reviewerid; + if (($keepselfassessments) && ($aaid == $arid)) { + $keepids[$assessmentid] = null; + continue; + } + foreach ($newallocations as $newallocation) { + list($nrid, $naid) = each($newallocation); + if (array($arid, $aaid) == array($nrid, $naid)) { + // re-allocation found - let us continue with the next assessment + $keepids[$assessmentid] = null; + continue 2; + } + } + } + return array_keys(array_diff_key($assessments, $keepids)); + } + + /** + * TODO: short description. + * + * @param array $authors + * @param resource $reviewers + * @param array $assessments + * @param mixed $numofreviews + * @param mixed $numper + * @return TODO + */ + protected function random_allocation($authors, $reviewers, $assessments, $numofreviews, $numper) { + } } diff --git a/mod/workshop/allocation/random/settings_form.php b/mod/workshop/allocation/random/settings_form.php new file mode 100644 index 0000000000..291c4bda46 --- /dev/null +++ b/mod/workshop/allocation/random/settings_form.php @@ -0,0 +1,70 @@ +. + +/** + * Random allocator settings form + * + * @package mod-workshop + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot.'/lib/formslib.php'); + +/** + * Allocator settings form + * + * This is used by {@see workshop_random_allocator::ui()} to set up allocation paramters. + * + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class workshop_random_allocator_form extends moodleform { + + /** + * Definition of the setting form elements + */ + public function definition() { + $mform = $this->_form; + //$workshop = $this->_customdata['workshop']; + + $mform->addElement('header', 'settings', get_string('allocationsettings', 'workshop')); + + $options_numofreviewes = array(0=>0,1=>1, 2=>2, 3=>2, 4=>4); + $options_numper = array(WORKSHOP_USERTYPE_AUTHOR => get_string('numperauthor', 'workshop'), + WORKSHOP_USERTYPE_REVIEWER => get_string('numperreviewer', 'workshop')); + $grpnumofreviews = array(); + $grpnumofreviews[] =& $mform->createElement('select', 'numofreviews', '', $options_numofreviewes); + $mform->setDefault('numofreviews', 0); + $grpnumofreviews[] =& $mform->createElement('select', 'numper', '', $options_numper); + $mform->setDefault('numper', WORKSHOP_USERTYPE_AUTHOR); + $mform->addGroup($grpnumofreviews, 'grpnumofreviews', get_string('numofreviews', 'workshop'), array(' '), false); + + $mform->addElement('advcheckbox', 'removecurrent', get_string('removecurrentallocations', 'workshop')); + $mform->setDefault('removecurrent', 0); + + $mform->addElement('advcheckbox', 'assesswosubmission', get_string('assesswosubmission', 'workshop')); + $mform->setDefault('assesswosubmission', 0); + + $mform->addElement('advcheckbox', 'addselfassessment', get_string('addselfassessment', 'workshop')); + $mform->setDefault('addselfassessment', 0); + + $this->add_action_buttons(); + } +} diff --git a/mod/workshop/allocation/random/simpletest/testallocator.php b/mod/workshop/allocation/random/simpletest/testallocator.php new file mode 100644 index 0000000000..50c1dbcddc --- /dev/null +++ b/mod/workshop/allocation/random/simpletest/testallocator.php @@ -0,0 +1,181 @@ +. + +/** + * Unit tests for mod/workshop/allocation/random/allocator.php + * + * @package mod-workshop + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +// Include the code to test +require_once($CFG->dirroot . '/mod/workshop/allocation/random/allocator.php'); + +/** + * Make protected methods we want to test public + */ +class testable_workshop_random_allocator extends workshop_random_allocator { + public function self_allocation($authors=array(), $reviewers=array(), $assessments=array()) { + return parent::self_allocation($authors, $reviewers, $assessments); + } + public function get_author_ids($newallocations) { + return parent::get_author_ids($newallocations); + } + public function index_submissions_by_authors($submissions) { + return parent::index_submissions_by_authors($submissions); + } + public function get_unique_allocations($newallocations) { + return parent::get_unique_allocations($newallocations); + } + public function get_unkept_assessments($assessments, $newallocations, $keepselfassessments) { + return parent::get_unkept_assessments($assessments, $newallocations, $keepselfassessments); + } +} + +class workshop_allocation_random_test extends UnitTestCase { + + /** workshop instance emulation */ + protected $workshop; + + /** allocator instance */ + protected $allocator; + + public function setUp() { + $this->workshop = new workshop((object)array('id' => 42), new stdClass()); + $this->allocator = new testable_workshop_random_allocator($this->workshop); + } + + public function tearDown() { + $this->allocator = null; + $this->workshop = null; + } + + public function test_self_allocation_empty_values() { + // fixture setup & exercise SUT & verify + $this->assertEqual(array(), $this->allocator->self_allocation()); + } + + public function test_self_allocation_equal_user_groups() { + // fixture setup + $authors = array(0 => array_fill_keys(array(4, 6, 10), new stdClass())); + $reviewers = array(0 => array_fill_keys(array(4, 6, 10), new stdClass())); + // exercise SUT + $newallocations = $this->allocator->self_allocation($authors, $reviewers); + // verify + $this->assertEqual(array(array(4 => 4), array(6 => 6), array(10 => 10)), $newallocations); + } + + public function test_self_allocation_different_user_groups() { + // fixture setup + $authors = array(0 => array_fill_keys(array(1, 4, 5, 10, 13), new stdClass())); + $reviewers = array(0 => array_fill_keys(array(4, 7, 10), new stdClass())); + // exercise SUT + $newallocations = $this->allocator->self_allocation($authors, $reviewers); + // verify + $this->assertEqual(array(array(4 => 4), array(10 => 10)), $newallocations); + } + + public function test_self_allocation_skip_existing() { + // fixture setup + $authors = array(0 => array_fill_keys(array(3, 4, 10), new stdClass())); + $reviewers = array(0 => array_fill_keys(array(3, 4, 10), new stdClass())); + $assessments = array(23 => (object)array('authorid' => 3, 'reviewerid' => 3)); + // exercise SUT + $newallocations = $this->allocator->self_allocation($authors, $reviewers, $assessments); + // verify + $this->assertEqual(array(array(4 => 4), array(10 => 10)), $newallocations); + } + + public function test_get_author_ids() { + // fixture setup + $newallocations = array(array(1 => 3), array(2 => 1), array(3 => 1)); + // exercise SUT & verify + $this->assertEqual(array(3, 1), $this->allocator->get_author_ids($newallocations)); + } + + public function test_index_submissions_by_authors() { + // fixture setup + $submissions = array( + 676 => (object)array('id' => 676, 'userid' => 23), + 121 => (object)array('id' => 121, 'userid' => 56), + ); + // exercise SUT + $submissions = $this->allocator->index_submissions_by_authors($submissions); + // verify + $this->assertEqual(array( + 23 => (object)array('id' => 676, 'userid' => 23), + 56 => (object)array('id' => 121, 'userid' => 56), + ), $submissions); + } + + public function test_index_submissions_by_authors_duplicate_author() { + // fixture setup + $submissions = array( + 14 => (object)array('id' => 676, 'userid' => 3), + 87 => (object)array('id' => 121, 'userid' => 3), + ); + // set expectation + $this->expectException('moodle_workshop_exception'); + // exercise SUT + $submissions = $this->allocator->index_submissions_by_authors($submissions); + } + + public function test_get_unique_allocations() { + // fixture setup + $newallocations = array(array(4 => 5), array(6 => 6), array(1 => 16), array(4 => 5), array(16 => 1)); + // exercise SUT + $newallocations = $this->allocator->get_unique_allocations($newallocations); + // verify + $this->assertEqual(array(array(4 => 5), array(6 => 6), array(1 => 16), array(16 => 1)), $newallocations); + } + + public function test_get_unkept_assessments_no_keep_selfassessments() { + // fixture setup + $assessments = array( + 23 => (object)array('authorid' => 3, 'reviewerid' => 3), + 45 => (object)array('authorid' => 5, 'reviewerid' => 11), + 12 => (object)array('authorid' => 6, 'reviewerid' => 3), + ); + $newallocations = array(array(4 => 5), array(11 => 5), array(1 => 16), array(4 => 5), array(16 => 1)); + // exercise SUT + $delassessments = $this->allocator->get_unkept_assessments($assessments, $newallocations, false); + // verify + // we want to keep $assessments[45] because it has been re-allocated + $this->assertEqual(array(23, 12), $delassessments); + } + + public function test_get_unkept_assessments_keep_selfassessments() { + // fixture setup + $assessments = array( + 23 => (object)array('authorid' => 3, 'reviewerid' => 3), + 45 => (object)array('authorid' => 5, 'reviewerid' => 11), + 12 => (object)array('authorid' => 6, 'reviewerid' => 3), + ); + $newallocations = array(array(4 => 5), array(11 => 5), array(1 => 16), array(4 => 5), array(16 => 1)); + // exercise SUT + $delassessments = $this->allocator->get_unkept_assessments($assessments, $newallocations, true); + // verify + // we want to keep $assessments[45] because it has been re-allocated + // we want to keep $assessments[23] because if is self assessment + $this->assertEqual(array(12), $delassessments); + } + + +} diff --git a/mod/workshop/assessment.php b/mod/workshop/assessment.php index 9b926434c3..7968eec80f 100644 --- a/mod/workshop/assessment.php +++ b/mod/workshop/assessment.php @@ -1,7 +1,7 @@ . - - + /** * Assess a submission or preview the assessment form * @@ -57,10 +56,10 @@ if ($preview = optional_param('preview', 0, PARAM_INT)) { } if (!$cm = get_coursemodule_from_instance('workshop', $workshop->id, $workshop->course)) { print_error('invalidcoursemodule'); - } + } if (!$course = $DB->get_record('course', array('id' => $cm->course))) { print_error('coursemisconf'); - } + } } require_login($course, false, $cm); @@ -93,8 +92,6 @@ $strategy = $workshop->grading_strategy_instance(); //todo $formdata = $strategy->load_assessment($assessment); - - // load the form to edit the grading strategy dimensions $mform = $strategy->get_assessment_form($selfurl, $mode); @@ -118,21 +115,21 @@ if ($mform->is_cancelled()) { // build the navigation and the header $navlinks = array(); -$navlinks[] = array('name' => get_string('modulenameplural', 'workshop'), - 'link' => "index.php?id=$course->id", +$navlinks[] = array('name' => get_string('modulenameplural', 'workshop'), + 'link' => "index.php?id=$course->id", 'type' => 'activity'); -$navlinks[] = array('name' => format_string($workshop->name), +$navlinks[] = array('name' => format_string($workshop->name), 'link' => "view.php?id=$cm->id", 'type' => 'activityinstance'); if ($mode == 'preview') { - $navlinks[] = array('name' => get_string('editingassessmentform', 'workshop'), + $navlinks[] = array('name' => get_string('editingassessmentform', 'workshop'), 'link' => $editurl, 'type' => 'title'); - $navlinks[] = array('name' => get_string('previewassessmentform', 'workshop'), + $navlinks[] = array('name' => get_string('previewassessmentform', 'workshop'), 'link' => '', 'type' => 'title'); } elseif ($mode == 'assessment') { - $navlinks[] = array('name' => get_string('assessingsubmission', 'workshop'), + $navlinks[] = array('name' => get_string('assessingsubmission', 'workshop'), 'link' => '', 'type' => 'title'); } diff --git a/mod/workshop/db/access.php b/mod/workshop/db/access.php index 1adddd0e82..13caa3a70a 100644 --- a/mod/workshop/db/access.php +++ b/mod/workshop/db/access.php @@ -1,7 +1,7 @@ . - /** * Capability definitions for the workshop module * @@ -192,5 +191,5 @@ $mod_workshop_capabilities = array( 'admin' => CAP_ALLOW ) ), - + ); diff --git a/mod/workshop/editform.php b/mod/workshop/editform.php index 87b9030b8c..eabdbe9ade 100644 --- a/mod/workshop/editform.php +++ b/mod/workshop/editform.php @@ -1,7 +1,7 @@ . - - + /** * Edit grading form in for a particular instance of workshop * @@ -28,14 +27,14 @@ require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); require_once(dirname(__FILE__).'/locallib.php'); $cmid = required_param('cmid', PARAM_INT); // course module id - + if (!$cm = get_coursemodule_from_id('workshop', $cmid)) { print_error('invalidcoursemodule'); -} - +} + if (!$course = $DB->get_record('course', array('id' => $cm->course))) { print_error('coursemisconf'); -} +} require_login($course, false, $cm); @@ -88,13 +87,13 @@ if ($mform->is_cancelled()) { // build the navigation and the header $navlinks = array(); -$navlinks[] = array('name' => get_string('modulenameplural', 'workshop'), - 'link' => "index.php?id=$course->id", +$navlinks[] = array('name' => get_string('modulenameplural', 'workshop'), + 'link' => "index.php?id=$course->id", 'type' => 'activity'); -$navlinks[] = array('name' => format_string($workshop->name), +$navlinks[] = array('name' => format_string($workshop->name), 'link' => "view.php?id=$cm->id", 'type' => 'activityinstance'); -$navlinks[] = array('name' => get_string('editingassessmentform', 'workshop'), +$navlinks[] = array('name' => get_string('editingassessmentform', 'workshop'), 'link' => '', 'type' => 'title'); $navigation = build_navigation($navlinks); diff --git a/mod/workshop/grading/accumulative/assessment_form.php b/mod/workshop/grading/accumulative/assessment_form.php index f4b10bd21b..ea36b5bb44 100644 --- a/mod/workshop/grading/accumulative/assessment_form.php +++ b/mod/workshop/grading/accumulative/assessment_form.php @@ -1,7 +1,7 @@ . - - + /** * This file defines an mform to assess a submission by accumulative grading strategy * @@ -29,7 +28,6 @@ defined('MOODLE_INTERNAL') || die(); //require_once(dirname(dirname(dirname(__FILE__))).'/lib.php'); // module library require_once(dirname(dirname(__FILE__)).'/assessment_form.php'); // parent class definition - /** * Class representing a form for assessing submissions by accumulative grading strategy * @@ -38,10 +36,10 @@ require_once(dirname(dirname(__FILE__)).'/assessment_form.php'); // parent cl class workshop_accumulative_assessment_form extends workshop_assessment_form { /** - * Define the elements to be displayed at the form + * Define the elements to be displayed at the form * * Called by the parent::definition() - * + * * @access protected * @return void */ @@ -72,5 +70,4 @@ class workshop_accumulative_assessment_form extends workshop_assessment_form { } - } diff --git a/mod/workshop/grading/accumulative/edit_form.php b/mod/workshop/grading/accumulative/edit_form.php index 71f8d19c8b..d530b66ac1 100644 --- a/mod/workshop/grading/accumulative/edit_form.php +++ b/mod/workshop/grading/accumulative/edit_form.php @@ -1,7 +1,7 @@ . - - + /** * This file defines an mform to edit accumulative grading strategy forms. * @@ -29,7 +28,6 @@ defined('MOODLE_INTERNAL') || die(); require_once(dirname(dirname(dirname(__FILE__))).'/lib.php'); // module library require_once(dirname(dirname(__FILE__)).'/edit_form.php'); // parent class definition - /** * Class for editing accumulative grading strategy forms. * @@ -38,10 +36,10 @@ require_once(dirname(dirname(__FILE__)).'/edit_form.php'); // parent class de class workshop_edit_accumulative_strategy_form extends workshop_edit_strategy_form { /** - * Define the elements to be displayed at the form + * Define the elements to be displayed at the form * * Called by the parent::definition() - * + * * @access protected * @return void */ @@ -52,16 +50,16 @@ class workshop_edit_accumulative_strategy_form extends workshop_edit_strategy_fo $repeated = array(); $repeated[] =& $mform->createElement('hidden', 'dimensionid', 0); - $repeated[] =& $mform->createElement('header', 'dimension', + $repeated[] =& $mform->createElement('header', 'dimension', get_string('dimensionnumberaccumulative', 'workshop', '{no}')); $repeated[] =& $mform->createElement('htmleditor', 'description', get_string('dimensiondescription', 'workshop'), array()); $repeated[] =& $mform->createElement('select', 'grade', get_string('grade'), $gradeoptions); $repeated[] =& $mform->createElement('select', 'weight', get_string('dimensionweight', 'workshop'), $weights); - + $repeatedoptions = array(); $repeatedoptions['description']['type'] = PARAM_CLEANHTML; - $repeatedoptions['description']['helpbutton'] = array('dimensiondescription', + $repeatedoptions['description']['helpbutton'] = array('dimensiondescription', get_string('dimensiondescription', 'workshop'), 'workshop'); $repeatedoptions['grade']['default'] = 10; $repeatedoptions['weight']['default'] = 1; @@ -72,5 +70,4 @@ class workshop_edit_accumulative_strategy_form extends workshop_edit_strategy_fo $this->repeat_elements($repeated, $numofdisplaydimensions, $repeatedoptions, 'numofdimensions', 'adddimensions', $numofdimensionstoadd, get_string('addmoredimensionsaccumulative', 'workshop', $numofdimensionstoadd)); } - } diff --git a/mod/workshop/grading/accumulative/simpletest/teststrategy.php b/mod/workshop/grading/accumulative/simpletest/teststrategy.php index 180f704115..ff0b353b06 100644 --- a/mod/workshop/grading/accumulative/simpletest/teststrategy.php +++ b/mod/workshop/grading/accumulative/simpletest/teststrategy.php @@ -1,7 +1,7 @@ . - - + /** * Unit tests for (some of) mod/workshop/grading/accumulative/strategy.php * @@ -29,7 +28,6 @@ defined('MOODLE_INTERNAL') || die(); // Include the code to test require_once($CFG->dirroot . '/mod/workshop/grading/accumulative/strategy.php'); - /** * Test subclass that makes all the protected methods we want to test public */ @@ -45,7 +43,6 @@ class testable_workshop_accumulative_strategy extends workshop_accumulative_stra } - class workshop_accumulative_strategy_test extends UnitTestCase { /** workshop instance emulation */ @@ -62,7 +59,7 @@ class workshop_accumulative_strategy_test extends UnitTestCase { /** setup testing environment */ public function setUp() { - + $this->workshop = new stdClass; $this->workshop->id = 42; $this->workshop->strategy = 'accumulative'; @@ -103,16 +100,14 @@ class workshop_accumulative_strategy_test extends UnitTestCase { } - public function tearDown() { $this->strategy = null; $this->rawform = null; $this->rawdb = null; } - public function test_cook_dimension_records() { - + $cooked = $this->strategy->_cook_dimension_records($this->rawdb); $this->assertIsA($cooked, 'stdClass'); $cooked = (array)$cooked; @@ -130,7 +125,6 @@ class workshop_accumulative_strategy_test extends UnitTestCase { $this->assertEqual($cooked['weight[1]'], 1); } - public function test_cook_form_data() { $cooked = $this->strategy->_cook_form_data($this->rawform); @@ -163,5 +157,5 @@ class workshop_accumulative_strategy_test extends UnitTestCase { 'weight' => 1, )); } - + } diff --git a/mod/workshop/grading/accumulative/strategy.php b/mod/workshop/grading/accumulative/strategy.php index 3c1ac32eae..2aa4fbf274 100644 --- a/mod/workshop/grading/accumulative/strategy.php +++ b/mod/workshop/grading/accumulative/strategy.php @@ -1,7 +1,7 @@ . - - + /** * This file defines a class with accumulative grading strategy logic * @@ -28,7 +27,6 @@ defined('MOODLE_INTERNAL') || die(); require_once(dirname(dirname(__FILE__)) . '/strategy.php'); // parent class - /** * Accumulative grading strategy logic. */ @@ -42,13 +40,12 @@ class workshop_accumulative_strategy extends workshop_base_strategy { return $this->_cook_dimension_records($dims); } - /** * Transpones the dimension data from DB so the assessment form editor can be populated by set_data * * Called internally from load_form(). Could be private but keeping protected * for unit testing purposes. - * + * * @param array $raw Array of raw dimension records as fetched by get_record() * @return array Array of fields data to be used by the mform set_data */ @@ -67,7 +64,6 @@ class workshop_accumulative_strategy extends workshop_base_strategy { return (object)$formdata; } - /** * Save the assessment dimensions into database * @@ -110,15 +106,14 @@ class workshop_accumulative_strategy extends workshop_base_strategy { $DB->delete_records_list('workshop_forms_' . $this->name, 'id', $todelete); } - /** * Prepares data returned by mform so they can be saved into database * - * It automatically adds some columns into every record. The sorting is + * It automatically adds some columns into every record. The sorting is * done by the order of the returned array and starts with 1. * Called internally from save_form() only. Could be private but * keeping protected for unit testing purposes. - * + * * @param object $raw Raw data returned by mform * @return array Array of objects to be inserted/updated in DB */ @@ -139,5 +134,4 @@ class workshop_accumulative_strategy extends workshop_base_strategy { return $cook; } - } diff --git a/mod/workshop/grading/assessment_form.php b/mod/workshop/grading/assessment_form.php index bedb1d4738..0599923fb5 100644 --- a/mod/workshop/grading/assessment_form.php +++ b/mod/workshop/grading/assessment_form.php @@ -1,7 +1,7 @@ . - - + /** * This file defines a base class for all assessment forms * @@ -28,19 +27,18 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir . '/formslib.php'); // parent class definition - /** * Base class for all assessment forms * - * This defines the common fields that all assessment forms need. - * Strategies should define their own class that inherits from this one, and + * This defines the common fields that all assessment forms need. + * Strategies should define their own class that inherits from this one, and * implements the definition_inner() method. - * + * * @uses moodleform */ class workshop_assessment_form extends moodleform { - /** object Strategy logic instance */ + /** object Strategy logic instance */ protected $strategy; /** array Assessment form fields defined by teacher */ @@ -52,11 +50,11 @@ class workshop_assessment_form extends moodleform { /** * Add the fields that are common for all grading strategies. * - * If the strategy does not support all these fields, then you can override - * this method and remove the ones you don't want with + * If the strategy does not support all these fields, then you can override + * this method and remove the ones you don't want with * $mform->removeElement(). * Strategy subclassess should define their own fields in definition_inner() - * + * * @access public * @return void */ @@ -85,7 +83,6 @@ class workshop_assessment_form extends moodleform { $mform->closeHeaderBefore('buttonar'); } - /** * Add any strategy specific form fields. * diff --git a/mod/workshop/grading/edit_form.php b/mod/workshop/grading/edit_form.php index ac5ebabfdf..57f6fae6f6 100644 --- a/mod/workshop/grading/edit_form.php +++ b/mod/workshop/grading/edit_form.php @@ -1,7 +1,7 @@ . - - + /** * This file defines a base class for all grading strategy editing forms. * @@ -28,29 +27,28 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir . '/formslib.php'); // parent class definition - /** * Base class for editing all the strategy grading forms. * - * This defines the common fields that all strategy grading forms need. - * Strategies should define their own class that inherits from this one, and + * This defines the common fields that all strategy grading forms need. + * Strategies should define their own class that inherits from this one, and * implements the definition_inner() method. - * + * * @uses moodleform */ class workshop_edit_strategy_form extends moodleform { - /** strategy logic instance that this class is editor of */ + /** strategy logic instance that this class is editor of */ protected $strategy; /** * Add the fields that are common for all grading strategies. * - * If the strategy does not support all these fields, then you can override - * this method and remove the ones you don't want with + * If the strategy does not support all these fields, then you can override + * this method and remove the ones you don't want with * $mform->removeElement(). * Stretegy subclassess should define their own fields in definition_inner() - * + * * @access public * @return void */ @@ -78,7 +76,6 @@ class workshop_edit_strategy_form extends moodleform { $mform->closeHeaderBefore('buttonar'); } - /** * Add any strategy specific form fields. * diff --git a/mod/workshop/grading/noerrors/edit_form.php b/mod/workshop/grading/noerrors/edit_form.php index cc7403444f..b3634ba598 100644 --- a/mod/workshop/grading/noerrors/edit_form.php +++ b/mod/workshop/grading/noerrors/edit_form.php @@ -1,7 +1,7 @@ . - - + /** * This file defines an mform to edit "Number of errors" grading strategy forms. * @@ -29,7 +28,6 @@ defined('MOODLE_INTERNAL') || die(); require_once(dirname(dirname(dirname(__FILE__))).'/lib.php'); // module library require_once(dirname(dirname(__FILE__)).'/edit_form.php'); // parent class definition - /** * Class for editing "Number of errors" grading strategy forms. * @@ -38,10 +36,10 @@ require_once(dirname(dirname(__FILE__)).'/edit_form.php'); // parent class de class workshop_edit_noerrors_strategy_form extends workshop_edit_strategy_form { /** - * Define the elements to be displayed at the form + * Define the elements to be displayed at the form * * Called by the parent::definition() - * + * * @access protected * @return void */ @@ -52,17 +50,17 @@ class workshop_edit_noerrors_strategy_form extends workshop_edit_strategy_form { $repeated = array(); $repeated[] =& $mform->createElement('hidden', 'dimensionid', 0); - $repeated[] =& $mform->createElement('header', 'dimension', + $repeated[] =& $mform->createElement('header', 'dimension', get_string('dimensionnumbernoerrors', 'workshop', '{no}')); $repeated[] =& $mform->createElement('htmleditor', 'description', get_string('dimensiondescription', 'workshop'), array()); $repeated[] =& $mform->createElement('text', 'grade0', get_string('noerrorsgrade0', 'workshop'), array('size'=>'15')); $repeated[] =& $mform->createElement('text', 'grade1', get_string('noerrorsgrade1', 'workshop'), array('size'=>'15')); $repeated[] =& $mform->createElement('select', 'weight', get_string('dimensionweight', 'workshop'), $weights); - + $repeatedoptions = array(); $repeatedoptions['description']['type'] = PARAM_CLEANHTML; - $repeatedoptions['description']['helpbutton'] = array('dimensiondescription', + $repeatedoptions['description']['helpbutton'] = array('dimensiondescription', get_string('dimensiondescription', 'workshop'), 'workshop'); $repeatedoptions['grade0']['type'] = PARAM_TEXT; $repeatedoptions['grade0']['default'] = $workshopconfig->noerrorsgrade0; @@ -72,25 +70,25 @@ class workshop_edit_noerrors_strategy_form extends workshop_edit_strategy_form { $numofdimensionstoadd = 2; $numofinitialdimensions = 3; - $numofdisplaydimensions = max($this->strategy->get_number_of_dimensions() + $numofdimensionstoadd, + $numofdisplaydimensions = max($this->strategy->get_number_of_dimensions() + $numofdimensionstoadd, $numofinitialdimensions); - $numofdisplaydimensions = $this->repeat_elements($repeated, $numofdisplaydimensions, $repeatedoptions, + $numofdisplaydimensions = $this->repeat_elements($repeated, $numofdisplaydimensions, $repeatedoptions, 'numofdimensions', 'adddimensions', $numofdimensionstoadd, - get_string('addmoredimensionsnoerrors', 'workshop', $numofdimensionstoadd)); + get_string('addmoredimensionsnoerrors', 'workshop', $numofdimensionstoadd)); $mform->addElement('header', 'mappingheader', get_string('noerrorsgrademapping', 'workshop')); - $mform->addElement('static', 'mappinginfo', get_string('noerrorsmaperror', 'workshop'), + $mform->addElement('static', 'mappinginfo', get_string('noerrorsmaperror', 'workshop'), get_string('noerrorsmapgrade', 'workshop')); $percents = array(); $percents[''] = ''; for ($i = 100; $i >= 0; $i--) { $percents[$i] = get_string('percents', 'workshop', $i); } - $mform->addElement('static', 'mappingzero', 0, get_string('percents', 'workshop', 100)); + $mform->addElement('static', 'mappingzero', 0, get_string('percents', 'workshop', 100)); $mform->addElement('hidden', 'map[0]', 100); for ($i = 1; $i <= $numofdisplaydimensions; $i++) { $selects = array(); $selects[] =& $mform->createElement('select', "map[$i]", $i, $percents); - $selects[] =& $mform->createElement('static', "mapdefault[$i]", '', + $selects[] =& $mform->createElement('static', "mapdefault[$i]", '', get_string('percents', 'workshop', floor(100 - $i * 100 / $numofdisplaydimensions))); $mform->addGroup($selects, "grademapping$i", $i, array(' '), false); $mform->setDefault("map[$i]", ''); @@ -98,5 +96,4 @@ class workshop_edit_noerrors_strategy_form extends workshop_edit_strategy_form { } - } diff --git a/mod/workshop/grading/noerrors/strategy.php b/mod/workshop/grading/noerrors/strategy.php index 0243f9e91c..149e2a2b5e 100644 --- a/mod/workshop/grading/noerrors/strategy.php +++ b/mod/workshop/grading/noerrors/strategy.php @@ -1,7 +1,7 @@ . - - + /** * This file defines a class with "Number of errors" grading strategy logic * @@ -28,7 +27,6 @@ defined('MOODLE_INTERNAL') || die(); require_once(dirname(dirname(__FILE__)) . '/strategy.php'); // parent class - /** * "Number of errors" grading strategy logic. */ @@ -43,15 +41,14 @@ class workshop_noerrors_strategy extends workshop_base_strategy { return $this->_cook_database_records($dims, $maps); } - /** * Transpones the dimension data from DB so the assessment form editor can be populated by set_data * * Called internally from load_form(). Could be private but keeping protected * for unit testing purposes. - * + * * @param array $dims Array of raw dimension records as fetched by get_record() - * @param array $maps Array of grade mappings + * @param array $maps Array of grade mappings * @return array Object to be used by the mform set_data */ protected function _cook_database_records(array $dims, array $maps) { @@ -78,7 +75,6 @@ class workshop_noerrors_strategy extends workshop_base_strategy { return (object)$formdata; } - /** * Save the definition of a "Number of errors" grading form * @@ -127,7 +123,7 @@ class workshop_noerrors_strategy extends workshop_base_strategy { } unset($currentx); $todelete = array(); - + foreach ($data->map as $nonegative => $grade) { if ($nonegative == 0) { // no negative response automatically maps to 100%, do not save such mapping @@ -153,7 +149,7 @@ class workshop_noerrors_strategy extends workshop_base_strategy { } else { list($insql, $params) = array('', array()); } - $sql = 'DELETE FROM {workshop_forms_noerrors_map} + $sql = 'DELETE FROM {workshop_forms_noerrors_map} WHERE ((' . $insql . 'nonegative > ?) AND (workshopid = ?))'; $params[] = count($data->map) - 1; $params[] = $this->workshop->id; @@ -162,15 +158,14 @@ class workshop_noerrors_strategy extends workshop_base_strategy { } } - /** * Prepares dimensions data returned by mform so they can be saved into database * - * It automatically adds some columns into every record. The sorting is + * It automatically adds some columns into every record. The sorting is * done by the order of the returned array and starts with 1. * Called internally from save_form() only. Could be private but * keeping protected for unit testing purposes. - * + * * @param object $raw Raw data returned by mform * @return array Array of objects to be inserted/updated in DB */ @@ -192,5 +187,4 @@ class workshop_noerrors_strategy extends workshop_base_strategy { return $cook; } - } diff --git a/mod/workshop/grading/strategy.php b/mod/workshop/grading/strategy.php index 22afba2436..e5f5185912 100644 --- a/mod/workshop/grading/strategy.php +++ b/mod/workshop/grading/strategy.php @@ -1,7 +1,7 @@ . - - + /** * This file defines a base class for all grading strategy logic * @@ -33,7 +32,7 @@ interface workshop_strategy { /** * Factory method returning an instance of an assessment form editor class - * + * * The returned class will probably expand the base workshop_edit_strategy_form. The number of * dimensions that will be passed by set_data() must be already known here becase the * definition() of the form has to know the number and it is called before set_data(). @@ -44,11 +43,10 @@ interface workshop_strategy { */ public function get_edit_strategy_form($actionurl=null); - /** * Load the assessment dimensions and other grading form elements - * - * Assessment dimension (also know as assessment element) represents one aspect or criterion + * + * Assessment dimension (also know as assessment element) represents one aspect or criterion * to be evaluated. Each dimension consists of a set of form fields. Strategy-specific information * are saved in workshop_forms_{strategyname} tables. * The returned object is passed to the mform set_data() method. @@ -58,11 +56,10 @@ interface workshop_strategy { */ public function load_form(); - /** * Save the assessment dimensions and other grading form elements * - * Assessment dimension (also know as assessment element) represents one aspect or criterion + * Assessment dimension (also know as assessment element) represents one aspect or criterion * to be evaluated. Each dimension consists of a set of form fields. Strategy-specific information * are saved in workshop_forms_{strategyname} tables. * @@ -72,18 +69,15 @@ interface workshop_strategy { */ public function save_form(stdClass $data); - /** * Return the number of assessment dimensions defined in the instance of the strategy - * + * * @return int Zero or positive integer */ public function get_number_of_dimensions(); } - - /** * Base class for grading strategy logic * @@ -102,8 +96,8 @@ abstract class workshop_base_strategy implements workshop_strategy { protected $nodimensions; /** - * Constructor - * + * Constructor + * * @param object $workshop The workshop instance record * @access public * @return void @@ -115,7 +109,6 @@ abstract class workshop_base_strategy implements workshop_strategy { $this->nodimensions = null; } - /** * Factory method returning an instance of an assessment form editor class * @@ -126,7 +119,7 @@ abstract class workshop_base_strategy implements workshop_strategy { */ public function get_edit_strategy_form($actionurl=null) { global $CFG; // needed because the included files use it - + $strategyform = dirname(__FILE__) . '/' . $this->name . '/edit_form.php'; if (file_exists($strategyform)) { require_once($strategyform); @@ -145,10 +138,9 @@ abstract class workshop_base_strategy implements workshop_strategy { } - /** - * By default, the number of loaded dimensions is set by load_form() - * + * By default, the number of loaded dimensions is set by load_form() + * * @access public * @return Array of records */ @@ -156,7 +148,6 @@ abstract class workshop_base_strategy implements workshop_strategy { return $this->nodimensions; } - /** * Factory method returning an instance of an assessment form * @@ -168,7 +159,7 @@ abstract class workshop_base_strategy implements workshop_strategy { */ public function get_assessment_form($actionurl=null, $mode='preview') { global $CFG; // needed because the included files use it - + $assessmentform = dirname(__FILE__) . '/' . $this->name . '/assessment_form.php'; if (is_readable($assessmentform)) { require_once($assessmentform); @@ -189,5 +180,4 @@ abstract class workshop_base_strategy implements workshop_strategy { } - } diff --git a/mod/workshop/index.php b/mod/workshop/index.php index a4d3ac984c..c1decbbde7 100644 --- a/mod/workshop/index.php +++ b/mod/workshop/index.php @@ -1,7 +1,7 @@ . - - + /** * This is a one-line short description of the file * @@ -54,7 +53,7 @@ $navlinks[] = array('name' => get_string('modulenameplural', 'workshop'), 'link' => '', 'type' => 'activity'); $navigation = build_navigation($navlinks); - + echo $OUTPUT->header($navigation); /// Get all the appropriate data diff --git a/mod/workshop/lang/en_utf8/workshop.php b/mod/workshop/lang/en_utf8/workshop.php index 67c7ee1461..aa94d6f9c4 100644 --- a/mod/workshop/lang/en_utf8/workshop.php +++ b/mod/workshop/lang/en_utf8/workshop.php @@ -1,7 +1,7 @@ . - /** * English strings for Workshop module * @@ -40,18 +39,28 @@ $string[''] = ''; $string[''] = ''; $string[''] = ''; $string[''] = ''; +$string[''] = ''; +$string[''] = ''; +$string[''] = ''; +$string[''] = ''; +$string[''] = ''; +$string[''] = ''; $string['accesscontrol'] = 'Access control'; $string['addmoredimensionsaccumulative'] = 'Blanks for $a more aspects'; $string['addmoredimensionsnoerrors'] = 'Blanks for $a more assertions'; $string['addreviewee'] = 'Add reviewee'; $string['addreviewer'] = 'Add reviewer'; +$string['addselfassessment'] = 'Add self-assessments'; $string['addsubmissiontoreview'] = 'Assign submission'; $string['agreeassessments'] = 'Assessments must be agreed'; $string['agreeassessmentsdesc'] = 'Authors may comment assessments of their work and agree/disagree with it'; +$string['allocationaddeddetail'] = 'New assessment to be done: $a->reviewername is reviewer of $a->authorname'; $string['allocationadded'] = 'The submission has been successfully allocated'; +$string['allocationdeallocategraded'] = 'Unable to deallocate already graded assessment: reviewer $a->reviewername, submission author: $a->authorname'; $string['allocationexists'] = 'The allocation already exists'; $string['allocationmanual'] = 'Manual allocation'; $string['allocationrandom'] = 'Random allocation'; +$string['allocationsettings'] = 'Allocation settings'; $string['allocation'] = 'Submission allocation'; $string['allocationview'] = 'View current allocations'; $string['areyousuretodeallocate'] = 'Are you sure you want deallocate the selected assessment?'; @@ -59,15 +68,14 @@ $string['areyousuretodeallocategraded'] = 'You are going to remove the assessmen $string['assessallexamples'] = 'Assess all examples'; $string['assessingsubmission'] = 'Assessing submission'; $string['assessmentcomps'] = 'Required level of assessments similarity'; -$string['assessmentdeleted'] = 'Submission deallocated and assessment deleted'; +$string['assessmentdeleted'] = 'Assessment deallocated'; +$string['assessmentdeleteddetail'] = 'Assessment deallocated: $a->reviewername is no longer reviewer of $a->authorname'; $string['assessmentend'] = 'End of assessment phase'; $string['assessmentform'] = 'Assessment form'; $string['assessmentsettings'] = 'Assessment settings'; $string['assessmentstart'] = 'Start of assessment phase'; -$string['assesswosubmission'] = 'Assess without submission'; -$string['assesswosubmissiondesc'] = 'Users can assess peers even without their own submission'; +$string['assesswosubmission'] = 'Users can assess without their own submission'; $string['backtoeditform'] = 'Back to editing form'; -$string['cantassesswosubmission'] = 'Users can\'t assess without own submission in this workshop'; $string['comparisonhigh'] = 'High'; $string['comparisonlow'] = 'Low'; $string['comparisonnormal'] = 'Normal'; @@ -109,6 +117,7 @@ $string['modulenameplural'] = 'Workshops'; $string['modulename'] = 'Workshop'; $string['nattachments'] = 'Maximum number of submission attachments'; $string['nexassessments'] = 'Number of required assessments of examples'; +$string['noallocationtoadd'] = 'No allocations to add'; $string['noerrorsgrademapping'] = 'Grade mapping table'; $string['noerrorsgrade0default'] = 'No'; $string['noerrorsgrade0'] = 'Word for the error'; @@ -121,12 +130,19 @@ $string['nosubmissionfound'] = 'No submission found for this user'; $string['nosubmissions'] = 'No submissions yet in this workshop'; $string['nothingtoreview'] = 'Nothing to review'; $string['nsassessments'] = 'Number of required assessments of other users\' work'; +$string['numofdeallocatedassessment'] = 'Deallocating $a assessment(s)'; +$string['numofreviews'] = 'Number of reviews'; +$string['numofselfallocatedsubmissions'] = 'Self-allocating $a submission(s)'; +$string['numperauthor'] = 'per submission'; +$string['numperreviewer'] = 'per reviewer'; $string['participant'] = 'Participant'; $string['participantrevierof'] = 'Participant is reviewer of'; $string['participantreviewedby'] = 'Participant is reviewed by'; $string['percents'] = '$a%'; $string['previewassessmentform'] = 'Preview'; +$string['randomallocationdone'] = 'Random allocation done'; $string['releasegrades'] = 'Push final grades into the gradebook'; +$string['removecurrentallocations'] = 'Remove current allocations'; $string['requirepassword'] = 'Require password'; $string['saveandclose'] = 'Save and close'; $string['saveandcontinue'] = 'Save and continue editing'; @@ -150,5 +166,6 @@ $string['usepeerassessmentdesc'] = 'Users perform peer assessment of others\' wo $string['usepeerassessment'] = 'Use peer assessment'; $string['useselfassessmentdesc'] = 'Users perform self assessment of their own work'; $string['useselfassessment'] = 'Use self assessment'; +$string['withoutsubmission'] = 'Warning - reviewer without own submission'; $string['workshopfeatures'] = 'Workshop features'; $string['workshopname'] = 'Workshop name'; diff --git a/mod/workshop/lib.php b/mod/workshop/lib.php index 73b6aac4f8..3b719c258b 100644 --- a/mod/workshop/lib.php +++ b/mod/workshop/lib.php @@ -1,7 +1,7 @@ . - - + /** * Library of interface functions and constants for module workshop * - * All the core Moodle functions, neeeded to allow the module to work + * All the core Moodle functions, neeeded to allow the module to work * integrated in Moodle should be placed here. - * All the workshop specific functions, needed to implement all the module - * logic, should go to locallib.php. This will help to save some memory when + * All the workshop specific functions, needed to implement all the module + * logic, should go to locallib.php. This will help to save some memory when * Moodle is performing actions across all modules. * * @package mod-workshop @@ -39,7 +38,6 @@ define('WORKSHOP_EXAMPLES_VOLUNTARY', 0); define('WORKSHOP_EXAMPLES_BEFORE_SUBMISSION', 1); define('WORKSHOP_EXAMPLES_BEFORE_ASSESSMENT', 2); - /** * The internal codes of the required level of assessment similarity */ @@ -49,7 +47,6 @@ define('WORKSHOP_COMPARISON_NORMAL', 2); /* f = 2.50 */ define('WORKSHOP_COMPARISON_HIGH', 3); /* f = 3.00 */ define('WORKSHOP_COMPARISON_VERYHIGH', 4); /* f = 5.00 */ - /** * The base class of workshop instances * @@ -83,7 +80,6 @@ class workshop { $this->cm = $cm; } - /** * Saves a new instance of the workshop into the database * @@ -105,7 +101,6 @@ class workshop { } } - /** * Given an object containing all the necessary data, * (defined by the form in mod_form.php) this function @@ -119,7 +114,6 @@ function workshop_add_instance($data) { return workshop::add_instance($data); } - /** * Given an object containing all the necessary data, * (defined by the form in mod_form.php) this function @@ -137,7 +131,6 @@ function workshop_update_instance($workshop) { return $DB->update_record('workshop', $workshop); } - /** * Given an ID of an instance of this module, * this function will permanently delete the instance @@ -164,7 +157,6 @@ function workshop_delete_instance($id) { return $result; } - /** * Return a small object with summary information about what a * user has done with a given particular instance of this module @@ -182,7 +174,6 @@ function workshop_user_outline($course, $user, $mod, $workshop) { return $return; } - /** * Print a detailed representation of what a user has done with * a given particular instance of this module, for user activity reports. @@ -194,7 +185,6 @@ function workshop_user_complete($course, $user, $mod, $workshop) { return true; } - /** * Given a course and a time, this module should find recent activity * that has occurred in workshop activities and print it out. @@ -207,7 +197,6 @@ function workshop_print_recent_activity($course, $isteacher, $timestart) { return false; // True if anything was printed, otherwise false } - /** * Function to be run periodically according to the moodle cron * This function searches for things that need to be done, such @@ -220,7 +209,6 @@ function workshop_cron () { return true; } - /** * Must return an array of user records (all data) who are participants * for a given instance of workshop. Must include every user involved @@ -234,7 +222,6 @@ function workshop_get_participants($workshopid) { return false; } - /** * This function returns if a scale is being used by one workshop * if it has support for grading and scales. Commented code should be @@ -257,7 +244,6 @@ function workshop_scale_used($workshopid, $scaleid) { return $return; } - /** * Checks if scale is being used by any instance of workshop. * This function was added in 1.9 @@ -274,7 +260,6 @@ function workshop_scale_used_anywhere($scaleid) { } } - /** * Execute post-install custom actions for the module * This function was added in 1.9 @@ -285,7 +270,6 @@ function workshop_install() { return true; } - /** * Execute post-uninstall custom actions for the module * This function was added in 1.9 @@ -296,6 +280,25 @@ function workshop_uninstall() { return true; } +/** + * Returns the information if the module supports a feature + * + * @see plugin_supports() in lib/moodlelib.php + * @todo review and add features + * @param string $feature FEATURE_xx constant for requested feature + * @return mixed true if the feature is supported, null if unknown + */ +function workshop_supports($feature) { + switch($feature) { + case FEATURE_GROUPS: return true; + case FEATURE_GROUPINGS: return true; + case FEATURE_GROUPMEMBERSONLY: return true; + case FEATURE_MOD_INTRO: return true; + case FEATURE_GRADE_HAS_GRADE: return true; + default: return null; + } +} + //////////////////////////////////////////////////////////////////////////////// // Other functions needed by Moodle core follows. They can't be put into // // locallib.php because they are used by some core scripts (like modedit.php) // @@ -305,10 +308,10 @@ function workshop_uninstall() { /** * Return an array of numeric values that can be used as maximum grades * - * Used at several places where maximum grade for submission and grade for + * Used at several places where maximum grade for submission and grade for * assessment are defined via a HTML select form element. By default it returns * an array 0, 1, 2, ..., 98, 99, 100. - * + * * @access public * @return array Array of integers */ @@ -321,7 +324,6 @@ function workshop_get_maxgrades() { return $grades; } - /** * Return an array of possible numbers of assessments to be done * @@ -341,10 +343,9 @@ function workshop_get_numbers_of_assessments() { return $options; } - /** * Return an array of possible values for weight of teacher assessment - * + * * @return array Array of integers 0, 1, 2, ..., 10 */ function workshop_get_teacher_weights() { @@ -356,10 +357,9 @@ function workshop_get_teacher_weights() { return $weights; } - /** * Return an array of possible values of assessment dimension weight - * + * * @return array Array of integers 0, 1, 2, ..., 16 */ function workshop_get_dimension_weights() { @@ -371,7 +371,6 @@ function workshop_get_dimension_weights() { return $weights; } - /** * Return an array of the localized grading strategy names * @@ -389,7 +388,6 @@ function workshop_get_strategies() { return $forms; } - /** * Return an array of available example assessment modes * @@ -405,7 +403,6 @@ function workshop_get_example_modes() { return $modes; } - /** * Return array of assessment comparison levels * diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index 079c9bee7b..015fb88bc8 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -1,7 +1,7 @@ . - - + /** * Library of internal classes and functions for module workshop * - * All the workshop specific functions, needed to implement the module + * All the workshop specific functions, needed to implement the module * logic, should go to here. Instead of having bunch of function named - * workshop_something() taking the workshop instance as the first + * workshop_something() taking the workshop instance as the first * parameter, we use a class workshop_api that provides all methods. - * + * * @package mod-workshop * @copyright 2009 David Mudrak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -34,13 +33,11 @@ defined('MOODLE_INTERNAL') || die(); require_once(dirname(__FILE__).'/lib.php'); // we extend this library here define('WORKSHOP_ALLOCATION_EXISTS', -1); // return status of {@link add_allocation} -define('WORKSHOP_ALLOCATION_WOSUBMISSION', -2); // return status of {@link add_allocation} - /** * Full-featured workshop API * - * This extends the module base API and adds the internal methods that are called + * This extends the module base API and adds the internal methods that are called * from the module itself. The class should be initialized right after you get * $workshop and $cm records at the begining of the script. */ @@ -60,17 +57,16 @@ class workshop_api extends workshop { parent::__construct($instance, $cm); } - /** * Fetches all users with the capability mod/workshop:submit in the current context * * Static variables used to cache the results. The returned objects contain id, lastname * and firstname properties and are ordered by lastname,firstname - * - * @param bool $musthavesubmission If true, returns only users with existing submission. All possible authors otherwise. - * @return array Array of '(int)userid => (stdClass)userinfo' + * + * @param bool $musthavesubmission If true, return only users who have already submitted. All possible authors otherwise. + * @return array array[userid] => stdClass{->id ->lastname ->firstname} */ - public function get_peer_authors($musthavesubmission=false) { + public function get_peer_authors($musthavesubmission=true) { global $DB; static $users = null; static $userswithsubmission = null; @@ -93,42 +89,125 @@ class workshop_api extends workshop { } } + /** + * Returns all users with the capability mod/workshop:submit sorted by groups + * + * This takes the module grouping settings into account. If "Available for group members only" + * is set, returns only groups withing the course module grouping. + * + * @param bool $musthavesubmission If true, return only users who have already submitted. All possible authors otherwise. + * @return array array[groupid][userid] => stdClass{->id ->lastname ->firstname} + */ + public function get_peer_authors_by_group($musthavesubmission=true) { + global $DB; + + $authors = $this->get_peer_authors($musthavesubmission); + $gauthors = array(); // grouped authors to be returned + if ($this->cm->groupmembersonly) { + // Available for group members only - the workshop is available only + // to users assigned to groups within the selected grouping, or to + // any group if no grouping is selected. + $groupingid = $this->cm->groupingid; + // All authors that are members of at least one group will be + // added into a virtual group id 0 + $gauthors[0] = array(); + } else { + $groupingid = 0; + // there is no need to be member of a group so $gauthors[0] will contain + // all authors with a submission + $gauthors[0] = $authors; + } + $gmemberships = groups_get_all_groups($this->cm->course, array_keys($authors), $groupingid, + 'gm.id,gm.groupid,gm.userid'); + foreach ($gmemberships as $gmembership) { + if (!isset($gauthors[$gmembership->groupid])) { + $gauthors[$gmembership->groupid] = array(); + } + $gauthors[$gmembership->groupid][$gmembership->userid] = $authors[$gmembership->userid]; + $gauthors[0][$gmembership->userid] = $authors[$gmembership->userid]; + } + return $gauthors; + } /** * Fetches all users with the capability mod/workshop:peerassess in the current context * * Static variable used to cache the results. The returned objects contain id, lastname * and firstname properties and are ordered by lastname,firstname - * - * @param object $context The context instance where the capability should be checked - * @return array Array of '(int)userid => (stdClass)userinfo' + * + * @param bool $musthavesubmission If true, return only users who have already submitted. All possible users otherwise. + * @return array array[userid] => stdClass{->id ->lastname ->firstname} */ - public function get_peer_reviewers() { + public function get_peer_reviewers($musthavesubmission=false) { global $DB; - static $users=null; + static $users = null; + static $userswithsubmission = null; if (is_null($users)) { $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); $users = get_users_by_capability($context, 'mod/workshop:peerassess', 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); - if (!$this->assesswosubmission) { + if ($musthavesubmission && is_null($userswithsubmission)) { // users without their own submission can not be reviewers - $withsubmission = $DB->get_records_list('workshop_submissions', 'userid', array_keys($users),'', 'userid'); - $users = array_intersect_key($users, $withsubmission); + $userswithsubmission = $DB->get_records_list('workshop_submissions', 'userid', array_keys($users),'', 'userid'); + $userswithsubmission = array_intersect_key($users, $userswithsubmission); } } - return $users; + if ($musthavesubmission) { + return $userswithsubmission; + } else { + return $users; + } } + /** + * Returns all users with the capability mod/workshop:peerassess sorted by groups + * + * This takes the module grouping settings into account. If "Available for group members only" + * is set, returns only groups withing the course module grouping. + * + * @param bool $musthavesubmission If true, return only users who have already submitted. All possible users otherwise. + * @return array array[groupid][userid] => stdClass{->id ->lastname ->firstname} + */ + public function get_peer_reviewers_by_group($musthavesubmission=false) { + global $DB; + + $reviewers = $this->get_peer_reviewers($musthavesubmission); + $greviewers = array(); // grouped reviewers to be returned + if ($this->cm->groupmembersonly) { + // Available for group members only - the workshop is available only + // to users assigned to groups within the selected grouping, or to + // any group if no grouping is selected. + $groupingid = $this->cm->groupingid; + // All reviewers that are members of at least one group will be + // added into a virtual group id 0 + $greviewers[0] = array(); + } else { + $groupingid = 0; + // there is no need to be member of a group so $greviewers[0] will contain + // all reviewers + $greviewers[0] = $reviewers; + } + $gmemberships = groups_get_all_groups($this->cm->course, array_keys($reviewers), $groupingid, + 'gm.id,gm.groupid,gm.userid'); + foreach ($gmemberships as $gmembership) { + if (!isset($greviewers[$gmembership->groupid])) { + $greviewers[$gmembership->groupid] = array(); + } + $greviewers[$gmembership->groupid][$gmembership->userid] = $reviewers[$gmembership->userid]; + $greviewers[0][$gmembership->userid] = $reviewers[$gmembership->userid]; + } + return $greviewers; + } /** * Returns submissions from this workshop * * Fetches data from {workshop_submissions} and adds some useful information from other * tables. - * - * @param mixed $userid If set to integer ID, return submission of the given user only - * @param mixed $examples false|true|all Only regular submissions, only examples, all submissions + * + * @param mixed $userid int|array|'all' If set to [array of] integer, return submission[s] of the given user[s] only + * @param mixed $examples false|true|'all' Only regular submissions, only examples, all submissions * @todo unittest * @return object moodle_recordset */ @@ -160,6 +239,46 @@ class workshop_api extends workshop { return $DB->get_recordset_sql($sql, $params); } + /** + * Returns a submission submitted by the given author or authors. + * + * This is intended for regular workshop participants, not for example submissions by teachers. + * If an array of authors is provided, returns array of stripped submission records so they do not + * include text fields (to prevent possible memory-lack issues). + * + * @param mixed $id integer|array author ID or IDs + * @return mixed false if not found, object if $id is int, array if $id is array + */ + public function get_submission_by_author($id) { + if (empty($id)) { + return false; + } + $rs = $this->get_submissions_recordset($id, false); + if (is_int($id)) { + $submission = $rs->current(); + $rs->close(); + if (empty($submission->id)) { + return false; + } else { + return $submission; + } + } elseif (is_array($id)) { + $submissions = array(); + foreach ($rs as $submission) { + $submissions[$submission->id] = new stdClass(); + foreach ($submission as $property => $value) { + // we do not want text fields here to prevent possible memory issues + if (in_array($property, array('id', 'workshopid', 'example', 'userid', 'authorlastname', 'authorfirstname', + 'timecreated', 'timemodified', 'grade', 'gradeover', 'gradeoverby', 'gradinggrade'))) { + $submissions[$submission->id]->{$property} = $value; + } + } + } + return $submissions; + } else { + throw new moodle_workshop_exception($this, 'wrongparameter'); + } + } /** * Returns the list of assessments with some data added @@ -173,15 +292,15 @@ class workshop_api extends workshop { */ public function get_assessments_recordset($reviewerid='all', $id='all') { global $DB; - + $sql = 'SELECT a.*, reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname, s.title, author.id AS authorid, author.firstname AS authorfirstname,author.lastname as authorlastname FROM {workshop_assessments} a - LEFT JOIN {user} reviewer ON (a.userid = reviewer.id) - LEFT JOIN {workshop_submissions} s ON (a.submissionid = s.id) - LEFT JOIN {user} author ON (s.userid = author.id) + INNER JOIN {user} reviewer ON (a.userid = reviewer.id) + INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id) + INNER JOIN {user} author ON (s.userid = author.id) WHERE s.workshopid = ?'; $params = array($this->id); if (is_int($reviewerid)) { @@ -201,6 +320,71 @@ class workshop_api extends workshop { return $DB->get_recordset_sql($sql, $params); } + /** + * Returns the list of assessments with some data added + * + * Fetches data from {workshop_assessments} and adds some useful information from other + * tables. The returned objects are lightweight version of those returned by get_assessments_recordset(), + * mainly they do not contain text fields. + * + * @param mixed $reviewerid 'all'|int|array User ID of the reviewer + * @return array of objects + * @see workshop_api::get_assessments_recordset() for the structure of returned objects + */ + public function get_assessments($reviewerid='all') { + $rs = $this->get_assessments_recordset($reviewerid, 'all'); + $assessments = array(); + foreach ($rs as $assessment) { + // copy selected properties into the array to be returned. This is here mainly in order not + // to include text comments. + $assessments[$assessment->id] = new stdClass; + foreach ($assessment as $property => $value) { + if (in_array($property, array('id', 'submissionid', 'userid', 'timecreated', 'timemodified', + 'timeagreed', 'grade', 'gradinggrade', 'gradinggradeover', 'gradinggradeoverby', + 'reviewerid', 'reviewerfirstname', 'reviewerlastname', 'title', 'authorid', + 'authorfirstname', 'authorlastname'))) { + $assessments[$assessment->id]->{$property} = $value; + } + } + } + $rs->close(); + return $assessments; + } + + /** + * Get the information about the given assessment + * + * @param int $id Assessment ID + * @see workshop_api::get_assessments_recordset() for the structure of data returned + * @return mixed false if not found, object otherwise + */ + public function get_assessment_by_id($id) { + $rs = $this->get_assessments_recordset('all', $id); + $assessment = $rs->current(); + $rs->close(); + if (empty($assessment->id)) { + return false; + } else { + return $assessment; + } + } + + /** + * Get the information about all assessments assigned to the given reviewer + * + * @param int $id Reviewer ID + * @see workshop_api::get_assessments_recordset() for the structure of data returned + * @return array array of objects + */ + public function get_assessments_by_reviewer($id) { + $rs = $this->get_assessments_recordset($id); + $assessments = array(); + foreach ($rs as $assessment) { + $assessments[$assessment->id] = $assessment; + } + $rs->close(); + return $assessment; + } /** * Returns the list of allocations in the workshop @@ -208,17 +392,18 @@ class workshop_api extends workshop { * This returns the list of all users who can submit their work or review submissions (or both * which is the common case). So basically this is to return list of all students participating * in the workshop. For every participant, it adds information about their submission and their - * reviews. This is mainly intended for allocation reports and originally was written for - * manula allocation ui. + * reviews. * * The returned structure is recordset of objects with following properties: * [authorid] [authorfirstname] [authorlastname] [authorpicture] [authorimagealt] * [submissionid] [submissiontitle] [submissiongrade] [assessmentid] - * [timeallocated] [reviewerid] [reviewerfirstname] [reviewerlastname] + * [timeallocated] [reviewerid] [reviewerfirstname] [reviewerlastname] * [reviewerpicture] [reviewerimagealt] * * This should be refactored when capability handling proposed by Petr is implemented so that * we can check capabilities directly in SQL joins. + * Note that the returned recordset includes participants without submission as well as those + * without any review allocated yet. * * @return object moodle_recordset */ @@ -235,10 +420,10 @@ class workshop_api extends workshop { list($usql, $params) = $DB->get_in_or_equal(array_keys($users)); $params[] = $this->id; - $sql = 'SELECT author.id AS authorid, author.firstname AS authorfirstname, author.lastname AS authorlastname, + $sql = 'SELECT author.id AS authorid, author.firstname AS authorfirstname, author.lastname AS authorlastname, author.picture AS authorpicture, author.imagealt AS authorimagealt, - s.id AS submissionid, s.title AS submissiontitle, s.grade AS submissiongrade, - a.id AS assessmentid, a.timecreated AS timeallocated, a.userid AS reviewerid, + s.id AS submissionid, s.title AS submissiontitle, s.grade AS submissiongrade, + a.id AS assessmentid, a.timecreated AS timeallocated, a.userid AS reviewerid, reviewer.firstname AS reviewerfirstname, reviewer.lastname AS reviewerlastname, reviewer.picture as reviewerpicture, reviewer.imagealt AS reviewerimagealt FROM {user} author @@ -250,61 +435,58 @@ class workshop_api extends workshop { return $DB->get_recordset_sql($sql, $params); } - /** * Allocate a submission to a user for review - * + * * @param object $submission Submission record * @param int $reviewerid User ID + * @param bool $bulk repeated inserts into DB expected * @return int ID of the new assessment or an error code */ - public function add_allocation(stdClass $submission, $reviewerid) { + public function add_allocation(stdClass $submission, $reviewerid, $bulk=false) { global $DB; if ($DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'userid' => $reviewerid))) { return WORKSHOP_ALLOCATION_EXISTS; } - if (!$this->assesswosubmission) { - // reviewer must have submitted his own work - if (!$DB->record_exists('workshop_submissions', array('workshopid' => $this->id, 'userid' => $reviewerid))) { - return WORKSHOP_ALLOCATION_WOSUBMISSION; - } - } - $now = time(); $assessment = new stdClass(); - $assessment->submissionid = $submission->id; + $assessment->submissionid = $submission->id; $assessment->userid = $reviewerid; $assessment->timecreated = $now; $assessment->timemodified = $now; - return $DB->insert_record('workshop_assessments', $assessment); + return $DB->insert_record('workshop_assessments', $assessment, true, $bulk); } - /** - * delete_assessment + * Delete assessment record or records * - * @todo finish and document this method - * + * @param mixed $id int|array assessment id or array of assessments ids + * @return bool false if $id not a valid parameter, true otherwise */ public function delete_assessment($id) { global $DB; // todo remove all given grades from workshop_grades; - return $DB->delete_records('workshop_assessments', array('id' => $id)); - } + if (is_numeric($id)) { + return $DB->delete_records('workshop_assessments', array('id' => $id)); + } + if (is_array($id)) { + return $DB->delete_records_list('workshop_assessments', 'id', $id); + } + return false; + } /** * Returns instance of grading strategy class - * + * * @param object $workshop Workshop record * @return object Instance of a grading strategy */ public function grading_strategy_instance() { - if (!($this->strategy === clean_param($workshop->strategy, PARAM_ALPHA))) { throw new moodle_workshop_exception($this, 'invalidstrategyname'); } @@ -314,7 +496,7 @@ class workshop_api extends workshop { if (is_readable($strategylib)) { require_once($strategylib); } else { - throw new moodle_exception('missingstrategy', 'workshop'); + throw new moodle_workshop_exception($this, 'missingstrategy'); } $classname = 'workshop_' . $workshop->strategy . '_strategy'; $this->strategy_api = new $classname($this); @@ -326,14 +508,12 @@ class workshop_api extends workshop { return $this->strategy_api; } - /** * Return list of available allocation methods * * @return array Array ['string' => 'string'] of localized allocation method names */ public function installed_allocators() { - $installed = get_list_of_plugins('mod/workshop/allocation'); $forms = array(); foreach ($installed as $allocation) { @@ -348,29 +528,25 @@ class workshop_api extends workshop { return $forms; } - /** * Returns instance of submissions allocator - * + * * @param object $method The name of the allocation method, must be PARAM_ALPHA * @return object Instance of submissions allocator */ public function allocator_instance($method) { - $allocationlib = dirname(__FILE__) . '/allocation/' . $method . '/allocator.php'; if (is_readable($allocationlib)) { require_once($allocationlib); } else { - throw new moodle_exception('missingallocator', 'workshop'); + throw new moodle_workshop_exception($this, 'missingallocator'); } $classname = 'workshop_' . $method . '_allocator'; return new $classname($this); } - } - /** * Class for workshop exceptions. Just saves a couple of arguments of the * constructor for a moodle_exception. @@ -398,5 +574,3 @@ class moodle_workshop_exception extends moodle_exception { } } - - diff --git a/mod/workshop/mod_form.php b/mod/workshop/mod_form.php index 8ead07b0b8..804285e03d 100644 --- a/mod/workshop/mod_form.php +++ b/mod/workshop/mod_form.php @@ -1,7 +1,7 @@ . - - + /** * The main workshop configuration form * * The UI mockup has been proposed in MDL-18688 - * It uses the standard core Moodle formslib. For more info about them, please + * It uses the standard core Moodle formslib. For more info about them, please * visit: http://docs.moodle.org/en/Development:lib/formslib.php * * @package mod-workshop @@ -115,7 +114,7 @@ class mod_workshop_mod_form extends moodleform_mod { $mform->setHelpButton('nattachments', array('nattachments', $label, 'workshop')); $mform->setAdvanced('nattachments'); - $options = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes); + $options = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes); $options[0] = get_string('courseuploadlimit') . ' ('.display_size($COURSE->maxbytes).')'; $mform->addElement('select', 'maxbytes', get_string('maximumsize', 'assignment'), $options); $mform->setDefault('maxbytes', $workshopconfig->maxbytes); @@ -209,9 +208,10 @@ class mod_workshop_mod_form extends moodleform_mod { $mform->setHelpButton('password', array('requirepassword', $label, 'workshop')); $mform->setAdvanced('password'); - /// Common module settinga, Restrict availability, Activity completion etc. ---- - // add standard elements, common to all modules + $features = array('groups'=>true, 'groupings'=>true, 'groupmembersonly'=>true, + 'outcomes'=>true, 'gradecat'=>false, 'idnumber'=>false); + $this->standard_coursemodule_elements(); /// Save and close, Save, Cancel ----------------------------------------------- diff --git a/mod/workshop/renderer.php b/mod/workshop/renderer.php index 6250f9cf94..423d8ac983 100644 --- a/mod/workshop/renderer.php +++ b/mod/workshop/renderer.php @@ -1,7 +1,7 @@ . - - + /** * All workshop module renderers are defined here * @@ -27,15 +26,12 @@ defined('MOODLE_INTERNAL') || die(); /** - * Workshop module renderer class - * - * @package - * @version $Id$ - * @copyright - * @author David Mudrak - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * Workshop module renderer class + * + * @copyright 2009 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class moodle_mod_workshop_renderer { +class moodle_mod_workshop_renderer extends moodle_renderer_base { /** the underlying renderer to use */ protected $output; @@ -43,10 +39,9 @@ class moodle_mod_workshop_renderer { /** the page we are doing output for */ protected $page; - /** * Workshop renderer constructor - * + * * @param mixed $page the page we are doing output for * @param mixed $output lower-level renderer, typically moodle_core_renderer * @access public @@ -57,6 +52,60 @@ class moodle_mod_workshop_renderer { $this->output = $output; } + /** + * Returns html code for a status message + * + * This should be replaced by a core system of displaying messages, as for example Mahara has. + * + * @param string $message to display + * @return string html + */ + public function status_message(stdClass $message) { + if (empty($message->text)) { + return ''; + } + $sty = $message->sty ? $message->sty : 'info'; + + $o = $this->output->output_tag('span', array(), $message->text); + $closer = $this->output->output_tag('a', array('href' => $this->page->url->out()), + get_string('messageclose', 'workshop')); + $o .= $this->output->container($closer, 'status-message-closer'); + if (isset($message->extra)) { + $o .= $message->extra; + } + return $this->output->container($o, array('status-message', $sty)); + } + /** + * Wraps html code returned by the allocator init() method + * + * Supplied argument can be either integer status code or an array of string messages. Messages + * in a array can have optional prefix or prefixes, using '::' as delimiter. Prefixes determine + * the type of the message and may influence its visualisation. + * + * @param mixed $result int|array returned by init() + * @return string $html to be echoed + */ + public function allocation_init_result($result='') { + $msg = new stdClass(); + if ($result === 'WORKSHOP_ALLOCATION_RANDOM_ERROR') { + $msg = (object)array('text' => get_string('randomallocationerror', 'workshop'), 'sty' => 'error'); + } else { + $msg = (object)array('text' => get_string('randomallocationdone', 'workshop'), 'sty' => 'ok'); + } + $o = $this->status_message($msg); + if (is_array($result)) { + $o .= $this->output->output_start_tag('ul', array('class' => 'allocation-init-results')); + foreach ($result as $message) { + $parts = explode('::', $message); + $text = array_pop($parts); + $class = implode(' ', $parts); + $o .= $this->output->output_tag('li', array('class' => $class), $text); + } + $o .= $this->output->output_end_tag('ul'); + $o .= $this->output->continue_button($this->page->url->out()); + } + return $o; + } } diff --git a/mod/workshop/settings.php b/mod/workshop/settings.php index 8f1412234f..6ae78675e1 100644 --- a/mod/workshop/settings.php +++ b/mod/workshop/settings.php @@ -15,7 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . - /** * The workshop module configuration variables * @@ -66,11 +65,11 @@ foreach (workshop_get_comparison_levels() as $code => $level) { $settings->add(new admin_setting_configselect('workshop/assessmentcomps', get_string('assessmentcomps', 'workshop'), get_string('configassessmentcomps', 'workshop'), WORKSHOP_COMPARISON_NORMAL, $levels)); -$settings->add(new admin_setting_configtext('workshop/noerrorsgrade0', get_string('noerrorsgrade0', 'workshop'), +$settings->add(new admin_setting_configtext('workshop/noerrorsgrade0', get_string('noerrorsgrade0', 'workshop'), get_string('confignoerrorsgrade0', 'workshop'), get_string('noerrorsgrade0default', 'workshop'), $paramtype=PARAM_TEXT, $size=15)); -$settings->add(new admin_setting_configtext('workshop/noerrorsgrade1', get_string('noerrorsgrade1', 'workshop'), +$settings->add(new admin_setting_configtext('workshop/noerrorsgrade1', get_string('noerrorsgrade1', 'workshop'), get_string('confignoerrorsgrade1', 'workshop'), get_string('noerrorsgrade1default', 'workshop'), $paramtype=PARAM_TEXT, $size=15)); diff --git a/mod/workshop/simpletest/testlib.php b/mod/workshop/simpletest/testlib.php index 7496308564..6ac80f0399 100644 --- a/mod/workshop/simpletest/testlib.php +++ b/mod/workshop/simpletest/testlib.php @@ -1,7 +1,7 @@ . - - + /** * Unit tests for (some of) mod/workshop/lib.php * @@ -25,11 +24,11 @@ */ defined('MOODLE_INTERNAL') || die(); - + // Make sure the code being tested is accessible. require_once($CFG->dirroot . '/mod/workshop/lib.php'); // Include the code to test - -/** + +/** * Test cases for the functions in lib.php */ class workshop_lib_test extends UnitTestCase { @@ -46,5 +45,5 @@ class workshop_lib_test extends UnitTestCase { } $this->assertTrue($values_are_integers, 'Array values must be integers'); } - + } diff --git a/mod/workshop/simpletest/testworkshopapi.php b/mod/workshop/simpletest/testworkshopapi.php index 3777d8c6fa..50d6931d55 100644 --- a/mod/workshop/simpletest/testworkshopapi.php +++ b/mod/workshop/simpletest/testworkshopapi.php @@ -1,7 +1,7 @@ . - - + /** * Unit tests for workshop_api class defined in mod/workshop/locallib.php * @@ -25,10 +24,9 @@ */ defined('MOODLE_INTERNAL') || die(); - + // Make sure the code being tested is accessible. require_once($CFG->dirroot . '/mod/workshop/locallib.php'); // Include the code to test - /** * Test subclass that makes all the protected methods we want to test public. @@ -38,8 +36,7 @@ class testable_workshop_api extends workshop_api { } - -/** +/** * Test cases for the internal workshop api */ class workshop_api_test extends UnitTestCase { @@ -60,6 +57,4 @@ class workshop_api_test extends UnitTestCase { $this->workshop = null; } - - } diff --git a/mod/workshop/styles.php b/mod/workshop/styles.php index b3dd88c0a5..49eb4ea62d 100644 --- a/mod/workshop/styles.php +++ b/mod/workshop/styles.php @@ -1,86 +1,118 @@ +/** + * Elements generated by the workshop renderer + */ +.mod-workshop .status-message { + padding: 5px 5em 5px 15px; + margin: 0px auto 20px auto; + width: 60%; + font-size: 80%; + position: relative; +} + +.mod-workshop .status-message-closer { + font-weight: bold; + position: absolute; + top: 5px; + right: 15px; +} + +.mod-workshop .status-message.ok { + color: #547c22; + background-color: #e7f1c3; +} + +.mod-workshop .status-message.error { + color: #dd0221; + background-color: #ffd3d9; +} + +.mod-workshop .status-message.info { + color: #1666a9; + background-color: #d2ebff; +} + +/** + * Allocators + */ + +.mod-workshop .allocation-init-results { + margin: 10px auto; + width: 60%; + font-size: 80%; +} + +.mod-workshop .allocation-init-results .ident { + margin-left: 20px; +} + +.mod-workshop .allocation-init-results .ok { + color: #547c22; + background-color: #e7f1c3; +} + +.mod-workshop .allocation-init-results .error { + color: #dd0221; + background-color: #ffd3d9; +} + +.mod-workshop .allocation-init-results .info { + color: #1666a9; + background-color: #d2ebff; +} + /** * Manual allocator */ -.manual-allocator .allocations { +.mod-workshop .manual-allocator .allocations { margin: 0px auto; } -.manual-allocator .allocations .r0 { +.mod-workshop .manual-allocator .allocations .r0 { background-color: #eee; } -.manual-allocator .allocations .highlightreviewedby .reviewedby, -.manual-allocator .allocations .highlightreviewerof .reviewerof { +.mod-workshop .manual-allocator .allocations .highlightreviewedby .reviewedby, +.mod-workshop .manual-allocator .allocations .highlightreviewerof .reviewerof { background-color: #fff3d2; } -.manual-allocator .allocations tr td { +.mod-workshop .manual-allocator .allocations tr td { vertical-align: top; padding: 5px; } -.manual-allocator .allocations tr td.peer { +.mod-workshop .manual-allocator .allocations tr td.peer { border-left: 1px solid #ccc; border-right: 1px solid #ccc; } -.manual-allocator .allocations .reviewedby .info, -.manual-allocator .allocations .peer .info, -.manual-allocator .allocations .reviewerof .info { +.mod-workshop .manual-allocator .allocations .reviewedby .info, +.mod-workshop .manual-allocator .allocations .peer .info, +.mod-workshop .manual-allocator .allocations .reviewerof .info { font-size: 80%; color: #888; font-style: italic; } -.manual-allocator .allocations .reviewedby img.userpicture, -.manual-allocator .allocations .reviewerof img.userpicture { +.mod-workshop .manual-allocator .allocations .reviewedby img.userpicture, +.mod-workshop .manual-allocator .allocations .reviewerof img.userpicture { height: 16px; width: 16px; margin-right: 3px; vertical-align: middle; } -.manual-allocator .allocations .peer img.userpicture { +.mod-workshop .manual-allocator .allocations .peer img.userpicture { height: 35px; width: 35px; vertical-align: middle; margin-right: 5px; } -.manual-allocator .allocations .peer .submission { +.mod-workshop .manual-allocator .allocations .peer .submission { font-size: 90%; margin-top: 1em; } -.manual-allocator .status-message { - padding: 5px 5em 5px 15px; - margin: 0px auto 20px auto; - width: 60%; - font-size: 80%; - position: relative; -} - -.manual-allocator .status-message-closer { - font-weight: bold; - position: absolute; - top: 5px; - right: 15px; -} - -.manual-allocator .status-message.ok { - color: #547c22; - background-color: #e7f1c3; -} - -.manual-allocator .status-message.error { - color: #dd0221; - background-color: #ffd3d9; -} - -.manual-allocator .status-message.info { - color: #1666a9; - background-color: #d2ebff; -} - diff --git a/mod/workshop/submission.php b/mod/workshop/submission.php index 766051e324..0ff7fe06af 100644 --- a/mod/workshop/submission.php +++ b/mod/workshop/submission.php @@ -1,7 +1,7 @@ . - /** * Submit an assignment or edit the already submitted work * @@ -64,7 +63,7 @@ if ($id) { // submission is specified if (!$submission = workshop_get_user_submission($workshop, $USER->id)) { $submission = new object(); $submission->id = null; - } + } } unset($id); // not needed anymore @@ -109,9 +108,9 @@ if ($mform->is_cancelled()) { } // save and relink embedded images and save attachments - $submission = file_postupdate_standard_editor($submission, 'data', $dataoptions, $context, + $submission = file_postupdate_standard_editor($submission, 'data', $dataoptions, $context, 'workshop_submission', $submission->id); - $submission = file_postupdate_standard_filemanager($submission, 'attachment', $attachmentoptions, $context, + $submission = file_postupdate_standard_filemanager($submission, 'attachment', $attachmentoptions, $context, 'workshop_attachment', $submission->id); // store the updated values or re-save the new submission diff --git a/mod/workshop/submission_form.php b/mod/workshop/submission_form.php index 45be3e62b5..41a356adb2 100644 --- a/mod/workshop/submission_form.php +++ b/mod/workshop/submission_form.php @@ -1,7 +1,7 @@ . - /** * Submit an assignment or edit the already submitted work * @@ -51,7 +50,7 @@ class workshop_submission_form extends moodleform { if ($workshop->nattachments > 0) { $mform->addElement('static', 'filemanagerinfo', get_string('nattachments', 'workshop'), $workshop->nattachments); - $mform->addElement('filemanager', 'attachment_filemanager', get_string('submissionattachment', 'workshop'), + $mform->addElement('filemanager', 'attachment_filemanager', get_string('submissionattachment', 'workshop'), null, $attachmentoptions); } @@ -63,7 +62,6 @@ class workshop_submission_form extends moodleform { $this->set_data($current); } - function validation($data, $files) { global $CFG, $USER, $DB; diff --git a/mod/workshop/version.php b/mod/workshop/version.php index f316497fe3..3a08cce692 100644 --- a/mod/workshop/version.php +++ b/mod/workshop/version.php @@ -1,7 +1,7 @@ . - /** * Defines the version of workshop * - * This code fragment is called by moodle_needs_upgrading() and + * This code fragment is called by moodle_needs_upgrading() and * /admin/index.php * * @package mod-workshop diff --git a/mod/workshop/view.php b/mod/workshop/view.php index eb583ab7a5..027cdeaff2 100644 --- a/mod/workshop/view.php +++ b/mod/workshop/view.php @@ -1,7 +1,7 @@ . - - + /** * Prints a particular instance of workshop * @@ -61,10 +60,10 @@ $PAGE->set_button(update_module_button($cm->id, $course->id, get_string('modulen // todo navigation will be changed yet for Moodle 2.0 $navlinks = array(); -$navlinks[] = array('name' => get_string('modulenameplural', 'workshop'), - 'link' => "index.php?id=$course->id", +$navlinks[] = array('name' => get_string('modulenameplural', 'workshop'), + 'link' => "index.php?id=$course->id", 'type' => 'activity'); -$navlinks[] = array('name' => format_string($workshop->name), +$navlinks[] = array('name' => format_string($workshop->name), 'link' => '', 'type' => 'activityinstance'); $navigation = build_navigation($navlinks); -- 2.39.5