CoolFace
Apppublic

Mehramd/multi-file-rag-chat

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
utils.py168 linesDownload Raw Back to root
1from pathlib import Path
2import xml.etree.ElementTree as ET
3import pandas as pd
4
5from pypdf import PdfReader
6from docx import Document as DoxDocument
7
8from langchain_core.documents import Document
9
10# OCR fallback deps
11from pdf2image import convert_from_path
12import pytesseract
13
14DATA_DIR = Path("data")
15
16SUPPORTED_EXTENSIONS = {
17    ".pdf",
18    ".docx",
19    ".xml",
20    ".txt",
21    ".md",
22    ".csv",
23    ".xlsx",
24    ".xls",
25}
26
27def read_pdf(file_path):
28    """
29    Extracts text per page. If a page has no native text layer (scanned /
30    image-only / diagram-heavy page), falls back to OCR via pytesseract.
31    """
32    reader = PdfReader(Path(file_path))
33    documents = []
34    ocr_pages = []
35
36    for page_num, page in enumerate(reader.pages, start=1):
37        text = page.extract_text()
38        used_ocr = False
39
40        if not text or not text.strip():
41            # Native extraction found nothing -> try OCR on this page only
42            try:
43                images = convert_from_path(
44                    str(file_path),
45                    dpi=300,
46                    first_page=page_num,
47                    last_page=page_num,
48                )
49                if images:
50                    text = pytesseract.image_to_string(images[0])
51                    used_ocr = True
52            except Exception as e:
53                print(f"OCR failed on page {page_num} of {file_path.name}: {e}")
54                text = ""
55
56        if text and text.strip():
57            if used_ocr:
58                ocr_pages.append(page_num)
59            documents.append(
60                Document(
61                    page_content=text,
62                    metadata={
63                        "source": file_path.name,
64                        "page": page_num,
65                        "type": "pdf",
66                        "ocr": used_ocr,
67                    }
68                )
69            )
70
71    if ocr_pages:
72        print(f"ℹ️ {file_path.name}: used OCR fallback on pages {ocr_pages} (no native text layer found).")
73
74    return documents
75
76def read_docx(file_path):
77    reader = DoxDocument(Path(file_path))
78    parts = []
79    for paragraph in reader.paragraphs:
80        if paragraph.text.strip():
81            parts.append(paragraph.text.strip())
82
83    for table in reader.tables:
84        for row in table.rows:
85            row_text = "|".join(cell.text.strip() for cell in row.cells)
86            if row_text.strip():
87                parts.append(row_text)
88    return "\n".join(parts)
89
90def read_xml(file_path):
91    try:
92        tree = ET.parse(file_path)
93        root = tree.getroot()
94
95        text_parts = []
96
97        for element in root.iter():
98            if element.text and element.text.strip():
99                text_parts.append(element.text.strip())
100
101        return "\n".join(text_parts)
102
103    except Exception:
104        return Path(file_path).read_text(
105            encoding="utf-8",
106            errors="ignore"
107        )
108
109def read_txt_or_md(file_path):
110    return Path(file_path).read_text(
111        encoding="utf-8",
112        errors="ignore"
113    )
114
115
116def dataframe_to_text(df, source_name):
117    return (
118        f"Source: {source_name}\n\n"
119        + df.fillna("").to_string(index=False)
120    )
121
122def read_csv(file_path):
123    df = pd.read_csv(file_path)
124    return [Document(page_content=dataframe_to_text(df,file_path.name), metadata={"source": file_path.name, "type": "csv", "rows": len(df), "columns":len(df.columns)})]
125
126def read_excel(file_path):
127    docs = []
128    sheets = pd.read_excel(file_path, sheet_name = None)
129    for sheet_name, df in sheets.items():
130        docs.append(Document(page_content=dataframe_to_text(df,file_path.name), metadata={"source": file_path.name, "type": "excel", "rows": len(df), "columns":len(df.columns)}))
131    return docs
132
133def load_single_file(file_path):
134    path = Path(file_path)
135    suffix = path.suffix.lower()
136    if suffix == ".pdf":
137        return read_pdf(path)
138    if suffix == ".docx":
139        return [Document(page_content= read_docx(path), metadata = {"source":file_path.name,"type":"docx"})]
140    if suffix == ".xml":
141        return [Document(page_content= read_xml(path), metadata = {"source":file_path.name,"type":"xml"})]
142    if suffix in {".txt",".md"}:
143        return [Document(page_content= read_txt_or_md(path), metadata = {"source":file_path.name,"type":suffix.replace(".","")})]
144    if suffix == ".csv":
145        return read_csv(path)
146    if suffix == ".xlsx" or suffix == ".xls":
147        return read_excel(path)
148    return []
149
150def load_document_from_directory(directory= DATA_DIR):
151    documents = []
152    for file_path in directory.iterdir():
153        if file_path.suffix.lower() in SUPPORTED_EXTENSIONS:
154            documents.extend(load_single_file(file_path))
155    return documents
156
157def load_documents_from_paths(file_paths):
158    documents = []
159
160    for fp in file_paths:
161        try:
162            documents.extend(
163                load_single_file(Path(fp))
164            )
165        except Exception as e:
166            print(f"Failed to load {fp}: {e}")
167
168    return documents