Completed
Branch dev-1.7.x (184d88)
by Boudry
05:25
created

VotesProcess::getVoteKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/*
3
    Condorcet PHP - Election manager and results calculator.
4
    Designed for the Condorcet method. Integrating a large number of algorithms extending Condorcet. Expandable for all types of voting systems.
5
6
    By Julien Boudry and contributors - MIT LICENSE (Please read LICENSE.txt)
7
    https://github.com/julien-boudry/Condorcet
8
*/
9
declare(strict_types=1);
10
11
namespace Condorcet\ElectionProcess;
12
13
use Condorcet\CondorcetException;
14
use Condorcet\CondorcetUtil;
15
use Condorcet\Vote;
16
use Condorcet\DataManager\VotesManager;
17
18
// Manage Results for Election class
19
trait VotesProcess
20
{
21
22
/////////// CONSTRUCTOR ///////////
23
24
    // Data and global options
25
    protected $_Votes; // Votes list
26
    protected $_ignoreStaticMaxVote = false;
27
28
29 1
    public function ignoreMaxVote (bool $state = true) : bool
30
    {
31 1
        return $this->_ignoreStaticMaxVote = $state;
32
    }
33
34
35
/////////// VOTES LIST ///////////
36
37
    // How many votes are registered ?
38 11
    public function countVotes ($tag = null, bool $with = true) : int
39
    {
40 11
        return $this->_Votes->countVotes(VoteUtil::tagsConvert($tag),$with);
41
    }
42
43
    // Sum votes weight
44 5
    public function sumVotesWeight () : int
45
    {
46 5
        return $this->_Votes->sumVotesWeight();
47
    }
48
49
    // Get the votes registered list
50 11
    public function getVotesList ($tag = null, bool $with = true) : array
51
    {
52 11
        return $this->_Votes->getVotesList(VoteUtil::tagsConvert($tag), $with);
53
    }
54
55 5
    public function getVotesListAsString () : string
56
    {
57 5
        return $this->_Votes->getVotesListAsString();
58
    }
59
60 94
    public function getVotesManager () : VotesManager {
61 94
        return $this->_Votes;
62
    }
63
64 2
    public function getVotesListGenerator ($tag = null, bool $with = true) : \Generator
65
    {
66 2
        return $this->_Votes->getVotesListGenerator(VoteUtil::tagsConvert($tag), $with);
67
    }
68
69 5
    public function getVoteKey (Vote $vote) {
70 5
        return $this->_Votes->getVoteKey($vote);
71
    }
72
73
74
/////////// ADD & REMOVE VOTE ///////////
75
76
    // Add a single vote. Array key is the rank, each candidate in a rank are separate by ',' It is not necessary to register the last rank.
77 109
    public function addVote ($vote, $tag = null) : Vote
78
    {
79 109
        $this->prepareVoteInput($vote, $tag);
80
81
        // Check Max Vote Count
82 109
        if ( self::$_maxVoteNumber !== null && !$this->_ignoreStaticMaxVote && $this->countVotes() >= self::$_maxVoteNumber ) :
83 1
            throw new CondorcetException(16, self::$_maxVoteNumber);
84
        endif;
85
86
87
        // Register vote
88 109
        return $this->registerVote($vote, $tag); // Return the vote object
89
    }
90
91
    // return True or throw an Exception
92 6
    public function prepareModifyVote (Vote $existVote) : void
93
    {
94
        try {
95 6
            $this->prepareVoteInput($existVote);
96 6
            $this->setStateToVote();
97
98 6
            if ($this->_Votes->isUsingHandler()) : 
99 6
                $this->_Votes[$this->getVoteKey($existVote)] = $existVote;
100
            endif;
101
        }
102
        catch (\Exception $e) {
103
            throw $e;
104
        }
105 6
    }
106
107 109
    public function checkVoteCandidate (Vote $vote) : bool
108
    {
109 109
        $linkCount = $vote->countLinks();
110 109
        $links = $vote->getLinks();
111
112 109
        $mirror = $vote->getRanking();
113 109
        $change = false;
114 109
        foreach ($vote as $rank => $choice) :
115 109
            foreach ($choice as $choiceKey => $candidate) :
116 109
                if ( !$this->existCandidateId($candidate, true) ) :
117 102
                    if ($candidate->getProvisionalState() && $this->existCandidateId($candidate, false)) :
118 101
                        if ( $linkCount === 0 || ($linkCount === 1 && reset($links) === $this) ) :
119 101
                            $mirror[$rank][$choiceKey] = $this->_Candidates[$this->getCandidateKey((string) $candidate)];
120 101
                            $change = true;
121
                        else :
122 109
                            return false;
123
                        endif;
124
                    endif;
125
                endif;
126
            endforeach;
127
        endforeach;
128
129 109
        if ($change) :
130 101
            $vote->setRanking(  $mirror,
131 101
                                ( abs($vote->getTimestamp() - microtime(true)) > 0.5 ) ? ($vote->getTimestamp() + 0.001) : null
132
            );
133
        endif;
134
135 109
        return true;
136
    }
137
138
    // Write a new vote
139 109
    protected function registerVote (Vote $vote, $tag = null) : Vote
140
    {
141
        // Vote identifiant
142 109
        $vote->addTags($tag);
143
        
144
        // Register
145
        try {
146 109
            $vote->registerLink($this);
147 109
            $this->_Votes[] = $vote;
148
        } catch (CondorcetException $e) {
149
            // Security : Check if vote object not already register
150
            throw new CondorcetException(6,'Vote object already registred');
151
        }
152
153 109
        return $vote;
154
    }
155
156 5
    public function removeVote ($in, bool $with = true) : array
157
    {    
158 5
        $rem = [];
159
160 5
        if ($in instanceof Vote) :
161 4
            $key = $this->getVoteKey($in);
162 4
            if ($key !== false) :
163 4
                $this->_Votes[$key]->destroyLink($this);
164
165 4
                $rem[] = $this->_Votes[$key];
166
167 4
                unset($this->_Votes[$key]);
168
            endif;
169
        else :
170
            // Prepare Tags
171 3
            $tag = VoteUtil::tagsConvert($in);
172
173
            // Deleting
174 3
            foreach ($this->getVotesList($tag, $with) as $key => $value) :
175 3
                $this->_Votes[$key]->destroyLink($this);
176
177 3
                $rem[] = $this->_Votes[$key];
178
179 3
                unset($this->_Votes[$key]);
180
            endforeach;
181
182
        endif;
183
184 5
        return $rem;
185
    }
186
187
188
/////////// PARSE VOTE ///////////
189
190
    // Return the well formated vote to use.
191 109
    protected function prepareVoteInput (&$vote, $tag = null) : void
192
    {
193 109
        if (!($vote instanceof Vote)) :
194 93
            $vote = new Vote ($vote, $tag);
195
        endif;
196
197
        // Check array format && Make checkVoteCandidate
198 109
        if ( !$this->checkVoteCandidate($vote) ) :
199
            throw new CondorcetException(5);
200
        endif;
201 109
    }
202
203 2
    public function jsonVotes (string $input)
204
    {
205 2
        $input = CondorcetUtil::prepareJson($input);
206 1
        if ($input === false) :
207
            return $input;
208
        endif;
209
210
            //////
211
212 1
        $adding = [];
213
214 1
        foreach ($input as $record) :
215 1
            if (empty($record['vote'])) :
216 1
                continue;
217
            endif;
218
219 1
            $tags = (!isset($record['tag'])) ? null : $record['tag'];
220 1
            $multi = (!isset($record['multi'])) ? 1 : $record['multi'];
221
222 1
            for ($i = 0; $i < $multi; $i++) :
223 1
                if (self::$_maxParseIteration !== null && $this->countVotes() >= self::$_maxParseIteration) :
224
                    throw new CondorcetException(12, self::$_maxParseIteration);
225
                endif;
226
227
                try {
228 1
                    $adding[] = $this->addVote($record['vote'], $tags);
229
                } catch (\Exception $e) {
230
                    // Ignore invalid vote
231
                }
232
            endfor;
233
        endforeach;
234
235 1
        return $adding;
236
    }
237
238 81
    public function parseVotes (string $input, bool $allowFile = true)
239
    {
240 81
        $input = CondorcetUtil::prepareParse($input, $allowFile);
241 81
        if ($input === false) :
242
            return $input;
243
        endif;
244
245
        // Check each lines
246 81
        $adding = [];
247 81
        foreach ($input as $line) :
248
            // Empty Line
249 81
            if (empty($line)) :
250 70
                continue;
251
            endif;
252
253
            // Multiples
254 81
            $is_multiple = mb_strpos($line, '*');
255 81 View Code Duplication
            if ($is_multiple !== false) :
256 70
                $multiple = trim( substr($line, $is_multiple + 1) );
257
258
                // Errors
259 70
                if ( !is_numeric($multiple) ) :
260
                    throw new CondorcetException(13, null);
261
                endif;
262
263 70
                $multiple = intval($multiple);
264
265
                // Reformat line
266 70
                $line = substr($line, 0, $is_multiple);
267
            else :
268 14
                $multiple = 1;
269
            endif;
270
271
            // Vote Weight
272 81
            $is_voteWeight = mb_strpos($line, '^');
273 81 View Code Duplication
            if ($is_voteWeight !== false) :
274 3
                $weight = trim( substr($line, $is_voteWeight + 1) );
275
276
                // Errors
277 3
                if ( !is_numeric($weight) ) :
278
                    throw new CondorcetException(13, null);
279
                endif;
280
281 3
                $weight = intval($weight);
282
283
                // Reformat line
284 3
                $line = substr($line, 0, $is_voteWeight);
285
            else :
286 80
                $weight = 1;
287
            endif;
288
289
            // Tags + vote
290 81
            if (mb_strpos($line, '||') !== false) :
291 6
                $data = explode('||', $line);
292
293 6
                $vote = $data[1];
294 6
                $tags = $data[0];
295
            // Vote without tags
296
            else :
297 80
                $vote = $line;
298 80
                $tags = null;
299
            endif;
300
301
            // addVote
302 81
            for ($i = 0; $i < $multiple; $i++) :
303 81 View Code Duplication
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
304 1
                    throw new CondorcetException(12, self::$_maxParseIteration);
305
                endif;
306
307
                try {
308 81
                    $adding[] = ($newVote = $this->addVote($vote, $tags));
309 81
                    $newVote->setWeight($weight);
310 1
                } catch (CondorcetException $e) {}
1 ignored issue
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
311
            endfor;
312
        endforeach;
313
314 81
        return $adding;
315
    }
316
}
317