Passed
Push — master ( d0aec4...3a1c32 )
by
unknown
16:01 queued 07:49
created

AttendanceController   F

Complexity

Total Complexity 85

Size/Duplication

Total Lines 579
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 339
dl 0
loc 579
rs 2
c 1
b 0
f 0
wmc 85

11 Methods

Rating   Name   Duplication   Size   Complexity  
A generateQrCode() 0 34 5
B exportToXls() 0 73 11
B getUsersWithFaults() 0 55 9
A getFullAttendanceData() 0 12 2
A __construct() 0 5 1
F saveAttendanceSheet() 0 118 22
F exportToPdf() 0 130 17
B getStudentDates() 0 55 7
A listWithDoneCount() 0 28 4
A updateAttendanceResults() 0 31 6
A saveAttendanceLog() 0 11 1

How to fix   Complexity   

Complex Class

Complex classes like AttendanceController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AttendanceController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chamilo\CoreBundle\Controller;
6
7
use Chamilo\CoreBundle\Entity\Course;
8
use Chamilo\CoreBundle\Entity\Session;
9
use Chamilo\CoreBundle\Entity\User;
10
use Chamilo\CoreBundle\Repository\Node\UserRepository;
11
use Chamilo\CourseBundle\Entity\CAttendance;
12
use Chamilo\CourseBundle\Entity\CAttendanceCalendar;
13
use Chamilo\CourseBundle\Entity\CAttendanceResult;
14
use Chamilo\CourseBundle\Entity\CAttendanceResultComment;
15
use Chamilo\CourseBundle\Entity\CAttendanceSheet;
16
use Chamilo\CourseBundle\Entity\CAttendanceSheetLog;
17
use Chamilo\CourseBundle\Repository\CAttendanceCalendarRepository;
18
use Chamilo\CourseBundle\Repository\CAttendanceSheetRepository;
19
use DateTime;
20
use Doctrine\ORM\EntityManagerInterface;
21
use Endroid\QrCode\Builder\Builder;
22
use Exception;
23
use Mpdf\Mpdf;
24
use Mpdf\MpdfException;
25
use Mpdf\Output\Destination;
26
use PhpOffice\PhpSpreadsheet\Spreadsheet;
27
use PhpOffice\PhpSpreadsheet\Writer\Xls;
28
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
29
use Symfony\Component\HttpFoundation\JsonResponse;
30
use Symfony\Component\HttpFoundation\Request;
31
use Symfony\Component\HttpFoundation\Response;
32
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
33
use Symfony\Component\HttpFoundation\StreamedResponse;
34
use Symfony\Component\Routing\Attribute\Route;
35
use Symfony\Contracts\Translation\TranslatorInterface;
36
37
#[Route('/attendance')]
38
class AttendanceController extends AbstractController
39
{
40
    public function __construct(
41
        private readonly CAttendanceCalendarRepository $attendanceCalendarRepository,
42
        private readonly EntityManagerInterface $em,
43
        private readonly TranslatorInterface $translator
44
    ) {}
45
46
    #[Route('/full-data', name: 'chamilo_core_attendance_get_full_data', methods: ['GET'])]
47
    public function getFullAttendanceData(Request $request): JsonResponse
48
    {
49
        $attendanceId = (int) $request->query->get('attendanceId', 0);
50
51
        if (!$attendanceId) {
52
            return $this->json(['error' => 'Attendance ID is required'], 400);
53
        }
54
55
        $data = $this->attendanceCalendarRepository->findAttendanceWithData($attendanceId);
56
57
        return $this->json($data, 200);
58
    }
59
60
    #[Route('/{id}/users/context', name: 'chamilo_core_get_users_with_faults', methods: ['GET'])]
61
    public function getUsersWithFaults(
62
        int $id,
63
        Request $request,
64
        UserRepository $userRepository,
65
        CAttendanceCalendarRepository $calendarRepository,
66
        CAttendanceSheetRepository $sheetRepository
67
    ): JsonResponse {
68
        $courseId = (int) $request->query->get('courseId', 0);
69
        $sessionId = $request->query->get('sessionId') ? (int) $request->query->get('sessionId') : null;
70
        $groupId = $request->query->get('groupId') ? (int) $request->query->get('groupId') : null;
71
72
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
73
        if (!$attendance) {
74
            return $this->json(['error' => 'Attendance not found'], 404);
75
        }
76
77
        $calendars = $attendance->getCalendars();
78
        $totalCalendars = count($calendars);
79
80
        $users = $userRepository->findUsersByContext($courseId, $sessionId, $groupId);
81
82
        $formattedUsers = array_map(function ($user) use ($userRepository, $courseId, $groupId, $calendarRepository, $sheetRepository, $calendars) {
0 ignored issues
show
Unused Code introduced by
The import $groupId is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The import $courseId is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The import $calendarRepository is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
83
84
            $absences = 0;
85
86
            foreach ($calendars as $calendar) {
87
                $sheet = $sheetRepository->findOneBy([
88
                    'user' => $user,
89
                    'attendanceCalendar' => $calendar,
90
                ]);
91
92
                if (!$sheet || $sheet->getPresence() === null) {
93
                    continue;
94
                }
95
96
                if ($sheet->getPresence() !== 1) {
97
                    $absences++;
98
                }
99
            }
100
101
            $percentage = count($calendars) > 0 ? round(($absences * 100) / count($calendars)) : 0;
102
103
            return [
104
                'id' => $user->getId(),
105
                'firstname' => $user->getFirstname(),
106
                'lastname' => $user->getLastname(),
107
                'email' => $user->getEmail(),
108
                'username' => $user->getUsername(),
109
                'photo' => $userRepository->getUserPicture($user->getId()),
110
                'notAttended' => "$absences/" . count($calendars) . " ({$percentage}%)",
111
            ];
112
        }, $users);
113
114
        return $this->json($formattedUsers, 200);
115
    }
116
117
    #[Route('/list_with_done_count', name: 'attendance_list_with_done_count', methods: ['GET'])]
118
    public function listWithDoneCount(Request $request): JsonResponse
119
    {
120
        $courseId = (int) $request->query->get('cid', 0);
121
        $sessionId = $request->query->get('sid') ? (int) $request->query->get('sid') : null;
122
        $groupId = $request->query->get('gid') ? (int) $request->query->get('gid') : null;
123
        $parentNode = (int) $request->query->get('resourceNode.parent', 0);
124
125
        $attendances = $this->em->getRepository(CAttendance::class)->findBy([
126
            'active' => 1,
127
        ]);
128
129
        $result = [];
130
        foreach ($attendances as $attendance) {
131
            $doneCount = $this->attendanceCalendarRepository->countDoneAttendanceByAttendanceAndGroup($attendance->getIid(), $groupId);
132
133
            $result[] = [
134
                'id' => $attendance->getIid(),
135
                'title' => $attendance->getTitle(),
136
                'description' => $attendance->getDescription(),
137
                'attendanceWeight' => $attendance->getAttendanceWeight(),
138
                'attendanceQualifyTitle' => $attendance->getAttendanceQualifyTitle(),
139
                'resourceLinkListFromEntity' => $attendance->getResourceLinkListFromEntity(),
140
                'doneCalendars' => $doneCount,
141
            ];
142
        }
143
144
        return $this->json($result);
145
    }
146
147
    #[Route('/{id}/export/pdf', name: 'attendance_export_pdf', methods: ['GET'])]
148
    public function exportToPdf(int $id, Request $request): Response
149
    {
150
        $courseId = (int) $request->query->get('cid');
151
        $sessionId = ((int) $request->query->get('sid')) ?: null;
152
        $groupId = ((int) $request->query->get('gid')) ?: null;
153
154
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
155
        if (!$attendance) {
156
            throw $this->createNotFoundException('Attendance not found');
157
        }
158
159
        $calendars = $attendance->getCalendars();
160
        $totalCalendars = count($calendars);
161
162
        $students = $this->em->getRepository(User::class)->findUsersByContext($courseId, $sessionId, $groupId);
163
        $sheetRepo = $this->em->getRepository(CAttendanceSheet::class);
164
165
        $course = $this->em->getRepository(Course::class)->find($courseId);
166
        $teacher = null;
167
168
        if ($sessionId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sessionId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
169
            $session = $this->em->getRepository(Session::class)->find($sessionId);
170
            $teacher = $session?->getCourseCoachesSubscriptions()
171
                ->filter(fn($rel) => $rel->getCourse()?->getId() === $courseId)
172
                ->first()?->getUser()?->getFullname();
173
        } else {
174
            $teacher = $course?->getTeachersSubscriptions()?->first()?->getUser()?->getFullname();
175
        }
176
177
        // Header
178
        $dataTable = [];
179
        $header = ['#', 'Last Name', 'First Name', 'Not Attended'];
180
        foreach ($calendars as $calendar) {
181
            $header[] = $calendar->getDateTime()->format('d/m H:i');
182
        }
183
        $dataTable[] = $header;
184
185
        // Rows
186
        $count = 1;
187
        $stateLabels = CAttendanceSheet::getPresenceLabels();
188
189
        foreach ($students as $student) {
190
            $row = [
191
                $count++,
192
                $student->getLastname(),
193
                $student->getFirstname(),
194
                '',
195
            ];
196
197
            $absences = 0;
198
            foreach ($calendars as $calendar) {
199
                $sheetEntity = $sheetRepo->findOneBy([
200
                    'user' => $student,
201
                    'attendanceCalendar' => $calendar,
202
                ]);
203
204
                if (!$sheetEntity || $sheetEntity->getPresence() === null) {
205
                    $row[] = '';
206
                    continue;
207
                }
208
209
                $presence = $sheetEntity->getPresence();
210
                $row[] = $stateLabels[$presence] ?? 'NP';
211
212
                if ($presence === CAttendanceSheet::ABSENT) {
213
                    $absences++;
214
                }
215
            }
216
217
            $percentage = $totalCalendars > 0 ? round(($absences * 100) / $totalCalendars) : 0;
218
            $row[3] = "$absences/$totalCalendars ($percentage%)";
219
            $dataTable[] = $row;
220
        }
221
222
        // Render HTML
223
        $html = '
224
            <style>
225
                body { font-family: sans-serif; font-size: 12px; }
226
                h2 { text-align: center; margin-bottom: 5px; }
227
                table.meta { margin: 0 auto 10px auto; width: 80%; }
228
                table.meta td { padding: 2px 5px; }
229
                table.attendance { border-collapse: collapse; width: 100%; }
230
                .attendance th, .attendance td { border: 1px solid #000; padding: 4px; text-align: center; }
231
                .np { color: red; font-weight: bold; }
232
            </style>
233
234
            <h2>' . htmlspecialchars($attendance->getTitle()) . '</h2>
235
236
            <table class="meta">
237
                <tr><td><strong>Trainer:</strong></td><td>' . htmlspecialchars($teacher ?? '-') . '</td></tr>
238
                <tr><td><strong>Course:</strong></td><td>' . htmlspecialchars($course?->getTitleAndCode() ?? '-') . '</td></tr>
239
                <tr><td><strong>Date:</strong></td><td>' . date('F d, Y \a\t h:i A') . '</td></tr>
240
            </table>
241
242
            <table class="attendance">
243
            <tr>';
244
        foreach ($dataTable[0] as $cell) {
245
            $html .= '<th>' . htmlspecialchars((string) $cell) . '</th>';
246
        }
247
        $html .= '</tr>';
248
249
        foreach (array_slice($dataTable, 1) as $row) {
250
            $html .= '<tr>';
251
            foreach ($row as $cell) {
252
                $class = $cell === 'NP' ? ' class="np"' : '';
253
                $html .= "<td$class>" . htmlspecialchars((string) $cell) . "</td>";
254
            }
255
            $html .= '</tr>';
256
        }
257
258
        $html .= '</table>';
259
260
        try {
261
            $mpdf = new \Mpdf\Mpdf([
262
                'orientation' => 'L',
263
                'tempDir' => api_get_path(SYS_ARCHIVE_PATH) . 'mpdf/',
264
            ]);
265
            $mpdf->WriteHTML($html);
266
267
            return new Response(
268
                $mpdf->Output('', \Mpdf\Output\Destination::INLINE),
269
                200,
270
                [
271
                    'Content-Type' => 'application/pdf',
272
                    'Content-Disposition' => 'attachment; filename="attendance-' . $id . '.pdf"',
273
                ]
274
            );
275
        } catch (\Mpdf\MpdfException $e) {
276
            throw new \RuntimeException('Failed to generate PDF: ' . $e->getMessage(), 500, $e);
277
        }
278
    }
279
280
    #[Route('/{id}/export/xls', name: 'attendance_export_xls', methods: ['GET'])]
281
    public function exportToXls(int $id, Request $request): Response
282
    {
283
        $courseId = (int) $request->query->get('cid');
284
        $sessionId = $request->query->get('sid') ? (int) $request->query->get('sid') : null;
285
        $groupId = $request->query->get('gid') ? (int) $request->query->get('gid') : null;
286
287
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
288
        if (!$attendance) {
289
            throw $this->createNotFoundException('Attendance not found');
290
        }
291
292
        $calendars = $attendance->getCalendars();
293
        $totalCalendars = count($calendars);
294
        $students = $this->em->getRepository(User::class)->findUsersByContext($courseId, $sessionId, $groupId);
295
        $sheetRepo = $this->em->getRepository(CAttendanceSheet::class);
296
297
        $stateLabels = CAttendanceSheet::getPresenceLabels();
298
299
        $spreadsheet = new Spreadsheet();
300
        $sheet = $spreadsheet->getActiveSheet();
301
        $sheet->setTitle('Attendance');
302
303
        // Header
304
        $headers = ['#', 'Last Name', 'First Name', 'Not Attended'];
305
        foreach ($calendars as $calendar) {
306
            $headers[] = $calendar->getDateTime()->format('d/m H:i');
307
        }
308
        $sheet->fromArray($headers, null, 'A1');
309
310
        // Rows
311
        $rowNumber = 2;
312
        $count = 1;
313
        foreach ($students as $student) {
314
            $row = [$count++, $student->getLastname(), $student->getFirstname()];
315
            $absences = 0;
316
317
            foreach ($calendars as $calendar) {
318
                $sheetEntity = $sheetRepo->findOneBy([
319
                    'user' => $student,
320
                    'attendanceCalendar' => $calendar,
321
                ]);
322
323
                if (!$sheetEntity || $sheetEntity->getPresence() === null) {
324
                    $row[] = '';
325
                    continue;
326
                }
327
328
                $presence = $sheetEntity->getPresence();
329
                $row[] = $stateLabels[$presence] ?? 'NP';
330
331
                if ($presence === CAttendanceSheet::ABSENT) {
332
                    $absences++;
333
                }
334
            }
335
336
            $percentage = $totalCalendars > 0 ? round(($absences * 100) / $totalCalendars) : 0;
337
            array_splice($row, 3, 0, "$absences/$totalCalendars ($percentage%)");
338
339
            $sheet->fromArray($row, null, 'A' . $rowNumber++);
340
        }
341
342
        // Output
343
        $writer = new Xls($spreadsheet);
344
        $response = new StreamedResponse(fn() => $writer->save('php://output'));
345
        $disposition = $response->headers->makeDisposition(
346
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
347
            "attendance-$id.xls"
348
        );
349
350
        $response->headers->set('Content-Type', 'application/vnd.ms-excel');
351
        $response->headers->set('Content-Disposition', $disposition);
352
        return $response;
353
    }
354
355
    #[Route('/{id}/qrcode', name: 'attendance_qrcode', methods: ['GET'])]
356
    public function generateQrCode(int $id, Request $request): Response
357
    {
358
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
359
        if (!$attendance) {
360
            throw $this->createNotFoundException('Attendance not found');
361
        }
362
363
        $resourceNodeId = $attendance->getResourceNode()?->getParent()?->getId();
364
        if (!$resourceNodeId) {
365
            throw new \RuntimeException('Missing resourceNode for course');
366
        }
367
368
        $sid = $request->query->get('sid');
369
        $gid = $request->query->get('gid');
370
371
        $query = 'readonly=1';
372
        if ($sid) {
373
            $query .= "&sid=$sid";
374
        }
375
        if ($gid) {
376
            $query .= "&gid=$gid";
377
        }
378
379
        $url = "/resources/attendance/$resourceNodeId/$id/sheet-list?$query";
380
        $fullUrl = $request->getSchemeAndHttpHost() . $url;
381
382
        $result = Builder::create()
383
            ->data($fullUrl)
384
            ->size(300)
385
            ->margin(10)
386
            ->build();
387
388
        return new Response($result->getString(), 200, ['Content-Type' => $result->getMimeType()]);
389
    }
390
391
392
    #[Route('/sheet/save', name: 'chamilo_core_attendance_sheet_save', methods: ['POST'])]
393
    public function saveAttendanceSheet(
394
        Request $request,
395
        UserRepository $userRepository,
396
        CAttendanceSheetRepository $sheetRepository
397
    ): JsonResponse {
398
        $data = json_decode($request->getContent(), true);
399
400
        if (empty($data['attendanceData']) || empty($data['courseId'])) {
401
            return $this->json(['error' => 'Missing required parameters'], 400);
402
        }
403
404
        $attendanceData = $data['attendanceData'];
405
        $courseId = (int) $data['courseId'];
406
        $sessionId = isset($data['sessionId']) ? (int) $data['sessionId'] : null;
407
        $groupId = isset($data['groupId']) ? (int) $data['groupId'] : null;
408
409
        $usersInCourse = $userRepository->findUsersByContext($courseId, $sessionId, $groupId);
410
        $userIdsInCourse = array_map(fn (User $user) => $user->getId(), $usersInCourse);
411
412
        $affectedRows = 0;
413
414
        try {
415
            foreach ($attendanceData as $entry) {
416
                $userId = (int) $entry['userId'];
417
                $calendarId = (int) $entry['calendarId'];
418
                $presence = \array_key_exists('presence', $entry) ? $entry['presence'] : null;
419
                $signature = $entry['signature'] ?? null;
420
                $comment = $entry['comment'] ?? null;
421
422
                $calendar = $this->attendanceCalendarRepository->find($calendarId);
423
                if (!$calendar) {
424
                    return $this->json(['error' => "Attendance calendar with ID $calendarId not found"], 404);
425
                }
426
427
                $user = $this->em->getRepository(User::class)->find($userId);
428
                if (!$user) {
429
                    continue;
430
                }
431
432
                $sheet = $sheetRepository->findOneBy([
433
                    'user' => $user,
434
                    'attendanceCalendar' => $calendar,
435
                ]);
436
437
                if ($sheet && null === $presence) {
438
                    $this->em->remove($sheet);
439
440
                    continue;
441
                }
442
443
                if (!$sheet && null === $presence) {
444
                    continue;
445
                }
446
447
                if (!$sheet) {
448
                    $sheet = new CAttendanceSheet();
449
                }
450
451
                $sheet->setUser($user)
452
                    ->setAttendanceCalendar($calendar)
453
                    ->setPresence($presence)
454
                    ->setSignature($signature)
455
                ;
456
457
                $this->em->persist($sheet);
458
459
                $this->em->flush();
460
461
                if (null !== $comment) {
462
                    $existingComment = $this->em->getRepository(CAttendanceResultComment::class)->findOneBy([
463
                        'attendanceSheetId' => $sheet->getIid(),
464
                        'userId' => $user->getId(),
465
                    ]);
466
467
                    if (!$existingComment) {
468
                        $existingComment = new CAttendanceResultComment();
469
                        $existingComment->setAttendanceSheetId($sheet->getIid());
470
                        $existingComment->setUserId($user->getId());
471
                        $existingComment->setAuthorUserId($this->getUser()->getId());
472
                    }
473
474
                    $existingComment->setComment($comment);
475
                    $existingComment->setUpdatedAt(new DateTime());
476
477
                    $this->em->persist($existingComment);
478
                }
479
            }
480
481
            $calendarIds = array_unique(array_column($attendanceData, 'calendarId'));
482
            foreach ($calendarIds as $calendarId) {
483
                $calendar = $this->attendanceCalendarRepository->find($calendarId);
484
                if ($calendar && !$calendar->getDoneAttendance()) {
485
                    $calendar->setDoneAttendance(true);
486
                    $this->em->persist($calendar);
487
                }
488
            }
489
490
            $calendars = $this->attendanceCalendarRepository->findBy(['iid' => $calendarIds]);
491
            $attendance = $calendars[0]->getAttendance();
492
            $this->updateAttendanceResults($attendance);
493
494
            $lasteditType = $calendars[0]->getDoneAttendance()
495
                ? 'UPDATED_ATTENDANCE_LOG_TYPE'
496
                : 'DONE_ATTENDANCE_LOG_TYPE';
497
498
            foreach ($calendars as $calendar) {
499
                $this->saveAttendanceLog($attendance, $lasteditType, $calendar);
500
            }
501
502
            $this->em->flush();
503
504
            return $this->json([
505
                'message' => $this->translator->trans('Attendance data and comments saved successfully'),
506
                'affectedRows' => $affectedRows,
507
            ]);
508
        } catch (Exception $e) {
509
            return $this->json(['error' => 'An error occurred: '.$e->getMessage()], 500);
510
        }
511
    }
512
513
    #[Route('/{id}/student-dates', name: 'attendance_student_dates', methods: ['GET'])]
514
    public function getStudentDates(int $id): JsonResponse
515
    {
516
        $user = $this->getUser();
517
        if (!$user) {
518
            return $this->json(['error' => 'Unauthorized'], 401);
519
        }
520
521
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
522
        if (!$attendance) {
523
            return $this->json(['error' => 'Attendance not found'], 404);
524
        }
525
526
        $dates = $attendance->getCalendars()->map(function (CAttendanceCalendar $calendar) use ($user) {
527
            $sheet = $calendar->getSheets()->filter(
528
                fn ($s) => $s->getUser()->getId() === $user->getId()
529
            )->first() ?: null;
530
531
            return [
532
                'id' => $calendar->getIid(),
533
                'label' => $calendar->getDateTime()->format('M d, Y - h:i A'),
534
                'done' => $calendar->getDoneAttendance(),
535
                'presence' => $sheet ? $sheet->getPresence() : null,
536
                'sheetId' => $sheet?->getIid(),
537
                'signature' => $sheet?->getSignature(),
538
            ];
539
        })->toArray();
540
541
        $attendanceData = [];
542
        $commentData = [];
543
        $signatureData = [];
544
545
        foreach ($dates as $item) {
546
            $key = $user->getId() . '-' . $item['id'];
547
            $attendanceData[$key] = $item['presence'];
548
            $signatureData[$key] = $item['signature'];
549
550
            if (!empty($item['sheetId'])) {
551
                $comment = $this->em->getRepository(CAttendanceResultComment::class)->findOneBy([
552
                    'attendanceSheetId' => $item['sheetId'],
553
                    'userId' => $user->getId(),
554
                ]);
555
                $commentData[$key] = $comment?->getComment();
556
            }
557
        }
558
559
        return $this->json([
560
            'attendanceDates' => array_map(fn ($d) => [
561
                'id' => $d['id'],
562
                'label' => $d['label'],
563
                'done' => $d['done'],
564
            ], $dates),
565
            'attendanceData' => $attendanceData,
566
            'commentData' => $commentData,
567
            'signatureData' => $signatureData,
568
        ]);
569
    }
570
571
    private function updateAttendanceResults(CAttendance $attendance): void
572
    {
573
        $sheets = $attendance->getCalendars()->map(fn ($calendar) => $calendar->getSheets())->toArray();
574
        $results = [];
575
576
        foreach ($sheets as $calendarSheets) {
577
            foreach ($calendarSheets as $sheet) {
578
                $userId = $sheet->getUser()->getId();
579
                $results[$userId] = ($results[$userId] ?? 0) + $sheet->getPresence();
580
            }
581
        }
582
583
        foreach ($results as $userId => $score) {
584
            $user = $this->em->getRepository(User::class)->find($userId);
585
            if (!$user) {
586
                continue;
587
            }
588
589
            $result = $this->em->getRepository(CAttendanceResult::class)->findOneBy([
590
                'user' => $user,
591
                'attendance' => $attendance,
592
            ]);
593
594
            if (!$result) {
595
                $result = new CAttendanceResult();
596
                $result->setUser($user);
597
                $result->setAttendance($attendance);
598
            }
599
600
            $result->setScore((int) $score);
601
            $this->em->persist($result);
602
        }
603
    }
604
605
    private function saveAttendanceLog(CAttendance $attendance, string $lasteditType, CAttendanceCalendar $calendar): void
606
    {
607
        $log = new CAttendanceSheetLog();
608
        $log->setAttendance($attendance)
609
            ->setLasteditDate(new DateTime())
610
            ->setLasteditType($lasteditType)
611
            ->setCalendarDateValue($calendar->getDateTime())
612
            ->setUser($this->getUser())
613
        ;
614
615
        $this->em->persist($log);
616
    }
617
}
618