EntryOverviewHelper::computeOverview()   F
last analyzed

Complexity

Conditions 22
Paths 2040

Size

Total Lines 109
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 22
eloc 59
nc 2040
nop 6
dl 0
loc 109
rs 0
c 1
b 0
f 0

How to fix   Long Method    Complexity   

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:

1
<?php
2
3
namespace App\Book;
4
5
use App\Entity\BookComment;
6
use App\Entity\Grade;
7
use App\Entity\LessonEntry;
8
use App\Entity\Substitution;
9
use App\Entity\Teacher;
10
use App\Entity\TimetableLesson;
11
use App\Entity\Tuition;
12
use App\Grouping\Grouper;
13
use App\Grouping\LessonDayStrategy;
14
use App\Repository\AppointmentCategoryRepositoryInterface;
15
use App\Repository\AppointmentRepositoryInterface;
16
use App\Repository\BookCommentRepositoryInterface;
17
use App\Repository\FreeTimespanRepositoryInterface;
18
use App\Repository\LessonAttendanceRepositoryInterface;
19
use App\Repository\LessonEntryRepositoryInterface;
20
use App\Repository\SubstitutionRepositoryInterface;
21
use App\Repository\TimetableLessonRepositoryInterface;
22
use App\Repository\TuitionRepositoryInterface;
23
use App\Section\SectionResolverInterface;
24
use App\Settings\TimetableSettings;
25
use App\Sorting\LessonDayGroupStrategy;
26
use App\Sorting\LessonStrategy;
27
use App\Sorting\Sorter;
28
use App\Utils\ArrayUtils;
29
use DateTime;
30
31
class EntryOverviewHelper {
32
    public function __construct(private TimetableLessonRepositoryInterface $lessonRepository, private TuitionRepositoryInterface $tuitionRepository, private LessonEntryRepositoryInterface $entryRepository, private BookCommentRepositoryInterface $commentRepository, private LessonAttendanceRepositoryInterface $attendanceRepository, private SectionResolverInterface $sectionResolver, private TimetableSettings $timetableSettings, private AppointmentCategoryRepositoryInterface $appointmentCategoryRepository, private AppointmentRepositoryInterface $appointmentRepository, private FreeTimespanRepositoryInterface $freeTimespanRepository, private SubstitutionRepositoryInterface $substitutionRepository, private Grouper $grouper, private Sorter $sorter)
33
    {
34
    }
35
36
    public function computeOverviewForTuition(Tuition $tuition, DateTime $start, DateTime $end): EntryOverview {
37
        $entries = $this->entryRepository->findAllByTuition($tuition, $start, $end);
38
        $comments = $this->commentRepository->findAllByDateAndTuition($tuition, $start, $end);
39
40
        return $this->computeOverview([$tuition], $entries, $comments, [ ], $start, $end);
41
    }
42
43
    /**
44
     * @param Tuition[] $tuitions
45
     * @param LessonEntry[] $entries
46
     * @param BookComment[] $comments
47
     * @param Substitution[] $substitutions
48
     */
49
    private function computeOverview(array $tuitions, array $entries, array $comments, array $substitutions, DateTime $start, DateTime $end): EntryOverview {
50
        if($start > $end) {
51
            $tmp = $start;
52
            $start = $end;
53
            $end = $tmp;
54
        }
55
56
        $tuitions = array_filter($tuitions, fn(Tuition $tuition) => $tuition->isBookEnabled());
57
58
        $lessons = [ ];
59
60
        foreach($entries as $entry) {
61
            if($entry->getTuition() === null) {
62
                // discard entries without tuition
63
                continue;
64
            }
65
66
            $presentCount = $this->attendanceRepository->countPresent($entry);
67
            $absentCount = $this->attendanceRepository->countAbsent($entry);
68
            $lateCount = $this->attendanceRepository->countLate($entry);
69
70
            for($lessonNumber = $entry->getLessonStart(); $lessonNumber <= $entry->getLessonEnd(); $lessonNumber++) {
71
                $key = sprintf('%s-%d-%s', $entry->getLesson()->getDate()->format('Y-m-d'), $lessonNumber, $entry->getTuition()->getUuid()->toString());
72
73
                $lesson = new Lesson(clone $entry->getLesson()->getDate(), $lessonNumber, null, $entry);
74
                $lesson->setAbsentCount($absentCount);
75
                $lesson->setPresentCount($presentCount);
76
                $lesson->setLateCount($lateCount);
77
78
                $lessons[$key] = $lesson;
79
            }
80
        }
81
82
        $weekNumbers = [ ];
83
        $currentWeek = clone $start;
84
        while($currentWeek < $end) {
85
            $weekNumber = (int)$currentWeek->format('W');
86
87
            if(!in_array($weekNumber, $weekNumbers)) {
88
                $weekNumbers[] = $weekNumber;
89
            }
90
91
            $currentWeek = $currentWeek->modify('+7 days');
92
        }
93
94
        $timetableLessons = $this->lessonRepository->findAllByTuitions($start, $end, $tuitions);
95
96
        $current = clone $start;
97
        while($current <= $end) {
98
            $dailyLessons = array_filter($timetableLessons, fn(TimetableLesson $lesson) => $lesson->getDate() == $current);
99
100
            foreach($dailyLessons as $dailyLesson) {
101
                for($lessonNumber = $dailyLesson->getLessonStart(); $lessonNumber <= $dailyLesson->getLessonEnd(); $lessonNumber++) {
102
                    $key = sprintf('%s-%d-%s', $current->format('Y-m-d'), $lessonNumber, $dailyLesson->getTuition()->getUuid()->toString());
103
104
                    if (!array_key_exists($key, $lessons)) {
105
                        $lessons[$key] = new Lesson(clone $current, $lessonNumber);
106
                    }
107
108
                    $lessons[$key]->setLesson($dailyLesson);
109
                }
110
            }
111
112
            $current = $current->modify('+1 day');
113
        }
114
115
        foreach($substitutions as $substitution) {
116
            $tuition = $this->tuitionRepository->findOneBySubstitution($substitution, $this->sectionResolver->getSectionForDate($substitution->getDate()));
0 ignored issues
show
Bug introduced by
It seems like $substitution->getDate() can also be of type null; however, parameter $dateTime of App\Section\SectionResol...ce::getSectionForDate() does only seem to accept DateTime, 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

116
            $tuition = $this->tuitionRepository->findOneBySubstitution($substitution, $this->sectionResolver->getSectionForDate(/** @scrutinizer ignore-type */ $substitution->getDate()));
Loading history...
Bug introduced by
It seems like $this->sectionResolver->...ubstitution->getDate()) can also be of type null; however, parameter $section of App\Repository\TuitionRe...findOneBySubstitution() 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

116
            $tuition = $this->tuitionRepository->findOneBySubstitution($substitution, /** @scrutinizer ignore-type */ $this->sectionResolver->getSectionForDate($substitution->getDate()));
Loading history...
117
118
            if($tuition === null) {
119
                continue;
120
            }
121
122
            $timetableLessons = $this->lessonRepository->findAllByTuitions($substitution->getDate(), $substitution->getDate(), [$tuition]);
0 ignored issues
show
Bug introduced by
It seems like $substitution->getDate() can also be of type null; however, parameter $end of App\Repository\Timetable...ce::findAllByTuitions() does only seem to accept DateTime, 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

122
            $timetableLessons = $this->lessonRepository->findAllByTuitions($substitution->getDate(), /** @scrutinizer ignore-type */ $substitution->getDate(), [$tuition]);
Loading history...
Bug introduced by
It seems like $substitution->getDate() can also be of type null; however, parameter $start of App\Repository\Timetable...ce::findAllByTuitions() does only seem to accept DateTime, 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

122
            $timetableLessons = $this->lessonRepository->findAllByTuitions(/** @scrutinizer ignore-type */ $substitution->getDate(), $substitution->getDate(), [$tuition]);
Loading history...
123
124
            // Filter correct lesson
125
            foreach($timetableLessons as $dailyLesson) {
126
                for($lessonNumber = $dailyLesson->getLessonStart(); $lessonNumber <= $dailyLesson->getLessonEnd(); $lessonNumber++) {
127
                    if($substitution->getLessonStart() <= $lessonNumber && $lessonNumber <= $substitution->getLessonEnd()) {
128
                        $key = sprintf('%s-%d-%s', $substitution->getDate()->format('Y-m-d'), $lessonNumber, $dailyLesson->getTuition()->getUuid()->toString());
129
130
                        if (!array_key_exists($key, $lessons)) {
131
                            $lessons[$key] = new Lesson($substitution->getDate(), $lessonNumber, $dailyLesson, null, $substitution);
0 ignored issues
show
Bug introduced by
It seems like $substitution->getDate() can also be of type null; however, parameter $date of App\Book\Lesson::__construct() does only seem to accept DateTime, 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

131
                            $lessons[$key] = new Lesson(/** @scrutinizer ignore-type */ $substitution->getDate(), $lessonNumber, $dailyLesson, null, $substitution);
Loading history...
132
                        }
133
134
                        // Set correct entry
135
                        /** @var LessonEntry $lessonEntry */
136
                        foreach($dailyLesson->getEntries() as $lessonEntry) {
137
                            if($lessonEntry->getLessonStart() <= $lessonNumber && $lessonNumber <= $lessonEntry->getLessonEnd()) {
138
                                $lessons[$key]->setEntry($lessonEntry);
139
                                break;
140
                            }
141
                        }
142
143
                        if($lessons[$key]->getLesson() === null) {
144
                            $lessons[$key]->setLesson($dailyLesson);
145
                        }
146
                    }
147
                }
148
            }
149
        }
150
151
        $groups = $this->grouper->group(array_values($lessons), LessonDayStrategy::class);
152
        $this->sorter->sort($groups, LessonDayGroupStrategy::class);
153
        $this->sorter->sortGroupItems($groups, LessonStrategy::class);
154
155
        $freeTimespans = $this->computeFreeTimespans($start, $end);
156
157
        return new EntryOverview($start, $end, $groups, $comments, $freeTimespans);
158
    }
