add meta: and fn: tag namespaces, bib/meta.py for docstring ↔ Zotero mapping

- Tag.meta() for section-level citations (cclf-ip-s2.2.1, 42-cfr-425.502)
- Tag.fn() for linking Zotero items to express functions
- bib/meta.py: extract_meta_tags() parses docstrings for CCLF IP, CFR,
  FR citation patterns; tag_items_from_pipeline() walks pipeline and
  adds fn:/meta: tags to matched Zotero items; generate_docstring_refs()
  pulls Zotero items for an expression into a formatted reference block
This commit is contained in:
kert
2026-03-11 15:55:23 -04:00
parent aa9840ae1d
commit b407fd308a
3 changed files with 594 additions and 0 deletions

301
src/bib/meta.py Normal file
View File

@@ -0,0 +1,301 @@
"""Bidirectional mapping between Zotero bibliography and module docstrings.
Extracts structured citation references from express function docstrings,
generates ``meta:`` and ``fn:`` tags for Zotero items, and produces
formatted reference blocks that can be embedded in docstrings.
Three capabilities:
1. **Extract** — parse docstring citation patterns into ``meta:`` tags
2. **Tag** — walk a pipeline and tag Zotero items with ``fn:`` and ``meta:``
3. **Generate** — pull Zotero items for an expression and format a
reference block suitable for a docstring
Usage::
from bib.meta import extract_meta_tags, tag_items_from_pipeline
from bib.meta import generate_docstring_refs
from bib.store import Store
# Extract meta tags from a function's docstring
tags = extract_meta_tags(cclf.stg_beneficiary_xref)
# [Tag(meta, cclf-ip-s3.1), Tag(meta, cclf-ip-s5.1.1)]
# Tag all Zotero items referenced by a pipeline
store = Store("data/bib.sqlite")
report = tag_items_from_pipeline(pipeline, store)
# Generate a reference block for a docstring
refs = generate_docstring_refs("cclf._stg_beneficiary_xref", store)
"""
from __future__ import annotations
import re
from typing import TYPE_CHECKING
from bib.tag import Tag
if TYPE_CHECKING:
from collections.abc import Callable
from bib.store import Store
# ── Citation pattern extractors ───────────────────────────────────
# CCLF IP Section 2.2.1 "Title" (p.8-9):
_CCLF_IP_RE = re.compile(
r"CCLF\s+IP\s+Section\s+([\d.]+)"
r'(?:\s+"([^"]*)")?'
r"(?:\s*\(p\.([\d-]+)\))?"
)
# 42 CFR § 425.502 or 42 CFR 425.502
_CFR_RE = re.compile(r"(\d+)\s+CFR\s+§?\s*([\d.]+)")
# Federal Register: 90 FR 86252
_FR_RE = re.compile(r"(\d+)\s+FR\s+(\d+)")
def extract_meta_tags(fn: Callable) -> list[Tag]:
"""Parse a function's docstring for citation patterns.
Returns ``meta:`` tags for each structured reference found.
Recognized patterns:
- ``CCLF IP Section 2.2.1 "Title" (p.8)`` → ``meta:cclf-ip-s2.2.1``
- ``42 CFR § 425.502`` → ``meta:42-cfr-425.502``
- ``90 FR 86252`` → ``meta:fr-90-86252``
"""
doc = getattr(fn, "__doc__", None) or ""
tags: list[Tag] = []
seen: set[str] = set()
for m in _CCLF_IP_RE.finditer(doc):
section = m.group(1)
ref = f"cclf-ip-s{section}"
if ref not in seen:
seen.add(ref)
tags.append(Tag.meta(ref))
for m in _CFR_RE.finditer(doc):
title, section = m.group(1), m.group(2)
ref = f"{title}-cfr-{section}"
if ref not in seen:
seen.add(ref)
tags.append(Tag.meta(ref))
for m in _FR_RE.finditer(doc):
volume, page = m.group(1), m.group(2)
ref = f"fr-{volume}-{page}"
if ref not in seen:
seen.add(ref)
tags.append(Tag.meta(ref))
return tags
def extract_meta_details(fn: Callable) -> list[dict[str, str]]:
"""Parse a function's docstring for citation details.
Returns a list of dicts with keys: ``tag``, ``title``, ``page``,
``source``. More detailed than :func:`extract_meta_tags`.
"""
doc = getattr(fn, "__doc__", None) or ""
details: list[dict[str, str]] = []
seen: set[str] = set()
for m in _CCLF_IP_RE.finditer(doc):
section = m.group(1)
ref = f"cclf-ip-s{section}"
if ref in seen:
continue
seen.add(ref)
details.append(
{
"tag": f"meta:{ref}",
"source": "CCLF Information Packet",
"title": m.group(2) or "",
"section": section,
"page": m.group(3) or "",
}
)
for m in _CFR_RE.finditer(doc):
title, section = m.group(1), m.group(2)
ref = f"{title}-cfr-{section}"
if ref in seen:
continue
seen.add(ref)
details.append(
{
"tag": f"meta:{ref}",
"source": "Code of Federal Regulations",
"title": f"{title} CFR § {section}",
"section": section,
"page": "",
}
)
for m in _FR_RE.finditer(doc):
volume, page = m.group(1), m.group(2)
ref = f"fr-{volume}-{page}"
if ref in seen:
continue
seen.add(ref)
details.append(
{
"tag": f"meta:{ref}",
"source": "Federal Register",
"title": f"{volume} FR {page}",
"section": "",
"page": page,
}
)
return details
def expr_fn_tag(expr_name: str) -> Tag:
"""Generate a ``fn:`` tag from an expression name.
Strips the schema prefix to produce the function-level reference::
"cclf._stg_beneficiary_xref" → fn:cclf.stg_beneficiary_xref
"readmissions._int_encounter" → fn:readmissions.int_encounter
Internal names (prefixed with ``_``) have the underscore stripped
from the table part since ``fn:`` tags identify the *function*, not
the table naming convention.
"""
schema, _, table = expr_name.partition(".")
# Strip leading underscore from intermediate table names
fn_name = table.lstrip("_")
return Tag.fn(f"{schema}.{fn_name}")
# ── Pipeline → Zotero tagging ────────────────────────────────────
def tag_items_from_pipeline(
pipeline: object,
store: Store,
) -> dict[str, list[str]]:
"""Walk a pipeline and tag matching Zotero items.
For each expression in the pipeline:
1. Generates a ``fn:`` tag from the expression name
2. Extracts ``meta:`` tags from the function's docstring
3. Finds Zotero items that match the expression's ``refs`` tags
4. Adds the ``fn:`` and ``meta:`` tags to those items
Parameters
----------
pipeline : Pipeline
Pipeline with ``.exprs`` list of ``Expr`` objects.
store : Store
Bibliography store to update.
Returns
-------
dict[str, list[str]]
Mapping of expression name → list of tags added.
"""
report: dict[str, list[str]] = {}
for expr in pipeline.exprs: # type: ignore[attr-defined]
fn_tag = expr_fn_tag(expr.name)
meta_tags = extract_meta_tags(expr.fn)
all_new_tags = [fn_tag.label, *(t.label for t in meta_tags)]
# Find Zotero items matching the expression's existing refs
matched_keys: set[str] = set()
for ref in expr.refs:
items = store.list_items(tag=ref.label)
for item in items:
matched_keys.add(item.key)
# Also match by source tags from the module-level _REFS
if not matched_keys:
# Try the table tag as a fallback
table_tag = Tag.table(expr.name)
items = store.list_items(tag=table_tag.label)
for item in items:
matched_keys.add(item.key)
# Add new tags to matched items
added: list[str] = []
for key in matched_keys:
item = store.get(key)
for tag_label in all_new_tags:
if tag_label not in item.tags:
store.add_tag(key, tag_label)
added.append(f"{key}:{tag_label}")
report[expr.name] = added
return report
# ── Docstring reference generation ────────────────────────────────
def generate_docstring_refs(
expr_name: str,
store: Store,
*,
style: str = "apa",
) -> str:
"""Generate a formatted reference block for an expression's docstring.
Looks up Zotero items tagged with the expression's ``fn:`` tag
and formats them as a bibliography section.
Parameters
----------
expr_name : str
Qualified expression name (e.g. ``cclf._stg_beneficiary_xref``).
store : Store
Bibliography store to query.
style : str
Citation style (``apa`` or ``bluebook``).
Returns
-------
str
Formatted reference block, or empty string if no items found.
"""
fn_tag = expr_fn_tag(expr_name)
items = store.list_items(tag=fn_tag.label)
if not items:
return ""
lines = ["References", "~~~~~~~~~~"]
for item in items:
citation = store.format_citation(item.key, style=style)
lines.append(f"- {citation}")
return "\n".join(lines)
def generate_pipeline_bibliography(
pipeline: object,
store: Store,
*,
style: str = "apa",
) -> dict[str, str]:
"""Generate reference blocks for every expression in a pipeline.
Returns
-------
dict[str, str]
Mapping of expression name → formatted reference block.
"""
result: dict[str, str] = {}
for expr in pipeline.exprs: # type: ignore[attr-defined]
refs = generate_docstring_refs(expr.name, store, style=style)
if refs:
result[expr.name] = refs
return result

