acl-api / sync /anthology_source.py
ivykopal's picture
feat: add pdf_url field to paper metadata and update related functionality
cbd62a3
Raw
History Blame Contribute Delete
4.07 kB
"""Reads paper records out of the acl-anthology package's data model."""
from pathlib import Path
from typing import Iterator
from acl_anthology import Anthology
def _author_name(author) -> str:
"""Convert an author entry to a plain display string.
In the real acl_anthology package (verified against the installed 1.2.0
source), Paper.authors is a tuple of NameSpecification objects, each
exposing a `.name` property returning a `Name` object with
`.as_first_last()`. We prefer that when available, but fall back to
plain str() for anything else (e.g. plain strings in tests/mocks), since
str() is idempotent on strings and works for any object with __str__.
"""
name = getattr(author, "name", None)
if name is not None and hasattr(name, "as_first_last"):
return name.as_first_last()
return str(author)
def _markup_text(value) -> str:
"""Convert a MarkupText-like value (or plain string/None) to plain text.
Paper.title is a `MarkupText` (has `.as_text()`); Paper.abstract is an
`Optional[MarkupText]`. Fall back to str() for plain strings/mocks.
"""
if value is None:
return ""
if hasattr(value, "as_text"):
return value.as_text()
return str(value)
def _bibtex(paper) -> str:
"""Generate the canonical ACL Anthology BibTeX entry for a paper.
`Paper.to_bibtex()` (verified against the installed acl_anthology==1.2.0
source) produces the same BibTeX the anthology website serves — including
author, booktitle/journal, pages, editor, publisher, address, doi, etc.
Some papers (frontmatter, or those whose bibkey is `NO_BIBKEY`) raise
`ValueError` when a bibkey is unavailable; return "" for those so they stay
citatation-less rather than crashing the sync. Also coerces non-string
returns (e.g. unittest mocks) to "".
"""
try:
value = paper.to_bibtex()
except Exception:
return ""
return value if isinstance(value, str) else ""
def _pdf_url(paper) -> str:
"""Return the direct PDF URL for a paper, or "" if unavailable.
`Paper.pdf` (verified against the installed acl_anthology==1.2.0 source)
is an `Optional[PDFReference]` whose `.url` property returns the
canonical anthology PDF URL. Frontmatter and some older papers have no
PDF reference. Mirrors `_bibtex`'s defensive try/except for mocks/edge
cases.
"""
try:
pdf = paper.pdf
if pdf is None:
return ""
value = pdf.url
except Exception:
return ""
return value if isinstance(value, str) else ""
def iter_papers(anthology_path: str) -> Iterator[dict]:
"""Iterate over every paper in a local acl-anthology data checkout.
`anthology_path` is expected to be the root of a cloned acl-anthology
git repo (i.e. the directory containing `data/`), not a repo URL.
Verified against the installed acl_anthology==1.2.0 source:
- `Paper.id` is only unique within its parent Volume (e.g. "1"); the
globally unique identifier is `Paper.full_id` (e.g. "2023.acl-long.1").
- `Paper.venue_acronym` does not exist on Paper; it's on the parent
Volume (`Paper.parent.venue_acronym`), inherited from the collection.
- `Paper.url` does not exist; the paper's canonical URL is
`Paper.web_url`.
- `Paper.year` returns a str (falls back to the parent Volume's year
when not set directly on the paper); cast to int for storage.
"""
anthology = Anthology(datadir=str(Path(anthology_path) / "data"))
for paper in anthology.papers():
authors = getattr(paper, "authors", None) or []
yield {
"id": paper.full_id,
"title": _markup_text(paper.title),
"abstract": _markup_text(paper.abstract),
"authors": ", ".join(_author_name(a) for a in authors),
"venue": paper.parent.venue_acronym,
"year": int(paper.year),
"url": paper.web_url,
"bibtex": _bibtex(paper),
"pdf_url": _pdf_url(paper),
}