"""Base class for structured job-board connectors (spec §9.3): the sensor URL is the public JSON/XML endpoint the vendor's own career page loads. Each subclass implements `parse_jobs(data, sensor)`; the base turns the list into an `Extraction` whose blocks mirror the jobs one-to-one (stable keys from external ids) so the block diff equals the job delta.""" from __future__ import annotations import re from collections.abc import Mapping from typing import Any, ClassVar from companyatlas.connectors._util import finish_job, job_blocks, jobs_text, load_json from companyatlas.fetch import FetchResult from companyatlas.sdk.connector import Connector from companyatlas.sdk.models import ExtractedJob, Extraction from companyatlas.taxonomy import Surface MAX_JOBS = 5000 class AtsConnector(Connector): vendor: ClassVar[str] = "" token_re: ClassVar[re.Pattern[str] | None] = None def token(self, sensor: Mapping[str, Any]) -> str | None: cfg = sensor.get("config") or {} if isinstance(cfg, Mapping) and cfg.get("token"): return str(cfg["token"]) if self.token_re is not None: m = self.token_re.search(str(sensor.get("url") or "")) if m: return m.group(1) return None def load(self, result: FetchResult) -> Any: return load_json(result) def parse_jobs(self, data: Any, sensor: Mapping[str, Any]) -> list[ExtractedJob]: # pragma: no cover - abstract raise NotImplementedError def extract(self, sensor: Mapping[str, Any], result: FetchResult) -> Extraction: data = self.load(result) jobs = [finish_job(j) for j in self.parse_jobs(data, sensor)[:MAX_JOBS] if j.title] # dedupe by external id / url seen: set[str] = set() unique: list[ExtractedJob] = [] for j in jobs: k = (j.external_id or j.url or f"{j.title}|{j.location_text}").lower() if k in seen: continue seen.add(k) unique.append(j) header = f"{self.vendor} board — {len(unique)} open positions" meta = {"vendor": self.vendor, "token": self.token(sensor), "job_count": len(unique), "source_url": result.final_url, "structured": True, "pages": result.headers.get("x-companyatlas-pages", "1")} return Extraction(text=jobs_text(unique, header), blocks=job_blocks(unique, path=f"{self.vendor} jobs"), title=header, language=None, meta=meta, jobs=unique) def surface_jobs_board() -> str: return str(Surface.JOBS_BOARD) __all__ = ["MAX_JOBS", "AtsConnector", "surface_jobs_board"]