162 lines
4.7 KiB
Python
Executable File
162 lines
4.7 KiB
Python
Executable File
#!/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())
|