# -*- coding: utf-8 -*- """Parsers de documentos (item 1: planilha, documentos, PDF) com degradação graciosa: dependências opcionais (pypdf, openpyxl) e saída de texto limpa. Todas as funções retornam str (vazio quando não suportado) e nunca lançam exceção para entrada malformada — violações de contrato são reportadas pelo chamador quando o texto vazio viola expectativa. """ from __future__ import annotations import csv import io import os def texto_de_pdf(caminho: str) -> str: try: from pypdf import PdfReader except ImportError: return "" try: leitor = PdfReader(caminho) return "\n".join((p.extract_text() or "") for p in leitor.pages).strip() except Exception: return "" def texto_de_planilha(caminho: str) -> str: """CSV/XLSX → texto linear por linhas (células separadas por ' | ').""" ext = os.path.splitext(caminho)[1].lower() try: if ext == ".csv": with open(caminho, "r", encoding="utf-8", errors="replace", newline="") as f: return "\n".join(" | ".join(linha) for linha in csv.reader(f)).strip() if ext == ".xlsx": try: from openpyxl import load_workbook except ImportError: return "" wb = load_workbook(caminho, read_only=True, data_only=True) linhas = [] for ws in wb.worksheets: for row in ws.iter_rows(values_only=True): if row: linhas.append(" | ".join("" if c is None else str(c) for c in row)) return "\n".join(linhas).strip() except Exception: return "" return "" def texto_de_docx(caminho: str) -> str: try: from docx import Document # python-docx doc = Document(caminho) return "\n".join(p.text for p in doc.paragraphs if p.text.strip()).strip() except ImportError: return "" except Exception: return "" def texto_de_arquivo(caminho: str) -> str: ext = os.path.splitext(caminho)[1].lower() if ext == ".pdf": return texto_de_pdf(caminho) if ext in (".csv", ".xlsx", ".xls", ".tsv"): return texto_de_planilha(caminho) if ext == ".docx": return texto_de_docx(caminho) try: with open(caminho, "r", encoding="utf-8", errors="replace") as f: return f.read().strip() except Exception: return ""