Spaces:
Paused
Paused
| """Sample room data for testing the FDAM AI Pipeline. | |
| Provides 4 pre-configured sample scenarios with complete room data, | |
| images, and qualitative observations. | |
| MVP Simplification: Single room, no project-level data. | |
| """ | |
| import uuid | |
| import io | |
| from pathlib import Path | |
| from dataclasses import dataclass, field | |
| from PIL import Image | |
| from ui.state import ( | |
| SessionState, | |
| RoomFormData, | |
| ImageFormData, | |
| ObservationsFormData, | |
| ) | |
| from ui.components import image_store | |
| # Path to sample images directory | |
| SAMPLE_IMAGES_DIR = Path(__file__).parent.parent / "sample_images" | |
| class SampleScenario: | |
| """Definition of a sample fire damage scenario.""" | |
| id: str | |
| name: str | |
| description: str | |
| room_data: dict | |
| observations_data: dict | |
| image_files: list[str] = field(default_factory=list) | |
| # --- Sample Scenario Definitions --- | |
| SAMPLE_SCENARIOS = [ | |
| # 1. Bar & Dining Area | |
| SampleScenario( | |
| id="bar_dining", | |
| name="Bar & Dining Area", | |
| description="3 images", | |
| room_data={ | |
| "name": "Bar & Dining Area", | |
| "length_ft": 40.0, | |
| "width_ft": 30.0, | |
| "ceiling_height_ft": 12.0, | |
| "facility_classification": "non-operational", | |
| "construction_era": "pre-1980", | |
| }, | |
| observations_data={ | |
| "smoke_fire_odor": True, | |
| "odor_intensity": "strong", | |
| "visible_soot_deposits": True, | |
| "soot_pattern_description": "Heavy soot deposits on corrugated metal ceiling, moderate wall discoloration", | |
| "large_char_particles": True, | |
| "char_density_estimate": "moderate", | |
| "ash_like_residue": True, | |
| "ash_color_texture": "Ash deposits on horizontal surfaces and upholstered furniture", | |
| "surface_discoloration": True, | |
| "discoloration_description": "Tan/brown soot staining on walls, yellowing on decorative elements", | |
| "dust_loading_interference": False, | |
| "dust_notes": "", | |
| "wildfire_indicators": False, | |
| "wildfire_notes": "", | |
| "additional_notes": "", | |
| }, | |
| image_files=[ | |
| "Bar and dining area1.jpg", | |
| "Bar and dining area2.jpg", | |
| "Bar and dining area3.jpg", | |
| ], | |
| ), | |
| # 2. Bar Area | |
| SampleScenario( | |
| id="bar_area", | |
| name="Bar Area", | |
| description="3 images", | |
| room_data={ | |
| "name": "Bar Area", | |
| "length_ft": 25.0, | |
| "width_ft": 20.0, | |
| "ceiling_height_ft": 14.0, | |
| "facility_classification": "non-operational", | |
| "construction_era": "pre-1980", | |
| }, | |
| observations_data={ | |
| "smoke_fire_odor": True, | |
| "odor_intensity": "strong", | |
| "visible_soot_deposits": True, | |
| "soot_pattern_description": "Dense black coating on ceiling/ductwork, severe overhead damage", | |
| "large_char_particles": True, | |
| "char_density_estimate": "dense", | |
| "ash_like_residue": True, | |
| "ash_color_texture": "Heavy ash on shelving and bottled goods", | |
| "surface_discoloration": True, | |
| "discoloration_description": "Metal oxidation, melted plastic signage, deformed ductwork", | |
| "dust_loading_interference": False, | |
| "dust_notes": "", | |
| "wildfire_indicators": False, | |
| "wildfire_notes": "", | |
| "additional_notes": "", | |
| }, | |
| image_files=[ | |
| "Bar area1.jpg", | |
| "Bar area2.jpg", | |
| "Bar area3.jpg", | |
| ], | |
| ), | |
| # 3. Kitchen | |
| SampleScenario( | |
| id="kitchen", | |
| name="Kitchen", | |
| description="6 images", | |
| room_data={ | |
| "name": "Commercial Kitchen", | |
| "length_ft": 30.0, | |
| "width_ft": 25.0, | |
| "ceiling_height_ft": 10.0, | |
| "facility_classification": "non-operational", | |
| "construction_era": "1980-2000", | |
| }, | |
| observations_data={ | |
| "smoke_fire_odor": True, | |
| "odor_intensity": "strong", | |
| "visible_soot_deposits": True, | |
| "soot_pattern_description": "Heavy soot on all surfaces, ceiling collapse debris", | |
| "large_char_particles": True, | |
| "char_density_estimate": "dense", | |
| "ash_like_residue": True, | |
| "ash_color_texture": "Thick ash deposits on work surfaces, equipment heavily coated", | |
| "surface_discoloration": True, | |
| "discoloration_description": "Charred drywall, oxidized metal equipment, concrete staining", | |
| "dust_loading_interference": False, | |
| "dust_notes": "", | |
| "wildfire_indicators": False, | |
| "wildfire_notes": "", | |
| "additional_notes": "", | |
| }, | |
| image_files=[ | |
| "Kitchen 1.jpg", | |
| "Kitchen 2.jpg", | |
| "Kitchen 3.jpg", | |
| "Kitchen 4.jpg", | |
| "Kitchen 5.jpg", | |
| "Kitchen 6.jpg", | |
| ], | |
| ), | |
| # 4. Factory Area | |
| SampleScenario( | |
| id="factory", | |
| name="Factory Area", | |
| description="1 image", | |
| room_data={ | |
| "name": "Factory Production Area", | |
| "length_ft": 80.0, | |
| "width_ft": 60.0, | |
| "ceiling_height_ft": 25.0, | |
| "facility_classification": "operational", | |
| "construction_era": "pre-1980", | |
| }, | |
| observations_data={ | |
| "smoke_fire_odor": True, | |
| "odor_intensity": "strong", | |
| "visible_soot_deposits": True, | |
| "soot_pattern_description": "Complete structural compromise, deep char on all surfaces", | |
| "large_char_particles": True, | |
| "char_density_estimate": "dense", | |
| "ash_like_residue": True, | |
| "ash_color_texture": "Heavy ash coating throughout, debris accumulation", | |
| "surface_discoloration": True, | |
| "discoloration_description": "Extreme oxidation on metal framing, thermal spalling on concrete", | |
| "dust_loading_interference": False, | |
| "dust_notes": "", | |
| "wildfire_indicators": False, | |
| "wildfire_notes": "", | |
| "additional_notes": "", | |
| }, | |
| image_files=[ | |
| "factory_area.jpg", | |
| ], | |
| ), | |
| ] | |
| # Create lookup dict for fast access | |
| SAMPLE_SCENARIOS_BY_ID = {s.id: s for s in SAMPLE_SCENARIOS} | |
| def get_sample_choices() -> list[tuple[str, str]]: | |
| """Get dropdown choices for sample selector. | |
| Returns: | |
| List of (label, value) tuples for Gradio dropdown. | |
| """ | |
| choices = [("Select a sample scenario...", "")] | |
| for scenario in SAMPLE_SCENARIOS: | |
| label = f"{scenario.name} ({scenario.description})" | |
| choices.append((label, scenario.id)) | |
| return choices | |
| def load_sample_images(scenario: SampleScenario, room_id: str) -> list[ImageFormData]: | |
| """Load sample images from disk into image_store. | |
| Args: | |
| scenario: The sample scenario to load images for. | |
| room_id: The room ID to associate images with. | |
| Returns: | |
| List of ImageFormData objects for the loaded images. | |
| """ | |
| image_metas = [] | |
| for filename in scenario.image_files: | |
| filepath = SAMPLE_IMAGES_DIR / filename | |
| if filepath.exists(): | |
| try: | |
| # Read and convert image to PNG bytes | |
| img = Image.open(filepath) | |
| img_bytes = io.BytesIO() | |
| img.save(img_bytes, format="PNG") | |
| # Generate unique image ID | |
| image_id = f"sample-{uuid.uuid4().hex[:8]}" | |
| # Store in image_store | |
| image_store.store(image_id, img_bytes.getvalue()) | |
| # Create metadata | |
| image_metas.append( | |
| ImageFormData( | |
| id=image_id, | |
| filename=filename, | |
| room_id=room_id, | |
| description=f"Sample image: {filename}", | |
| ) | |
| ) | |
| except Exception: | |
| # Skip files that can't be opened as images | |
| continue | |
| return image_metas | |
| def load_sample(scenario_id: str) -> SessionState | None: | |
| """Load a sample scenario into a new SessionState. | |
| Args: | |
| scenario_id: The ID of the scenario to load. | |
| Returns: | |
| A new SessionState populated with the scenario data, or None if not found. | |
| """ | |
| scenario = SAMPLE_SCENARIOS_BY_ID.get(scenario_id) | |
| if not scenario: | |
| return None | |
| # Create room with unique ID and all fields from room_data | |
| room_id = f"room-{uuid.uuid4().hex[:8]}" | |
| room = RoomFormData( | |
| id=room_id, | |
| name=scenario.room_data["name"], | |
| length_ft=scenario.room_data["length_ft"], | |
| width_ft=scenario.room_data["width_ft"], | |
| ceiling_height_ft=scenario.room_data["ceiling_height_ft"], | |
| facility_classification=scenario.room_data.get("facility_classification", "non-operational"), | |
| construction_era=scenario.room_data.get("construction_era", "post-2000"), | |
| ) | |
| # Load images | |
| images = load_sample_images(scenario, room_id) | |
| # Create session with single room | |
| session = SessionState( | |
| room=room, | |
| images=images, | |
| observations=ObservationsFormData(**scenario.observations_data), | |
| name=scenario.room_data["name"], | |
| ) | |
| # Mark input as complete since sample has all required data | |
| session.input_complete = True | |
| return session | |
| def get_scenario_by_id(scenario_id: str) -> SampleScenario | None: | |
| """Get a sample scenario by its ID. | |
| Args: | |
| scenario_id: The scenario ID. | |
| Returns: | |
| The SampleScenario object or None if not found. | |
| """ | |
| return SAMPLE_SCENARIOS_BY_ID.get(scenario_id) | |