Test Failed
Push — master ( 2dcdef...5f4570 )
by Julien
03:01
created

TreeGen   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 281
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 7
dl 0
loc 281
rs 8.8
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
C run() 0 87 9
A getMaxCompetitorByEntity() 0 12 3
B getFightersByEntity() 0 30 2
B getByeGroup() 0 23 4
A getTreeSize() 0 17 3
A createNullsGroup() 0 12 3
A repart() 0 13 4
C insertByes() 0 27 7
1
<?php
2
3
4
namespace Xoco70\KendoTournaments\TreeGen;
5
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\Facades\Config;
8
use Xoco70\KendoTournaments\Contracts\TreeGenerable;
9
use Xoco70\KendoTournaments\Exceptions\TreeGenerationException;
10
use Xoco70\KendoTournaments\Models\Championship;
11
use Xoco70\KendoTournaments\Models\Competitor;
12
use Xoco70\KendoTournaments\Models\Team;
13
use Xoco70\KendoTournaments\Models\Round;
14
15
class TreeGen implements TreeGenerable
16
{
17
18
    protected $groupBy;
19
    public $championship, $settings;
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
20
21
22
    public function __construct(Championship $championship, $groupBy, $settings)
23
    {
24
        $this->championship = $championship;
25
        $this->groupBy = $groupBy;
26
        $this->settings = $settings;
27
    }
28
29
    /**
30
     * Generate tree groups for a championship
31
     * @return Collection
32
     * @throws TreeGenerationException
33
     */
34
    public function run()
35
    {
36
        // If previous trees already exist, delete all
37
        $this->championship->tree()->delete();
38
        $tree = new Collection();
39
40
        // Get Areas
41
        $areas = $this->settings->fightingAreas;
42
43
44
        $this->championship->category->isTeam()
45
            ? $fighters = $this->championship->teams
46
            : $fighters = $this->championship->competitors;
47
48
49
        if ($fighters->count() / $areas < config('kendo-tournaments.MIN_COMPETITORS_X_AREA')) {
50
            throw new TreeGenerationException(trans('msg.min_competitor_required', ['number' => Config::get('kendo-tournaments.MIN_COMPETITORS_X_AREA')]));
51
52
        }
53
        // Get Competitor's / Team list ordered by entities ( Federation, Assoc, Club, etc...)
54
        $fightersByEntity = $this->getFightersByEntity($fighters);
55
56
        // Chunk user by areas
57
58
        $usersByArea = $fightersByEntity->chunk(sizeof($fightersByEntity) / $areas);
59
60
        $area = 1;
61
62
        // loop on areas
63
        foreach ($usersByArea as $fightersByEntity) {
64
65
            // Chunking to make small round robin groups
66
            if ($this->championship->hasPreliminary()) {
67
                $fightersGroup = $fightersByEntity->chunk($this->settings->preliminaryGroupSize)->shuffle();
68
69
            } else if ($this->championship->isDirectEliminationType()) {
70
                $fightersGroup = $fightersByEntity->chunk(2)->shuffle();
71
            } else {
72
                $fightersGroup = $fightersByEntity;
73
74
//                // Not so good, Round Robin has no trees
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
75
//                $round = new Round;
76
//                $round->area = $area;
77
//                $round->order = 1;
78
//                $round->championship_id = $this->championship->id;
79
//                if ($this->championship->category->isTeam()) {
80
//                    $round->isTeam = 1;
81
//                }
82
//                $round->save();
83
//                $tree->push($round);
84
            }
85
86
            $order = 1;
87
88
            // Before doing anything, check last group if numUser = 1
89
            foreach ($fightersGroup as $fighters) {
90
91
                $fighters = $fighters->pluck('id')->shuffle();
92
93
                $round = new Round;
94
                $round->area = $area;
95
                $round->order = $order;
96
//                if ($this->championship->category->isTeam()) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
97
                $round->championship_id = $this->championship->id;
98
99
                $round->save();
100
                $tree->push($round);
101
                $order++;
102
103
                // Add all competitors to Pivot Table
104
                if ($this->championship->category->isTeam()) {
105
                    $round->teams()->sync($fighters);
106
                } else {
107
                    $round->competitors()->detach();
108
                    foreach ($fighters as $fighter){
109
                        $round->competitors()->attach($fighter);
110
                    }
111
//                    $round->competitors()->sync($fighters);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
112
                }
113
114
115
            }
116
            $area++;
117
        }
118
119
        return $tree;
120
    }
121
122
    /**
123
     * @param $userGroups
124
     * @return int
125
     */
126
    private function getMaxCompetitorByEntity($userGroups): int
127
    {
128
        // Surely there is a Laravel function that does it ;)
129
        $max = 0;
130
        foreach ($userGroups as $userGroup) {
131
            if (sizeof($userGroup) > $max) {
132
                $max = sizeof($userGroup);
133
            }
134
135
        }
136
        return $max;
137
    }
138
139
    /**
140
     * Get Competitor's list ordered by entities
141
     * Countries for Internation Tournament, State for a National Tournament, etc
142
     * @return Collection
143
     */
144
    private function getFightersByEntity($fighters): Collection
145
    {
146
        // Right now, we are treating users and teams as equals.
147
        // It doesn't matter right now, because we only need name attribute which is common to both models
148
149
        // $this->groupBy contains federation_id, association_id, club_id, etc.
150
        if (($this->groupBy) != null) {
151
            $fighterGroups = $fighters->groupBy($this->groupBy); // Collection of Collection
152
        } else {
153
            $fighterGroups = $fighters->chunk(1); // Collection of Collection
154
        }
155
156
157
        $tmpFighterGroups = clone $fighterGroups;
158
159
        $byeGroup = $this->getByeGroup($this->championship, $fighters);
160
161
162
        // Get biggest competitor's group
163
        $max = $this->getMaxCompetitorByEntity($tmpFighterGroups);
164
165
        // We reacommodate them so that we can mix them up and they don't fight with another competitor of his entity.
166
167
        $competitors = $this->repart($fighterGroups, $max);
168
169
        $competitors = $this->insertByes($competitors, $byeGroup);
170
171
        return $competitors;
172
173
    }
174
175
    /**
176
     * Calculate the Byes need to fill the Championship Tree
177
     * @param Championship $championship
178
     * @return Collection
179
     */
180
    private function getByeGroup(Championship $championship, $fighters)
181
    {
182
        $groupSizeDefault = 3;
183
        $preliminaryGroupSize = 2;
184
185
        $fighterCount = $fighters->count();
186
187
        if ($championship->hasPreliminary()) {
188
            $preliminaryGroupSize = $championship->settings != null
189
                ? $championship->settings->preliminaryGroupSize
190
                : $groupSizeDefault;
191
        } else if ($championship->isDirectEliminationType()) {
192
            $preliminaryGroupSize = 2;
193
194
        } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
195
            // No Preliminary and No Direct Elimination --> Round Robin
196
            // Should Have no tree
197
        }
198
        $treeSize = $this->getTreeSize($fighterCount, $preliminaryGroupSize);
199
200
        $byeCount = $treeSize - $fighterCount;
201
        return $this->createNullsGroup($byeCount, $championship->category->isTeam);
202
    }
