Paillasse/portail/app/Console/Commands/PurgerClassesArchivees.php
Adam Belkacemi f2bca621cd ajoute la surveillance d'examen (chantier vrai cockpit, dernière étape)
Une séance peut être lancée en mode examen avec une durée dédiée.
Réutilise le mécanisme d'expiration déjà en place (labs:expirer-sessions,
expire_a) plutôt que d'en construire un nouveau. Le navigateur de
l'élève signale les changements d'onglet et les sorties de plein écran
(evenements_examen : un type et un horodatage, rien d'autre - pas de
capture d'écran ni de webcam) ; le prof voit le compte par élève et le
temps restant dans le cockpit de sa classe et la salle d'attente.

Choix d'Adam après discussion des options (suivi chronométré simple /
+ détection d'onglet / + tentative de plein écran) : la troisième,
honnêtement présentée - un navigateur ne permet pas de vraiment
empêcher de sortir, seulement de le signaler.

Avec cette étape, les quatre fonctions fictives du tableau de bord
démo (quiz, devoirs, observations, examen) sont maintenant réelles.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-14 00:35:52 +02:00

57 lines
2 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Classe;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
/**
* RGPD (CLAUDE.md, étape 14) : supprime les classes archivées depuis
* 90 jours, avec en cascade leurs élèves, séances, sessions de lab, machines,
* corrections, quiz et devoirs maison (chantier « vrai cockpit »), ainsi que
* les annonces, observations, le chat élève ↔ prof (séparation vitrine/
* application, 2026-10-13) et les événements d'examen (2026-10-14). Le
* compte du prof est conservé. Le journal ne garde que des identifiants et
* des nombres, jamais de nom d'élève.
*
* Les sauvegardes chiffrées contenant encore ces données expirent d'elles-
* mêmes au plus 4 semaines plus tard (rotation, docs/runbooks/sauvegardes.md).
*/
class PurgerClassesArchivees extends Command
{
protected $signature = 'classes:purger-archivees';
protected $description = 'Supprime les classes archivées depuis 90 jours et les données de leurs élèves';
public function handle(): void
{
$classes = Classe::whereNotNull('archivee_a')
->where('archivee_a', '<=', now()->subDays(Classe::DELAI_PURGE_JOURS))
->withCount('eleves')
->get();
$purgees = 0;
foreach ($classes as $classe) {
// Ne peut arriver qu'en cas d'anomalie (un lab expire en 45 min) :
// on ne supprime pas une session dont la stack Heat vivrait encore.
if ($classe->aDesLabsEnCours()) {
Log::warning('Purge RGPD reportée : lab en cours', ['classe_id' => $classe->id]);
continue;
}
DB::transaction(fn () => $classe->delete());
Log::info('Purge RGPD : classe supprimée', [
'classe_id' => $classe->id,
'eleves' => $classe->eleves_count,
'archivee_a' => $classe->archivee_a->toDateString(),
]);
$purgees++;
}
$this->info("{$purgees} classe(s) purgée(s).");
}
}