SPB Git forge

spb/job-ka

Public
226commits 1branches 0releases
37.5 MBsize
maindefault branch
9 h agolast push
HTML 82.1% Python 14.6% TypeScript 1.9% CSS 1% JavaScript 0.5%

Expansion ×: 59 nouveaux employeurs connectés via sondage massif d ATS + détection de pages carrières

- scripts/probe_ats.py : sonde 800+ slugs candidats sur les 9 ATS (timeout 6 s,
  throttle 0,3 s/domaine, 8 threads) ; confirmé = ≥1 offre ET ≥1 offre QC
  (is_quebec_location du projet). Découverte Workday sans page HTML (bloquée
  406) : cxs 422=tenant absent / 404=hôte bon, puis devinette du site.
- scripts/detect_ats.py : visite 6 337 domaines d employeurs QC (accueil +
  /carrieres|/careers|/emplois|/jobs|/fr/carrieres + liens carrières),
  reconnaît l ATS par motifs URL/DOM ; ATS non supportés (taleo 11,
  successfactors 6, adp 5, icims 4, njoyn 4, ukg 3) consignés dans
  data/detected-other-ats.jsonl pour traitement séparé.
- data/feeds-expansion.json : 59 flux vérifiés -> 59 connecteurs générés
  (workday 22, smartrecruiters 10, bamboohr 12, greenhouse 6, lever 4,
  workable 2, ashby 1, recruitee 1, breezy 0) dont Vidéotron, CDPQ, BDC,
  Beneva, Intact, PSP, Investissement Québec, Rio Tinto, Alcoa, Airbus,
  Walmart Canada, Canadian Tire, RCGT, Lightspeed, Behaviour, Cirque du
  Soleil, Gameloft, Canam, QSL, Promutuel, EXFO…
- 2 107 offres actives ajoutées, 100 % villes QC ; faux positif retiré
  (genetec smartrecruiters, doublon faible de genetec_inc workable)
- normalize.py : villes hors-QC supplémentaires (Las Vegas, Orlando…)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 1 mo ago (Aug 18, 2026) parent fd4bfc8

65 changed files +3,728 −2

