Spaces:
Paused
Paused
| from fpdf import FPDF | |
| from datetime import datetime | |
| import tempfile | |
| import os | |
| class EcoVerifyReport(FPDF): | |
| def header(self): | |
| self.set_fill_color(45, 122, 79) | |
| self.rect(0, 0, 210, 28, 'F') | |
| self.set_font("Helvetica", "B", 16) | |
| self.set_text_color(255, 255, 255) | |
| self.set_xy(0, 8) | |
| self.cell(0, 10, "EcoVerify AI - Official Site Report", align="C") | |
| self.set_font("Helvetica", "", 9) | |
| self.set_xy(0, 18) | |
| self.cell(0, 6, "AI-powered Waste Management Verification for ASEAN Ecotourism", align="C") | |
| self.set_text_color(0, 0, 0) | |
| self.ln(18) | |
| def footer(self): | |
| self.set_y(-15) | |
| self.set_font("Helvetica", "I", 8) | |
| self.set_text_color(128, 128, 128) | |
| self.cell(0, 10, f"Generated by EcoVerify AI | {datetime.now().strftime('%d %b %Y %H:%M')} | Page {self.page_no()}", align="C") | |
| def section_title(self, title): | |
| self.set_fill_color(240, 250, 244) | |
| self.set_draw_color(45, 122, 79) | |
| self.set_font("Helvetica", "B", 11) | |
| self.set_text_color(30, 80, 50) | |
| self.cell(0, 9, f" {title}", ln=True, fill=True, border="LB") | |
| self.set_text_color(0, 0, 0) | |
| self.ln(2) | |
| def key_value(self, key, value, color=None): | |
| self.set_font("Helvetica", "B", 10) | |
| self.cell(70, 7, f" {key}:", ln=False) | |
| self.set_font("Helvetica", "", 10) | |
| if color: | |
| self.set_text_color(*color) | |
| self.cell(0, 7, str(value), ln=True) | |
| self.set_text_color(0, 0, 0) | |
| def body_text(self, text): | |
| self.set_font("Helvetica", "", 10) | |
| self.multi_cell(0, 6, f" {text}") | |
| self.ln(1) | |
| def risk_bar(self, label, score): | |
| self.set_font("Helvetica", "", 9) | |
| self.cell(55, 6, f" {label}", ln=False) | |
| # Bar background | |
| bar_x = self.get_x() | |
| bar_y = self.get_y() + 1 | |
| self.set_fill_color(220, 220, 220) | |
| self.rect(bar_x, bar_y, 80, 4, 'F') | |
| # Bar fill | |
| filled = score * 80 | |
| if score > 0.6: | |
| self.set_fill_color(231, 76, 60) | |
| elif score > 0.3: | |
| self.set_fill_color(243, 156, 18) | |
| else: | |
| self.set_fill_color(46, 204, 113) | |
| self.rect(bar_x, bar_y, filled, 4, 'F') | |
| self.set_x(bar_x + 85) | |
| self.set_font("Helvetica", "B", 9) | |
| self.cell(20, 6, f"{score:.2f}", ln=True) | |
| def generate_pdf( | |
| site_name, | |
| site_type, | |
| site_size, | |
| visitor_count, | |
| nlp_risk, | |
| img_risk, | |
| combined_risk, | |
| nlp_output, | |
| img_output, | |
| rec_output, | |
| impact_output, | |
| warn_output, | |
| language="English" | |
| ) -> str: | |
| pdf = EcoVerifyReport() | |
| pdf.set_auto_page_break(auto=True, margin=20) | |
| pdf.add_page() | |
| # ββ SITE INFO βββββββββββββββββββββββββββββββββββββββββ | |
| pdf.section_title("Site Information") | |
| pdf.key_value("Site Name", site_name) | |
| pdf.key_value("Site Type", site_type) | |
| pdf.key_value("Site Size", site_size) | |
| pdf.key_value("Avg Visitors/day", str(int(visitor_count))) | |
| pdf.key_value("Report Date", datetime.now().strftime("%d %B %Y")) | |
| pdf.ln(4) | |
| # ββ RISK SUMMARY ββββββββββββββββββββββββββββββββββββββ | |
| pdf.section_title("Risk Summary") | |
| if combined_risk > 0.6: | |
| verdict = "HIGH RISK - Greenwashing Suspected" | |
| v_color = (231, 76, 60) | |
| elif combined_risk > 0.3: | |
| verdict = "MEDIUM RISK - Partial Inconsistency" | |
| v_color = (243, 156, 18) | |
| else: | |
| verdict = "LOW RISK - Claims Appear Consistent" | |
| v_color = (46, 204, 113) | |
| pdf.key_value("Overall Verdict", verdict, color=v_color) | |
| pdf.ln(2) | |
| pdf.risk_bar("NLP Risk Score", nlp_risk) | |
| pdf.risk_bar("Image Risk Score", img_risk) | |
| pdf.risk_bar("Combined Risk Score", combined_risk) | |
| pdf.ln(4) | |
| # ββ EARLY WARNING βββββββββββββββββββββββββββββββββββββ | |
| pdf.section_title("Early Warning Analysis") | |
| clean_warn = _clean_text(warn_output) | |
| pdf.body_text(clean_warn[:800]) | |
| pdf.ln(4) | |
| # ββ RECOMMENDATION ββββββββββββββββββββββββββββββββββββ | |
| pdf.section_title("Waste Management Recommendation") | |
| clean_rec = _clean_text(rec_output) | |
| pdf.body_text(clean_rec[:1000]) | |
| pdf.ln(4) | |
| # ββ IMPACT ββββββββββββββββββββββββββββββββββββββββββββ | |
| pdf.section_title("Environmental & Economic Impact") | |
| waste_total = visitor_count * 0.5 | |
| unmanaged = waste_total * combined_risk | |
| water_bod = round(unmanaged * 0.15, 2) | |
| co2 = round(unmanaged * 2.5, 2) if combined_risk > 0.5 else 0.0 | |
| bad_cost = round(unmanaged * 1.5, 1) | |
| good_cost = round(waste_total * 0.4, 1) | |
| daily_save = round(max(bad_cost - good_cost, 0), 1) | |
| annual_save = int(daily_save * 365) | |
| pdf.key_value("Unmanaged Waste", f"{unmanaged:.1f} kg/day") | |
| pdf.key_value("Water Pollution", f"{water_bod} kg BOD/day") | |
| pdf.key_value("CO2 Emission", f"{co2} kg/day") | |
| pdf.key_value("Current Cost", f"${bad_cost}/day") | |
| pdf.key_value("Optimized Cost", f"${good_cost}/day") | |
| pdf.key_value("Potential Savings", f"${annual_save:,}/year", color=(46, 204, 113)) | |
| pdf.ln(4) | |
| # ββ CERTIFICATION READINESS βββββββββββββββββββββββββββ | |
| pdf.section_title("ASEAN Certification Readiness") | |
| waste_ready = round((1 - combined_risk) * 100) | |
| water_ready = round((1 - combined_risk * 0.8) * 100) | |
| energy_ready = round((1 - combined_risk * 0.6) * 100) | |
| overall_ready = round((waste_ready + water_ready + energy_ready) / 3) | |
| months_needed = max(1, round((100 - overall_ready) / 8)) | |
| pdf.risk_bar("Waste Management", 1 - waste_ready/100) | |
| pdf.risk_bar("Wastewater", 1 - water_ready/100) | |
| pdf.risk_bar("Energy Efficiency", 1 - energy_ready/100) | |
| pdf.ln(2) | |
| pdf.key_value("Overall Readiness", f"{overall_ready}%") | |
| pdf.key_value("Est. Time to Certify", f"{months_needed} months") | |
| pdf.ln(6) | |
| # ββ FOOTER NOTE βββββββββββββββββββββββββββββββββββββββ | |
| pdf.set_font("Helvetica", "I", 8) | |
| pdf.set_text_color(128, 128, 128) | |
| pdf.multi_cell(0, 5, | |
| "This report is generated by EcoVerify AI for informational purposes. " | |
| "It supports post-certification monitoring and does not replace official inspection. " | |
| "Built for AI ASEAN Ready Competition." | |
| ) | |
| # ββ SAVE ββββββββββββββββββββββββββββββββββββββββββββββ | |
| tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf", | |
| prefix=f"EcoVerify_{site_name.replace(' ','_')}_") | |
| pdf.output(tmp.name) | |
| return tmp.name | |
| def _clean_text(text: str) -> str: | |
| """Strip markdown symbols cho PDF.""" | |
| import re | |
| text = re.sub(r"#{1,6}\s?", "", text) | |
| text = re.sub(r"\*{1,3}(.*?)\*{1,3}", r"\1", text) | |
| text = re.sub(r"\|.*?\|", "", text) | |
| text = re.sub(r"-{3,}", "", text) | |
| text = re.sub(r"\n{3,}", "\n\n", text) | |
| return text.strip() |