# ============================================================================== # Author: Simon-Pierre Boucher # File: tests/test_inspections.py # Desc: Connecteur MAPAQ (condamnations) — import CSV HORS LIGNE, parsing # des dates/amendes/code postal, croisement CONSERVATEUR avec les # restaurants (nom+lieu, adresse+nom, jamais de fusion hasardeuse), # anti-doublon au ré-import, endpoint et details.mapaq. # ============================================================================== import json from restoka import inspections from restoka.schema import Restaurant from restoka import db as rdb RECORDS = [ { # croisement par nom exact + ville contenue dans l'adresse "Nom_exploitant": "9277-4876 QUEBEC INC.", "Raison_sociale": "RESTAURANT CHEZ MAMY", "Description_infraction": "LIEUX NON PROPRES.", "Adresse_lieu_infraction": "1999 SAINTE-FAMILLE SAGUENAY, (QC) G7X4X5", "Type_etablissement": "CASSE-CROUTE", "Date_infraction": "11/27/2025 00:00:00", "Date_jugement": "05/19/2026 00:00:00", "Date_publication": "05/22/2026 00:00:00", "Amende": "500 $", "SOC_NOM_LOI": "Loi sur les produits alimentaires", "SOC_NOM_ARTCL_INFRC": "INSALUBRITE", "SOC_DESC_REGRP_TYP_ENTT": "RESTAURATION", }, { # croisement par code postal + civique + similarité de nom "Nom_exploitant": "GESTION PIZZA BELLA INC.", "Raison_sociale": "PIZZA BELLA", "Description_infraction": "TEMPERATURE INADEQUATE.", "Adresse_lieu_infraction": "300 RUE PRINCIPALE QUEBEC, (QC) G1K 3Y2", "Type_etablissement": "RESTAURANT", "Date_infraction": "01/15/2026 00:00:00", "Date_jugement": "03/10/2026 00:00:00", "Date_publication": "03/12/2026 00:00:00", "Amende": "1 250 $", "SOC_NOM_LOI": "Loi sur les produits alimentaires", "SOC_NOM_ARTCL_INFRC": "TEMPERATURE", "SOC_DESC_REGRP_TYP_ENTT": "RESTAURATION", }, { # AUCUN croisement : même nom qu'un resto mais autre ville/CP "Nom_exploitant": "FERME LAITIERE X INC.", "Raison_sociale": "CHEZ MAMY", "Description_infraction": "EQUIPEMENTS NON PROPRES.", "Adresse_lieu_infraction": "714 RANG OUEST SAINT-JEAN-DE-DIEU, (QC) G0L3M0", "Type_etablissement": "FERME LAITIERE", "Date_infraction": "05/21/2025 00:00:00", "Date_jugement": "04/14/2026 00:00:00", "Date_publication": "04/17/2026 00:00:00", "Amende": "2 000 $", "SOC_NOM_LOI": "Loi sur les produits alimentaires", "SOC_NOM_ARTCL_INFRC": "INSALUBRITE", "SOC_DESC_REGRP_TYP_ENTT": "LAIT", }, ] def _seed_restaurants(con): restos = [ Restaurant(source="osm", external_id="node/1", name="Chez Mamy", address="1999 Rue Sainte-Famille", city="Saguenay", postal_code="G7X 4X5", lat=48.42, lng=-71.06), Restaurant(source="osm", external_id="node/2", name="Bella Pizza Resto", address="300 Rue Principale", city="Québec", postal_code="G1K 3Y2", lat=46.81, lng=-71.21), # piège : même nom que la condamnation #1 mais dans une AUTRE ville Restaurant(source="osm", external_id="node/3", name="Chez Mamy", address="12 Rue Untel", city="Gatineau", postal_code="J8X 1A1", lat=45.47, lng=-75.70), ] rdb.sync_source(con, "osm", [r.finalize() for r in restos]) def test_import_croisement_et_antidoublon(con): _seed_restaurants(con) added = inspections.import_rows(con, RECORDS) assert added == 3 # parsing : date ISO, amende numérique, code postal extrait row = con.execute("SELECT * FROM inspections WHERE etablissement=" "'RESTAURANT CHEZ MAMY'").fetchone() assert row["date_jugement"] == "2026-05-19" assert row["montant_amende"] == 500.0 assert row["postal_code"] == "G7X4X5" stats = inspections.match(con) assert stats["matched_total"] == 2 # #1 : nom+lieu -> le Chez Mamy de SAGUENAY, jamais celui de Gatineau assert con.execute("SELECT uid, matched_by FROM inspections WHERE " "etablissement='RESTAURANT CHEZ MAMY'").fetchone()["uid"] \ == "osm:node/1" # #2 : adresse+nom (CP + civique + token « pizza/bella » partagé) m2 = con.execute("SELECT uid, matched_by FROM inspections WHERE " "etablissement='PIZZA BELLA'").fetchone() assert m2["uid"] == "osm:node/2" and m2["matched_by"] == "adresse+nom" # #3 : la ferme laitière homonyme ne matche RIEN (ville et CP différents) assert con.execute("SELECT uid FROM inspections WHERE " "exploitant='FERME LAITIERE X INC.'").fetchone()["uid"] is None # résumé conformité écrit dans details.mapaq du resto croisé details = json.loads(con.execute( "SELECT details FROM restaurants WHERE uid='osm:node/1'") .fetchone()["details"]) assert details["mapaq"]["condamnations"] == 1 assert details["mapaq"]["total_amendes"] == 500.0 # ré-import : aucun doublon assert inspections.import_rows(con, RECORDS) == 0 assert con.execute("SELECT COUNT(*) c FROM inspections").fetchone()["c"] == 3 def test_norm_et_core_name(): n = inspections.norm_name("RESTAURANT CHEZ MAMY INC.") assert n == "restaurant chez mamy" assert inspections.core_name(n) == "mamy" assert inspections.norm_name("Pâtisserie L'Éclair Ltée") == "patisserie l eclair"