import gradio as gr import pandas as pd import plotly.graph_objects as go from ui.multi_report_functions import ( load_multi_pdfs, run_coverage_scores, run_commitment_scores, run_positioning_chart, recalculate_from_revised_file, run_revised_positioning_chart ) def build_multi_report_page(): """ Builds the Multi-Report Comparative Analysis page. Returns back_btn so interface.py can wire the Back navigation. This page is placed inside a gr.Tab by build_interface() rather than toggled via Column visibility, to avoid Gradio's known rendering bug where complex children (Dataframe, Plot, Gallery) can break after a parent Column's visibility is toggled off and back on. """ back_btn = gr.Button("⬅ Back to Home", size="sm") gr.Markdown("# Multi-Report Comparative Analysis") gr.Markdown( "Upload multiple company sustainability reports to compare " "their ESG disclosure coverage and commitment levels." ) # ========================= # UPLOAD # ========================= gr.Markdown("## Step 1: Upload Reports") multi_file = gr.File( label="Upload PDF Reports (you can select multiple files)", file_count="multiple" ) load_multi_btn = gr.Button("Load Reports", variant="primary") upload_status = gr.Markdown() extracted_download = gr.File(label="Download Combined Extracted CSV") # ========================= # COVERAGE SCORES # ========================= gr.Markdown("## Step 2: Coverage Scores") gr.Markdown( "Measures how much of each report is sustainability-relevant: " "**Relevant ÷ (Relevant + Irrelevant) × 100**" ) coverage_btn = gr.Button("Run Coverage Scores", variant="primary") coverage_status = gr.Markdown() coverage_table = gr.Dataframe(value=pd.DataFrame(), label="Coverage Scores (ranked)", interactive=False) coverage_download = gr.File(label="Download Coverage Scores CSV (per-report summary)") coverage_detail_download = gr.File(label="Download Per-Paragraph Detail CSV") # ========================= # COMMITMENT SCORES # ========================= gr.Markdown("## Step 3: Commitment Scores") gr.Markdown( "Measures the depth of disclosure among relevant paragraphs " "(quantitative claims are weighted higher than qualitative ones). " "Requires Coverage Scores to be run first." ) commitment_btn = gr.Button("Run Commitment Scores", variant="primary") commitment_status = gr.Markdown() commitment_table = gr.Dataframe(value=pd.DataFrame(), label="Commitment Scores (ranked)", interactive=False) commitment_download = gr.File(label="Download Commitment Scores CSV (per-report summary)") commitment_detail_download = gr.File(label="Download Per-Paragraph Detail CSV (PM/FC retrieval + classification)") # ---------- Positioning Quadrant Chart ---------- gr.Markdown("### Positioning") gr.Markdown( "Plots each company by Commitment Score (x-axis) vs Coverage Score " "(y-axis), split into four quadrants at the average of each score " "across all uploaded reports." ) positioning_btn = gr.Button("Run Positioning", variant="primary") positioning_status = gr.Markdown() positioning_chart = gr.Plot(value=go.Figure(), label="Positioning") # ========================= # REVISE & RECALCULATE # ========================= gr.Markdown("## Step 4: Revise & Recalculate (Optional)") gr.Markdown( "If you reviewed the downloaded per-paragraph detail CSVs and " "corrected some predictions by hand, upload the revised file here " "to recompute Coverage Scores, Commitment Scores, and a new " "Positioning chart from it. The original results above are left " "untouched, so you can compare both side by side.\n\n" "Expected columns: `Doc_name`, `SA_label` (Relevant/Irrelevant), " "`QQ_label` (Qualitative/Quantitative), `PMFC_Label` " "(`Answer: PM` / `Answer: FC`). Rows with missing or unrecognized " "values in these columns are excluded from the relevant counts " "rather than causing an error." ) revised_file = gr.File(label="Upload Revised Per-Paragraph CSV") recalculate_btn = gr.Button("Recalculate Scores", variant="primary") recalculate_status = gr.Markdown() revised_coverage_table = gr.Dataframe(value=pd.DataFrame(), label="Revised Coverage Scores (ranked)", interactive=False) revised_commitment_table = gr.Dataframe(value=pd.DataFrame(), label="Revised Commitment Scores (ranked)", interactive=False) revised_positioning_btn = gr.Button("Run Revised Positioning", variant="primary") revised_positioning_status = gr.Markdown() revised_positioning_chart = gr.Plot(value=go.Figure(), label="Positioning (Revised)") # ========================= # CONNECTIONS # ========================= load_multi_btn.click( load_multi_pdfs, inputs=multi_file, outputs=[extracted_download, upload_status] ) coverage_btn.click( run_coverage_scores, outputs=[coverage_table, coverage_download, coverage_detail_download, coverage_status] ) commitment_btn.click( run_commitment_scores, outputs=[commitment_table, commitment_download, commitment_detail_download, commitment_status] ) positioning_btn.click( run_positioning_chart, outputs=[positioning_chart, positioning_status] ) recalculate_btn.click( recalculate_from_revised_file, inputs=revised_file, outputs=[revised_coverage_table, revised_commitment_table, recalculate_status] ) revised_positioning_btn.click( run_revised_positioning_chart, outputs=[revised_positioning_chart, revised_positioning_status] ) return back_btn