Resto·Ka — tous les restaurants du Québec, menus complets et prix réels (famille ·Ka)
Python 69.3%
TypeScript 16.7%
CSS 7.9%
JavaScript 4.7%
HTML 1.4%
1# ==============================================================================2# Author: Simon-Pierre Boucher <contact@spboucher.ai>3# File: tests/test_inspections.py4# Desc: Connecteur MAPAQ (condamnations) — import CSV HORS LIGNE, parsing5# des dates/amendes/code postal, croisement CONSERVATEUR avec les6# restaurants (nom+lieu, adresse+nom, jamais de fusion hasardeuse),7# anti-doublon au ré-import, endpoint et details.mapaq.8# ==============================================================================9import json1011from restoka import inspections12from restoka.schema import Restaurant13from restoka import db as rdb141516RECORDS = [17 { # croisement par nom exact + ville contenue dans l'adresse18 "Nom_exploitant": "9277-4876 QUEBEC INC.",19 "Raison_sociale": "RESTAURANT CHEZ MAMY",20 "Description_infraction": "LIEUX NON PROPRES.",21 "Adresse_lieu_infraction": "1999 SAINTE-FAMILLE SAGUENAY, (QC) G7X4X5",22 "Type_etablissement": "CASSE-CROUTE",23 "Date_infraction": "11/27/2025 00:00:00",24 "Date_jugement": "05/19/2026 00:00:00",25 "Date_publication": "05/22/2026 00:00:00",26 "Amende": "500 $",27 "SOC_NOM_LOI": "Loi sur les produits alimentaires",28 "SOC_NOM_ARTCL_INFRC": "INSALUBRITE",29 "SOC_DESC_REGRP_TYP_ENTT": "RESTAURATION",30 },31 { # croisement par code postal + civique + similarité de nom32 "Nom_exploitant": "GESTION PIZZA BELLA INC.",33 "Raison_sociale": "PIZZA BELLA",34 "Description_infraction": "TEMPERATURE INADEQUATE.",35 "Adresse_lieu_infraction": "300 RUE PRINCIPALE QUEBEC, (QC) G1K 3Y2",36 "Type_etablissement": "RESTAURANT",37 "Date_infraction": "01/15/2026 00:00:00",38 "Date_jugement": "03/10/2026 00:00:00",39 "Date_publication": "03/12/2026 00:00:00",40 "Amende": "1 250 $",41 "SOC_NOM_LOI": "Loi sur les produits alimentaires",42 "SOC_NOM_ARTCL_INFRC": "TEMPERATURE",43 "SOC_DESC_REGRP_TYP_ENTT": "RESTAURATION",44 },45 { # AUCUN croisement : même nom qu'un resto mais autre ville/CP46 "Nom_exploitant": "FERME LAITIERE X INC.",47 "Raison_sociale": "CHEZ MAMY",48 "Description_infraction": "EQUIPEMENTS NON PROPRES.",49 "Adresse_lieu_infraction": "714 RANG OUEST SAINT-JEAN-DE-DIEU, (QC) G0L3M0",50 "Type_etablissement": "FERME LAITIERE",51 "Date_infraction": "05/21/2025 00:00:00",52 "Date_jugement": "04/14/2026 00:00:00",53 "Date_publication": "04/17/2026 00:00:00",54 "Amende": "2 000 $",55 "SOC_NOM_LOI": "Loi sur les produits alimentaires",56 "SOC_NOM_ARTCL_INFRC": "INSALUBRITE",57 "SOC_DESC_REGRP_TYP_ENTT": "LAIT",58 },59]606162def _seed_restaurants(con):63 restos = [64 Restaurant(source="osm", external_id="node/1", name="Chez Mamy",65 address="1999 Rue Sainte-Famille", city="Saguenay",66 postal_code="G7X 4X5", lat=48.42, lng=-71.06),67 Restaurant(source="osm", external_id="node/2", name="Bella Pizza Resto",68 address="300 Rue Principale", city="Québec",69 postal_code="G1K 3Y2", lat=46.81, lng=-71.21),70 # piège : même nom que la condamnation #1 mais dans une AUTRE ville71 Restaurant(source="osm", external_id="node/3", name="Chez Mamy",72 address="12 Rue Untel", city="Gatineau",73 postal_code="J8X 1A1", lat=45.47, lng=-75.70),74 ]75 rdb.sync_source(con, "osm", [r.finalize() for r in restos])767778def test_import_croisement_et_antidoublon(con):79 _seed_restaurants(con)80 added = inspections.import_rows(con, RECORDS)81 assert added == 382 # parsing : date ISO, amende numérique, code postal extrait83 row = con.execute("SELECT * FROM inspections WHERE etablissement="84 "'RESTAURANT CHEZ MAMY'").fetchone()85 assert row["date_jugement"] == "2026-05-19"86 assert row["montant_amende"] == 500.087 assert row["postal_code"] == "G7X4X5"8889 stats = inspections.match(con)90 assert stats["matched_total"] == 291 # #1 : nom+lieu -> le Chez Mamy de SAGUENAY, jamais celui de Gatineau92 assert con.execute("SELECT uid, matched_by FROM inspections WHERE "93 "etablissement='RESTAURANT CHEZ MAMY'").fetchone()["uid"] \94 == "osm:node/1"95 # #2 : adresse+nom (CP + civique + token « pizza/bella » partagé)96 m2 = con.execute("SELECT uid, matched_by FROM inspections WHERE "97 "etablissement='PIZZA BELLA'").fetchone()98 assert m2["uid"] == "osm:node/2" and m2["matched_by"] == "adresse+nom"99 # #3 : la ferme laitière homonyme ne matche RIEN (ville et CP différents)100 assert con.execute("SELECT uid FROM inspections WHERE "101 "exploitant='FERME LAITIERE X INC.'").fetchone()["uid"] is None102103 # résumé conformité écrit dans details.mapaq du resto croisé104 details = json.loads(con.execute(105 "SELECT details FROM restaurants WHERE uid='osm:node/1'")106 .fetchone()["details"])107 assert details["mapaq"]["condamnations"] == 1108 assert details["mapaq"]["total_amendes"] == 500.0109110 # ré-import : aucun doublon111 assert inspections.import_rows(con, RECORDS) == 0112 assert con.execute("SELECT COUNT(*) c FROM inspections").fetchone()["c"] == 3113114115def test_norm_et_core_name():116 n = inspections.norm_name("RESTAURANT CHEZ MAMY INC.")117 assert n == "restaurant chez mamy"118 assert inspections.core_name(n) == "mamy"119 assert inspections.norm_name("Pâtisserie L'Éclair Ltée") == "patisserie l eclair"120