SPB Git forge

spb/job-ka

Public
229commits 1branches 0releases
38.1 MBsize
maindefault branch
5 h agolast push
HTML 82.1% Python 14.6% TypeScript 1.9% CSS 1% JavaScript 0.5%
2.9 KB · 71 lines python
Raw Blame History
1# =============================================================================2# Job·Ka — Groupe KA3# Auteur  : Simon-Pierre Boucher4# Contact : contact@spboucher.ai5# Fichier : jobka/connectors/greenhouse.py6# Rôle    : Classe de plateforme Greenhouse (boards-api.greenhouse.io) — un7#           employeur = une sous-classe de ~8 lignes (BOARD, EMPLOYER)8# Créé    : 2026-08-17   Modifié : 2026-08-179# =============================================================================10"""Plateforme Greenhouse.1112API publique : GET https://boards-api.greenhouse.io/v1/boards/<board>/jobs13avec ?content=true -> {jobs:[{id, title, absolute_url, location:{name},14first_published, updated_at, application_deadline, content(HTML échappé),15departments, offices, metadata}]}.1617Une seule requête liste TOUT (contenu inclus) : pas de page détail.18`application_deadline` alimente date_deadline (retrait automatique).19"""20from __future__ import annotations2122from ..schema import JobPosting, clean_html, is_quebec_location23from .base import BaseConnector242526class GreenhouseConnector(BaseConnector):27    """Base Greenhouse — sous-classes : définir source_id, EMPLOYER, BOARD."""2829    ats = "greenhouse"30    request_delay = 1.03132    EMPLOYER = ""33    BOARD = ""34    quebec_only = True3536    def _keep(self, item: dict) -> bool:37        if not self.quebec_only:38            return True39        # strict sur le lieu du POSTE (les bureaux de l'entreprise sont trop40        # larges : un employeur québécois peut afficher un poste aux États-Unis)41        return is_quebec_location((item.get("location") or {}).get("name") or "")4243    def fetch(self) -> list[JobPosting]:44        data = self.get(45            f"https://boards-api.greenhouse.io/v1/boards/{self.BOARD}/jobs",46            params={"content": "true"}).json()47        out: list[JobPosting] = []48        for item in data.get("jobs") or []:49            if not self._keep(item):50                continue51            deadline = item.get("application_deadline") or None52            job = JobPosting(53                source=self.source_id, external_id=str(item.get("id") or ""),54                url=item.get("absolute_url") or "",55                employer=self.EMPLOYER or item.get("company_name") or "",56                title=item.get("title") or "",57                description=clean_html(item.get("content") or ""),58                location_label=(item.get("location") or {}).get("name") or "",59                date_posted=item.get("first_published") or item.get("updated_at"),60                date_deadline=deadline,61                language=(item.get("language") or "")[:2],  # « fr » / « en »62                ats=self.ats,63            )64            deps = [d.get("name") for d in item.get("departments") or [] if d.get("name")]65            if deps:66                job.details["team"] = deps[0]67            if item.get("requisition_id"):68                job.details["requisition"] = str(item["requisition_id"])69            out.append(job)70        return out71