getSecurityUser(); $tenantId = TenantId::fromString($user->tenantId()); $homeworkId = HomeworkId::fromString($id); $homework = $this->homeworkRepository->findById($homeworkId, $tenantId); if ($homework === null) { throw new NotFoundHttpException('Devoir non trouvé.'); } if ((string) $homework->teacherId !== $user->userId()) { throw new AccessDeniedHttpException('Accès non autorisé.'); } $submissions = $this->submissionRepository->findByHomework($homeworkId, $tenantId); $students = $this->classStudentsReader->studentsInClass((string) $homework->classId, $tenantId); $studentNameMap = []; foreach ($students as $student) { /** @var string $studentId */ $studentId = $student['id']; /** @var string $studentName */ $studentName = $student['name']; $studentNameMap[$studentId] = $studentName; } $submittedStudentIds = array_map( static fn (HomeworkSubmission $s): string => (string) $s->studentId, $submissions, ); $rows = array_map( static fn (HomeworkSubmission $s): array => [ 'id' => (string) $s->id, 'studentId' => (string) $s->studentId, 'studentName' => $studentNameMap[(string) $s->studentId] ?? '', 'status' => $s->status->value, 'submittedAt' => $s->submittedAt?->format(DateTimeImmutable::ATOM), 'createdAt' => $s->createdAt->format(DateTimeImmutable::ATOM), ], $submissions, ); foreach ($students as $student) { /** @var string $sId */ $sId = $student['id']; if (!in_array($sId, $submittedStudentIds, true)) { /** @var string $sName */ $sName = $student['name']; $rows[] = [ 'id' => null, 'studentId' => $sId, 'studentName' => $sName, 'status' => 'not_submitted', 'submittedAt' => null, 'createdAt' => null, ]; } } return new JsonResponse(['data' => $rows]); } #[Route('/api/homework/{id}/submissions/stats', name: 'api_teacher_submission_stats', methods: ['GET'])] public function stats(string $id): JsonResponse { $user = $this->getSecurityUser(); $tenantId = TenantId::fromString($user->tenantId()); $homeworkId = HomeworkId::fromString($id); $homework = $this->homeworkRepository->findById($homeworkId, $tenantId); if ($homework === null) { throw new NotFoundHttpException('Devoir non trouvé.'); } if ((string) $homework->teacherId !== $user->userId()) { throw new AccessDeniedHttpException('Accès non autorisé.'); } $submissions = $this->submissionRepository->findByHomework($homeworkId, $tenantId); $students = $this->classStudentsReader->studentsInClass((string) $homework->classId, $tenantId); $submittedStudentIds = array_map( static fn (HomeworkSubmission $s): string => (string) $s->studentId, array_filter( $submissions, static fn (HomeworkSubmission $s): bool => !$s->status->estModifiable(), ), ); $allStudentIds = array_column($students, 'id'); $missingStudentIds = array_diff($allStudentIds, $submittedStudentIds); $studentNameMap = []; foreach ($students as $student) { /** @var string $sId */ $sId = $student['id']; /** @var string $sName */ $sName = $student['name']; $studentNameMap[$sId] = $sName; } $missingStudents = array_values(array_map( static fn (string $studentId): array => [ 'id' => $studentId, 'name' => $studentNameMap[$studentId] ?? '', ], array_filter( $missingStudentIds, static fn (string $studentId): bool => !in_array($studentId, $submittedStudentIds, true), ), )); return new JsonResponse([ 'data' => [ 'totalStudents' => count($students), 'submittedCount' => count($submittedStudentIds), 'missingStudents' => $missingStudents, ], ]); } #[Route('/api/homework/{id}/submissions/{submissionId}', name: 'api_teacher_submission_detail', methods: ['GET'])] public function detail(string $id, string $submissionId): JsonResponse { $user = $this->getSecurityUser(); $tenantId = TenantId::fromString($user->tenantId()); $homeworkId = HomeworkId::fromString($id); $homework = $this->homeworkRepository->findById($homeworkId, $tenantId); if ($homework === null) { throw new NotFoundHttpException('Devoir non trouvé.'); } if ((string) $homework->teacherId !== $user->userId()) { throw new AccessDeniedHttpException('Accès non autorisé.'); } $submission = $this->submissionRepository->findById( HomeworkSubmissionId::fromString($submissionId), $tenantId, ); if ($submission === null || !$submission->homeworkId->equals($homeworkId)) { throw new NotFoundHttpException('Rendu non trouvé.'); } $attachments = $this->attachmentRepository->findBySubmissionId($submission->id); $students = $this->classStudentsReader->studentsInClass((string) $homework->classId, $tenantId); $studentName = ''; foreach ($students as $student) { if ($student['id'] === (string) $submission->studentId) { /** @var string $name */ $name = $student['name']; $studentName = $name; break; } } return new JsonResponse([ 'data' => [ 'id' => (string) $submission->id, 'studentId' => (string) $submission->studentId, 'studentName' => $studentName, 'responseHtml' => $submission->responseHtml, 'status' => $submission->status->value, 'submittedAt' => $submission->submittedAt?->format(DateTimeImmutable::ATOM), 'createdAt' => $submission->createdAt->format(DateTimeImmutable::ATOM), 'attachments' => array_map( static fn (SubmissionAttachment $a): array => [ 'id' => (string) $a->id, 'filename' => $a->filename, 'fileSize' => $a->fileSize, 'mimeType' => $a->mimeType, ], $attachments, ), ], ]); } #[Route('/api/homework/{homeworkId}/submissions/{submissionId}/attachments/{attachmentId}', name: 'api_teacher_submission_attachment_download', methods: ['GET'])] public function downloadAttachment(string $homeworkId, string $submissionId, string $attachmentId): StreamedResponse { $user = $this->getSecurityUser(); $tenantId = TenantId::fromString($user->tenantId()); $homework = $this->homeworkRepository->findById(HomeworkId::fromString($homeworkId), $tenantId); if ($homework === null) { throw new NotFoundHttpException('Devoir non trouvé.'); } if ((string) $homework->teacherId !== $user->userId()) { throw new AccessDeniedHttpException('Accès non autorisé.'); } $submission = $this->submissionRepository->findById( HomeworkSubmissionId::fromString($submissionId), $tenantId, ); if ($submission === null || !$submission->homeworkId->equals(HomeworkId::fromString($homeworkId))) { throw new NotFoundHttpException('Rendu non trouvé.'); } $attachments = $this->attachmentRepository->findBySubmissionId($submission->id); foreach ($attachments as $attachment) { if ((string) $attachment->id === $attachmentId) { try { $stream = $this->fileStorage->readStream($attachment->filePath); } catch (RuntimeException) { throw new NotFoundHttpException('Pièce jointe non trouvée.'); } $response = new StreamedResponse(static function () use ($stream): void { try { fpassthru($stream); } finally { fclose($stream); } }); $disposition = HeaderUtils::makeDisposition( HeaderUtils::DISPOSITION_INLINE, $attachment->filename, ); $response->headers->set('Content-Type', $attachment->mimeType); $response->headers->set('Content-Disposition', $disposition); $response->headers->set('Content-Length', (string) $attachment->fileSize); return $response; } } throw new NotFoundHttpException('Pièce jointe non trouvée.'); } private function getSecurityUser(): SecurityUser { $user = $this->security->getUser(); if (!$user instanceof SecurityUser) { throw new AccessDeniedHttpException('Authentification requise.'); } return $user; } }