format: use 4 spaces
This commit is contained in:
+1
-1
@@ -7,4 +7,4 @@ charset = utf-8
|
|||||||
|
|
||||||
[*.py]
|
[*.py]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
|
|||||||
@@ -3,4 +3,4 @@
|
|||||||
from paperlib.cli import main
|
from paperlib.cli import main
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
+36
-36
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user