ExamController::index()   F
last analyzed

Complexity

Conditions 20
Paths 182

Size

Total Lines 73
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 420

Importance

Changes 0
Metric Value
eloc 51
c 0
b 0
f 0
dl 0
loc 73
rs 3.4833
ccs 0
cts 47
cp 0
cc 20
nc 182
nop 9
crap 420

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
}