159
160
    public function computeOverviewForTeacher(Teacher $teacher, DateTime $start, DateTime $end): EntryOverview {
161
        $section = $this->sectionResolver->getSectionForDate($start);
162
        $entries = [ ];
163
        $tuitions = [ ];
164
        $comments = [ ];
165
166
        if($section !== null) {
167
            $entries = $this->entryRepository->findAllBySubstituteTeacher($teacher, $start, $end);
168
            $tuitions = $this->tuitionRepository->findAllByTeacher($teacher, $section);
169
170
            foreach($tuitions as $tuition) {
171
                $entries = array_merge($entries, $this->entryRepository->findAllByTuition($tuition, $start, $end));
172
                $comments = array_merge($comments, $this->commentRepository->findAllByDateAndTuition($tuition, $start, $end));
173
            }
174
175
            $comments = ArrayUtils::unique($comments);
176
        }
177
178
        $substitutions = [ ];
179
180
        $current = clone $start;
181
        while($current <= $end) {
182
            $substitutions = array_merge(
183
                $substitutions,
184
                $this->substitutionRepository->findAllForTeacher($teacher, $current)
185
            );
186
            $current = $current->modify('+1 day');
187
        }
188
189
        $substitutions = array_filter($substitutions, fn(Substitution $substitution) => $substitution->getReplacementTeachers()->contains($teacher));
190
191
        return $this->computeOverview($tuitions, $entries, $comments, $substitutions, $start, $end);
192
    }
