"""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)