diff --git a/deploy/services/parse.py b/deploy/services/parse.py index 0576809..20cc37e 100644 --- a/deploy/services/parse.py +++ b/deploy/services/parse.py @@ -1,6 +1,5 @@ -"""Parse service — PDF (PyPDF2), DOCX (python-docx), plain text fallback.""" +"""Parse service — PDF (pdfplumber), DOCX (python-docx), plain text fallback.""" import io -from PyPDF2 import PdfReader def parse_file(filename, data): @@ -20,15 +19,30 @@ def parse_file(filename, data): def _parse_pdf(data): - reader = PdfReader(io.BytesIO(data)) + """Parse PDF with pdfplumber — preserves table structure (unlike PyPDF2).""" + import pdfplumber elements = [] - for page in reader.pages: - text = page.extract_text() - if text: - for line in text.split("\n"): - line = line.strip() - if line: - elements.append({"type": "paragraph", "text": line, "style": ""}) + with pdfplumber.open(io.BytesIO(data)) as pdf: + for page in pdf.pages: + # Tables first — preserves column structure critical for specs + tables = page.extract_tables() + for table in tables: + if table: + rows = [] + for row in table: + if row: + cells = [str(cell or "").strip() for cell in row] + if any(cells): + rows.append(cells) + if rows: + elements.append({"type": "table", "rows": rows}) + # Remaining text as paragraphs + text = page.extract_text() + if text: + for line in text.split("\n"): + line = line.strip() + if line: + elements.append({"type": "paragraph", "text": line, "style": ""}) return {"status": "parsed", "element_count": len(elements), "elements": elements}