View File

@@ -38,6 +38,17 @@ Namespaces
slug (``YEAR_ASSET_VERSION``): slug (``YEAR_ASSET_VERSION``):
``sup:2023_PFS_PR``, ``sup:2026_PFS_FR`` ``sup:2023_PFS_PR``, ``sup:2026_PFS_FR``
``meta``
Section-level citation reference from a source document:
``meta:cclf-ip-s2.2.1``, ``meta:42-cfr-425.502``,
``meta:fr-90-86252``
``fn``
Links a bibliography item to a specific express function
that references it:
``fn:cclf.stg_beneficiary_xref``,
``fn:readmissions.int_encounter``
Usage:: Usage::
from bib.tag import Tag from bib.tag import Tag
@@ -172,6 +183,40 @@ class Tag(BaseModel):
""" """
return cls(namespace="program", value=program_name.lower()) return cls(namespace="program", value=program_name.lower())
@classmethod
def meta(cls, citation_ref: str) -> Tag:
"""Tag for a section-level citation reference.
Parameters
----------
citation_ref : str
Structured reference string, e.g. ``cclf-ip-s2.2.1``,
``42-cfr-425.502``, ``fr-90-86252``.
Examples::
Tag.meta("cclf-ip-s2.2.1") # meta:cclf-ip-s2.2.1
Tag.meta("42-cfr-425.502") # meta:42-cfr-425.502
"""
return cls(namespace="meta", value=citation_ref)
@classmethod
def fn(cls, qualified_name: str) -> Tag:
"""Tag linking a bibliography item to an express function.
Parameters
----------
qualified_name : str
Dotted function path without the ``aco.express.`` prefix,
e.g. ``cclf.stg_beneficiary_xref``.
Examples::
Tag.fn("cclf.stg_beneficiary_xref") # fn:cclf.stg_beneficiary_xref
Tag.fn("readmissions.int_encounter") # fn:readmissions.int_encounter
"""
return cls(namespace="fn", value=qualified_name)
_RULE_KIND = {"Final": "FR", "Proposed": "PR", "Correction": "CR"} _RULE_KIND = {"Final": "FR", "Proposed": "PR", "Correction": "CR"}

