SmokeScan / app.py
KinetoLabs's picture
Initial commit: FDAM AI Pipeline v4.0.1
88bdcff
raw
history blame
13.1 kB
"""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()