Passed
Push — master ( 291666...ef8eea )
by Boudry
03:25
created

VotesProcess::getVotesListGenerator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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