Spaces:
Sleeping
Sleeping
| # core/file_builder.py | |
| from pathlib import Path | |
| from io import BytesIO | |
| from datetime import datetime | |
| from docx import Document | |
| from docx.shared import Inches, Pt, RGBColor | |
| from docx.enum.text import WD_ALIGN_PARAGRAPH | |
| import openpyxl | |
| from openpyxl.styles import Font, PatternFill, Alignment, Border, Side | |
| DOCS_DIR = Path("data/docs") | |
| DOCS_DIR.mkdir(parents=True, exist_ok=True) | |
| def build_docx(title: str, content: str, images: list[bytes] = None) -> Path | None: | |
| doc = Document() | |
| # Configuración básica | |
| section = doc.sections[0] | |
| section.top_margin = section.bottom_margin = Inches(1) | |
| section.left_margin = section.right_margin = Inches(1.2) | |
| # Título | |
| p = doc.add_heading(title, 0) | |
| p.alignment = WD_ALIGN_PARAGRAPH.CENTER | |
| if p.runs: | |
| run = p.runs[0] | |
| run.font.color.rgb = RGBColor(0x1a, 0x56, 0xdb) | |
| # Fecha | |
| doc.add_paragraph(f"Generado por Mission Control AI — {datetime.now().strftime('%B %d, %Y')}", style='Italic') | |
| doc.add_paragraph() | |
| # Procesar contenido markdown-like | |
| lines = content.splitlines() | |
| current_p = None | |
| for line in lines: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| if line.startswith('## '): | |
| doc.add_heading(line[3:], level=1) | |
| elif line.startswith('### '): | |
| doc.add_heading(line[4:], level=2) | |
| elif line.startswith('- ') or line.startswith('* '): | |
| p = doc.add_paragraph(line[2:], style='List Bullet') | |
| else: | |
| if current_p is None: | |
| current_p = doc.add_paragraph() | |
| current_p.add_run(line + " ") | |
| # Insertar imágenes si existen | |
| if images: | |
| for img_bytes in images: | |
| try: | |
| doc.add_picture(BytesIO(img_bytes), width=Inches(5)) | |
| last_p = doc.paragraphs[-1] | |
| last_p.alignment = WD_ALIGN_PARAGRAPH.CENTER | |
| except: | |
| pass | |
| # Pie de página | |
| p = doc.add_paragraph("— Generado por Mission Control AI —") | |
| p.alignment = WD_ALIGN_PARAGRAPH.CENTER | |
| if p.runs: | |
| p.runs[0].italic = True | |
| p.runs[0].font.size = Pt(9) | |
| p.runs[0].font.color.rgb = RGBColor(0x6b, 0x72, 0x80) | |
| # Guardar | |
| safe_title = "".join(c for c in title if c.isalnum() or c in " -_")[:40] | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filename = DOCS_DIR / f"{safe_title}_{timestamp}.docx" | |
| doc.save(filename) | |
| return filename | |
| def build_excel(task: str, excel_data: dict) -> Path | None: | |
| wb = openpyxl.Workbook() | |
| ws = wb.active | |
| ws.title = excel_data.get("sheet_name", "Hoja1")[:31] | |
| title = excel_data.get("title", task[:50]) | |
| headers = excel_data.get("headers", []) | |
| rows = excel_data.get("sample_rows", []) | |
| # Título | |
| if title and headers: | |
| ws.merge_cells(f"A1:{chr(64 + len(headers))}1") | |
| cell = ws["A1"] | |
| cell.value = title | |
| cell.font = Font(bold=True, size=14, color="FFFFFF") | |
| cell.fill = PatternFill("solid", fgColor="1A56DB") | |
| cell.alignment = Alignment(horizontal="center", vertical="center") | |
| ws.row_dimensions[1].height = 30 | |
| # Headers | |
| for col, h in enumerate(headers, 1): | |
| cell = ws.cell(row=2, column=col, value=h) | |
| cell.font = Font(bold=True, color="FFFFFF") | |
| cell.fill = PatternFill("solid", fgColor="2563EB") | |
| cell.alignment = Alignment(horizontal="center", vertical="center") | |
| # Datos | |
| for r_idx, row in enumerate(rows, 3): | |
| for c_idx, val in enumerate(row, 1): | |
| ws.cell(row=r_idx, column=c_idx, value=val) | |
| safe = "".join(c for c in task if c.isalnum() or c in (' ', '-'))[:30] | |
| ts = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| path = DOCS_DIR / f"{safe}_{ts}.xlsx" | |
| wb.save(path) | |
| return path |