ApiController   A
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 38
eloc 79
dl 0
loc 209
rs 9.36
c 1
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A activeEventAction() 0 16 4
A semestersAction() 0 5 1
A activeAnswersAction() 0 13 3
A checkAllFieldsSet() 0 11 3
A canGiveFeedback() 0 25 5
C answerAction() 0 56 17
A finishAction() 0 25 5
1
<?php
2
3
/*
4
 * This file is part of the thealternativezurich/feedback project.
5
 *
6
 * (c) Florian Moser <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace App\Controller;
13
14
use App\Controller\Base\BaseApiController;
15
use App\Entity\Answer;
16
use App\Entity\Event;
17
use App\Entity\Participant;
18
use App\Entity\Semester;
19
use Symfony\Component\HttpFoundation\JsonResponse;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\Routing\Annotation\Route;
22
23
/**
24
 * @Route("/api")
25
 */
26
class ApiController extends BaseApiController
27
{
28
    /**
29
     * @Route("/active", name="api_active")
30
     *
31
     * @return JsonResponse
32
     */
33
    public function activeEventAction()
34
    {
35
        $now = new \DateTime();
36
        $today = $now->format('Y-m-d');
37
        $time = $now->format('H:i');
38
39
        $events = $this->getDoctrine()->getRepository(Event::class)->findBy(['date' => $today]);
40
        foreach ($events as $event) {
41
            if ($event->getFeedbackStartTime() < $time && $event->getFeedbackEndTime() > $time) {
42
                $event->loadTemplateIfSafe($this->getParameter('PUBLIC_DIR'));
43
44
                return $this->returnEvent($event);
45
            }
46
        }
47
48
        return $this->returnEvent(null);
49
    }
50
51
    /**
52
     * @Route("/{event}/{identifier}/answers", name="api_active_answers")
53
     *
54
     * @param $identifier
55
     *
56
     * @throws \Exception
57
     *
58
     * @return JsonResponse
59
     */
60
    public function activeAnswersAction(Event $event, $identifier)
61
    {
62
        if (!$this->canGiveFeedback($event)) {
63
            return $this->json([]);
64
        }
65
66
        //get participant & return answers
67
        $participant = $this->getDoctrine()->getRepository(Participant::class)->findOneBy(['event' => $event->getId(), 'identifier' => $identifier]);
68
        if (null !== $participant) {
69
            return $this->returnAnswers($participant->getAnswers());
70
        }
71
72
        return $this->json([]);
73
    }
74
75
    /**
76
     * @Route("/semesters", name="api_semesters")
77
     *
78
     * @return JsonResponse
79
     */
80
    public function semestersAction()
81
    {
82
        $semesters = $this->getDoctrine()->getRepository(Semester::class)->findBy([], ['name' => 'DESC']);
83
84
        return $this->returnSemester($semesters);
85
    }
86
87
    /**
88
     * @Route("/{event}/answer", name="api_event_answer")
89
     *
90
     * @throws \Exception
91
     *
92
     * @return JsonResponse
93
     */
94
    public function answerAction(Request $request, Event $event)
95
    {
96
        if (!$this->canGiveFeedback($event)) {
97
            return $this->json(false);
98
        }
99
100
        //get request fields
101
        if (!$this->checkAllFieldsSet($request, ['identifier', 'questionIndex', 'value', 'action'], $payload)) {
102
            return $this->json(false);
103
        }
104
105
        //write fields
106
        $identifier = $payload->identifier;
107
        $questionIndex = $payload->questionIndex;
108
        $value = $payload->value;
109
        $action = $payload->action;
110
        $private = property_exists($payload, 'private') && 'true' === $payload->private;
111
112
        //ensure all fields set & valid
113
        if (!isset($identifier) || !\is_int($questionIndex) || !isset($value) || !\in_array($action, ['override', 'ensure_value_exists', 'remove_value'], true)) {
114
            return $this->json(false);
115
        }
116
117
        //get participant or create a new one
118
        $participant = $this->getDoctrine()->getRepository(Participant::class)->findOneBy(['identifier' => $identifier, 'event' => $event->getId()]);
119
        if (null === $participant) {
120
            $participant = new Participant();
121
            $participant->setEvent($event);
122
            $participant->setIdentifier($identifier);
123
            $this->fastSave($participant);
124
        }
125
126
        //try to find existing answer
127
        $conditions = ['questionIndex' => $questionIndex, 'participant' => $participant->getId()];
128
        if ('ensure_value_exists' === $action || 'remove_value' === $action) {
129
            $conditions += ['value' => $value];
130
        }
131
        $answer = $this->getDoctrine()->getRepository(Answer::class)->findOneBy($conditions);
132
133
        //create new of not found && not want to remove
134
        if (null === $answer && 'remove_value' !== $action) {
135
            $answer = new Answer();
136
            $answer->setParticipant($participant);
137
            $answer->setPrivate($private);
138
            $answer->setQuestionIndex($questionIndex);
139
        }
140
        $answer->setValue($value);
141
142
        //process actions
143
        if ('override' === $action || 'ensure_value_exists' === $action) {
144
            $this->fastSave($answer);
145
        } elseif ('remove_value' === $action && null !== $answer) {
146
            $this->fastRemove($answer);
147
        }
148
149
        return $this->json(true);
150
    }
151
152
    /**
153
     * @Route("/{event}/finish", name="api_event_finish")
154
     *
155
     * @throws \Exception
156
     *
157
     * @return JsonResponse
158
     */
159
    public function finishAction(Request $request, Event $event)
160
    {
161
        if (!$this->canGiveFeedback($event)) {
162
            return $this->json(1);
163
        }
164
165
        if (!$this->checkAllFieldsSet($request, ['identifier', 'timeNeededInSeconds'], $payload)) {
166
            return $this->json(false);
167
        }
168
169
        //write fields
170
        $identifier = $payload->identifier;
171
        $timeNeededInSeconds = (int) $payload->timeNeededInSeconds;
172
173
        //get participant
174
        $participant = $this->getDoctrine()->getRepository(Participant::class)->findOneBy(['identifier' => $identifier, 'event' => $event->getId()]);
175
        if (null === $participant || null !== $participant->getTimeNeededInSeconds()) {
176
            return $this->json(false);
177
        }
178
179
        //set time info
180
        $participant->setTimeNeededInSeconds($timeNeededInSeconds);
181
        $this->fastSave($participant);
182
183
        return $this->json(true);
184
    }
185
186
    /**
187
     * @param string[] $requiredFields
188
     * @param string[] $payload
189
     *
190
     * @return bool
191
     */
192
    private function checkAllFieldsSet(Request $request, array $requiredFields, &$payload)
193
    {
194
        //get request fields
195
        $payload = json_decode($request->getContent());
196
        foreach ($requiredFields as $requiredField) {
197
            if (!property_exists($payload, $requiredField)) {
198
                return false;
199
            }
200
        }
201
202
        return true;
203
    }
204
205
    /**
206
     * @throws \Exception
207
     *
208
     * @return bool
209
     */
210
    private function canGiveFeedback(Event $event)
211
    {
212
        //prevent hand in too early
213
        if (!$event->feedbackHasStarted()) {
214
            return false;
215
        }
216
217
        //prevent hand in on other days
218
        $now = new \DateTime();
219
        $today = $now->format('Y-m-d');
220
        if ($today !== $event->getDate()) {
221
            return false;
222
        }
223
224
        //prevent hand in too late
225
        $currentTime = $now->format('H:i');
226
        $eventEndTime = \DateTime::createFromFormat('H:i:s', $event->getFeedbackEndTime());
227
        //allow additional 1 hour to hand in after the feedback has been closed
228
        $threshold = $eventEndTime->add(new \DateInterval('PT1H'))->format('H:i');
229
        //prevent if current time higher than threshold & threshold has not done a wrap around
230
        if ($currentTime > $threshold && $event->getFeedbackEndTime() < $threshold) {
231
            return false;
232
        }
233
234
        return true;
235
    }
236
}
237