HTML 82.1%
Python 14.6%
TypeScript 1.9%
CSS 1%
JavaScript 0.5%
1# =============================================================================2# Job·Ka — Groupe KA3# Auteur : Simon-Pierre Boucher4# Contact : contact@spboucher.ai5# Fichier : jobka/connectors/workable.py6# Rôle : Classe de plateforme Workable (widget public v1) — un employeur =7# une sous-classe de ~8 lignes (ORG, EMPLOYER)8# Créé : 2026-08-17 Modifié : 2026-08-179# =============================================================================10"""Plateforme Workable.1112API publique : GET https://apply.workable.com/api/v1/widget/accounts/<org>13 ?details=true14-> {name, jobs:[{title, shortcode, employment_type("Full-time"…),15 telecommuting(bool), department, url, published_on("2026-06-19"),16 country, city, state, locations:[{country, countryCode, city, region}],17 education, experience, description(HTML)}]}.1819Une seule requête liste TOUT (descriptions incluses). Pas de salaire dans le20widget v1 — finalize() tente l'extraction depuis la description.21"""22from __future__ import annotations2324from ..schema import JobPosting, clean_html, is_quebec_location25from .base import BaseConnector262728class WorkableConnector(BaseConnector):29 """Base Workable — sous-classes : définir source_id, EMPLOYER, ORG."""3031 ats = "workable"32 request_delay = 1.03334 EMPLOYER = ""35 ORG = ""36 quebec_only = True3738 @staticmethod39 def _locations(item: dict) -> list[str]:40 locs = [f"{item.get('city') or ''}, {item.get('state') or ''}"]41 locs += [f"{l.get('city') or ''}, {l.get('region') or ''}"42 for l in item.get("locations") or []43 if (l.get("countryCode") or "").upper() in ("CA", "")]44 return [l.strip(", ") for l in locs if l.strip(", ")]4546 def _keep(self, item: dict) -> bool:47 if not self.quebec_only:48 return True49 return any(is_quebec_location(l) for l in self._locations(item))5051 def fetch(self) -> list[JobPosting]:52 data = self.get("https://apply.workable.com/api/v1/widget/accounts/"53 f"{self.ORG}", params={"details": "true"}).json()54 out: list[JobPosting] = []55 for item in data.get("jobs") or []:56 if not self._keep(item):57 continue58 locs = self._locations(item)59 job = JobPosting(60 source=self.source_id,61 external_id=str(item.get("shortcode") or ""),62 url=item.get("url") or item.get("shortlink") or "",63 employer=self.EMPLOYER or data.get("name") or "",64 title=item.get("title") or "",65 description=clean_html(item.get("description") or ""),66 location_label=locs[0] if locs else "",67 work_mode="teletravail" if item.get("telecommuting") else None,68 date_posted=item.get("published_on") or item.get("created_at"),69 apply_url=item.get("application_url") or "",70 ats=self.ats,71 )72 job.details["employment_label"] = item.get("employment_type") or ""73 if item.get("department"):74 job.details["team"] = item["department"]75 if item.get("function"):76 job.details["function"] = item["function"]77 if item.get("industry"):78 job.details["industry"] = item["industry"]79 if item.get("experience"):80 job.requirements["experience"] = item["experience"]81 if item.get("education"):82 job.requirements["education"] = item["education"]83 out.append(job)84 return out85