248
tests/bib/test_meta.py Normal file
View File

@@ -0,0 +1,248 @@
"""Tests for bib.meta — docstring ↔ Zotero bidirectional mapping."""
from __future__ import annotations
from bib.meta import (
expr_fn_tag,
extract_meta_details,
extract_meta_tags,
generate_docstring_refs,
tag_items_from_pipeline,
)
from bib.tag import Tag
# ── extract_meta_tags ─────────────────────────────────────────────
class TestExtractMetaTags:
def test_cclf_ip_section(self):
def fn():
"""CCLF IP Section 2.2.1 "Part A Claims Header File" (p.8-9):"""
tags = extract_meta_tags(fn)
assert len(tags) == 1
assert tags[0].label == "meta:cclf-ip-s2.2.1"
def test_multiple_cclf_sections(self):
def fn():
"""CCLF IP Section 3.1 "Matching MBIs" (p.14):
CCLF IP Section 5.1.1 "Creation of MBI field" (p.19):
"""
tags = extract_meta_tags(fn)
assert len(tags) == 2
labels = {t.label for t in tags}
assert labels == {"meta:cclf-ip-s3.1", "meta:cclf-ip-s5.1.1"}
def test_cclf_section_no_title(self):
def fn():
"""CCLF IP Section 3.1 (p.14):"""
tags = extract_meta_tags(fn)
assert len(tags) == 1
assert tags[0].label == "meta:cclf-ip-s3.1"
def test_cclf_section_no_page(self):
def fn():
"""CCLF IP Section 5.3.1"""
tags = extract_meta_tags(fn)
assert len(tags) == 1
assert tags[0].label == "meta:cclf-ip-s5.3.1"
def test_cfr_reference(self):
def fn():
"""See 42 CFR § 425.502 for details."""
tags = extract_meta_tags(fn)
assert len(tags) == 1
assert tags[0].label == "meta:42-cfr-425.502"
def test_cfr_without_section_symbol(self):
def fn():
"""See 42 CFR 425.502 for details."""
tags = extract_meta_tags(fn)
assert len(tags) == 1
assert tags[0].label == "meta:42-cfr-425.502"
def test_federal_register(self):
def fn():
"""Published at 90 FR 86252."""
tags = extract_meta_tags(fn)
assert len(tags) == 1
assert tags[0].label == "meta:fr-90-86252"
def test_mixed_citations(self):
def fn():
"""CCLF IP Section 2.2 "Part A" (p.8):
See also 42 CFR § 425.502 and 90 FR 86252.
"""
tags = extract_meta_tags(fn)
assert len(tags) == 3
labels = {t.label for t in tags}
assert "meta:cclf-ip-s2.2" in labels
assert "meta:42-cfr-425.502" in labels
assert "meta:fr-90-86252" in labels
def test_deduplication(self):
def fn():
"""CCLF IP Section 2.2 (p.8):
Another ref to CCLF IP Section 2.2 (p.8):
"""
tags = extract_meta_tags(fn)
assert len(tags) == 1
def test_no_docstring(self):
fn = lambda: None # noqa: E731
assert extract_meta_tags(fn) == []
def test_no_citations(self):
def fn():
"""Just a regular function."""
assert extract_meta_tags(fn) == []
def test_real_cclf_function(self):
from aco.express.cclf import stg_beneficiary_xref
tags = extract_meta_tags(stg_beneficiary_xref)
labels = {t.label for t in tags}
assert "meta:cclf-ip-s3.1" in labels
assert "meta:cclf-ip-s5.1.1" in labels
# ── extract_meta_details ──────────────────────────────────────────
class TestExtractMetaDetails:
def test_cclf_ip_details(self):
def fn():
"""CCLF IP Section 2.2.1 "Part A Claims Header File" (p.8-9):"""
details = extract_meta_details(fn)
assert len(details) == 1
d = details[0]
assert d["tag"] == "meta:cclf-ip-s2.2.1"
assert d["source"] == "CCLF Information Packet"
assert d["title"] == "Part A Claims Header File"
assert d["section"] == "2.2.1"
assert d["page"] == "8-9"
def test_cfr_details(self):
def fn():
"""42 CFR § 425.502"""
details = extract_meta_details(fn)
assert len(details) == 1
assert details[0]["source"] == "Code of Federal Regulations"
assert details[0]["title"] == "42 CFR § 425.502"
# ── expr_fn_tag ───────────────────────────────────────────────────
class TestExprFnTag:
def test_intermediate_table(self):
tag = expr_fn_tag("cclf._stg_beneficiary_xref")
assert tag.label == "fn:cclf.stg_beneficiary_xref"
def test_output_table(self):
tag = expr_fn_tag("cclf.medical_claim")
assert tag.label == "fn:cclf.medical_claim"
def test_double_underscore_internal(self):
tag = expr_fn_tag("readmissions._int_encounter")
assert tag.label == "fn:readmissions.int_encounter"
# ── Tag factory methods ───────────────────────────────────────────
class TestTagFactories:
def test_meta_tag(self):
t = Tag.meta("cclf-ip-s2.2.1")
assert t.namespace == "meta"
assert t.value == "cclf-ip-s2.2.1"
assert t.label == "meta:cclf-ip-s2.2.1"
def test_fn_tag(self):
t = Tag.fn("cclf.stg_beneficiary_xref")
assert t.namespace == "fn"
assert t.value == "cclf.stg_beneficiary_xref"
assert t.label == "fn:cclf.stg_beneficiary_xref"
def test_from_label_meta(self):
t = Tag.from_label("meta:cclf-ip-s2.2.1")
assert t.namespace == "meta"
assert t.value == "cclf-ip-s2.2.1"
def test_from_label_fn(self):
t = Tag.from_label("fn:cclf.stg_beneficiary_xref")
assert t.namespace == "fn"
assert t.value == "cclf.stg_beneficiary_xref"
# ── tag_items_from_pipeline ───────────────────────────────────────
class TestTagItemsFromPipeline:
def test_tags_matched_items(self):
from bib.item import Source
from bib.store import Store
store = Store(":memory:")
item = Source(
title="CCLF Information Packet",
tags=["source:cms-cclf-ip", "module:aco"],
)
key = store.create(item)
from aco.pipe.cclf import pipeline
report = tag_items_from_pipeline(pipeline, store)
# At least some expressions should have tagged the item
tagged = {k: v for k, v in report.items() if v}
assert len(tagged) > 0
# Verify the item now has fn: and meta: tags
updated = store.get(key)
fn_tags = [t for t in updated.tags if t.startswith("fn:")]
meta_tags = [t for t in updated.tags if t.startswith("meta:")]
assert len(fn_tags) > 0
assert len(meta_tags) > 0
# ── generate_docstring_refs ───────────────────────────────────────
class TestGenerateDocstringRefs:
def test_no_items(self):
from bib.store import Store
store = Store(":memory:")
refs = generate_docstring_refs("cclf._stg_beneficiary_xref", store)
assert refs == ""
def test_with_tagged_item(self):
from bib.item import Source
from bib.store import Store
store = Store(":memory:")
item = Source(
title="CCLF Information Packet v41.0",
tags=["fn:cclf.stg_beneficiary_xref"],
institution="CMS",
date_published="2025-07-16",
)
store.create(item)
refs = generate_docstring_refs("cclf._stg_beneficiary_xref", store)
assert "References" in refs
assert "CCLF Information Packet" in refs