SPB Git

spb/job-ka

Public

Qualité lieux : rejet des villes canadiennes/monde hors Québec + bâtiments McGill mappés vers Montréal

- normalize.py : _NON_QC_CITIES (villes CA hors QC + grandes villes monde) —
  parse_location ne prend plus ces libellés pour des villes et
  is_quebec_location les rejette explicitement ; purge BD : 127 offres
  actives hors-QC désactivées (Toronto 50, etc.)
- mcgill.py : les lieux Workday de McGill sont des bâtiments (Bronfman
  Building, Currie Gymnasium…) -> ville forcée Montréal (Outaouais ->
  Gatineau) ; 182 offres corrigées en BD

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Aug 18, 2026) parent 67cf3c2 Browse files

2 changed files +44 −2

modified jobka/connectors/mcgill.py +13 −1
@@ -6,7 +6,8 @@
6 # Rôle : Connecteur Université McGill (Montréal) — Workday mcgill.wd3, 6 # Rôle : Connecteur Université McGill (Montréal) — Workday mcgill.wd3,
7 # site « McGill_Careers ». Tous les postes sont au Québec (campus) : 7 # site « McGill_Careers ». Tous les postes sont au Québec (campus) :
8 # les lieux sont des bâtiments, pas des villes -> quebec_only=False 8 # les lieux sont des bâtiments, pas des villes -> quebec_only=False
9−# Créé : 2026-08-17 Modifié : 2026-08-17 9+# et ville forcée à Montréal (Gatineau pour le campus Outaouais)
10+# Créé : 2026-08-17 Modifié : 2026-08-18
10 # ============================================================================= 11 # =============================================================================
11 from .workday import WorkdayConnector 12 from .workday import WorkdayConnector
12 13
@@ -18,3 +19,14 @@ class McgillConnector(WorkdayConnector):
18 HOST = "wd3" 19 HOST = "wd3"
19 SITE = "McGill_Careers" 20 SITE = "McGill_Careers"
20 quebec_only = False # campus montréalais : lieux = noms de bâtiments 21 quebec_only = False # campus montréalais : lieux = noms de bâtiments
22+
23+ def fetch(self):
24+ """Les « lieux » Workday de McGill sont des bâtiments (« Bronfman
25+ Building », « Currie Gymnasium »…) que le parseur prendrait pour des
26+ villes : on force la ville réelle du campus."""
27+ jobs = super().fetch()
28+ for j in jobs:
29+ label = (j.location_label or j.city or "").lower()
30+ j.city = "Gatineau" if "outaouais" in label else "Montréal"
31+ j.region = "Québec"
32+ return jobs
modified jobka/normalize.py +31 −1
@@ -5,7 +5,7 @@
5 # Fichier : jobka/normalize.py 5 # Fichier : jobka/normalize.py
6 # Rôle : Normalisation commune des offres (salaires, dates, mode/type 6 # Rôle : Normalisation commune des offres (salaires, dates, mode/type
7 # d'emploi, lieu, catégorie) — jamais de valeur inventée 7 # d'emploi, lieu, catégorie) — jamais de valeur inventée
8−# Créé : 2026-08-17 Modifié : 2026-08-17 8+# Créé : 2026-08-17 Modifié : 2026-08-18
9 # ============================================================================= 9 # =============================================================================
10 """Couche de normalisation commune appelée par JobPosting.finalize(). 10 """Couche de normalisation commune appelée par JobPosting.finalize().
11 11
@@ -358,6 +358,33 @@ _QC_MARKERS = re.compile(
358 r"\bqc\b|\bquebec\b|\bqu[e]bec, canada\b|province de quebec", re.I) 358 r"\bqc\b|\bquebec\b|\bqu[e]bec, canada\b|province de quebec", re.I)
359 _POSTAL_RE = re.compile(r"\b([GHJ]\d[A-Z])\s*(\d[A-Z]\d)\b", re.I) # G/H/J = Québec 359 _POSTAL_RE = re.compile(r"\b([GHJ]\d[A-Z])\s*(\d[A-Z]\d)\b", re.I) # G/H/J = Québec
360 360
361+# Villes canadiennes hors Québec (et grandes villes étrangères) fréquentes
362+# dans les flux pancanadiens/mondiaux — jamais des villes d'offres
363+# québécoises : à rejeter lors du parsing de lieu (clés sans accents).
364+_NON_QC_CITIES = frozenset({
365+ "toronto", "ottawa", "vancouver", "calgary", "edmonton", "winnipeg",
366+ "mississauga", "brampton", "hamilton", "kitchener", "waterloo", "halifax",
367+ "dartmouth", "victoria", "saskatoon", "regina", "st. john's", "st johns",
368+ "saint john", "fredericton", "moncton", "charlottetown", "london",
369+ "markham", "vaughan", "richmond hill", "oakville", "burlington", "oshawa",
370+ "whitby", "ajax", "pickering", "guelph", "kingston", "barrie",
371+ "kelowna", "kamloops", "nanaimo", "abbotsford", "surrey", "burnaby",
372+ "coquitlam", "langley", "north york", "scarborough", "etobicoke",
373+ "north vancouver", "milton", "cambridge", "st. catharines",
374+ "niagara falls", "thunder bay", "sudbury", "greater sudbury",
375+ "sault ste. marie", "red deer", "lethbridge", "medicine hat",
376+ "grande prairie", "fort mcmurray", "yellowknife", "whitehorse", "iqaluit",
377+ "can", "canada", "ontario", "alberta", "british columbia", "manitoba",
378+ "saskatchewan", "nova scotia", "new brunswick",
379+ "new york", "new york city", "san francisco", "boston", "chicago",
380+ "seattle", "austin", "denver", "los angeles", "miami", "atlanta",
381+ "dallas", "houston", "philadelphia", "washington", "phoenix", "portland",
382+ "paris", "londres", "amsterdam", "berlin", "munich", "dublin", "madrid",
383+ "barcelona", "barcelone", "lisbon", "lisbonne", "sydney", "melbourne",
384+ "singapore", "singapour", "tokyo", "bangalore", "bengaluru", "mumbai",
385+ "mexico city", "sao paulo", "bogota", "buenos aires",
386+})
387+
361 388
362 def canonical_city(raw: str) -> str: 389 def canonical_city(raw: str) -> str:
363 """Canonicalise un nom de ville (« Montreal » -> « Montréal », 390 """Canonicalise un nom de ville (« Montreal » -> « Montréal »,
@@ -403,6 +430,7 @@ def parse_location(raw: str) -> dict:
403 seg_key = _key(seg) 430 seg_key = _key(seg)
404 if (seg and len(seg) <= 40 and re.search(r"[a-z]", seg_key) 431 if (seg and len(seg) <= 40 and re.search(r"[a-z]", seg_key)
405 and len(seg.split()) <= 3 432 and len(seg.split()) <= 3
433+ and seg_key not in _NON_QC_CITIES
406 and not re.search( 434 and not re.search(
407 r"remote|teletravail|canada|multiple|various|partout|" 435 r"remote|teletravail|canada|multiple|various|partout|"
408 r"\d+\s+locations?|\d+\s+lieux|caisse|succursale|" 436 r"\d+\s+locations?|\d+\s+lieux|caisse|succursale|"
@@ -416,6 +444,8 @@ def is_quebec_location(raw: str) -> bool:
416 key = _key(clean_text(raw)) 444 key = _key(clean_text(raw))
417 if not key: 445 if not key:
418 return False 446 return False
447+ if key in _NON_QC_CITIES: # libellé = ville canadienne/monde hors QC
448+ return False
419 if _QC_MARKERS.search(key): 449 if _QC_MARKERS.search(key):
420 return True 450 return True
421 m = _POSTAL_RE.search(raw) 451 m = _POSTAL_RE.search(raw)
422 452
Menu

Theme