Completed
Branch dev-2.0 (210bc7)
by Boudry
06:33
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
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 CondorcetPHP\Condorcet\ElectionProcess;
12
13
use CondorcetPHP\Condorcet\CondorcetException;
14
use CondorcetPHP\Condorcet\CondorcetUtil;
15
use CondorcetPHP\Condorcet\Vote;
16
use CondorcetPHP\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
27
28
/////////// VOTES LIST ///////////
29
30
    // How many votes are registered ?
31 12
    public function countVotes ($tag = null, bool $with = true) : int
32
    {
33 12
        return $this->_Votes->countVotes(VoteUtil::tagsConvert($tag),$with);
34
    }
35
36 1
    public function countInvalidVoteWithConstraints () : int
37
    {
38 1
        return $this->_Votes->countInvalidVoteWithConstraints();
39
    }
40
41 1
    public function countValidVoteWithConstraints () : int
42
    {
43 1
        return $this->countVotes() - $this->countInvalidVoteWithConstraints();
44
    }
45
46
    // Sum votes weight
47 2
    public function sumVotesWeight () : int
48
    {
49 2
        return $this->_Votes->sumVotesWeight(false);
50
    }
51
52 6
    public function sumValidVotesWeightWithConstraints () : int
53
    {
54 6
        return $this->_Votes->sumVotesWeight(true);
55
    }
56
57
    // Get the votes registered list
58 11
    public function getVotesList ($tag = null, bool $with = true) : array
59
    {
60 11
        return $this->_Votes->getVotesList(VoteUtil::tagsConvert($tag), $with);
61
    }
62
63 5
    public function getVotesListAsString () : string
64
    {
65 5
        return $this->_Votes->getVotesListAsString();
66
    }
67
68 102
    public function getVotesManager () : VotesManager {
69 102
        return $this->_Votes;
70
    }
71
72 2
    public function getVotesListGenerator ($tag = null, bool $with = true) : \Generator
73
    {
74 2
        return $this->_Votes->getVotesListGenerator(VoteUtil::tagsConvert($tag), $with);
75
    }
76
77 9
    public function getVoteKey (Vote $vote) {
78 9
        return $this->_Votes->getVoteKey($vote);
79
    }
80
81
82
/////////// ADD & REMOVE VOTE ///////////
83
84
    // 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.
85 126
    public function addVote ($vote, $tag = null) : Vote
86
    {
87 126
        $this->prepareVoteInput($vote, $tag);
88
89
        // Check Max Vote Count
90 126
        if ( self::$_maxVoteNumber !== null && $this->countVotes() >= self::$_maxVoteNumber ) :
91 1
            throw new CondorcetException(16, (string) self::$_maxVoteNumber);
92
        endif;
93
94
95
        // Register vote
96 126
        return $this->registerVote($vote, $tag); // Return the vote object
97
    }
98
99 7
    public function prepareUpdateVote (Vote $existVote) : void
100
    {
101 7
        $this->_Votes->UpdateAndResetComputing($this->getVoteKey($existVote),2);
102 7
    }
103
104 7
    public function finishUpdateVote (Vote $existVote) : void
105
    {
106 7
        $this->_Votes->UpdateAndResetComputing($this->getVoteKey($existVote),1);
107
108 7
        if ($this->_Votes->isUsingHandler()) :
109 1
            $this->_Votes[$this->getVoteKey($existVote)] = $existVote;
110
        endif;
111 7
    }
112
113 126
    public function checkVoteCandidate (Vote $vote) : bool
114
    {
115 126
        $linkCount = $vote->countLinks();
116 126
        $links = $vote->getLinks();
117
118 126
        $mirror = $vote->getRanking();
119 126
        $change = false;
120 126
        foreach ($vote as $rank => $choice) :
121 126
            foreach ($choice as $choiceKey => $candidate) :
122 126
                if ( !$this->isRegisteredCandidate($candidate, true) ) :
0 ignored issues
show
Bug introduced by
It seems like isRegisteredCandidate() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
123 109
                    if ($candidate->getProvisionalState() && $this->isRegisteredCandidate($candidate, false)) :
0 ignored issues
show
Bug introduced by
It seems like isRegisteredCandidate() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
124 108
                        if ( $linkCount === 0 || ($linkCount === 1 && reset($links) === $this) ) :
125 108
                            $mirror[$rank][$choiceKey] = $this->_Candidates[$this->getCandidateKey((string) $candidate)];
126 108
                            $change = true;
127
                        else :
128 126
                            return false;
129
                        endif;
130
                    endif;
131
                endif;
132
            endforeach;
133
        endforeach;
134
135 126
        if ($change) :
136 108
            $vote->setRanking(  $mirror,
137 108
                                ( abs($vote->getTimestamp() - microtime(true)) > 0.5 ) ? ($vote->getTimestamp() + 0.001) : null
138
            );
139
        endif;
140
141 126
        return true;
142
    }
143
144
    // Write a new vote
145 126
    protected function registerVote (Vote $vote, $tag = null) : Vote
146
    {
147
        // Vote identifiant
148 126
        $vote->addTags($tag);
149
        
150
        // Register
151
        try {
152 126
            $vote->registerLink($this);
153 126
            $this->_Votes[] = $vote;
154 1
        } catch (CondorcetException $e) {
155
            // Security : Check if vote object not already register
156 1
            throw new CondorcetException(31);
157
        }
158
159 126
        return $vote;
160
    }
