ajoute les devoirs maison (chantier vrai cockpit, étape 3) : rendu en texte libre, suivi des rendus, notation
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
bd74eaf20f
commit
33531de44d
17 changed files with 582 additions and 2 deletions
|
|
@ -10,7 +10,7 @@ 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 et quiz (chantier « vrai cockpit », étape 2). Le compte du
|
||||
* corrections, quiz et devoirs maison (chantier « vrai cockpit »). Le compte du
|
||||
* prof est conservé. Le journal ne garde que des identifiants et des
|
||||
* nombres, jamais de nom d'élève.
|
||||
*
|
||||
|
|
|
|||
85
portail/app/Http/Controllers/DevoirController.php
Normal file
85
portail/app/Http/Controllers/DevoirController.php
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Classe;
|
||||
use App\Models\Devoir;
|
||||
use App\Models\RenduDevoir;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* Devoirs maison (chantier « vrai cockpit », étape 3). Côté prof : créer,
|
||||
* voir qui a rendu, noter. Côté élève : DevoirEleveController.
|
||||
*/
|
||||
class DevoirController extends Controller
|
||||
{
|
||||
public function index(Classe $classe): View
|
||||
{
|
||||
$this->verifierProprietaire($classe);
|
||||
|
||||
return view('devoirs.index', ['classe' => $classe, 'devoirs' => $classe->devoirs()->withCount('rendus')->get()]);
|
||||
}
|
||||
|
||||
public function store(Request $request, Classe $classe): RedirectResponse
|
||||
{
|
||||
$this->verifierProprietaire($classe);
|
||||
$donnees = $request->validate([
|
||||
'titre' => ['required', 'string', 'max:150'],
|
||||
'consigne' => ['required', 'string', 'max:5000'],
|
||||
'echeance' => ['required', 'date', 'after:now'],
|
||||
]);
|
||||
|
||||
$devoir = $classe->devoirs()->create($donnees);
|
||||
|
||||
return redirect()->route('mes-classes.devoirs.show', [$classe, $devoir]);
|
||||
}
|
||||
|
||||
public function show(Classe $classe, Devoir $devoir): View
|
||||
{
|
||||
$this->verifierProprietaire($classe);
|
||||
$this->verifierAppartenance($classe, $devoir);
|
||||
|
||||
$rendus = $devoir->rendus()->with('eleve')->get()->keyBy('eleve_id');
|
||||
|
||||
return view('devoirs.show', [
|
||||
'classe' => $classe,
|
||||
'devoir' => $devoir,
|
||||
'eleves' => $classe->eleves,
|
||||
'rendus' => $rendus,
|
||||
]);
|
||||
}
|
||||
|
||||
public function noter(Request $request, Classe $classe, Devoir $devoir, RenduDevoir $rendu): RedirectResponse
|
||||
{
|
||||
$this->verifierProprietaire($classe);
|
||||
$this->verifierAppartenance($classe, $devoir);
|
||||
abort_if($rendu->devoir_id !== $devoir->id, 404);
|
||||
|
||||
$donnees = $request->validate(['note' => ['required', 'integer', 'min:0', 'max:20']]);
|
||||
$rendu->update($donnees);
|
||||
|
||||
return redirect()->route('mes-classes.devoirs.show', [$classe, $devoir]);
|
||||
}
|
||||
|
||||
public function destroy(Classe $classe, Devoir $devoir): RedirectResponse
|
||||
{
|
||||
$this->verifierProprietaire($classe);
|
||||
$this->verifierAppartenance($classe, $devoir);
|
||||
$devoir->delete();
|
||||
|
||||
return redirect()->route('mes-classes.devoirs.index', $classe);
|
||||
}
|
||||
|
||||
private function verifierProprietaire(Classe $classe): void
|
||||
{
|
||||
abort_if($classe->user_id !== Auth::guard('web')->id(), 403);
|
||||
}
|
||||
|
||||
private function verifierAppartenance(Classe $classe, Devoir $devoir): void
|
||||
{
|
||||
abort_if($devoir->classe_id !== $classe->id, 404);
|
||||
}
|
||||
}
|
||||
51
portail/app/Http/Controllers/DevoirEleveController.php
Normal file
51
portail/app/Http/Controllers/DevoirEleveController.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Devoir;
|
||||
use App\Models\Eleve;
|
||||
use App\Models\RenduDevoir;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* Côté élève des devoirs maison (chantier « vrai cockpit », étape 3) :
|
||||
* rendu en texte libre, une seule fois par devoir, avant l'échéance.
|
||||
*/
|
||||
class DevoirEleveController extends Controller
|
||||
{
|
||||
public function show(Devoir $devoir): View
|
||||
{
|
||||
$eleve = $this->verifierAppartenance($devoir);
|
||||
|
||||
return view('devoirs.eleve', [
|
||||
'devoir' => $devoir,
|
||||
'rendu' => RenduDevoir::where('devoir_id', $devoir->id)->where('eleve_id', $eleve->id)->first(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function rendre(Request $request, Devoir $devoir): RedirectResponse
|
||||
{
|
||||
$eleve = $this->verifierAppartenance($devoir);
|
||||
abort_if($devoir->echeanceDepassee(), 409, 'L’échéance est dépassée.');
|
||||
|
||||
$donnees = $request->validate(['reponse' => ['required', 'string', 'max:10000']]);
|
||||
|
||||
RenduDevoir::updateOrCreate(
|
||||
['devoir_id' => $devoir->id, 'eleve_id' => $eleve->id],
|
||||
['reponse' => $donnees['reponse'], 'rendu_a' => now()]
|
||||
);
|
||||
|
||||
return redirect()->route('devoirs.eleve.show', $devoir)->with('rendu_envoye', true);
|
||||
}
|
||||
|
||||
private function verifierAppartenance(Devoir $devoir): Eleve
|
||||
{
|
||||
$eleve = Auth::guard('eleve')->user();
|
||||
abort_if($devoir->classe_id !== $eleve->classe_id, 403);
|
||||
|
||||
return $eleve;
|
||||
}
|
||||
}
|
||||
|
|
@ -73,6 +73,12 @@ class Classe extends Model
|
|||
return $this->hasMany(Quiz::class)->latest();
|
||||
}
|
||||
|
||||
/** @return HasMany<Devoir, $this> */
|
||||
public function devoirs(): HasMany
|
||||
{
|
||||
return $this->hasMany(Devoir::class)->latest('echeance');
|
||||
}
|
||||
|
||||
public function estArchivee(): bool
|
||||
{
|
||||
return $this->archivee_a !== null;
|
||||
|
|
|
|||
37
portail/app/Models/Devoir.php
Normal file
37
portail/app/Models/Devoir.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* Devoir maison (chantier « vrai cockpit », étape 3 — 2026-10-13) : rendu en
|
||||
* texte libre, une seule fois par élève, avant l'échéance.
|
||||
*/
|
||||
class Devoir extends Model
|
||||
{
|
||||
protected $fillable = ['classe_id', 'titre', 'consigne', 'echeance'];
|
||||
|
||||
protected $casts = [
|
||||
'echeance' => 'datetime',
|
||||
];
|
||||
|
||||
/** @return BelongsTo<Classe, $this> */
|
||||
public function classe(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Classe::class);
|
||||
}
|
||||
|
||||
/** @return HasMany<RenduDevoir, $this> */
|
||||
public function rendus(): HasMany
|
||||
{
|
||||
return $this->hasMany(RenduDevoir::class);
|
||||
}
|
||||
|
||||
public function echeanceDepassee(): bool
|
||||
{
|
||||
return $this->echeance->isPast();
|
||||
}
|
||||
}
|
||||
30
portail/app/Models/RenduDevoir.php
Normal file
30
portail/app/Models/RenduDevoir.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class RenduDevoir extends Model
|
||||
{
|
||||
protected $table = 'rendus_devoirs';
|
||||
|
||||
protected $fillable = ['devoir_id', 'eleve_id', 'reponse', 'note', 'rendu_a'];
|
||||
|
||||
protected $casts = [
|
||||
'rendu_a' => 'datetime',
|
||||
'note' => 'integer',
|
||||
];
|
||||
|
||||
/** @return BelongsTo<Devoir, $this> */
|
||||
public function devoir(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Devoir::class);
|
||||
}
|
||||
|
||||
/** @return BelongsTo<Eleve, $this> */
|
||||
public function eleve(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Eleve::class);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue