Completed
Push — master ( 80fdf3...351fbc )
by Vladimir
02:43
created

MatchController::recalculateAction()   C

Complexity

Conditions 7
Paths 3

Size

Total Lines 50
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 25.2891

Importance

Changes 0
Metric Value
dl 0
loc 50
ccs 7
cts 25
cp 0.28
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 33
nc 3
nop 2
crap 25.2891
1
<?php
2
3
use Symfony\Component\Form\Form;
4
use Symfony\Component\Form\FormError;
5
use Symfony\Component\HttpFoundation\RedirectResponse;
6
use Symfony\Component\HttpFoundation\Request;
7
use Symfony\Component\HttpFoundation\StreamedResponse;
8
9
class MatchController extends CRUDController
10
{
11
    /**
12
     * Whether the last edited match has had its ELO changed, requiring an ELO
13
     * recalculation
14
     *
15
     * This is useful so that a confirmation form is shown, asking the user if
16
     * they want to recalculate ELOs
17
     *
18
     * @var bool
19
     */
20
    public $recalculateNeeded = false;
21
22
    public function listAction(Request $request, Player $me, Team $team = null, Player $player = null, $type = null)
23
    {
24
        /** @var MatchQueryBuilder $qb */
25
        $qb = self::getQueryBuilder();
26
        $currentPage = $this->getCurrentPage();
27
28
        if ($player) {
29
            $team = $player;
30
        }
31
32
        $query = $qb
33
            ->sortBy('time')->reverse()
34
            ->with($team, $type)
0 ignored issues
show
Bug introduced by
It seems like $team defined by parameter $team on line 22 can also be of type null; however, MatchQueryBuilder::with() does only seem to accept object<Team>|object<Player>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
35
            ->limit(50)->fromPage($currentPage)
36
        ;
37
38
        $matchType = $request->query->get('type', 'all');
39
40
        if (in_array($matchType, [Match::FUN, Match::OFFICIAL, Match::SPECIAL])) {
41
            $query->where('type')->is($matchType);
42
        }
43
44
        $matches = $query->getModels($fast = true);
45
46
        /** @var Match $match */
47
        foreach ($matches as $match) {
48
            // Don't show wrong labels for matches
49
            $match->getOriginalTimestamp()->setTimezone($me->getTimezone());
50
        }
51
52
        $matches = __::groupBy($matches, function ($match) {
53
            /** @var Match $match */
54
            $ts = $match->getOriginalTimestamp();
55
56 1
            if ($ts->year !== TimeDate::now()->year) {
57
                return $ts->format(TimeDate::DATE_FULL);
58 1
            }
59
60
            return $ts->format(TimeDate::DATE_MEDIUM);
61 1
        });
62
63
        return [
64 1
            'matchType'   => $type,
65 1
            'matches'     => $matches,
66 1
            'team'        => $team,
67 1
            'currentPage' => $currentPage,
68
            'totalPages'  => $qb->countPages(),
69
        ];
70
    }
71
72
    public function showAction(Match $match)
73
    {
74
        return array("match" => $match);
75 1
    }
76
77
    public function createAction(Player $me)
78
    {
79
        return $this->create($me, function (Match $match) use ($me) {
80 View Code Duplication
            if ($me->canEdit($match)
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...
81
                && $match->isOfficial()
82
                && (!$match->getTeamA()->isLastMatch($match)
83
                || !$match->getTeamB()->isLastMatch($match))
84
            ) {
85
                $url = Service::getGenerator()->generate('match_recalculate', array(
86
                    'match' => $match->getId(),
87
                ));
88
89
                return new RedirectResponse($url);
90
            }
91
        });
92
    }
93
94
    public function deleteAction(Player $me, Match $match)
95
    {
96
        return $this->delete($match, $me, function () use ($match, $me) {
97 View Code Duplication
            if ($match->getTeamA()->isLastMatch($match) && $match->getTeamB()->isLastMatch($match)) {
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...
98
                $match->resetTeamElos();
99
                $match->resetPlayerElos();
100
            } elseif ($me->canEdit($match)) {
101
                $url = Service::getGenerator()->generate('match_recalculate', array(
102
                    'match' => $match->getId(),
103
                ));
104
105
                return new RedirectResponse($url);
106
            }
107
        });
108
    }
109
110 View Code Duplication
    public function editAction(Player $me, Match $match)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
111
    {
112
        // TODO: Generating this response is unnecessary
113
        $response = $this->edit($match, $me, "match");
114
115
        if ($this->recalculateNeeded && $match->isOfficial()) {
116
            // Redirect to a confirmation form if we are assigning a new leader
117
            $url = Service::getGenerator()->generate('match_recalculate', array(
118
                'match' => $match->getId(),
119
            ));
120
121
            return new RedirectResponse($url);
122
        }
123
124
        return $response;
125
    }
126
127
    public function recalculateAction(Player $me, $match)
128
    {
129
        $match = Match::get($match); // get a match even if it's deleted
130
131
        if (!$me->canEdit($match)) {
132
            throw new ForbiddenException("You are not allowed to edit that match.");
133
        }
134
135
        if (!$match->isOfficial()) {
136
            throw new BadRequestException("You can't recalculate ELO history for a special match.");
137
        }
138
139
        return $this->showConfirmationForm(function () use ($match) {
140
            $response = new StreamedResponse();
141
            $response->headers->set('Content-Type', 'text/plain');
142
            $response->setCallback(function () use ($match) {
143
                $this->log(Match::recalculateMatchesSince($match, function ($event) {
144
                    switch ($event['type']) {
145
                        case 'recalculation.count':
146
                            echo $event['type'] . "\n";
147
                            break;
148
149
                        case 'recalculation.progress':
150
                            echo 'm';
151
                            break;
152
153
                        case 'recalculation.complete':
154
                            echo "\n\nCalculation successful\n";
155
                            break;
156
157
                        default:
158
                            break;
159
                    }
160
                }));
161
            });
162 1
            $response->send();
163
        }, "Do you want to recalculate ELO history for all teams and matches after the specified match?",
164 1
            "ELO history recalculated",
165
            "Recalculate ELOs",
166
            function () use ($match) {
167 1
                if ($match->isDeleted()) {
168 1
                    return new RedirectResponse($match->getURL('list'));
169 1
                }
170 1
171
                return new RedirectResponse($match->getURL('show'));
172
            },
173
            "Match/recalculate.html.twig",
174
            $noButton = true
175 1
        );
176
    }
177
178 1
    /**
179
     * Echo a string and flush the buffers
180
     *
181
     * Useful for streamed AJAX responses
182 1
     *
183 1
     * @param string $string The string to echo
184
     */
185 1
    private function log($string)
186
    {
187
        echo $string;
188
        ob_flush();
189 1
        flush();
190 1
    }
191 1
192
    /**
193
     * {@inheritdoc}
194 1
     */
195
    protected function getMessages($type, $name = '')
196 1
    {
197 1
        $messages = parent::getMessages($type, $name);
198 1
199 1
        // Don't show the match info on the successful create/edit message
200
        foreach ($messages as &$action) {
201 1
            foreach ($action as &$status) {
202 1
                if (isset($status['named'])) {
203
                    $status['named'] = $status['unnamed'];
204 1
                }
205
            }
206 1
        }
207 1
208 1
        return $messages;
209
    }
210 1
211 1
    /**
212 1
     * @param Form $form
213
     */
214
    protected function validate($form)
215
    {
216
        // Make sure that two different teams participated in a match, i.e. a team
217 1
        // didn't match against itself
218
        $firstTeam  = $form->get('first_team')->get('team')->getData();
219
        $secondTeam = $form->get('second_team')->get('team')->getData();
220
221
        if (!$firstTeam || !$secondTeam) {
222
            return;
223
        }
224
225
        if ($firstTeam->isSameAs($secondTeam)) {
226
            $message = "You can't report a match where a team played against itself!";
227
            $form->addError(new FormError($message));
228
        }
229
230
        $matchType = $form->get('type')->getData();
231
232
        foreach (array('first_team', 'second_team') as $team) {
233
            $input = $form->get($team);
234
            $teamInput = $input->get('team');
235
            $teamParticipants = $input->get('participants');
236
237
            if ($matchType === Match::FUN) {
238
                if (!$teamInput->getData() instanceof ColorTeam) {
239
                    $message = "Please enter a team color for fun and special matches.";
240
                    $teamInput->addError(new FormError($message));
241
                }
242
            } elseif ($matchType === Match::OFFICIAL) {
243
                if ($teamInput->getData() instanceof ColorTeam) {
244
                    $participants = $teamParticipants->getData();
245
246
                    if (empty($participants)) {
247
                        $message = 'A player roster is necessary for a color team for a mixed official match.';
248
                        $teamInput->addError(new FormError($message));
249
                    }
250
                }
251
            }
252
        }
253
    }
254
255
    /**
256
     * @param Form $form
257
     * @param Match $match
258
     */
259
    protected function validateEdit($form, $match)
260
    {
261
        if ($match->isOfficial() && $form->get('type')->getData() !== Match::OFFICIAL) {
262
            $message = "You cannot change this match's type.";
263
            $form->get('type')->addError(new FormError($message));
264
        } elseif (!$match->isOfficial() && $form->get('type')->getData() === Match::OFFICIAL) {
265
            $message = "You can't make this an official match.";
266
            $form->get('type')->addError(new FormError($message));
267
        }
268
    }
269
}
270