| | """ |
| | Traffic Accident Reconstruction System |
| | ====================================== |
| | Main Streamlit Application |
| | Huawei AI Innovation Challenge 2026 |
| | |
| | This system uses MindSpore AI to analyze traffic accidents |
| | and generate probable scenarios with 2D simulation. |
| | """ |
| |
|
| | import streamlit as st |
| | import sys |
| | from pathlib import Path |
| |
|
| | |
| | sys.path.insert(0, str(Path(__file__).parent)) |
| |
|
| | from config import STREAMLIT_CONFIG, CASE_STUDY_LOCATION, COLORS |
| | from ui.components import render_sidebar, render_header, render_footer |
| | from ui.map_viewer import render_map_section |
| | from ui.vehicle_input import render_vehicle_input |
| | from ui.party_input import render_party_input, render_evidence_upload, render_party_summary |
| | from ui.results_display import render_results |
| |
|
| | |
| | |
| | |
| |
|
| | st.set_page_config( |
| | page_title=STREAMLIT_CONFIG["page_title"], |
| | page_icon=STREAMLIT_CONFIG["page_icon"], |
| | layout=STREAMLIT_CONFIG["layout"], |
| | initial_sidebar_state=STREAMLIT_CONFIG["initial_sidebar_state"] |
| | ) |
| |
|
| | |
| | |
| | |
| |
|
| | st.markdown(""" |
| | <style> |
| | /* Layout - Dark theme with modern spacing */ |
| | .block-container { |
| | padding-top: 1.4rem; |
| | padding-bottom: 2.5rem; |
| | max-width: 1200px; |
| | } |
| | |
| | [data-testid="stSidebar"] { |
| | min-width: 340px; |
| | max-width: 340px; |
| | background: linear-gradient(180deg, #0e1117 0%, #1a1f2e 100%); |
| | } |
| | |
| | /* Main container with gradient background */ |
| | .main { |
| | background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 50%, #0f1419 100%); |
| | color: rgba(255, 255, 255, 0.95); |
| | } |
| | |
| | /* Header styling - Modern minimal */ |
| | .main-header { |
| | background: linear-gradient(135deg, rgba(30, 58, 95, 0.3) 0%, rgba(45, 90, 135, 0.2) 100%); |
| | padding: 2rem; |
| | border-radius: 16px; |
| | border: 1px solid rgba(255, 255, 255, 0.12); |
| | color: white; |
| | margin-bottom: 2rem; |
| | text-align: center; |
| | box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); |
| | } |
| | |
| | .main-header h1 { |
| | margin: 0; |
| | font-size: 2.8rem; |
| | font-weight: 800; |
| | letter-spacing: 0.5px; |
| | background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%); |
| | -webkit-background-clip: text; |
| | -webkit-text-fill-color: transparent; |
| | } |
| | |
| | .main-header p { |
| | margin: 0.8rem 0 0 0; |
| | opacity: 0.75; |
| | font-size: 1.05rem; |
| | color: rgba(255, 255, 255, 0.72); |
| | } |
| | |
| | /* Card styling - Glass morphism effect */ |
| | .info-card { |
| | background: rgba(255, 255, 255, 0.04); |
| | backdrop-filter: blur(10px); |
| | padding: 1.5rem; |
| | border-radius: 16px; |
| | border: 1px solid rgba(255, 255, 255, 0.12); |
| | margin: 1rem 0; |
| | box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); |
| | } |
| | |
| | /* Step indicator - Modern pills */ |
| | .step-indicator { |
| | display: flex; |
| | justify-content: space-between; |
| | margin: 2rem 0; |
| | gap: 0.5rem; |
| | } |
| | |
| | .step { |
| | flex: 1; |
| | text-align: center; |
| | padding: 1rem; |
| | background: rgba(255, 255, 255, 0.05); |
| | border: 1px solid rgba(255, 255, 255, 0.1); |
| | margin: 0; |
| | border-radius: 12px; |
| | position: relative; |
| | transition: all 0.3s ease; |
| | } |
| | |
| | .step.active { |
| | background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); |
| | border-color: #3b82f6; |
| | color: white; |
| | box-shadow: 0 8px 24px rgba(59, 130, 246, 0.3); |
| | } |
| | |
| | .step.completed { |
| | background: linear-gradient(135deg, #10b981 0%, #059669 100%); |
| | border-color: #10b981; |
| | color: white; |
| | } |
| | |
| | /* Vehicle cards - Dark theme */ |
| | .vehicle-card { |
| | background: rgba(255, 255, 255, 0.06); |
| | border: 1px solid rgba(255, 255, 255, 0.15); |
| | border-radius: 12px; |
| | padding: 1.5rem; |
| | margin: 1rem 0; |
| | } |
| | |
| | .vehicle-card.vehicle-1 { |
| | border-color: rgba(255, 75, 75, 0.4); |
| | background: rgba(255, 75, 75, 0.08); |
| | } |
| | |
| | .vehicle-card.vehicle-2 { |
| | border-color: rgba(75, 123, 255, 0.4); |
| | background: rgba(75, 123, 255, 0.08); |
| | } |
| | |
| | /* Results styling - Modern cards */ |
| | .scenario-card { |
| | background: rgba(255, 255, 255, 0.06); |
| | border-radius: 12px; |
| | padding: 1.5rem; |
| | margin: 1rem 0; |
| | box-shadow: 0 4px 12px rgba(0,0,0,0.2); |
| | border: 1px solid rgba(255, 255, 255, 0.1); |
| | } |
| | |
| | .probability-high { |
| | color: #10b981; |
| | font-weight: bold; |
| | } |
| | |
| | .probability-medium { |
| | color: #f59e0b; |
| | font-weight: bold; |
| | } |
| | |
| | .probability-low { |
| | color: #ef4444; |
| | font-weight: bold; |
| | } |
| | |
| | /* Button styling - Modern gradient */ |
| | .stButton > button { |
| | width: 100%; |
| | background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); |
| | color: white; |
| | border: none; |
| | padding: 0.85rem 1.5rem; |
| | border-radius: 12px; |
| | font-weight: 600; |
| | font-size: 1rem; |
| | transition: all 0.3s ease; |
| | box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); |
| | } |
| | |
| | .stButton > button:hover { |
| | background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); |
| | box-shadow: 0 6px 16px rgba(59, 130, 246, 0.4); |
| | transform: translateY(-2px); |
| | } |
| | |
| | /* Input fields - Dark theme with better contrast */ |
| | .stTextInput > div > div > input, |
| | .stTextArea > div > div > textarea, |
| | .stSelectbox > div > div > select, |
| | .stNumberInput > div > div > input { |
| | background: rgba(255, 255, 255, 0.08) !important; |
| | border: 1px solid rgba(255, 255, 255, 0.15) !important; |
| | border-radius: 10px !important; |
| | color: white !important; |
| | padding: 0.75rem !important; |
| | } |
| | |
| | .stTextInput > div > div > input:focus, |
| | .stTextArea > div > div > textarea:focus, |
| | .stSelectbox > div > div > select:focus, |
| | .stNumberInput > div > div > input:focus { |
| | border-color: #3b82f6 !important; |
| | box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15) !important; |
| | } |
| | |
| | /* Labels - Better visibility */ |
| | label, .stTextInput > label, .stTextArea > label, .stSelectbox > label, .stSlider > label, .stNumberInput > label { |
| | color: rgba(255, 255, 255, 0.85) !important; |
| | font-weight: 500 !important; |
| | font-size: 0.95rem !important; |
| | } |
| | |
| | /* Metric cards - Modern design */ |
| | [data-testid="stMetricValue"] { |
| | font-size: 2.2rem; |
| | font-weight: 700; |
| | color: white; |
| | } |
| | |
| | [data-testid="stMetricLabel"] { |
| | color: rgba(255, 255, 255, 0.7); |
| | font-size: 0.95rem; |
| | font-weight: 500; |
| | } |
| | |
| | /* Tabs styling - Modern */ |
| | .stTabs [data-baseweb="tab-list"] { |
| | gap: 8px; |
| | background: rgba(255, 255, 255, 0.03); |
| | padding: 0.5rem; |
| | border-radius: 12px; |
| | } |
| | |
| | .stTabs [data-baseweb="tab"] { |
| | background: transparent; |
| | border-radius: 8px; |
| | color: rgba(255, 255, 255, 0.7); |
| | font-weight: 500; |
| | padding: 0.75rem 1.5rem; |
| | border: 1px solid transparent; |
| | } |
| | |
| | .stTabs [aria-selected="true"] { |
| | background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); |
| | color: white !important; |
| | border-color: #3b82f6; |
| | } |
| | |
| | /* Messages - Modern alerts */ |
| | .stSuccess, .element-container:has(.stSuccess) { |
| | background: rgba(16, 185, 129, 0.15) !important; |
| | border: 1px solid rgba(16, 185, 129, 0.3) !important; |
| | border-radius: 12px !important; |
| | padding: 1rem !important; |
| | } |
| | |
| | .stSuccess [data-testid="stMarkdownContainer"] p { |
| | color: #10b981 !important; |
| | } |
| | |
| | .stError { |
| | background: rgba(239, 68, 68, 0.15) !important; |
| | border: 1px solid rgba(239, 68, 68, 0.3) !important; |
| | border-radius: 12px !important; |
| | } |
| | |
| | .stWarning { |
| | background: rgba(245, 158, 11, 0.15) !important; |
| | border: 1px solid rgba(245, 158, 11, 0.3) !important; |
| | border-radius: 12px !important; |
| | } |
| | |
| | .stInfo { |
| | background: rgba(59, 130, 246, 0.15) !important; |
| | border: 1px solid rgba(59, 130, 246, 0.3) !important; |
| | border-radius: 12px !important; |
| | } |
| | |
| | /* Sidebar - Better styling */ |
| | [data-testid="stSidebar"] h1, [data-testid="stSidebar"] h2, [data-testid="stSidebar"] h3 { |
| | color: white !important; |
| | } |
| | |
| | [data-testid="stSidebar"] p, [data-testid="stSidebar"] span { |
| | color: rgba(255, 255, 255, 0.85) !important; |
| | } |
| | |
| | /* Expander - Modern style */ |
| | .streamlit-expanderHeader { |
| | background: rgba(255, 255, 255, 0.05) !important; |
| | border-radius: 10px !important; |
| | border: 1px solid rgba(255, 255, 255, 0.1) !important; |
| | color: white !important; |
| | font-weight: 500 !important; |
| | } |
| | |
| | .streamlit-expanderContent { |
| | background: rgba(255, 255, 255, 0.02) !important; |
| | border: 1px solid rgba(255, 255, 255, 0.08) !important; |
| | border-radius: 0 0 10px 10px !important; |
| | } |
| | |
| | /* Checkbox and Radio - Better visibility */ |
| | .stCheckbox label, .stRadio label { |
| | color: rgba(255, 255, 255, 0.85) !important; |
| | } |
| | |
| | /* Slider - Modern */ |
| | .stSlider [data-baseweb="slider"] { |
| | background: rgba(59, 130, 246, 0.2); |
| | } |
| | |
| | /* Headings - Better hierarchy */ |
| | h1, h2, h3, h4, h5, h6 { |
| | color: white !important; |
| | } |
| | |
| | h1 { |
| | font-weight: 800 !important; |
| | letter-spacing: -0.5px; |
| | } |
| | |
| | h2 { |
| | font-weight: 700 !important; |
| | letter-spacing: -0.3px; |
| | } |
| | |
| | h3 { |
| | font-weight: 600 !important; |
| | } |
| | |
| | /* Markdown text - Better readability */ |
| | p, span, div { |
| | color: rgba(255, 255, 255, 0.9); |
| | } |
| | |
| | /* Code blocks - Dark theme */ |
| | code { |
| | background: rgba(255, 255, 255, 0.08) !important; |
| | color: #60a5fa !important; |
| | padding: 0.2rem 0.4rem !important; |
| | border-radius: 4px !important; |
| | } |
| | |
| | /* Dataframe - Dark theme */ |
| | [data-testid="stDataFrame"] { |
| | background: rgba(255, 255, 255, 0.05); |
| | border-radius: 12px; |
| | border: 1px solid rgba(255, 255, 255, 0.1); |
| | } |
| | |
| | /* Hide Streamlit branding */ |
| | #MainMenu {visibility: hidden;} |
| | footer {visibility: hidden;} |
| | |
| | /* Progress bar */ |
| | .stProgress > div > div { |
| | background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 100%); |
| | border-radius: 8px; |
| | } |
| | </style> |
| | """, unsafe_allow_html=True) |
| |
|
| | |
| | |
| | |
| |
|
| | def init_session_state(): |
| | """Initialize all session state variables.""" |
| | |
| | |
| | if 'current_step' not in st.session_state: |
| | st.session_state.current_step = 1 |
| | |
| | |
| | if 'accident_info' not in st.session_state: |
| | st.session_state.accident_info = { |
| | 'location': CASE_STUDY_LOCATION.copy(), |
| | 'datetime': None, |
| | 'road_type': 'roundabout', |
| | 'weather': 'clear', |
| | 'road_condition': 'dry', |
| | 'visibility': 1.0, |
| | 'lighting': 'daylight', |
| | 'notes': '' |
| | } |
| | |
| | |
| | if 'vehicle_1' not in st.session_state: |
| | st.session_state.vehicle_1 = { |
| | 'type': 'sedan', |
| | 'speed': 50, |
| | 'direction': 'north', |
| | 'action': 'entering_roundabout', |
| | 'braking': False, |
| | 'signaling': False, |
| | 'lights_on': True, |
| | 'horn_used': False, |
| | 'path': [], |
| | 'description': '' |
| | } |
| | |
| | |
| | if 'vehicle_2' not in st.session_state: |
| | st.session_state.vehicle_2 = { |
| | 'type': 'sedan', |
| | 'speed': 50, |
| | 'direction': 'east', |
| | 'action': 'going_straight', |
| | 'braking': False, |
| | 'signaling': False, |
| | 'lights_on': True, |
| | 'horn_used': False, |
| | 'path': [], |
| | 'description': '' |
| | } |
| | |
| | |
| | if 'party_1' not in st.session_state: |
| | st.session_state.party_1 = { |
| | 'full_name': '', |
| | 'id_iqama': '', |
| | 'phone': '', |
| | 'role': 'Driver', |
| | 'vehicle_make_model': '', |
| | 'plate_number': '', |
| | 'insurance': '', |
| | 'damage_notes': '', |
| | 'statement': '' |
| | } |
| | |
| | |
| | if 'party_2' not in st.session_state: |
| | st.session_state.party_2 = { |
| | 'full_name': '', |
| | 'id_iqama': '', |
| | 'phone': '', |
| | 'role': 'Driver', |
| | 'vehicle_make_model': '', |
| | 'plate_number': '', |
| | 'insurance': '', |
| | 'damage_notes': '', |
| | 'statement': '' |
| | } |
| | |
| | |
| | if 'evidence_photos' not in st.session_state: |
| | st.session_state.evidence_photos = [] |
| | |
| | |
| | if 'analysis_results' not in st.session_state: |
| | st.session_state.analysis_results = None |
| | |
| | |
| | if 'scenarios' not in st.session_state: |
| | st.session_state.scenarios = [] |
| | |
| | |
| | if 'map_data' not in st.session_state: |
| | st.session_state.map_data = None |
| | |
| | |
| | if 'analysis_ready' not in st.session_state: |
| | st.session_state.analysis_ready = False |
| |
|
| | |
| | |
| | |
| |
|
| | def main(): |
| | """Main application entry point.""" |
| | |
| | |
| | init_session_state() |
| | |
| | |
| | col1, col2, col3 = st.columns([1, 6, 1]) |
| | with col1: |
| | st.markdown("## π") |
| | with col2: |
| | st.markdown(""" |
| | <div style='padding-top: 0.5rem;'> |
| | <h1 style='margin: 0; font-size: 2.5rem; font-weight: 800; background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;'>CrashLens AI</h1> |
| | <p style='margin: 0.3rem 0 0 0; color: rgba(255, 255, 255, 0.7); font-size: 1rem;'>Traffic Accident Scenario Analysis β’ MindSpore-ready β’ PDF report</p> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | with col3: |
| | st.write("") |
| | |
| | st.markdown("<br>", unsafe_allow_html=True) |
| | |
| | |
| | render_sidebar() |
| | |
| | |
| | steps = ["π Location", "π Vehicle 1", "π Vehicle 2", "π₯ Parties", "π Evidence", "π Analysis", "π Results"] |
| | cols = st.columns(len(steps)) |
| | |
| | for i, (col, step_name) in enumerate(zip(cols, steps), 1): |
| | with col: |
| | if i < st.session_state.current_step: |
| | |
| | st.markdown(f""" |
| | <div style='text-align: center; padding: 0.75rem 0.25rem; |
| | background: linear-gradient(135deg, #10b981 0%, #059669 100%); |
| | color: white; border-radius: 10px; font-size: 0.8rem; font-weight: 600; |
| | box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);'> |
| | β {step_name.split()[-1]} |
| | </div> |
| | """, unsafe_allow_html=True) |
| | elif i == st.session_state.current_step: |
| | |
| | st.markdown(f""" |
| | <div style='text-align: center; padding: 0.75rem 0.25rem; |
| | background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); |
| | color: white; border-radius: 10px; font-size: 0.8rem; font-weight: 600; |
| | box-shadow: 0 6px 16px rgba(59, 130, 246, 0.4); |
| | border: 2px solid rgba(96, 165, 250, 0.5);'> |
| | {step_name} |
| | </div> |
| | """, unsafe_allow_html=True) |
| | else: |
| | |
| | st.markdown(f""" |
| | <div style='text-align: center; padding: 0.75rem 0.25rem; |
| | background: rgba(255, 255, 255, 0.05); |
| | color: rgba(255, 255, 255, 0.4); border-radius: 10px; font-size: 0.8rem; |
| | border: 1px solid rgba(255, 255, 255, 0.1);'> |
| | {step_name.split()[-1]} |
| | </div> |
| | """, unsafe_allow_html=True) |
| | |
| | st.markdown("<div style='margin: 1.5rem 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);'></div>", unsafe_allow_html=True) |
| | |
| | |
| | if st.session_state.current_step == 1: |
| | render_step_1_location() |
| | elif st.session_state.current_step == 2: |
| | render_step_2_vehicle1() |
| | elif st.session_state.current_step == 3: |
| | render_step_3_vehicle2() |
| | elif st.session_state.current_step == 4: |
| | render_step_4_parties() |
| | elif st.session_state.current_step == 5: |
| | render_step_5_evidence() |
| | elif st.session_state.current_step == 6: |
| | render_step_6_analysis() |
| | elif st.session_state.current_step == 7: |
| | render_step_7_results() |
| | |
| | |
| | render_footer() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | def render_step_1_location(): |
| | """Render the location selection step.""" |
| | |
| | st.header("π Step 1: Accident Location") |
| | |
| | st.markdown(""" |
| | <div class="info-card"> |
| | <h4>Accident Details</h4> |
| | <p>Enter the location and conditions where the accident occurred.</p> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | |
| | col1, col2 = st.columns([2, 1]) |
| | |
| | with col1: |
| | |
| | render_map_section() |
| | |
| | with col2: |
| | st.subheader("Location Details") |
| | |
| | |
| | location_name = st.text_input( |
| | "Location Name", |
| | value=st.session_state.accident_info['location']['name'] |
| | ) |
| | |
| | |
| | lat = st.number_input( |
| | "Latitude", |
| | value=st.session_state.accident_info['location']['latitude'], |
| | format="%.6f", |
| | step=0.0001 |
| | ) |
| | |
| | lon = st.number_input( |
| | "Longitude", |
| | value=st.session_state.accident_info['location']['longitude'], |
| | format="%.6f", |
| | step=0.0001 |
| | ) |
| | |
| | |
| | road_types = [ |
| | 'roundabout', 'crossroad', 't_junction', 'highway_merge', |
| | 'parking', 'highway', 'urban_road', 'other' |
| | ] |
| | road_type_labels = { |
| | 'roundabout': 'Roundabout (Ψ―ΩΨ§Ψ±)', |
| | 'crossroad': 'Crossroad (ΨͺΩΨ§Ψ·ΨΉ)', |
| | 't_junction': 'T-Junction', |
| | 'highway_merge': 'Highway Merge', |
| | 'parking': 'Parking / Low Speed', |
| | 'highway': 'Highway', |
| | 'urban_road': 'Urban Road', |
| | 'other': 'Other' |
| | } |
| | |
| | road_type = st.selectbox( |
| | "Road Type", |
| | options=road_types, |
| | format_func=lambda x: road_type_labels.get(x, x.title()), |
| | index=road_types.index(st.session_state.accident_info.get('road_type', 'roundabout')) |
| | ) |
| | |
| | |
| | accident_date = st.date_input("Accident Date") |
| | accident_time = st.time_input("Accident Time") |
| | |
| | |
| | weather = st.selectbox( |
| | "Weather Conditions", |
| | options=['clear', 'cloudy', 'rainy', 'foggy', 'sandstorm'], |
| | format_func=lambda x: x.title() |
| | ) |
| | |
| | |
| | road_condition = st.selectbox( |
| | "Road Condition", |
| | options=['dry', 'wet', 'sandy', 'oily'], |
| | format_func=lambda x: x.title() |
| | ) |
| | |
| | |
| | notes = st.text_area( |
| | "Notes (optional)", |
| | value=st.session_state.accident_info.get('notes', ''), |
| | placeholder="Any extra context about the accident...", |
| | height=80 |
| | ) |
| | |
| | |
| | st.session_state.accident_info.update({ |
| | 'location': { |
| | 'name': location_name, |
| | 'latitude': lat, |
| | 'longitude': lon, |
| | 'radius_meters': 200 |
| | }, |
| | 'road_type': road_type, |
| | 'datetime': f"{accident_date} {accident_time}", |
| | 'weather': weather, |
| | 'road_condition': road_condition, |
| | 'notes': notes |
| | }) |
| | |
| | |
| | col1, col2, col3 = st.columns([1, 2, 1]) |
| | with col3: |
| | if st.button("Next: Vehicle 1 β", type="primary"): |
| | st.session_state.current_step = 2 |
| | st.rerun() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | def render_step_2_vehicle1(): |
| | """Render Vehicle 1 input step.""" |
| | |
| | st.header("π Step 2: First Vehicle Information") |
| | |
| | st.markdown(""" |
| | <div class="info-card" style="border-color: #FF4B4B;"> |
| | <h4>Vehicle 1 (Red)</h4> |
| | <p>Enter the details of the first vehicle involved in the accident.</p> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | |
| | col1, col2 = st.columns([2, 1]) |
| | |
| | with col1: |
| | |
| | render_map_section(vehicle_id=1) |
| | |
| | with col2: |
| | render_vehicle_input(1) |
| | |
| | |
| | col1, col2, col3 = st.columns([1, 2, 1]) |
| | with col1: |
| | if st.button("β Back"): |
| | st.session_state.current_step = 1 |
| | st.rerun() |
| | with col3: |
| | if st.button("Next: Vehicle 2 β", type="primary"): |
| | st.session_state.current_step = 3 |
| | st.rerun() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | def render_step_3_vehicle2(): |
| | """Render Vehicle 2 input step.""" |
| | |
| | st.header("π Step 3: Second Vehicle Information") |
| | |
| | st.markdown(""" |
| | <div class="info-card" style="border-color: #4B7BFF;"> |
| | <h4>Vehicle 2 (Blue)</h4> |
| | <p>Enter the details of the second vehicle involved in the accident.</p> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | |
| | col1, col2 = st.columns([2, 1]) |
| | |
| | with col1: |
| | |
| | render_map_section(vehicle_id=2) |
| | |
| | with col2: |
| | render_vehicle_input(2) |
| | |
| | |
| | col1, col2, col3 = st.columns([1, 2, 1]) |
| | with col1: |
| | if st.button("β Back"): |
| | st.session_state.current_step = 2 |
| | st.rerun() |
| | with col3: |
| | if st.button("Analyze Accident β", type="primary"): |
| | st.session_state.current_step = 4 |
| | st.rerun() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | def render_step_4_parties(): |
| | """Render the parties (driver details) input step.""" |
| | |
| | st.header("π₯ Step 4: Parties Information") |
| | |
| | st.markdown(""" |
| | <div class="info-card"> |
| | <h4>Driver & Vehicle Details</h4> |
| | <p>Enter information about the parties involved in the accident. This information will be included in the official report.</p> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | |
| | |
| | col1, col2 = st.columns(2) |
| | |
| | with col1: |
| | render_party_input(1) |
| | |
| | with col2: |
| | render_party_input(2) |
| | |
| | |
| | st.markdown("---") |
| | col1, col2, col3 = st.columns([1, 2, 1]) |
| | with col1: |
| | if st.button("β Back to Vehicle 2"): |
| | st.session_state.current_step = 3 |
| | st.rerun() |
| | with col3: |
| | if st.button("Next: Evidence β", type="primary"): |
| | st.session_state.current_step = 5 |
| | st.rerun() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | def render_step_5_evidence(): |
| | """Render the evidence upload step.""" |
| | |
| | st.header("π· Step 5: Evidence (Optional)") |
| | |
| | st.markdown(""" |
| | <div class="info-card"> |
| | <h4>Upload Evidence Photos</h4> |
| | <p>Upload photos of the accident scene, vehicle damage, or any other relevant evidence. This is optional but recommended.</p> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | |
| | |
| | render_evidence_upload() |
| | |
| | |
| | st.markdown("---") |
| | st.subheader("π Data Summary") |
| | |
| | col1, col2, col3 = st.columns(3) |
| | |
| | with col1: |
| | st.markdown("**π Location**") |
| | st.write(f"{st.session_state.accident_info['location'].get('name', 'Not set')}") |
| | st.write(f"Type: {st.session_state.accident_info.get('road_type', 'N/A').replace('_', ' ').title()}") |
| | |
| | with col2: |
| | st.markdown("**π Vehicle 1**") |
| | |
| | v1 = st.session_state.vehicle_1 |
| | if isinstance(v1, dict): |
| | st.write(f"{v1.get('type', 'sedan').title()} @ {v1.get('speed', 50)} km/h") |
| | else: |
| | st.write("Vehicle 1 data") |
| | party1_name = st.session_state.party_1.get('full_name', '') if isinstance(st.session_state.party_1, dict) else '' |
| | if party1_name: |
| | st.write(f"Driver: {party1_name}") |
| | |
| | with col3: |
| | st.markdown("**π Vehicle 2**") |
| | |
| | v2 = st.session_state.vehicle_2 |
| | if isinstance(v2, dict): |
| | st.write(f"{v2.get('type', 'sedan').title()} @ {v2.get('speed', 50)} km/h") |
| | else: |
| | st.write("Vehicle 2 data") |
| | party2_name = st.session_state.party_2.get('full_name', '') if isinstance(st.session_state.party_2, dict) else '' |
| | if party2_name: |
| | st.write(f"Driver: {party2_name}") |
| | |
| | |
| | st.markdown("---") |
| | col1, col2, col3 = st.columns([1, 2, 1]) |
| | with col1: |
| | if st.button("β Back to Parties"): |
| | st.session_state.current_step = 4 |
| | st.rerun() |
| | with col3: |
| | if st.button("Run Analysis β", type="primary"): |
| | st.session_state.current_step = 6 |
| | st.rerun() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | def render_step_6_analysis(): |
| | """Render the AI analysis step.""" |
| | |
| | st.header("π€ Step 4: AI Analysis") |
| | |
| | st.markdown(""" |
| | <div class="info-card"> |
| | <h4>MindSpore AI Analysis</h4> |
| | <p>Review the accident data below and click "Run AI Analysis" to generate possible scenarios.</p> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | |
| | |
| | validation_passed = True |
| | validation_messages = [] |
| | |
| | |
| | if not st.session_state.vehicle_1.get('path'): |
| | validation_messages.append("β οΈ Vehicle 1 path not defined - will use direction-based estimation") |
| | if not st.session_state.vehicle_2.get('path'): |
| | validation_messages.append("β οΈ Vehicle 2 path not defined - will use direction-based estimation") |
| | |
| | |
| | st.subheader("π Input Data Summary") |
| | |
| | col1, col2, col3 = st.columns(3) |
| | |
| | with col1: |
| | st.markdown(""" |
| | <div style='background: rgba(59, 130, 246, 0.15); padding: 1rem; border-radius: 12px; border-left: 4px solid #3b82f6; box-shadow: 0 4px 12px rgba(0,0,0,0.2);'> |
| | <h4 style='margin:0; color:#60a5fa; font-weight: 700;'>π Location</h4> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | st.write(f"**{st.session_state.accident_info['location'].get('name', 'Unknown')}**") |
| | st.write(f"π£οΈ Type: `{st.session_state.accident_info['road_type']}`") |
| | st.write(f"π€οΈ Weather: `{st.session_state.accident_info['weather']}`") |
| | st.write(f"π€οΈ Road: `{st.session_state.accident_info['road_condition']}`") |
| | st.write(f"π
{st.session_state.accident_info.get('datetime', 'Not specified')}") |
| | |
| | with col2: |
| | st.markdown(""" |
| | <div style='background: rgba(255, 75, 75, 0.15); padding: 1rem; border-radius: 12px; border-left: 4px solid #FF4B4B; box-shadow: 0 4px 12px rgba(0,0,0,0.2);'> |
| | <h4 style='margin:0; color:#ff6b6b; font-weight: 700;'>π Vehicle 1 (Red)</h4> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | v1 = st.session_state.vehicle_1 if isinstance(st.session_state.vehicle_1, dict) else {'type': 'sedan', 'speed': 50, 'direction': 'north', 'action': 'going_straight', 'path': []} |
| | st.write(f"**Type:** {v1.get('type', 'sedan').title()}") |
| | st.write(f"**Speed:** {v1.get('speed', 50)} km/h") |
| | st.write(f"**Direction:** {v1.get('direction', 'north').title()}") |
| | st.write(f"**Action:** {v1.get('action', 'going_straight').replace('_', ' ').title()}") |
| | path_status = "β
Defined" if v1.get('path') else "β οΈ Not set" |
| | st.write(f"**Path:** {path_status}") |
| | |
| | with col3: |
| | st.markdown(""" |
| | <div style='background: rgba(75, 123, 255, 0.15); padding: 1rem; border-radius: 12px; border-left: 4px solid #4B7BFF; box-shadow: 0 4px 12px rgba(0,0,0,0.2);'> |
| | <h4 style='margin:0; color:#6b9bff; font-weight: 700;'>π Vehicle 2 (Blue)</h4> |
| | </div> |
| | """, unsafe_allow_html=True) |
| | v2 = st.session_state.vehicle_2 if isinstance(st.session_state.vehicle_2, dict) else {'type': 'sedan', 'speed': 50, 'direction': 'east', 'action': 'going_straight', 'path': []} |
| | st.write(f"**Type:** {v2.get('type', 'sedan').title()}") |
| | st.write(f"**Speed:** {v2.get('speed', 50)} km/h") |
| | st.write(f"**Direction:** {v2.get('direction', 'east').title()}") |
| | st.write(f"**Action:** {v2.get('action', 'going_straight').replace('_', ' ').title()}") |
| | path_status = "β
Defined" if v2.get('path') else "β οΈ Not set" |
| | st.write(f"**Path:** {path_status}") |
| | |
| | |
| | if validation_messages: |
| | st.markdown("---") |
| | for msg in validation_messages: |
| | st.warning(msg) |
| | |
| | st.markdown("---") |
| | |
| | |
| | st.subheader("π Run Analysis") |
| | |
| | col1, col2, col3 = st.columns([1, 2, 1]) |
| | |
| | with col2: |
| | if st.button("π§ Run MindSpore AI Analysis", type="primary", use_container_width=True): |
| | |
| | |
| | progress_bar = st.progress(0) |
| | status_text = st.empty() |
| | |
| | try: |
| | |
| | status_text.text("Step 1/4: Validating input data...") |
| | progress_bar.progress(10) |
| | import time |
| | time.sleep(0.5) |
| | |
| | |
| | status_text.text("Step 2/4: Extracting features...") |
| | progress_bar.progress(30) |
| | time.sleep(0.5) |
| | |
| | |
| | status_text.text("Step 3/4: Running MindSpore AI model...") |
| | progress_bar.progress(50) |
| | |
| | |
| | from analysis.scenario_analyzer import analyze_accident |
| | |
| | results = analyze_accident( |
| | accident_info=st.session_state.accident_info, |
| | vehicle_1=st.session_state.vehicle_1, |
| | vehicle_2=st.session_state.vehicle_2 |
| | ) |
| | |
| | progress_bar.progress(80) |
| | time.sleep(0.3) |
| | |
| | |
| | status_text.text("Step 4/4: Generating scenarios...") |
| | progress_bar.progress(100) |
| | |
| | |
| | st.session_state.analysis_results = results |
| | st.session_state.scenarios = results.get('scenarios', []) |
| | st.session_state.analysis_ready = True |
| | |
| | status_text.empty() |
| | progress_bar.empty() |
| | |
| | |
| | st.success(f""" |
| | β
**Analysis Complete!** |
| | |
| | - Generated **{len(st.session_state.scenarios)}** possible scenarios |
| | - Most likely: **{results.get('most_likely_scenario', {}).get('type', 'Unknown').replace('_', ' ').title()}** |
| | - Confidence: **{results.get('overall_collision_probability', 0)*100:.1f}%** |
| | """) |
| | |
| | except Exception as e: |
| | st.error(f"β Analysis failed: {str(e)}") |
| | progress_bar.empty() |
| | status_text.empty() |
| | |
| | |
| | if st.session_state.analysis_results: |
| | st.markdown("---") |
| | st.subheader("π Quick Results Preview") |
| | |
| | results = st.session_state.analysis_results |
| | scenarios = st.session_state.scenarios |
| | |
| | |
| | metric_cols = st.columns(4) |
| | |
| | with metric_cols[0]: |
| | st.metric( |
| | "Scenarios Generated", |
| | len(scenarios) |
| | ) |
| | |
| | with metric_cols[1]: |
| | most_likely = results.get('most_likely_scenario', {}) |
| | st.metric( |
| | "Most Likely", |
| | f"#{most_likely.get('id', 1)}" |
| | ) |
| | |
| | with metric_cols[2]: |
| | prob = results.get('overall_collision_probability', 0) * 100 |
| | st.metric( |
| | "Collision Certainty", |
| | f"{prob:.1f}%" |
| | ) |
| | |
| | with metric_cols[3]: |
| | fault = results.get('preliminary_fault_assessment', {}) |
| | st.metric( |
| | "Primary Factor", |
| | fault.get('primary_factor', 'Unknown').replace('_', ' ').title()[:15] |
| | ) |
| | |
| | |
| | st.write("**Top Scenarios:**") |
| | for i, scenario in enumerate(scenarios[:3], 1): |
| | prob_pct = scenario['probability'] * 100 |
| | st.write(f"{i}. **{scenario['accident_type'].replace('_', ' ').title()}** - {prob_pct:.1f}%") |
| | |
| | |
| | st.markdown("---") |
| | col1, col2, col3 = st.columns([1, 2, 1]) |
| | with col1: |
| | if st.button("β Back to Evidence"): |
| | st.session_state.current_step = 5 |
| | st.rerun() |
| | |
| | with col3: |
| | if st.session_state.analysis_results: |
| | if st.button("View Full Results β", type="primary"): |
| | st.session_state.current_step = 7 |
| | st.rerun() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | def render_step_7_results(): |
| | """Render the results step.""" |
| | |
| | st.header("π Step 7: Analysis Results") |
| | |
| | if not st.session_state.analysis_results: |
| | st.warning("No analysis results available. Please run the analysis first.") |
| | if st.button("β Go to Analysis"): |
| | st.session_state.current_step = 6 |
| | st.rerun() |
| | return |
| | |
| | |
| | render_results() |
| | |
| | |
| | col1, col2, col3 = st.columns([1, 2, 1]) |
| | with col1: |
| | if st.button("β Back to Analysis"): |
| | st.session_state.current_step = 6 |
| | st.rerun() |
| | |
| | with col3: |
| | if st.button("π Start New Analysis"): |
| | |
| | for key in list(st.session_state.keys()): |
| | del st.session_state[key] |
| | st.rerun() |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | if __name__ == "__main__": |
| | main() |
| |
|