Files
contracts-flask/site/routes/upload_bp.py
T

76 lines
3.1 KiB
Python

"""Upload-модуль: конвертация .doc→.docx + хранение/парсинг (sink для VM-буфера)."""
import base64
import hashlib
import httpx
from services.parse import parse_file
from db import documents
import config
def _convert(filename: str, data: bytes) -> bytes:
""".doc → .docx через внешний libreoffice-сервис. Возвращает docx-байты."""
try:
resp = httpx.post(
config.CONVERT_SERVICE_URL + "/convert",
files={"file": (filename, data, "application/msword")},
timeout=120,
)
except httpx.TimeoutException:
raise Exception("conversion timeout")
if resp.status_code != 200:
try:
err = resp.json().get("error", "conversion failed")
except Exception:
err = "conversion failed"
raise Exception(err)
return resp.content
def _store_and_parse(filename: str, data: bytes, batch_id, contract_id, zip_source=None, mime_type="application/octet-stream"):
"""Общая логика: дедуп → insert в БД → авто-парсинг. Возвращает dict-результат."""
content_hash = hashlib.sha256(data).hexdigest()[:16]
# Дедупликация по хешу
if batch_id:
existing = documents.get_by_hash(batch_id, content_hash)
if existing:
return {"ok": False, "error": "duplicate", "doc_id": existing["id"], "duplicate_of": True}
doc = documents.insert(
filename=filename,
mime_type=mime_type,
original_bytes=base64.b64encode(data).decode(),
batch_id=batch_id,
zip_source=zip_source,
content_hash=content_hash,
)
# Авто-парсинг
try:
result = parse_file(filename, data)
if result["status"] == "parsed":
documents.set_parsed(doc["id"], result["elements"])
parsed = {"status": "parsed", "element_count": result.get("element_count", 0)}
else:
documents.set_error(doc["id"], result.get("error", "parse failed"))
parsed = {"status": "error", "error": result.get("error", "parse failed")}
except Exception as e:
documents.set_error(doc["id"], str(e))
parsed = {"status": "error", "error": str(e)}
return {"ok": True, "doc_id": doc["id"], "contract_id": contract_id, "parsed": parsed}
def contracts_upload_sink(name, content, batch_id=None, contract_id=None, zip_source=None):
"""Sink для переиспользуемого модуля upload: вставить файл в documents + авто-парсинг.
Вызывается модулем create_upload_refs_blueprint(cfg, sink=...) на каждый
вытянутый с ВМ файл. .doc конвертируется в .docx через внешний LibreOffice
(парсер умеет только .docx). Возвращает dict ok/doc_id/contract_id/parsed.
"""
if name.lower().endswith(".doc"):
content = _convert(name, content)
name = name[:-4] + ".docx"
return _store_and_parse(name, content, batch_id, contract_id, zip_source)