torchcell.literature.ZoteroLibrary
- class ZoteroLibrary(config: ZoteroConfig)[source]
Bases:
objectSynchronous handle to one Zotero library.
A thin, retry-hardened wrapper over a pyzotero client exposing the operations the literature-capture pipeline needs: list/resolve collections, look up an item by DOI, enumerate a paper’s PDF attachments (main article first, SI after), and download them into the on-disk artifact layout
<data_root>/torchcell-library/<citation_key>/paper.pdf si/si1.pdf si/si2.pdf …
This mirror is a backup to the existing URL-based dataset downloads, keyed by DOI so a dataset writer can fall back to it when an upstream source is down.
- classmethod from_env() ZoteroLibrary[source]
Build a library from
ZoteroConfig.from_env().
- list_collections() list[dict[str, Any]][source]
Every collection in the library (name + key dicts), across all pages.
pyzotero returns at most
limit=100rows per request, so the listing goes througheverything(...), which follows the API’snextlinks. Reading only the first page madecollection_keymiss any collection past the 100th, which is how the personaltorchcellroot went “missing” from the nightly bibliography export once the library grew (issue #563).
- collection_key(name: str, create_if_missing: bool = False) str[source]
Resolve a collection name to its key, optionally creating it.
The name is matched case-insensitively against EVERY collection in the library (see
list_collections()), so a name that exists is found no matter which page it sits on, andcreate_if_missingnever creates a duplicate of it.Raises
ValueErrornaming every candidate’s key and parent when two or more collections share the name (a nested name can repeat across branches; pass the key instead), andValueErrorlisting the available names when the name is not found andcreate_if_missingis False.
- collection_tree(root_collection: str) list[CollectionNode][source]
A collection and every collection nested beneath it, with display paths.
collection_itemsreturns only direct members, so a nested collection such astorchcell/torchcell-topics/microbe-perturb-seqis invisible without walking the tree, which is exactly where new reading is filed. Returned in depth-first order with the root first. The root is resolved against the same full, paged listing the tree is built from.
- find_item_by_doi(doi: str) dict[str, Any] | None[source]
Return the library item whose
data.DOImatches exactly, else None.Zotero’s
qquick search indexes title/creator/year (and, withqmode=everything, attachment full text) but NOT the DOI metadata field, so aq=<doi>search returns nothing here. The reliable lookup is a fulleverything(items())scan matched against each item’s actualdata["DOI"]– cheap for a curated library (a few paginated GETs).
- doi_in_library(doi: str) bool[source]
True if an item with this exact DOI already exists in the library.
- pdf_attachments(item_key: str) list[dict[str, Any]][source]
PDF attachments of an item, main article first then SI.
Auto-paginates: heavily-annotated items can accrue more than the default 100-item page, which would otherwise hide the source PDF.
- download_artifact(item: dict[str, Any], data_root: str | Path | None = None, library_dirname: str = 'torchcell-library') Path[source]
Download an item’s PDFs into its citation-keyed artifact directory.
Writes the main article to
paper.pdfand each SI PDF tosi/si1.pdf,si/si2.pdf, … under<data_root>/<library_dirname>/<citation_key>/. The citation key is taken from Zotero (native field orextra) so the directory matches the library; see_resolve_citation_key().- Parameters:
item – A Zotero item dict (as returned by
find_item_by_doi()).data_root – Root for the mirror; defaults to
$DATA_ROOT.library_dirname – Name of the library subdirectory under the root.
- Returns:
The artifact directory path.