Skip to content

Add AAFP clinical recommendation scraper - #22

Open
Abduhu1 wants to merge 1 commit into
MedARC-AI:mainfrom
Abduhu1:abdullah/aafp-scraper
Open

Add AAFP clinical recommendation scraper#22
Abduhu1 wants to merge 1 commit into
MedARC-AI:mainfrom
Abduhu1:abdullah/aafp-scraper

Conversation

@Abduhu1

@Abduhu1 Abduhu1 commented Aug 25, 2026

Copy link
Copy Markdown

What this adds

This PR adds an AAFP scraper for Clinical Insights pages.

It discovers eligible pages from the AAFP sitemap or accepts a specific page through --url. Each clinical recommendation drawer is saved as a separate ScrapedDocument. This avoids mixing recommendations with different populations, sources, dates, and AAFP positions into one document.

The scraper preserves the recommendation content as Markdown and records useful metadata, including:

  • Topic and category
  • Recommendation title and subtitle
  • Source links
  • AAFP status and status date
  • Topic URL and section anchor

It also reports errors when discovery or page parsing fails instead of silently returning an empty result.

Files changed

  • datasets/amfv_datasets/scraping/aafp.py

    • Adds AAFP discovery, parsing, metadata extraction, and error handling.
  • datasets/amfv_datasets/scraping/cli.py

    • Registers aafp as a scraper source.
    • Rejects --source all when used with a single --url.
  • datasets/test/test_scraping_aafp.py

    • Adds offline tests using synthetic HTML, XML, and mocked HTTP requests.
  • datasets/test/test_scraping_cli.py

    • Updates CLI source tests and adds coverage for the new validation rule.

No new dependencies were added.

How to test

uv sync --frozen

uv run --frozen pytest datasets/test/test_scraping_aafp.py -v

uv run --frozen pytest -q

uv run --frozen ruff check .

uv run --frozen ruff format --check .

uv run --frozen amfv-scrape --help

Optional live smoke test:

uv run --frozen amfv-scrape \
  --source aafp \
  --url "https://www.aafp.org/clinical-insights/cardiometabolic-health/hypertension" \
  --documents 2 \
  --no-progress \
  --output /tmp/aafp-smoke.jsonl

Results

  • AAFP scraper tests: 74 passed
  • Full test suite: 105 passed
  • Ruff checks and formatting passed
  • Direct URL and automatic discovery smoke tests produced valid documents

This PR contains scraper code and synthetic test fixtures only. It does not add scraped AAFP content.

@CLAassistant

CLAassistant commented Aug 25, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@zndr27 zndr27 mentioned this pull request Sep 2, 2026
return any(_normalized(heading.text_content()) == _GUIDELINES_HEADING for heading in main.iter("h2"))


def _category_drawers(main: lxml_html.HtmlElement) -> Iterator[tuple[str, lxml_html.HtmlElement]]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _category_drawers helper in aafp.py, the source collector, opens categories only at accordion headings. Its drawer branch ignores drawers when no category is open.

An accepted fix recognizes drawers directly under the guidelines heading while retaining the boundary that excludes unrelated resources. A regression fixture should cover both arrangements and the unrelated section. Next we examine a source link inside an accepted drawer.

TLDR A page with the same recommendations arranged differently can contribute no documents.



def _full_guideline_url(links: list[dict[str, str]]) -> str | None:
return next((link["url"] for link in links if _FULL_SOURCE_LINK_RE.search(link["text"])), None)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving down to _full_guideline_url in aafp.py, the metadata collector, the selection rule requires the literal full prefix. The drawer builder therefore leaves its dedicated guideline address empty for a link labelled Read the recommendation.

An accepted fix recognizes the source’s observed recommendation-link wording and tests it against unrelated references so a broader pattern does not choose the wrong link. Next we assemble the final decision.

TLDR The document keeps its link, but the dedicated field for finding the full guideline can stay empty.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants