DDL (CREATE TABLE) не возвращает строк — db.query() делал fetchall() и ломался. db.execute() делает commit без fetch — правильно для DDL.
125 lines
6.8 KiB
Python
125 lines
6.8 KiB
Python
"""
|
|
schema.py — Схема базы данных (DDL).
|
|
|
|
Создаёт все таблицы при старте приложения.
|
|
Вызывается ОДИН раз из app.py: schema.ensure_schema().
|
|
|
|
Таблицы:
|
|
documents — исходные файлы + распарсенный текст
|
|
contracts — договоры (клиент, номер, дата)
|
|
supplements — допники (привязка к договору)
|
|
spec_rows — строки спецификаций (после LLM-извлечения)
|
|
spec_history — история изменений строк между допниками
|
|
"""
|
|
|
|
import db
|
|
|
|
# ── Таблица documents ──────────────────────────────────────────
|
|
# Хранит исходный файл и результат парсинга (текст).
|
|
# status отслеживает прогресс: uploaded → parsed → extracted → error
|
|
|
|
DDL_DOCUMENTS = """
|
|
CREATE TABLE IF NOT EXISTS documents (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
filename TEXT NOT NULL, -- оригинальное имя файла
|
|
mime_type TEXT NOT NULL, -- MIME-тип (docx/pdf/zip)
|
|
original_bytes BYTEA NOT NULL, -- исходный файл как есть
|
|
parsed_text TEXT, -- текст после parser.py + textify.py
|
|
status TEXT DEFAULT 'uploaded', -- uploaded | parsed | extracted | error
|
|
error_message TEXT, -- текст ошибки, если status=error
|
|
created_at TIMESTAMPTZ DEFAULT now() -- когда загружен
|
|
);
|
|
"""
|
|
|
|
# ── Таблица contracts ──────────────────────────────────────────
|
|
# Один договор = одна запись. Может иметь цепочку допников.
|
|
|
|
DDL_CONTRACTS = """
|
|
CREATE TABLE IF NOT EXISTS contracts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
number TEXT NOT NULL, -- номер договора (например "03700_1")
|
|
client TEXT, -- клиент (ЗАО "XXX001")
|
|
date_signed DATE, -- дата подписания
|
|
status TEXT DEFAULT 'active', -- active | closed
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
"""
|
|
|
|
# ── Таблица supplements ────────────────────────────────────────
|
|
# Каждый допник (включая изначальный договор) — отдельная запись.
|
|
# type: initial (начальная спецификация) | amendment (изменение) | termination
|
|
|
|
DDL_SUPPLEMENTS = """
|
|
CREATE TABLE IF NOT EXISTS supplements (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
contract_id UUID REFERENCES contracts(id), -- к какому договору
|
|
number TEXT, -- номер допника
|
|
date_signed DATE, -- дата подписания
|
|
type TEXT DEFAULT 'initial', -- initial | amendment | termination
|
|
document_id UUID REFERENCES documents(id), -- исходный файл
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
"""
|
|
|
|
# ── Таблица spec_rows ──────────────────────────────────────────
|
|
# Строки спецификации после LLM-извлечения.
|
|
# Одна строка = одна услуга в спецификации.
|
|
|
|
DDL_SPEC_ROWS = """
|
|
CREATE TABLE IF NOT EXISTS spec_rows (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
supplement_id UUID REFERENCES supplements(id), -- из какого допника
|
|
row_num INTEGER, -- № п/п (порядковый номер в таблице)
|
|
name TEXT, -- наименование услуги
|
|
price NUMERIC, -- цена за единицу
|
|
qty NUMERIC, -- объём (количество)
|
|
sum NUMERIC, -- сумма (price * qty)
|
|
date_start DATE, -- дата начала оказания
|
|
date_end DATE, -- дата окончания (может быть NULL)
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
"""
|
|
|
|
# ── Таблица spec_history ───────────────────────────────────────
|
|
# История изменений: какая строка когда и как поменялась.
|
|
# change_type: added (новая) | changed (изменена) | deleted (удалена) | unchanged
|
|
|
|
DDL_SPEC_HISTORY = """
|
|
CREATE TABLE IF NOT EXISTS spec_history (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
contract_id UUID REFERENCES contracts(id), -- для быстрых запросов
|
|
supplement_id UUID REFERENCES supplements(id), -- в каком допнике изменение
|
|
row_num INTEGER, -- № п/п строки
|
|
change_type TEXT NOT NULL, -- added | changed | deleted | unchanged
|
|
old_values JSONB, -- старые значения {price, qty, sum, ...}
|
|
new_values JSONB, -- новые значения {price, qty, sum, ...}
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
"""
|
|
|
|
# ── Список всех DDL ────────────────────────────────────────────
|
|
|
|
ALL_DDL = [
|
|
("documents", DDL_DOCUMENTS),
|
|
("contracts", DDL_CONTRACTS),
|
|
("supplements", DDL_SUPPLEMENTS),
|
|
("spec_rows", DDL_SPEC_ROWS),
|
|
("spec_history", DDL_SPEC_HISTORY),
|
|
]
|
|
|
|
|
|
def ensure_schema():
|
|
"""
|
|
Создаёт все таблицы, если их ещё нет.
|
|
Вызывается при старте приложения (app.py).
|
|
Безопасна для многократного вызова (IF NOT EXISTS).
|
|
|
|
Использует db.execute() — DDL не возвращает строк.
|
|
"""
|
|
for name, ddl in ALL_DDL:
|
|
rowcount, err = db.execute(ddl)
|
|
if err:
|
|
print(f"[schema] ⚠️ {name}: {err}")
|
|
else:
|
|
print(f"[schema] ✅ {name}")
|