spb/forma-ka Public
Python 65.1%
TypeScript 17.9%
CSS 16.4%
HTML 0.5%
1# -----------------------------------------------------------------------------2# Forma-Ka — Agrégateur de formations (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# tests/test_normalize.py : couche de normalisation commune5# -----------------------------------------------------------------------------6from formaka.normalize import (7 extract_details,8 normalize_language,9 normalize_mode,10 normalize_type,11 parse_date_fr,12 parse_duration_hours,13 parse_price,14)15from formaka.schema import Formation161718def test_parse_price():19 assert parse_price("1 295,00 $ + tx") == 1295.020 assert parse_price("795 $") == 795.021 assert parse_price("$1,250.00") == 1250.022 assert parse_price("Gratuit") == 0.023 assert parse_price("Sans frais") == 0.024 assert parse_price("") is None25 assert parse_price("Sur demande") is None262728def test_parse_duration_hours():29 assert parse_duration_hours("2 jours") == 14.030 assert parse_duration_hours("45 heures") == 45.031 assert parse_duration_hours("3,5 h") == 3.532 assert parse_duration_hours("90 minutes") == 1.533 assert parse_duration_hours("demi-journée") == 3.534 assert parse_duration_hours("") is None353637def test_normalize_mode():38 assert normalize_mode("Classe virtuelle") == "en ligne"39 assert normalize_mode("À distance") == "en ligne"40 assert normalize_mode("En salle") == "présentiel"41 assert normalize_mode("Comodal") == "hybride"42 assert normalize_mode("À votre rythme") == "asynchrone"434445def test_normalize_type():46 assert normalize_type("Séminaire de 2 jours") == "Séminaire"47 assert normalize_type("Cours de 1er cycle") == "Cours universitaire"48 assert normalize_type("AEC en bureautique") == "Cours collégial"49 assert normalize_type("Webinaire gratuit") == "Webinaire"50 assert normalize_type("Formation continue") == "Formation continue"515253def test_normalize_language():54 assert normalize_language("Français") == "fr"55 assert normalize_language("English") == "en"56 assert normalize_language("FR et EN") == "fr/en"575859def test_parse_date_fr():60 assert parse_date_fr("Débute le 14 octobre 2026") == "2026-10-14"61 assert parse_date_fr("1er décembre 2026") == "2026-12-01"62 assert parse_date_fr("2026-09-08") == "2026-09-08"63 assert parse_date_fr("14/10/2026") == "2026-10-14"64 assert parse_date_fr("aucune date") is None656667def test_extract_details():68 d = extract_details("Ce cours de 3 crédits (1,4 UEC), niveau débutant, 45 heures")69 assert d["credits"] == 3.070 assert d["uec"] == 1.471 assert d["level"] == "débutant"72 assert d["duration_hours"] == 45.0737475def test_finalize_prix_optionnel():76 """Un cours universitaire sans prix est parfaitement valide."""77 f = Formation(source="t", external_id="1", url="u", title=" Cours X ",78 description="Cours de 3 crédits offert à distance.").finalize()79 assert f.price is None80 assert f.is_free is None81 assert f.credits == "3 crédits"82 assert f.title == "Cours X"838485def test_finalize_complet():86 f = Formation(source="t", external_id="2", url="u", title="Atelier",87 training_type="atelier", duration="2 jours",88 price_label="À partir de 795 $",89 schedule_label="Prochaine séance : 14 octobre 2026").finalize()90 assert f.training_type == "Atelier"91 assert f.price == 795.092 assert f.is_free is False93 assert f.duration_hours == 14.094 assert f.start_date == "2026-10-14"95 assert f.details.get("price_from") is True96