From d5aa70956b281b43cfdfd9799b4d7ab743adef88 Mon Sep 17 00:00:00 2001 From: Ignacio Ballesteros Date: Sat, 14 Feb 2026 18:19:57 +0100 Subject: [PATCH] open zotero pdf --- src/org_to_quartz/citations/zotero.py | 59 ++++++++++++++++----------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/org_to_quartz/citations/zotero.py b/src/org_to_quartz/citations/zotero.py index 9d51417..f0d4188 100644 --- a/src/org_to_quartz/citations/zotero.py +++ b/src/org_to_quartz/citations/zotero.py @@ -56,38 +56,48 @@ class ZoteroResolver: def _get_item_by_citekey(self, cite_key: str) -> dict | None: """Get Zotero item data by citation key.""" - # Better BibTeX method to search by citekey - result = self._rpc_call("item.search", [f"citekey:{cite_key}"]) + # Better BibTeX advanced search syntax: [["field", "operator", "value"]] + result = self._rpc_call("item.search", [[["citationKey", "is", cite_key]]]) if result and isinstance(result, list) and len(result) > 0: return result[0] return None + def _get_pdf_link(self, cite_key: str) -> str | None: + """Get zotero://open-pdf link for the first PDF attachment.""" + attachments = self._rpc_call("item.attachments", [cite_key]) + if attachments and isinstance(attachments, list): + # Find first PDF attachment + for att in attachments: + if isinstance(att, dict): + open_url = att.get("open", "") + path = att.get("path", "") + # Check if it's a PDF (either by URL or path) + if "open-pdf" in open_url or path.lower().endswith(".pdf"): + return open_url + return None + def _extract_authors(self, item: dict) -> str: - """Extract author string from Zotero item.""" - creators = item.get("creators", []) - authors = [c for c in creators if c.get("creatorType") == "author"] + """Extract author string from CSL-JSON item.""" + # item.search returns CSL-JSON format with 'author' field + authors = item.get("author", []) if not authors: return "" if len(authors) == 1: - return authors[0].get("lastName", "") + return authors[0].get("family", "") elif len(authors) == 2: - return f"{authors[0].get('lastName', '')} & {authors[1].get('lastName', '')}" + return f"{authors[0].get('family', '')} & {authors[1].get('family', '')}" else: - return f"{authors[0].get('lastName', '')} et al." + return f"{authors[0].get('family', '')} et al." def _extract_year(self, item: dict) -> str: - """Extract year from Zotero item.""" - date = item.get("date", "") - # Try to extract year from date string - if date: - # Common formats: "2024", "2024-01-15", "January 2024" - import re - - match = re.search(r"(\d{4})", date) - if match: - return match.group(1) + """Extract year from CSL-JSON item.""" + # CSL-JSON has 'issued' field with date-parts array + issued = item.get("issued", {}) + date_parts = issued.get("date-parts", [[]]) + if date_parts and len(date_parts) > 0 and len(date_parts[0]) > 0: + return str(date_parts[0][0]) return "" def try_resolve(self, cite_key: str) -> CitationInfo | None: @@ -103,12 +113,13 @@ class ZoteroResolver: if item is None: return None - # Build zotero:// select URL - item_key = item.get("key", "") - library_id = item.get("libraryID", 1) - - # Format: zotero://select/items/@citekey or zotero://select/library/items/ITEMKEY - zotero_url = f"zotero://select/items/@{cite_key}" + # Try to get PDF attachment link, fallback to item selection + pdf_link = self._get_pdf_link(cite_key) + if pdf_link: + zotero_url = pdf_link + else: + # Fallback: zotero://select/items/@citekey + zotero_url = f"zotero://select/items/@{cite_key}" info = CitationInfo( key=cite_key,