You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

59 lines
2.0 KiB
Python

"""Consulting BFrench Jobs adapter — JazzHR single-page table, keyword 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 = ["orthodontist", "orthodontic", "dentist"]
@register("bfrench")
class BFrenchAdapter(ManifestDrivenAdapter):
def collect_cards(self, page, manifest) -> list[RawJobCard]:
"""Extract all job rows from the table and filter by keywords."""
raw_data = page.evaluate("""() => {
const links = document.querySelectorAll('a[href*="/apply/jobs/details/"]');
return Array.from(links).map(a => {
const row = a.closest('tr');
const cells = row ? Array.from(row.querySelectorAll('td')) : [];
return {
title: a.innerText.trim(),
url: a.href,
location: cells[1]?.innerText?.trim() || null
};
}).filter(j => j.title);
}""")
cards = []
for item in raw_data:
if not self._matches_keywords(item["title"]):
continue
cards.append(RawJobCard(
title=item["title"],
company="Consulting BFrench",
location=item["location"],
url=item["url"],
))
logger.info(
f"[bfrench] Keyword filter: {len(cards)}/{len(raw_data)} cards matched"
)
return cards
def paginate(self, page, page_index: int, config) -> bool:
return False
def normalize(self, raw: RawJobCard) -> JobPostingCandidate:
return super().normalize(raw)
@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)