File size: 4,070 Bytes
df51fac
d1ccc47
df51fac
 
 
 
03b3d27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9ca8ee5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cbd62a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df51fac
d1ccc47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df51fac
 
 
d1ccc47
03b3d27
 
 
d1ccc47
 
 
9ca8ee5
cbd62a3
df51fac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""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),
        }