"""Arora Group Jobs adapter — Load More button, keyword post-filter.""" from __future__ import annotations 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 _KEYWORDS = ["dentist", "orthodontic", "orthodontist"] _MAX_LOAD_MORE = 3 # clicks in addition to the initial load @register("aroragroup") class AroraGroupAdapter(ManifestDrivenAdapter): _BASE_URL = "https://jobs.aroragroup.com" def collect_cards(self, page, manifest): """Load initial results, click 'Load More' up to N times, then filter by keywords.""" try: page.wait_for_selector(".job-post-row", timeout=15000, state="attached") except Exception: logger.debug("[aroragroup] Initial results wait timed out") for i in range(_MAX_LOAD_MORE): btn = page.query_selector("button#loadMore") if not btn or not btn.is_visible(): logger.debug(f"[aroragroup] Load More button gone after {i} extra loads") break current_count = len(page.query_selector_all(".job-post-row[onclick]")) btn.click() try: page.wait_for_function( f"document.querySelectorAll('.job-post-row[onclick]').length > {current_count}", timeout=15000, ) logger.debug(f"[aroragroup] Load More {i + 1}/{_MAX_LOAD_MORE}: loaded more cards") except Exception: logger.debug(f"[aroragroup] Load More {i + 1}: no new cards appeared — stopping") break all_cards = super().collect_cards(page, manifest) # Filter by keywords in title filtered = [c for c in all_cards if self._matches_keywords(c.title)] logger.debug( f"[aroragroup] Keyword filter: {len(filtered)}/{len(all_cards)} cards matched" ) return filtered def paginate(self, page, page_index: int, config) -> bool: """Pagination is handled inside collect_cards — always return False.""" return False 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 return candidate @staticmethod def _matches_keywords(title: str | None) -> bool: if not title: return False title_lower = title.lower() return any(kw in title_lower for kw in _KEYWORDS)