193
194
    public function computeOverviewForGrade(Grade $grade, DateTime $start, DateTime $end): EntryOverview {
195
        $section = $this->sectionResolver->getSectionForDate($start);
196
197
        if($section === null) {
198
            return new EntryOverview($start, $end, [ ], [ ], [ ]);
199
        }
200
201
        if($end > $section->getEnd()) {
202
            $end = $section->getEnd();
203
        }
204
205
        $entries = $this->entryRepository->findAllByGrade($grade, $start, $end);
0 ignored issues
show
Bug introduced by
It seems like $end can also be of type null; however, parameter $end of App\Repository\LessonEnt...rface::findAllByGrade() does only seem to accept DateTime, 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

205
        $entries = $this->entryRepository->findAllByGrade($grade, $start, /** @scrutinizer ignore-type */ $end);
Loading history...
206
        $tuitions = $this->tuitionRepository->findAllByGrades([$grade], $section);
207
208
        $comments = [ ];
209
        $section = $this->sectionResolver->getSectionForDate($start);
210
211
        if($section !== null) {
212
            $comments = $this->commentRepository->findAllByDateAndGrade($grade, $section, $start, $end);
0 ignored issues
show
Bug introduced by
It seems like $end can also be of type null; however, parameter $end of App\Repository\BookComme...findAllByDateAndGrade() does only seem to accept DateTime, 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

212
            $comments = $this->commentRepository->findAllByDateAndGrade($grade, $section, $start, /** @scrutinizer ignore-type */ $end);
Loading history...
213
        }
214
215
        return $this->computeOverview($tuitions, $entries, $comments, [ ], $start, $end);
0 ignored issues
show
Bug introduced by
It seems like $end can also be of type null; however, parameter $end of App\Book\EntryOverviewHelper::computeOverview() does only seem to accept DateTime, 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

215
        return $this->computeOverview($tuitions, $entries, $comments, [ ], $start, /** @scrutinizer ignore-type */ $end);
Loading history...
216
    }