203
204
    /**
205
     * @param $fighterCount
206
     * @return integer
207
     */
208
    private function getTreeSize($fighterCount, $groupSize)
209
    {
210
        $square = collect([1, 2, 4, 8, 16, 32, 64]);
211
        $squareMultiplied = $square->map(function ($item, $key) use ($groupSize) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
212
            return $item * $groupSize;
213
        });
214
215
216
        foreach ($squareMultiplied as $limit) {
217
            if ($fighterCount <= $limit) {
218
219
                return $limit;
220
            }
221
        }
222
        return 64 * $groupSize;
223
224
    }
225
226
    /**
227
     * @param $byeCount
228
     * @return Collection
229
     */
230
    private function createNullsGroup($byeCount, $isTeam): Collection
231
    {
232
        $isTeam
233
            ? $null = new Team()
234
            : $null = new Competitor();
235
236
        $byeGroup = new Collection();
237
        for ($i = 0; $i < $byeCount; $i++) {
238
            $byeGroup->push($null);
239
        }
240
        return $byeGroup;
241
    }
242
243
    /**
244
     * @param $fighterGroups
245
     * @param $max
246
     * @return Collection
247
     */
248
    private function repart($fighterGroups, $max)
249
    {
250
        $fighters = new Collection;
251
        for ($i = 0; $i < $max; $i++) {
252
            foreach ($fighterGroups as $fighterGroup) {
253
                $fighter = $fighterGroup->values()->get($i);
254
                if ($fighter != null) {
255
                    $fighters->push($fighter);
256
                }
257
            }
258
        }
259
        return $fighters;
260
    }
261
262
    /**
263
     * Insert byes in an homogen way
264
     * @param Collection $fighters
265
     * @param Collection $byeGroup
266
     * @return Collection
267
     */
268
    private function insertByes(Collection $fighters, Collection $byeGroup)
269
    {
270
        $bye = sizeof($byeGroup) > 0 ? $byeGroup[0] : [];
271
        $sizeFighters = sizeof($fighters);
272
        $sizeGroupBy = sizeof($byeGroup);
273
274
        $frequency = $sizeGroupBy != 0
275
            ? (int)floor($sizeFighters / $sizeGroupBy)
276
            : -1;
277
278
        // Create Copy of $competitors
279
        $newFighters = new Collection;
280
        $i = 0;
281
        $byeCount = 0;
282
        foreach ($fighters as $fighter) {
283
284
            if ($frequency != -1 && $i % $frequency == 0 && $byeCount < $sizeGroupBy) {
285
                $newFighters->push($bye);
286
                $byeCount++;
287
            }
288
            $newFighters->push($fighter);
289
            $i++;
290
291
        }
292
293
        return $newFighters;
294
    }
295
}