diff --git a/requirements.txt b/requirements.txt index c2c9aa4..d3a0b37 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,6 @@ requests psycopg2-binary python-dotenv pdfplumber -camelot-py[cv] +tabula-py httpx h2 diff --git a/site/parser.py b/site/parser.py index f4892e2..08bc465 100644 --- a/site/parser.py +++ b/site/parser.py @@ -124,16 +124,24 @@ def _parse_doc(file_bytes: bytes) -> dict: def _parse_pdf(file_bytes: bytes) -> dict: result = {"elements": [], "errors": []} - # Попробовать libreoffice → docx (точные таблицы) + # Попробовать tabula (Java) — точнее таблицы + tables_from_tabula = False try: - docx_result = _pdf_via_libreoffice(file_bytes) - if docx_result["elements"]: - return docx_result - result["errors"].extend(docx_result.get("errors", [])) + import tabula + tables = tabula.read_pdf(io.BytesIO(file_bytes), pages="all", multiple_tables=True) + for tbl in tables: + rows = [list(tbl.columns)] + tbl.values.tolist() + result["elements"].append({ + "type": "table", + "rows": rows, + }) + tables_from_tabula = len(tables) > 0 + except ImportError: + result["errors"].append("tabula not installed, using pdfplumber") except Exception as e: - result["errors"].append(f"libreoffice pdf: {e}") + result["errors"].append(f"tabula error: {e}") - # Fallback: pdfplumber + # pdfplumber: текст + таблицы (если tabula не сработал) try: import pdfplumber with pdfplumber.open(io.BytesIO(file_bytes)) as pdf: @@ -150,55 +158,21 @@ def _parse_pdf(file_bytes: bytes) -> dict: "text": line, "page": pn, }) - tables = page.extract_tables() - for tbl in tables: - if tbl: - result["elements"].append({ - "type": "table", - "rows": tbl, - "page": pn, - }) + if not tables_from_tabula: + tables = page.extract_tables() + for tbl in tables: + if tbl: + result["elements"].append({ + "type": "table", + "rows": tbl, + "page": pn, + }) except Exception as e: result["errors"].append(f"pdfplumber: {e}") return result -def _pdf_via_libreoffice(file_bytes: bytes) -> dict: - """Конвертировать PDF → DOCX через libreoffice, затем python-docx.""" - result = {"elements": [], "errors": []} - tmp_pdf = None - tmp_dir = None - try: - with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tmp: - tmp.write(file_bytes) - tmp_pdf = tmp.name - - tmp_dir = tempfile.mkdtemp() - subprocess.run( - ["libreoffice", "--headless", "--convert-to", "docx", "--outdir", tmp_dir, tmp_pdf], - timeout=60, capture_output=True, - ) - docx_files = [f for f in os.listdir(tmp_dir) if f.endswith(".docx")] - if docx_files: - with open(os.path.join(tmp_dir, docx_files[0]), "rb") as f: - result = _parse_docx(f.read()) - else: - result["errors"].append("libreoffice PDF→DOCX: no output") - except FileNotFoundError: - result["errors"].append("libreoffice not installed") - except Exception as e: - result["errors"].append(f"libreoffice PDF→DOCX: {e}") - finally: - if tmp_pdf and os.path.exists(tmp_pdf): - os.unlink(tmp_pdf) - if tmp_dir and os.path.exists(tmp_dir): - for f in os.listdir(tmp_dir): - os.unlink(os.path.join(tmp_dir, f)) - os.rmdir(tmp_dir) - return result - - # ── ZIP ── def _parse_zip(file_bytes: bytes) -> dict: diff --git a/site/templates/upload.html b/site/templates/upload.html index 2552f4c..a2dc4ec 100644 --- a/site/templates/upload.html +++ b/site/templates/upload.html @@ -59,7 +59,7 @@
Nubes - Сверка договоров v1.29 + Сверка договоров v1.30