diff --git a/classes/ratings_and_allocations_table.php b/classes/ratings_and_allocations_table.php index 48e386b4..d4de1ee6 100644 --- a/classes/ratings_and_allocations_table.php +++ b/classes/ratings_and_allocations_table.php @@ -56,6 +56,11 @@ class ratings_and_allocations_table extends \table_sql { */ private $showgroups; + /** + * @var bool if true the table should show a column with the teams of the teamvote grouping. + */ + private $showteams; + /** * @var bool if true the cells are rendered as radio buttons */ @@ -95,6 +100,7 @@ public function __construct(\mod_ratingallocate_renderer $renderer, $titles, $ra $this->shownames = true; // We only show the group column if at least one group is being used in at least one active restriction setting of a choice. $this->showgroups = !empty($allgroupsofchoices); + $this->showteams = (bool) $this->ratingallocate->get_teamvote_goups(); } /** @@ -181,6 +187,10 @@ public function setup_table($choices, $hidenorating = null, $showallocnecessary $this->ratingallocate->get_choice_groups($choice->id)); } } + if ($this->showteams) { + $columns[] = 'teams'; + $headers[] = get_string('teams'); + } } // Setup filter. @@ -211,12 +221,20 @@ public function setup_table($choices, $hidenorating = null, $showallocnecessary $this->define_headers($headers); // Set additional table settings. + if ($this->showteams) { + //$this->sortable(true, 'teams'); + } else { + + } $this->sortable(true, 'lastname'); $tableclasses = 'ratingallocate_ratings_table'; if ($this->showgroups) { $tableclasses .= ' includegroups'; $this->no_sorting('groups'); } + if ($this->showteams) { + $this->no_sorting('teams'); + } $this->set_attribute('class', $tableclasses); $this->initialbars(true); @@ -327,6 +345,18 @@ private function add_user_ratings_row($user, $userratings, $userallocations) { }, $groupsofuser); $row['groups'] = implode(';', $groupnames); } + if ($this->showteams) { + $teamofuser = array_filter(array_keys($this->ratingallocate->get_teamvote_goups()), + function($groupid) use ($user) { + return groups_is_member($groupid,$user->id); + } + ); + $teamname = array_map(function ($team) { + return $team->name; + }, $teamofuser); + // We should only have one team for each user, but we cant ensure that at this point. + $row['teams'] = implode(';', $teamname); + } } foreach ($userratings as $choiceid => $userrating) { @@ -662,6 +692,10 @@ function($c) { } + private function sort_by_teams ($teams) { + + } + /** * Sets up the sql statement for querying the table data. */ @@ -673,6 +707,7 @@ public function init_sql() { $userids = $this->filter_userids($userids); $sortfields = $this->get_sort_columns(); + var_dump($sortfields); $fields = "u.*"; if ($userids) { $where = "u.id in (" . implode(",", $userids) . ")"; @@ -690,6 +725,8 @@ public function init_sql() { $from .= " LEFT JOIN {ratingallocate_ratings} r$i ON u.id = r$i.userid AND r$i.choiceid = :choiceid$i "; $fields .= ", r$i.rating as $key"; $params["choiceid$i"] = $id; + } else if (substr($key, 0, 5) == "teams") { + //$from .= } } diff --git a/form_manual_allocation.php b/form_manual_allocation.php index 62356a13..c5f01237 100644 --- a/form_manual_allocation.php +++ b/form_manual_allocation.php @@ -148,6 +148,10 @@ public function definition_after_data() { $mform->setDefault('filtergroup', $filter['groupselect']); $mform->getElement('filtergroup')->setSelected($filter['groupselect']); + if ($this->ratingallocate->get_teamvote_goups()) { + + } + $PAGE->requires->js_call_amd('mod_ratingallocate/radiobuttondeselect', 'init'); // The rest must be done through output buffering due to the way flextable works. diff --git a/locallib.php b/locallib.php index eee10e89..2c9475b1 100644 --- a/locallib.php +++ b/locallib.php @@ -327,6 +327,8 @@ private function process_action_give_rating() { } else if ($status === self::DISTRIBUTION_STATUS_RATING_IN_PROGRESS) { // Rating is possible... + // Adde votegroup name zu form. + // Suche das richtige Formular nach Strategie. $strategyform = 'ratingallocate\\' . $this->ratingallocate->strategy . '\\mod_ratingallocate_view_form'; @@ -1290,8 +1292,14 @@ public function get_teamvote_goups() { // If voting for users not in groups is not disabled, we have to also consider the users that do not have a group. if ($this->db->get_field(this_db\ratingallocate::TABLE, 'preventvotenotingroup', ['id' => $this->ratingallocateid]) == 0) { + // Get all users not in a group of the teamvote grouping. - $usersnogroup = array_diff($this->get_raters_in_course(), groups_get_grouping_members($groupingid)); + $usersnogroup = array(); + foreach ($this->get_raters_in_course() as $rater) { + if (!in_array($rater, groups_get_grouping_members($groupingid))) { + $usersnogroup[] = $rater; + } + } $groupdata = new stdClass(); $groupdata->courseid = $this->course->id; diff --git a/solver/edmonds-karp.php b/solver/edmonds-karp.php index 74959b2f..57c1b093 100644 --- a/solver/edmonds-karp.php +++ b/solver/edmonds-karp.php @@ -67,6 +67,7 @@ public function compute_distribution($choicerecords, $ratings, $usercount, $team } else { + var_dump("Teamvote = true"); $teamcount = count($teamvote); $sink = $choicecount + $teamcount + 1; @@ -79,7 +80,7 @@ public function compute_distribution($choicerecords, $ratings, $usercount, $team // with Bellman-Ford as search function (see: Edmonds-Karp in Introduction to Algorithms) // http://stackoverflow.com/questions/6681075/while-loop-in-php-with-assignment-operator // Look for an augmenting path (a shortest path from the source to the sink). - while ($path = $this->find_shortest_path_bellf($source, $sink)) { // If the function returns null, the while will stop. + while ($path = $this->find_shortest_path_bellf_cspf($source, $sink, $teamvote, $toteamid)) { // If the function returns null, the while will stop. // Reverse the augmentin path, thereby distributing a user into a group. $this->augment_flow($path); unset($path); // Clear up old path. @@ -90,14 +91,190 @@ public function compute_distribution($choicerecords, $ratings, $usercount, $team } + /** + * Find the shortest path with constraint (enough space for all teammembers in choice). + * This is a modified version of the Yen Algorithm for the consstrained shortest path first problem. + * + * @param $from + * @param $to + * @param $teamvote + * @param $toteamid + * @return array|mixed|null array of the nodes in the path, null if no path found. + */ + private function find_shortest_path_bellf_cspf ($from, $to, $teamvote, $toteamid) { + + // Find the first shortest path. + $pathcandidates = array(); + $pathcandidates[0] = $this->find_shortest_path_bellf($from, $to); + + $nopathfound = is_null($pathcandidates[0]); + + // Check if the path fulfills our constraint: space in choice left >= teammembers. + $constraintflag = true; + $foundedge = null; + foreach ($this->graph[$pathcandidates[0][1]] as $edge) { + if ($edge->to == $pathcandidates[0][0]) { + $foundedge = $edge; + break; + } + } + if ($foundedge->space <= $teamvote[$toteamid[$pathcandidates[0][2]]]) { + $constraintflag = false; + } + + if ($constraintflag) { + // We just found the shortest path fulfilling the constraint. + return $pathcandidates[0]; + } + $constraintflag = true; + + // Array of the potential next shortest paths. + $nextpaths = array(); + $restoreedges = array(); + $restorenodes = array(); + + // Now find the next shortest path. + $k = 1; + // Exit if there are no more shortest paths (nopathfound=true). + while (!$nopathfound && $k < 100) { + for ($i = 0; $i < count($pathcandidates[$k - 1]); $i++) { + + var_dump("Im algo
"); + var_dump($pathcandidates); + var_dump(":Pathcandidates
"); + + // Spurnode ranges from first to next to last node in previous shortest path. + $spurnode = $pathcandidates[$k - 1][$i]; + $rootpath = array_slice($pathcandidates[$k - 1], 0, $i+1, true); + + foreach ($pathcandidates as $path) { + + if ($rootpath == array_slice($path, 0, $i+1, true)) { + foreach ($this->graph[$path[$i + 1]] as $index => $edge) { + if ($edge->to == $path[$i]) { + // Remove the links that are part of the previous shortest paths. + // Which share the same root path. + $restoreedges[$path[$i + 1]][$index] = $edge; + array_splice($this->graph[$path[$i + 1]], $index, 1); + break; + } + } + } else { + continue; + } + + foreach ($rootpath as $rootpathnode) { + if ($rootpathnode != $spurnode) { + // Remove $rootpathnode from graph. + foreach ($this->graph as $index => $graphnode) { + if ($graphnode == $rootpathnode) { + $restorenodes[$index] = $graphnode; + unset($this->graph[$index]); + } + } + } + } + + // Calculate the spur path from the spur node to the sink. + $spurpath = $this->find_shortest_path_bellf($i, $to); + + // Entire path is made up of the root path and spur path. + $totalpath = array_merge($rootpath, $spurpath); + + // Add the potential next shortest path to the heap. + $nextpaths[] = $totalpath; + + // Now add back edges and nodes that were removed from the graph. + foreach ($restoreedges as $index1 => $node) { + foreach ($node as $index2 => $edge) { + $this->graph[$index1][$index2] = $edge; + } + } + foreach ($restorenodes as $index => $node) { + $this->graph[$index] = $node; + } + } + + if (empty($nextpaths)) { + var_dump("No path found
"); + $nopathfound = true; + break; + } + + var_dump($nextpaths); + // Sort the potential next shortest paths by cost. -> nextpaths[0] = best path with lowest cost. + usort($nextpaths, function ($path1, $path2) { + return ($this->get_cost_of_path($path1) - $this->get_cost_of_path($path2)); + }); + var_dump("
Sortieren...
"); + var_dump($nextpaths); + + // Check if the next best path fullfillst our constraint. + foreach ($this->graph[$nextpaths[0][1]] as $edge) { + if ($edge->to == $nextpaths[0][0]) { + $foundedge = $edge; + break; + } + } + if ($foundedge->space <= $teamvote[$toteamid[$nextpaths[0][2]]]) { + $constraintflag = false; + } + + if ($constraintflag) { + var_dump("Path found"); + return $nextpaths[0]; + } + + $pathcandidates[$k] = $nextpaths[0]; + + // Reset flag condition. + $constraintflag = true; + + array_pop($nextpaths); + } + $k++; + } + return null; + } + + /** + * Returns the cost of the path by adding the weight of all edges in the path. + * + * @param $path + * @return int cost + */ + private function get_cost_of_path ($path) { + + $cost = 0; + + for ($i = count($path) - 1; $i > 0; $i--) { + $from = $path[$i]; + $to = $path[$i - 1]; + $edge = null; + // Find the edge. + foreach ($this->graph[$from] as $index => $edge) { + if ($edge->to == $to) { + $cost += $edge->weight; + break; + } + } + } + + return $cost; + } + /** * Bellman-Ford acc. to Cormen * - * @param $from index of starting node - * @param $to index of end node + * @param $from int index of starting node + * @param $to int index of end node * @return array with the of the nodes in the path */ private function find_shortest_path_bellf($from, $to) { + + // We have to alter this method to fit teamvote (find the shortest path with flow >= teammembers). + // This is a constrained shortest path first problem. + // Table of distances known so far. $dists = array(); // Table of predecessors (used to reconstruct the shortest path later). diff --git a/solver/solver-template.php b/solver/solver-template.php index cf5ebfab..82728f24 100644 --- a/solver/solver-template.php +++ b/solver/solver-template.php @@ -116,7 +116,7 @@ public function distribute_users(\ratingallocate $ratingallocate) { $userids = array(); foreach ($distributions as $choiceid => $teamids) { foreach ($teamids as $teamid) { - $userids[$teamid] = groups_get_members($teamid, 'id'); + $userids[$teamid] = groups_get_members($teamid, 'u.id'); } $userdistributions[$choiceid] = array_merge($userids); }