1<?php
2
3// must be run within Dokuwiki
4if (!defined('DOKU_INC')) die();
5
6class helper_plugin_schulzevote extends DokuWiki_Plugin {
7
8    function __construct() {
9        global $ID;
10        $data = p_get_metadata($ID, 'schulzevote');
11        $this->candys = $data['candys'];
12        $this->votes = array();
13        $this->outdated = false;
14        if (isset($data['outdated'])) {
15            $this->outdated = $data['outdated'];
16        }
17        if (!isset($data['votes'])) {
18            // did not find any votes list ; must be an old vote
19            if (isset($data['prefer']) && isset($data['votees'])) {
20                $this->updatePoll($data);
21            }
22        }
23        else {
24            $this->votes = $data['votes'];
25        }
26
27/* Ambiguous, but B > C and D > A
28        $prefer = array(
29'A' => array('A' => 0, 'B' => 5, 'C' => 5, 'D' => 3),
30'B' => array('A' => 4, 'B' => 0, 'C' => 7, 'D' => 5),
31'C' => array('A' => 4, 'B' => 2, 'C' => 0, 'D' => 5),
32'D' => array('A' => 6, 'B' => 4, 'C' => 4, 'D' => 0),
33);
34        $this->candys = array(
35'A' => 'A',
36'B' => 'B',
37'C' => 'C',
38'D' => 'D',
39);
40*/
41
42/* E > A > C > B > D
43        $prefer = array(
44'A' => array('A' => 0, 'B' => 20, 'C' => 26, 'D' => 30, 'E' => 22),
45'B' => array('A' => 25, 'B' => 0, 'C' => 16, 'D' => 33, 'E' => 18),
46'C' => array('A' => 19, 'B' => 29, 'C' => 0, 'D' => 17, 'E' => 24),
47'D' => array('A' => 15, 'B' => 12, 'C' => 28, 'D' => 0, 'E' => 14),
48'E' => array('A' => 23, 'B' => 27, 'C' => 21, 'D' => 31, 'E' => 0),
49);
50        $this->candys = array(
51'A' => 'A',
52'B' => 'B',
53'C' => 'C',
54'D' => 'D',
55'E' => 'E',
56);
57*/
58    }
59
60    public $outdated;
61
62    function __destruct() {
63        global $ID;
64        p_set_metadata($ID, array('schulzevote' => array(
65            'candys' => $this->candys,
66            'votes' => $this->votes,
67            'outdated' => $this->outdated)));
68    }
69
70    // run a vote $data = array('a' => 1, 'b' => 2, 'c' => 2, 'd' => 3)
71    // user need to be logged in
72    function vote($data) {
73        global $ID;
74        if ($this->hasVoted())
75            return false;
76        $unique_values = array();
77        foreach ($data as $cand => $score) {
78            if ($score === 0)
79                return false;
80            if (in_array($score, $unique_values))
81                return false;
82            $unique_values[] = $score;
83        }
84        $this->votes[] = array('user' => $_SERVER['REMOTE_USER'], 'data' => $data);
85        return true;
86    }
87
88    // recalc the winner and prefer matrix
89    function getPreferences() {
90        if (empty($this->votes))
91            return array();
92        $prefer = array();
93        foreach ($this->votes as $vote) {
94            foreach ($vote['data'] as $k => $v) {
95                foreach($vote['data'] as $k2 => $v2) {
96                    if ($v < $v2) {
97                        ++$prefer[$k][$k2];
98                    }
99                }
100            }
101        }
102        return $prefer;
103    }
104
105    function hasVoted() {
106        foreach ($this->votes as $vote)
107          if ($vote['user'] === $_SERVER['REMOTE_USER'])
108            return true;
109        return false;
110    }
111
112    /* Return strength of the strongest path */
113    function get() {
114
115        $in = $this->getPreferences();
116        $out = array();
117        foreach ($this->candys as $i) {
118            foreach ($this->candys as $j) {
119                if ($i != $j && $in[$i][$j] > $in[$j][$i]) {
120                    $out[$i][$j] = $in[$i][$j];
121                } else {
122                    $out[$i][$j] = 0;
123                }
124            }
125        }
126
127        foreach ($this->candys as $i) {
128            foreach ($this->candys as $j) {
129                if ($i!=$j) {
130                    foreach ($this->candys as $k) {
131                        if ($i!=$k) {
132                            if ($j!=$k) {
133                                $out[$j][$k] = max($out[$j][$k], min($out[$j][$i], $out[$i][$k]));
134                            }
135                        }
136                    }
137                }
138            }
139        }
140        return $out;
141    }
142
143    function getRanking() {
144        $get = $this->get();
145
146        $ret = array();
147        while (count($get) > 0) {
148            $winners = array();
149            foreach (array_keys($get) as $test) {
150                if ($this->isWinnerCandidate($test, $get)) {
151                    $winners[] = $test;
152                }
153            }
154            if (count($winners) == 0) {
155                $winners = array_keys($get);
156            }
157            foreach($winners as $winner) {
158                unset($get[$winner]);
159            }
160            $ret[] = $winners;
161        }
162        return $ret;
163    }
164
165    function getWinner() {
166        $get = $this->get();
167#dbg($get);
168
169        foreach ($this->candys as $test) {
170#            echo "test $test...";
171            if ($this->isWinner($test, $get)) {
172#                echo "winner!";
173                return $test;
174            }
175#            echo "looser!\n";
176        }
177        return null;
178    }
179
180    function isWinnerCandidate($candy, $get) {
181        foreach ($this->candys as $other) {
182            if ($candy == $other) continue;
183            if ($get[$candy][$other] < $get[$other][$candy]) {
184                return false;
185            }
186        }
187        return true;
188    }
189
190    function isWinner($candy, $get) {
191        foreach ($this->candys as $other) {
192            if ($candy == $other) continue;
193            if ($get[$candy][$other] <= $get[$other][$candy]) {
194                return false;
195            }
196        }
197        return true;
198    }
199
200    // create a new vote to a candidate array
201    function createVote($candidates) {
202        if ($candidates !== $this->candys) {
203            $this->candys = $candidates;
204            $this->votes = array();
205        }
206    }
207
208    // remove a vote for a candidate
209    function deleteVote() {
210        foreach ($this->votes as $id => $vote)
211            if ($vote['user'] === $_SERVER['REMOTE_USER']) {
212                unset ($this->votes[$id]);
213                return true;
214            }
215        return false;
216    }
217
218    // get user's vote
219    function getVote() {
220        foreach ($this->votes as $id => $vote) {
221            if ($vote['user'] === $_SERVER['REMOTE_USER']) {
222                return $vote['data'];
223            }
224        }
225        return array();
226    }
227
228    // dealing with older data
229    function updatePoll($data) {
230        $vote = array('user' => 'unknown', 'data' => array());
231        $vote_this = array('user' => $_SERVER['REMOTE_USER'], 'data' => array());
232        foreach ($this->candys as $cand) {
233            $vote['data'][$cand] = 1;
234            $vote_this['data'][$cand] = 0;
235        }
236        foreach ($data['prefer'] as $cand => $pref) {
237            foreach ($pref as $cand2 => $score) {
238                $vote['data'][$cand2] += $score;
239            }
240        }
241        array_push($this->votes, $vote, $vote_this);
242        $this->outdated = true;
243    }
244}
245