"""Fetch the latest (newest) NCMDR weekly alert row from the SFDA website.""" from __future__ import annotations from dataclasses import dataclass from datetime import date from urllib.parse import urljoin from bs4 import BeautifulSoup from sfda_parser.sfda_scraper.http_client import SFDA_BASE, create_session WEEKLY_ALERT_URL = f"{SFDA_BASE}/en/weekly-alert" # TEMPORARY: how many recent weekly bulletins to import/scan. Set to 1 for latest-only. WEEKLY_BULLETINS_TO_IMPORT = 1 @dataclass class WeeklyAlertRow: title: str alert_date: date | None pdf_url: str def _parse_date(value: str) -> date | None: value = (value or "").strip() if not value: return None try: return date.fromisoformat(value) except ValueError: return None def _parse_table_row(tr) -> WeeklyAlertRow | None: title_el = tr.select_one(".views-field-title") date_el = tr.select_one(".views-field-field-date") file_el = tr.select_one(".views-field-field-file a[href]") if not title_el or not file_el: return None title = title_el.get_text(strip=True) date_str = date_el.get_text(strip=True) if date_el else "" href = file_el.get("href") if not title or not href: return None return WeeklyAlertRow( title=title, alert_date=_parse_date(date_str), pdf_url=urljoin(SFDA_BASE, href), ) def fetch_all_weekly_alerts(session=None) -> list[WeeklyAlertRow]: """Return every weekly bulletin row from the SFDA table (newest usually listed first).""" session = session or create_session() response = session.get(WEEKLY_ALERT_URL, timeout=60) response.raise_for_status() soup = BeautifulSoup(response.text, "html.parser") rows: list[WeeklyAlertRow] = [] for tr in soup.select("tr"): parsed = _parse_table_row(tr) if parsed: rows.append(parsed) if not rows: raise ValueError("Could not find any weekly alert rows on SFDA page") return rows def _sort_weekly_alerts_newest_first(rows: list[WeeklyAlertRow]) -> list[WeeklyAlertRow]: """Newest bulletin first; top-of-page wins when dates tie.""" indexed = list(enumerate(rows)) indexed.sort( key=lambda item: (item[1].alert_date or date.min, -item[0]), reverse=True, ) return [item[1] for item in indexed] def fetch_recent_weekly_alerts( session=None, count: int | None = None ) -> list[WeeklyAlertRow]: """ Return the N newest weekly bulletins (default: WEEKLY_BULLETINS_TO_IMPORT). Order: newest first. Use reversed() when importing oldest-to-newest. """ if count is None: count = WEEKLY_BULLETINS_TO_IMPORT rows = _sort_weekly_alerts_newest_first(fetch_all_weekly_alerts(session=session)) return rows[: max(1, count)] def fetch_latest_weekly_alert(session=None) -> WeeklyAlertRow: """Return the single newest weekly bulletin.""" return fetch_recent_weekly_alerts(session=session, count=1)[0]