Passed
Push — master ( ea7d0c...20f36e )
by Marcel
09:46
created

UntisImportController::settings()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 144
Code Lines 111

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 111
nc 6
nop 5
dl 0
loc 144
rs 7.3777
c 1
b 0
f 0

How to fix   Long Method   

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\Controller;
4
5
use App\Converter\EnumStringConverter;
6
use App\Entity\Student;
7
use App\Form\Import\Untis\RoomImportType;
8
use App\Request\ValidationFailedException;
9
use App\Untis\Gpu\Room\RoomImporter;
10
use App\Untis\StudentId\StudentIdGenerator;
11
use App\Untis\StudentIdFormat;
12
use Symfony\Component\Form\Extension\Core\Type\EnumType;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Form\E...sion\Core\Type\EnumType 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...
13
use Symfony\Component\Form\Extension\Core\Type\TextType;
14
use Symfony\Component\HttpFoundation\Response;
15
use App\Form\Import\Untis\CalendarWeekSchoolWeekType;
16
use App\Form\Import\Untis\ExamImportType;
17
use App\Form\Import\Untis\SubjectOverrideType;
18
use App\Form\Import\Untis\SubstitutionGpuImportType;
19
use App\Form\Import\Untis\SubstitutionHtmlImportType;
20
use App\Form\Import\Untis\SupervisionImportType;
21
use App\Form\Import\Untis\TimetableHtmlImportType;
22
use App\Form\Import\Untis\TimetableImportType;
23
use App\Form\RegExpType;
24
use App\Import\ImportException;
25
use App\Section\SectionResolverInterface;
26
use App\Settings\UntisSettings;
27
use App\Untis\Database\Date\DateReader;
28
use App\Untis\Gpu\Exam\ExamImporter;
29
use App\Untis\Gpu\Substitution\SubstitutionImporter as GpuSubstitutionImporter;
30
use App\Untis\Gpu\Supervision\SupervisionImporter;
31
use App\Untis\Html\HtmlParseException;
32
use App\Untis\Html\Substitution\SubstitutionImporter as HtmlSubstitutionImporter;
33
use App\Untis\Html\Timetable\TimetableImporter as HtmlTimetableImporter;
34
use DateTime;
35
use Exception;
36
use League\Csv\Reader;
0 ignored issues
show
Bug introduced by
The type League\Csv\Reader 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...
37
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
38
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
39
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
40
use Symfony\Component\Form\Extension\Core\Type\FileType;
41
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
42
use Symfony\Component\HttpFoundation\File\UploadedFile;
43
use Symfony\Component\HttpFoundation\Request;
44
use Symfony\Component\Routing\Annotation\Route;
45
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
46
use Symfony\Component\Validator\Constraints\NotBlank;
47
use Symfony\Contracts\Translation\TranslatorInterface;
48
use ZipArchive;
49
50
#[Route(path: '/import')]
51
#[IsGranted('ROLE_IMPORTER')]
52
class UntisImportController extends AbstractController {
53
54
    #[Route(path: '/settings', name: 'import_untis_settings')]
55
    public function settings(UntisSettings $settings, Request $request, DateReader $reader, EnumStringConverter $enumStringConverter, StudentIdGenerator $studentIdGenerator): Response {
56
        $form = $this->createFormBuilder()
57
            ->add('overrides', CollectionType::class, [
58
                'entry_type' => SubjectOverrideType::class,
59
                'allow_add' => true,
60
                'allow_delete' => true,
61
                'by_reference' => false,
62
                'data' => $settings->getSubjectOverrides()
63
            ])
64
            ->add('weeks', CollectionType::class, [
65
                'entry_type' => CalendarWeekSchoolWeekType::class,
66
                'allow_add' => true,
67
                'allow_delete' => true,
68
                'by_reference' => false,
69
                'data' => $settings->getWeekMap()
70
            ])
71
            ->add('import_weeks', FileType::class, [
72
                'label' => 'dates.txt',
73
                'required' => false
74
            ])
75
            ->add('substitution_days', IntegerType::class, [
76
                'label' => 'import.settings.substitutions.days.label',
77
                'help' => 'import.settings.substitutions.days.help',
78
                'data' => $settings->getSubstitutionDays()
79
            ])
80
            ->add('collapse_substitutions', CheckboxType::class, [
81
                'label' => 'import.settings.substitutions.collapse.label',
82
                'help' => 'import.settings.substitutions.collapse.help',
83
                'required' => false,
84
                'label_attr' => [
85
                    'class' => 'checkbox-custom'
86
                ],
87
                'data' => $settings->isSubstitutionCollapsingEnabled()
88
            ])
89
            ->add('exam_writers', CheckboxType::class, [
90
                'label' => 'import.settings.exams.include_students.label',
91
                'help' => 'import.settings.exams.include_students.help',
92
                'required' => false,
93
                'label_attr' => [
94
                    'class' => 'checkbox-custom'
95
                ],
96
                'data' => $settings->alwaysImportExamWriters()
97
            ])
98
            ->add('ignore_options_regexp', RegExpType::class, [
99
                'label' => 'import.settings.exams.ignore_options_regexp.label',
100
                'help' => 'import.settings.exams.ignore_options_regexp.help',
101
                'required' => false,
102
                'data' => $settings->getIgnoreStudentOptionRegExp()
103
            ])
104
            ->add('student_id_format', EnumType::class, [
105
                'label' => 'import.settings.students.format.label',
106
                'help' => 'import.settings.students.format.help',
107
                'class' => StudentIdFormat::class,
108
                'choice_label' => function(StudentIdFormat $format) use($enumStringConverter) {
109
                    return $enumStringConverter->convert($format);
110
                },
111
                'data' => $settings->getStudentIdentifierFormat()
112
            ])
113
            ->add('student_id_firstname_letters', IntegerType::class, [
114
                'label' => 'import.settings.students.firstname_letters.label',
115
                'help' => 'import.settings.students.firstname_letters.help',
116
                'required' => false,
117
                'constraints' => [
118
                    new GreaterThanOrEqual(0)
119
                ],
120
                'data' => $settings->getStudentIdentifierNumberOfLettersOfFirstname()
121
            ])
122
            ->add('student_id_lastname_letters', IntegerType::class, [
123
                'label' => 'import.settings.students.lastname_letters.label',
124
                'help' => 'import.settings.students.lastname_letters.help',
125
                'required' => false,
126
                'constraints' => [
127
                    new GreaterThanOrEqual(0)
128
                ],
129
                'data' => $settings->getStudentIdentifierNumberOfLettersOfLastname()
130
            ])
131
            ->add('student_id_birthday_format', TextType::class, [
132
                'label' => 'import.settings.students.birthday_format.label',
133
                'help' => 'import.settings.students.birthday_format.help',
134
                'required' => false,
135
                'constraints' => [
136
                    new NotBlank(allowNull: true)
137
                ],
138
                'data' => $settings->getStudentIdentifierBirthdayFormat()
139
            ])
140
            ->add('student_id_separator', TextType::class, [
141
                'label' => 'import.settings.students.separator.label',
142
                'help' => 'import.settings.students.separator.help',
143
                'required' => false,
144
                'constraints' => [
145
                    new NotBlank(allowNull: true)
146
                ],
147
                'data' => $settings->getStudentIdentifierSeparator()
148
            ])
149
            ->getForm();
150
        $form->handleRequest($request);
151
152
        if($form->isSubmitted() && $form->isValid()) {
153
            $settings->setSubjectOverrides($form->get('overrides')->getData());
154
            $settings->setWeekMap($form->get('weeks')->getData());
155
            $settings->setSubstitutionDays($form->get('substitution_days')->getData());
156
            $settings->setSubstitutionCollapsingEnabled($form->get('collapse_substitutions')->getData());
157
            $settings->setAlwaysImportExamWriters($form->get('exam_writers')->getData());
158
            $settings->setIgnoreStudentOptionRegExp($form->get('ignore_options_regexp')->getData());
159
            $settings->setStudentIdentifierFormat($form->get('student_id_format')->getData());
160
            $settings->setStudentIdentifierNumberOfLettersOfFirstname($form->get('student_id_firstname_letters')->getData());
161
            $settings->setStudentIdentifierNumberOfLettersOfLastname($form->get('student_id_lastname_letters')->getData());
162
            $settings->setStudentIdentifierBirthdayFormat($form->get('student_id_birthday_format')->getData());
163
            $settings->setStudentIdentifierSeparator($form->get('student_id_separator')->getData());
164
165
            /** @var UploadedFile|null $file */
166
            $file = $form->get('import_weeks')->getData();
167
            if($file !== null) {
168
                try {
169
                    $dates = $reader->readDatabase(Reader::createFromPath($file->getRealPath()));
170
                    $map = [];
171
172
                    foreach ($dates as $date) {
173
                        $map[] = [
174
                            'calendar_week' => $date->getCalendarWeek(),
175
                            'school_week' => $date->getSchoolWeek()
176
                        ];
177
                    }
178
                    $settings->setWeekMap($map);
179
                } catch (ImportException $exception) {
180
                    $this->addFlash('error', $exception->getMessage());
181
                }
182
            }
183
184
            $this->addFlash('success', 'import.settings.success');
185
            return $this->redirectToRoute('import_untis_settings');
186
        }
187
188
        $student = (new Student())
189
            ->setFirstname('Erika')
190
            ->setLastname('Musterfrau')
191
            ->setBirthday((new DateTime())->setTime(0,0,0));
192
193
        $preview = $studentIdGenerator->generate($student);
194
195
        return $this->render('import/settings.html.twig', [
196
            'form' => $form->createView(),
197
            'student_preview' => $preview
198
        ]);
199
    }
200
201
    #[Route(path: '/substitutions/gpu', name: 'import_untis_substitutions_gpu')]
202
    public function substitutionsGpu(Request $request, GpuSubstitutionImporter $importer, TranslatorInterface $translator, UntisSettings $settings): Response {
203
        $data = [ ];
204
        if($settings->getSubstitutionDays() > 0) {
205
            $data = [
206
                'start' => new DateTime('today'),
207
                'end' => (new DateTime('today'))->modify('+' . $settings->getSubstitutionDays() . ' days')
208
            ];
209
        }
210
211
        $form = $this->createForm(SubstitutionGpuImportType::class, $data);
212
        $form->handleRequest($request);
213
214
        if($form->isSubmitted() && $form->isValid()) {
215
            /** @var DateTime $start */
216
            $start = $form->get('start')->getData();
217
            /** @var DateTime $end */
218
            $end = $form->get('end')->getData();
219
            /** @var UploadedFile $file */
220
            $file = $form->get('importFile')->getData();
221
            /** @var bool $suppressNotifications */
222
            $suppressNotifications = $form->get('suppressNotifications')->getData();
223
224
            $csvReader = Reader::createFromPath($file->getRealPath());
225
            try {
226
                $result = $importer->import($csvReader, $start, $end, $suppressNotifications);
227
228
                $this->addFlash('success', $translator->trans('import.substitutions.result', [
229
                    '%added%' => count($result->getAdded()),
230
                    '%ignored%' => count($result->getIgnored()),
231
                    '%updated%' => count($result->getUpdated()),
232
                    '%removed%' => count($result->getRemoved())
233
                ]));
234
235
                return $this->redirectToRoute('import_untis_substitutions_gpu');
236
            } catch (ImportException $exception) {
237
                $this->addFlash('error', $exception->getMessage());
238
            }
239
        }
240
241
        return $this->render('import/substitutions_gpu.html.twig', [
242
            'form' => $form->createView()
243
        ]);
244
    }
245
246
    #[Route(path: '/substitutions/html', name: 'import_untis_substitutions_html')]
247
    public function substitutionsHtml(Request $request, HtmlSubstitutionImporter $importer, TranslatorInterface $translator, UntisSettings $settings): Response {
0 ignored issues
show
Unused Code introduced by
The parameter $translator 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

247
    public function substitutionsHtml(Request $request, HtmlSubstitutionImporter $importer, /** @scrutinizer ignore-unused */ TranslatorInterface $translator, UntisSettings $settings): 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...
Unused Code introduced by
The parameter $settings 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

247
    public function substitutionsHtml(Request $request, HtmlSubstitutionImporter $importer, TranslatorInterface $translator, /** @scrutinizer ignore-unused */ UntisSettings $settings): 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...
248
        $form = $this->createForm(SubstitutionHtmlImportType::class);
249
        $form->handleRequest($request);
250
251
        if($form->isSubmitted() && $form->isValid()) {
252
            /** @var UploadedFile[] $files */
253
            $files = $form->get('importFiles')->getData();
254
            /** @var bool $suppressNotifications */
255
            $suppressNotifications = $form->get('suppressNotifications')->getData();
256
257
            try {
258
                for($idx = 0; $idx < count($files); $idx++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
259
                    $file = $files[$idx];
260
                    $isLast = $idx === (count($files) - 1);
261
262
                    $importer->import($file->getContent(), $isLast === false || $suppressNotifications);
263
                }
264
265
                $this->addFlash('success', 'import.substitutions.html.success');
266
                return $this->redirectToRoute('import_untis_substitutions_html');
267
            } catch (Exception $exception) {
268
                $this->addFlash('error', $exception->getMessage());
269
            }
270
        }
271
272
        return $this->render('import/substitutions_html.html.twig', [
273
            'form' => $form->createView()
274
        ]);
275
    }
276
277
    #[Route(path: '/supervisions', name: 'import_untis_supervisions')]
278
    public function supervisions(Request $request, SupervisionImporter $importer, TranslatorInterface $translator): Response {
279
        $form = $this->createForm(SupervisionImportType::class);
280
        $form->handleRequest($request);
281
282
        if($form->isSubmitted() && $form->isValid()) {
283
            /** @var DateTime $start */
284
            $start = $form->get('start')->getData();
285
            /** @var DateTime $end */
286
            $end = $form->get('end')->getData();
287
            /** @var UploadedFile $file */
288
            $file = $form->get('importFile')->getData();
289
290
            $csvReader = Reader::createFromPath($file->getRealPath());
291
            try {
292
                $result = $importer->import($csvReader, $start, $end);
293
294
                $this->addFlash('success', $translator->trans('import.supervisions.result', [
295
                    '%added%' => count($result->getAdded())
296
                ]));
297
298
                return $this->redirectToRoute('import_untis_supervisions');
299
            } catch (ImportException $exception) {
300
                $this->addFlash('error', $exception->getMessage());
301
            }
302
        }
303
304
        return $this->render('import/supervisions.html.twig', [
305
            'form' => $form->createView()
306
        ]);
307
    }
308
309
    #[Route(path: '/exams', name: 'import_untis_exams')]
310
    public function exams(Request $request, ExamImporter $importer, TranslatorInterface $translator, SectionResolverInterface $sectionResolver): Response {
311
        $currentSection = $sectionResolver->getCurrentSection();
312
        $data = [ ];
313
        if($currentSection !== null) {
314
            $data['start'] = $currentSection->getStart();
315
            $data['end'] = $currentSection->getEnd();
316
        }
317
318
        $form = $this->createForm(ExamImportType::class, $data);
319
        $form->handleRequest($request);
320
321
        if($form->isSubmitted() && $form->isValid()) {
322
            /** @var DateTime $start */
323
            $start = $form->get('start')->getData();
324
            /** @var DateTime $end */
325
            $end = $form->get('end')->getData();
326
            /** @var UploadedFile $tuitionFile */
327
            $tuitionFile = $form->get('tuitionFile')->getData();
328
            /** @var UploadedFile $examFile */
329
            $examFile = $form->get('examFile')->getData();
330
            /** @var bool $suppressNotifications */
331
            $suppressNotifications = $form->get('suppressNotifications')->getData();
332
333
            try {
334
                $result = $importer->import(Reader::createFromPath($examFile->getRealPath()), Reader::createFromPath($tuitionFile->getRealPath()), $start, $end, $suppressNotifications);
335
336
                $this->addFlash('success', $translator->trans('import.exams.result', [
337
                    '%added%' => is_countable($result->getAdded()) ? count($result->getAdded()) : 0,
338
                    '%ignored%' => is_countable($result->getIgnored()) ? count($result->getIgnored()) : 0,
339
                    '%updated%' => is_countable($result->getUpdated()) ? count($result->getUpdated()) : 0,
340
                    '%removed%' => is_countable($result->getRemoved()) ? count($result->getRemoved()) : 0
341
                ]));
342
343
                return $this->redirectToRoute('import_untis_exams');
344
            } catch (ImportException $exception) {
345
                $this->addFlash('error', $exception->getMessage());
346
            }
347
        }
348
349
        return $this->render('import/exams.html.twig', [
350
            'form' => $form->createView()
351
        ]);
352
    }
353
354
    #[Route('/rooms', name: 'import_untis_rooms')]
355
    public function rooms(Request $request, RoomImporter $roomImporter, TranslatorInterface $translator): Response {
356
        $form = $this->createForm(RoomImportType::class);
357
        $form->handleRequest($request);
358
359
        if($form->isSubmitted() && $form->isValid()) {
360
            /** @var UploadedFile $roomsFile */
361
            $roomsFile = $form->get('importFile')->getData();
362
363
            try {
364
                $result = $roomImporter->import(Reader::createFromPath($roomsFile->getRealPath()));
365
366
                $this->addFlash('success', $translator->trans('import.rooms.result', [
367
                    '%added%' => count($result->getAdded()),
368
                    '%ignored%' => count($result->getIgnored()),
369
                    '%updated%' => count($result->getUpdated()),
370
                    '%removed%' => count($result->getRemoved())
371
                ]));
372
373
                return $this->redirectToRoute('import_untis_rooms');
374
            } catch (ImportException $exception) {
375
                $this->addFlash('error', $exception->getMessage());
376
            }
377
        }
378
379
        return $this->render('import/rooms.html.twig', [
380
            'form' => $form->createView()
381
        ]);
382
    }
383
384
    #[Route(path: '/timetable/html', name: 'import_untis_timetable_html')]
385
    public function timetableHtml(Request $request, HtmlTimetableImporter $importer, TranslatorInterface $translator): Response {
386
        $form = $this->createForm(TimetableHtmlImportType::class);
387
        $form->handleRequest($request);
388
389
        $violations = [ ];
390
391
        if($form->isSubmitted() && $form->isValid()) {
392
            /** @var DateTime $start */
393
            $start = $form->get('start')->getData();
394
            /** @var DateTime $end */
395
            $end = $form->get('end')->getData();
396
            /** @var UploadedFile|null $grades */
397
            $grades = $form->get('grades')->getData();
398
            /** @var UploadedFile|null $subjects */
399
            $subjects = $form->get('subjects')->getData();
400
401
            try {
402
                $gradesHtml = $grades !== null ? $this->readZip($grades) : [ ];
403
                $subjectsHtml = $subjects !== null ? $this->readZip($subjects) : [ ];
404
405
                $result = $importer->import($gradesHtml, $subjectsHtml, $start, $end);
406
407
                $this->addFlash('success', $translator->trans('import.timetable.html.result', [
408
                    '%added%' => count($result->getAdded())
409
                ]));
410
411
                return $this->redirectToRoute('import_untis_timetable_html');
412
            } catch (ValidationFailedException $e) {
413
                $violations = $e->getViolations();
414
            }
415
            catch (HtmlParseException|ImportException $exception) {
416
                $this->addFlash('error', $exception->getMessage());
417
            }
418
        }
419
420
        return $this->render('import/timetable_html.html.twig', [
421
            'form' => $form->createView(),
422
            'violations' => $violations
423
        ]);
424
    }
425
426
    private function readZip(UploadedFile $file): array {
427
        $html = [ ];
428
429
        $zip = new ZipArchive();
430
        $zip->open($file->getRealPath(), ZipArchive::RDONLY);
431
        for($idx = 0; $idx < $zip->numFiles; $idx++) {
432
            $html[] = $zip->getFromIndex($idx);
433
        }
434
        $zip->close();
435
436
        return $html;
437
    }
438
}