Initial commit
This commit is contained in:
Executable
+161
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
from common import CONFIG
|
||||
|
||||
CATEGORIES = CONFIG.domain.categories
|
||||
|
||||
SCHEMA = """
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS categories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS papers (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
doi TEXT,
|
||||
title TEXT NOT NULL,
|
||||
title_normalized TEXT NOT NULL,
|
||||
first_author TEXT,
|
||||
year INTEGER,
|
||||
journal TEXT,
|
||||
volume TEXT,
|
||||
issue TEXT,
|
||||
pages TEXT,
|
||||
publisher TEXT,
|
||||
article_type TEXT,
|
||||
language TEXT,
|
||||
abstract TEXT,
|
||||
summary_ja TEXT,
|
||||
relevance INTEGER NOT NULL DEFAULT 0 CHECK (relevance BETWEEN 0 AND 5),
|
||||
priority TEXT NOT NULL DEFAULT 'D' CHECK (priority IN ('A','B','C','D')),
|
||||
review_status TEXT NOT NULL DEFAULT 'unreviewed',
|
||||
category_id INTEGER,
|
||||
pdf_path TEXT,
|
||||
file_size INTEGER,
|
||||
file_mtime TEXT,
|
||||
file_sha256 TEXT,
|
||||
source TEXT,
|
||||
landing_url TEXT,
|
||||
pdf_url TEXT,
|
||||
url_source TEXT,
|
||||
access_status TEXT,
|
||||
url_checked_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
checked_at TEXT,
|
||||
notes TEXT,
|
||||
FOREIGN KEY (category_id) REFERENCES categories(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS authors (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
name_normalized TEXT NOT NULL,
|
||||
orcid TEXT,
|
||||
affiliation TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS paper_authors (
|
||||
paper_id INTEGER NOT NULL,
|
||||
author_id INTEGER NOT NULL,
|
||||
author_order INTEGER NOT NULL,
|
||||
is_corresponding INTEGER NOT NULL DEFAULT 0 CHECK (is_corresponding IN (0,1)),
|
||||
PRIMARY KEY (paper_id, author_order),
|
||||
UNIQUE (paper_id, author_id),
|
||||
FOREIGN KEY (paper_id) REFERENCES papers(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS keywords (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
name_normalized TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS paper_keywords (
|
||||
paper_id INTEGER NOT NULL,
|
||||
keyword_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (paper_id, keyword_id),
|
||||
FOREIGN KEY (paper_id) REFERENCES papers(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (keyword_id) REFERENCES keywords(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS processing_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
pdf_path TEXT,
|
||||
paper_id INTEGER,
|
||||
status TEXT NOT NULL CHECK (status IN ('registered','updated','duplicate','skipped','error')),
|
||||
message TEXT,
|
||||
processed_at TEXT NOT NULL,
|
||||
FOREIGN KEY (paper_id) REFERENCES papers(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_papers_doi
|
||||
ON papers(doi) WHERE doi IS NOT NULL AND doi <> '';
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_papers_sha256
|
||||
ON papers(file_sha256) WHERE file_sha256 IS NOT NULL AND file_sha256 <> '';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_papers_title_normalized ON papers(title_normalized);
|
||||
CREATE INDEX IF NOT EXISTS idx_papers_year ON papers(year);
|
||||
CREATE INDEX IF NOT EXISTS idx_papers_relevance ON papers(relevance);
|
||||
CREATE INDEX IF NOT EXISTS idx_papers_priority ON papers(priority);
|
||||
CREATE INDEX IF NOT EXISTS idx_papers_category_id ON papers(category_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_authors_name_normalized ON authors(name_normalized);
|
||||
CREATE INDEX IF NOT EXISTS idx_keywords_name_normalized ON keywords(name_normalized);
|
||||
"""
|
||||
|
||||
URL_COLUMNS = {
|
||||
"landing_url": "TEXT",
|
||||
"pdf_url": "TEXT",
|
||||
"url_source": "TEXT",
|
||||
"access_status": "TEXT",
|
||||
"url_checked_at": "TEXT",
|
||||
}
|
||||
|
||||
|
||||
def ensure_url_columns(con: sqlite3.Connection) -> None:
|
||||
columns = {
|
||||
row[1] for row in con.execute("PRAGMA table_info(papers)")
|
||||
}
|
||||
for name, column_type in URL_COLUMNS.items():
|
||||
if name not in columns:
|
||||
con.execute(
|
||||
f'ALTER TABLE papers ADD COLUMN "{name}" {column_type}'
|
||||
)
|
||||
|
||||
|
||||
def initialize(path: Path) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with sqlite3.connect(path) as con:
|
||||
con.executescript(SCHEMA)
|
||||
ensure_url_columns(con)
|
||||
con.executemany(
|
||||
"INSERT OR IGNORE INTO categories(name, description) VALUES (?, NULL)",
|
||||
[(name,) for name in CATEGORIES],
|
||||
)
|
||||
result = con.execute("PRAGMA integrity_check").fetchone()[0]
|
||||
if result != "ok":
|
||||
raise RuntimeError(result)
|
||||
|
||||
def main() -> int:
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument(
|
||||
"database", nargs="?", default=str(CONFIG.path.database)
|
||||
)
|
||||
args = p.parse_args()
|
||||
initialize(Path(args.database))
|
||||
print(f"Initialized: {args.database}")
|
||||
print("integrity_check: ok")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user