added data/detected-other-ats.jsonl +33 −0
@@ -0,0 +1,33 @@
1 +{"name": "Canada Vie", "domain": "canadalife.com", "ats": "successfactors", "url": "https://career5.successfactors.eu/career?career_company=thegreatweP2&amp;lang=en_US&amp;company=thegreatweP2&amp;site=&amp;loginFlowRequired=true&#34;,&#34;label&#34;:&#34;View"}
2 +{"name": "Power Corporation du Canada", "domain": "powercorporation.com", "ats": "icims", "url": "https://careersfr-mackenzieinvestments.icims.com/jobs/search?amp;hashed=0&amp;mobile=false&amp;width=562&amp;height=500&amp;bga=true&amp;needsRedirect=false"}
3 +{"name": "Hydro-Québec", "domain": "hydroquebec.com", "ats": "successfactors", "url": "https://rmkcdn.successfactors.com/3119a4f4/be354fb9-0f6f-4e20-a2b9-e.jpg"}
4 +{"name": "Valero — raffinerie Jean-Gaulin", "domain": "valero.com", "ats": "taleo", "url": "https://valero.taleo.net/careersection/2/jobsearch.ftl?lang=en"}
5 +{"name": "Agnico Eagle", "domain": "agnicoeagle.com", "ats": "taleo", "url": "https://agnicoeagle.taleo.net/careersection/2/jobsearch.ftl?lang=fi"}
6 +{"name": "Produits forestiers Résolu", "domain": "pfresolu.com", "ats": "successfactors", "url": "https://rmkcdn.successfactors.com/2012394c/1b67750f-ca41-4435-be90-d.png"}
7 +{"name": "Cascades", "domain": "cascades.com", "ats": "successfactors", "url": "https://performancemanager4.successfactors.com/verp/vmod_v1/ui/extlib/jquery_3.5.1/jquery.js"}
8 +{"name": "Lassonde", "domain": "lassonde.com", "ats": "adp", "url": "https://workforcenow.adp.com/mascsr/default/mdf/recruitment/recruitment.html?cid=f96ce972-7721-4c7b-99ea-9a970ddc824a&amp;ccId=9200604444876_2&amp;lang=en_US"}
9 +{"name": "Domtar", "domain": "domtar.com", "ats": "successfactors", "url": "https://rmkcdn.successfactors.com/2012394c/1b67750f-ca41-4435-be90-d.png"}
10 +{"name": "Kruger", "domain": "kruger.com", "ats": "taleo", "url": "https://tre.tbe.taleo.net/tre01/ats/careers/v2/jobSearch?act=redirectCwsV2&cws=76&org=KRUGER2"}
11 +{"name": "Bell Textron Canada", "domain": "bellflight.com", "ats": "taleo", "url": "https://textron.taleo.net/careersection/bell/jobsearch.ftl?lang=en"}
12 +{"name": "exo", "domain": "exo.quebec", "ats": "taleo", "url": "https://phh.tbe.taleo.net/phh04/ats/careers/v2/jobSearch?org=AMT&amp;cws=39"}
13 +{"name": "STL — Société de transport de Laval", "domain": "stlaval.ca", "ats": "njoyn", "url": "https://lavaltransit.njoyn.com/CL2/xweb/xweb.asp?tbtoken=ZlxcRRkXCBJ3EQZwNlxUCCQ7d2FEcCIoA0hYVl8LE2cuKjIdXkobdWcBcQkbUhVWSXYqWw%3D%3D&amp;chk=ZVpaShw%3D&amp;clid=60406&amp;page=joblisting"}
14 +{"name": "RTC — Réseau de transport de la Capitale", "domain": "rtcquebec.ca", "ats": "njoyn", "url": "https://rtc.njoyn.com/CGI/xweb/Xweb.asp?tbtoken=ZlBQShwXCGl6Z3BwTFNSCCZAA2FEcCcuBkggJy0PE2YqWkcdLUpqdGcGcgkbVhZSSXkqWw%3D%3D&amp;chk=ZVpaShw%3D&amp;CLID=23009&amp;page=joblisting&amp;lang=2"}
15 +{"name": "CGI", "domain": "cgi.com", "ats": "njoyn", "url": "https://cgi.njoyn.com/CORP/xweb/xweb.asp?NTKN=c&amp;clid=21001&amp;page=joblisting&amp;lang=2"}
16 +{"name": "Groupe Dynamite", "domain": "groupedynamite.com", "ats": "icims", "url": "https://www.icims.com/legal/privacy-notice-website/"}
17 +{"name": "Sépaq", "domain": "sepaq.com", "ats": "taleo", "url": "https://tre.tbe.taleo.net/tre01/ats/careers/v2/jobSearch?org=SEPAQ&cws=37"}
18 +{"name": "Ville de Terrebonne", "domain": "ville.terrebonne.qc.ca", "ats": "njoyn", "url": "https://clients.njoyn.com/cl4/xweb/Xweb.asp?tbtoken=Z19fQBpfDVBzEAQHQSBVHU8%2FeBdZaVVbciBMWlB9f3ksUEsaKzIZBxEDB0YYGhJTTnZgF3Y%3D&amp;chk=ZVpaShM%3D&amp;CLID=71764&amp;page=joblisting&amp;lang=2"}
19 +{"name": "HEC Montréal", "domain": "hec.ca", "ats": "taleo", "url": "https://tre.tbe.taleo.net/tre01/ats/careers/v2/jobSearch?act=redirectCwsV2&cws=40&org=NYQEDG"}
20 +{"name": "Université TÉLUQ", "domain": "teluq.ca", "ats": "taleo", "url": "https://phh.tbe.taleo.net/phh01/ats/careers/jobSearch.jsp?org=TELUQ&cws=1"}
21 +{"name": "CIUSSS de l'Est-de-l'Île-de-Montréal", "domain": "ciusss-estmtl.gouv.qc.ca", "ats": "taleo", "url": "https://ciusssemtl.taleo.net/careersection/hmr/jobsearch.ftl?lang=fr&amp;portal=14170700380"}
22 +{"name": "Deloitte Canada", "domain": "deloitte.com", "ats": "taleo", "url": "https://dttit.taleo.net/careersection/d_corporate+28greece29/jobsearch.ftl?lang=en"}
23 +{"name": "Deloitte Canada", "domain": "deloitte.com", "ats": "successfactors", "url": "https://career4.successfactors.com/career?company=C0020897957P&"}
24 +{"name": "MSI Gestion immobilière", "domain": "msimmobiliers.com", "ats": "adp", "url": "https://workforcenow.adp.com/mascsr/default/mdf/recruitment/recruitment.html?cid=47d4e792-26e5-471c-92ac-73c60131bab8&amp;ccId=19000101_000001&amp;lang=fr_CA"}
25 +{"name": "CAPREIT", "domain": "capreit.ca", "ats": "icims", "url": "https://careers-capreit.icims.com/jobs/search"}
26 +{"name": "Osgoode Properties", "domain": "osgoodeproperties.com", "ats": "adp", "url": "https://workforcenow.adp.com/mascsr/default/mdf/recruitment/recruitment.html?cid=c6a2e782-c971-4a6d-9a63-1365f5ce7d21&amp;ccId=19000101_000001&amp;lang=en_CA"}
27 +{"name": "Fruit D'or Inc.", "domain": "fruitdor.ca", "ats": "ukg_ultipro", "url": "https://rec-cdn-prod.cdn.ultipro.com/rec-web/d3560791671d02f6e2ce98a5b18c441302d3f36a/site.css"}
28 +{"name": "Cultures Gen V inc.", "domain": "gen-v.com", "ats": "adp", "url": "https://workforcenow.adp.com/mascsr/default/mdf/recruitment/recruitment.html?cid=8b346731-d189-4cf2-a14c-9e48d38befde&amp;ccId=36427051_2&amp;lang=fr_CA&amp;selectedMenuKey=CurrentOpenings"}
29 +{"name": "Nortera Foods Inc.", "domain": "norterafoods.com", "ats": "ukg_ultipro", "url": "https://rec-cdn-prod.cdn.ultipro.com/rec-web/d3560791671d02f6e2ce98a5b18c441302d3f36a/site.css"}
30 +{"name": "Produits Kruger Inc", "domain": "krugerproducts.ca", "ats": "taleo", "url": "https://tre.tbe.taleo.net/tre01/ats/careers/v2/jobSearch?org=KRUGER2&amp;cws=78"}
31 +{"name": "Méchant Boeuf Bar & Brasserie", "domain": "mechantboeuf.com", "ats": "ukg_ultipro", "url": "https://rec-cdn-prod.cdn.ultipro.com/rec-web/d3560791671d02f6e2ce98a5b18c441302d3f36a/site.css"}
32 +{"name": "Bento Sushi", "domain": "bentosushi.com", "ats": "adp", "url": "https://workforcenow.adp.com/mascsr/default/mdf/recruitment/recruitment.html?cid=d0dfb14c-f365-4599-b39b-0fdccb3d1160&#038;ccId=19000101_000001&#038;lang=fr_CA"}
33 +{"name": "Jack Astor's", "domain": "jackastors.com", "ats": "icims", "url": "https://careersen-sircorp.icims.com/"}
added data/feeds-expansion.json +883 −0
@@ -0,0 +1,883 @@
1 +[
2 + {
3 + "ats": "ashby",
4 + "employer": "Lightspeed Commerce",
5 + "sectors": [
6 + "TI"
7 + ],
8 + "cities": [
9 + "Montréal"
10 + ],
11 + "url": "",
12 + "org": "lightspeedhq",
13 + "source_id": "lightspeedhq",
14 + "qc": 62,
15 + "api_name": ""
16 + },
17 + {
18 + "ats": "bamboohr",
19 + "employer": "Big Bang",
20 + "sectors": [
21 + "TI"
22 + ],
23 + "cities": [
24 + "Montréal"
25 + ],
26 + "url": "",
27 + "org": "bigbang",
28 + "source_id": "bigbang",
29 + "qc": 8,
30 + "api_name": ""
31 + },
32 + {
33 + "ats": "bamboohr",
34 + "employer": "Broccolini",
35 + "sectors": [
36 + "Construction",
37 + "Immobilier"
38 + ],
39 + "cities": [
40 + "Montréal"
41 + ],
42 + "url": "",
43 + "org": "broccolini",
44 + "source_id": "broccolini",
45 + "qc": 23,
46 + "api_name": ""
47 + },
48 + {
49 + "ats": "bamboohr",
50 + "employer": "Aliments Ouimet-Cordon Bleu inc.",
51 + "sectors": [
52 + "Production"
53 + ],
54 + "cities": [],
55 + "url": "https://clarkcordonbleu.ca",
56 + "org": "cordonbleu",
57 + "source_id": "cordonbleu",
58 + "qc": 2,
59 + "api_name": ""
60 + },
61 + {
62 + "ats": "bamboohr",
63 + "employer": "Devimco Appartements",
64 + "sectors": [
65 + "Immobilier"
66 + ],
67 + "cities": [],
68 + "url": "https://devimco.com",
69 + "org": "devimco",
70 + "source_id": "devimco",
71 + "qc": 7,
72 + "api_name": ""
73 + },
74 + {
75 + "ats": "bamboohr",
76 + "employer": "EVLO (Hydro-Québec)",
77 + "sectors": [
78 + "Énergie"
79 + ],
80 + "cities": [
81 + "Varennes"
82 + ],
83 + "url": "",
84 + "org": "evlo",
85 + "source_id": "evlo",
86 + "qc": 2,
87 + "api_name": ""
88 + },
89 + {
90 + "ats": "bamboohr",
91 + "employer": "Fenêtres Concept",
92 + "sectors": [
93 + "Production"
94 + ],
95 + "cities": [],
96 + "url": "https://fenetresconcept.com",
97 + "org": "fenetresconcept",
98 + "source_id": "fenetresconcept",
99 + "qc": 1,
100 + "api_name": ""
101 + },
102 + {
103 + "ats": "bamboohr",
104 + "employer": "Flare (cybersécurité)",
105 + "sectors": [
106 + "TI"
107 + ],
108 + "cities": [
109 + "Montréal"
110 + ],
111 + "url": "",
112 + "org": "flare",
113 + "source_id": "flare",
114 + "qc": 2,
115 + "api_name": ""
116 + },
117 + {
118 + "ats": "bamboohr",
119 + "employer": "Korem",
120 + "sectors": [
121 + "TI"
122 + ],
123 + "cities": [
124 + "Québec"
125 + ],
126 + "url": "",
127 + "org": "korem",
128 + "source_id": "korem",
129 + "qc": 4,
130 + "api_name": ""
131 + },
132 + {
133 + "ats": "bamboohr",
134 + "employer": "Logisco",
135 + "sectors": [
136 + "Immobilier"
137 + ],
138 + "cities": [],
139 + "url": "https://logisco.com",
140 + "org": "logisco",
141 + "source_id": "logisco",
142 + "qc": 13,
143 + "api_name": ""
144 + },
145 + {
146 + "ats": "greenhouse",
147 + "employer": "AlayaCare",
148 + "sectors": [
149 + "TI",
150 + "Santé"
151 + ],
152 + "cities": [
153 + "Montréal"
154 + ],
155 + "url": "https://www.alayacare.com",
156 + "org": "alayacare",
157 + "source_id": "alayacare",
158 + "qc": 10,
159 + "api_name": "AlayaCare"
160 + },
161 + {
162 + "ats": "greenhouse",
163 + "employer": "GURUS Solutions",
164 + "sectors": [
165 + "TI"
166 + ],
167 + "cities": [
168 + "Montréal"
169 + ],
170 + "url": "",
171 + "org": "gurussolutions",
172 + "source_id": "gurussolutions",
173 + "qc": 8,
174 + "api_name": "GURUS Solutions"
175 + },
176 + {
177 + "ats": "greenhouse",
178 + "employer": "Levio",
179 + "sectors": [
180 + "TI"
181 + ],
182 + "cities": [
183 + "Québec",
184 + "Montréal"
185 + ],
186 + "url": "",
187 + "org": "levio",
188 + "source_id": "levio",
189 + "qc": 42,
190 + "api_name": "Levio"
191 + },
192 + {
193 + "ats": "greenhouse",
194 + "employer": "Sid Lee",
195 + "sectors": [
196 + "Marketing",
197 + "Création"
198 + ],
199 + "cities": [
200 + "Montréal"
201 + ],
202 + "url": "",
203 + "org": "sidlee",
204 + "source_id": "sidlee",
205 + "qc": 15,
206 + "api_name": "Sid Lee"
207 + },
208 + {
209 + "ats": "greenhouse",
210 + "employer": "Valtech",
211 + "sectors": [
212 + "TI"
213 + ],
214 + "cities": [
215 + "Montréal"
216 + ],
217 + "url": "",
218 + "org": "valtech",
219 + "source_id": "valtech",
220 + "qc": 17,
221 + "api_name": "Valtech"
222 + },
223 + {
224 + "ats": "greenhouse",
225 + "employer": "Vooban",
226 + "sectors": [
227 + "TI"
228 + ],
229 + "cities": [
230 + "Québec"
231 + ],
232 + "url": "",
233 + "org": "vooban",
234 + "source_id": "vooban",
235 + "qc": 18,
236 + "api_name": ""
237 + },
238 + {
239 + "ats": "lever",
240 + "employer": "Behaviour Interactif",
241 + "sectors": [
242 + "Jeux vidéo"
243 + ],
244 + "cities": [
245 + "Montréal"
246 + ],
247 + "url": "",
248 + "org": "bhvr",
249 + "source_id": "bhvr",
250 + "qc": 39,
251 + "api_name": ""
252 + },
253 + {
254 + "ats": "lever",
255 + "employer": "Cirque du Soleil",
256 + "sectors": [
257 + "Divertissement",
258 + "Création"
259 + ],
260 + "cities": [
261 + "Montréal"
262 + ],
263 + "url": "https://www.cirquedusoleil.com",
264 + "org": "cirquedusoleil",
265 + "source_id": "cirquedusoleil",
266 + "qc": 19,
267 + "api_name": ""
268 + },
269 + {
270 + "ats": "lever",
271 + "employer": "Mirego",
272 + "sectors": [
273 + "TI"
274 + ],
275 + "cities": [
276 + "Québec",
277 + "Montréal"
278 + ],
279 + "url": "",
280 + "org": "mirego",
281 + "source_id": "mirego",
282 + "qc": 11,
283 + "api_name": ""
284 + },
285 + {
286 + "ats": "lever",
287 + "employer": "Spiria",
288 + "sectors": [
289 + "TI"
290 + ],
291 + "cities": [
292 + "Montréal",
293 + "Gatineau"
294 + ],
295 + "url": "",
296 + "org": "spiria",
297 + "source_id": "spiria",
298 + "qc": 16,
299 + "api_name": ""
300 + },
301 + {
302 + "ats": "recruitee",
303 + "employer": "Amilia",
304 + "sectors": [
305 + "TI"
306 + ],
307 + "cities": [
308 + "Montréal"
309 + ],
310 + "url": "",
311 + "org": "amilia",
312 + "source_id": "amilia",
313 + "qc": 1,
314 + "api_name": ""
315 + },
316 + {
317 + "ats": "smartrecruiters",
318 + "employer": "Gameloft",
319 + "sectors": [
320 + "Jeux vidéo"
321 + ],
322 + "cities": [
323 + "Montréal"
324 + ],
325 + "url": "https://www.gameloft.com",
326 + "org": "gameloft",
327 + "source_id": "gameloft",
328 + "qc": 15,
329 + "api_name": "Gameloft"
330 + },
331 + {
332 + "ats": "smartrecruiters",
333 + "employer": "GoLogic",
334 + "sectors": [
335 + "TI"
336 + ],
337 + "cities": [
338 + "Montréal"
339 + ],
340 + "url": "",
341 + "org": "gologic",
342 + "source_id": "gologic",
343 + "qc": 1,
344 + "api_name": "Gologic"
345 + },
346 + {
347 + "ats": "smartrecruiters",
348 + "employer": "Groupe Canam",
349 + "sectors": [
350 + "Production",
351 + "Construction"
352 + ],
353 + "cities": [
354 + "Saint-Georges"
355 + ],
356 + "url": "",
357 + "org": "groupecanam",
358 + "source_id": "groupecanam",
359 + "qc": 66,
360 + "api_name": "Canam"
361 + },
362 + {
363 + "ats": "smartrecruiters",
364 + "employer": "Playmind",
365 + "sectors": [
366 + "Jeux vidéo"
367 + ],
368 + "cities": [
369 + "Montréal"
370 + ],
371 + "url": "",
372 + "org": "playmind",
373 + "source_id": "playmind",
374 + "qc": 2,
375 + "api_name": "PLAYMIND"
376 + },
377 + {
378 + "ats": "smartrecruiters",
379 + "employer": "QSL",
380 + "sectors": [],
381 + "cities": [
382 + "Québec"
383 + ],
384 + "url": "https://qsl.com",
385 + "org": "qsl",
386 + "source_id": "qsl",
387 + "qc": 37,
388 + "api_name": "QSL"
389 + },
390 + {
391 + "ats": "smartrecruiters",
392 + "employer": "Stelpro",
393 + "sectors": [
394 + "Production"
395 + ],
396 + "cities": [
397 + "Saint-Bruno-de-Montarville"
398 + ],
399 + "url": "",
400 + "org": "stelpro",
401 + "source_id": "stelpro",
402 + "qc": 1,
403 + "api_name": "Stelpro"
404 + },
405 + {
406 + "ats": "smartrecruiters",
407 + "employer": "Talsom",
408 + "sectors": [
409 + "TI"
410 + ],
411 + "cities": [
412 + "Montréal"
413 + ],
414 + "url": "",
415 + "org": "talsom",
416 + "source_id": "talsom",
417 + "qc": 1,
418 + "api_name": "Talsom"
419 + },
420 + {
421 + "ats": "smartrecruiters",
422 + "employer": "Vidéotron",
423 + "sectors": [],
424 + "cities": [
425 + "Montréal"
426 + ],
427 + "url": "https://videotron.com",
428 + "org": "videotron",
429 + "source_id": "videotron",
430 + "qc": 77,
431 + "api_name": "Videotron"
432 + },
433 + {
434 + "ats": "smartrecruiters",
435 + "employer": "Vosker",
436 + "sectors": [
437 + "TI",
438 + "Production"
439 + ],
440 + "cities": [
441 + "Victoriaville"
442 + ],
443 + "url": "",
444 + "org": "vosker",
445 + "source_id": "vosker",
446 + "qc": 20,
447 + "api_name": "Vosker"
448 + },
449 + {
450 + "ats": "workable",
451 + "employer": "Genetec",
452 + "sectors": [],
453 + "cities": [
454 + "Montréal"
455 + ],
456 + "url": "https://genetec.com",
457 + "org": "genetec-inc",
458 + "source_id": "genetec_inc",
459 + "qc": 81,
460 + "api_name": "Genetec"
461 + },
462 + {
463 + "ats": "workable",
464 + "employer": "Mila — Institut québécois d'IA",
465 + "sectors": [],
466 + "cities": [
467 + "Montréal"
468 + ],
469 + "url": "https://mila.quebec",
470 + "org": "mila-2",
471 + "source_id": "mila_2",
472 + "qc": 8,
473 + "api_name": "Mila - Institut québécois d'intelligence artificielle"
474 + },
475 + {
476 + "ats": "workday",
477 + "employer": "Airbus Canada",
478 + "sectors": [],
479 + "cities": [
480 + "Mirabel"
481 + ],
482 + "url": "https://airbus.com",
483 + "tenant": "ag",
484 + "host": "wd3",
485 + "site": "Airbus",
486 + "source_id": "airbus",
487 + "qc": 1,
488 + "api_name": ""
489 + },
490 + {
491 + "ats": "workday",
492 + "employer": "Alcoa Canada",
493 + "sectors": [],
494 + "cities": [
495 + "Baie-Comeau"
496 + ],
497 + "url": "https://alcoa.com",
498 + "tenant": "alcoa",
499 + "host": "wd5",
500 + "site": "Careers",
501 + "source_id": "alcoa",
502 + "qc": 2,
503 + "api_name": ""
504 + },
505 + {
506 + "ats": "workday",
507 + "employer": "Altasciences",
508 + "sectors": [
509 + "Pharmaceutique"
510 + ],
511 + "cities": [
512 + "Laval"
513 + ],
514 + "url": "",
515 + "tenant": "altasciences",
516 + "host": "wd1",
517 + "site": "Careers",
518 + "source_id": "altasciences",
519 + "qc": 6,
520 + "api_name": ""
521 + },
522 + {
523 + "ats": "workday",
524 + "employer": "BDC",
525 + "sectors": [
526 + "Finance"
527 + ],
528 + "cities": [
529 + "Montréal"
530 + ],
531 + "url": "",
532 + "tenant": "bdc",
533 + "host": "wd10",
534 + "site": "bdc_careers",
535 + "source_id": "bdc",
536 + "qc": 10,
537 + "api_name": ""
538 + },
539 + {
540 + "ats": "workday",
541 + "employer": "Beneva",
542 + "sectors": [],
543 + "cities": [
544 + "Québec"
545 + ],
546 + "url": "https://beneva.ca",
547 + "tenant": "beneva",
548 + "host": "wd10",
549 + "site": "BenevaSite_carriere",
550 + "source_id": "beneva",
551 + "qc": 4,
552 + "api_name": ""
553 + },
554 + {
555 + "ats": "workday",
556 + "employer": "CAA-Québec",
557 + "sectors": [],
558 + "cities": [
559 + "Québec"
560 + ],
561 + "url": "https://caaquebec.com",
562 + "tenant": "caaquebec",
563 + "host": "wd3",
564 + "site": "CarrieresCAA-Quebec",
565 + "source_id": "caaquebec",
566 + "qc": 12,
567 + "api_name": ""
568 + },
569 + {
570 + "ats": "workday",
571 + "employer": "Canadian Tire",
572 + "sectors": [],
573 + "cities": [
574 + "Montréal"
575 + ],
576 + "url": "https://canadiantire.ca",
577 + "tenant": "canadiantirecorporation",
578 + "host": "wd3",
579 + "site": "Enterprise_External_Careers_Site",
580 + "source_id": "canadiantirecorporation",
581 + "qc": 9,
582 + "api_name": ""
583 + },
584 + {
585 + "ats": "workday",
586 + "employer": "CDPQ — Caisse de dépôt et placement du Québec",
587 + "sectors": [],
588 + "cities": [
589 + "Montréal"
590 + ],
591 + "url": "https://cdpq.com",
592 + "tenant": "cdpq",
593 + "host": "wd10",
594 + "site": "CDPQ",
595 + "source_id": "cdpq",
596 + "qc": 20,
597 + "api_name": ""
598 + },
599 + {
600 + "ats": "workday",
601 + "employer": "Church & Dwight Canada",
602 + "sectors": [
603 + "Production"
604 + ],
605 + "cities": [],
606 + "url": "https://churchdwight.ca",
607 + "tenant": "churchdwight",
608 + "host": "wd1",
609 + "site": "chdcanadacareers",
610 + "source_id": "churchdwight",
611 + "qc": 6,
612 + "api_name": ""
613 + },
614 + {
615 + "ats": "workday",
616 + "employer": "EXFO",
617 + "sectors": [
618 + "TI",
619 + "Télécommunications"
620 + ],
621 + "cities": [
622 + "Québec"
623 + ],
624 + "url": "",
625 + "tenant": "exfo",
626 + "host": "wd10",
627 + "site": "exfo_careers",
628 + "source_id": "exfo",
629 + "qc": 12,
630 + "api_name": ""
631 + },
632 + {
633 + "ats": "workday",
634 + "employer": "Fonds de solidarité FTQ",
635 + "sectors": [],
636 + "cities": [
637 + "Montréal"
638 + ],
639 + "url": "https://fondsftq.com",
640 + "tenant": "fondsftq",
641 + "host": "wd10",
642 + "site": "sitecarriere_externe",
643 + "source_id": "fondsftq",
644 + "qc": 10,
645 + "api_name": ""
646 + },
647 + {
648 + "ats": "workday",
649 + "employer": "Intact Corporation financière",
650 + "sectors": [
651 + "Assurance"
652 + ],
653 + "cities": [
654 + "Montréal"
655 + ],
656 + "url": "",
657 + "tenant": "intactfc",
658 + "host": "wd3",
659 + "site": "Intactfc",
660 + "source_id": "intactfc",
661 + "qc": 2,
662 + "api_name": ""
663 + },
664 + {
665 + "ats": "workday",
666 + "employer": "Investissements PSP",
667 + "sectors": [],
668 + "cities": [
669 + "Montréal"
670 + ],
671 + "url": "https://investpsp.com",
672 + "tenant": "investpsp",
673 + "host": "wd3",
674 + "site": "psp_careers",
675 + "source_id": "investpsp",
676 + "qc": 19,
677 + "api_name": ""
678 + },
679 + {
680 + "ats": "workday",
681 + "employer": "Investissement Québec",
682 + "sectors": [],
683 + "cities": [
684 + "Québec"
685 + ],
686 + "url": "https://investquebec.com",
687 + "tenant": "investquebec",
688 + "host": "wd10",
689 + "site": "Ext",
690 + "source_id": "investquebec",
691 + "qc": 16,
692 + "api_name": ""
693 + },
694 + {
695 + "ats": "workday",
696 + "employer": "Plusgrade",
697 + "sectors": [],
698 + "cities": [
699 + "Montréal"
700 + ],
701 + "url": "https://plusgrade.com",
702 + "tenant": "plusgrade",
703 + "host": "wd10",
704 + "site": "Plusgrade_External_Site",
705 + "source_id": "plusgrade",
706 + "qc": 4,
707 + "api_name": ""
708 + },
709 + {
710 + "ats": "workday",
711 + "employer": "Promutuel Assurance",
712 + "sectors": [],
713 + "cities": [
714 + "Québec"
715 + ],
716 + "url": "https://promutuelassurance.ca",
717 + "tenant": "promutuel",
718 + "host": "wd3",
719 + "site": "promutuel_carrieres",
720 + "source_id": "promutuel",
721 + "qc": 7,
722 + "api_name": ""
723 + },
724 + {
725 + "ats": "workday",
726 + "employer": "Raymond Chabot Grant Thornton",
727 + "sectors": [],
728 + "cities": [
729 + "Montréal"
730 + ],
731 + "url": "https://rcgt.com",
732 + "tenant": "rcgt",
733 + "host": "wd3",
734 + "site": "External",
735 + "source_id": "rcgt",
736 + "qc": 17,
737 + "api_name": ""
738 + },
739 + {
740 + "ats": "workday",
741 + "employer": "Rio Tinto",
742 + "sectors": [
743 + "Mines"
744 + ],
745 + "cities": [
746 + "Saguenay",
747 + "Montréal"
748 + ],
749 + "url": "",
750 + "tenant": "riotinto",
751 + "host": "wd3",
752 + "site": "riotinto_careers",
753 + "source_id": "riotinto",
754 + "qc": 3,
755 + "api_name": ""
756 + },
757 + {
758 + "ats": "workday",
759 + "employer": "Suncor Énergie",
760 + "sectors": [],
761 + "cities": [
762 + "Montréal"
763 + ],
764 + "url": "https://suncor.com",
765 + "tenant": "suncor",
766 + "host": "wd1",
767 + "site": "Suncor_External",
768 + "source_id": "suncor",
769 + "qc": 2,
770 + "api_name": ""
771 + },
772 + {
773 + "ats": "workday",
774 + "employer": "Walmart Canada",
775 + "sectors": [],
776 + "cities": [
777 + "Montréal"
778 + ],
779 + "url": "https://walmart.ca",
780 + "tenant": "walmart",
781 + "host": "wd504",
782 + "site": "WalmartExternal",
783 + "source_id": "walmart",
784 + "qc": 2,
785 + "api_name": ""
786 + },
787 + {
788 + "ats": "bamboohr",
789 + "employer": "MXO",
790 + "sectors": [
791 + "manufacturier/commerce"
792 + ],
793 + "cities": [],
794 + "url": "https://mxoconseil.com",
795 + "org": "mxo",
796 + "qc": 6,
797 + "api_name": ""
798 + },
799 + {
800 + "ats": "bamboohr",
801 + "employer": "Poulet Rouge",
802 + "sectors": [
803 + "restauration"
804 + ],
805 + "cities": [
806 + "Montréal"
807 + ],
808 + "url": "https://poulet-rouge.ca",
809 + "org": "pouletrouge",
810 + "qc": 1,
811 + "api_name": ""
812 + },
813 + {
814 + "ats": "bamboohr",
815 + "employer": "Ziptrek Tremblant",
816 + "sectors": [
817 + "manufacturier/commerce"
818 + ],
819 + "cities": [],
820 + "url": "https://tremblant.ziptrek.com",
821 + "org": "ziptrekeco",
822 + "qc": 2,
823 + "api_name": ""
824 + },
825 + {
826 + "ats": "workday",
827 + "employer": "JG Rive-Sud (GFS)",
828 + "sectors": [
829 + "manufacturier/commerce"
830 + ],
831 + "cities": [],
832 + "url": "https://jgfruitsetlegumes.com",
833 + "tenant": "gfs",
834 + "host": "wd5",
835 + "site": "jgrive-sud",
836 + "source_id": "jgrivesud",
837 + "qc": 10,
838 + "api_name": ""
839 + },
840 + {
841 + "ats": "workday",
842 + "employer": "Service alimentaire Gordon",
843 + "sectors": [
844 + "manufacturier/commerce"
845 + ],
846 + "cities": [],
847 + "url": "https://gfs.ca",
848 + "tenant": "gfs",
849 + "host": "wd5",
850 + "site": "usjobs-french",
851 + "source_id": "gfs",
852 + "qc": 19,
853 + "api_name": ""
854 + },
855 + {
856 + "ats": "workday",
857 + "employer": "Scotts Canada (Fafard)",
858 + "sectors": [
859 + "manufacturier/commerce"
860 + ],
861 + "cities": [],
862 + "url": "https://scottsmiraclegro.com",
863 + "tenant": "scottsmiraclegro",
864 + "host": "wd5",
865 + "site": "SMGExternal",
866 + "qc": 3,
867 + "api_name": ""
868 + },
869 + {
870 + "ats": "workday",
871 + "employer": "Brasseries Sleeman (Unibroue)",
872 + "sectors": [
873 + "manufacturier/commerce"
874 + ],
875 + "cities": [],
876 + "url": "https://unibroue.com",
877 + "tenant": "sleeman",
878 + "host": "wd3",
879 + "site": "SBL",
880 + "qc": 3,
881 + "api_name": ""
882 + }
883 +]
modified data/sources.json +1041 −1
@@ -3,7 +3,7 @@
3 3 "project": "Job·Ka — Groupe KA",
4 4 "author": "Simon-Pierre Boucher <contact@spboucher.ai>",
5 5 "role": "Registre descriptif des sources (alimente la page Sources)",
6 "updated": "2026-08-17"
6 + "updated": "2026-08-18"
7 7 },
8 8 "sources": [
9 9 {
@@ -1714,6 +1714,1046 @@
1714 1714 "connector": "bamboohr",
1715 1715 "status": "actif",
1716 1716 "region": "Montréal"
1717 + },
1718 + {
1719 + "id": "lightspeedhq",
1720 + "name": "Lightspeed Commerce",
1721 + "url": "",
1722 + "careers_url": "https://jobs.ashbyhq.com/lightspeedhq",
1723 + "sectors": [
1724 + "TI"
1725 + ],
1726 + "connector": "ashby",
1727 + "status": "actif",
1728 + "region": "Montréal"
1729 + },
1730 + {
1731 + "id": "bigbang",
1732 + "name": "Big Bang",
1733 + "url": "",
1734 + "careers_url": "https://bigbang.bamboohr.com/careers",
1735 + "sectors": [
1736 + "TI"
1737 + ],
1738 + "connector": "bamboohr",
1739 + "status": "actif",
1740 + "region": "Montréal"
1741 + },
1742 + {
1743 + "id": "broccolini",
1744 + "name": "Broccolini",
1745 + "url": "",
1746 + "careers_url": "https://broccolini.bamboohr.com/careers",
1747 + "sectors": [
1748 + "Construction",
1749 + "Immobilier"
1750 + ],
1751 + "connector": "bamboohr",
1752 + "status": "actif",
1753 + "region": "Montréal"
1754 + },
1755 + {
1756 + "id": "cordonbleu",
1757 + "name": "Aliments Ouimet-Cordon Bleu inc.",
1758 + "url": "https://clarkcordonbleu.ca",
1759 + "careers_url": "https://cordonbleu.bamboohr.com/careers",
1760 + "sectors": [
1761 + "Production"
1762 + ],
1763 + "connector": "bamboohr",
1764 + "status": "actif",
1765 + "region": ""
1766 + },
1767 + {
1768 + "id": "devimco",
1769 + "name": "Devimco Appartements",
1770 + "url": "https://devimco.com",
1771 + "careers_url": "https://devimco.bamboohr.com/careers",
1772 + "sectors": [
1773 + "Immobilier"
1774 + ],
1775 + "connector": "bamboohr",
1776 + "status": "actif",
1777 + "region": ""
1778 + },
1779 + {
1780 + "id": "evlo",
1781 + "name": "EVLO (Hydro-Québec)",
1782 + "url": "",
1783 + "careers_url": "https://evlo.bamboohr.com/careers",
1784 + "sectors": [
1785 + "Énergie"
1786 + ],
1787 + "connector": "bamboohr",
1788 + "status": "actif",
1789 + "region": "Varennes"
1790 + },
1791 + {
1792 + "id": "fenetresconcept",
1793 + "name": "Fenêtres Concept",
1794 + "url": "https://fenetresconcept.com",
1795 + "careers_url": "https://fenetresconcept.bamboohr.com/careers",
1796 + "sectors": [
1797 + "Production"
1798 + ],
1799 + "connector": "bamboohr",
1800 + "status": "actif",
1801 + "region": ""
1802 + },
1803 + {
1804 + "id": "flare",
1805 + "name": "Flare (cybersécurité)",
1806 + "url": "",
1807 + "careers_url": "https://flare.bamboohr.com/careers",
1808 + "sectors": [
1809 + "TI"
1810 + ],
1811 + "connector": "bamboohr",
1812 + "status": "actif",
1813 + "region": "Montréal"
1814 + },
1815 + {
1816 + "id": "korem",
1817 + "name": "Korem",
1818 + "url": "",
1819 + "careers_url": "https://korem.bamboohr.com/careers",
1820 + "sectors": [
1821 + "TI"
1822 + ],
1823 + "connector": "bamboohr",
1824 + "status": "actif",
1825 + "region": "Québec"
1826 + },
1827 + {
1828 + "id": "logisco",
1829 + "name": "Logisco",
1830 + "url": "https://logisco.com",
1831 + "careers_url": "https://logisco.bamboohr.com/careers",
1832 + "sectors": [
1833 + "Immobilier"
1834 + ],
1835 + "connector": "bamboohr",
1836 + "status": "actif",
1837 + "region": ""
1838 + },
1839 + {
1840 + "id": "alayacare",
1841 + "name": "AlayaCare",
1842 + "url": "https://www.alayacare.com",
1843 + "careers_url": "https://job-boards.greenhouse.io/alayacare",
1844 + "sectors": [
1845 + "TI",
1846 + "Santé"
1847 + ],
1848 + "connector": "greenhouse",
1849 + "status": "actif",
1850 + "region": "Montréal"
1851 + },
1852 + {
1853 + "id": "gurussolutions",
1854 + "name": "GURUS Solutions",
1855 + "url": "",
1856 + "careers_url": "https://job-boards.greenhouse.io/gurussolutions",
1857 + "sectors": [
1858 + "TI"
1859 + ],
1860 + "connector": "greenhouse",
1861 + "status": "actif",
1862 + "region": "Montréal"
1863 + },
1864 + {
1865 + "id": "levio",
1866 + "name": "Levio",
1867 + "url": "",
1868 + "careers_url": "https://job-boards.greenhouse.io/levio",
1869 + "sectors": [
1870 + "TI"
1871 + ],
1872 + "connector": "greenhouse",
1873 + "status": "actif",
1874 + "region": "Québec, Montréal"
1875 + },
1876 + {
1877 + "id": "sidlee",
1878 + "name": "Sid Lee",
1879 + "url": "",
1880 + "careers_url": "https://job-boards.greenhouse.io/sidlee",
1881 + "sectors": [
1882 + "Marketing",
1883 + "Création"
1884 + ],
1885 + "connector": "greenhouse",
1886 + "status": "actif",
1887 + "region": "Montréal"
1888 + },
1889 + {
1890 + "id": "valtech",
1891 + "name": "Valtech",
1892 + "url": "",
1893 + "careers_url": "https://job-boards.greenhouse.io/valtech",
1894 + "sectors": [
1895 + "TI"
1896 + ],
1897 + "connector": "greenhouse",
1898 + "status": "actif",
1899 + "region": "Montréal"
1900 + },
1901 + {
1902 + "id": "vooban",
1903 + "name": "Vooban",
1904 + "url": "",
1905 + "careers_url": "https://job-boards.greenhouse.io/vooban",
1906 + "sectors": [
1907 + "TI"
1908 + ],
1909 + "connector": "greenhouse",
1910 + "status": "actif",
1911 + "region": "Québec"
1912 + },
1913 + {
1914 + "id": "bhvr",
1915 + "name": "Behaviour Interactif",
1916 + "url": "",
1917 + "careers_url": "https://jobs.lever.co/bhvr",
1918 + "sectors": [
1919 + "Jeux vidéo"
1920 + ],
1921 + "connector": "lever",
1922 + "status": "actif",
1923 + "region": "Montréal"
1924 + },
1925 + {
1926 + "id": "cirquedusoleil",
1927 + "name": "Cirque du Soleil",
1928 + "url": "https://www.cirquedusoleil.com",
1929 + "careers_url": "https://jobs.lever.co/cirquedusoleil",
1930 + "sectors": [
1931 + "Divertissement",
1932 + "Création"
1933 + ],
1934 + "connector": "lever",
1935 + "status": "actif",
1936 + "region": "Montréal"
1937 + },
1938 + {
1939 + "id": "mirego",
1940 + "name": "Mirego",
1941 + "url": "",
1942 + "careers_url": "https://jobs.lever.co/mirego",
1943 + "sectors": [
1944 + "TI"
1945 + ],
1946 + "connector": "lever",
1947 + "status": "actif",
1948 + "region": "Québec, Montréal"
1949 + },
1950 + {
1951 + "id": "spiria",
1952 + "name": "Spiria",
1953 + "url": "",
1954 + "careers_url": "https://jobs.lever.co/spiria",
1955 + "sectors": [
1956 + "TI"
1957 + ],
1958 + "connector": "lever",
1959 + "status": "actif",
1960 + "region": "Montréal, Gatineau"
1961 + },
1962 + {
1963 + "id": "amilia",
1964 + "name": "Amilia",
1965 + "url": "",
1966 + "careers_url": "https://amilia.recruitee.com",
1967 + "sectors": [
1968 + "TI"
1969 + ],
1970 + "connector": "recruitee",
1971 + "status": "actif",
1972 + "region": "Montréal"
1973 + },
1974 + {
1975 + "id": "gameloft",
1976 + "name": "Gameloft",
1977 + "url": "https://www.gameloft.com",
1978 + "careers_url": "https://jobs.smartrecruiters.com/company/gameloft",
1979 + "sectors": [
1980 + "Jeux vidéo"
1981 + ],
1982 + "connector": "smartrecruiters",
1983 + "status": "actif",
1984 + "region": "Montréal"
1985 + },
1986 + {
1987 + "id": "gologic",
1988 + "name": "GoLogic",
1989 + "url": "",
1990 + "careers_url": "https://jobs.smartrecruiters.com/company/gologic",
1991 + "sectors": [
1992 + "TI"
1993 + ],
1994 + "connector": "smartrecruiters",
1995 + "status": "actif",
1996 + "region": "Montréal"
1997 + },
1998 + {
1999 + "id": "groupecanam",
2000 + "name": "Groupe Canam",
2001 + "url": "",
2002 + "careers_url": "https://jobs.smartrecruiters.com/company/groupecanam",
2003 + "sectors": [
2004 + "Production",
2005 + "Construction"
2006 + ],
2007 + "connector": "smartrecruiters",
2008 + "status": "actif",
2009 + "region": "Saint-Georges"
2010 + },
2011 + {
2012 + "id": "playmind",
2013 + "name": "Playmind",
2014 + "url": "",
2015 + "careers_url": "https://jobs.smartrecruiters.com/company/playmind",
2016 + "sectors": [
2017 + "Jeux vidéo"
2018 + ],
2019 + "connector": "smartrecruiters",
2020 + "status": "actif",
2021 + "region": "Montréal"
2022 + },
2023 + {
2024 + "id": "qsl",
2025 + "name": "QSL",
2026 + "url": "https://qsl.com",
2027 + "careers_url": "https://jobs.smartrecruiters.com/company/qsl",
2028 + "sectors": [],
2029 + "connector": "smartrecruiters",
2030 + "status": "actif",
2031 + "region": "Québec"
2032 + },
2033 + {
2034 + "id": "stelpro",
2035 + "name": "Stelpro",
2036 + "url": "",
2037 + "careers_url": "https://jobs.smartrecruiters.com/company/stelpro",
2038 + "sectors": [
2039 + "Production"
2040 + ],
2041 + "connector": "smartrecruiters",
2042 + "status": "actif",
2043 + "region": "Saint-Bruno-de-Montarville"
2044 + },
2045 + {
2046 + "id": "talsom",
2047 + "name": "Talsom",
2048 + "url": "",
2049 + "careers_url": "https://jobs.smartrecruiters.com/company/talsom",
2050 + "sectors": [
2051 + "TI"
2052 + ],
2053 + "connector": "smartrecruiters",
2054 + "status": "actif",
2055 + "region": "Montréal"
2056 + },
2057 + {
2058 + "id": "videotron",
2059 + "name": "Vidéotron",
2060 + "url": "https://videotron.com",
2061 + "careers_url": "https://jobs.smartrecruiters.com/company/videotron",
2062 + "sectors": [],
2063 + "connector": "smartrecruiters",
2064 + "status": "actif",
2065 + "region": "Montréal"
2066 + },
2067 + {
2068 + "id": "vosker",
2069 + "name": "Vosker",
2070 + "url": "",
2071 + "careers_url": "https://jobs.smartrecruiters.com/company/vosker",
2072 + "sectors": [
2073 + "TI",
2074 + "Production"
2075 + ],
2076 + "connector": "smartrecruiters",
2077 + "status": "actif",
2078 + "region": "Victoriaville"
2079 + },
2080 + {
2081 + "id": "genetec_inc",
2082 + "name": "Genetec",
2083 + "url": "https://genetec.com",
2084 + "careers_url": "https://apply.workable.com/genetec-inc",
2085 + "sectors": [],
2086 + "connector": "workable",
2087 + "status": "actif",
2088 + "region": "Montréal"
2089 + },
2090 + {
2091 + "id": "mila_2",
2092 + "name": "Mila — Institut québécois d'IA",
2093 + "url": "https://mila.quebec",
2094 + "careers_url": "https://apply.workable.com/mila-2",
2095 + "sectors": [],
2096 + "connector": "workable",
2097 + "status": "actif",
2098 + "region": "Montréal"
2099 + },
2100 + {
2101 + "id": "airbus",
2102 + "name": "Airbus Canada",
2103 + "url": "https://airbus.com",
2104 + "careers_url": "https://ag.wd3.myworkdayjobs.com/Airbus",
2105 + "sectors": [],
2106 + "connector": "workday",
2107 + "status": "actif",
2108 + "region": "Mirabel"
2109 + },
2110 + {
2111 + "id": "alcoa",
2112 + "name": "Alcoa Canada",
2113 + "url": "https://alcoa.com",
2114 + "careers_url": "https://alcoa.wd5.myworkdayjobs.com/Careers",
2115 + "sectors": [],
2116 + "connector": "workday",
2117 + "status": "actif",
2118 + "region": "Baie-Comeau"
2119 + },
2120 + {
2121 + "id": "altasciences",
2122 + "name": "Altasciences",
2123 + "url": "",
2124 + "careers_url": "https://altasciences.wd1.myworkdayjobs.com/Careers",
2125 + "sectors": [
2126 + "Pharmaceutique"
2127 + ],
2128 + "connector": "workday",
2129 + "status": "actif",
2130 + "region": "Laval"
2131 + },
2132 + {
2133 + "id": "bdc",
2134 + "name": "BDC",
2135 + "url": "",
2136 + "careers_url": "https://bdc.wd10.myworkdayjobs.com/bdc_careers",
2137 + "sectors": [
2138 + "Finance"
2139 + ],
2140 + "connector": "workday",
2141 + "status": "actif",
2142 + "region": "Montréal"
2143 + },
2144 + {
2145 + "id": "beneva",
2146 + "name": "Beneva",
2147 + "url": "https://beneva.ca",
2148 + "careers_url": "https://beneva.wd10.myworkdayjobs.com/BenevaSite_carriere",
2149 + "sectors": [],
2150 + "connector": "workday",
2151 + "status": "actif",
2152 + "region": "Québec"
2153 + },
2154 + {
2155 + "id": "caaquebec",
2156 + "name": "CAA-Québec",
2157 + "url": "https://caaquebec.com",
2158 + "careers_url": "https://caaquebec.wd3.myworkdayjobs.com/CarrieresCAA-Quebec",
2159 + "sectors": [],
2160 + "connector": "workday",
2161 + "status": "actif",
2162 + "region": "Québec"
2163 + },
2164 + {
2165 + "id": "canadiantirecorporation",
2166 + "name": "Canadian Tire",
2167 + "url": "https://canadiantire.ca",
2168 + "careers_url": "https://canadiantirecorporation.wd3.myworkdayjobs.com/Enterprise_External_Careers_Site",
2169 + "sectors": [],
2170 + "connector": "workday",
2171 + "status": "actif",
2172 + "region": "Montréal"
2173 + },
2174 + {
2175 + "id": "cdpq",
2176 + "name": "CDPQ — Caisse de dépôt et placement du Québec",
2177 + "url": "https://cdpq.com",
2178 + "careers_url": "https://cdpq.wd10.myworkdayjobs.com/CDPQ",
2179 + "sectors": [],
2180 + "connector": "workday",
2181 + "status": "actif",
2182 + "region": "Montréal"
2183 + },
2184 + {
2185 + "id": "churchdwight",
2186 + "name": "Church & Dwight Canada",
2187 + "url": "https://churchdwight.ca",
2188 + "careers_url": "https://churchdwight.wd1.myworkdayjobs.com/chdcanadacareers",
2189 + "sectors": [
2190 + "Production"
2191 + ],
2192 + "connector": "workday",
2193 + "status": "actif",
2194 + "region": ""
2195 + },
2196 + {
2197 + "id": "exfo",
2198 + "name": "EXFO",
2199 + "url": "",
2200 + "careers_url": "https://exfo.wd10.myworkdayjobs.com/exfo_careers",
2201 + "sectors": [
2202 + "TI",
2203 + "Télécommunications"
2204 + ],
2205 + "connector": "workday",
2206 + "status": "actif",
2207 + "region": "Québec"
2208 + },
2209 + {
2210 + "id": "fondsftq",
2211 + "name": "Fonds de solidarité FTQ",
2212 + "url": "https://fondsftq.com",
2213 + "careers_url": "https://fondsftq.wd10.myworkdayjobs.com/sitecarriere_externe",
2214 + "sectors": [],
2215 + "connector": "workday",
2216 + "status": "actif",
2217 + "region": "Montréal"
2218 + },
2219 + {
2220 + "id": "intactfc",
2221 + "name": "Intact Corporation financière",
2222 + "url": "",
2223 + "careers_url": "https://intactfc.wd3.myworkdayjobs.com/Intactfc",
2224 + "sectors": [
2225 + "Assurance"
2226 + ],
2227 + "connector": "workday",
2228 + "status": "actif",
2229 + "region": "Montréal"
2230 + },
2231 + {
2232 + "id": "investpsp",
2233 + "name": "Investissements PSP",
2234 + "url": "https://investpsp.com",
2235 + "careers_url": "https://investpsp.wd3.myworkdayjobs.com/psp_careers",
2236 + "sectors": [],
2237 + "connector": "workday",
2238 + "status": "actif",
2239 + "region": "Montréal"
2240 + },
2241 + {
2242 + "id": "investquebec",
2243 + "name": "Investissement Québec",
2244 + "url": "https://investquebec.com",
2245 + "careers_url": "https://investquebec.wd10.myworkdayjobs.com/Ext",
2246 + "sectors": [],
2247 + "connector": "workday",
2248 + "status": "actif",
2249 + "region": "Québec"
2250 + },
2251 + {
2252 + "id": "plusgrade",
2253 + "name": "Plusgrade",
2254 + "url": "https://plusgrade.com",
2255 + "careers_url": "https://plusgrade.wd10.myworkdayjobs.com/Plusgrade_External_Site",
2256 + "sectors": [],
2257 + "connector": "workday",
2258 + "status": "actif",
2259 + "region": "Montréal"
2260 + },
2261 + {
2262 + "id": "promutuel",
2263 + "name": "Promutuel Assurance",
2264 + "url": "https://promutuelassurance.ca",
2265 + "careers_url": "https://promutuel.wd3.myworkdayjobs.com/promutuel_carrieres",
2266 + "sectors": [],
2267 + "connector": "workday",
2268 + "status": "actif",
2269 + "region": "Québec"
2270 + },
2271 + {
2272 + "id": "rcgt",
2273 + "name": "Raymond Chabot Grant Thornton",
2274 + "url": "https://rcgt.com",
2275 + "careers_url": "https://rcgt.wd3.myworkdayjobs.com/External",
2276 + "sectors": [],
2277 + "connector": "workday",
2278 + "status": "actif",
2279 + "region": "Montréal"
2280 + },
2281 + {
2282 + "id": "riotinto",
2283 + "name": "Rio Tinto",
2284 + "url": "",
2285 + "careers_url": "https://riotinto.wd3.myworkdayjobs.com/riotinto_careers",
2286 + "sectors": [
2287 + "Mines"
2288 + ],
2289 + "connector": "workday",
2290 + "status": "actif",
2291 + "region": "Saguenay, Montréal"
2292 + },
2293 + {
2294 + "id": "suncor",
2295 + "name": "Suncor Énergie",
2296 + "url": "https://suncor.com",
2297 + "careers_url": "https://suncor.wd1.myworkdayjobs.com/Suncor_External",
2298 + "sectors": [],
2299 + "connector": "workday",
2300 + "status": "actif",
2301 + "region": "Montréal"
2302 + },
2303 + {
2304 + "id": "walmart",
2305 + "name": "Walmart Canada",
2306 + "url": "https://walmart.ca",
2307 + "careers_url": "https://walmart.wd504.myworkdayjobs.com/WalmartExternal",
2308 + "sectors": [],
2309 + "connector": "workday",
2310 + "status": "actif",
2311 + "region": "Montréal"
2312 + },
2313 + {
2314 + "id": "agnico_eagle",
2315 + "name": "Agnico Eagle Mines",
2316 + "url": "https://www.agnicoeagle.com",
2317 + "careers_url": "https://agnicoeagle.taleo.net/careersection/2/jobsearch.ftl?lang=fr",
2318 + "sectors": [
2319 + "Mines",
2320 + "Ingénierie",
2321 + "Métiers spécialisés"
2322 + ],
2323 + "connector": "taleo",
2324 + "status": "actif",
2325 + "region": "Abitibi-Témiscamingue, Malartic, Rouyn-Noranda"
2326 + },
2327 + {
2328 + "id": "ciusss_est_montreal",
2329 + "name": "CIUSSS de l'Est-de-l'Île-de-Montréal",
2330 + "url": "https://ciusss-estmtl.gouv.qc.ca",
2331 + "careers_url": "https://ciusssemtl.taleo.net/careersection/cemtl/jobsearch.ftl?lang=fr",
2332 + "sectors": [
2333 + "Santé",
2334 + "Services sociaux",
2335 + "Secteur public"
2336 + ],
2337 + "connector": "taleo",
2338 + "status": "actif",
2339 + "region": "Montréal"
2340 + },
2341 + {
2342 + "id": "bayshore",
2343 + "name": "Bayshore HealthCare",
2344 + "url": "https://www.bayshore.ca",
2345 + "careers_url": "https://bayshore.taleo.net/careersection/bs_ex/jobsearch.ftl?lang=fr",
2346 + "sectors": [
2347 + "Santé",
2348 + "Soins à domicile"
2349 + ],
2350 + "connector": "taleo",
2351 + "status": "actif",
2352 + "region": "Montréal, Québec"
2353 + },
2354 + {
2355 + "id": "bell_textron",
2356 + "name": "Bell Textron Canada",
2357 + "url": "https://www.bellflight.com",
2358 + "careers_url": "https://textron.taleo.net/careersection/bell/jobsearch.ftl?lang=fr",
2359 + "sectors": [
2360 + "Aéronautique",
2361 + "Fabrication"
2362 + ],
2363 + "connector": "taleo",
2364 + "status": "actif",
2365 + "region": "Mirabel"
2366 + },
2367 + {
2368 + "id": "sante_montreal",
2369 + "name": "Santé Québec — Montréal",
2370 + "url": "https://santemontreal.ca",
2371 + "careers_url": "https://emplois.santemontreal.qc.ca/CL3/xweb/xweb.asp?clid=54327&page=joblisting&lang=2",
2372 + "sectors": [
2373 + "Santé",
2374 + "Services sociaux",
2375 + "Secteur public"
2376 + ],
2377 + "connector": "njoyn",
2378 + "status": "actif",
2379 + "region": "Montréal"
2380 + },
2381 + {
2382 + "id": "ville_gatineau",
2383 + "name": "Ville de Gatineau",
2384 + "url": "https://www.gatineau.ca",
2385 + "careers_url": "https://gatineau.njoyn.com/CL2/xweb/xweb.asp?clid=27082&page=joblisting&lang=2",
2386 + "sectors": [
2387 + "Municipal",
2388 + "Secteur public"
2389 + ],
2390 + "connector": "njoyn",
2391 + "status": "actif",
2392 + "region": "Gatineau"
2393 + },
2394 + {
2395 + "id": "ville_terrebonne",
2396 + "name": "Ville de Terrebonne",
2397 + "url": "https://www.ville.terrebonne.qc.ca",
2398 + "careers_url": "https://clients.njoyn.com/cl4/xweb/xweb.asp?clid=71764&page=joblisting&lang=2",
2399 + "sectors": [
2400 + "Municipal",
2401 + "Secteur public"
2402 + ],
2403 + "connector": "njoyn",
2404 + "status": "actif",
2405 + "region": "Terrebonne"
2406 + },
2407 + {
2408 + "id": "stl_laval",
2409 + "name": "Société de transport de Laval",
2410 + "url": "https://www.stlaval.ca",
2411 + "careers_url": "https://lavaltransit.njoyn.com/CL2/xweb/xweb.asp?clid=60406&page=joblisting&lang=2",
2412 + "sectors": [
2413 + "Transport collectif",
2414 + "Secteur public"
2415 + ],
2416 + "connector": "njoyn",
2417 + "status": "actif",
2418 + "region": "Laval"
2419 + },
2420 + {
2421 + "id": "rtc_quebec",
2422 + "name": "Réseau de transport de la Capitale",
2423 + "url": "https://www.rtcquebec.ca",
2424 + "careers_url": "https://rtc.njoyn.com/CGI/xweb/xweb.asp?clid=23009&page=joblisting&lang=2",
2425 + "sectors": [
2426 + "Transport collectif",
2427 + "Secteur public"
2428 + ],
2429 + "connector": "njoyn",
2430 + "status": "actif",
2431 + "region": "Québec"
2432 + },
2433 + {
2434 + "id": "messer_canada",
2435 + "name": "Messer Canada",
2436 + "url": "https://www.messer-ca.com",
2437 + "careers_url": "https://recruiting.ultipro.com/MES1005MESR/JobBoard/dbd63926-d756-486f-a254-b2a6ede1d26e",
2438 + "sectors": [
2439 + "Gaz industriels",
2440 + "Industrie"
2441 + ],
2442 + "connector": "ultipro",
2443 + "status": "actif",
2444 + "region": "Montréal, Québec, Saint-Georges"
2445 + },
2446 + {
2447 + "id": "capreit",
2448 + "name": "CAPREIT",
2449 + "url": "https://www.capreit.ca",
2450 + "careers_url": "https://careers-capreit.icims.com/jobs/search?ss=1",
2451 + "sectors": [
2452 + "Immobilier",
2453 + "Gestion immobilière"
2454 + ],
2455 + "connector": "icims",
2456 + "status": "actif",
2457 + "region": "Montréal"
2458 + },
2459 + {
2460 + "id": "mccarthy_tetrault",
2461 + "name": "McCarthy Tétrault",
2462 + "url": "https://www.mccarthy.ca",
2463 + "careers_url": "https://careers-mccarthyca.icims.com/jobs/search?ss=1",
2464 + "sectors": [
2465 + "Juridique",
2466 + "Services professionnels"
2467 + ],
2468 + "connector": "icims",
2469 + "status": "actif",
2470 + "region": "Montréal, Québec"
2471 + },
2472 + {
2473 + "id": "omni_hotels_horaire",
2474 + "name": "Omni Hôtel Mont-Royal",
2475 + "url": "https://www.omnihotels.com/fr/hotels/montreal-mont-royal",
2476 + "careers_url": "https://externalhourly-omnihotels.icims.com/jobs/search?ss=1",
2477 + "sectors": [
2478 + "Hôtellerie",
2479 + "Restauration"
2480 + ],
2481 + "connector": "icims",
2482 + "status": "actif",
2483 + "region": "Montréal"
2484 + },
2485 + {
2486 + "id": "omni_hotels_gestion",
2487 + "name": "Omni Hôtel Mont-Royal",
2488 + "url": "https://www.omnihotels.com/fr/hotels/montreal-mont-royal",
2489 + "careers_url": "https://externalmanager-omnihotels.icims.com/jobs/search?ss=1",
2490 + "sectors": [
2491 + "Hôtellerie",
2492 + "Gestion"
2493 + ],
2494 + "connector": "icims",
2495 + "status": "actif",
2496 + "region": "Montréal"
2497 + },
2498 + {
2499 + "id": "edf_renouvelables",
2500 + "name": "EDF Renouvelables Canada",
2501 + "url": "https://www.edf-renouvelables.ca",
2502 + "careers_url": "https://cafrench-edf-re.icims.com/jobs/search?ss=1",
2503 + "sectors": [
2504 + "Énergie",
2505 + "Ingénierie"
2506 + ],
2507 + "connector": "icims",
2508 + "status": "actif",
2509 + "region": "Montréal"
2510 + },
2511 + {
2512 + "id": "bombardier",
2513 + "name": "Bombardier",
2514 + "url": "https://bombardier.com",
2515 + "careers_url": "https://jobs.bombardier.com",
2516 + "sectors": [
2517 + "Aéronautique",
2518 + "Ingénierie",
2519 + "Fabrication"
2520 + ],
2521 + "connector": "successfactors",
2522 + "status": "actif",
2523 + "region": "Dorval, Mirabel, Montréal"
2524 + },
2525 + {
2526 + "id": "domtar",
2527 + "name": "Domtar",
2528 + "url": "https://www.domtar.com",
2529 + "careers_url": "https://jobs.domtar.com",
2530 + "sectors": [
2531 + "Pâtes et papiers",
2532 + "Foresterie",
2533 + "Fabrication"
2534 + ],
2535 + "connector": "successfactors",
2536 + "status": "actif",
2537 + "region": "Montréal, Windsor, Lachine"
2538 + },
2539 + {
2540 + "id": "cascades",
2541 + "name": "Cascades",
2542 + "url": "https://www.cascades.com",
2543 + "careers_url": "https://jobs.cascades.com",
2544 + "sectors": [
2545 + "Pâtes et papiers",
2546 + "Emballage",
2547 + "Fabrication"
2548 + ],
2549 + "connector": "successfactors",
2550 + "status": "actif",
2551 + "region": "Kingsey Falls, Drummondville, Candiac"
2552 + },
2553 + {
2554 + "id": "lassonde",
2555 + "name": "Industries Lassonde",
2556 + "url": "https://www.lassonde.com",
2557 + "careers_url": "https://workforcenow.adp.com/mascsr/default/mdf/recruitment/recruitment.html?cid=f96ce972-7721-4c7b-99ea-9a970ddc824a&ccId=19000101_000001&lang=fr_CA",
2558 + "sectors": [
2559 + "Agroalimentaire",
2560 + "Fabrication"
2561 + ],
2562 + "connector": "adp",
2563 + "status": "actif",
2564 + "region": "Rougemont, Montréal"
2565 + },
2566 + {
2567 + "id": "mitsubishi_hc_capital",
2568 + "name": "Mitsubishi HC Capital Canada",
2569 + "url": "https://www.mhccna.com",
2570 + "careers_url": "https://workforcenow.adp.com/mascsr/default/mdf/recruitment/recruitment.html?cid=b3ef4f03-f8ff-4ded-80c8-6dd5c5a224f7&ccId=9200144510729_2&lang=fr_CA",
2571 + "sectors": [
2572 + "Finance",
2573 + "Financement commercial"
2574 + ],
2575 + "connector": "adp",
2576 + "status": "actif",
2577 + "region": "Laval, Trois-Rivières"
2578 + },
2579 + {
2580 + "id": "msi_gestion",
2581 + "name": "MSI Gestion immobilière",
2582 + "url": "https://www.msimmobiliers.com",
2583 + "careers_url": "https://workforcenow.adp.com/mascsr/default/mdf/recruitment/recruitment.html?cid=47d4e792-26e5-471c-92ac-73c60131bab8&ccId=19000101_000001&lang=fr_CA",
2584 + "sectors": [
2585 + "Immobilier",
2586 + "Gestion immobilière"
2587 + ],
2588 + "connector": "adp",
2589 + "status": "actif",
2590 + "region": "Montréal"
2591 + },
2592 + {
2593 + "id": "sante_quebec",
2594 + "name": "Santé Québec",
2595 + "url": "https://sante.quebec",
2596 + "careers_url": "https://emplois.sante.quebec",
2597 + "sectors": [
2598 + "Santé",
2599 + "Secteur public",
2600 + "Administration"
2601 + ],
2602 + "connector": "digitalrecruiters",
2603 + "status": "actif",
2604 + "region": "Montréal, Québec"
2605 + },
2606 + {
2607 + "id": "cssdm",
2608 + "name": "Centre de services scolaire de Montréal",
2609 + "url": "https://www.cssdm.gouv.qc.ca",
2610 + "careers_url": "https://www.cssdm.gouv.qc.ca/travailler-cssdm/offres-emploi/",
2611 + "sectors": [
2612 + "Éducation",
2613 + "Secteur public"
2614 + ],
2615 + "connector": "workland",
2616 + "status": "actif",
2617 + "region": "Montréal"
2618 + },
2619 + {
2620 + "id": "guichet_emplois",
2621 + "name": "Guichet-Emplois (Canada)",
2622 + "url": "https://www.guichetemplois.gc.ca",
2623 + "careers_url": "https://www.guichetemplois.gc.ca/jobsearch/rechercheemplois?fprov=QC",
2624 + "sectors": [
2625 + "Portail",
2626 + "Tous secteurs"
2627 + ],
2628 + "connector": "portail",
2629 + "status": "actif",
2630 + "region": "Québec (province)",
2631 + "aggregator": true
2632 + },
2633 + {
2634 + "id": "jobillico",
2635 + "name": "Jobillico",
2636 + "url": "https://www.jobillico.com",
2637 + "careers_url": "https://www.jobillico.com/recherche-emploi",
2638 + "sectors": [
2639 + "Portail",
2640 + "Tous secteurs"
2641 + ],
2642 + "connector": "portail",
2643 + "status": "actif",
2644 + "region": "Québec (province)",
2645 + "aggregator": true
2646 + },
2647 + {
2648 + "id": "espresso_jobs",
2649 + "name": "Espresso-Jobs",
2650 + "url": "https://www.espresso-jobs.com",
2651 + "careers_url": "https://www.espresso-jobs.com/emploi",
2652 + "sectors": [
2653 + "Portail",
2654 + "TI",
2655 + "Créatif"
2656 + ],
2657 + "connector": "portail",
2658 + "status": "actif",
2659 + "region": "Québec (province)",
2660 + "aggregator": true
2661 + },
2662 + {
2663 + "id": "depot-direct",
2664 + "name": "Dépôt direct employeurs",
2665 + "url": "https://www.job-ka.com/employeurs",
2666 + "careers_url": "https://www.job-ka.com/employeurs",
2667 + "sectors": [
2668 + "Tous secteurs"
2669 + ],
2670 + "connector": "depot-direct",
2671 + "status": "modération",
2672 + "region": "Québec (province)"
2673 + },
2674 + {
2675 + "id": "mxo",
2676 + "name": "MXO",
2677 + "url": "https://mxoconseil.com",
2678 + "careers_url": "https://mxo.bamboohr.com/careers",
2679 + "sectors": [
2680 + "manufacturier/commerce"
2681 + ],
2682 + "connector": "bamboohr",
2683 + "status": "actif",
2684 + "region": ""
2685 + },
2686 + {
2687 + "id": "pouletrouge",
2688 + "name": "Poulet Rouge",
2689 + "url": "https://poulet-rouge.ca",
2690 + "careers_url": "https://pouletrouge.bamboohr.com/careers",
2691 + "sectors": [
2692 + "restauration"
2693 + ],
2694 + "connector": "bamboohr",
2695 + "status": "actif",
2696 + "region": "Montréal"
2697 + },
2698 + {
2699 + "id": "ziptrekeco",
2700 + "name": "Ziptrek Tremblant",
2701 + "url": "https://tremblant.ziptrek.com",
2702 + "careers_url": "https://ziptrekeco.bamboohr.com/careers",
2703 + "sectors": [
2704 + "manufacturier/commerce"
2705 + ],
2706 + "connector": "bamboohr",
2707 + "status": "actif",
2708 + "region": ""
2709 + },
2710 + {
2711 + "id": "jgrivesud",
2712 + "name": "JG Rive-Sud (GFS)",
2713 + "url": "https://jgfruitsetlegumes.com",
2714 + "careers_url": "https://gfs.wd5.myworkdayjobs.com/jgrive-sud",
2715 + "sectors": [
2716 + "manufacturier/commerce"
2717 + ],
2718 + "connector": "workday",
2719 + "status": "actif",
2720 + "region": ""
2721 + },
2722 + {
2723 + "id": "gfs",
2724 + "name": "Service alimentaire Gordon",
2725 + "url": "https://gfs.ca",
2726 + "careers_url": "https://gfs.wd5.myworkdayjobs.com/usjobs-french",
2727 + "sectors": [
2728 + "manufacturier/commerce"
2729 + ],
2730 + "connector": "workday",
2731 + "status": "actif",
2732 + "region": ""
2733 + },
2734 + {
2735 + "id": "scottsmiraclegro",
2736 + "name": "Scotts Canada (Fafard)",
2737 + "url": "https://scottsmiraclegro.com",
2738 + "careers_url": "https://scottsmiraclegro.wd5.myworkdayjobs.com/SMGExternal",
2739 + "sectors": [
2740 + "manufacturier/commerce"
2741 + ],
2742 + "connector": "workday",
2743 + "status": "actif",
2744 + "region": ""
2745 + },
2746 + {
2747 + "id": "sleeman",
2748 + "name": "Brasseries Sleeman (Unibroue)",
2749 + "url": "https://unibroue.com",
2750 + "careers_url": "https://sleeman.wd3.myworkdayjobs.com/SBL",
2751 + "sectors": [
2752 + "manufacturier/commerce"
2753 + ],
2754 + "connector": "workday",
2755 + "status": "actif",
2756 + "region": ""
1717 2757 }
1718 2758 ]
1719 2759 }
added jobka/connectors/airbus.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/airbus.py
6 +# Rôle : Connecteur Airbus Canada — workday (tenant ag.wd3, site Airbus)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class AirbusConnector(WorkdayConnector):
14 + source_id = 'airbus'
15 + EMPLOYER = 'Airbus Canada'
16 + TENANT = 'ag'
17 + HOST = 'wd3'
18 + SITE = 'Airbus'
added jobka/connectors/alayacare.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/alayacare.py
6 +# Rôle : Connecteur AlayaCare — greenhouse (org « alayacare »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .greenhouse import GreenhouseConnector
11 +
12 +
13 +class AlayacareConnector(GreenhouseConnector):
14 + source_id = 'alayacare'
15 + EMPLOYER = 'AlayaCare'
16 + BOARD = 'alayacare'
added jobka/connectors/alcoa.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/alcoa.py
6 +# Rôle : Connecteur Alcoa Canada — workday (tenant alcoa.wd5, site Careers)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class AlcoaConnector(WorkdayConnector):
14 + source_id = 'alcoa'
15 + EMPLOYER = 'Alcoa Canada'
16 + TENANT = 'alcoa'
17 + HOST = 'wd5'
18 + SITE = 'Careers'
added jobka/connectors/altasciences.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/altasciences.py
6 +# Rôle : Connecteur Altasciences — workday (tenant altasciences.wd1, site Careers)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class AltasciencesConnector(WorkdayConnector):
14 + source_id = 'altasciences'
15 + EMPLOYER = 'Altasciences'
16 + TENANT = 'altasciences'
17 + HOST = 'wd1'
18 + SITE = 'Careers'
added jobka/connectors/amilia.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/amilia.py
6 +# Rôle : Connecteur Amilia — recruitee (org « amilia »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .recruitee import RecruiteeConnector
11 +
12 +
13 +class AmiliaConnector(RecruiteeConnector):
14 + source_id = 'amilia'
15 + EMPLOYER = 'Amilia'
16 + ORG = 'amilia'
added jobka/connectors/bdc.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/bdc.py
6 +# Rôle : Connecteur BDC — workday (tenant bdc.wd10, site bdc_careers)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class BdcConnector(WorkdayConnector):
14 + source_id = 'bdc'
15 + EMPLOYER = 'BDC'
16 + TENANT = 'bdc'
17 + HOST = 'wd10'
18 + SITE = 'bdc_careers'
added jobka/connectors/beneva.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/beneva.py
6 +# Rôle : Connecteur Beneva — workday (tenant beneva.wd10, site BenevaSite_carriere)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class BenevaConnector(WorkdayConnector):
14 + source_id = 'beneva'
15 + EMPLOYER = 'Beneva'
16 + TENANT = 'beneva'
17 + HOST = 'wd10'
18 + SITE = 'BenevaSite_carriere'
added jobka/connectors/bhvr.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/bhvr.py
6 +# Rôle : Connecteur Behaviour Interactif — lever (org « bhvr »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .lever import LeverConnector
11 +
12 +
13 +class BhvrConnector(LeverConnector):
14 + source_id = 'bhvr'
15 + EMPLOYER = 'Behaviour Interactif'
16 + ORG = 'bhvr'
added jobka/connectors/bigbang.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/bigbang.py
6 +# Rôle : Connecteur Big Bang — bamboohr (org « bigbang »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class BigbangConnector(BambooHRConnector):
14 + source_id = 'bigbang'
15 + EMPLOYER = 'Big Bang'
16 + ORG = 'bigbang'
added jobka/connectors/broccolini.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/broccolini.py
6 +# Rôle : Connecteur Broccolini — bamboohr (org « broccolini »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class BroccoliniConnector(BambooHRConnector):
14 + source_id = 'broccolini'
15 + EMPLOYER = 'Broccolini'
16 + ORG = 'broccolini'
added jobka/connectors/caaquebec.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/caaquebec.py
6 +# Rôle : Connecteur CAA-Québec — workday (tenant caaquebec.wd3, site CarrieresCAA-Quebec)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class CaaquebecConnector(WorkdayConnector):
14 + source_id = 'caaquebec'
15 + EMPLOYER = 'CAA-Québec'
16 + TENANT = 'caaquebec'
17 + HOST = 'wd3'
18 + SITE = 'CarrieresCAA-Quebec'
added jobka/connectors/canadiantirecorporation.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/canadiantirecorporation.py
6 +# Rôle : Connecteur Canadian Tire — workday (tenant canadiantirecorporation.wd3, site Enterprise_External_Careers_Site)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class CanadiantirecorporationConnector(WorkdayConnector):
14 + source_id = 'canadiantirecorporation'
15 + EMPLOYER = 'Canadian Tire'
16 + TENANT = 'canadiantirecorporation'
17 + HOST = 'wd3'
18 + SITE = 'Enterprise_External_Careers_Site'
added jobka/connectors/cdpq.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/cdpq.py
6 +# Rôle : Connecteur CDPQ — Caisse de dépôt et placement du Québec — workday (tenant cdpq.wd10, site CDPQ)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class CdpqConnector(WorkdayConnector):
14 + source_id = 'cdpq'
15 + EMPLOYER = 'CDPQ — Caisse de dépôt et placement du Québec'
16 + TENANT = 'cdpq'
17 + HOST = 'wd10'
18 + SITE = 'CDPQ'
added jobka/connectors/churchdwight.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/churchdwight.py
6 +# Rôle : Connecteur Church & Dwight Canada — workday (tenant churchdwight.wd1, site chdcanadacareers)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class ChurchdwightConnector(WorkdayConnector):
14 + source_id = 'churchdwight'
15 + EMPLOYER = 'Church & Dwight Canada'
16 + TENANT = 'churchdwight'
17 + HOST = 'wd1'
18 + SITE = 'chdcanadacareers'
added jobka/connectors/cirquedusoleil.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/cirquedusoleil.py
6 +# Rôle : Connecteur Cirque du Soleil — lever (org « cirquedusoleil »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .lever import LeverConnector
11 +
12 +
13 +class CirquedusoleilConnector(LeverConnector):
14 + source_id = 'cirquedusoleil'
15 + EMPLOYER = 'Cirque du Soleil'
16 + ORG = 'cirquedusoleil'
added jobka/connectors/cordonbleu.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/cordonbleu.py
6 +# Rôle : Connecteur Aliments Ouimet-Cordon Bleu inc. — bamboohr (org « cordonbleu »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class CordonbleuConnector(BambooHRConnector):
14 + source_id = 'cordonbleu'
15 + EMPLOYER = 'Aliments Ouimet-Cordon Bleu inc.'
16 + ORG = 'cordonbleu'
added jobka/connectors/devimco.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/devimco.py
6 +# Rôle : Connecteur Devimco Appartements — bamboohr (org « devimco »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class DevimcoConnector(BambooHRConnector):
14 + source_id = 'devimco'
15 + EMPLOYER = 'Devimco Appartements'
16 + ORG = 'devimco'
added jobka/connectors/evlo.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/evlo.py
6 +# Rôle : Connecteur EVLO (Hydro-Québec) — bamboohr (org « evlo »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class EvloConnector(BambooHRConnector):
14 + source_id = 'evlo'
15 + EMPLOYER = 'EVLO (Hydro-Québec)'
16 + ORG = 'evlo'
added jobka/connectors/exfo.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/exfo.py
6 +# Rôle : Connecteur EXFO — workday (tenant exfo.wd10, site exfo_careers)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class ExfoConnector(WorkdayConnector):
14 + source_id = 'exfo'
15 + EMPLOYER = 'EXFO'
16 + TENANT = 'exfo'
17 + HOST = 'wd10'
18 + SITE = 'exfo_careers'
added jobka/connectors/fenetresconcept.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/fenetresconcept.py
6 +# Rôle : Connecteur Fenêtres Concept — bamboohr (org « fenetresconcept »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class FenetresconceptConnector(BambooHRConnector):
14 + source_id = 'fenetresconcept'
15 + EMPLOYER = 'Fenêtres Concept'
16 + ORG = 'fenetresconcept'
added jobka/connectors/flare.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/flare.py
6 +# Rôle : Connecteur Flare (cybersécurité) — bamboohr (org « flare »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class FlareConnector(BambooHRConnector):
14 + source_id = 'flare'
15 + EMPLOYER = 'Flare (cybersécurité)'
16 + ORG = 'flare'
added jobka/connectors/fondsftq.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/fondsftq.py
6 +# Rôle : Connecteur Fonds de solidarité FTQ — workday (tenant fondsftq.wd10, site sitecarriere_externe)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class FondsftqConnector(WorkdayConnector):
14 + source_id = 'fondsftq'
15 + EMPLOYER = 'Fonds de solidarité FTQ'
16 + TENANT = 'fondsftq'
17 + HOST = 'wd10'
18 + SITE = 'sitecarriere_externe'
added jobka/connectors/gameloft.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/gameloft.py
6 +# Rôle : Connecteur Gameloft — smartrecruiters (org « gameloft »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class GameloftConnector(SmartRecruitersConnector):
14 + source_id = 'gameloft'
15 + EMPLOYER = 'Gameloft'
16 + COMPANY = 'gameloft'
added jobka/connectors/genetec_inc.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/genetec_inc.py
6 +# Rôle : Connecteur Genetec — workable (org « genetec-inc »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workable import WorkableConnector
11 +
12 +
13 +class GenetecIncConnector(WorkableConnector):
14 + source_id = 'genetec_inc'
15 + EMPLOYER = 'Genetec'
16 + ORG = 'genetec-inc'
added jobka/connectors/gfs.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/gfs.py
6 +# Rôle : Connecteur Service alimentaire Gordon — workday (tenant gfs.wd5, site usjobs-french)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class GfsConnector(WorkdayConnector):
14 + source_id = 'gfs'
15 + EMPLOYER = 'Service alimentaire Gordon'
16 + TENANT = 'gfs'
17 + HOST = 'wd5'
18 + SITE = 'usjobs-french'
added jobka/connectors/gologic.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/gologic.py
6 +# Rôle : Connecteur GoLogic — smartrecruiters (org « gologic »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class GologicConnector(SmartRecruitersConnector):
14 + source_id = 'gologic'
15 + EMPLOYER = 'GoLogic'
16 + COMPANY = 'gologic'
added jobka/connectors/groupecanam.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/groupecanam.py
6 +# Rôle : Connecteur Groupe Canam — smartrecruiters (org « groupecanam »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class GroupecanamConnector(SmartRecruitersConnector):
14 + source_id = 'groupecanam'
15 + EMPLOYER = 'Groupe Canam'
16 + COMPANY = 'groupecanam'
added jobka/connectors/gurussolutions.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/gurussolutions.py
6 +# Rôle : Connecteur GURUS Solutions — greenhouse (org « gurussolutions »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .greenhouse import GreenhouseConnector
11 +
12 +
13 +class GurussolutionsConnector(GreenhouseConnector):
14 + source_id = 'gurussolutions'
15 + EMPLOYER = 'GURUS Solutions'
16 + BOARD = 'gurussolutions'
added jobka/connectors/intactfc.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/intactfc.py
6 +# Rôle : Connecteur Intact Corporation financière — workday (tenant intactfc.wd3, site Intactfc)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class IntactfcConnector(WorkdayConnector):
14 + source_id = 'intactfc'
15 + EMPLOYER = 'Intact Corporation financière'
16 + TENANT = 'intactfc'
17 + HOST = 'wd3'
18 + SITE = 'Intactfc'
added jobka/connectors/investpsp.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/investpsp.py
6 +# Rôle : Connecteur Investissements PSP — workday (tenant investpsp.wd3, site psp_careers)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class InvestpspConnector(WorkdayConnector):
14 + source_id = 'investpsp'
15 + EMPLOYER = 'Investissements PSP'
16 + TENANT = 'investpsp'
17 + HOST = 'wd3'
18 + SITE = 'psp_careers'
added jobka/connectors/investquebec.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/investquebec.py
6 +# Rôle : Connecteur Investissement Québec — workday (tenant investquebec.wd10, site Ext)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class InvestquebecConnector(WorkdayConnector):
14 + source_id = 'investquebec'
15 + EMPLOYER = 'Investissement Québec'
16 + TENANT = 'investquebec'
17 + HOST = 'wd10'
18 + SITE = 'Ext'
added jobka/connectors/jgrivesud.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/jgrivesud.py
6 +# Rôle : Connecteur JG Rive-Sud (GFS) — workday (tenant gfs.wd5, site jgrive-sud)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class JgrivesudConnector(WorkdayConnector):
14 + source_id = 'jgrivesud'
15 + EMPLOYER = 'JG Rive-Sud (GFS)'
16 + TENANT = 'gfs'
17 + HOST = 'wd5'
18 + SITE = 'jgrive-sud'
added jobka/connectors/korem.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/korem.py
6 +# Rôle : Connecteur Korem — bamboohr (org « korem »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class KoremConnector(BambooHRConnector):
14 + source_id = 'korem'
15 + EMPLOYER = 'Korem'
16 + ORG = 'korem'
added jobka/connectors/levio.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/levio.py
6 +# Rôle : Connecteur Levio — greenhouse (org « levio »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .greenhouse import GreenhouseConnector
11 +
12 +
13 +class LevioConnector(GreenhouseConnector):
14 + source_id = 'levio'
15 + EMPLOYER = 'Levio'
16 + BOARD = 'levio'
added jobka/connectors/lightspeedhq.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/lightspeedhq.py
6 +# Rôle : Connecteur Lightspeed Commerce — ashby (org « lightspeedhq »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .ashby import AshbyConnector
11 +
12 +
13 +class LightspeedhqConnector(AshbyConnector):
14 + source_id = 'lightspeedhq'
15 + EMPLOYER = 'Lightspeed Commerce'
16 + ORG = 'lightspeedhq'
added jobka/connectors/logisco.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/logisco.py
6 +# Rôle : Connecteur Logisco — bamboohr (org « logisco »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class LogiscoConnector(BambooHRConnector):
14 + source_id = 'logisco'
15 + EMPLOYER = 'Logisco'
16 + ORG = 'logisco'
added jobka/connectors/mila_2.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/mila_2.py
6 +# Rôle : Connecteur Mila — Institut québécois d'IA — workable (org « mila-2 »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workable import WorkableConnector
11 +
12 +
13 +class Mila2Connector(WorkableConnector):
14 + source_id = 'mila_2'
15 + EMPLOYER = "Mila — Institut québécois d'IA"
16 + ORG = 'mila-2'
added jobka/connectors/mirego.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/mirego.py
6 +# Rôle : Connecteur Mirego — lever (org « mirego »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .lever import LeverConnector
11 +
12 +
13 +class MiregoConnector(LeverConnector):
14 + source_id = 'mirego'
15 + EMPLOYER = 'Mirego'
16 + ORG = 'mirego'
added jobka/connectors/mxo.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/mxo.py
6 +# Rôle : Connecteur MXO — bamboohr (org « mxo »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class MxoConnector(BambooHRConnector):
14 + source_id = 'mxo'
15 + EMPLOYER = 'MXO'
16 + ORG = 'mxo'
added jobka/connectors/playmind.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/playmind.py
6 +# Rôle : Connecteur Playmind — smartrecruiters (org « playmind »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class PlaymindConnector(SmartRecruitersConnector):
14 + source_id = 'playmind'
15 + EMPLOYER = 'Playmind'
16 + COMPANY = 'playmind'
added jobka/connectors/plusgrade.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/plusgrade.py
6 +# Rôle : Connecteur Plusgrade — workday (tenant plusgrade.wd10, site Plusgrade_External_Site)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class PlusgradeConnector(WorkdayConnector):
14 + source_id = 'plusgrade'
15 + EMPLOYER = 'Plusgrade'
16 + TENANT = 'plusgrade'
17 + HOST = 'wd10'
18 + SITE = 'Plusgrade_External_Site'
added jobka/connectors/pouletrouge.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/pouletrouge.py
6 +# Rôle : Connecteur Poulet Rouge — bamboohr (org « pouletrouge »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class PouletrougeConnector(BambooHRConnector):
14 + source_id = 'pouletrouge'
15 + EMPLOYER = 'Poulet Rouge'
16 + ORG = 'pouletrouge'
added jobka/connectors/promutuel.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/promutuel.py
6 +# Rôle : Connecteur Promutuel Assurance — workday (tenant promutuel.wd3, site promutuel_carrieres)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class PromutuelConnector(WorkdayConnector):
14 + source_id = 'promutuel'
15 + EMPLOYER = 'Promutuel Assurance'
16 + TENANT = 'promutuel'
17 + HOST = 'wd3'
18 + SITE = 'promutuel_carrieres'
added jobka/connectors/qsl.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/qsl.py
6 +# Rôle : Connecteur QSL — smartrecruiters (org « qsl »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class QslConnector(SmartRecruitersConnector):
14 + source_id = 'qsl'
15 + EMPLOYER = 'QSL'
16 + COMPANY = 'qsl'
added jobka/connectors/rcgt.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/rcgt.py
6 +# Rôle : Connecteur Raymond Chabot Grant Thornton — workday (tenant rcgt.wd3, site External)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class RcgtConnector(WorkdayConnector):
14 + source_id = 'rcgt'
15 + EMPLOYER = 'Raymond Chabot Grant Thornton'
16 + TENANT = 'rcgt'
17 + HOST = 'wd3'
18 + SITE = 'External'
added jobka/connectors/riotinto.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/riotinto.py
6 +# Rôle : Connecteur Rio Tinto — workday (tenant riotinto.wd3, site riotinto_careers)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class RiotintoConnector(WorkdayConnector):
14 + source_id = 'riotinto'
15 + EMPLOYER = 'Rio Tinto'
16 + TENANT = 'riotinto'
17 + HOST = 'wd3'
18 + SITE = 'riotinto_careers'
added jobka/connectors/scottsmiraclegro.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/scottsmiraclegro.py
6 +# Rôle : Connecteur Scotts Canada (Fafard) — workday (tenant scottsmiraclegro.wd5, site SMGExternal)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class ScottsmiraclegroConnector(WorkdayConnector):
14 + source_id = 'scottsmiraclegro'
15 + EMPLOYER = 'Scotts Canada (Fafard)'
16 + TENANT = 'scottsmiraclegro'
17 + HOST = 'wd5'
18 + SITE = 'SMGExternal'
added jobka/connectors/sidlee.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/sidlee.py
6 +# Rôle : Connecteur Sid Lee — greenhouse (org « sidlee »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .greenhouse import GreenhouseConnector
11 +
12 +
13 +class SidleeConnector(GreenhouseConnector):
14 + source_id = 'sidlee'
15 + EMPLOYER = 'Sid Lee'
16 + BOARD = 'sidlee'
added jobka/connectors/sleeman.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/sleeman.py
6 +# Rôle : Connecteur Brasseries Sleeman (Unibroue) — workday (tenant sleeman.wd3, site SBL)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class SleemanConnector(WorkdayConnector):
14 + source_id = 'sleeman'
15 + EMPLOYER = 'Brasseries Sleeman (Unibroue)'
16 + TENANT = 'sleeman'
17 + HOST = 'wd3'
18 + SITE = 'SBL'
added jobka/connectors/spiria.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/spiria.py
6 +# Rôle : Connecteur Spiria — lever (org « spiria »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .lever import LeverConnector
11 +
12 +
13 +class SpiriaConnector(LeverConnector):
14 + source_id = 'spiria'
15 + EMPLOYER = 'Spiria'
16 + ORG = 'spiria'
added jobka/connectors/stelpro.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/stelpro.py
6 +# Rôle : Connecteur Stelpro — smartrecruiters (org « stelpro »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class StelproConnector(SmartRecruitersConnector):
14 + source_id = 'stelpro'
15 + EMPLOYER = 'Stelpro'
16 + COMPANY = 'stelpro'
added jobka/connectors/suncor.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/suncor.py
6 +# Rôle : Connecteur Suncor Énergie — workday (tenant suncor.wd1, site Suncor_External)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class SuncorConnector(WorkdayConnector):
14 + source_id = 'suncor'
15 + EMPLOYER = 'Suncor Énergie'
16 + TENANT = 'suncor'
17 + HOST = 'wd1'
18 + SITE = 'Suncor_External'
added jobka/connectors/talsom.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/talsom.py
6 +# Rôle : Connecteur Talsom — smartrecruiters (org « talsom »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class TalsomConnector(SmartRecruitersConnector):
14 + source_id = 'talsom'
15 + EMPLOYER = 'Talsom'
16 + COMPANY = 'talsom'
added jobka/connectors/valtech.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/valtech.py
6 +# Rôle : Connecteur Valtech — greenhouse (org « valtech »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .greenhouse import GreenhouseConnector
11 +
12 +
13 +class ValtechConnector(GreenhouseConnector):
14 + source_id = 'valtech'
15 + EMPLOYER = 'Valtech'
16 + BOARD = 'valtech'
added jobka/connectors/videotron.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/videotron.py
6 +# Rôle : Connecteur Vidéotron — smartrecruiters (org « videotron »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class VideotronConnector(SmartRecruitersConnector):
14 + source_id = 'videotron'
15 + EMPLOYER = 'Vidéotron'
16 + COMPANY = 'videotron'
added jobka/connectors/vooban.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/vooban.py
6 +# Rôle : Connecteur Vooban — greenhouse (org « vooban »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .greenhouse import GreenhouseConnector
11 +
12 +
13 +class VoobanConnector(GreenhouseConnector):
14 + source_id = 'vooban'
15 + EMPLOYER = 'Vooban'
16 + BOARD = 'vooban'
added jobka/connectors/vosker.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/vosker.py
6 +# Rôle : Connecteur Vosker — smartrecruiters (org « vosker »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .smartrecruiters import SmartRecruitersConnector
11 +
12 +
13 +class VoskerConnector(SmartRecruitersConnector):
14 + source_id = 'vosker'
15 + EMPLOYER = 'Vosker'
16 + COMPANY = 'vosker'
added jobka/connectors/walmart.py +18 −0
@@ -0,0 +1,18 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/walmart.py
6 +# Rôle : Connecteur Walmart Canada — workday (tenant walmart.wd504, site WalmartExternal)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .workday import WorkdayConnector
11 +
12 +
13 +class WalmartConnector(WorkdayConnector):
14 + source_id = 'walmart'
15 + EMPLOYER = 'Walmart Canada'
16 + TENANT = 'walmart'
17 + HOST = 'wd504'
18 + SITE = 'WalmartExternal'
added jobka/connectors/ziptrekeco.py +16 −0
@@ -0,0 +1,16 @@
1 +# =============================================================================
2 +# Job·Ka — Groupe KA
3 +# Auteur : Simon-Pierre Boucher
4 +# Contact : contact@spboucher.ai
5 +# Fichier : jobka/connectors/ziptrekeco.py
6 +# Rôle : Connecteur Ziptrek Tremblant — bamboohr (org « ziptrekeco »)
7 +# [généré par scripts/gen_connectors.py, flux vérifié le 2026-08-18]
8 +# Créé : 2026-08-18 Modifié : 2026-08-18
9 +# =============================================================================
10 +from .bamboohr import BambooHRConnector
11 +
12 +
13 +class ZiptrekecoConnector(BambooHRConnector):
14 + source_id = 'ziptrekeco'
15 + EMPLOYER = 'Ziptrek Tremblant'
16 + ORG = 'ziptrekeco'
modified jobka/normalize.py +2 −1
@@ -382,7 +382,8 @@ _NON_QC_CITIES = frozenset({
382 382 "paris", "londres", "amsterdam", "berlin", "munich", "dublin", "madrid",
383 383 "barcelona", "barcelone", "lisbon", "lisbonne", "sydney", "melbourne",
384 384 "singapore", "singapour", "tokyo", "bangalore", "bengaluru", "mumbai",
385 "mexico city", "sao paulo", "bogota", "buenos aires",
385 + "mexico city", "sao paulo", "bogota", "buenos aires", "las vegas",
386 + "orlando", "nashville", "macau",
386 387 })
387 388
388 389
added scripts/detect_ats.py +277 −0
@@ -0,0 +1,277 @@
1 +#!/usr/bin/env python3
2 +# =============================================================================
3 +# Job·Ka — Groupe KA
4 +# Auteur : Simon-Pierre Boucher
5 +# Contact : contact@spboucher.ai
6 +# Fichier : scripts/detect_ats.py
7 +# Rôle : Détecteur d'ATS par page carrières — visite le site de chaque
8 +# employeur (accueil + chemins carrières usuels), reconnaît l'ATS
9 +# par motifs d'URL/DOM et extrait le slug. Les 9 ATS supportés
10 +# deviennent des candidats pour scripts/probe_ats.py ; les autres
11 +# (njoyn/taleo/ukg/icims/successfactors/adp) sont consignés dans
12 +# data/detected-other-ats.jsonl pour traitement séparé.
13 +# Créé : 2026-08-18 Modifié : 2026-08-18
14 +# =============================================================================
15 +"""Usage :
16 + python3 scripts/detect_ats.py employeurs.jsonl [autres.jsonl …] \
17 + --out candidats.jsonl --other data/detected-other-ats.jsonl \
18 + [--cap 1500] [--workers 8]
19 +
20 +Entrée : JSONL {"name": "...", "url"/"domain"/"domain_hint": "...",
21 + "city": "...", "categorie": "..."} (ordre = priorité).
22 +"""
23 +from __future__ import annotations
24 +
25 +import argparse
26 +import json
27 +import re
28 +import sys
29 +import threading
30 +import time
31 +from concurrent.futures import ThreadPoolExecutor, as_completed
32 +from pathlib import Path
33 +from urllib.parse import urljoin, urlparse
34 +
35 +import requests
36 +import urllib3
37 +
38 +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
39 +
40 +UA = "JobKaBot/1.0 (+https://www.job-ka.com; contact@spboucher.ai)"
41 +TIMEOUT = 6
42 +PATHS = ["", "/carrieres", "/careers", "/emplois", "/jobs", "/fr/carrieres"]
43 +MAX_HTML = 500_000
44 +
45 +# slugs jamais valides (pages génériques des ATS eux-mêmes)
46 +BAD_SLUGS = {"www", "app", "api", "apply", "embed", "boards", "jobs", "job",
47 + "careers", "career", "fr", "en", "help", "support", "cdn",
48 + "assets", "status", "docs", "login", "my", "sso", "static",
49 + "oneclick-ui", "attachments", "hire", "home", "account",
50 + "js", "css", "images", "img", "media", "email", "click"}
51 +
52 +# ATS supportés par Job·Ka : (ats, regex, groupes)
53 +SUPPORTED = [
54 + ("workday", re.compile(
55 + r"https?://([\w-]+)\.(wd\d+)\.myworkdayjobs\.com"
56 + r"(?:/(?:[a-z]{2}-[A-Z]{2}/)?([A-Za-z0-9_\-]+))?", re.I)),
57 + ("greenhouse", re.compile(
58 + r"(?:boards|job-boards)\.greenhouse\.io/([A-Za-z0-9_\-]+)", re.I)),
59 + ("greenhouse", re.compile(
60 + r"greenhouse\.io/embed/job_board\?[^\"'\s]*for=([A-Za-z0-9_\-]+)", re.I)),
61 + ("lever", re.compile(r"jobs\.(?:eu\.)?lever\.co/([A-Za-z0-9_\-]+)", re.I)),
62 + ("smartrecruiters", re.compile(
63 + r"(?:careers|jobs)\.smartrecruiters\.com/([A-Za-z0-9]+)")),
64 + ("ashby", re.compile(r"jobs\.ashbyhq\.com/([A-Za-z0-9_\-.%]+)", re.I)),
65 + ("workable", re.compile(
66 + r"apply\.workable\.com/(?:api/v\d/accounts/)?([A-Za-z0-9_\-]+)", re.I)),
67 + ("recruitee", re.compile(r"https?://([A-Za-z0-9\-]+)\.recruitee\.com", re.I)),
68 + ("breezy", re.compile(r"https?://([A-Za-z0-9\-]+)\.breezy\.hr", re.I)),
69 + ("bamboohr", re.compile(r"https?://([A-Za-z0-9\-]+)\.bamboohr\.com", re.I)),
70 +]
71 +
72 +# ATS repérés mais non supportés ici -> data/detected-other-ats.jsonl
73 +OTHER = [
74 + ("njoyn", re.compile(r"https?://[\w.-]*njoyn\.com[^\"'\s<>]*", re.I)),
75 + ("taleo", re.compile(r"https?://[\w.-]*taleo\.net[^\"'\s<>]*", re.I)),
76 + ("ukg_ultipro", re.compile(
77 + r"https?://[\w.-]*(?:ultipro|ukg)\.com[^\"'\s<>]*", re.I)),
78 + ("icims", re.compile(r"https?://[\w-]+\.icims\.com[^\"'\s<>]*", re.I)),
79 + ("successfactors", re.compile(
80 + r"https?://[\w.-]*successfactors\.(?:com|eu)[^\"'\s<>]*", re.I)),
81 + ("adp", re.compile(
82 + r"https?://(?:workforcenow|recruiting|jobs)\.adp\.com[^\"'\s<>]*", re.I)),
83 +]
84 +
85 +CAREER_LINK = re.compile(
86 + r'href=["\']([^"\']*(?:carri|career|emploi|job|recrut|joignez|joindre|'
87 + r'postul|travaill)[^"\']*)["\']', re.I)
88 +
89 +_print_lock = threading.Lock()
90 +
91 +
92 +def norm_domain(rec: dict) -> str:
93 + raw = (rec.get("domain_hint") or rec.get("domain") or rec.get("url")
94 + or "").strip()
95 + if not raw:
96 + return ""
97 + if "://" not in raw:
98 + raw = "https://" + raw
99 + host = urlparse(raw).netloc.lower()
100 + return host[4:] if host.startswith("www.") else host
101 +
102 +
103 +def fetch(session: requests.Session, url: str) -> tuple[str, str]:
104 + r = session.get(url, timeout=TIMEOUT, allow_redirects=True, verify=False,
105 + headers={"User-Agent": UA})
106 + if r.status_code >= 400:
107 + return r.url, ""
108 + return r.url, (r.text or "")[:MAX_HTML]
109 +
110 +
111 +def scan_text(text: str) -> tuple[list[tuple], list[tuple]]:
112 + """-> ([(ats, slug, extra…)], [(ats, url)])."""
113 + hits, others = [], []
114 + for ats, rx in SUPPORTED:
115 + for m in rx.finditer(text):
116 + if ats == "workday":
117 + tenant, host = m.group(1).lower(), m.group(2).lower()
118 + site = m.group(3) or ""
119 + if tenant in BAD_SLUGS or tenant == "impl":
120 + continue
121 + hits.append((ats, tenant, host, site))
122 + else:
123 + slug = m.group(1)
124 + if slug.lower() in BAD_SLUGS:
125 + continue
126 + hits.append((ats, slug))
127 + for ats, rx in OTHER:
128 + m = rx.search(text)
129 + if m:
130 + others.append((ats, m.group(0)[:300]))
131 + return hits, others
132 +
133 +
134 +def detect_one(rec: dict) -> dict:
135 + """-> {"candidates": [...], "others": [...], "domain": d}."""
136 + domain = norm_domain(rec)
137 + out = {"domain": domain, "candidates": [], "others": []}
138 + if not domain:
139 + return out
140 + session = requests.Session()
141 + seen_hits: set[tuple] = set()
142 + seen_others: set[str] = set()
143 + extra_links: list[str] = []
144 + pages = 0
145 + for i, path in enumerate(PATHS + ["@extra1", "@extra2"]):
146 + if path.startswith("@"):
147 + if not extra_links:
148 + continue
149 + url = extra_links.pop(0)
150 + else:
151 + url = f"https://{domain}{path}"
152 + try:
153 + final_url, html = fetch(session, url)
154 + except requests.RequestException:
155 + if path == "": # racine injoignable en https -> essai www/http
156 + for alt in (f"https://www.{domain}", f"http://{domain}"):
157 + try:
158 + final_url, html = fetch(session, alt)
159 + break
160 + except requests.RequestException:
161 + final_url, html = "", ""
162 + if not html:
163 + continue
164 + else:
165 + continue
166 + pages += 1
167 + hits, others = scan_text(final_url + "\n" + html)
168 + for h in hits:
169 + if h in seen_hits:
170 + continue
171 + seen_hits.add(h)
172 + for ats, u in others:
173 + if ats not in seen_others:
174 + seen_others.add(ats)
175 + out["others"].append({"name": rec.get("name", ""),
176 + "domain": domain, "ats": ats, "url": u})
177 + if path == "" and html and not seen_hits:
178 + # relever jusqu'à 2 liens « carrières » à suivre
179 + base = final_url or f"https://{domain}"
180 + for link in CAREER_LINK.findall(html):
181 + if link.startswith(("mailto:", "tel:", "#", "javascript")):
182 + continue
183 + full = urljoin(base, link)
184 + if full not in extra_links:
185 + extra_links.append(full)
186 + if len(extra_links) >= 2:
187 + break
188 + if seen_hits:
189 + break # ATS supporté trouvé : inutile d'aller plus loin
190 + for h in seen_hits:
191 + cand = {"employer": rec.get("name") or domain, "domain": domain,
192 + "url": f"https://{domain}",
193 + "sectors": [s for s in [rec.get("categorie", "")] if s],
194 + "cities": [c for c in [rec.get("city", "")] if c],
195 + "group": rec.get("name") or domain}
196 + if h[0] == "workday":
197 + cand.update(ats="workday", slug=h[1], tenant=h[1], host=h[2])
198 + if h[3] and not re.fullmatch(r"[a-z]{2}-[A-Z]{2}", h[3]):
199 + cand["site"] = h[3]
200 + else:
201 + cand.update(ats=h[0], slug=h[1])
202 + out["candidates"].append(cand)
203 + return out
204 +
205 +
206 +def main() -> None:
207 + ap = argparse.ArgumentParser()
208 + ap.add_argument("inputs", nargs="+")
209 + ap.add_argument("--out", required=True)
210 + ap.add_argument("--other", default="data/detected-other-ats.jsonl")
211 + ap.add_argument("--cap", type=int, default=1500)
212 + ap.add_argument("--workers", type=int, default=8)
213 + args = ap.parse_args()
214 +
215 + recs, seen = [], set()
216 + for f in args.inputs:
217 + for line in Path(f).read_text(encoding="utf-8").splitlines():
218 + line = line.strip()
219 + if not line:
220 + continue
221 + rec = json.loads(line)
222 + d = norm_domain(rec)
223 + if d and d not in seen:
224 + seen.add(d)
225 + recs.append(rec)
226 + recs = recs[:args.cap]
227 + print(f"[detect] {len(recs)} domaines à visiter (cap {args.cap})",
228 + flush=True)
229 +
230 + cands, others = [], []
231 + t0 = time.time()
232 + done = 0
233 + with ThreadPoolExecutor(max_workers=args.workers) as ex:
234 + futs = [ex.submit(detect_one, r) for r in recs]
235 + for fut in as_completed(futs):
236 + done += 1
237 + try:
238 + res = fut.result()
239 + except Exception:
240 + continue
241 + for c in res["candidates"]:
242 + cands.append(c)
243 + with _print_lock:
244 + print(f" ✓ {c['employer']} -> {c['ats']}:{c['slug']}",
245 + flush=True)
246 + others.extend(res["others"])
247 + if done % 100 == 0:
248 + print(f"[detect] {done}/{len(recs)} domaines, "
249 + f"{len(cands)} candidats, {len(others)} autres ATS, "
250 + f"{time.time()-t0:.0f}s", flush=True)
251 +
252 + Path(args.out).write_text(
253 + "\n".join(json.dumps(c, ensure_ascii=False) for c in cands) + "\n",
254 + encoding="utf-8")
255 + op = Path(args.other)
256 + existing = set()
257 + if op.exists():
258 + existing = {l for l in op.read_text(encoding="utf-8").splitlines()
259 + if l.strip()}
260 + with op.open("a", encoding="utf-8") as fh:
261 + for o in others:
262 + line = json.dumps(o, ensure_ascii=False)
263 + if line not in existing:
264 + fh.write(line + "\n")
265 + existing.add(line)
266 +
267 + per = {}
268 + for o in others:
269 + per[o["ats"]] = per.get(o["ats"], 0) + 1
270 + print(f"\n[detect] terminé en {time.time()-t0:.0f}s : "
271 + f"{len(cands)} candidats ATS supportés -> {args.out}")
272 + print(f"[detect] autres ATS ({sum(per.values())}) -> {args.other} : "
273 + + ", ".join(f"{k}={v}" for k, v in sorted(per.items())))
274 +
275 +
276 +if __name__ == "__main__":
277 + main()
added scripts/probe_ats.py +500 −0
@@ -0,0 +1,500 @@
1 +#!/usr/bin/env python3
2 +# =============================================================================
3 +# Job·Ka — Groupe KA
4 +# Auteur : Simon-Pierre Boucher
5 +# Contact : contact@spboucher.ai
6 +# Fichier : scripts/probe_ats.py
7 +# Rôle : Sondage massif de slugs candidats sur les 9 ATS supportés —
8 +# un slug est CONFIRMÉ si l'API répond ≥1 offre ET ≥1 offre au
9 +# Québec (réutilise is_quebec_location du projet). Émet des entrées
10 +# prêtes pour data/feeds-*.json (scripts/gen_connectors.py).
11 +# Créé : 2026-08-18 Modifié : 2026-08-18
12 +# =============================================================================
13 +"""Sonde des slugs candidats sur les 9 plateformes ATS de Job·Ka.
14 +
15 +Usage :
16 + python3 scripts/probe_ats.py candidats.jsonl --out confirmed.json \
17 + [--rejected rejected.jsonl] [--workers 8]
18 +
19 +Entrée (JSONL, ou JSON liste) — un candidat par ligne :
20 + {"slug": "acme", "employer": "Acme inc.", "ats": "lever"?,
21 + "sectors": ["TI"], "cities": ["Montréal"], "group": "Acme"?,
22 + "no_workday": true?, "tenant"/"host"/"site" (workday)?}
23 +
24 +- sans "ats" : les 9 plateformes sont sondées dans l'ordre DEFAULT_ORDER
25 + (Workday en dernier — découverte automatique de l'hôte wdN et du site
26 + par le redirect de la racine du tenant).
27 +- les candidats partageant le même "group" (défaut : employer) sont des
28 + variantes du même employeur : on s'arrête à la première confirmation.
29 +- les slugs/tenants déjà connectés (sources.json + jobka/connectors/*.py)
30 + sont sautés automatiquement.
31 +
32 +Politesse : timeout 6 s, throttle 0,3 s par domaine d'API, 8 threads.
33 +"""
34 +from __future__ import annotations
35 +
36 +import argparse
37 +import json
38 +import re
39 +import sys
40 +import threading
41 +import time
42 +from concurrent.futures import ThreadPoolExecutor, as_completed
43 +from pathlib import Path
44 +from urllib.parse import urlparse
45 +
46 +import requests
47 +
48 +ROOT = Path(__file__).resolve().parent.parent
49 +sys.path.insert(0, str(ROOT))
50 +
51 +from jobka.normalize import is_quebec_location # noqa: E402
52 +
53 +UA = "JobKaBot/1.0 (+https://www.job-ka.com; contact@spboucher.ai)"
54 +TIMEOUT = 6
55 +THROTTLE = 0.3 # s entre deux requêtes vers un même domaine d'API
56 +WD_HOSTS = ["wd3", "wd1", "wd5", "wd10", "wd12", "wd103", "wd502", "wd504"]
57 +
58 +# Noms de sites carrières Workday fréquents — essayés quand le site est
59 +# inconnu. L'API cxs répond 422 si le tenant/hôte est faux mais 404 si seul
60 +# le site l'est : on découvre d'abord l'hôte, puis on devine le site.
61 +WD_COMMON_SITES = [
62 + "External", "Careers", "External_Careers", "ExternalCareers", "careers",
63 + "External_Career_Site", "CareerSite", "Career", "Global", "external",
64 + "Global_Careers", "GlobalCareers", "jobs", "Jobs", "External_Site",
65 + "ExternalSite", "Externe", "Carrieres", "carrieres", "Recruiting",
66 + "Recrutement", "Emplois", "External_Portal", "EXTERNAL", "Ext", "EXT",
67 +]
68 +
69 +DEFAULT_ORDER = ["lever", "greenhouse", "ashby", "smartrecruiters",
70 + "workable", "recruitee", "breezy", "bamboohr", "workday"]
71 +
72 +# --- throttling par domaine d'API -------------------------------------------
73 +_locks: dict[str, threading.Lock] = {}
74 +_last: dict[str, float] = {}
75 +_glock = threading.Lock()
76 +_nreq = [0]
77 +
78 +
79 +def _req(family: str, method: str, url: str, **kw) -> requests.Response:
80 + """Requête HTTP throttlée (verrou + espacement par famille/domaine)."""
81 + with _glock:
82 + lock = _locks.setdefault(family, threading.Lock())
83 + _nreq[0] += 1
84 + headers = {"User-Agent": UA}
85 + headers.update(kw.pop("headers", None) or {})
86 + with lock:
87 + wait = THROTTLE - (time.time() - _last.get(family, 0.0))
88 + if wait > 0:
89 + time.sleep(wait)
90 + try:
91 + return requests.request(method, url, timeout=TIMEOUT,
92 + headers=headers, **kw)
93 + finally:
94 + _last[family] = time.time()
95 +
96 +
97 +def _qc_stats(labels: list[list[str]]) -> tuple[int, list[str]]:
98 + """[[libellés d'une offre], …] -> (nb d'offres QC, libellés QC exemples)."""
99 + qc, ex = 0, []
100 + for locs in labels:
101 + hit = next((l for l in locs if l and is_quebec_location(l)), None)
102 + if hit:
103 + qc += 1
104 + if len(ex) < 5 and hit not in ex:
105 + ex.append(hit)
106 + return qc, ex
107 +
108 +
109 +# --- sondes par ATS ----------------------------------------------------------
110 +def probe_lever(slug: str, cand: dict) -> dict | None:
111 + r = _req("api.lever.co", "GET",
112 + f"https://api.lever.co/v0/postings/{slug}?mode=json")
113 + if r.status_code != 200:
114 + return None
115 + data = r.json()
116 + if not isinstance(data, list) or not data:
117 + return None
118 + labels = []
119 + for p in data:
120 + locs = [(p.get("categories") or {}).get("location") or ""]
121 + locs += [l for l in (p.get("allLocations") or []) if isinstance(l, str)]
122 + labels.append(locs)
123 + qc, ex = _qc_stats(labels)
124 + return {"found": len(data), "qc": qc, "qc_ex": ex, "api_name": ""}
125 +
126 +
127 +def probe_greenhouse(slug: str, cand: dict) -> dict | None:
128 + r = _req("boards-api.greenhouse.io", "GET",
129 + f"https://boards-api.greenhouse.io/v1/boards/{slug}/jobs")
130 + if r.status_code != 200:
131 + return None
132 + jobs = r.json().get("jobs") or []
133 + if not jobs:
134 + return None
135 + labels = [[(j.get("location") or {}).get("name") or ""] for j in jobs]
136 + qc, ex = _qc_stats(labels)
137 + name = ""
138 + if qc:
139 + try:
140 + r2 = _req("boards-api.greenhouse.io", "GET",
141 + f"https://boards-api.greenhouse.io/v1/boards/{slug}")
142 + if r2.status_code == 200:
143 + name = r2.json().get("name") or ""
144 + except requests.RequestException:
145 + pass
146 + return {"found": len(jobs), "qc": qc, "qc_ex": ex, "api_name": name}
147 +
148 +
149 +def probe_ashby(slug: str, cand: dict) -> dict | None:
150 + r = _req("api.ashbyhq.com", "GET",
151 + f"https://api.ashbyhq.com/posting-api/job-board/{slug}")
152 + if r.status_code != 200:
153 + return None
154 + data = r.json()
155 + jobs = data.get("jobs") or []
156 + if not jobs:
157 + return None
158 + labels = []
159 + for j in jobs:
160 + locs = [j.get("location") or ""]
161 + locs += [(s.get("location") or "") for s in (j.get("secondaryLocations") or [])]
162 + labels.append(locs)
163 + qc, ex = _qc_stats(labels)
164 + return {"found": len(jobs), "qc": qc, "qc_ex": ex,
165 + "api_name": data.get("name") or ""}
166 +
167 +
168 +def probe_smartrecruiters(slug: str, cand: dict) -> dict | None:
169 + variants = [slug]
170 + if slug != slug.capitalize():
171 + variants.append(slug.capitalize())
172 + for v in variants:
173 + r = _req("api.smartrecruiters.com", "GET",
174 + f"https://api.smartrecruiters.com/v1/companies/{v}/postings?limit=100")
175 + if r.status_code != 200:
176 + continue
177 + data = r.json()
178 + content = data.get("content") or []
179 + total = int(data.get("totalFound") or len(content))
180 + if not content:
181 + continue
182 + labels, name = [], ""
183 + for p in content:
184 + loc = p.get("location") or {}
185 + country = (loc.get("country") or "").lower()
186 + lbl = ", ".join(x for x in (loc.get("city"), loc.get("region"),
187 + loc.get("country")) if x)
188 + reg = (loc.get("region") or "").lower()
189 + ok = reg in ("qc", "quebec", "québec") or (
190 + country in ("ca", "canada") and is_quebec_location(lbl))
191 + labels.append([lbl] if ok else [""])
192 + name = name or (p.get("company") or {}).get("name") or ""
193 + qc, ex = _qc_stats(labels)
194 + return {"found": total, "qc": qc, "qc_ex": ex, "api_name": name,
195 + "org_override": v}
196 + return None
197 +
198 +
199 +def probe_workable(slug: str, cand: dict) -> dict | None:
200 + r = _req("apply.workable.com", "GET",
201 + f"https://apply.workable.com/api/v1/widget/accounts/{slug}?details=false")
202 + if r.status_code != 200:
203 + return None
204 + data = r.json()
205 + jobs = data.get("jobs") or []
206 + if not jobs:
207 + return None
208 + labels = [[", ".join(x for x in (j.get("city"), j.get("state"),
209 + j.get("country")) if x)] for j in jobs]
210 + qc, ex = _qc_stats(labels)
211 + return {"found": len(jobs), "qc": qc, "qc_ex": ex,
212 + "api_name": data.get("name") or ""}
213 +
214 +
215 +def probe_recruitee(slug: str, cand: dict) -> dict | None:
216 + r = _req(f"{slug}.recruitee.com", "GET",
217 + f"https://{slug}.recruitee.com/api/offers/")
218 + if r.status_code != 200:
219 + return None
220 + offers = r.json().get("offers") or []
221 + if not offers:
222 + return None
223 + labels = []
224 + for o in offers:
225 + locs = [o.get("location") or "",
226 + ", ".join(x for x in (o.get("city"), o.get("country")) if x)]
227 + labels.append(locs)
228 + qc, ex = _qc_stats(labels)
229 + return {"found": len(offers), "qc": qc, "qc_ex": ex, "api_name": ""}
230 +
231 +
232 +def probe_breezy(slug: str, cand: dict) -> dict | None:
233 + r = _req(f"{slug}.breezy.hr", "GET", f"https://{slug}.breezy.hr/json")
234 + if r.status_code != 200:
235 + return None
236 + try:
237 + data = r.json()
238 + except ValueError:
239 + return None
240 + if not isinstance(data, list) or not data:
241 + return None
242 + labels, name = [], ""
243 + for p in data:
244 + loc = p.get("location") or {}
245 + labels.append([loc.get("name") or "",
246 + ", ".join(x for x in (loc.get("city"),
247 + (loc.get("state") or {}).get("name")
248 + if isinstance(loc.get("state"), dict)
249 + else loc.get("state"),
250 + loc.get("country", {}).get("name")
251 + if isinstance(loc.get("country"), dict)
252 + else loc.get("country")) if x)])
253 + name = name or (p.get("company") or {}).get("name", "") \
254 + if isinstance(p.get("company"), dict) else name
255 + qc, ex = _qc_stats(labels)
256 + return {"found": len(data), "qc": qc, "qc_ex": ex, "api_name": name}
257 +
258 +
259 +def probe_bamboohr(slug: str, cand: dict) -> dict | None:
260 + r = _req(f"{slug}.bamboohr.com", "GET",
261 + f"https://{slug}.bamboohr.com/careers/list",
262 + headers={"User-Agent": UA, "Accept": "application/json"})
263 + if r.status_code != 200:
264 + return None
265 + try:
266 + data = r.json()
267 + except ValueError:
268 + return None
269 + jobs = data.get("result") or []
270 + if not jobs:
271 + return None
272 + labels = []
273 + for j in jobs:
274 + loc = j.get("location") or {}
275 + labels.append([", ".join(x for x in (loc.get("city"),
276 + loc.get("state")) if x)])
277 + qc, ex = _qc_stats(labels)
278 + return {"found": len(jobs), "qc": qc, "qc_ex": ex,
279 + "api_name": (data.get("meta") or {}).get("companyName") or ""}
280 +
281 +
282 +def _wd_post(tenant: str, host: str, site: str,
283 + search: str = "") -> requests.Response:
284 + url = (f"https://{tenant}.{host}.myworkdayjobs.com"
285 + f"/wday/cxs/{tenant}/{site}/jobs")
286 + return _req(f"{tenant}.{host}.myworkdayjobs.com", "POST", url,
287 + json={"appliedFacets": {}, "limit": 20, "offset": 0,
288 + "searchText": search})
289 +
290 +
291 +def _wd_eval(tenant: str, host: str, site: str) -> dict | None:
292 + """Site valide -> confirme s'il y a ≥1 offre dont ≥1 au Québec
293 + (page 1 brute, puis recherches « Québec »)."""
294 + total = 0
295 + for st in ("", "Québec", "Quebec, Canada"):
296 + try:
297 + r = _wd_post(tenant, host, site, st)
298 + except requests.RequestException:
299 + return None
300 + if r.status_code != 200:
301 + return None
302 + try:
303 + data = r.json()
304 + except ValueError:
305 + return None
306 + items = data.get("jobPostings") or []
307 + total = max(total, int(data.get("total") or 0))
308 + if st == "" and not total:
309 + return None
310 + labels = [[i.get("locationsText") or ""] for i in items]
311 + qc, ex = _qc_stats(labels)
312 + if qc:
313 + return {"found": total, "qc": qc, "qc_ex": ex, "api_name": "",
314 + "tenant": tenant, "host": host, "site": site}
315 + return None
316 +
317 +
318 +def probe_workday(slug: str, cand: dict) -> dict | None:
319 + """La page racine myworkdayjobs répond 406 aux bots : on passe par l'API
320 + cxs — 422 = tenant absent de cet hôte, 404 = hôte bon mais site inconnu,
321 + 200 = tenant+site valides. Hôte scanné puis site deviné au besoin."""
322 + tenant = (cand.get("tenant") or slug).lower()
323 + hosts = [cand["host"]] if cand.get("host") else WD_HOSTS
324 + for host in hosts:
325 + try:
326 + r = _wd_post(tenant, host, cand.get("site") or "__jobka_probe__")
327 + except requests.RequestException:
328 + continue
329 + if r.status_code == 200: # site fourni et valide
330 + return _wd_eval(tenant, host, cand["site"])
331 + if r.status_code != 404:
332 + continue # 422/5xx : hôte suivant
333 + # hôte confirmé — deviner le nom du site
334 + t = tenant
335 + derived = [f"{t}careers", f"{t}_careers", f"{t.capitalize()}Careers",
336 + f"{t.capitalize()}_Careers", f"{t.upper()}_CAREERS",
337 + f"{t.capitalize()}_External_Careers", t.capitalize(),
338 + f"Carrieres{t.capitalize()}", f"{t}_carrieres"]
339 + seen: list[str] = []
340 + for s in ([cand["site"]] if cand.get("site") else []) + derived \
341 + + WD_COMMON_SITES:
342 + if s in seen:
343 + continue
344 + seen.append(s)
345 + if len(seen) > 34:
346 + break
347 + try:
348 + r2 = _wd_post(tenant, host, s)
349 + except requests.RequestException:
350 + continue
351 + if r2.status_code == 200:
352 + return _wd_eval(tenant, host, s)
353 + return None # hôte trouvé mais nom de site introuvable
354 + return None
355 +
356 +
357 +PROBES = {
358 + "lever": probe_lever, "greenhouse": probe_greenhouse, "ashby": probe_ashby,
359 + "smartrecruiters": probe_smartrecruiters, "workable": probe_workable,
360 + "recruitee": probe_recruitee, "breezy": probe_breezy,
361 + "bamboohr": probe_bamboohr, "workday": probe_workday,
362 +}
363 +
364 +
365 +# --- registre existant (pour sauter les slugs déjà connectés) ----------------
366 +def known_slugs() -> set[str]:
367 + known: set[str] = set()
368 + src = ROOT / "data" / "sources.json"
369 + if src.exists():
370 + for s in json.loads(src.read_text())["sources"]:
371 + known.add(s["id"])
372 + pat = re.compile(r"^\s*(?:ORG|BOARD|COMPANY|TENANT)\s*=\s*['\"]([^'\"]+)",
373 + re.M)
374 + for f in (ROOT / "jobka" / "connectors").glob("*.py"):
375 + for m in pat.finditer(f.read_text(encoding="utf-8")):
376 + known.add(m.group(1).lower())
377 + return known
378 +
379 +
380 +# --- pipeline -----------------------------------------------------------------
381 +def probe_group(group: str, cands: list[dict], stats: dict,
382 + slock: threading.Lock) -> tuple[dict | None, dict]:
383 + """Essaie chaque variante du groupe ; retourne (feed confirmé, rejet)."""
384 + best = None
385 + for cand in cands:
386 + slug = cand["slug"]
387 + order = [cand["ats"]] if cand.get("ats") else [
388 + a for a in DEFAULT_ORDER if not (cand.get("no_workday")
389 + and a == "workday")]
390 + for ats in order:
391 + with slock:
392 + stats.setdefault(ats, [0, 0])[0] += 1
393 + try:
394 + res = PROBES[ats](slug, cand)
395 + except Exception: # API hors-format = slug non confirmé
396 + res = None
397 + if not res:
398 + continue
399 + if res["qc"] >= 1:
400 + with slock:
401 + stats[ats][1] += 1
402 + feed = {"ats": ats,
403 + "employer": cand.get("employer") or slug,
404 + "sectors": cand.get("sectors") or [],
405 + "cities": cand.get("cities") or [],
406 + "url": cand.get("url", ""),
407 + "_found": res["found"], "_qc": res["qc"],
408 + "_qc_locations": res["qc_ex"],
409 + "_api_name": res.get("api_name", "")}
410 + if ats == "workday":
411 + feed.update(tenant=res["tenant"], host=res["host"],
412 + site=res["site"])
413 + else:
414 + feed["org"] = res.get("org_override", slug)
415 + return feed, {}
416 + if best is None or res["found"] > best.get("found", 0):
417 + best = {"ats": ats, "slug": slug, "found": res["found"]}
418 + reject = {"group": group, "slugs": [c["slug"] for c in cands],
419 + "reason": "no_qc" if best else "no_api", "best": best}
420 + return None, reject
421 +
422 +
423 +def main() -> None:
424 + ap = argparse.ArgumentParser()
425 + ap.add_argument("candidates")
426 + ap.add_argument("--out", required=True)
427 + ap.add_argument("--rejected", default="")
428 + ap.add_argument("--workers", type=int, default=8)
429 + args = ap.parse_args()
430 +
431 + raw = Path(args.candidates).read_text(encoding="utf-8").strip()
432 + if raw.startswith("["):
433 + cands = json.loads(raw)
434 + else:
435 + cands = [json.loads(l) for l in raw.splitlines() if l.strip()]
436 +
437 + known = known_slugs()
438 + groups: dict[str, list[dict]] = {}
439 + skipped = 0
440 + for c in cands:
441 + slug = (c.get("slug") or "").strip()
442 + if not slug:
443 + continue
444 + if slug.lower() in known or (c.get("tenant") or "").lower() in known:
445 + skipped += 1
446 + continue
447 + groups.setdefault(c.get("group") or c.get("employer") or slug,
448 + []).append(c)
449 +
450 + n_slugs = sum(len(v) for v in groups.values())
451 + print(f"[probe] {len(groups)} groupes / {n_slugs} slugs à sonder "
452 + f"({skipped} déjà connectés, sautés)", flush=True)
453 +
454 + stats: dict[str, list[int]] = {}
455 + slock = threading.Lock()
456 + confirmed, rejected = [], []
457 + t0 = time.time()
458 + with ThreadPoolExecutor(max_workers=args.workers) as ex:
459 + futs = {ex.submit(probe_group, g, cs, stats, slock): g
460 + for g, cs in groups.items()}
461 + done = 0
462 + for fut in as_completed(futs):
463 + done += 1
464 + try:
465 + feed, reject = fut.result()
466 + except Exception as exc:
467 + print(f"[probe] {futs[fut]} : erreur {exc}", flush=True)
468 + continue
469 + if feed:
470 + confirmed.append(feed)
471 + print(f" ✓ {feed['employer']} -> {feed['ats']}"
472 + f" ({feed.get('org') or feed.get('tenant')}) "
473 + f"qc={feed['_qc']}/{feed['_found']}", flush=True)
474 + elif reject:
475 + rejected.append(reject)
476 + if done % 50 == 0:
477 + print(f"[probe] {done}/{len(groups)} groupes, "
478 + f"{len(confirmed)} confirmés, {_nreq[0]} requêtes, "
479 + f"{time.time()-t0:.0f}s", flush=True)
480 +
481 + confirmed.sort(key=lambda f: (f["ats"], f.get("org") or f.get("tenant", "")))
482 + Path(args.out).write_text(
483 + json.dumps(confirmed, ensure_ascii=False, indent=2) + "\n",
484 + encoding="utf-8")
485 + if args.rejected:
486 + Path(args.rejected).write_text(
487 + "\n".join(json.dumps(r, ensure_ascii=False) for r in rejected)
488 + + "\n", encoding="utf-8")
489 +
490 + print(f"\n[probe] terminé en {time.time()-t0:.0f}s — {_nreq[0]} requêtes")
491 + print(f"[probe] slugs sondés par ATS (sondés/confirmés) :")
492 + for ats in DEFAULT_ORDER:
493 + p, c = stats.get(ats, [0, 0])
494 + print(f" {ats:16s} {p:5d} / {c}")
495 + print(f"[probe] {len(confirmed)} groupes confirmés, "
496 + f"{len(rejected)} rejetés -> {args.out}")
497 +
498 +
499 +if __name__ == "__main__":
500 + main()
501