From aa40adbf9f9a24d0b005e47c97493e4c25ed2069 Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Mon, 4 Jan 2010 18:07:38 +0000 Subject: [PATCH] MDL-19717 workshop manual allocator - pagination support and performance issues --- mod/workshop/allocation.php | 2 +- mod/workshop/allocation/manual/lib.php | 203 +++++++++++++---- mod/workshop/allocation/manual/renderer.php | 106 ++++----- mod/workshop/locallib.php | 234 +++++++++----------- 4 files changed, 317 insertions(+), 228 deletions(-) diff --git a/mod/workshop/allocation.php b/mod/workshop/allocation.php index 6fcfb748f2..5ebf93870c 100644 --- a/mod/workshop/allocation.php +++ b/mod/workshop/allocation.php @@ -57,7 +57,7 @@ $initresult = $allocator->init(); $wsoutput = $PAGE->theme->get_renderer('mod_workshop', $PAGE); echo $OUTPUT->header(); -$allocators = $workshop->installed_allocators(); +$allocators = workshop::installed_allocators(); $currenttab = 'allocation'; include(dirname(__FILE__) . '/tabs.php'); diff --git a/mod/workshop/allocation/manual/lib.php b/mod/workshop/allocation/manual/lib.php index 2182cb6b2d..147e8b0ac5 100644 --- a/mod/workshop/allocation/manual/lib.php +++ b/mod/workshop/allocation/manual/lib.php @@ -33,6 +33,9 @@ require_once(dirname(dirname(dirname(__FILE__))) . '/locallib.php'); // works */ class workshop_manual_allocator implements workshop_allocator { + /** participants per page */ + const PERPAGE = 30; + /** constants that are used to pass status messages between init() and ui() */ const MSG_ADDED = 1; const MSG_NOSUBMISSION = 2; @@ -129,15 +132,17 @@ class workshop_manual_allocator implements workshop_allocator { * Prints user interface - current allocation and a form to edit it */ public function ui() { - global $PAGE, $OUTPUT; + global $PAGE, $OUTPUT, $DB; + $pagingvar = 'page'; + $page = optional_param($pagingvar, 0, PARAM_INT); $hlauthorid = -1; // highlight this author $hlreviewerid = -1; // highlight this reviewer $msg = new stdClass(); // message to render - $m = optional_param('m', '', PARAM_ALPHANUMEXT); // message stdClass + $m = optional_param('m', '', PARAM_ALPHANUMEXT); // message code if ($m) { - $m = explode('-', $m); // unserialize + $m = explode('-', $m); switch ($m[0]) { case self::MSG_ADDED: $hlauthorid = $m[1]; @@ -190,56 +195,170 @@ class workshop_manual_allocator implements workshop_allocator { } } - $peers = array(); - $rs = $this->workshop->get_allocations_recordset(); - if (!is_null($rs)) { - foreach ($rs as $allocation) { - $currentuserid = $allocation->authorid; - if (!isset($peers[$currentuserid])) { - $peers[$currentuserid] = new stdClass(); - $peers[$currentuserid]->id = $allocation->authorid; - $peers[$currentuserid]->firstname = $allocation->authorfirstname; - $peers[$currentuserid]->lastname = $allocation->authorlastname; - $peers[$currentuserid]->picture = $allocation->authorpicture; - $peers[$currentuserid]->imagealt = $allocation->authorimagealt; - $peers[$currentuserid]->submissionid = $allocation->submissionid; - $peers[$currentuserid]->submissiontitle = $allocation->submissiontitle; - $peers[$currentuserid]->submissiongrade = $allocation->submissiongrade; - $peers[$currentuserid]->reviewedby = array(); // users who are reviewing this user's submission - $peers[$currentuserid]->reviewerof = array(); // users whom submission is being reviewed by this user - } - if (!empty($allocation->reviewerid)) { - // example: "submission of user with id 45 is reviewed by user with id 87 in the assessment record 12" - $peers[$currentuserid]->reviewedby[$allocation->reviewerid] = $allocation->assessmentid; + if ($hlauthorid > 0 and $hlreviewerid > 0) { + // display just those users, no pagination + $participants = get_users_by_capability($PAGE->context, array('mod/workshop:submit', 'mod/workshop:peerassess'), + 'u.id,u.lastname,u.firstname,u.picture,u.imagealt', 'u.lastname,u.firstname,u.id', + '', '', '', '', false, false, true); + $participants = array_intersect_key($participants, array($hlauthorid => null, $hlreviewerid => null)); + } else { + // the paginated list of users to be displayed in the middle column ("Participant") + $participants = get_users_by_capability($PAGE->context, array('mod/workshop:submit', 'mod/workshop:peerassess'), + 'u.id,u.lastname,u.firstname,u.picture,u.imagealt', 'u.lastname,u.firstname,u.id', + $page * self::PERPAGE, self::PERPAGE, '', '', false, false, true); + } + + + // this will hold the information needed to display user names and pictures + $userinfo = $participants; + + // load the participants' submissions + $submissions = $this->workshop->get_submissions(array_keys($participants), false); + + // get current reviewers + $reviewers = array(); + if ($submissions) { + list($submissionids, $params) = $DB->get_in_or_equal(array_keys($submissions), SQL_PARAMS_NAMED); + $sql = "SELECT a.id AS assessmentid, a.submissionid, + r.id AS reviewerid, r.lastname, r.firstname, r.picture, r.imagealt, + s.id AS submissionid, s.userid AS authorid + FROM {workshop_assessments} a + JOIN {user} r ON (a.userid = r.id) + JOIN {workshop_submissions} s ON (a.submissionid = s.id) + WHERE a.submissionid $submissionids"; + $reviewers = $DB->get_records_sql($sql, $params); + foreach ($reviewers as $reviewer) { + if (!isset($userinfo[$reviewer->reviewerid])) { + $userinfo[$reviewer->reviewerid] = new stdClass(); + $userinfo[$reviewer->reviewerid]->id = $reviewer->reviewerid; + $userinfo[$reviewer->reviewerid]->firstname = $reviewer->firstname; + $userinfo[$reviewer->reviewerid]->lastname = $reviewer->lastname; + $userinfo[$reviewer->reviewerid]->picture = $reviewer->picture; + $userinfo[$reviewer->reviewerid]->imagealt = $reviewer->imagealt; + $userinfo[$reviewer->reviewerid]->firstname = $reviewer->firstname; } } - $rs->close(); } - foreach ($peers as $author) { - foreach ($author->reviewedby as $reviewerid => $assessmentid) { - if (isset($peers[$reviewerid])) { - // example: "user with id 87 is reviewer of the work submitted by user id 45 in the assessment record 12" - $peers[$reviewerid]->reviewerof[$author->id] = $assessmentid; - } + // get current reviewees + list($participantids, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED); + $params['workshopid'] = $this->workshop->id; + $sql = "SELECT a.id AS assessmentid, a.submissionid, + u.id AS reviewerid, + s.id AS submissionid, + r.id AS revieweeid, r.lastname, r.firstname, r.picture, r.imagealt + FROM {user} u + JOIN {workshop_assessments} a ON (a.userid = u.id) + JOIN {workshop_submissions} s ON (a.submissionid = s.id) + JOIN {user} r ON (s.userid = r.id) + WHERE u.id $participantids AND s.workshopid = :workshopid"; + $reviewees = $DB->get_records_sql($sql, $params); + foreach ($reviewees as $reviewee) { + if (!isset($userinfo[$reviewee->revieweeid])) { + $userinfo[$reviewee->revieweeid] = new stdClass(); + $userinfo[$reviewee->revieweeid]->id = $reviewee->revieweeid; + $userinfo[$reviewee->revieweeid]->firstname = $reviewee->firstname; + $userinfo[$reviewee->revieweeid]->lastname = $reviewee->lastname; + $userinfo[$reviewee->revieweeid]->picture = $reviewee->picture; + $userinfo[$reviewee->revieweeid]->imagealt = $reviewee->imagealt; + $userinfo[$reviewee->revieweeid]->firstname = $reviewee->firstname; } } + // the information about the allocations + $allocations = array(); + + foreach ($participants as $participant) { + $allocations[$participant->id] = new stdClass; + $allocations[$participant->id]->userid = $participant->id; + $allocations[$participant->id]->submissionid = null; + $allocations[$participant->id]->reviewedby = array(); + $allocations[$participant->id]->reviewerof = array(); + } + unset($participants); + foreach ($submissions as $submission) { + $allocations[$submission->authorid]->submissionid = $submission->id; + $allocations[$submission->authorid]->submissiontitle = $submission->title; + $allocations[$submission->authorid]->submissiongrade = $submission->grade; + } + unset($submissions); + foreach($reviewers as $reviewer) { + $allocations[$reviewer->authorid]->reviewedby[$reviewer->reviewerid] = $reviewer->assessmentid; + } + unset($reviewers); + foreach($reviewees as $reviewee) { + $allocations[$reviewee->reviewerid]->reviewerof[$reviewee->revieweeid] = $reviewee->assessmentid; + } + unset($reviewees); + // we have all data, let us pass it to the renderer and return the output $wsoutput = $PAGE->theme->get_renderer('mod_workshop', $PAGE); $uioutput = $PAGE->theme->get_renderer('workshopallocation_manual', $PAGE); + // prepare data to be displayed - $data = new stdClass(); - $data->wsoutput = $wsoutput; - $data->peers = $peers; - $data->authors = $this->workshop->get_potential_authors($PAGE->context); - $data->reviewers = $this->workshop->get_potential_reviewers($PAGE->context); - $data->hlauthorid = $hlauthorid; - $data->hlreviewerid = $hlreviewerid; - $data->msg = $msg; - $data->useselfassessment = $this->workshop->useselfassessment; - - return $uioutput->display_allocations($data); + $data = new stdClass(); + $data->wsoutput = $wsoutput; + $data->allocations = $allocations; + $data->userinfo = $userinfo; + $data->authors = $this->workshop->get_potential_authors($PAGE->context); + $data->reviewers = $this->workshop->get_potential_reviewers($PAGE->context); + $data->hlauthorid = $hlauthorid; + $data->hlreviewerid = $hlreviewerid; + $data->selfassessment = $this->workshop->useselfassessment; + + // prepare paging bar + $pagingbar = new moodle_paging_bar(); + $pagingbar->totalcount = count($data->authors); + $pagingbar->page = $page; + $pagingbar->perpage = self::PERPAGE; + $pagingbar->baseurl = $PAGE->url; + $pagingbar->pagevar = $pagingvar; + $pagingbar->nocurr = true; + + $pagingbarout = $OUTPUT->paging_bar($pagingbar); + + return $pagingbarout . $wsoutput->status_message($msg) . $uioutput->display_allocations($data) . $pagingbarout; } + /** + * Returns the list of all allocations where the given users are involved + * + * We must use recordset here because we do not have any unique identifier available + * + * @param array [userid] => whatever + * @return moodle_recordset|null + */ + protected function get_allocations_recordset(array $users) { + global $DB, $PAGE; + + if (empty($users)) { + return null; + } + if (count($users) > 9999) { + throw coding_exception('two many users'); + } + + list($authorids, $authorparams) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED, 'a0000'); + list($reviewerids, $reviewerparams) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED, 'r0000'); + $params = array_merge($authorparams, $reviewerparams); + $params['workshopid'] = $this->workshop->id; + + $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, + reviewer.firstname AS reviewerfirstname, reviewer.lastname AS reviewerlastname, + reviewer.picture as reviewerpicture, reviewer.imagealt AS reviewerimagealt + FROM {user} author + LEFT JOIN {workshop_submissions} s ON (s.userid = author.id) + LEFT JOIN {workshop_assessments} a ON (s.id = a.submissionid) + LEFT JOIN {user} reviewer ON (a.userid = reviewer.id) + WHERE (author.id $authorids OR reviewer.id $reviewerids) AND (s.id IS NULL OR s.workshopid = :workshopid) + ORDER BY author.lastname,author.firstname,author.id,reviewer.lastname,reviewer.firstname,reviewer.id"; + + return $DB->get_recordset_sql($sql, $params); + } + + + } diff --git a/mod/workshop/allocation/manual/renderer.php b/mod/workshop/allocation/manual/renderer.php index 8d408f39d5..769bd5aabb 100644 --- a/mod/workshop/allocation/manual/renderer.php +++ b/mod/workshop/allocation/manual/renderer.php @@ -56,16 +56,16 @@ class moodle_workshopallocation_manual_renderer extends moodle_renderer_base { */ public function display_allocations(stdClass $data) { $wsoutput = $data->wsoutput; // moodle_mod_workshop_renderer - $peers = $data->peers; // array prepared array of all allocations data - $authors = $data->authors; // array submission authors + $allocations = $data->allocations; // array prepared array of all allocations data + $userinfo = $data->userinfo; // names and pictures of all required users + $authors = $data->authors; // array potential reviewees $reviewers = $data->reviewers; // array potential submission reviewers $hlauthorid = $data->hlauthorid; // int id of the author to highlight $hlreviewerid = $data->hlreviewerid; // int id of the reviewer to highlight - $useselfassessment = $data->useselfassessment; // bool is the self-assessment allowed in this workshop? - $msg = $data->msg; // stdClass message to display + $selfassessment = $data->selfassessment; // bool is the self-assessment allowed in this workshop? $wsoutput = $this->page->theme->get_renderer('mod_workshop', $this->page); - if (empty($peers)) { + if (empty($allocations)) { return $wsoutput->status_message((object)array('text' => get_string('nosubmissions', 'workshop'))); } @@ -77,41 +77,39 @@ class moodle_workshopallocation_manual_renderer extends moodle_renderer_base { $table->rowclasses = array(); $table->colclasses = array('reviewedby', 'peer', 'reviewerof'); $table->data = array(); - foreach ($peers as $user) { + foreach ($allocations as $allocation) { $row = array(); - $row[] = $this->reviewers_of_participant($user, $peers, $reviewers, $useselfassessment); - $row[] = $this->participant($user); - $row[] = $this->reviewees_of_participant($user, $peers, $authors, $useselfassessment); + $row[] = $this->reviewers_of_participant($allocation, $userinfo, $reviewers, $selfassessment); + $row[] = $this->participant($allocation, $userinfo); + $row[] = $this->reviewees_of_participant($allocation, $userinfo, $authors, $selfassessment); $thisrowclasses = array(); - if ($user->id == $hlauthorid) { + if ($allocation->userid == $hlauthorid) { $thisrowclasses[] = 'highlightreviewedby'; } - if ($user->id == $hlreviewerid) { + if ($allocation->userid == $hlreviewerid) { $thisrowclasses[] = 'highlightreviewerof'; } $table->rowclasses[] = implode(' ', $thisrowclasses); $table->data[] = $row; } - return $this->output->container($wsoutput->status_message($msg) . $this->output->table($table), 'manual-allocator'); + return $this->output->container($this->output->table($table), 'manual-allocator'); } /** * Returns information about the workshop participant * - * @param stdClass $user participant data - * @param workshop API * @return string HTML code */ - protected function participant(stdClass $user) { - $o = $this->output->user_picture($user, $this->page->course->id); - $o .= fullname($user); + protected function participant(stdClass $allocation, array $userinfo) { + $o = $this->output->user_picture($userinfo[$allocation->userid], $this->page->course->id); + $o .= fullname($userinfo[$allocation->userid]); $o .= $this->output->container_start(array('submission')); - if (is_null($user->submissionid)) { + if (is_null($allocation->submissionid)) { $o .= $this->output->container(get_string('nosubmissionfound', 'workshop'), 'info'); } else { - $o .= $this->output->container(format_string($user->submissiontitle), 'title'); - if (is_null($user->submissiongrade)) { + $o .= $this->output->container(format_string($allocation->submissiontitle), 'title'); + if (is_null($allocation->submissiongrade)) { $o .= $this->output->container(get_string('nogradeyet', 'workshop'), array('grade', 'missing')); } else { $o .= $this->output->container(get_string('alreadygraded', 'workshop'), array('grade', 'missing')); @@ -124,41 +122,38 @@ class moodle_workshopallocation_manual_renderer extends moodle_renderer_base { /** * Returns information about the current reviewers of the given participant and a selector do add new one * - * @param stdClass $user participant data - * @param array $peers objects with properties to display picture and fullname - * @param array $reviewers potential reviewers - * @param bool $useselfassessment shall a user be offered as a reviewer of him/herself * @return string html code */ - protected function reviewers_of_participant(stdClass $user, array $peers, array $reviewers, $useselfassessment) { + protected function reviewers_of_participant(stdClass $allocation, array $userinfo, array $reviewers, $selfassessment) { $o = ''; - if (is_null($user->submissionid)) { + if (is_null($allocation->submissionid)) { $o .= $this->output->container(get_string('nothingtoreview', 'workshop'), 'info'); } else { $exclude = array(); - if (! $useselfassessment) { - $exclude[$user->id] = true; + if (! $selfassessment) { + $exclude[$allocation->userid] = true; } // todo add an option to exclude users without own submission $options = $this->users_to_menu_options($reviewers, $exclude); if ($options) { - $handler = new moodle_url($this->page->url, array('mode' => 'new', 'of' => $user->id, 'sesskey' => sesskey())); - $select = html_select::make_popup_form($handler, 'by', $options, 'addreviewof' . $user->id, '', - get_string('addreviewer', 'workshopallocation_manual')); + $handler = new moodle_url($this->page->url, + array('mode' => 'new', 'of' => $allocation->userid, 'sesskey' => sesskey())); + $select = html_select::make_popup_form($handler, 'by', $options, 'addreviewof' . $allocation->userid, '', + get_string('addreviewer', 'workshopallocation_manual')); $select->nothinglabel = get_string('chooseuser', 'workshop'); $select->set_label(get_string('addreviewer', 'workshopallocation_manual'), $select->id); $o .= $this->output->select($select); } } $o .= $this->output->output_start_tag('ul', array()); - foreach ($user->reviewedby as $reviewerid => $assessmentid) { + foreach ($allocation->reviewedby as $reviewerid => $assessmentid) { $o .= $this->output->output_start_tag('li', array()); - $userpic = new moodle_user_picture(); - $userpic->user = $peers[$reviewerid]; - $userpic->courseid = $this->page->course->id; - $userpic->size = 16; - $o .= $this->output->user_picture($userpic); - $o .= fullname($peers[$reviewerid]); + $allocationpic = new moodle_user_picture(); + $allocationpic->user = $userinfo[$reviewerid]; + $allocationpic->courseid = $this->page->course->id; + $allocationpic->size = 16; + $o .= $this->output->user_picture($allocationpic); + $o .= fullname($userinfo[$reviewerid]); // delete icon $handler = new moodle_url($this->page->url, array('mode' => 'del', 'what' => $assessmentid, 'sesskey' => sesskey())); @@ -173,28 +168,25 @@ class moodle_workshopallocation_manual_renderer extends moodle_renderer_base { /** * Returns information about the current reviewees of the given participant and a selector do add new one * - * @param stdClass $user participant data - * @param array $peers objects with properties to display picture and fullname - * @param array $authors potential authors to be reviewed - * @param bool $useselfassessment shall a user be offered as a reviewer of him/herself * @return string html code */ - protected function reviewees_of_participant(stdClass $user, array $peers, array $authors, $useselfassessment) { + protected function reviewees_of_participant(stdClass $allocation, array $userinfo, array $authors, $selfassessment) { $o = ''; - if (is_null($user->submissionid)) { + if (is_null($allocation->submissionid)) { $o .= $this->output->container(get_string('withoutsubmission', 'workshop'), 'info'); } $exclude = array(); - if (! $useselfassessment) { - $exclude[$user->id] = true; + if (! $selfassessment) { + $exclude[$allocation->userid] = true; $o .= $this->output->container(get_string('selfassessmentdisabled', 'workshop'), 'info'); } // todo add an option to exclude users without own submission $options = $this->users_to_menu_options($authors, $exclude); if ($options) { - $handler = new moodle_url($this->page->url, array('mode' => 'new', 'by' => $user->id, 'sesskey' => sesskey())); - $select = html_select::make_popup_form($handler, 'of', $options, 'addreviewby' . $user->id, '', - get_string('addreviewee', 'workshopallocation_manual')); + $handler = new moodle_url($this->page->url, + array('mode' => 'new', 'by' => $allocation->userid, 'sesskey' => sesskey())); + $select = html_select::make_popup_form($handler, 'of', $options, 'addreviewby' . $allocation->userid, '', + get_string('addreviewee', 'workshopallocation_manual')); $select->nothinglabel = get_string('chooseuser', 'workshop'); $select->set_label(get_string('addreviewee', 'workshopallocation_manual'), $select->id); $o .= $this->output->select($select); @@ -202,14 +194,14 @@ class moodle_workshopallocation_manual_renderer extends moodle_renderer_base { $o .= $this->output->container(get_string('nothingtoreview', 'workshop'), 'info'); } $o .= $this->output->output_start_tag('ul', array()); - foreach ($user->reviewerof as $authorid => $assessmentid) { + foreach ($allocation->reviewerof as $authorid => $assessmentid) { $o .= $this->output->output_start_tag('li', array()); - $userpic = new moodle_user_picture(); - $userpic->user = $peers[$authorid]; - $userpic->courseid = $this->page->course->id; - $userpic->size = 16; - $o .= $this->output->user_picture($userpic, $this->page->course->id); - $o .= fullname($peers[$authorid]); + $allocationpic = new moodle_user_picture(); + $allocationpic->user = $userinfo[$authorid]; + $allocationpic->courseid = $this->page->course->id; + $allocationpic->size = 16; + $o .= $this->output->user_picture($allocationpic, $this->page->course->id); + $o .= fullname($userinfo[$authorid]); // delete icon $handler = new moodle_url($this->page->url, array('mode' => 'del', 'what' => $assessmentid, 'sesskey' => sesskey())); @@ -252,6 +244,4 @@ class moodle_workshopallocation_manual_renderer extends moodle_renderer_base { return $this->output->action_icon($icon); } - - } diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index 0b12eaa489..e914532304 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -91,47 +91,60 @@ class workshop { // 3rd normal form with no real performance gain } + //////////////////////////////////////////////////////////////////////////////// + // Static methods // + //////////////////////////////////////////////////////////////////////////////// + /** - * Given a list of user ids, returns the filtered one containing just ids of users with own submission - * - * Example submissions are ignored. + * Return list of available allocation methods * - * @param array $userids - * @return array + * @return array Array ['string' => 'string'] of localized allocation method names */ - protected function users_with_submission(array $userids) { - global $DB; - - if (empty($userids)) { - return array(); + public static function installed_allocators() { + $installed = get_plugin_list('workshopallocation'); + $forms = array(); + foreach ($installed as $allocation => $allocationpath) { + if (file_exists($allocationpath . '/lib.php')) { + $forms[$allocation] = get_string('pluginname', 'workshopallocation_' . $allocation); + } } - $userswithsubmission = array(); - list($usql, $uparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); - $sql = "SELECT id,userid - FROM {workshop_submissions} - WHERE example = 0 AND workshopid = :workshopid AND userid $usql"; - $params = array('workshopid' => $this->id); - $params = array_merge($params, $uparams); - $submissions = $DB->get_records_sql($sql, $params); - foreach ($submissions as $submission) { - $userswithsubmission[$submission->userid] = null; + // usability - make sure that manual allocation appears the first + if (isset($forms['manual'])) { + $m = array('manual' => $forms['manual']); + unset($forms['manual']); + $forms = array_merge($m, $forms); } + return $forms; + } - return $userswithsubmission; + /** + * Returns an array of options for the editors that are used for submitting and assessing instructions + * + * @param stdClass $context + * @return array + */ + public static function instruction_editors_options(stdClass $context) { + return array('subdirs' => 1, 'maxbytes' => 0, 'maxfiles' => EDITOR_UNLIMITED_FILES, + 'changeformat' => 1, 'context' => $context, 'noclean' => 1, 'trusttext' => 0); } + //////////////////////////////////////////////////////////////////////////////// + // Workshop API // + //////////////////////////////////////////////////////////////////////////////// + /** * Fetches all users with the capability mod/workshop:submit in the current context * * The returned objects contain id, lastname and firstname properties and are ordered by lastname,firstname * + * @todo handle with limits and groups * @param stdClass $context * @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_potential_authors(stdClass $context, $musthavesubmission=true) { $users = get_users_by_capability($context, 'mod/workshop:submit', - 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); + 'u.id,u.lastname,u.firstname', 'u.lastname,u.firstname,u.id', 0, 1000, '', '', false, false, true); if ($musthavesubmission) { $users = array_intersect_key($users, $this->users_with_submission(array_keys($users))); } @@ -143,13 +156,14 @@ class workshop { * * The returned objects contain id, lastname and firstname properties and are ordered by lastname,firstname * + * @todo handle with limits and groups * @param stdClass $context * @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_potential_reviewers(stdClass $context, $musthavesubmission=false) { $users = get_users_by_capability($context, 'mod/workshop:peerassess', - 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); + 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname,u.id', 0, 1000, '', '', false, false, true); if ($musthavesubmission) { // users without their own submission can not be reviewers $users = array_intersect_key($users, $this->users_with_submission(array_keys($users))); @@ -201,6 +215,26 @@ class workshop { return $grouped; } + /** + * Returns the list of all allocations (it est assigned assessments) in the workshop + * + * Assessments of example submissions are ignored + * + * @return array + */ + public function get_allocations() { + global $DB; + + $sql = 'SELECT a.id, a.submissionid, a.userid AS reviewerid, + s.userid AS authorid + FROM {workshop_assessments} a + INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id) + WHERE s.example = 0 AND s.workshopid = :workshopid'; + $params = array('workshopid' => $this->id); + + return $DB->get_records_sql($sql, $params); + } + /** * Returns submissions from this workshop * @@ -365,56 +399,6 @@ class workshop { return $DB->get_records_sql($sql, $params); } - /** - * Returns the list of allocations in the 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, if such information is available (null elsewhere). - * - * The returned structure is recordset of objects with following properties: - * [authorid] [authorfirstname] [authorlastname] [authorpicture] [authorimagealt] - * [submissionid] [submissiontitle] [submissiongrade] [assessmentid] - * [timeallocated] [reviewerid] [reviewerfirstname] [reviewerlastname] - * [reviewerpicture] [reviewerimagealt] - * - * TODO 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 null|stdClass moodle_recordset - */ - public function get_allocations_recordset() { - global $DB, $PAGE; - - $users = get_users_by_capability($PAGE->context, array('mod/workshop:submit', 'mod/workshop:peerassess'), - 'u.id', 'u.lastname,u.firstname', '', '', '', '', false, false, true); - - if (empty($users)) { - return null; - } - - list($usql, $params) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED); - $params['workshopid'] = $this->id; - - $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, - reviewer.firstname AS reviewerfirstname, reviewer.lastname AS reviewerlastname, - reviewer.picture as reviewerpicture, reviewer.imagealt AS reviewerimagealt - FROM {user} author - LEFT JOIN {workshop_submissions} s ON (s.userid = author.id) - LEFT JOIN {workshop_assessments} a ON (s.id = a.submissionid) - LEFT JOIN {user} reviewer ON (a.userid = reviewer.id) - WHERE author.id $usql AND (s.id IS NULL OR s.workshopid = :workshopid) - ORDER BY author.lastname,author.firstname,reviewer.lastname,reviewer.firstname"; - - return $DB->get_recordset_sql($sql, $params); - } - /** * Allocate a submission to a user for review * @@ -506,28 +490,6 @@ class workshop { return $this->evaluationinstance; } - /** - * Return list of available allocation methods - * - * @return array Array ['string' => 'string'] of localized allocation method names - */ - public function installed_allocators() { - $installed = get_plugin_list('workshopallocation'); - $forms = array(); - foreach ($installed as $allocation => $allocationpath) { - if (file_exists($allocationpath . '/lib.php')) { - $forms[$allocation] = get_string('pluginname', 'workshopallocation_' . $allocation); - } - } - // usability - make sure that manual allocation appears the first - if (isset($forms['manual'])) { - $m = array('manual' => $forms['manual']); - unset($forms['manual']); - $forms = array_merge($m, $forms); - } - return $forms; - } - /** * Returns instance of submissions allocator * @@ -761,22 +723,19 @@ class workshop { $task->link = $this->allocation_url(); $authors = array(); $allocations = array(); // 'submissionid' => isallocated - $rs = $this->get_allocations_recordset(); - if (!is_null($rs)) { - foreach ($rs as $allocation) { - if (!isset($authors[$allocation->authorid])) { - $authors[$allocation->authorid] = true; + $records = $this->get_allocations(); + foreach ($records as $allocation) { + if (!isset($authors[$allocation->authorid])) { + $authors[$allocation->authorid] = true; + } + if (isset($allocation->submissionid)) { + if (!isset($allocations[$allocation->submissionid])) { + $allocations[$allocation->submissionid] = false; } - if (isset($allocation->submissionid)) { - if (!isset($allocations[$allocation->submissionid])) { - $allocations[$allocation->submissionid] = false; - } - if (!empty($allocation->reviewerid)) { - $allocations[$allocation->submissionid] = true; - } + if (!empty($allocation->reviewerid)) { + $allocations[$allocation->submissionid] = true; } } - $rs->close(); } $numofauthors = count($authors); $numofsubmissions = count($allocations); @@ -914,19 +873,6 @@ class workshop { return $this->grading_strategy_instance()->form_ready(); } - /** - * @return array of available workshop phases - */ - protected function available_phases() { - return array( - self::PHASE_SETUP => true, - self::PHASE_SUBMISSION => true, - self::PHASE_ASSESSMENT => true, - self::PHASE_EVALUATION => true, - self::PHASE_CLOSED => true, - ); - } - /** * Switch to a new workshop phase * @@ -966,17 +912,51 @@ class workshop { return $grade; } -// Static methods + //////////////////////////////////////////////////////////////////////////////// + // Internal methods (implementation details) // + //////////////////////////////////////////////////////////////////////////////// /** - * Returns an array of options for the editors that are used for submitting and assessing instructions + * Given a list of user ids, returns the filtered one containing just ids of users with own submission * - * @param stdClass $context + * Example submissions are ignored. + * + * @param array $userids * @return array */ - public static function instruction_editors_options(stdClass $context) { - return array('subdirs' => 1, 'maxbytes' => 0, 'maxfiles' => EDITOR_UNLIMITED_FILES, - 'changeformat' => 1, 'context' => $context, 'noclean' => 1, 'trusttext' => 0); + protected function users_with_submission(array $userids) { + global $DB; + + if (empty($userids)) { + return array(); + } + $userswithsubmission = array(); + list($usql, $uparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); + $sql = "SELECT id,userid + FROM {workshop_submissions} + WHERE example = 0 AND workshopid = :workshopid AND userid $usql"; + $params = array('workshopid' => $this->id); + $params = array_merge($params, $uparams); + $submissions = $DB->get_records_sql($sql, $params); + foreach ($submissions as $submission) { + $userswithsubmission[$submission->userid] = null; + } + + return $userswithsubmission; } + /** + * @return array of available workshop phases + */ + protected function available_phases() { + return array( + self::PHASE_SETUP => true, + self::PHASE_SUBMISSION => true, + self::PHASE_ASSESSMENT => true, + self::PHASE_EVALUATION => true, + self::PHASE_CLOSED => true, + ); + } + + } -- 2.39.5