refactor: services/ → compare/ + drhider/ (разделение Сверки и Хайдера)
Deploy contracts-flask / validate (push) Successful in 0s
Deploy contracts-flask / validate (push) Successful in 0s
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""Parse service — PDF (pdfplumber), DOCX (python-docx), plain text fallback."""
|
||||
import io
|
||||
|
||||
|
||||
def parse_file(filename, data):
|
||||
"""Parse file bytes → {status, elements, element_count}."""
|
||||
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
|
||||
try:
|
||||
if ext == "pdf":
|
||||
return _parse_pdf(data)
|
||||
elif ext == "docx":
|
||||
return _parse_docx(data)
|
||||
elif ext == "doc":
|
||||
return _parse_docx(data) # python-docx handles most .doc
|
||||
else:
|
||||
return _parse_text(data)
|
||||
except Exception as e:
|
||||
return {"status": "error", "error": str(e), "element_count": 0}
|
||||
|
||||
|
||||
def _parse_pdf(data):
|
||||
"""Parse PDF with pdfplumber — preserves table structure (unlike PyPDF2)."""
|
||||
import pdfplumber
|
||||
elements = []
|
||||
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}
|
||||
|
||||
|
||||
def _parse_docx(data):
|
||||
import docx
|
||||
doc = docx.Document(io.BytesIO(data))
|
||||
elements = []
|
||||
|
||||
for block in doc.element.body:
|
||||
tag = block.tag.split("}")[-1] if "}" in block.tag else block.tag
|
||||
if tag == "p":
|
||||
text = _extract_paragraph_text(block)
|
||||
if text:
|
||||
elements.append({"type": "paragraph", "text": text, "style": ""})
|
||||
elif tag == "tbl":
|
||||
rows = []
|
||||
for tr in block.findall(".//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}tr"):
|
||||
cells = []
|
||||
for tc in tr.findall(".//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}tc"):
|
||||
cell_text = "".join(t.text or "" for t in tc.findall(".//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}t"))
|
||||
cells.append(cell_text.strip())
|
||||
if cells:
|
||||
rows.append(cells)
|
||||
if rows:
|
||||
elements.append({"type": "table", "rows": rows})
|
||||
|
||||
return {"status": "parsed", "element_count": len(elements), "elements": elements}
|
||||
|
||||
|
||||
def _extract_paragraph_text(p_elem):
|
||||
texts = []
|
||||
for t in p_elem.findall(".//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}t"):
|
||||
if t.text:
|
||||
texts.append(t.text)
|
||||
return "".join(texts).strip()
|
||||
|
||||
|
||||
def _parse_text(data):
|
||||
text = data.decode("utf-8", errors="ignore")[:5000]
|
||||
lines = [l.strip() for l in text.split("\n") if l.strip()]
|
||||
return {"status": "parsed", "element_count": len(lines),
|
||||
"elements": [{"type": "paragraph", "text": l, "style": ""} for l in lines]}
|
||||
Reference in New Issue
Block a user