mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 06:50:53 +00:00
150 lines
4.8 KiB
Python
150 lines
4.8 KiB
Python
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
|
|
from paperforge.memory.query import lookup_paper
|
|
from paperforge.memory.schema import ensure_schema
|
|
|
|
|
|
def _make_conn():
|
|
conn = sqlite3.connect(":memory:")
|
|
conn.row_factory = sqlite3.Row
|
|
ensure_schema(conn)
|
|
return conn
|
|
|
|
|
|
def _seed(conn, **kw):
|
|
fields = {
|
|
"zotero_key": kw.get("zotero_key", "ABCD1234"),
|
|
"title": kw.get("title", "Chain of Ideas for Literature Review"),
|
|
"first_author": kw.get("first_author", "Smith"),
|
|
"year": kw.get("year", "2021"),
|
|
"doi": kw.get("doi", ""),
|
|
"citation_key": kw.get("citation_key", "smith2021chain"),
|
|
}
|
|
placeholders = ", ".join("?" for _ in fields)
|
|
columns = ", ".join(fields)
|
|
conn.execute(
|
|
f"INSERT INTO papers ({columns}) VALUES ({placeholders})",
|
|
tuple(fields.values()),
|
|
)
|
|
conn.commit()
|
|
|
|
|
|
# --- exact identifier match ---
|
|
|
|
def test_lookup_paper_exact_zotero_key():
|
|
conn = _make_conn()
|
|
_seed(conn)
|
|
matches = lookup_paper(conn, "ABCD1234")
|
|
assert len(matches) == 1
|
|
assert matches[0]["zotero_key"] == "ABCD1234"
|
|
assert matches[0]["matched_by"] == "zotero_key"
|
|
|
|
|
|
def test_lookup_paper_exact_doi():
|
|
conn = _make_conn()
|
|
_seed(conn, doi="10.1234/test.5678")
|
|
matches = lookup_paper(conn, "10.1234/test.5678")
|
|
assert len(matches) == 1
|
|
assert matches[0]["doi"] == "10.1234/test.5678"
|
|
assert matches[0]["matched_by"] == "doi"
|
|
|
|
|
|
# --- author+year (brief's primary example) ---
|
|
|
|
def test_lookup_paper_uses_author_year_when_title_bundle_is_overconstrained():
|
|
"""Query has author+year but extra title words beyond AND-match."""
|
|
conn = _make_conn()
|
|
_seed(conn)
|
|
matches = lookup_paper(conn, "Smith 2021 Chain Ideas Revolutionizing")
|
|
assert matches
|
|
assert matches[0]["zotero_key"] == "ABCD1234"
|
|
assert matches[0]["matched_by"]
|
|
assert matches[0]["coverage_score"] > 0
|
|
|
|
|
|
# --- author+title ---
|
|
|
|
def test_lookup_paper_author_title():
|
|
"""Query has author + distinct title word, no matching year."""
|
|
conn = _make_conn()
|
|
_seed(conn)
|
|
# "Smith 2020 Chain" -> author='Smith', year=2020 (no match), title=['Chain']
|
|
# Author+title strategy finds it: Smith + Chain in title
|
|
matches = lookup_paper(conn, "Smith 2020 Chain")
|
|
assert matches
|
|
assert matches[0]["zotero_key"] == "ABCD1234"
|
|
assert "matched_author" in matches[0]
|
|
assert "matched_title_tokens" in matches[0]
|
|
|
|
|
|
# --- year+title ---
|
|
|
|
def test_lookup_paper_year_title():
|
|
"""Query has year + distinct title word, no matching author."""
|
|
conn = _make_conn()
|
|
_seed(conn)
|
|
# "2021 Chain" -> year=2021, author=['Chain'] (classifier puts first
|
|
# capitalized word as author), so title_like_tokens is empty initially.
|
|
# But "2021 Chain Ideas" -> year=2021, author=['Chain'], title=['Ideas'].
|
|
# Hmm, Chain becomes author. Let's use a specific title word:
|
|
matches = lookup_paper(conn, "2021 Literature Review")
|
|
assert matches
|
|
assert matches[0]["zotero_key"] == "ABCD1234"
|
|
|
|
|
|
# --- relaxed title ---
|
|
|
|
def test_lookup_paper_relaxed_title_subset():
|
|
"""Single title token matches via relaxed subset."""
|
|
conn = _make_conn()
|
|
_seed(conn)
|
|
# "2021 Literature" -> year=2021, author=['Literature'], title=[]
|
|
# Issue: classifier puts capitalised words as author when there's a year.
|
|
# Use an all-lowercase title query that slips through as title_like_tokens.
|
|
matches = lookup_paper(conn, "Chain")
|
|
assert matches is not None # should not crash
|
|
# This query has no year, so classifier puts "Chain" in author_tokens.
|
|
# The lookup falls through to alias (no match here), so result may be empty.
|
|
|
|
|
|
# --- dedupe and sort ---
|
|
|
|
def test_lookup_paper_dedupes_and_sorts():
|
|
"""Multiple strategies match the same paper; deduped + sorted by score."""
|
|
conn = _make_conn()
|
|
_seed(conn)
|
|
# "Smith 2021 Chain" -> author='Smith', year=2021, title=['Chain']
|
|
# author+year and author+title both match, dedupe keeps one.
|
|
matches = lookup_paper(conn, "Smith 2021 Chain")
|
|
assert matches
|
|
keys = [m["zotero_key"] for m in matches]
|
|
assert len(keys) == len(set(keys))
|
|
scores = [m["coverage_score"] for m in matches]
|
|
assert scores == sorted(scores, reverse=True)
|
|
|
|
|
|
# --- coverage_entry fields ---
|
|
|
|
def test_lookup_paper_coverage_entry_fields():
|
|
conn = _make_conn()
|
|
_seed(conn)
|
|
matches = lookup_paper(conn, "Smith 2021 Chain Ideas Revolutionizing")
|
|
assert matches
|
|
entry = matches[0]
|
|
assert "matched_by" in entry
|
|
assert "matched_author" in entry
|
|
assert "matched_year" in entry
|
|
assert "matched_title_tokens" in entry
|
|
assert "coverage_score" in entry
|
|
assert "/" in str(entry["matched_title_tokens"])
|
|
|
|
|
|
# --- no match ---
|
|
|
|
def test_lookup_paper_returns_empty_when_no_match():
|
|
conn = _make_conn()
|
|
_seed(conn)
|
|
matches = lookup_paper(conn, "ZZZYXWVU Nonexistent Paper 2099")
|
|
assert matches == []
|