Spaces:
Paused
Paused
| """FDAM AI Pipeline - Fire Damage Assessment Methodology v4.0.1 | |
| Main Gradio application entry point with session state and tab validation. | |
| """ | |
| import gradio as gr | |
| from config.settings import settings | |
| from models.loader import get_models | |
| from ui.state import SessionState, create_new_session, session_to_json, session_from_json | |
| from ui.storage import get_head_html | |
| from ui.tabs import project, rooms, images, observations, results | |
| def create_app() -> gr.Blocks: | |
| """Create the main Gradio application.""" | |
| # Initialize models at startup | |
| model_stack = get_models() | |
| # Note: head parameter moved to launch() in Gradio 6.0 | |
| # localStorage JS will be injected there | |
| with gr.Blocks( | |
| title="FDAM AI Pipeline - Fire Damage Assessment", | |
| ) as app: | |
| # Session state (stored in Gradio State component) | |
| session_state = gr.State(value=create_new_session()) | |
| # Header | |
| gr.Markdown( | |
| """ | |
| # FDAM AI Pipeline | |
| ## Fire Damage Assessment Methodology v4.0.1 | |
| Upload images and project information to generate a professional | |
| Cleaning Specification / Scope of Work. | |
| """ | |
| ) | |
| # Mode indicator | |
| if settings.mock_models: | |
| gr.Markdown( | |
| """ | |
| > **Development Mode**: Using mock models for testing. | |
| > Set `MOCK_MODELS=false` for production inference. | |
| """ | |
| ) | |
| # Tab navigation | |
| with gr.Tabs() as tabs: | |
| # Tab 1: Project Information | |
| with gr.Tab("1. Project Info", id=0): | |
| tab1 = project.create_tab() | |
| # Tab 2: Building/Rooms | |
| with gr.Tab("2. Building/Rooms", id=1): | |
| tab2 = rooms.create_tab() | |
| # Tab 3: Images | |
| with gr.Tab("3. Images", id=2): | |
| tab3 = images.create_tab() | |
| # Tab 4: Observations | |
| with gr.Tab("4. Observations", id=3): | |
| tab4 = observations.create_tab() | |
| # Tab 5: Generate Results | |
| with gr.Tab("5. Generate Results", id=4): | |
| tab5 = results.create_tab() | |
| # --- Event Handlers --- | |
| # Tab 1: Project Info | |
| tab1["validate_btn"].click( | |
| fn=project.validate_and_continue, | |
| inputs=[ | |
| session_state, | |
| tab1["project_name"], | |
| tab1["address"], | |
| tab1["city"], | |
| tab1["state"], | |
| tab1["zip_code"], | |
| tab1["client_name"], | |
| tab1["fire_date"], | |
| tab1["assessment_date"], | |
| tab1["facility_classification"], | |
| tab1["construction_era"], | |
| tab1["assessor_name"], | |
| tab1["assessor_credentials"], | |
| ], | |
| outputs=[ | |
| session_state, | |
| tab1["validation_status"], | |
| tabs, | |
| ], | |
| ) | |
| # Tab 2: Building/Rooms | |
| tab2["add_room_btn"].click( | |
| fn=rooms.add_room, | |
| inputs=[ | |
| session_state, | |
| tab2["room_name"], | |
| tab2["room_floor"], | |
| tab2["room_length"], | |
| tab2["room_width"], | |
| tab2["room_height"], | |
| ], | |
| outputs=[ | |
| session_state, | |
| tab2["rooms_table"], | |
| tab2["validation_status"], | |
| tab2["room_count"], | |
| tab2["total_area"], | |
| tab2["total_volume"], | |
| tab2["room_name"], | |
| tab2["room_floor"], | |
| tab2["room_length"], | |
| tab2["room_width"], | |
| tab2["room_height"], | |
| ], | |
| ) | |
| tab2["clear_form_btn"].click( | |
| fn=lambda: ("", "", None, None, None), | |
| outputs=[ | |
| tab2["room_name"], | |
| tab2["room_floor"], | |
| tab2["room_length"], | |
| tab2["room_width"], | |
| tab2["room_height"], | |
| ], | |
| ) | |
| tab2["remove_last_btn"].click( | |
| fn=rooms.remove_last_room, | |
| inputs=[session_state], | |
| outputs=[ | |
| session_state, | |
| tab2["rooms_table"], | |
| tab2["validation_status"], | |
| tab2["room_count"], | |
| tab2["total_area"], | |
| tab2["total_volume"], | |
| ], | |
| ) | |
| tab2["clear_all_btn"].click( | |
| fn=rooms.clear_all_rooms, | |
| inputs=[session_state], | |
| outputs=[ | |
| session_state, | |
| tab2["rooms_table"], | |
| tab2["validation_status"], | |
| tab2["room_count"], | |
| tab2["total_area"], | |
| tab2["total_volume"], | |
| ], | |
| ) | |
| tab2["validate_btn"].click( | |
| fn=rooms.validate_and_continue, | |
| inputs=[session_state], | |
| outputs=[ | |
| session_state, | |
| tab2["validation_status"], | |
| tabs, | |
| ], | |
| ) | |
| tab2["back_btn"].click( | |
| fn=lambda: 0, | |
| outputs=[tabs], | |
| ) | |
| # Tab 3: Images | |
| # Update room dropdown when entering tab | |
| tabs.select( | |
| fn=lambda session, selected: ( | |
| images.update_room_choices(session) if selected == 2 else gr.update() | |
| ), | |
| inputs=[session_state, tabs], | |
| outputs=[tab3["room_select"]], | |
| ) | |
| tab3["add_image_btn"].click( | |
| fn=images.add_image, | |
| inputs=[ | |
| session_state, | |
| tab3["image_upload"], | |
| tab3["room_select"], | |
| tab3["image_description"], | |
| ], | |
| outputs=[ | |
| session_state, | |
| tab3["images_gallery"], | |
| tab3["validation_status"], | |
| tab3["image_count"], | |
| tab3["image_upload"], | |
| tab3["image_description"], | |
| tab3["room_select"], | |
| ], | |
| ) | |
| tab3["clear_upload_btn"].click( | |
| fn=lambda: (None, ""), | |
| outputs=[ | |
| tab3["image_upload"], | |
| tab3["image_description"], | |
| ], | |
| ) | |
| tab3["remove_last_btn"].click( | |
| fn=images.remove_last_image, | |
| inputs=[session_state], | |
| outputs=[ | |
| session_state, | |
| tab3["images_gallery"], | |
| tab3["validation_status"], | |
| tab3["image_count"], | |
| ], | |
| ) | |
| tab3["clear_all_btn"].click( | |
| fn=images.clear_all_images, | |
| inputs=[session_state], | |
| outputs=[ | |
| session_state, | |
| tab3["images_gallery"], | |
| tab3["validation_status"], | |
| tab3["image_count"], | |
| ], | |
| ) | |
| tab3["validate_btn"].click( | |
| fn=images.validate_and_continue, | |
| inputs=[session_state], | |
| outputs=[ | |
| session_state, | |
| tab3["validation_status"], | |
| tabs, | |
| ], | |
| ) | |
| tab3["back_btn"].click( | |
| fn=lambda: 1, | |
| outputs=[tabs], | |
| ) | |
| # Tab 4: Observations | |
| tab4["validate_btn"].click( | |
| fn=observations.validate_and_continue, | |
| inputs=[ | |
| session_state, | |
| tab4["smoke_odor"], | |
| tab4["odor_intensity"], | |
| tab4["visible_soot"], | |
| tab4["soot_description"], | |
| tab4["large_char"], | |
| tab4["char_density"], | |
| tab4["ash_residue"], | |
| tab4["ash_description"], | |
| tab4["surface_discoloration"], | |
| tab4["discoloration_description"], | |
| tab4["dust_interference"], | |
| tab4["dust_notes"], | |
| tab4["wildfire_indicators"], | |
| tab4["wildfire_notes"], | |
| tab4["additional_notes"], | |
| ], | |
| outputs=[ | |
| session_state, | |
| tab4["validation_status"], | |
| tabs, | |
| ], | |
| ) | |
| tab4["back_btn"].click( | |
| fn=lambda: 2, | |
| outputs=[tabs], | |
| ) | |
| # Tab 5: Generate Results | |
| # Update preflight check when entering tab | |
| tabs.select( | |
| fn=lambda session, selected: ( | |
| results.check_preflight(session) if selected == 4 else "" | |
| ), | |
| inputs=[session_state, tabs], | |
| outputs=[tab5["preflight_status"]], | |
| ) | |
| tab5["generate_btn"].click( | |
| fn=results.generate_assessment, | |
| inputs=[session_state], | |
| outputs=[ | |
| session_state, | |
| tab5["processing_status"], | |
| tab5["progress_html"], | |
| tab5["annotated_gallery"], | |
| tab5["stats_output"], | |
| tab5["sow_output"], | |
| tab5["download_md"], | |
| tab5["download_pdf"], | |
| ], | |
| ) | |
| tab5["regenerate_btn"].click( | |
| fn=results.generate_assessment, | |
| inputs=[session_state], | |
| outputs=[ | |
| session_state, | |
| tab5["processing_status"], | |
| tab5["progress_html"], | |
| tab5["annotated_gallery"], | |
| tab5["stats_output"], | |
| tab5["sow_output"], | |
| tab5["download_md"], | |
| tab5["download_pdf"], | |
| ], | |
| ) | |
| tab5["back_btn"].click( | |
| fn=lambda: 3, | |
| outputs=[tabs], | |
| ) | |
| # --- Session Resume Handlers --- | |
| # Load form data when navigating to tabs | |
| # Tab 1 (Project): Load project form fields | |
| tabs.select( | |
| fn=lambda session, selected: ( | |
| project.load_form_from_session(session) if selected == 0 | |
| else tuple([gr.update()] * 12) | |
| ), | |
| inputs=[session_state, tabs], | |
| outputs=[ | |
| tab1["project_name"], | |
| tab1["address"], | |
| tab1["city"], | |
| tab1["state"], | |
| tab1["zip_code"], | |
| tab1["client_name"], | |
| tab1["fire_date"], | |
| tab1["assessment_date"], | |
| tab1["facility_classification"], | |
| tab1["construction_era"], | |
| tab1["assessor_name"], | |
| tab1["assessor_credentials"], | |
| ], | |
| ) | |
| # Tab 2 (Rooms): Load room table and stats | |
| tabs.select( | |
| fn=lambda session, selected: ( | |
| rooms.load_from_session(session) if selected == 1 | |
| else (gr.update(), gr.update(), gr.update(), gr.update()) | |
| ), | |
| inputs=[session_state, tabs], | |
| outputs=[ | |
| tab2["rooms_table"], | |
| tab2["room_count"], | |
| tab2["total_area"], | |
| tab2["total_volume"], | |
| ], | |
| ) | |
| # Tab 3 (Images): Load gallery and count (room dropdown already handled above) | |
| tabs.select( | |
| fn=lambda session, selected: ( | |
| images.load_from_session(session) if selected == 2 | |
| else (gr.update(), gr.update(), gr.update()) | |
| ), | |
| inputs=[session_state, tabs], | |
| outputs=[ | |
| tab3["images_gallery"], | |
| tab3["image_count"], | |
| tab3["resume_warning"], | |
| ], | |
| ) | |
| # Tab 4 (Observations): Load observation form fields | |
| tabs.select( | |
| fn=lambda session, selected: ( | |
| observations.load_form_from_session(session) if selected == 3 | |
| else tuple([gr.update()] * 15) | |
| ), | |
| inputs=[session_state, tabs], | |
| outputs=[ | |
| tab4["smoke_odor"], | |
| tab4["odor_intensity"], | |
| tab4["visible_soot"], | |
| tab4["soot_description"], | |
| tab4["large_char"], | |
| tab4["char_density"], | |
| tab4["ash_residue"], | |
| tab4["ash_description"], | |
| tab4["surface_discoloration"], | |
| tab4["discoloration_description"], | |
| tab4["dust_interference"], | |
| tab4["dust_notes"], | |
| tab4["wildfire_indicators"], | |
| tab4["wildfire_notes"], | |
| tab4["additional_notes"], | |
| ], | |
| ) | |
| return app | |
| def main(): | |
| """Entry point for the application.""" | |
| print(f"Starting FDAM AI Pipeline...") | |
| print(f"Mock models: {settings.mock_models}") | |
| print(f"Server: {settings.server_host}:{settings.server_port}") | |
| app = create_app() | |
| app.launch( | |
| server_name=settings.server_host, | |
| server_port=settings.server_port, | |
| share=False, | |
| head=get_head_html(), # Inject localStorage JavaScript | |
| ) | |
| if __name__ == "__main__": | |
| main() | |