161
162 5
    public function removeVote ($in, bool $with = true) : array
163
    {    
164 5
        $rem = [];
165
166 5
        if ($in instanceof Vote) :
167 4
            $key = $this->getVoteKey($in);
168 4
            if ($key !== false) :
169 4
                $deletedVote = $this->_Votes[$key];
170 4
                $rem[] = $deletedVote;
171
172 4
                unset($this->_Votes[$key]);
173
174 4
                 $deletedVote->destroyLink($this);
175
            endif;
176
        else :
177
            // Prepare Tags
178 3
            $tag = VoteUtil::tagsConvert($in);
179
180
            // Deleting
181 3
            foreach ($this->getVotesList($tag, $with) as $key => $value) :
182 3
                $deletedVote = $this->_Votes[$key];
183 3
                $rem[] = $deletedVote;
184
185 3
                unset($this->_Votes[$key]);
186
187 3
                $deletedVote->destroyLink($this);
188
            endforeach;
189
190
        endif;
191
192 5
        return $rem;
193
    }
194
195
196
/////////// PARSE VOTE ///////////
197
198
    // Return the well formated vote to use.
199 126
    protected function prepareVoteInput (&$vote, $tag = null) : void
200
    {
201 126
        if (!($vote instanceof Vote)) :
202 18
            $vote = new Vote ($vote, $tag);
203
        endif;
204
205
        // Check array format && Make checkVoteCandidate
206 126
        if ( !$this->checkVoteCandidate($vote) ) :
207
            throw new CondorcetException(5);
208
        endif;
209 126
    }
210
211 2
    public function jsonVotes (string $input) : array
212
    {
213 2
        $input = CondorcetUtil::prepareJson($input);
214
215
            //////
216
217 1
        $adding = [];
218
219 1
        foreach ($input as $record) :
220 1
            if (empty($record['vote'])) :
221 1
                continue;
222
            endif;
223
224 1
            $tags = !isset($record['tag']) ? null : $record['tag'];
225 1
            $multi = !isset($record['multi']) ? 1 : $record['multi'];
226
227 1
            $adding_predicted_count = count($adding) + $multi;
228
229 1 View Code Duplication
            if (self::$_maxVoteNumber && self::$_maxVoteNumber < ($this->countVotes() + $adding_predicted_count)) :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
                throw new CondorcetException(16, self::$_maxParseIteration);
231
            endif;
232
233 1 View Code Duplication
            if (self::$_maxParseIteration !== null && $adding_predicted_count >= self::$_maxParseIteration) :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
234
                throw new CondorcetException(12, self::$_maxParseIteration);
235
            endif;
236
237 1
            for ($i = 0; $i < $multi; $i++) :
238 1
                $adding[] = new Vote ($record['vote'], $tags);
239
            endfor;
240
        endforeach;
241
242 1
        foreach ($adding as $oneNewVote) :
243 1
            $this->addVote($oneNewVote);
244
        endforeach;
245
246 1
        return $adding;
247
    }
248
249 89
    public function parseVotes (string $input, bool $isFile = false) : array
250
    {
251 89
        $input = CondorcetUtil::prepareParse($input, $isFile);
252
253
        // Check each lines
254 89
        $adding = [];
255 89
        foreach ($input as $line) :
256
            // Empty Line
257 89
            if (empty($line)) :
258 78
                continue;
259
            endif;
260
261
            // Multiples
262 89
            $multiple = VoteUtil::parseAnalysingOneLine(mb_strpos($line, '*'),$line);
263
264
            // Vote Weight
265 89
            $weight = VoteUtil::parseAnalysingOneLine(mb_strpos($line, '^'),$line);
266
267
            // Tags + vote
268 89
            if (mb_strpos($line, '||') !== false) :
269 6
                $data = explode('||', $line);
270
271 6
                $vote = $data[1];
272 6
                $tags = $data[0];
273
            // Vote without tags
274
            else :
275 88
                $vote = $line;
276 88
                $tags = null;
277
            endif;
278
279 89
            $adding_predicted_count = count($adding) + $multiple;
280
281 89 View Code Duplication
            if (self::$_maxVoteNumber && self::$_maxVoteNumber < ($this->countVotes() + $adding_predicted_count)) :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282 1
                throw new CondorcetException(16, (string) self::$_maxParseIteration);
283
            endif;
284
285 89 View Code Duplication
            if (self::$_maxParseIteration !== null && $adding_predicted_count >= self::$_maxParseIteration) :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286 2
                throw new CondorcetException(12, (string) self::$_maxParseIteration);
287
            endif;
288
289
            // addVote
290 88
            for ($i = 0; $i < $multiple; $i++) :
291 88
                $newVote = new Vote ($vote, $tags);
292 88
                $newVote->setWeight($weight);
293
294 88
                $adding[] = $newVote;
295
            endfor;
296
        endforeach;
297
298 87
        foreach ($adding as $oneNewVote) :
299 87
            $this->addVote($oneNewVote);
300
        endforeach;
301
302 87
        return $adding;
303
    }
304
305
}
306