ExamController   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 101
c 0
b 0
f 0
dl 0
loc 172
ccs 0
cts 95
cp 0
rs 9.84
wmc 32

7 Methods

Rating   Name   Duplication   Size   Complexity  
A isVisibleForGrade() 0 18 4
A show() 0 31 2
A ics() 0 7 1
A export() 0 19 3
A getMessageScope() 0 2 1
A __construct() 0 3 1
F index() 0 73 20
1
<?php
2
3
namespace App\Controller;
4
5
use App\Dashboard\Absence\AbsenceResolver;
6
use App\Dashboard\Absence\ExamStudentsResolver;
7
use App\Dashboard\AbsentStudent;
8
use App\Date\WeekOfYear;
9
use App\Entity\Exam;
10
use App\Entity\IcsAccessToken;
11
use App\Entity\IcsAccessTokenType;
12
use App\Entity\MessageScope;
13
use App\Entity\Section;
14
use App\Entity\Student;
15
use App\Entity\User;
16
use App\Export\ExamIcsExporter;
17
use App\Form\IcsAccessTokenType as DeviceTokenTypeForm;
18
use App\Grouping\ExamWeekGroup;
19
use App\Grouping\ExamWeekStrategy;
20
use App\Grouping\Grouper;
21
use App\Message\DismissedMessagesHelper;
22
use App\Repository\ExamRepositoryInterface;
23
use App\Repository\ImportDateTypeRepositoryInterface;
24
use App\Repository\MessageRepositoryInterface;
25
use App\Security\IcsAccessToken\IcsAccessTokenManager;
26
use App\Security\Voter\ExamVoter;
27
use App\Security\Voter\StudentAbsenceVoter;
28
use App\Settings\ExamSettings;
29
use App\Sorting\ExamDateLessonStrategy as ExamDateSortingStrategy;
30
use App\Sorting\ExamWeekGroupStrategy;
31
use App\Sorting\Sorter;
32
use App\Sorting\StudentStrategy;
33
use App\Utils\ArrayUtils;
34
use App\Utils\EnumArrayUtils;
0 ignored issues
show
Bug introduced by
The type App\Utils\EnumArrayUtils was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
35
use App\View\Filter\GradeFilter;
36
use App\View\Filter\SectionFilter;
37
use App\View\Filter\StudentFilter;
38
use App\View\Filter\StudyGroupFilter;
39
use App\View\Filter\TeacherFilter;
40
use Closure;
41
use DateTime;
42
use SchulIT\CommonBundle\Helper\DateHelper;
43
use SchulIT\CommonBundle\Utils\RefererHelper;
44
use Symfony\Component\HttpFoundation\Request;
45
use Symfony\Component\HttpFoundation\Response;
46
use Symfony\Component\Routing\Annotation\Route;
47
48
49
#[Route(path: '/exams')]
50
class ExamController extends AbstractControllerWithMessages {
51
52
    private static int $ItemsPerPage = 25;
53
54
    public function __construct(MessageRepositoryInterface $messageRepository, DismissedMessagesHelper $dismissedMessagesHelper, private ImportDateTypeRepositoryInterface $importDateTypeRepository,
55
                                DateHelper $dateHelper, private Grouper $grouper, private Sorter $sorter, RefererHelper $refererHelper) {
56
        parent::__construct($messageRepository, $dismissedMessagesHelper, $dateHelper, $refererHelper);
57
    }
58
59
    #[Route(path: '', name: 'exams')]
60
    public function index(SectionFilter $sectionFilter, TeacherFilter $teacherFilter, StudentFilter $studentsFilter, GradeFilter $gradeFilter, StudyGroupFilter $studyGroupFilter,
61
                          ExamRepositoryInterface $examRepository, ExamSettings $examSettings, Request $request, DateHelper $dateHelper): Response {
0 ignored issues
show
Unused Code introduced by
The parameter $dateHelper is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

61
                          ExamRepositoryInterface $examRepository, ExamSettings $examSettings, Request $request, /** @scrutinizer ignore-unused */ DateHelper $dateHelper): Response {

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

Loading history...
62
        /** @var User $user */
63
        $user = $this->getUser();
64
65
        $sectionFilterView = $sectionFilter->handle($request->query->get('section'));
66
        $studyGroupFilterView = $studyGroupFilter->handle($request->query->get('study_group', null), $sectionFilterView->getCurrentSection(), $user);
67
        $gradeFilterView = $gradeFilter->handle($request->query->get('grade', null), $sectionFilterView->getCurrentSection(), $user);
68
        $studentFilterView = $studentsFilter->handle($request->query->get('student', null), $sectionFilterView->getCurrentSection(), $user, $studyGroupFilterView->getCurrentStudyGroup() === null && $gradeFilterView->getCurrentGrade() === null);
69
        $teacherFilterView = $teacherFilter->handle($request->query->get('teacher', null), $sectionFilterView->getCurrentSection(), $user, $request->query->get('teacher') !== '✗' && $studyGroupFilterView->getCurrentStudyGroup() === null && $gradeFilterView->getCurrentGrade() === null && $studentFilterView->getCurrentStudent() === null);
70
        $includePastExams = $request->query->getBoolean('past', false);
71
72
        $isVisible = $examSettings->isVisibileFor($user->getUserType()) && $this->isVisibleForGrade($user, $examSettings, $sectionFilterView->getCurrentSection());
0 ignored issues
show
Bug introduced by
It seems like $sectionFilterView->getCurrentSection() can also be of type null; however, parameter $section of App\Controller\ExamController::isVisibleForGrade() does only seem to accept App\Entity\Section, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

72
        $isVisible = $examSettings->isVisibileFor($user->getUserType()) && $this->isVisibleForGrade($user, $examSettings, /** @scrutinizer ignore-type */ $sectionFilterView->getCurrentSection());
Loading history...
73
        $isVisibleAdmin = false;
74
75
        $groups = [ ];
0 ignored issues
show
Unused Code introduced by
The assignment to $groups is dead and can be removed.
Loading history...
76
        $exams = [ ];
77
78
        $page = $request->query->getInt('page', 1);
79
        $pages = 1;
80
81
        if($isVisible === true || $this->isGranted('ROLE_EXAMS_CREATOR') || $this->isGranted('ROLE_EXAMS_ADMIN')) {
82
            if($isVisible === false) {
83
                $isVisibleAdmin = $this->isGranted('ROLE_EXAMS_CREATOR') || $this->isGranted('ROLE_EXAMS_ADMIN');
84
            }
85
86
            $threshold = $includePastExams ? null : $this->dateHelper->getToday();
87
            $paginator = null;
88
89
            if ($studentFilterView->getCurrentStudent() !== null) {
90
                $paginator = $examRepository->getPaginator(self::$ItemsPerPage, $page, null, null, $studentFilterView->getCurrentStudent(), null, true, $threshold);
91
            } else if ($studyGroupFilterView->getCurrentStudyGroup() !== null) {
92
                $paginator = $examRepository->getPaginator(self::$ItemsPerPage, $page, null, null, null, $studyGroupFilterView->getCurrentStudyGroup(), true, $threshold);
93
            } else if ($gradeFilterView->getCurrentGrade() !== null) {
94
                $paginator = $examRepository->getPaginator(self::$ItemsPerPage, $page, $gradeFilterView->getCurrentGrade(), null, null, null, true, $threshold);
95
            } else if ($teacherFilterView->getCurrentTeacher() !== null) {
96
                $paginator = $examRepository->getPaginator(self::$ItemsPerPage, $page, null, $teacherFilterView->getCurrentTeacher(), null, null, true, $threshold);
97
            } else {
98
                $paginator = $examRepository->getPaginator(self::$ItemsPerPage, $page, null, null, null, null, true, $threshold);
99
            }
100
101
            if($paginator !== null) {
102
                /** @var Exam $exam */
103
                foreach ($paginator->getIterator() as $exam) {
104
                    if ($this->isGranted(ExamVoter::Show, $exam)) {
105
                        $exams[] = $exam;
106
                    }
107
                }
108
109
                if($paginator->count() > 0) {
110
                    $pages = ceil((float)$paginator->count() / self::$ItemsPerPage);
111
                }
112
            }
113
        }
114
115
        $groups = $this->grouper->group($exams, ExamWeekStrategy::class);
116
        $this->sorter->sort($groups, ExamWeekGroupStrategy::class);
117
        $this->sorter->sortGroupItems($exams, ExamDateSortingStrategy::class);
118
119
        return $this->renderWithMessages('exams/index.html.twig', [
120
            'examWeekGroups' => $groups,
121
            'sectionFilter' => $sectionFilterView,
122
            'studentFilter' => $studentFilterView,
123
            'teacherFilter' => $teacherFilterView,
124
            'gradeFilter' => $gradeFilterView,
125
            'studyGroupFilter' => $studyGroupFilterView,
126
            'isVisible' => $isVisible,
127
            'isVisibleAdmin' => $isVisibleAdmin,
128
            'includePastExams' => $includePastExams,
129
            'pages' => $pages,
130
            'page' => $page,
131
            'last_import' => $this->importDateTypeRepository->findOneByEntityClass(Exam::class)
132
        ]);
133
    }
134
135
    private function isVisibleForGrade(User $user, ExamSettings $examSettings, Section $section) {
136
        if($user->isStudentOrParent() === false) {
137
            return true;
138
        }
139
140
        $visibleGradeIds = $examSettings->getVisibleGradeIds();
141
        $gradeIds = [ ];
142
143
        /** @var Student $student */
144
        foreach($user->getStudents() as $student) {
145
            $grade = $student->getGrade($section);
146
147
            if($grade !== null) {
148
                $gradeIds[] = $grade->getId();
149
            }
150
        }
151
152
        return count(array_intersect($visibleGradeIds, $gradeIds)) > 0;
153
    }
154
155
    #[Route(path: '/export', name: 'exams_export')]
156
    public function export(Request $request, IcsAccessTokenManager $manager): Response {
157
        /** @var User $user */
158
        $user = $this->getUser();
159
160
        $deviceToken = (new IcsAccessToken())
161
            ->setType(IcsAccessTokenType::Exams)
162
            ->setUser($user);
163
164
        $form = $this->createForm(DeviceTokenTypeForm::class, $deviceToken);
165
        $form->handleRequest($request);
166
167
        if($form->isSubmitted() && $form->isValid()) {
168
            $deviceToken = $manager->persistToken($deviceToken);
169
        }
170
171
        return $this->renderWithMessages('exams/export.html.twig', [
172
            'form' => $form->createView(),
173
            'token' => $deviceToken
174
        ]);
175
    }
176
177
    #[Route(path: '/ics/download', name: 'exams_ics')]
178
    #[Route(path: '/ics/download/{token}', name: 'exams_ics_token')]
179
    public function ics(ExamIcsExporter $exporter): Response {
180
        /** @var User $user */
181
        $user = $this->getUser();
182
183
        return $exporter->getIcsResponse($user);
184
    }
185
186
    protected function getMessageScope(): MessageScope {
187
        return MessageScope::Exams;
188
    }
189
190
    #[Route(path: '/{uuid}', name: 'show_exam', requirements: ['id' => '\d+'])]
191
    public function show(Exam $exam, AbsenceResolver $absenceResolver): Response {
192
        $this->denyAccessUnlessGranted(ExamVoter::Show, $exam);
193
194
        $studyGroups = [ ];
195
        /** @var Student[] $students */
196
        $students = $exam->getStudents()->toArray();
0 ignored issues
show
Bug introduced by
The method getStudents() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

196
        $students = $exam->/** @scrutinizer ignore-call */ getStudents()->toArray();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
197
198
        $this->sorter->sort($students, StudentStrategy::class);
199
200
        $absentStudents = [ ];
201
        if($this->isGranted(StudentAbsenceVoter::CanViewAny)) {
202
            $absentStudents = ArrayUtils::createArrayWithKeys(
203
                $absenceResolver->resolve(
204
                    $exam->getDate(),
205
                    $exam->getLessonStart(),
206
                    $exam->getStudents()->toArray(),
207
                    [
208
                        ExamStudentsResolver::class
209
                    ]
210
                ),
211
                fn(AbsentStudent $student) => $student->getStudent()->getUuid()->toString()
212
            );
213
        }
214
215
        return $this->renderWithMessages('exams/details.html.twig', [
216
            'exam' => $exam,
217
            'students' => $students,
218
            'studyGroups' => $studyGroups,
219
            'absentStudents' => $absentStudents,
220
            'last_import' => $this->importDateTypeRepository->findOneByEntityClass(Exam::class)
221
        ]);
222
    }
223
}