Skip to content
Browse files

Merge branch 'workshop_random_allocator' of git://github.com/mudrd8mz…

…/moodle
  • Loading branch information...
2 parents a477c0d + 241e58e commit bb871bc4ca5a9fa92445e40fcb8de6f3a3d0d78a @skodak skodak committed Jan 17, 2011
View
3 mod/workshop/allocation/random/lang/en/workshopallocation_random.php
@@ -32,7 +32,10 @@
$string['assessmentdeleteddetail'] = 'Assessment deallocated: <strong>{$a->reviewername}</strong> is no longer reviewer of <strong>{$a->authorname}</strong>';
$string['assesswosubmission'] = 'Participants can assess without having submitted anything';
$string['confignumofreviews'] = 'Default number of submissions to be randomly allocated';
+$string['excludesamegroup'] = 'Prevent reviews by peers from the same group';
$string['noallocationtoadd'] = 'No allocations to add';
+$string['nogroupusers'] = '<p>Warning: If the workshop is in \'visible groups\' mode or \'separate groups\' mode, then users MUST be part of at least one group to have peer-assessments allocated to them by this tool. Non-grouped users can still be given new self-assessments or have existing assessments removed.</p>
+<p>These users are currently not in a group: {$a}</p>';
$string['numofdeallocatedassessment'] = 'Deallocating {$a} assessment(s)';
$string['numofrandomlyallocatedsubmissions'] = 'Randomly allocating {$a} submissions';
$string['numofreviews'] = 'Number of reviews';
View
213 mod/workshop/allocation/random/lib.php
@@ -76,6 +76,7 @@ public function init() {
$o = array(); // list of output messages
$numofreviews = required_param('numofreviews', PARAM_INT);
$numper = required_param('numper', PARAM_INT);
+ $excludesamegroup = optional_param('excludesamegroup', false, PARAM_BOOL);
$removecurrent = optional_param('removecurrent', false, PARAM_BOOL);
$assesswosubmission = optional_param('assesswosubmission', false, PARAM_BOOL);
$addselfassessment = optional_param('addselfassessment', false, PARAM_BOOL);
@@ -96,7 +97,11 @@ public function init() {
} else {
$curassessments = $assessments;
}
- $randomallocations = $this->random_allocation($authors, $reviewers, $curassessments, $numofreviews, $numper, $o);
+ $options = array();
+ $options['numofreviews'] = $numofreviews;
+ $options['numper'] = $numper;
+ $options['excludesamegroup'] = $excludesamegroup;
+ $randomallocations = $this->random_allocation($authors, $reviewers, $curassessments, $o, $options);
$newallocations = array_merge($newallocations, $randomallocations);
$o[] = 'ok::' . get_string('numofrandomlyallocatedsubmissions', 'workshopallocation_random', count($randomallocations));
unset($randomallocations);
@@ -178,10 +183,37 @@ public function ui() {
$this->mform->display();
$out .= ob_get_contents();
ob_end_clean();
- $out .= $output->container_end();
+
+ // if there are some not-grouped participant in a group mode, warn the user
+ $gmode = groups_get_activity_groupmode($this->workshop->cm, $this->workshop->course);
+ if (VISIBLEGROUPS == $gmode or SEPARATEGROUPS == $gmode) {
+ $users = $this->workshop->get_potential_authors() + $this->workshop->get_potential_reviewers();
+ $users = $this->workshop->get_grouped($users);
+ if (isset($users[0])) {
+ $nogroupusers = $users[0];
+ foreach ($users as $groupid => $groupusers) {
+ if ($groupid == 0) {
+ continue;
+ }
+ foreach ($groupusers as $groupuserid => $groupuser) {
+ unset($nogroupusers[$groupuserid]);
+ }
+ }
+ if (!empty($nogroupusers)) {
+ $list = array();
+ foreach ($nogroupusers as $nogroupuser) {
+ $list[] = fullname($nogroupuser);
+ }
+ $a = implode(', ', $list);
+ $out .= $output->box(get_string('nogroupusers', 'workshopallocation_random', $a), 'generalbox warning nogroupusers');
+ }
+ }
+ }
// TODO $out .= $output->heading(get_string('stats', 'workshopallocation_random'));
+ $out .= $output->container_end();
+
return $out;
}
@@ -371,19 +403,27 @@ protected function get_unkept_assessments($assessments, $newallocations, $keepse
* is to connect each "circle" (circles are representing either authors or reviewers) with a required
* number of "squares" (the other type than circles are).
*
+ * The passed $options array must provide keys:
+ * (int)numofreviews - number of reviews to be allocated to each circle
+ * (int)numper - what user type the circles represent.
+ * (bool)excludesamegroup - whether to prevent peer submissions from the same group in visible group mode
+ *
* @param array $authors structure of grouped authors
* @param resource $reviewers structure of grouped reviewers
* @param array $assessments currently assigned assessments to be kept
- * @param mixed $numofreviews number of reviews to be allocated to each circle
- * @param mixed $numper what user type the circles represent
* @param array $o reference to an array of log messages
+ * @param array $options allocation options
* @return array array of (reviewerid => authorid) pairs
*/
- protected function random_allocation($authors, $reviewers, $assessments, $numofreviews, $numper, &$o) {
+ protected function random_allocation($authors, $reviewers, $assessments, &$o, array $options) {
if (empty($authors) || empty($reviewers)) {
// nothing to be done
return array();
}
+
+ $numofreviews = $options['numofreviews'];
+ $numper = $options['numper'];
+
if (self::USERTYPE_AUTHOR == $numper) {
// circles are authors, squares are reviewers
$o[] = 'info::Trying to allocate ' . $numofreviews . ' review(s) per author'; // todo translate
@@ -401,6 +441,22 @@ protected function random_allocation($authors, $reviewers, $assessments, $numofr
} else {
throw new moodle_exception('unknownusertypepassed', 'workshop');
}
+ // get the users that are not in any group. in visible groups mode, these users are exluded
+ // from allocation by this method
+ // $nogroupcircles is array (int)$userid => undefined
+ if (isset($allcircles[0])) {
+ $nogroupcircles = array_flip(array_keys($allcircles[0]));
+ } else {
+ $nogroupcircles = array();
+ }
+ foreach ($allcircles as $circlegroupid => $circles) {
+ if ($circlegroupid == 0) {
+ continue;
+ }
+ foreach ($circles as $circleid => $circle) {
+ unset($nogroupcircles[$circleid]);
+ }
+ }
// $o[] = 'debug::circle links = ' . json_encode($circlelinks);
// $o[] = 'debug::square links = ' . json_encode($squarelinks);
$squareworkload = array(); // individual workload indexed by squareid
@@ -428,76 +484,96 @@ protected function random_allocation($authors, $reviewers, $assessments, $numofr
// all users will be processed at once
$circlegroups = array(0);
}
- $this->shuffle_assoc($circlegroups);
// $o[] = 'debug::circle groups = ' . json_encode($circlegroups);
foreach ($circlegroups as $circlegroupid) {
$o[] = 'debug::processing circle group id ' . $circlegroupid;
$circles = $allcircles[$circlegroupid];
- $this->shuffle_assoc($circles);
- foreach ($circles as $circleid => $circle) {
- $o[] = 'debug::processing circle id ' . $circleid;
- if (!isset($circlelinks[$circleid])) {
- $circlelinks[$circleid] = array();
- }
- $keeptrying = true; // is there a chance to find a square for this circle?
- $failedgroups = array(); // array of groupids where the square should be chosen from (because
- // of their group workload) but it was not possible (for example there
- // was the only square and it had been already connected
- while ($keeptrying && (count($circlelinks[$circleid]) < $numofreviews)) {
- // firstly, choose a group to pick the square from
- if (NOGROUPS == $gmode) {
- if (in_array(0, $failedgroups)) {
- $keeptrying = false;
- $o[] = 'error::indent::No more peers available'; // todo translate
- break;
+ // iterate over all circles in the group until the requested number of links per circle exists
+ // or it is not possible to fulfill that requirment
+ // during the first iteration, we try to make sure that at least one circlelink exists. during the
+ // second iteration, we try to allocate two, etc.
+ for ($requiredreviews = 1; $requiredreviews <= $numofreviews; $requiredreviews++) {
+ $this->shuffle_assoc($circles);
+ $o[] = 'debug::iteration ' . $requiredreviews;
+ foreach ($circles as $circleid => $circle) {
+ if (VISIBLEGROUPS == $gmode and isset($nogroupcircles[$circleid])) {
+ $o[] = 'debug::skipping circle id ' . $circleid;
+ continue;
+ }
+ $o[] = 'debug::processing circle id ' . $circleid;
+ if (!isset($circlelinks[$circleid])) {
+ $circlelinks[$circleid] = array();
+ }
+ $keeptrying = true; // is there a chance to find a square for this circle?
+ $failedgroups = array(); // array of groupids where the square should be chosen from (because
+ // of their group workload) but it was not possible (for example there
+ // was the only square and it had been already connected
+ while ($keeptrying && (count($circlelinks[$circleid]) < $requiredreviews)) {
+ // firstly, choose a group to pick the square from
+ if (NOGROUPS == $gmode) {
+ if (in_array(0, $failedgroups)) {
+ $keeptrying = false;
+ $o[] = 'error::indent::No more peers available'; // todo translate
+ break;
+ }
+ $targetgroup = 0;
+ } elseif (SEPARATEGROUPS == $gmode) {
+ if (in_array($circlegroupid, $failedgroups)) {
+ $keeptrying = false;
+ $o[] = 'error::indent::No more peers available in this separate group'; // todo translate
+ break;
+ }
+ $targetgroup = $circlegroupid;
+ } elseif (VISIBLEGROUPS == $gmode) {
+ $trygroups = array_diff_key($squaregroupsworkload, array(0 => null)); // all but [0]
+ $trygroups = array_diff_key($trygroups, array_flip($failedgroups)); // without previous failures
+ if ($options['excludesamegroup']) {
+ // exclude groups the circle is member of
+ $excludegroups = array();
+ foreach (array_diff_key($allcircles, array(0 => null)) as $exgroupid => $exgroupmembers) {
+ if (array_key_exists($circleid, $exgroupmembers)) {
+ $excludegroups[$exgroupid] = null;
+ }
+ }
+ $trygroups = array_diff_key($trygroups, $excludegroups);
+ }
+ $targetgroup = $this->get_element_with_lowest_workload($trygroups);
}
- $targetgroup = 0;
- } elseif (SEPARATEGROUPS == $gmode) {
- if (in_array($circlegroupid, $failedgroups)) {
+ if ($targetgroup === false) {
$keeptrying = false;
- $o[] = 'error::indent::No more peers available in this separate group'; // todo translate
+ $o[] = 'error::indent::Not enough peers available'; // todo translate
break;
}
- $targetgroup = $circlegroupid;
- } elseif (VISIBLEGROUPS == $gmode) {
- $trygroups = array_diff_key($squaregroupsworkload, array(0 => null)); // all but [0]
- $trygroups = array_diff_key($trygroups, array_flip($failedgroups)); // without previous failures
- $targetgroup = $this->get_element_with_lowest_workload($trygroups);
- }
- if ($targetgroup === false) {
- $keeptrying = false;
- $o[] = 'error::indent::Not enough peers available'; // todo translate
- break;
- }
- $o[] = 'debug::indent::next square should be from group id ' . $targetgroup;
- // now, choose a square from the target group
- $trysquares = array_intersect_key($squareworkload, $allsquares[$targetgroup]);
- // $o[] = 'debug::indent::individual workloads in this group are ' . json_encode($trysquares);
- unset($trysquares[$circleid]); // can't allocate to self
- $trysquares = array_diff_key($trysquares, array_flip($circlelinks[$circleid])); // can't re-allocate the same
- $targetsquare = $this->get_element_with_lowest_workload($trysquares);
- if (false === $targetsquare) {
- $o[] = 'debug::indent::unable to find an available square. trying another group';
- $failedgroups[] = $targetgroup;
- continue;
- }
- $o[] = 'debug::indent::target square = ' . $targetsquare;
- // ok - we have found the square
- $circlelinks[$circleid][] = $targetsquare;
- $squarelinks[$targetsquare][] = $circleid;
- $squareworkload[$targetsquare]++;
- $o[] = 'debug::indent::increasing square workload to ' . $squareworkload[$targetsquare];
- if ($targetgroup) {
- // recalculate the group workload
- $squaregroupsworkload[$targetgroup] = 0;
- foreach ($allsquares[$targetgroup] as $squareid => $square) {
- $squaregroupsworkload[$targetgroup] += $squareworkload[$squareid];
+ $o[] = 'debug::indent::next square should be from group id ' . $targetgroup;
+ // now, choose a square from the target group
+ $trysquares = array_intersect_key($squareworkload, $allsquares[$targetgroup]);
+ // $o[] = 'debug::indent::individual workloads in this group are ' . json_encode($trysquares);
+ unset($trysquares[$circleid]); // can't allocate to self
+ $trysquares = array_diff_key($trysquares, array_flip($circlelinks[$circleid])); // can't re-allocate the same
+ $targetsquare = $this->get_element_with_lowest_workload($trysquares);
+ if (false === $targetsquare) {
+ $o[] = 'debug::indent::unable to find an available square. trying another group';
+ $failedgroups[] = $targetgroup;
+ continue;
}
- $squaregroupsworkload[$targetgroup] /= count($allsquares[$targetgroup]);
- $o[] = 'debug::indent::increasing group workload to ' . $squaregroupsworkload[$targetgroup];
- }
- } // end of processing this circle
- } // end of processing circles in the group
+ $o[] = 'debug::indent::target square = ' . $targetsquare;
+ // ok - we have found the square
+ $circlelinks[$circleid][] = $targetsquare;
+ $squarelinks[$targetsquare][] = $circleid;
+ $squareworkload[$targetsquare]++;
+ $o[] = 'debug::indent::increasing square workload to ' . $squareworkload[$targetsquare];
+ if ($targetgroup) {
+ // recalculate the group workload
+ $squaregroupsworkload[$targetgroup] = 0;
+ foreach ($allsquares[$targetgroup] as $squareid => $square) {
+ $squaregroupsworkload[$targetgroup] += $squareworkload[$squareid];
+ }
+ $squaregroupsworkload[$targetgroup] /= count($allsquares[$targetgroup]);
+ $o[] = 'debug::indent::increasing group workload to ' . $squaregroupsworkload[$targetgroup];
+ }
+ } // end of processing this circle
+ } // end of one iteration of processing circles in the group
+ } // end of all iterations over circles in the group
} // end of processing circle groups
$returned = array();
if (self::USERTYPE_AUTHOR == $numper) {
@@ -569,15 +645,14 @@ protected function get_element_with_lowest_workload($workload) {
/**
* Shuffle the order of array elements preserving the key=>values
*
- * @author rich at home dot nl
- * @link http://php.net/manual/en/function.shuffle.php#80586
* @param array $array to be shuffled
* @return true
*/
protected function shuffle_assoc(&$array) {
if (count($array) > 1) {
// $keys needs to be an array, no need to shuffle 1 item or empty arrays, anyway
- $keys = array_rand($array, count($array));
+ $keys = array_keys($array);
+ shuffle($keys);
foreach($keys as $key) {
$new[$key] = $array[$key];
}
View
8 mod/workshop/allocation/random/settings_form.php
@@ -74,6 +74,14 @@ public function definition() {
$mform->setDefault('numper', workshop_random_allocator::USERTYPE_AUTHOR);
$mform->addGroup($grpnumofreviews, 'grpnumofreviews', get_string('numofreviews', 'workshopallocation_random'),
array(' '), false);
+
+ if (VISIBLEGROUPS == $gmode) {
+ $mform->addElement('checkbox', 'excludesamegroup', get_string('excludesamegroup', 'workshopallocation_random'));
+ $mform->setDefault('excludesamegroup', 0);
+ } else {
+ $mform->addElement('hidden', 'excludesamegroup', 0);
+ }
+
$mform->addElement('checkbox', 'removecurrent', get_string('removecurrentallocations', 'workshopallocation_random'));
$mform->setDefault('removecurrent', 0);
View
4 mod/workshop/allocation/random/styles.css
@@ -0,0 +1,4 @@
+.path-mod-workshop .random-allocator .warning {
+ width: 80%;
+ margin: 0px auto 15px auto;
+}

0 comments on commit bb871bc

Please sign in to comment.
Something went wrong with that request. Please try again.