# ============================================================================= # Job·Ka — Groupe KA # Auteur : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Fichier : jobka/connectors/workable.py # Rôle : Classe de plateforme Workable (widget public v1) — un employeur = # une sous-classe de ~8 lignes (ORG, EMPLOYER) # Créé : 2026-08-17 Modifié : 2026-08-17 # ============================================================================= """Plateforme Workable. API publique : GET https://apply.workable.com/api/v1/widget/accounts/ ?details=true -> {name, jobs:[{title, shortcode, employment_type("Full-time"…), telecommuting(bool), department, url, published_on("2026-06-19"), country, city, state, locations:[{country, countryCode, city, region}], education, experience, description(HTML)}]}. Une seule requête liste TOUT (descriptions incluses). Pas de salaire dans le widget v1 — finalize() tente l'extraction depuis la description. """ from __future__ import annotations from ..schema import JobPosting, clean_html, is_quebec_location from .base import BaseConnector class WorkableConnector(BaseConnector): """Base Workable — sous-classes : définir source_id, EMPLOYER, ORG.""" ats = "workable" request_delay = 1.0 EMPLOYER = "" ORG = "" quebec_only = True @staticmethod def _locations(item: dict) -> list[str]: locs = [f"{item.get('city') or ''}, {item.get('state') or ''}"] locs += [f"{l.get('city') or ''}, {l.get('region') or ''}" for l in item.get("locations") or [] if (l.get("countryCode") or "").upper() in ("CA", "")] return [l.strip(", ") for l in locs if l.strip(", ")] def _keep(self, item: dict) -> bool: if not self.quebec_only: return True return any(is_quebec_location(l) for l in self._locations(item)) def fetch(self) -> list[JobPosting]: data = self.get("https://apply.workable.com/api/v1/widget/accounts/" f"{self.ORG}", params={"details": "true"}).json() out: list[JobPosting] = [] for item in data.get("jobs") or []: if not self._keep(item): continue locs = self._locations(item) job = JobPosting( source=self.source_id, external_id=str(item.get("shortcode") or ""), url=item.get("url") or item.get("shortlink") or "", employer=self.EMPLOYER or data.get("name") or "", title=item.get("title") or "", description=clean_html(item.get("description") or ""), location_label=locs[0] if locs else "", work_mode="teletravail" if item.get("telecommuting") else None, date_posted=item.get("published_on") or item.get("created_at"), apply_url=item.get("application_url") or "", ats=self.ats, ) job.details["employment_label"] = item.get("employment_type") or "" if item.get("department"): job.details["team"] = item["department"] if item.get("function"): job.details["function"] = item["function"] if item.get("industry"): job.details["industry"] = item["industry"] if item.get("experience"): job.requirements["experience"] = item["experience"] if item.get("education"): job.requirements["education"] = item["education"] out.append(job) return out