Spaces:
Sleeping
Sleeping
| """ | |
| Document Conversion Module | |
| Converts various document formats (PDF, DOCX, TXT) to markdown format | |
| """ | |
| import fitz # PyMuPDF | |
| from pathlib import Path | |
| from typing import Optional | |
| import logging | |
| from docx import Document | |
| from config import ( | |
| DOCUMENTS_DIR, | |
| PROCESSED_DOCS_DIR, | |
| SUPPORTED_FORMATS, | |
| ) | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| def convert_pdf_to_markdown(pdf_path: Path) -> str: | |
| """ | |
| Convert PDF file to markdown format using PyMuPDF | |
| Args: | |
| pdf_path: Path to the PDF file | |
| Returns: | |
| Markdown formatted text | |
| """ | |
| try: | |
| doc = fitz.open(pdf_path) | |
| markdown_content = [] | |
| for page_num, page in enumerate(doc, 1): | |
| # Extract text from page | |
| text = page.get_text() | |
| # Add page header | |
| markdown_content.append(f"\n## Page {page_num}\n") | |
| markdown_content.append(text) | |
| doc.close() | |
| return "\n".join(markdown_content) | |
| except Exception as e: | |
| logger.error(f"Error converting PDF {pdf_path}: {e}") | |
| raise | |
| def convert_docx_to_markdown(docx_path: Path) -> str: | |
| """ | |
| Convert DOCX file to markdown format | |
| Args: | |
| docx_path: Path to the DOCX file | |
| Returns: | |
| Markdown formatted text | |
| """ | |
| try: | |
| doc = Document(docx_path) | |
| markdown_content = [] | |
| for para in doc.paragraphs: | |
| text = para.text.strip() | |
| if not text: | |
| continue | |
| # Determine heading level based on style | |
| if para.style.name.startswith('Heading'): | |
| level = para.style.name.replace('Heading ', '') | |
| if level.isdigit(): | |
| markdown_content.append(f"\n{'#' * int(level)} {text}\n") | |
| else: | |
| markdown_content.append(f"\n## {text}\n") | |
| else: | |
| markdown_content.append(text) | |
| return "\n".join(markdown_content) | |
| except Exception as e: | |
| logger.error(f"Error converting DOCX {docx_path}: {e}") | |
| raise | |
| def convert_txt_to_markdown(txt_path: Path) -> str: | |
| """ | |
| Read plain text file (already in markdown or plain text format) | |
| Args: | |
| txt_path: Path to the text file | |
| Returns: | |
| File content as string | |
| """ | |
| try: | |
| with open(txt_path, 'r', encoding='utf-8') as f: | |
| return f.read() | |
| except UnicodeDecodeError: | |
| # Try with different encoding | |
| with open(txt_path, 'r', encoding='latin-1') as f: | |
| return f.read() | |
| except Exception as e: | |
| logger.error(f"Error reading text file {txt_path}: {e}") | |
| raise | |
| def convert_document_to_markdown(file_path: Path) -> Optional[str]: | |
| """ | |
| Convert a document to markdown format based on its extension | |
| Args: | |
| file_path: Path to the document | |
| Returns: | |
| Markdown formatted text or None if conversion fails | |
| """ | |
| suffix = file_path.suffix.lower() | |
| if suffix not in SUPPORTED_FORMATS: | |
| logger.warning(f"Unsupported format: {suffix}") | |
| return None | |
| try: | |
| if suffix == '.pdf': | |
| return convert_pdf_to_markdown(file_path) | |
| elif suffix == '.docx': | |
| return convert_docx_to_markdown(file_path) | |
| elif suffix in ['.txt', '.md']: | |
| return convert_txt_to_markdown(file_path) | |
| else: | |
| logger.warning(f"No converter available for {suffix}") | |
| return None | |
| except Exception as e: | |
| logger.error(f"Failed to convert {file_path}: {e}") | |
| return None | |
| def convert_all_documents() -> dict[str, Path]: | |
| """ | |
| Convert all documents in the documents directory to markdown | |
| Returns: | |
| Dictionary mapping original filenames to converted file paths | |
| """ | |
| converted_files = {} | |
| if not DOCUMENTS_DIR.exists(): | |
| logger.error(f"Documents directory not found: {DOCUMENTS_DIR}") | |
| return converted_files | |
| # Find all supported documents | |
| for file_path in DOCUMENTS_DIR.iterdir(): | |
| if file_path.suffix.lower() not in SUPPORTED_FORMATS: | |
| continue | |
| if file_path.name.startswith('.'): | |
| continue | |
| logger.info(f"Converting {file_path.name}...") | |
| # Convert to markdown | |
| markdown_content = convert_document_to_markdown(file_path) | |
| if markdown_content is None: | |
| logger.warning(f"Skipping {file_path.name}") | |
| continue | |
| # Save converted content | |
| output_filename = file_path.stem + ".md" | |
| output_path = PROCESSED_DOCS_DIR / output_filename | |
| try: | |
| with open(output_path, 'w', encoding='utf-8') as f: | |
| f.write(markdown_content) | |
| converted_files[file_path.name] = output_path | |
| logger.info(f"Successfully converted {file_path.name} -> {output_filename}") | |
| except Exception as e: | |
| logger.error(f"Failed to save {output_filename}: {e}") | |
| logger.info(f"Converted {len(converted_files)} documents") | |
| return converted_files | |
| def download_test_document() -> Optional[Path]: | |
| """ | |
| Download the test document (Think Python PDF) if not already present | |
| Returns: | |
| Path to the downloaded file or None if download fails | |
| """ | |
| import requests | |
| from config import TEST_DOCUMENT_URL, TEST_DOCUMENT_NAME | |
| output_path = DOCUMENTS_DIR / TEST_DOCUMENT_NAME | |
| if output_path.exists(): | |
| logger.info(f"Test document already exists: {TEST_DOCUMENT_NAME}") | |
| return output_path | |
| try: | |
| logger.info(f"Downloading test document from {TEST_DOCUMENT_URL}...") | |
| response = requests.get(TEST_DOCUMENT_URL, stream=True, timeout=30) | |
| response.raise_for_status() | |
| with open(output_path, 'wb') as f: | |
| for chunk in response.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| logger.info(f"Successfully downloaded {TEST_DOCUMENT_NAME}") | |
| return output_path | |
| except Exception as e: | |
| logger.error(f"Failed to download test document: {e}") | |
| return None | |
| if __name__ == "__main__": | |
| # Test the conversion functions | |
| logger.info("Testing document conversion...") | |
| # Download test document | |
| test_doc = download_test_document() | |
| if test_doc: | |
| # Convert all documents | |
| converted = convert_all_documents() | |
| logger.info(f"Conversion complete. Converted files: {list(converted.keys())}") | |
| else: | |
| logger.error("Failed to download test document") | |