Passed
Push — master ( c4b679...7a8098 )
by
unknown
16:14 queued 07:34
created

AttendanceController   F

Complexity

Total Complexity 100

Size/Duplication

Total Lines 664
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 389
c 2
b 0
f 0
dl 0
loc 664
rs 2
wmc 100

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A generateQrCode() 0 35 5
B getUsersWithFaults() 0 55 9
B getDateSheet() 0 46 7
B exportToXls() 0 75 11
B getFullAttendanceData() 0 31 8
F saveAttendanceSheet() 0 118 22
F exportToPdf() 0 140 19
A listWithDoneCount() 0 28 4
B getStudentDates() 0 60 7
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\CourseRelUser;
9
use Chamilo\CoreBundle\Entity\Session;
10
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
11
use Chamilo\CoreBundle\Entity\User;
12
use Chamilo\CoreBundle\Repository\Node\UserRepository;
13
use Chamilo\CourseBundle\Entity\CAttendance;
14
use Chamilo\CourseBundle\Entity\CAttendanceCalendar;
15
use Chamilo\CourseBundle\Entity\CAttendanceResult;
16
use Chamilo\CourseBundle\Entity\CAttendanceResultComment;
17
use Chamilo\CourseBundle\Entity\CAttendanceSheet;
18
use Chamilo\CourseBundle\Entity\CAttendanceSheetLog;
19
use Chamilo\CourseBundle\Repository\CAttendanceCalendarRepository;
20
use Chamilo\CourseBundle\Repository\CAttendanceSheetRepository;
21
use DateTime;
22
use Doctrine\ORM\EntityManagerInterface;
23
use Endroid\QrCode\Builder\Builder;
24
use Exception;
25
use Mpdf\Mpdf;
26
use Mpdf\MpdfException;
27
use Mpdf\Output\Destination;
28
use PhpOffice\PhpSpreadsheet\Spreadsheet;
29
use PhpOffice\PhpSpreadsheet\Writer\Xls;
30
use RuntimeException;
31
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
32
use Symfony\Component\HttpFoundation\JsonResponse;
33
use Symfony\Component\HttpFoundation\Request;
34
use Symfony\Component\HttpFoundation\Response;
35
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
36
use Symfony\Component\HttpFoundation\StreamedResponse;
37
use Symfony\Component\Routing\Attribute\Route;
38
use Symfony\Contracts\Translation\TranslatorInterface;
39
40
#[Route('/attendance')]
41
class AttendanceController extends AbstractController
42
{
43
    public function __construct(
44
        private readonly CAttendanceCalendarRepository $attendanceCalendarRepository,
45
        private readonly EntityManagerInterface $em,
46
        private readonly TranslatorInterface $translator
47
    ) {}
48
49
    #[Route('/full-data', name: 'chamilo_core_attendance_get_full_data', methods: ['GET'])]
50
    public function getFullAttendanceData(Request $request): JsonResponse
51
    {
52
        $attendanceId = (int) $request->query->get('attendanceId', 0);
53
54
        if (!$attendanceId) {
55
            return $this->json(['error' => 'Attendance ID is required'], 400);
56
        }
57
58
        $data = $this->attendanceCalendarRepository->findAttendanceWithData($attendanceId);
59
60
        if (isset($data['attendanceDates']) && \is_array($data['attendanceDates'])) {
61
            foreach ($data['attendanceDates'] as &$date) {
62
                if (!isset($date['id'])) {
63
                    continue;
64
                }
65
66
                $calendar = $this->attendanceCalendarRepository->find((int) $date['id']);
67
                if (!$calendar instanceof CAttendanceCalendar) {
68
                    continue;
69
                }
70
71
                $duration = $calendar->getDuration();
72
                if (null !== $duration) {
73
                    $date['duration'] = $duration;
74
                }
75
            }
76
            unset($date);
77
        }
78
79
        return $this->json($data, 200);
80
    }
81
82
    #[Route('/{id}/users/context', name: 'chamilo_core_get_users_with_faults', methods: ['GET'])]
83
    public function getUsersWithFaults(
84
        int $id,
85
        Request $request,
86
        UserRepository $userRepository,
87
        CAttendanceCalendarRepository $calendarRepository,
88
        CAttendanceSheetRepository $sheetRepository
89
    ): JsonResponse {
90
        $courseId = (int) $request->query->get('courseId', 0);
91
        $sessionId = $request->query->get('sessionId') ? (int) $request->query->get('sessionId') : null;
92
        $groupId = $request->query->get('groupId') ? (int) $request->query->get('groupId') : null;
93
94
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
95
        if (!$attendance) {
96
            return $this->json(['error' => 'Attendance not found'], 404);
97
        }
98
99
        $calendars = $attendance->getCalendars();
100
        $totalCalendars = \count($calendars);
101
102
        $users = $userRepository->findUsersByContext($courseId, $sessionId, $groupId);
103
104
        $formattedUsers = array_map(function ($user) use ($userRepository, $sheetRepository, $calendars, $totalCalendars) {
105
            $absences = 0;
106
107
            foreach ($calendars as $calendar) {
108
                $sheet = $sheetRepository->findOneBy([
109
                    'user' => $user,
110
                    'attendanceCalendar' => $calendar,
111
                ]);
112
113
                if (!$sheet || null === $sheet->getPresence()) {
114
                    continue;
115
                }
116
117
                // Only count full absences, same logic as in PDF/XLS export
118
                if (CAttendanceSheet::ABSENT === $sheet->getPresence()) {
119
                    $absences++;
120
                }
121
            }
122
123
            $percentage = $totalCalendars > 0 ? \round(($absences * 100) / $totalCalendars) : 0;
124
125
            return [
126
                'id' => $user->getId(),
127
                'firstname' => $user->getFirstname(),
128
                'lastname' => $user->getLastname(),
129
                'email' => $user->getEmail(),
130
                'username' => $user->getUsername(),
131
                'photo' => $userRepository->getUserPicture($user->getId()),
132
                'notAttended' => $absences.'/'.$totalCalendars." ({$percentage}%)",
133
            ];
134
        }, $users);
135
136
        return $this->json($formattedUsers, 200);
137
    }
138
139
    #[Route('/list_with_done_count', name: 'attendance_list_with_done_count', methods: ['GET'])]
140
    public function listWithDoneCount(Request $request): JsonResponse
141
    {
142
        $courseId = (int) $request->query->get('cid', 0);
143
        $sessionId = $request->query->get('sid') ? (int) $request->query->get('sid') : null;
144
        $groupId = $request->query->get('gid') ? (int) $request->query->get('gid') : null;
145
        $parentNode = (int) $request->query->get('resourceNode.parent', 0);
146
147
        $attendances = $this->em->getRepository(CAttendance::class)->findBy([
148
            'active' => 1,
149
        ]);
150
151
        $result = [];
152
        foreach ($attendances as $attendance) {
153
            $doneCount = $this->attendanceCalendarRepository->countDoneAttendanceByAttendanceAndGroup($attendance->getIid(), $groupId);
154
155
            $result[] = [
156
                'id' => $attendance->getIid(),
157
                'title' => $attendance->getTitle(),
158
                'description' => $attendance->getDescription(),
159
                'attendanceWeight' => $attendance->getAttendanceWeight(),
160
                'attendanceQualifyTitle' => $attendance->getAttendanceQualifyTitle(),
161
                'resourceLinkListFromEntity' => $attendance->getResourceLinkListFromEntity(),
162
                'doneCalendars' => $doneCount,
163
            ];
164
        }
165
166
        return $this->json($result);
167
    }
168
169
    #[Route('/{id}/export/pdf', name: 'attendance_export_pdf', methods: ['GET'])]
170
    public function exportToPdf(int $id, Request $request): Response
171
    {
172
        $courseId = (int) $request->query->get('cid');
173
        $sessionId = ((int) $request->query->get('sid')) ?: null;
174
        $groupId = ((int) $request->query->get('gid')) ?: null;
175
176
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
177
        if (!$attendance) {
178
            throw $this->createNotFoundException('Attendance not found');
179
        }
180
181
        $calendars = $attendance->getCalendars();
182
        $totalCalendars = \count($calendars);
183
184
        $students = $this->em->getRepository(User::class)->findUsersByContext($courseId, $sessionId, $groupId);
185
        $sheetRepo = $this->em->getRepository(CAttendanceSheet::class);
186
187
        $course = $this->em->getRepository(Course::class)->find($courseId);
188
        $teacher = null;
189
190
        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...
191
            $session = $this->em->getRepository(Session::class)->find($sessionId);
192
            $rel = $session?->getCourseCoachesSubscriptions()
193
                ->filter(fn ($rel) => $rel->getCourse()?->getId() === $courseId)
194
                ->first()
195
            ;
196
197
            $teacher = $rel instanceof SessionRelCourseRelUser
198
                ? $rel->getUser()?->getFullName()
199
                : null;
200
        } else {
201
            $rel = $course?->getTeachersSubscriptions()?->first();
202
203
            $teacher = $rel instanceof CourseRelUser
204
                ? $rel->getUser()?->getFullName()
205
                : null;
206
        }
207
208
        // Header
209
        $dataTable = [];
210
        $header = ['#', 'Last Name', 'First Name', 'Not Attended'];
211
        foreach ($calendars as $calendar) {
212
            $header[] = $calendar->getDateTime()->format('d/m H:i');
213
        }
214
        $dataTable[] = $header;
215
216
        // Rows
217
        $count = 1;
218
        $stateLabels = CAttendanceSheet::getPresenceLabels();
219
220
        foreach ($students as $student) {
221
            $row = [
222
                $count++,
223
                $student->getLastname(),
224
                $student->getFirstname(),
225
                '',
226
            ];
227
228
            $absences = 0;
229
            foreach ($calendars as $calendar) {
230
                $sheetEntity = $sheetRepo->findOneBy([
231
                    'user' => $student,
232
                    'attendanceCalendar' => $calendar,
233
                ]);
234
235
                if (!$sheetEntity || null === $sheetEntity->getPresence()) {
236
                    $row[] = '';
237
238
                    continue;
239
                }
240
241
                $presence = $sheetEntity->getPresence();
242
                $row[] = $stateLabels[$presence] ?? 'NP';
243
244
                if (CAttendanceSheet::ABSENT === $presence) {
245
                    $absences++;
246
                }
247
            }
248
249
            $percentage = $totalCalendars > 0 ? round(($absences * 100) / $totalCalendars) : 0;
250
            $row[3] = "$absences/$totalCalendars ($percentage%)";
251
            $dataTable[] = $row;
252
        }
253
254
        // Render HTML
255
        $html = '
256
            <style>
257
                body { font-family: sans-serif; font-size: 12px; }
258
                h2 { text-align: center; margin-bottom: 5px; }
259
                table.meta { margin: 0 auto 10px auto; width: 80%; }
260
                table.meta td { padding: 2px 5px; }
261
                table.attendance { border-collapse: collapse; width: 100%; }
262
                .attendance th, .attendance td { border: 1px solid #000; padding: 4px; text-align: center; }
263
                .np { color: red; font-weight: bold; }
264
            </style>
265
266
            <h2>'.htmlspecialchars($attendance->getTitle()).'</h2>
267
268
            <table class="meta">
269
                <tr><td><strong>Trainer:</strong></td><td>'.htmlspecialchars($teacher ?? '-').'</td></tr>
270
                <tr><td><strong>Course:</strong></td><td>'.htmlspecialchars($course?->getTitleAndCode() ?? '-').'</td></tr>
271
                <tr><td><strong>Date:</strong></td><td>'.date('F d, Y \a\t h:i A').'</td></tr>
272
            </table>
273
274
            <table class="attendance">
275
            <tr>';
276
        foreach ($dataTable[0] as $cell) {
277
            $html .= '<th>'.htmlspecialchars((string) $cell).'</th>';
278
        }
279
        $html .= '</tr>';
280
281
        foreach (\array_slice($dataTable, 1) as $row) {
282
            $html .= '<tr>';
283
            foreach ($row as $cell) {
284
                $class = 'NP' === $cell ? ' class="np"' : '';
285
                $html .= "<td$class>".htmlspecialchars((string) $cell).'</td>';
286
            }
287
            $html .= '</tr>';
288
        }
289
290
        $html .= '</table>';
291
292
        try {
293
            $mpdf = new Mpdf([
294
                'orientation' => 'L',
295
                'tempDir' => api_get_path(SYS_ARCHIVE_PATH).'mpdf/',
296
            ]);
297
            $mpdf->WriteHTML($html);
298
299
            return new Response(
300
                $mpdf->Output('', Destination::INLINE),
301
                200,
302
                [
303
                    'Content-Type' => 'application/pdf',
304
                    'Content-Disposition' => 'attachment; filename="attendance-'.$id.'.pdf"',
305
                ]
306
            );
307
        } catch (MpdfException $e) {
308
            throw new RuntimeException('Failed to generate PDF: '.$e->getMessage(), 500, $e);
309
        }
310
    }
311
312
    #[Route('/{id}/export/xls', name: 'attendance_export_xls', methods: ['GET'])]
313
    public function exportToXls(int $id, Request $request): Response
314
    {
315
        $courseId = (int) $request->query->get('cid');
316
        $sessionId = $request->query->get('sid') ? (int) $request->query->get('sid') : null;
317
        $groupId = $request->query->get('gid') ? (int) $request->query->get('gid') : null;
318
319
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
320
        if (!$attendance) {
321
            throw $this->createNotFoundException('Attendance not found');
322
        }
323
324
        $calendars = $attendance->getCalendars();
325
        $totalCalendars = \count($calendars);
326
        $students = $this->em->getRepository(User::class)->findUsersByContext($courseId, $sessionId, $groupId);
327
        $sheetRepo = $this->em->getRepository(CAttendanceSheet::class);
328
329
        $stateLabels = CAttendanceSheet::getPresenceLabels();
330
331
        $spreadsheet = new Spreadsheet();
332
        $sheet = $spreadsheet->getActiveSheet();
333
        $sheet->setTitle('Attendance');
334
335
        // Header
336
        $headers = ['#', 'Last Name', 'First Name', 'Not Attended'];
337
        foreach ($calendars as $calendar) {
338
            $headers[] = $calendar->getDateTime()->format('d/m H:i');
339
        }
340
        $sheet->fromArray($headers, null, 'A1');
341
342
        // Rows
343
        $rowNumber = 2;
344
        $count = 1;
345
        foreach ($students as $student) {
346
            $row = [$count++, $student->getLastname(), $student->getFirstname()];
347
            $absences = 0;
348
349
            foreach ($calendars as $calendar) {
350
                $sheetEntity = $sheetRepo->findOneBy([
351
                    'user' => $student,
352
                    'attendanceCalendar' => $calendar,
353
                ]);
354
355
                if (!$sheetEntity || null === $sheetEntity->getPresence()) {
356
                    $row[] = '';
357
358
                    continue;
359
                }
360
361
                $presence = $sheetEntity->getPresence();
362
                $row[] = $stateLabels[$presence] ?? 'NP';
363
364
                if (CAttendanceSheet::ABSENT === $presence) {
365
                    $absences++;
366
                }
367
            }
368
369
            $percentage = $totalCalendars > 0 ? round(($absences * 100) / $totalCalendars) : 0;
370
            array_splice($row, 3, 0, "$absences/$totalCalendars ($percentage%)");
371
372
            $sheet->fromArray($row, null, 'A'.$rowNumber++);
373
        }
374
375
        // Output
376
        $writer = new Xls($spreadsheet);
377
        $response = new StreamedResponse(fn () => $writer->save('php://output'));
378
        $disposition = $response->headers->makeDisposition(
379
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
380
            "attendance-$id.xls"
381
        );
382
383
        $response->headers->set('Content-Type', 'application/vnd.ms-excel');
384
        $response->headers->set('Content-Disposition', $disposition);
385
386
        return $response;
387
    }
388
389
    #[Route('/{id}/qrcode', name: 'attendance_qrcode', methods: ['GET'])]
390
    public function generateQrCode(int $id, Request $request): Response
391
    {
392
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
393
        if (!$attendance) {
394
            throw $this->createNotFoundException('Attendance not found');
395
        }
396
397
        $resourceNodeId = $attendance->getResourceNode()?->getParent()?->getId();
398
        if (!$resourceNodeId) {
399
            throw new RuntimeException('Missing resourceNode for course');
400
        }
401
402
        $sid = $request->query->get('sid');
403
        $gid = $request->query->get('gid');
404
405
        $query = 'readonly=1';
406
        if ($sid) {
407
            $query .= "&sid=$sid";
408
        }
409
        if ($gid) {
410
            $query .= "&gid=$gid";
411
        }
412
413
        $url = "/resources/attendance/$resourceNodeId/$id/sheet-list?$query";
414
        $fullUrl = $request->getSchemeAndHttpHost().$url;
415
416
        $result = Builder::create()
417
            ->data($fullUrl)
418
            ->size(300)
419
            ->margin(10)
420
            ->build()
421
        ;
422
423
        return new Response($result->getString(), 200, ['Content-Type' => $result->getMimeType()]);
424
    }
425
426
    #[Route('/sheet/save', name: 'chamilo_core_attendance_sheet_save', methods: ['POST'])]
427
    public function saveAttendanceSheet(
428
        Request $request,
429
        UserRepository $userRepository,
430
        CAttendanceSheetRepository $sheetRepository
431
    ): JsonResponse {
432
        $data = json_decode($request->getContent(), true);
433
434
        if (empty($data['attendanceData']) || empty($data['courseId'])) {
435
            return $this->json(['error' => 'Missing required parameters'], 400);
436
        }
437
438
        $attendanceData = $data['attendanceData'];
439
        $courseId = (int) $data['courseId'];
440
        $sessionId = isset($data['sessionId']) ? (int) $data['sessionId'] : null;
441
        $groupId = isset($data['groupId']) ? (int) $data['groupId'] : null;
442
443
        $usersInCourse = $userRepository->findUsersByContext($courseId, $sessionId, $groupId);
444
        $userIdsInCourse = array_map(fn (User $user) => $user->getId(), $usersInCourse);
445
446
        $affectedRows = 0;
447
448
        try {
449
            foreach ($attendanceData as $entry) {
450
                $userId = (int) $entry['userId'];
451
                $calendarId = (int) $entry['calendarId'];
452
                $presence = \array_key_exists('presence', $entry) ? $entry['presence'] : null;
453
                $signature = $entry['signature'] ?? null;
454
                $comment = $entry['comment'] ?? null;
455
456
                $calendar = $this->attendanceCalendarRepository->find($calendarId);
457
                if (!$calendar) {
458
                    return $this->json(['error' => "Attendance calendar with ID $calendarId not found"], 404);
459
                }
460
461
                $user = $this->em->getRepository(User::class)->find($userId);
462
                if (!$user) {
463
                    continue;
464
                }
465
466
                $sheet = $sheetRepository->findOneBy([
467
                    'user' => $user,
468
                    'attendanceCalendar' => $calendar,
469
                ]);
470
471
                if ($sheet && null === $presence) {
472
                    $this->em->remove($sheet);
473
474
                    continue;
475
                }
476
477
                if (!$sheet && null === $presence) {
478
                    continue;
479
                }
480
481
                if (!$sheet) {
482
                    $sheet = new CAttendanceSheet();
483
                }
484
485
                $sheet->setUser($user)
486
                    ->setAttendanceCalendar($calendar)
487
                    ->setPresence($presence)
488
                    ->setSignature($signature)
489
                ;
490
491
                $this->em->persist($sheet);
492
493
                $this->em->flush();
494
495
                if (null !== $comment) {
496
                    $existingComment = $this->em->getRepository(CAttendanceResultComment::class)->findOneBy([
497
                        'attendanceSheetId' => $sheet->getIid(),
498
                        'userId' => $user->getId(),
499
                    ]);
500
501
                    if (!$existingComment) {
502
                        $existingComment = new CAttendanceResultComment();
503
                        $existingComment->setAttendanceSheetId($sheet->getIid());
504
                        $existingComment->setUserId($user->getId());
505
                        $existingComment->setAuthorUserId($this->getUser()->getId());
506
                    }
507
508
                    $existingComment->setComment($comment);
509
                    $existingComment->setUpdatedAt(new DateTime());
510
511
                    $this->em->persist($existingComment);
512
                }
513
            }
514
515
            $calendarIds = array_unique(array_column($attendanceData, 'calendarId'));
516
            foreach ($calendarIds as $calendarId) {
517
                $calendar = $this->attendanceCalendarRepository->find($calendarId);
518
                if ($calendar && !$calendar->getDoneAttendance()) {
519
                    $calendar->setDoneAttendance(true);
520
                    $this->em->persist($calendar);
521
                }
522
            }
523
524
            $calendars = $this->attendanceCalendarRepository->findBy(['iid' => $calendarIds]);
525
            $attendance = $calendars[0]->getAttendance();
526
            $this->updateAttendanceResults($attendance);
527
528
            $lasteditType = $calendars[0]->getDoneAttendance()
529
                ? 'UPDATED_ATTENDANCE_LOG_TYPE'
530
                : 'DONE_ATTENDANCE_LOG_TYPE';
531
532
            foreach ($calendars as $calendar) {
533
                $this->saveAttendanceLog($attendance, $lasteditType, $calendar);
534
            }
535
536
            $this->em->flush();
537
538
            return $this->json([
539
                'message' => $this->translator->trans('Attendance data and comments saved successfully'),
540
                'affectedRows' => $affectedRows,
541
            ]);
542
        } catch (Exception $e) {
543
            return $this->json(['error' => 'An error occurred: '.$e->getMessage()], 500);
544
        }
545
    }
546
547
    #[Route('/{id}/student-dates', name: 'attendance_student_dates', methods: ['GET'])]
548
    public function getStudentDates(int $id): JsonResponse
549
    {
550
        $user = $this->getUser();
551
        if (!$user) {
552
            return $this->json(['error' => 'Unauthorized'], 401);
553
        }
554
555
        $attendance = $this->em->getRepository(CAttendance::class)->find($id);
556
        if (!$attendance) {
557
            return $this->json(['error' => 'Attendance not found'], 404);
558
        }
559
560
        $dates = $attendance->getCalendars()->map(function (CAttendanceCalendar $calendar) use ($user) {
561
            $sheet = $calendar->getSheets()->filter(
562
                fn ($s) => $s->getUser()->getId() === $user->getId()
563
            )->first() ?: null;
564
565
            return [
566
                'id' => $calendar->getIid(),
567
                'label' => $calendar->getDateTime()->format('M d, Y - h:i A'),
568
                'done' => $calendar->getDoneAttendance(),
569
                'presence' => $sheet ? $sheet->getPresence() : null,
570
                'sheetId' => $sheet?->getIid(),
571
                'signature' => $sheet?->getSignature(),
572
                'duration' => $calendar->getDuration(),
573
            ];
574
        })->toArray();
575
576
        $attendanceData = [];
577
        $commentData = [];
578
        $signatureData = [];
579
580
        foreach ($dates as $item) {
581
            $key = $user->getId().'-'.$item['id'];
582
            $attendanceData[$key] = $item['presence'];
583
            $signatureData[$key] = $item['signature'];
584
585
            if (!empty($item['sheetId'])) {
586
                $comment = $this->em->getRepository(CAttendanceResultComment::class)->findOneBy([
587
                    'attendanceSheetId' => $item['sheetId'],
588
                    'userId' => $user->getId(),
589
                ]);
590
                $commentData[$key] = $comment?->getComment();
591
            }
592
        }
593
594
        return $this->json([
595
            'attendanceDates' => array_map(
596
                static fn ($d) => [
597
                    'id' => $d['id'],
598
                    'label' => $d['label'],
599
                    'done' => $d['done'],
600
                    'duration' => $d['duration'],
601
                ],
602
                $dates
603
            ),
604
            'attendanceData' => $attendanceData,
605
            'commentData' => $commentData,
606
            'signatureData' => $signatureData,
607
        ]);
608
    }
609
610
    #[Route('/{attendanceId}/date/{calendarId}/sheet', name: 'attendance_date_sheet', methods: ['GET'])]
611
    public function getDateSheet(
612
        int $attendanceId,
613
        int $calendarId,
614
        Request $request,
615
        UserRepository $userRepository,
616
        CAttendanceCalendarRepository $calendarRepo,
617
        CAttendanceSheetRepository $sheetRepo
618
    ): JsonResponse {
619
        $cid = (int) $request->query->get('cid', 0);
620
        $sid = $request->query->get('sid') ? (int) $request->query->get('sid') : null;
621
        $gid = $request->query->get('gid') ? (int) $request->query->get('gid') : null;
622
623
        $calendar = $calendarRepo->find($calendarId);
624
        if (!$calendar || $calendar->getAttendance()?->getIid() !== $attendanceId) {
625
            return $this->json(['error' => 'Calendar not found'], 404);
626
        }
627
628
        $users = $userRepository->findUsersByContext($cid, $sid, $gid);
629
        $presence = [];
630
        $comments = [];
631
        $signatures = [];
632
633
        foreach ($users as $u) {
634
            $sheet = $sheetRepo->findOneBy(['user' => $u, 'attendanceCalendar' => $calendar]);
635
            $k = $u->getId().'-'.$calendarId;
636
            if ($sheet) {
637
                $presence[$k] = $sheet->getPresence();
638
                $signatures[$k] = $sheet->getSignature();
639
            }
640
        }
641
642
        $formatted = array_map(static fn ($u) => [
643
            'id' => $u->getId(),
644
            'firstName' => $u->getFirstname(),
645
            'lastName'  => $u->getLastname(),
646
            'photo'     => $userRepository->getUserPicture($u->getId()),
647
        ], $users);
648
649
        return $this->json([
650
            'dateLabel'  => $calendar->getDateTime()->format('M d, Y - h:i A'),
651
            'isLocked'   => (bool) $calendar->getDoneAttendance() === true,
652
            'users'      => $formatted,
653
            'presence'   => $presence,
654
            'comments'   => $comments,
655
            'signatures' => $signatures,
656
        ]);
657
    }
658
659
    private function updateAttendanceResults(CAttendance $attendance): void
660
    {
661
        $sheets = $attendance->getCalendars()->map(fn ($calendar) => $calendar->getSheets())->toArray();
662
        $results = [];
663
664
        foreach ($sheets as $calendarSheets) {
665
            foreach ($calendarSheets as $sheet) {
666
                $userId = $sheet->getUser()->getId();
667
                $results[$userId] = ($results[$userId] ?? 0) + $sheet->getPresence();
668
            }
669
        }
670
671
        foreach ($results as $userId => $score) {
672
            $user = $this->em->getRepository(User::class)->find($userId);
673
            if (!$user) {
674
                continue;
675
            }
676
677
            $result = $this->em->getRepository(CAttendanceResult::class)->findOneBy([
678
                'user' => $user,
679
                'attendance' => $attendance,
680
            ]);
681
682
            if (!$result) {
683
                $result = new CAttendanceResult();
684
                $result->setUser($user);
685
                $result->setAttendance($attendance);
686
            }
687
688
            $result->setScore((int) $score);
689
            $this->em->persist($result);
690
        }
691
    }
692
693
    private function saveAttendanceLog(CAttendance $attendance, string $lasteditType, CAttendanceCalendar $calendar): void
694
    {
695
        $log = new CAttendanceSheetLog();
696
        $log->setAttendance($attendance)
697
            ->setLasteditDate(new DateTime())
698
            ->setLasteditType($lasteditType)
699
            ->setCalendarDateValue($calendar->getDateTime())
700
            ->setUser($this->getUser())
701
        ;
702
703
        $this->em->persist($log);
704
    }
705
}
706