| 5 |
5 |
# Fichier : jobka/normalize.py |
| 6 |
6 |
# Rôle : Normalisation commune des offres (salaires, dates, mode/type |
| 7 |
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 |
10 |
"""Couche de normalisation commune appelée par JobPosting.finalize(). |
| 11 |
11 |
|
| 358 |
358 |
r"\bqc\b|\bquebec\b|\bqu[e]bec, canada\b|province de quebec", re.I) |
| 359 |
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 |
389 |
def canonical_city(raw: str) -> str: |
| 363 |
390 |
"""Canonicalise un nom de ville (« Montreal » -> « Montréal », |
| 403 |
430 |
seg_key = _key(seg) |
| 404 |
431 |
if (seg and len(seg) <= 40 and re.search(r"[a-z]", seg_key) |
| 405 |
432 |
and len(seg.split()) <= 3 |
|
433 |
+ and seg_key not in _NON_QC_CITIES |
| 406 |
434 |
and not re.search( |
| 407 |
435 |
r"remote|teletravail|canada|multiple|various|partout|" |
| 408 |
436 |
r"\d+\s+locations?|\d+\s+lieux|caisse|succursale|" |
| 416 |
444 |
key = _key(clean_text(raw)) |
| 417 |
445 |
if not key: |
| 418 |
446 |
return False |
|
447 |
+ if key in _NON_QC_CITIES: # libellé = ville canadienne/monde hors QC |
|
448 |
+ return False |
| 419 |
449 |
if _QC_MARKERS.search(key): |
| 420 |
450 |
return True |
| 421 |
451 |
m = _POSTAL_RE.search(raw) |
| 422 |
452 |
|