217
218
    /**
219
     * @return FreeTimespan[]
220
     */
221
    public function computeFreeTimespans(DateTime $start, DateTime $end): array {
222
        $result = [ ];
223
224
        $categories = array_map(fn(int $id) => $this->appointmentCategoryRepository->findOneById($id), $this->timetableSettings->getCategoryIds());
225
226
        if(count($categories) > 0) {
227
            $appointments = $this->appointmentRepository->findAllStartEnd($start, $end, $categories);
228
229
            foreach($appointments as $appointment) {
230
                $current = clone $appointment->getStart();
231
                while($current < $appointment->getEnd()) {
232
                    $result[] = new FreeTimespan(clone $current, 1, $this->timetableSettings->getMaxLessons(), $appointment->getTitle());
0 ignored issues
show
Bug introduced by
It seems like $appointment->getTitle() can also be of type null; however, parameter $reason of App\Book\FreeTimespan::__construct() does only seem to accept string, 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

232
                    $result[] = new FreeTimespan(clone $current, 1, $this->timetableSettings->getMaxLessons(), /** @scrutinizer ignore-type */ $appointment->getTitle());
Loading history...
233
                    $current = $current->modify('+1 day');
234
                }
235
            }
236
        }
237
238
        // findAllStartEnd queries appointments which includes the end date - but that may cause issues when $start==$end, so manually filter
239
        $result = array_filter($result, fn(FreeTimespan $timespan) => $timespan->getDate() >= $start && $timespan->getDate() <= $end);
240
241
        $current = clone $start;
242
        while($current <= $end) {
243
            $freeTimespans = $this->freeTimespanRepository->findAllByDate($current);
244
245
            foreach($freeTimespans as $timespan) {
246
                $result[] = new FreeTimespan(clone $timespan->getDate(), $timespan->getStart(), $timespan->getEnd(), 'Vertretungsplan');
247
            }
248
249
            $current = $current->modify('+1 day');
250
        }
251
252
        return $result;
253
    }
254
}