fix: add creators/extra to load_export_rows row dict, fix journal key lookup for first_author/journal/IF in library-records; add first_author, journal, impact_factor columns to Base views; fix PDF storage/ subdirectory resolution

This commit is contained in:
Research Assistant 2026-04-27 20:25:35 +08:00
parent 67a52a898a
commit a3270dc791
5 changed files with 39 additions and 19 deletions

View file

@ -60,7 +60,7 @@ def resolve_pdf_path(
# Try Zotero storage-relative (format: "storage:XXXX/item.pdf")
if raw.startswith("storage:") and zotero_dir is not None:
storage_rel = raw[len("storage:") :].lstrip("/")
storage_candidate = (zotero_dir / storage_rel.replace("/", os.sep)).resolve()
storage_candidate = (zotero_dir / "storage" / storage_rel.replace("/", os.sep)).resolve()
if is_valid_pdf(storage_candidate):
return str(storage_candidate)

View file

@ -110,6 +110,9 @@ def build_base_views(domain: str) -> list[dict]:
"file.name",
"title",
"year",
"first_author",
"journal",
"impact_factor",
"has_pdf",
"do_ocr",
"analyze",
@ -125,6 +128,9 @@ def build_base_views(domain: str) -> list[dict]:
"order": [
"year",
"title",
"first_author",
"journal",
"impact_factor",
"has_pdf",
"do_ocr",
"analyze",
@ -137,27 +143,27 @@ def build_base_views(domain: str) -> list[dict]:
},
{
"name": "待 OCR",
"order": ["year", "title", "has_pdf", "do_ocr", "ocr_status", "pdf_path"],
"order": ["year", "first_author", "title", "has_pdf", "do_ocr", "ocr_status", "pdf_path"],
"filter": 'do_ocr = true AND ocr_status = "pending"',
},
{
"name": "OCR 完成",
"order": ["year", "title", "has_pdf", "do_ocr", "ocr_status", "pdf_path"],
"order": ["year", "first_author", "title", "has_pdf", "do_ocr", "ocr_status", "pdf_path"],
"filter": 'ocr_status = "done"',
},
{
"name": "待深度阅读",
"order": ["year", "title", "has_pdf", "do_ocr", "analyze", "ocr_status", "deep_reading_status", "pdf_path"],
"order": ["year", "first_author", "title", "has_pdf", "do_ocr", "analyze", "ocr_status", "deep_reading_status", "pdf_path"],
"filter": 'analyze = true AND ocr_status = "done" AND deep_reading_status = "pending"',
},
{
"name": "深度阅读完成",
"order": ["year", "title", "has_pdf", "do_ocr", "analyze", "ocr_status", "deep_reading_status", "pdf_path"],
"order": ["year", "first_author", "title", "has_pdf", "do_ocr", "analyze", "ocr_status", "deep_reading_status", "pdf_path"],
"filter": 'deep_reading_status = "done"',
},
{
"name": "正式卡片",
"order": ["title", "year", "has_pdf", "deep_reading_status", "pdf_path"],
"order": ["title", "year", "first_author", "journal", "impact_factor", "has_pdf", "deep_reading_status", "pdf_path"],
"filter": 'deep_reading_status = "done"',
},
{
@ -165,6 +171,9 @@ def build_base_views(domain: str) -> list[dict]:
"order": [
"title",
"year",
"first_author",
"journal",
"impact_factor",
"has_pdf",
"do_ocr",
"analyze",
@ -245,6 +254,12 @@ def merge_base_views(existing_content: str | None, new_views: list[dict]) -> str
displayName: "Title"
year:
displayName: "Year"
first_author:
displayName: "First Author"
journal:
displayName: "Journal"
impact_factor:
displayName: "IF"
has_pdf:
displayName: "PDF"
do_ocr:
@ -374,6 +389,12 @@ properties:
displayName: "Title"
year:
displayName: "Year"
first_author:
displayName: "First Author"
journal:
displayName: "Journal"
impact_factor:
displayName: "IF"
has_pdf:
displayName: "PDF"
do_ocr:

View file

@ -279,7 +279,7 @@ def obsidian_wikilink_for_pdf(pdf_path: str, vault_dir: Path, zotero_dir: Path |
# Handle storage: prefix paths by resolving through zotero_dir
if text.startswith("storage:") and zotero_dir is not None:
storage_rel = text[len("storage:") :].lstrip("/").lstrip("\\")
absolute_pdf_path = (zotero_dir / storage_rel.replace("/", os.sep)).resolve()
absolute_pdf_path = (zotero_dir / "storage" / storage_rel.replace("/", os.sep)).resolve()
absolute_str = str(absolute_pdf_path)
else:
absolute_str = absolutize_vault_path(vault_dir, text, resolve_junction=True)
@ -491,8 +491,10 @@ def load_export_rows(path: Path) -> list[dict]:
"key": item.get("key") or item.get("itemKey", ""),
"title": item.get("title", ""),
"authors": extract_authors(item),
"creators": item.get("creators", []),
"abstract": item.get("abstractNote", ""),
"journal": item.get("publicationTitle", ""),
"extra": item.get("extra", ""),
"year": _extract_year(item.get("date", "")),
"date": item.get("date", ""),
"doi": item.get("DOI", ""),
@ -888,7 +890,7 @@ def run_selection_sync(vault: Path, verbose: bool = False) -> int:
if c.get("creatorType") == "author":
first_author = f"{c.get('firstName', '')} {c.get('lastName', '')}".strip()
break
journal = item.get("publicationTitle", "")
journal = item.get("journal", "")
extra = item.get("extra", "")
impact_factor = lookup_impact_factor(journal, extra, vault)
# Convert supplementary storage: paths to wikilinks

View file

@ -197,18 +197,17 @@ class TestWikilinkGeneration:
"""Tests for obsidian_wikilink_for_pdf() wikilink generation."""
def test_basic_wikilink(self, tmp_path: Path) -> None:
"""storage:KEY/file.pdf resolves to [[system/Zotero/KEY/file.pdf]]."""
"""storage:KEY/file.pdf resolves to [[system/Zotero/storage/KEY/file.pdf]]."""
vault_dir = tmp_path / "vault"
zotero_dir = vault_dir / "system" / "Zotero"
# Current implementation joins storage: directly under zotero_dir
storage_dir = zotero_dir / "KEY"
storage_dir = zotero_dir / "storage" / "KEY"
storage_dir.mkdir(parents=True)
pdf = storage_dir / "file.pdf"
pdf.write_text("PDF content")
result = obsidian_wikilink_for_pdf("storage:KEY/file.pdf", vault_dir, zotero_dir)
assert result == "[[system/Zotero/KEY/file.pdf]]"
assert result == "[[system/Zotero/storage/KEY/file.pdf]]"
def test_junction_resolution(self, tmp_path: Path, monkeypatch) -> None:
"""Junction resolved before relative path computed."""
@ -239,8 +238,7 @@ class TestWikilinkGeneration:
"""Output wikilink uses forward slashes, never backslashes."""
vault_dir = tmp_path / "vault"
zotero_dir = vault_dir / "system" / "Zotero"
# Current implementation joins storage: directly under zotero_dir
storage_dir = zotero_dir / "KEY"
storage_dir = zotero_dir / "storage" / "KEY"
storage_dir.mkdir(parents=True)
pdf = storage_dir / "file.pdf"
pdf.write_text("PDF content")
@ -254,8 +252,7 @@ class TestWikilinkGeneration:
"""Chinese characters preserved in wikilink without escaping."""
vault_dir = tmp_path / "vault"
zotero_dir = vault_dir / "system" / "Zotero"
# Current implementation joins storage: directly under zotero_dir
storage_dir = zotero_dir / "KEY"
storage_dir = zotero_dir / "storage" / "KEY"
storage_dir.mkdir(parents=True)
pdf = storage_dir / "中文论文.pdf"
pdf.write_text("PDF content")
@ -263,7 +260,7 @@ class TestWikilinkGeneration:
result = obsidian_wikilink_for_pdf("storage:KEY/中文论文.pdf", vault_dir, zotero_dir)
assert "中文论文" in result
assert result == "[[system/Zotero/KEY/中文论文.pdf]]"
assert result == "[[system/Zotero/storage/KEY/中文论文.pdf]]"
def test_empty_pdf_path(self, tmp_path: Path) -> None:
"""Empty pdf_path returns empty string."""

View file

@ -50,9 +50,9 @@ class TestResolvePdfPath:
assert result == str(target.resolve())
def test_zotero_storage_relative_path(self, tmp_path: Path) -> None:
"""storage: prefixed path resolves against zotero_dir."""
"""storage: prefixed path resolves against zotero_dir/storage/."""
zotero_dir = tmp_path / "zotero"
storage_dir = zotero_dir / "ABC123"
storage_dir = zotero_dir / "storage" / "ABC123"
storage_dir.mkdir(parents=True)
pdf = storage_dir / "item.pdf"
pdf.write_text("PDF content")