feat: Permettre à l'élève de rendre un devoir avec réponse texte et pièces jointes
L'élève peut désormais répondre à un devoir via un éditeur WYSIWYG, joindre des fichiers (PDF, JPEG, PNG, DOCX), sauvegarder un brouillon et soumettre définitivement son rendu. Le système détecte automatiquement les soumissions en retard par rapport à la date d'échéance. Côté enseignant, une page dédiée affiche la liste complète des élèves avec leur statut (soumis, en retard, brouillon, non rendu), le détail de chaque rendu avec ses pièces jointes téléchargeables, et les statistiques de rendus par classe.
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Model\HomeworkSubmission;
|
||||
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Scolarite\Domain\Event\DevoirRendu;
|
||||
use App\Scolarite\Domain\Event\DevoirRenduEnRetard;
|
||||
use App\Scolarite\Domain\Exception\RenduDejaSoumisException;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkId;
|
||||
use App\Shared\Domain\AggregateRoot;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
|
||||
final class HomeworkSubmission extends AggregateRoot
|
||||
{
|
||||
public private(set) DateTimeImmutable $updatedAt;
|
||||
|
||||
private function __construct(
|
||||
public private(set) HomeworkSubmissionId $id,
|
||||
public private(set) TenantId $tenantId,
|
||||
public private(set) HomeworkId $homeworkId,
|
||||
public private(set) UserId $studentId,
|
||||
public private(set) ?string $responseHtml,
|
||||
public private(set) SubmissionStatus $status,
|
||||
public private(set) ?DateTimeImmutable $submittedAt,
|
||||
public private(set) DateTimeImmutable $createdAt,
|
||||
) {
|
||||
$this->updatedAt = $createdAt;
|
||||
}
|
||||
|
||||
public static function creerBrouillon(
|
||||
TenantId $tenantId,
|
||||
HomeworkId $homeworkId,
|
||||
UserId $studentId,
|
||||
?string $responseHtml,
|
||||
DateTimeImmutable $now,
|
||||
): self {
|
||||
return new self(
|
||||
id: HomeworkSubmissionId::generate(),
|
||||
tenantId: $tenantId,
|
||||
homeworkId: $homeworkId,
|
||||
studentId: $studentId,
|
||||
responseHtml: $responseHtml,
|
||||
status: SubmissionStatus::DRAFT,
|
||||
submittedAt: null,
|
||||
createdAt: $now,
|
||||
);
|
||||
}
|
||||
|
||||
public function modifierBrouillon(?string $responseHtml, DateTimeImmutable $now): void
|
||||
{
|
||||
if (!$this->status->estModifiable()) {
|
||||
throw RenduDejaSoumisException::pourRendu($this->id);
|
||||
}
|
||||
|
||||
$this->responseHtml = $responseHtml;
|
||||
$this->updatedAt = $now;
|
||||
}
|
||||
|
||||
public function soumettre(DateTimeImmutable $dueDate, DateTimeImmutable $now): void
|
||||
{
|
||||
if (!$this->status->estModifiable()) {
|
||||
throw RenduDejaSoumisException::pourRendu($this->id);
|
||||
}
|
||||
|
||||
$this->submittedAt = $now;
|
||||
$this->updatedAt = $now;
|
||||
|
||||
$estEnRetard = $now > $dueDate;
|
||||
|
||||
$this->status = $estEnRetard
|
||||
? SubmissionStatus::LATE
|
||||
: SubmissionStatus::SUBMITTED;
|
||||
|
||||
if ($estEnRetard) {
|
||||
$this->recordEvent(new DevoirRenduEnRetard(
|
||||
submissionId: $this->id,
|
||||
homeworkId: $this->homeworkId,
|
||||
studentId: $this->studentId,
|
||||
tenantId: $this->tenantId,
|
||||
occurredOn: $now,
|
||||
));
|
||||
} else {
|
||||
$this->recordEvent(new DevoirRendu(
|
||||
submissionId: $this->id,
|
||||
homeworkId: $this->homeworkId,
|
||||
studentId: $this->studentId,
|
||||
tenantId: $this->tenantId,
|
||||
occurredOn: $now,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Pour usage Infrastructure uniquement
|
||||
*/
|
||||
public static function reconstitute(
|
||||
HomeworkSubmissionId $id,
|
||||
TenantId $tenantId,
|
||||
HomeworkId $homeworkId,
|
||||
UserId $studentId,
|
||||
?string $responseHtml,
|
||||
SubmissionStatus $status,
|
||||
?DateTimeImmutable $submittedAt,
|
||||
DateTimeImmutable $createdAt,
|
||||
DateTimeImmutable $updatedAt,
|
||||
): self {
|
||||
$submission = new self(
|
||||
id: $id,
|
||||
tenantId: $tenantId,
|
||||
homeworkId: $homeworkId,
|
||||
studentId: $studentId,
|
||||
responseHtml: $responseHtml,
|
||||
status: $status,
|
||||
submittedAt: $submittedAt,
|
||||
createdAt: $createdAt,
|
||||
);
|
||||
|
||||
$submission->updatedAt = $updatedAt;
|
||||
|
||||
return $submission;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Model\HomeworkSubmission;
|
||||
|
||||
use App\Shared\Domain\EntityId;
|
||||
|
||||
final readonly class HomeworkSubmissionId extends EntityId
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Model\HomeworkSubmission;
|
||||
|
||||
use App\Scolarite\Domain\Exception\PieceJointeInvalideException;
|
||||
use DateTimeImmutable;
|
||||
|
||||
use function in_array;
|
||||
|
||||
final class SubmissionAttachment
|
||||
{
|
||||
private const int MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 Mo
|
||||
|
||||
private const array ALLOWED_MIME_TYPES = [
|
||||
'application/pdf',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
public private(set) SubmissionAttachmentId $id,
|
||||
public private(set) string $filename,
|
||||
public private(set) string $filePath,
|
||||
public private(set) int $fileSize {
|
||||
set(int $fileSize) {
|
||||
if ($fileSize > self::MAX_FILE_SIZE) {
|
||||
throw PieceJointeInvalideException::fichierTropGros($fileSize, self::MAX_FILE_SIZE);
|
||||
}
|
||||
$this->fileSize = $fileSize;
|
||||
}
|
||||
},
|
||||
public private(set) string $mimeType {
|
||||
set(string $mimeType) {
|
||||
if (!in_array($mimeType, self::ALLOWED_MIME_TYPES, true)) {
|
||||
throw PieceJointeInvalideException::typeFichierNonAutorise($mimeType);
|
||||
}
|
||||
$this->mimeType = $mimeType;
|
||||
}
|
||||
},
|
||||
public private(set) DateTimeImmutable $uploadedAt,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Model\HomeworkSubmission;
|
||||
|
||||
use App\Shared\Domain\EntityId;
|
||||
|
||||
final readonly class SubmissionAttachmentId extends EntityId
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Model\HomeworkSubmission;
|
||||
|
||||
enum SubmissionStatus: string
|
||||
{
|
||||
case DRAFT = 'draft';
|
||||
case SUBMITTED = 'submitted';
|
||||
case LATE = 'late';
|
||||
|
||||
public function estModifiable(): bool
|
||||
{
|
||||
return $this === self::DRAFT;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user