Étape 3 : import CSV/Excel des bénévoles et disponibilités
- Fichier unique, processus en deux temps : POST /editions/{id}/import/analyze
(colonnes détectées, aperçu, mapping suggéré, demi-journées de l'édition)
puis POST /editions/{id}/import/execute (mapping validé)
- Parser CSV (délimiteur et encodage devinés, BOM) et XLSX (openpyxl),
détection XLSX par nombre magique
- Vocabulaire des valeurs de disponibilité (tri-état, accents/casse) et des
tailles de t-shirt ; compétences créées à la volée dans le référentiel
- Rapport ligne par ligne : codes de traduction + paramètres, jamais de
phrase. Doublons : upsert sur email, signalement sur (nom, prénom) sans
email, doublons intra-fichier. Mode simulation (rollback, rien de persisté)
- 40 tests : parser, valeurs, service (tous les cas), endpoints
- Dépendances : python-multipart, openpyxl
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
cb2e84adf0
commit
bb919554ff
@@ -0,0 +1,66 @@
|
||||
import pytest
|
||||
|
||||
from app.importing.text import slug_code, split_skills
|
||||
from app.importing.values import parse_availability_cell, parse_tshirt_size
|
||||
from app.models import AvailabilityStatus, TshirtSize
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("cell", "expected"),
|
||||
[
|
||||
("x", AvailabilityStatus.AVAILABLE),
|
||||
("OUI", AvailabilityStatus.AVAILABLE),
|
||||
(" Disponible ", AvailabilityStatus.AVAILABLE),
|
||||
("si besoin", AvailabilityStatus.IF_NEEDED),
|
||||
("Peut-être", AvailabilityStatus.IF_NEEDED),
|
||||
("non", AvailabilityStatus.UNAVAILABLE),
|
||||
("0", AvailabilityStatus.UNAVAILABLE),
|
||||
],
|
||||
)
|
||||
def test_recognized_availability_values(cell, expected):
|
||||
status, recognized = parse_availability_cell(cell)
|
||||
assert recognized is True
|
||||
assert status is expected
|
||||
|
||||
|
||||
def test_empty_cell_is_recognized_but_no_declaration():
|
||||
assert parse_availability_cell(" ") == (None, True)
|
||||
assert parse_availability_cell("N/A") == (None, True)
|
||||
|
||||
|
||||
def test_unknown_cell_is_flagged():
|
||||
assert parse_availability_cell("bof") == (None, False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("raw", "expected"),
|
||||
[
|
||||
("M", TshirtSize.M),
|
||||
(" m ", TshirtSize.M),
|
||||
("xl", TshirtSize.XL),
|
||||
("2XL", TshirtSize.XXL),
|
||||
("3 XL", TshirtSize.XXXL),
|
||||
],
|
||||
)
|
||||
def test_tshirt_sizes(raw, expected):
|
||||
assert parse_tshirt_size(raw) is expected
|
||||
|
||||
|
||||
def test_unknown_tshirt_size_is_none():
|
||||
assert parse_tshirt_size("large") is None
|
||||
|
||||
|
||||
def test_slug_code_is_stable_across_accents_and_case():
|
||||
assert slug_code("Régie son") == "REGIE_SON"
|
||||
assert slug_code(" permis B ") == "PERMIS_B"
|
||||
assert slug_code("SST") == "SST"
|
||||
|
||||
|
||||
def test_split_skills():
|
||||
assert split_skills("SST, Permis B / régie son\nlangues") == [
|
||||
"SST",
|
||||
"Permis B",
|
||||
"régie son",
|
||||
"langues",
|
||||
]
|
||||
assert split_skills("") == []
|
||||
Reference in New Issue
Block a user