Droughts_OKC / app.py
Navya-Sree's picture
Update app.py
34af337 verified
Raw
History Blame Contribute Delete
24.9 kB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Oklahoma Damage Assessment System with Comprehensive Sentiment Analysis
Optimized for Hugging Face Spaces deployment
"""
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
import random
import json
from typing import Dict, List, Tuple, Optional
import time
import os
# Hugging Face optimization
os.environ['STREAMLIT_BROWSER_GATHER_USAGE_STATS'] = 'false'
# Page configuration - MUST be first Streamlit command
st.set_page_config(
page_title="πŸŒͺ️ Oklahoma Damage Assessment",
page_icon="⚠️",
layout="wide",
initial_sidebar_state="expanded",
menu_items={
'Get Help': 'https://github.com/your-repo/oklahoma-damage-assessment',
'Report a bug': 'https://github.com/your-repo/oklahoma-damage-assessment/issues',
'About': "# Oklahoma Damage Assessment System\nComprehensive disaster impact analysis with sentiment analysis!"
}
)
# Enhanced CSS for better Hugging Face compatibility
def load_css():
st.markdown("""
<style>
.main-header {
background: linear-gradient(135deg, #ff7b7b 0%, #d4526e 100%);
color: white;
padding: 2rem;
border-radius: 15px;
text-align: center;
margin-bottom: 2rem;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
}
.sentiment-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1.5rem;
border-radius: 15px;
margin: 1rem 0;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
}
.metric-box {
background: rgba(255, 255, 255, 0.9);
border: 1px solid #dee2e6;
padding: 1rem;
border-radius: 10px;
text-align: center;
margin: 0.5rem 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.damage-alert {
background: linear-gradient(135deg, #ff9a56 0%, #ff6b35 100%);
border-left: 4px solid #ff4500;
padding: 1.5rem;
margin: 1rem 0;
border-radius: 10px;
color: white;
box-shadow: 0 4px 15px rgba(255, 107, 53, 0.4);
}
.psychological-panel {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
padding: 2rem;
border-radius: 15px;
margin: 1rem 0;
border: 1px solid rgba(255, 255, 255, 0.3);
}
.stMetric {
background: white;
padding: 1rem;
border-radius: 10px;
border: 1px solid #e0e0e0;
}
/* Fix for Hugging Face iframe */
.main .block-container {
padding-top: 1rem;
padding-bottom: 1rem;
}
/* Responsive design */
@media (max-width: 768px) {
.main-header h1 { font-size: 1.5rem; }
.sentiment-card { padding: 1rem; }
}
</style>
""", unsafe_allow_html=True)
# Data Management with Closures (Simplified for HF)
@st.cache_data
def get_location_data():
"""Get Oklahoma location data with closure pattern"""
counties = [
'Oklahoma', 'Tulsa', 'Cleveland', 'Canadian', 'Comanche', 'Garfield',
'Rogers', 'Washington', 'Pottawatomie', 'Creek', 'Kay', 'Carter',
'Payne', 'McClain', 'Grady', 'Stephens', 'Pontotoc', 'Lincoln'
]
cities = [
'Oklahoma City', 'Tulsa', 'Norman', 'Lawton', 'Edmond', 'Moore',
'Midwest City', 'Enid', 'Stillwater', 'Muskogee', 'Bartlesville',
'Owasso', 'Shawnee', 'Yukon', 'Ardmore', 'Ponca City'
]
return {
'counties': counties,
'cities': cities
}
@st.cache_data
def get_sentiment_lexicon():
"""Get emotional lexicon for sentiment analysis"""
return {
'devastating': {'sentiment': -0.95, 'emotion': 'despair', 'intensity': 'extreme'},
'catastrophic': {'sentiment': -0.90, 'emotion': 'terror', 'intensity': 'extreme'},
'destroyed': {'sentiment': -0.85, 'emotion': 'grief', 'intensity': 'high'},
'damaged': {'sentiment': -0.65, 'emotion': 'distress', 'intensity': 'moderate'},
'threatened': {'sentiment': -0.60, 'emotion': 'anxiety', 'intensity': 'moderate'},
'displaced': {'sentiment': -0.75, 'emotion': 'fear', 'intensity': 'high'},
'injured': {'sentiment': -0.80, 'emotion': 'pain', 'intensity': 'high'},
'affected': {'sentiment': -0.45, 'emotion': 'concern', 'intensity': 'moderate'},
'recovering': {'sentiment': 0.50, 'emotion': 'hope', 'intensity': 'moderate'},
'resilient': {'sentiment': 0.70, 'emotion': 'strength', 'intensity': 'high'},
'helping': {'sentiment': 0.60, 'emotion': 'compassion', 'intensity': 'moderate'},
'support': {'sentiment': 0.55, 'emotion': 'comfort', 'intensity': 'moderate'}
}
def analyze_sentiment(text):
"""Simplified sentiment analysis"""
lexicon = get_sentiment_lexicon()
words = text.lower().split()
sentiments = []
emotions = {}
for word in words:
clean_word = ''.join(c for c in word if c.isalnum())
if clean_word in lexicon:
data = lexicon[clean_word]
sentiments.append(data['sentiment'])
emotions[data['emotion']] = emotions.get(data['emotion'], 0) + 1
avg_sentiment = np.mean(sentiments) if sentiments else 0.0
dominant_emotion = max(emotions.items(), key=lambda x: x[1])[0] if emotions else 'neutral'
# Classification
if avg_sentiment <= -0.6:
classification = 'highly_negative'
elif avg_sentiment <= -0.3:
classification = 'negative'
elif avg_sentiment <= 0.3:
classification = 'neutral'
else:
classification = 'positive'
return {
'sentiment_score': avg_sentiment,
'classification': classification,
'dominant_emotion': dominant_emotion,
'emotion_distribution': emotions
}
def generate_damage_data(location):
"""Generate comprehensive damage data"""
damage_types = [
{'id': 'drought', 'name': 'Drought', 'color': '#ff8c00', 'icon': '🌡'},
{'id': 'flood', 'name': 'Flood', 'color': '#1e90ff', 'icon': '🌊'},
{'id': 'tornado', 'name': 'Tornado', 'color': '#dc143c', 'icon': 'πŸŒͺ️'},
{'id': 'wildfire', 'name': 'Wildfire', 'color': '#ff4500', 'icon': 'πŸ”₯'},
{'id': 'hail', 'name': 'Hail Storm', 'color': '#87ceeb', 'icon': '🌧️'},
{'id': 'wind', 'name': 'High Winds', 'color': '#a9a9a9', 'icon': 'πŸ’¨'}
]
severity_levels = [
{'id': 1, 'name': 'Minimal', 'color': '#fff3cd'},
{'id': 2, 'name': 'Minor', 'color': '#ffeeba'},
{'id': 3, 'name': 'Moderate', 'color': '#ffdf7e'},
{'id': 4, 'name': 'Severe', 'color': '#ffcc00'},
{'id': 5, 'name': 'Extreme', 'color': '#ff8c00'}
]
damage_type = random.choice(damage_types)
severity = random.choice(severity_levels)
impact_templates = {
'drought': "Agricultural operations in {location} are experiencing {severity_adj} stress, causing {emotion} in farming communities",
'flood': "Flash flooding in {location} has {severity_adj} damaged infrastructure, creating {emotion} among residents",
'tornado': "Tornado touchdown in {location} has {severity_adj} damaged structures, leaving survivors with {emotion}",
'wildfire': "Wildfire near {location} has {severity_adj} threatened areas, forcing evacuations and causing {emotion}",
'hail': "Severe hail storm in {location} has {severity_adj} damaged property, creating {emotion} among homeowners",
'wind': "High winds in {location} have {severity_adj} damaged infrastructure, causing {emotion} in the community"
}
severity_adjectives = {
1: 'slightly', 2: 'moderately', 3: 'significantly', 4: 'severely', 5: 'catastrophically'
}
emotions = {
1: 'mild concern', 2: 'growing anxiety', 3: 'widespread distress',
4: 'severe trauma', 5: 'overwhelming devastation'
}
impact_text = impact_templates[damage_type['id']].format(
location=location,
severity_adj=severity_adjectives[severity['id']],
emotion=emotions[severity['id']]
)
base_multiplier = severity['id']
population_factor = 1.5 if 'City' in location else 1.0
assessments = {
'people': {
'injured': int(random.randint(0, 20) * base_multiplier),
'displaced': int(random.randint(0, 100) * base_multiplier),
'affected': int(random.randint(100, 1000) * base_multiplier * population_factor),
'psychologicalCasualties': int(random.randint(50, 300) * base_multiplier),
'communityMorale': max(20, 100 - (base_multiplier * random.randint(15, 25)))
},
'property': {
'homes_damaged': int(random.randint(0, 100) * base_multiplier),
'businesses_affected': int(random.randint(0, 25) * base_multiplier),
'estimated_cost': int(random.randint(100000, 5000000) * base_multiplier)
},
'infrastructure': {
'roads_damaged_miles': round(random.uniform(1, 30) * base_multiplier, 1),
'power_outages': int(random.randint(500, 10000) * base_multiplier),
'water_systems_affected': int(random.randint(0, 10) * base_multiplier)
},
'agriculture': {
'crop_loss_acres': int(random.randint(500, 10000) * base_multiplier),
'livestock_loss': int(random.randint(0, 200) * base_multiplier),
'estimated_cost': int(random.randint(100000, 2000000) * base_multiplier)
}
}
return {
'location': location,
'damageType': damage_type,
'severity': severity,
'impact': impact_text,
'assessments': assessments,
'timestamp': datetime.now().isoformat()
}
def calculate_psychological_impact(assessments):
"""Calculate psychological impact score"""
people_impact = min(1.0, (
assessments['people']['injured'] * 0.1 +
assessments['people']['displaced'] * 0.05 +
assessments['people']['affected'] * 0.0005
))
property_impact = min(1.0, assessments['property']['homes_damaged'] * 0.01)
economic_impact = min(1.0, assessments['property']['estimated_cost'] / 10000000)
total_impact = (people_impact * 0.5 + property_impact * 0.3 + economic_impact * 0.2)
if total_impact < 0.25:
level = 'minimal'
elif total_impact < 0.5:
level = 'moderate'
elif total_impact < 0.75:
level = 'severe'
else:
level = 'critical'
return {
'total_score': total_impact,
'level': level,
'components': {
'people_impact': people_impact,
'property_impact': property_impact,
'economic_impact': economic_impact
}
}
def assess_trauma_risk(assessments):
"""Assess trauma risk indicators"""
indicators = {
'direct_victimization': assessments['people']['injured'] > 0,
'home_destruction': assessments['property']['homes_damaged'] > 0,
'economic_devastation': assessments['property']['estimated_cost'] > 1000000,
'social_disruption': assessments['people']['displaced'] > 50,
'infrastructure_collapse': assessments['infrastructure']['power_outages'] > 5000,
'community_breakdown': assessments['people']['communityMorale'] < 50
}
active_indicators = sum(indicators.values())
risk_score = active_indicators / len(indicators)
if risk_score < 0.3:
risk_level = 'low'
elif risk_score < 0.6:
risk_level = 'moderate'
else:
risk_level = 'high'
return {
'indicators': indicators,
'active_count': active_indicators,
'risk_score': risk_score,
'risk_level': risk_level
}
def calculate_overall_mental_health(sentiment_data, psych_impact, trauma_risk, community_morale):
"""Calculate overall mental health score"""
sentiment_score = (sentiment_data['sentiment_score'] + 1) / 2 # Normalize to 0-1
psychological_score = 1 - psych_impact['total_score']
trauma_score = 1 - trauma_risk['risk_score']
morale_score = community_morale / 100
overall_score = (
sentiment_score * 0.25 +
psychological_score * 0.35 +
trauma_score * 0.25 +
morale_score * 0.15
)
if overall_score > 0.75:
level = 'excellent'
elif overall_score > 0.6:
level = 'good'
elif overall_score > 0.4:
level = 'concerning'
else:
level = 'critical'
return {
'overall_score': overall_score,
'level': level,
'components': {
'sentiment': sentiment_score,
'psychological': psychological_score,
'trauma': trauma_score,
'morale': morale_score
}
}
def main():
"""Main application function"""
# Load CSS
load_css()
# Header
st.markdown("""
<div class="main-header">
<h1>⚠️ Oklahoma Damage Assessment System</h1>
<h2>🧠 Comprehensive Sentiment & Psychological Analysis</h2>
<p>Advanced disaster impact analysis with emotional and mental health assessment</p>
</div>
""", unsafe_allow_html=True)
# Sidebar controls
st.sidebar.title("πŸŽ›οΈ Assessment Controls")
st.sidebar.markdown("---")
location_data = get_location_data()
# Location selection
location_type = st.sidebar.radio("πŸ“ Location Type", ["County", "City"])
if location_type == "County":
selected_location = st.sidebar.selectbox("πŸ›οΈ Select County", location_data['counties'])
else:
selected_location = st.sidebar.selectbox("πŸ™οΈ Select City", location_data['cities'])
# Analysis options
st.sidebar.markdown("---")
st.sidebar.subheader("πŸ”§ Analysis Options")
show_detailed = st.sidebar.checkbox("Show Detailed Analysis", value=True)
show_trauma = st.sidebar.checkbox("Show Trauma Assessment", value=True)
show_timeline = st.sidebar.checkbox("Show Recovery Timeline", value=True)
# Generate new assessment button
if st.sidebar.button("πŸ”„ Generate New Assessment", type="primary"):
st.rerun()
# Generate data
if 'damage_data' not in st.session_state or st.sidebar.button("πŸ”„ Refresh Data"):
st.session_state.damage_data = generate_damage_data(selected_location)
damage_data = st.session_state.damage_data
# Update location if changed
if damage_data['location'] != selected_location:
damage_data = generate_damage_data(selected_location)
st.session_state.damage_data = damage_data
# Perform analysis
sentiment_data = analyze_sentiment(damage_data['impact'])
psych_impact = calculate_psychological_impact(damage_data['assessments'])
trauma_risk = assess_trauma_risk(damage_data['assessments'])
overall_mental_health = calculate_overall_mental_health(
sentiment_data, psych_impact, trauma_risk,
damage_data['assessments']['people']['communityMorale']
)
# Main damage alert
damage_type = damage_data['damageType']
severity = damage_data['severity']
st.markdown(f"""
<div class="damage-alert">
<h3>{damage_type['icon']} {damage_data['location']} Impact Assessment</h3>
<h4>{damage_type['name']} - {severity['name']} Severity</h4>
<p><strong>Impact:</strong> {damage_data['impact']}</p>
</div>
""", unsafe_allow_html=True)
# Mental health overview
overall_score = overall_mental_health['overall_score']
level = overall_mental_health['level']
color_map = {
'excellent': '#28a745',
'good': '#17a2b8',
'concerning': '#ffc107',
'critical': '#dc3545'
}
color = color_map.get(level, '#6c757d')
st.markdown(f"""
<div class="sentiment-card">
<h4>🎯 Overall Community Mental Health Assessment</h4>
<div style="display: flex; align-items: center; justify-content: space-between;">
<div>
<h2 style="color: white; margin: 0;">{overall_score:.1%}</h2>
<p style="margin: 0; font-size: 1.2em;">Mental Health Score</p>
</div>
<div style="text-align: right;">
<h3 style="color: white; margin: 0;">{level.upper()}</h3>
<p style="margin: 0;">Status Level</p>
</div>
</div>
</div>
""", unsafe_allow_html=True)
# Key metrics
st.subheader("πŸ“Š Key Impact Metrics")
col1, col2, col3, col4 = st.columns(4)
assessments = damage_data['assessments']
with col1:
st.metric("πŸš‘ Injured", assessments['people']['injured'])
st.metric("🧠 Psychological", assessments['people']['psychologicalCasualties'])
with col2:
st.metric("🏠 Homes Damaged", assessments['property']['homes_damaged'])
st.metric("πŸš— Property Cost", f"${assessments['property']['estimated_cost']:,}")
with col3:
st.metric("πŸ‘₯ Affected", assessments['people']['affected'])
st.metric("🏠 Displaced", assessments['people']['displaced'])
with col4:
st.metric("πŸ’ͺ Community Morale", f"{assessments['people']['communityMorale']}%")
st.metric("⚑ Power Outages", f"{assessments['infrastructure']['power_outages']:,}")
# Detailed analysis sections
if show_detailed:
st.subheader("πŸ“ˆ Detailed Sentiment Analysis")
col1, col2 = st.columns(2)
with col1:
st.markdown("**Text Sentiment Analysis:**")
classification = sentiment_data['classification']
class_colors = {
'highly_negative': '#dc3545',
'negative': '#fd7e14',
'neutral': '#6c757d',
'positive': '#28a745'
}
st.markdown(f"""
<div style="background: {class_colors.get(classification, '#6c757d')}20;
border-left: 4px solid {class_colors.get(classification, '#6c757d')};
padding: 1rem; border-radius: 5px;">
<strong>Classification:</strong> {classification.replace('_', ' ').title()}<br>
<strong>Sentiment Score:</strong> {sentiment_data['sentiment_score']:.3f}<br>
<strong>Dominant Emotion:</strong> {sentiment_data['dominant_emotion'].title()}
</div>
""", unsafe_allow_html=True)
with col2:
st.markdown("**Psychological Impact Analysis:**")
level_colors = {
'minimal': '#28a745',
'moderate': '#ffc107',
'severe': '#fd7e14',
'critical': '#dc3545'
}
level_color = level_colors.get(psych_impact['level'], '#6c757d')
st.markdown(f"""
<div style="background: {level_color}20;
border-left: 4px solid {level_color};
padding: 1rem; border-radius: 5px;">
<strong>Impact Level:</strong> {psych_impact['level'].title()}<br>
<strong>Total Score:</strong> {psych_impact['total_score']:.3f}<br>
<strong>Primary Factors:</strong> People Impact, Property Loss, Economic Stress
</div>
""", unsafe_allow_html=True)
# Trauma assessment
if show_trauma:
st.subheader("⚠️ Trauma Risk Assessment")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("⚠️ Risk Level", trauma_risk['risk_level'].upper())
with col2:
st.metric("🚨 Active Indicators", f"{trauma_risk['active_count']}/6")
with col3:
st.metric("πŸ“Š Risk Score", f"{trauma_risk['risk_score']:.1%}")
# Trauma indicators
st.markdown("**Trauma Risk Indicators:**")
indicators_data = []
for indicator, active in trauma_risk['indicators'].items():
indicators_data.append({
'Indicator': indicator.replace('_', ' ').title(),
'Status': 'Active' if active else 'Inactive',
'Risk_Level': 1 if active else 0
})
indicators_df = pd.DataFrame(indicators_data)
if not indicators_df.empty:
fig = px.bar(
indicators_df,
x='Indicator',
y='Risk_Level',
color='Status',
title="Trauma Risk Indicators",
color_discrete_map={'Active': '#dc3545', 'Inactive': '#28a745'}
)
fig.update_layout(height=400, xaxis_tickangle=-45)
st.plotly_chart(fig, use_container_width=True)
# Recovery timeline
if show_timeline:
st.subheader("πŸ“… Recovery Timeline")
timeline_phases = [
("Immediate Response", "0-72 hours", "Acute stress management"),
("Short-term Recovery", "3 days - 4 weeks", "PTSD prevention"),
("Medium-term Adjustment", "1-6 months", "Adaptation support"),
("Long-term Recovery", "6+ months", "Post-traumatic growth")
]
for i, (phase, duration, description) in enumerate(timeline_phases, 1):
with st.expander(f"Phase {i}: {phase} ({duration})"):
st.write(f"**Focus:** {description}")
if i == 1:
st.write(f"β€’ Expected acute stress in {trauma_risk['risk_score']:.1%} of population")
st.write("β€’ Deploy crisis counselors immediately")
elif i == 2:
st.write(f"β€’ PTSD screening for {trauma_risk['active_count'] * 100} individuals")
st.write("β€’ Establish support groups")
elif i == 3:
st.write("β€’ Economic counseling for affected households")
st.write("β€’ Long-term housing solutions")
else:
st.write(f"β€’ Post-traumatic growth potential: {overall_score:.1%}")
st.write("β€’ Community resilience building")
# Mental health resources
st.subheader("πŸ₯ Mental Health Resource Recommendations")
total_affected = assessments['people']['affected']
psychological_casualties = assessments['people']['psychologicalCasualties']
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("**🚨 Immediate Response**")
counselors_needed = max(5, psychological_casualties // 50)
st.write(f"β€’ Deploy {counselors_needed} crisis counselors")
st.write("β€’ Activate 24/7 mental health hotline")
st.write("β€’ Set up mobile crisis units")
with col2:
st.markdown("**⏰ Short-term Support**")
support_groups = max(3, total_affected // 200)
st.write(f"β€’ Establish {support_groups} support groups")
st.write("β€’ Trauma screening programs")
st.write("β€’ Children's mental health services")
with col3:
st.markdown("**🌱 Long-term Recovery**")
st.write("β€’ Community resilience workshops")
st.write("β€’ PTSD treatment programs")
st.write("β€’ Economic counseling services")
# Data export
st.sidebar.markdown("---")
st.sidebar.subheader("πŸ“Š Export Data")
if st.sidebar.button("πŸ“₯ Export Assessment"):
export_data = {
'damage_assessment': damage_data,
'sentiment_analysis': sentiment_data,
'psychological_impact': psych_impact,
'trauma_assessment': trauma_risk,
'mental_health_score': overall_mental_health,
'generated_at': datetime.now().isoformat()
}
st.sidebar.download_button(
label="Download JSON Report",
data=json.dumps(export_data, indent=2, default=str),
file_name=f"oklahoma_assessment_{selected_location.replace(' ', '_')}.json",
mime="application/json"
)
# Footer
st.markdown("---")
st.markdown("""
<div style="text-align: center; padding: 2rem; background: #f8f9fa; border-radius: 10px;">
<h4>⚠️ Oklahoma Damage Assessment System</h4>
<p>🧠 Comprehensive Sentiment & Psychological Analysis Platform</p>
<p><small>Last Updated: {}</small></p>
</div>
""".format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')), unsafe_allow_html=True)
if __name__ == "__main__":
main()