"""Hospital Jobs Online adapter — URL-based pagination with page= parameter.""" from __future__ import annotations import re from loguru import logger from gimme_job.adapters.base import ManifestDrivenAdapter from gimme_job.adapters.registry import register from gimme_job.models.dto import JobPostingCandidate, RawJobCard @register("hospitaljobsonline") class HospitalJobsOnlineAdapter(ManifestDrivenAdapter): _BASE_URL = "https://www.hospitaljobsonline.com" def _wait_for_results(self, page) -> None: try: page.wait_for_selector("div.jobresult", timeout=15000, state="attached") except Exception: logger.debug("[hospitaljobsonline] Results wait timed out — may be zero results") def collect_cards(self, page, manifest): self._wait_for_results(page) return super().collect_cards(page, manifest) def paginate(self, page, page_index: int, config) -> bool: """Navigate to next page by incrementing the ?page= URL parameter.""" if page_index >= config.pagination.max_pages - 1: return False current_url = page.url next_page = page_index + 2 if re.search(r"[?&]page=\d+", current_url): next_url = re.sub(r"(page=)\d+", f"page={next_page}", current_url) else: sep = "&" if "?" in current_url else "?" next_url = f"{current_url}{sep}page={next_page}" logger.debug(f"[hospitaljobsonline] Paginating to page {next_page}: {next_url}") page.goto(next_url, timeout=30000) self._wait_for_results(page) return True def normalize(self, raw: RawJobCard) -> JobPostingCandidate: candidate = super().normalize(raw) if candidate.job_url and candidate.job_url.startswith("/"): candidate.job_url = self._BASE_URL + candidate.job_url # Parse location text: "1 week ago - Company Name - Location: City, ST" if candidate.location: loc = candidate.location # Extract part after "Location:" if present m = re.search(r"Location:\s*(.+)$", loc, re.IGNORECASE) if m: candidate.location = m.group(1).strip() else: # Fallback: take last segment after " - " parts = [p.strip() for p in loc.split(" - ")] if len(parts) >= 2: candidate.location = parts[-1] return candidate