Spaces:
Sleeping
Sleeping
| import csv | |
| from fpdf import FPDF | |
| import tempfile | |
| import os | |
| def _safe_text_for_pdf(s: object) -> str: | |
| """ | |
| Ensure text passed to FPDF only contains characters encodable in latin-1. | |
| FPDF (classic) writes using latin-1 encoding by default and will raise | |
| an error when encountering characters outside that range (e.g. •). | |
| This helper replaces unsupported characters with a best-effort replacement | |
| so PDF generation doesn't fail. | |
| """ | |
| if s is None: | |
| return "" | |
| try: | |
| text = str(s) | |
| except Exception: | |
| text = "" | |
| # encode -> decode using latin-1 with replacement to avoid exceptions | |
| return text.encode('latin-1', 'replace').decode('latin-1') | |
| def export_evidence_pack_pdf(data, filename=None): | |
| """ | |
| Export evidence pack as PDF. Data should include clause, summary, checklist, scenario, metadata. | |
| Returns path to PDF file. | |
| """ | |
| pdf = FPDF() | |
| pdf.add_page() | |
| pdf.set_font("Arial", "B", size=16) | |
| pdf.cell(200, 10, txt="Evidence Pack", ln=True, align='C') | |
| pdf.ln(10) | |
| # Clause section | |
| pdf.set_font("Arial", "B", size=12) | |
| pdf.cell(0, 8, txt="Clause:", ln=True) | |
| pdf.set_font("Arial", size=10) | |
| clause_text = data.get('clause_text', 'No clause information available') | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(clause_text)) | |
| pdf.ln(3) | |
| # Summary section | |
| pdf.set_font("Arial", "B", size=12) | |
| pdf.cell(0, 8, txt="Summary:", ln=True) | |
| pdf.set_font("Arial", size=10) | |
| summary_text = data.get('summary', 'No summary available') | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(summary_text)) | |
| pdf.ln(3) | |
| # Checklist section | |
| pdf.set_font("Arial", "B", size=12) | |
| pdf.cell(0, 8, txt="Checklist:", ln=True) | |
| pdf.set_font("Arial", size=10) | |
| checklist = data.get('role_checklist', []) | |
| if checklist: | |
| for item in checklist: | |
| # use a simple hyphen bullet and sanitize text | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(f"- {item}")) | |
| else: | |
| pdf.multi_cell(0, 6, "No checklist items available") | |
| pdf.ln(3) | |
| # Source information section | |
| pdf.set_font("Arial", "B", size=12) | |
| pdf.cell(0, 8, txt="Source Information:", ln=True) | |
| pdf.set_font("Arial", size=10) | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(f"Source: {data.get('source_title', 'Not specified')}")) | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(f"Clause ID: {data.get('clause_id', 'Not assigned')}")) | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(f"Date: {data.get('date', 'Not specified')}")) | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(f"URL: {data.get('url', 'Not available')}")) | |
| # User role and context information | |
| if data.get('user_role'): | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(f"User Role: {data.get('user_role', '').title()}")) | |
| if data.get('language_preference'): | |
| pdf.multi_cell(0, 6, _safe_text_for_pdf(f"Language: {data.get('language_preference', '').title()}")) | |
| pdf.ln(5) | |
| scenario = data.get('scenario_analysis',{}) | |
| if scenario: | |
| pdf.multi_cell(0, 8, _safe_text_for_pdf(f"Scenario Analysis:")) | |
| pdf.multi_cell(0, 8, _safe_text_for_pdf(f"Yearly Results: {scenario.get('yearly_results','')}")) | |
| pdf.multi_cell(0, 8, _safe_text_for_pdf(f"Cumulative Base: {scenario.get('cumulative_base','')}")) | |
| pdf.multi_cell(0, 8, _safe_text_for_pdf(f"Cumulative Scenario: {scenario.get('cumulative_scenario','')}")) | |
| pdf.multi_cell(0, 8, _safe_text_for_pdf(f"Optimistic: {scenario.get('optimistic','')}")) | |
| pdf.multi_cell(0, 8, _safe_text_for_pdf(f"Pessimistic: {scenario.get('pessimistic','')}")) | |
| pdf.multi_cell(0, 8, _safe_text_for_pdf(f"Driver Breakdown: {scenario.get('driver_breakdown','')}")) | |
| if not filename: | |
| filename = os.path.join(tempfile.gettempdir(), f"evidence_pack_{os.getpid()}.pdf") | |
| pdf.output(filename) | |
| return filename | |
| def export_evidence_pack_csv(data, filename=None): | |
| """ | |
| Export evidence pack as CSV. Data should include clause, summary, checklist, scenario, metadata. | |
| Returns path to CSV file. | |
| """ | |
| if not filename: | |
| filename = os.path.join(tempfile.gettempdir(), f"evidence_pack_{os.getpid()}.csv") | |
| with open(filename, 'w', newline='', encoding='utf-8') as csvfile: | |
| writer = csv.writer(csvfile) | |
| writer.writerow(["Field", "Value"]) | |
| writer.writerow(["Clause", data.get('clause_text', 'No clause information available')]) | |
| writer.writerow(["Summary", data.get('summary', 'No summary available')]) | |
| # Handle checklist properly | |
| checklist = data.get('role_checklist', []) | |
| if checklist: | |
| checklist_text = '; '.join(checklist) | |
| else: | |
| checklist_text = "No checklist items available" | |
| writer.writerow(["Checklist", checklist_text]) | |
| writer.writerow(["Source", data.get('source_title', 'Not specified')]) | |
| writer.writerow(["Clause ID", data.get('clause_id', 'Not assigned')]) | |
| writer.writerow(["Date", data.get('date', 'Not specified')]) | |
| writer.writerow(["URL", data.get('url', 'Not available')]) | |
| # Add timestamp if available | |
| if 'timestamp' in data: | |
| writer.writerow(["Generated At", data.get('timestamp', '')]) | |
| # Add original query if available | |
| if 'original_query' in data: | |
| writer.writerow(["Original Query", data.get('original_query', '')]) | |
| # Add user context information | |
| if 'user_role' in data: | |
| writer.writerow(["User Role", data.get('user_role', '').title()]) | |
| if 'language_preference' in data: | |
| writer.writerow(["Language Preference", data.get('language_preference', '').title()]) | |
| scenario = data.get('scenario_analysis', {}) | |
| if scenario: | |
| writer.writerow(["=== SCENARIO ANALYSIS ===", ""]) | |
| writer.writerow(["Yearly Results", scenario.get('yearly_results', '')]) | |
| writer.writerow(["Cumulative Base", scenario.get('cumulative_base', '')]) | |
| writer.writerow(["Cumulative Scenario", scenario.get('cumulative_scenario', '')]) | |
| writer.writerow(["Optimistic", scenario.get('optimistic', '')]) | |
| writer.writerow(["Pessimistic", scenario.get('pessimistic', '')]) | |
| writer.writerow(["Driver Breakdown", scenario.get('driver_breakdown', '')]) | |
| return filename | |