Files

157 lines
5.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""routes/main.py — Главные маршруты: загрузка файлов, просмотр договора."""
import datetime
from flask import render_template, request, jsonify
import db
import mimeutil
def _save_file_to_db(filename, file_bytes, mime, contract_id, conn=None):
"""
Сохранить загруженный файл в БД.
Создаёт запись в documents и привязывает к договору через supplements.
Если conn передан — используется одно соединение для всей транзакции.
Иначе — каждый запрос в своём соединении.
Возвращает doc_id или None при ошибке.
"""
if conn:
# Транзакционный режим: все операции в одном соединении
cur = conn.cursor()
cur.execute(
"INSERT INTO documents (filename, mime_type, original_bytes, status) VALUES (%s,%s,%s,'uploaded')",
(filename, mime, file_bytes),
)
cur.execute(
"SELECT id FROM documents WHERE filename=%s AND status='uploaded' ORDER BY created_at DESC LIMIT 1",
(filename,),
)
row = cur.fetchone()
doc_id = row[0] if row else None
if doc_id:
cur.execute(
"INSERT INTO supplements (contract_id, document_id, type) VALUES (%s,%s,'initial')",
(str(contract_id), str(doc_id)),
)
conn.commit()
cur.close()
return doc_id
# Автономный режим: каждый вызов db.execute сам управляет соединением
db.execute(
"INSERT INTO documents (filename, mime_type, original_bytes, status) VALUES (%s,%s,%s,'uploaded')",
(filename, mime, file_bytes),
)
doc, _ = db.query_one(
"SELECT id FROM documents WHERE filename=%s AND status='uploaded' ORDER BY created_at DESC LIMIT 1",
(filename,),
)
doc_id = doc["id"] if doc else None
if doc_id:
db.execute(
"INSERT INTO supplements (contract_id, document_id, type) VALUES (%s,%s,'initial')",
(str(contract_id), str(doc_id)),
)
return doc_id
def index():
"""
GET / — страница загрузки
GET /?id=<cid> — просмотр договора
POST / — приём файлов (multipart/form-data)
"""
if request.method == "POST":
return _upload_files()
cid = request.args.get("id")
if cid:
return _show_contract(cid)
return render_template("upload.html")
def _upload_files():
"""
Принять файлы через multipart/form-data.
Поле формы: 'files' (один или несколько).
?cid=X — добавить файлы к существующему договору.
Если cid не указан — создаётся новый договор.
"""
files = request.files.getlist("files")
if not files or not any(f.filename for f in files):
return jsonify({"error": "Нет файлов"}), 400
cid = request.args.get("cid") or request.form.get("cid")
if cid:
# Добавляем к существующему договору
contract_id = cid
else:
# Создаём новый договор
conn, err = db.connect()
if err:
return jsonify({"error": f"БД: {err}"}), 500
cur = conn.cursor()
cur.execute(
"INSERT INTO contracts (number, client) VALUES (%s, %s) RETURNING id",
("б/н " + datetime.datetime.now().strftime("%Y%m%d-%H%M"), ""),
)
contract_id = cur.fetchone()[0]
conn.commit()
cur.close()
db.put_conn(conn)
# Сохраняем каждый файл
for f in files:
if not f.filename:
continue
filename = f.filename
mime = mimeutil.guess_mime(filename) or f.content_type or "application/octet-stream"
_save_file_to_db(filename, f.read(), mime, str(contract_id))
return jsonify({"contract_id": str(contract_id)})
def _show_contract(cid):
"""
Показать страницу договора со списком файлов и кнопкой «Парсинг».
Загружает: информацию о договоре, список допников/файлов,
существующие строки спецификации (если уже извлечены).
"""
contract, _ = db.query_one(
"SELECT id, number, created_at FROM contracts WHERE id = %s",
(cid,),
)
if not contract:
return render_template("upload.html", error="Договор не найден")
# Все файлы, привязанные к договору
supps, _ = db.query(
"SELECT s.id, s.created_at, d.filename, d.status, d.parsed_text IS NOT NULL as has_text "
"FROM supplements s JOIN documents d ON s.document_id = d.id "
"WHERE s.contract_id = %s ORDER BY s.created_at",
(cid,),
)
supp_list = [dict(zip(supps["columns"], r)) for r in supps["rows"]] if supps else []
# Уже извлечённые строки спецификации
rows_res, _ = db.query(
"SELECT sr.row_num, sr.name, sr.price, sr.qty, sr.sum, sr.date_start "
"FROM spec_rows sr JOIN supplements s ON sr.supplement_id = s.id "
"WHERE s.contract_id = %s ORDER BY sr.row_num",
(cid,),
)
all_rows = [dict(zip(rows_res["columns"], r)) for r in rows_res["rows"]] if rows_res else []
# Файлы, готовые к парсингу
unprocessed = [s for s in supp_list if s["status"] in ("uploaded", "parsed")]
return render_template("upload.html",
contract=contract,
supplements=supp_list,
all_rows=all_rows,
unprocessed=unprocessed,
)