Add CPR cross-referencing (related documents)

Clicking any flagged card that contains CPR hits now shows a "Related documents" section in the preview panel,
  listing other items from the same scan session that share at least one CPR number. Items are ordered by number of
  shared CPRs; clicking any entry opens it in the preview panel. Works in both live mode and scan history mode.

  Implementation
  - GDPRDb.get_related_items() — SQL self-join on the existing cpr_index table using the same symmetric 300 s session
  window as get_session_items. No new data collection needed.
  - GET /api/db/related/<item_id>?ref=N — new endpoint in routes/database.py, consistent with the ?ref convention used
   by /api/db/flagged.
  - #previewRelated div injected between the metadata block and disposition row in the preview panel.
  - _loadRelated(f) in results.js fetches and renders the list; window._openRelated() resolves items from the live
  grid or falls back to the API response for history-mode items.

  Also
  - Added keyword/FTS5 search as a deferred idea in SUGGESTIONS.md
  - Updated CHANGELOG.md, README.md, and CLAUDE.md
This commit is contained in:
StyxX65 committed 2026-04-25 21:15:50 +02:00
1 parent 8b55e9d933
commit d84e57239a
8 files changed
+118 -1

No files matched your search

+31
View File
@@ -523,6 +523,37 @@ class ScanDB:
result.append(d)
return result
def get_related_items(self, item_id: str, ref_scan_id: int | None = None,
window_seconds: int = 300) -> list[dict]:
"""Return flagged items from the same session that share at least one CPR
hash with *item_id*, ordered by number of shared CPRs descending."""
if ref_scan_id:
row = self._connect().execute(
"SELECT started_at FROM scans WHERE id=?", (ref_scan_id,)
).fetchone()
else:
row = self._connect().execute(
"SELECT started_at FROM scans WHERE finished_at IS NOT NULL ORDER BY id DESC LIMIT 1"
).fetchone()
if not row:
return []
latest_start = row[0]
rows = self._connect().execute(
"""SELECT fi.*, COUNT(DISTINCT ci2.cpr_hash) AS shared_cprs
FROM cpr_index ci1
JOIN cpr_index ci2 ON ci2.cpr_hash = ci1.cpr_hash
JOIN flagged_items fi ON fi.id = ci2.item_id
JOIN scans s ON fi.scan_id = s.id
WHERE ci1.item_id = ?
AND fi.id != ?
AND s.started_at BETWEEN ? AND ?
AND s.finished_at IS NOT NULL
GROUP BY fi.id
ORDER BY shared_cprs DESC, fi.cpr_count DESC""",
(item_id, item_id, latest_start - window_seconds, latest_start + window_seconds),
).fetchall()
return [dict(r) for r in rows]
def get_session_sources(self, window_seconds: int = 300) -> set:
"""Return the union of all source keys scanned in the current session.