Retrait des compétences ; disponibilités en fenêtres horaires libres

Compétences supprimées de bout en bout :
- tables `skill`, `volunteer_skill`, `shift_required_skill` ;
- `Volunteer.skill_codes`, `Shift.required_skill_codes`, colonne `skills`
  de l'import, règle de validation `MISSING_REQUIRED_SKILL`, jeu de démo.

Disponibilités repensées : `Availability` passe de (`day_part_id`,
`status` tri-état) à une **fenêtre horaire libre** (`date`, `start_time`,
`end_time` ; `end <= start` ⇒ passe minuit), plusieurs par jour.
- Nouveaux endpoints `GET`/`PUT /editions/{id}/volunteers/{vid}/availability`
  (PUT = remplacement complet, COORDINATION) et
  `GET /editions/{id}/availability` (toute l'édition, pour la grille).
- `OUTSIDE_AVAILABILITY` (toujours WARNING) : compare le créneau à l'union
  des fenêtres du bénévole ce jour-là ; renvoie `date` + `uncovered_minutes`.
  `DayPart` ne sert plus qu'à la borne de « journée » du cumul 8 h.
- L'import ne gère plus les disponibilités (fenêtres saisies dans l'app) :
  `values.py` supprimé, mapping réduit à nom/email/téléphone/commentaire.

Migration 0007 (recrée `availability`). `AvailabilityStatus` supprimé.
158 tests, `alembic check` et `ruff` au vert. Front à suivre.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
AdamThomas
2026-09-10 22:46:45 +02:00
co-authored by Claude Sonnet 5
parent 81609eecc5
commit be51770b51
36 changed files with 800 additions and 1199 deletions
+4 -36
View File
@@ -1,10 +1,8 @@
"""Endpoints d'import : analyse puis exécution."""
import datetime as dt
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from pydantic import ValidationError
from sqlmodel import Session, select
from sqlmodel import Session
from app.api.deps import get_edition
from app.auth.deps import require_coordinator
@@ -12,13 +10,12 @@ from app.db import get_session
from app.importing.parser import ImportParseError, parse_upload
from app.importing.schemas import (
AnalyzeResponse,
DayPartInfo,
ImportMapping,
ImportReport,
PreviewRow,
)
from app.importing.service import run_import, suggest_mapping, validate_mapping
from app.models import Account, DayPart, Edition
from app.models import Account, Edition
router = APIRouter(prefix="/editions/{edition_id}/import", tags=["import"])
@@ -45,27 +42,11 @@ def _is_xlsx(filename: str, content: bytes) -> bool:
)
def _edition_dates(edition: Edition) -> list[dt.date]:
span = (edition.end_date - edition.start_date).days
return [edition.start_date + dt.timedelta(days=i) for i in range(span + 1)]
def _day_parts(session: Session, edition: Edition) -> list[DayPart]:
return list(
session.exec(
select(DayPart)
.where(DayPart.edition_id == edition.id)
.order_by(DayPart.order)
)
)
@router.post("/analyze", response_model=AnalyzeResponse)
async def analyze(
file: UploadFile = File(...),
edition: Edition = Depends(get_edition),
_edition: Edition = Depends(get_edition),
_guard: Account = Depends(require_coordinator),
session: Session = Depends(get_session),
) -> AnalyzeResponse:
content = await _read_upload(file)
try:
@@ -87,17 +68,6 @@ async def analyze(
row_count=len(table.rows),
preview=preview,
suggested_mapping=suggest_mapping(table.headers),
edition_dates=_edition_dates(edition),
day_parts=[
DayPartInfo(
code=dp.code,
label=dp.label,
start_time=dp.start_time,
end_time=dp.end_time,
order=dp.order,
)
for dp in _day_parts(session, edition)
],
)
@@ -127,9 +97,7 @@ async def execute(
detail={"code": exc.code, "params": exc.params},
) from exc
mapping_errors = validate_mapping(
table, parsed_mapping, edition, _day_parts(session, edition)
)
mapping_errors = validate_mapping(table, parsed_mapping)
if mapping_errors:
raise HTTPException(
status_code=422,