format: use 4 spaces

This commit is contained in:
2026-04-17 15:24:15 -04:00
parent 82e4ed6fec
commit 06eff1c255
4 changed files with 112 additions and 109 deletions
+1 -1
View File
@@ -7,4 +7,4 @@ charset = utf-8
[*.py] [*.py]
indent_style = space indent_style = space
indent_size = 2 indent_size = 4
+1 -1
View File
@@ -3,4 +3,4 @@
from paperlib.cli import main from paperlib.cli import main
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+36 -36
View File
@@ -16,42 +16,42 @@ DEFAULT_CONFIG_FILENAME = "config.toml"
@dataclass(frozen=True, slots=True) @dataclass(frozen=True, slots=True)
class LibraryPaths: class LibraryPaths:
"""Resolved filesystem layout for a paper library.""" """Resolved filesystem layout for a paper library."""
root: Path root: Path
config_dir: Path config_dir: Path
papers_dir: Path papers_dir: Path
inbox_dir: Path inbox_dir: Path
db_dir: Path db_dir: Path
cache_dir: Path cache_dir: Path
db_path: Path db_path: Path
config_path: Path config_path: Path
@classmethod @classmethod
def from_root(cls, root: Path) -> LibraryPaths: def from_root(cls, root: Path) -> LibraryPaths:
"""Build a standard library layout from a root directory.""" """Build a standard library layout from a root directory."""
resolved_root = root.expanduser().resolve() resolved_root = root.expanduser().resolve()
config_dir = resolved_root / DEFAULT_CONFIG_DIRNAME config_dir = resolved_root / DEFAULT_CONFIG_DIRNAME
db_dir = resolved_root / DEFAULT_DB_DIRNAME db_dir = resolved_root / DEFAULT_DB_DIRNAME
return cls( return cls(
root=resolved_root, root=resolved_root,
config_dir=config_dir, config_dir=config_dir,
papers_dir=resolved_root / DEFAULT_PAPERS_DIRNAME, papers_dir=resolved_root / DEFAULT_PAPERS_DIRNAME,
inbox_dir=resolved_root / DEFAULT_INBOX_DIRNAME, inbox_dir=resolved_root / DEFAULT_INBOX_DIRNAME,
db_dir=db_dir, db_dir=db_dir,
cache_dir=resolved_root / DEFAULT_CACHE_DIRNAME, cache_dir=resolved_root / DEFAULT_CACHE_DIRNAME,
db_path=db_dir / DEFAULT_DB_FILENAME, db_path=db_dir / DEFAULT_DB_FILENAME,
config_path=config_dir / DEFAULT_CONFIG_FILENAME, config_path=config_dir / DEFAULT_CONFIG_FILENAME,
) )
def create_directories(self) -> None: def create_directories(self) -> None:
"""Create the standard library directories if they do not exist.""" """Create the standard library directories if they do not exist."""
for path in ( for path in (
self.root, self.root,
self.config_dir, self.config_dir,
self.papers_dir, self.papers_dir,
self.inbox_dir, self.inbox_dir,
self.db_dir, self.db_dir,
self.cache_dir, self.cache_dir,
): ):
path.mkdir(parents=True, exist_ok=True) path.mkdir(parents=True, exist_ok=True)
+74 -71
View File
@@ -31,35 +31,36 @@ class DatabaseManager:
with self._get_connection() as conn: with self._get_connection() as conn:
# Main papers table # Main papers table
conn.execute(""" conn.execute("""
CREATE TABLE IF NOT EXISTS papers ( CREATE TABLE IF NOT EXISTS papers (
paper_id TEXT PRIMARY KEY, paper_id TEXT PRIMARY KEY,
source_type TEXT NOT NULL, source_type TEXT NOT NULL,
source_id TEXT, source_id TEXT,
title TEXT NOT NULL, title TEXT NOT NULL,
authors_json TEXT NOT NULL, -- JSON array of authors authors_json TEXT NOT NULL, -- JSON array of authors
published_date TEXT, -- ISO format published_date TEXT, -- ISO format
updated_date TEXT, -- ISO format updated_date TEXT, -- ISO format
categories_json TEXT NOT NULL, -- JSON array of categories categories_json TEXT NOT NULL, -- JSON array of categories
pdf_path TEXT, pdf_path TEXT,
paper_md_path TEXT, paper_md_path TEXT,
summary_json_path TEXT, summary_json_path TEXT,
summary_md_path TEXT, summary_md_path TEXT,
imported_at TEXT NOT NULL, -- ISO format imported_at TEXT NOT NULL, -- ISO format
conversion_status TEXT NOT NULL, conversion_status TEXT NOT NULL,
summary_status TEXT NOT NULL, summary_status TEXT NOT NULL,
tags_json TEXT NOT NULL, -- JSON array of tags tags_json TEXT NOT NULL, -- JSON array of tags
notes TEXT NOT NULL, notes TEXT NOT NULL,
-- Computed fields for search -- Computed fields for search
search_text TEXT, -- Full-text search content search_text TEXT, -- Full-text search content
author_list TEXT, -- Space-separated authors for search author_list TEXT, -- Space-separated authors for search
category_list TEXT -- Space-separated categories for search category_list TEXT -- Space-separated categories
) )
""") """)
# Create indexes for common queries # Create indexes for common queries
conn.execute( conn.execute(
"CREATE INDEX IF NOT EXISTS idx_papers_source_type ON papers(source_type)" "CREATE INDEX IF NOT EXISTS idx_papers_source_type "
"ON papers(source_type)"
) )
conn.execute( conn.execute(
"CREATE INDEX IF NOT EXISTS idx_papers_source_id ON papers(source_id)" "CREATE INDEX IF NOT EXISTS idx_papers_source_id ON papers(source_id)"
@@ -73,52 +74,51 @@ class DatabaseManager:
"ON papers(summary_status)" "ON papers(summary_status)"
) )
conn.execute( conn.execute(
"CREATE INDEX IF NOT EXISTS idx_papers_imported_at ON papers(imported_at)" "CREATE INDEX IF NOT EXISTS idx_papers_imported_at "
"ON papers(imported_at)"
) )
# Full-text search virtual table # Full-text search virtual table
conn.execute(""" conn.execute("""
CREATE VIRTUAL TABLE IF NOT EXISTS papers_fts USING fts5( CREATE VIRTUAL TABLE IF NOT EXISTS papers_fts USING fts5(
paper_id UNINDEXED, paper_id UNINDEXED,
title, title,
authors, authors,
search_text, search_text,
categories, categories,
tags, tags,
notes, notes
content='papers', )
content_rowid='rowid' """)
)
""")
def index_paper(self, metadata: PaperMetadata) -> None: def index_paper(self, metadata: PaperMetadata) -> None:
"""Index a paper in the database.""" """Index a paper in the database."""
import json import json
with self._get_connection() as conn: with self._get_connection() as conn:
# Prepare data for insertion # Prepare data for insertion
parts = [ parts = [
metadata.title, metadata.title,
" ".join(metadata.authors), " ".join(metadata.authors),
" ".join(metadata.categories), " ".join(metadata.categories),
" ".join(metadata.tags), " ".join(metadata.tags),
metadata.notes, metadata.notes,
] ]
search_text = " ".join(parts) search_text = " ".join(parts)
author_list = " ".join(metadata.authors) author_list = " ".join(metadata.authors)
category_list = " ".join(metadata.categories) category_list = " ".join(metadata.categories)
# Insert or replace in main table # Insert or replace in main table
conn.execute( conn.execute(
""" """
INSERT OR REPLACE INTO papers ( INSERT OR REPLACE INTO papers (
paper_id, source_type, source_id, title, authors_json, paper_id, source_type, source_id, title, authors_json,
published_date, updated_date, categories_json, pdf_path, published_date, updated_date, categories_json, pdf_path,
paper_md_path, summary_json_path, summary_md_path, paper_md_path, summary_json_path, summary_md_path,
imported_at, conversion_status, summary_status, imported_at, conversion_status, summary_status,
tags_json, notes, search_text, author_list, category_list tags_json, notes, search_text, author_list, category_list
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", """,
( (
metadata.paper_id, metadata.paper_id,
metadata.source_type.value, metadata.source_type.value,
@@ -150,10 +150,10 @@ class DatabaseManager:
# Update FTS table # Update FTS table
conn.execute( conn.execute(
""" """
INSERT OR REPLACE INTO papers_fts ( INSERT OR REPLACE INTO papers_fts (
paper_id, title, authors, search_text, categories, tags, notes paper_id, title, authors, search_text, categories, tags, notes
) VALUES (?, ?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?, ?)
""", """,
( (
metadata.paper_id, metadata.paper_id,
metadata.title, metadata.title,
@@ -226,12 +226,12 @@ class DatabaseManager:
# Use FTS for full-text search # Use FTS for full-text search
cursor = conn.execute( cursor = conn.execute(
""" """
SELECT papers.* FROM papers_fts SELECT papers.* FROM papers_fts
JOIN papers ON papers.paper_id = papers_fts.paper_id JOIN papers ON papers.paper_id = papers_fts.paper_id
WHERE papers_fts MATCH ? WHERE papers_fts MATCH ?
ORDER BY rank ORDER BY rank
LIMIT ? LIMIT ?
""", """,
(query, limit), (query, limit),
) )
@@ -257,7 +257,8 @@ class DatabaseManager:
where_clause = f"{field} LIKE ?" where_clause = f"{field} LIKE ?"
params = [f"%{value}%"] params = [f"%{value}%"]
query = f"SELECT * FROM papers WHERE {where_clause} ORDER BY imported_at DESC LIMIT ?" order_by = "ORDER BY imported_at DESC LIMIT ?"
query = f"SELECT * FROM papers WHERE {where_clause} {order_by}"
params.append(limit) params.append(limit)
with self._get_connection() as conn: with self._get_connection() as conn:
@@ -284,7 +285,8 @@ class DatabaseManager:
# By conversion status # By conversion status
cursor = conn.execute( cursor = conn.execute(
"SELECT conversion_status, COUNT(*) as count FROM papers GROUP BY conversion_status" "SELECT conversion_status, COUNT(*) as count FROM papers "
"GROUP BY conversion_status"
) )
stats["by_conversion_status"] = { stats["by_conversion_status"] = {
row["conversion_status"]: row["count"] for row in cursor row["conversion_status"]: row["count"] for row in cursor
@@ -292,7 +294,8 @@ class DatabaseManager:
# By summary status # By summary status
cursor = conn.execute( cursor = conn.execute(
"SELECT summary_status, COUNT(*) as count FROM papers GROUP BY summary_status" "SELECT summary_status, COUNT(*) as count FROM papers "
"GROUP BY summary_status"
) )
stats["by_summary_status"] = { stats["by_summary_status"] = {
row["summary_status"]: row["count"] for row in cursor row["summary_status"]: row["count"] for row in cursor