Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 5 additions & 158 deletions backend/alertsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def fetch_gis_alert_data():
)

from flask_cors import CORS
from backend.risk_calculator import calculate_risk_scores, get_detailed_risks, generate_alerts

# =========================================================
# APP CONFIG
Expand Down Expand Up @@ -308,98 +309,13 @@ def get_weather_insights():
# RISK CALCULATIONS
# ----------------------------------------------------

flood_risk_metric = round(
min(
1.0,
(
rain_val * 0.6 +
humid_val * 0.3 +
wind_val * 0.1
) / 100
),
3
)

heat_risk_metric = round(
min(
1.0,
(
max(temp_val - 25, 0) * 2 +
humid_val * 0.3
) / 100
),
3
)

wildfire_risk_metric = round(
min(
1.0,
(
max(temp_val - 32, 0) * 1.5 +
(100 - humid_val) * 0.5 +
wind_val * 0.2
) / 100
),
3
)

cyclone_risk_metric = round(
min(
1.0,
(
wind_val * 1.5 +
rain_val * 0.5
) / 100
),
3
)

drought_risk_metric = round(
min(
1.0,
(
max(temp_val - 28, 0) +
(100 - humid_val)
) / 100
),
3
)
current_risk_scores = calculate_risk_scores(temp_val, humid_val, wind_val, rain_val)

# ----------------------------------------------------
# ALERTS
# ----------------------------------------------------

calculated_alerts = []

if flood_risk_metric >= 0.6:
calculated_alerts.append(
"⚠ High Flood Risk Detected"
)

if heat_risk_metric >= 0.6:
calculated_alerts.append(
"🔥 Heatwave Conditions Possible"
)

if wildfire_risk_metric >= 0.6:
calculated_alerts.append(
"🌲 Elevated Wildfire Risk"
)

if cyclone_risk_metric >= 0.6:
calculated_alerts.append(
"🌀 Cyclone Risk Detected"
)

if drought_risk_metric >= 0.6:
calculated_alerts.append(
"☀ Drought Conditions Possible"
)

if not calculated_alerts:
calculated_alerts.append(
"✅ No major climate threats detected."
)
calculated_alerts = generate_alerts(current_risk_scores)

# ----------------------------------------------------
# FORECAST GENERATION
Expand Down Expand Up @@ -430,60 +346,7 @@ def get_weather_insights():
"humidity": day_humidity,
"rainfall": round(day_rain, 1),
"wind_speed": day_wind,
"risks": {
"flood_risk": round(
min(
1.0,
(
day_rain * 0.6 +
day_humidity * 0.3 +
day_wind * 0.1
) / 100
),
3
),
"heat_risk": round(
min(
1.0,
(
max(day_temp - 25, 0) * 2 +
day_humidity * 0.3
) / 100
),
3
),
"wildfire_risk": round(
min(
1.0,
(
max(day_temp - 32, 0) * 1.5 +
(100 - day_humidity) * 0.5 +
day_wind * 0.2
) / 100
),
3
),
"cyclone_risk": round(
min(
1.0,
(
day_wind * 1.5 +
day_rain * 0.5
) / 100
),
3
),
"drought_risk": round(
min(
1.0,
(
max(day_temp - 28, 0) +
(100 - day_humidity)
) / 100
),
3
)
}
"risks": calculate_risk_scores(day_temp, day_humidity, day_wind, day_rain)
})

return jsonify({
Expand All @@ -505,23 +368,7 @@ def get_weather_insights():
"wind_speed": wind_val
},

"risks": {
"flood_risk": round(flood_risk_metric, 3),
"flood_risk_confidence": round(flood_risk_metric * 100, 1),
"flood_risk_level": "HIGH" if flood_risk_metric >= 0.6 else "MEDIUM" if flood_risk_metric >= 0.3 else "LOW",
"heat_risk": round(heat_risk_metric, 3),
"heat_risk_confidence": round(heat_risk_metric * 100, 1),
"heat_risk_level": "HIGH" if heat_risk_metric >= 0.6 else "MEDIUM" if heat_risk_metric >= 0.3 else "LOW",
"wildfire_risk": round(wildfire_risk_metric, 3),
"wildfire_risk_confidence": round(wildfire_risk_metric * 100, 1),
"wildfire_risk_level": "HIGH" if wildfire_risk_metric >= 0.6 else "MEDIUM" if wildfire_risk_metric >= 0.3 else "LOW",
"cyclone_risk": round(cyclone_risk_metric, 3),
"cyclone_risk_confidence": round(cyclone_risk_metric * 100, 1),
"cyclone_risk_level": "HIGH" if cyclone_risk_metric >= 0.6 else "MEDIUM" if cyclone_risk_metric >= 0.3 else "LOW",
"drought_risk": round(drought_risk_metric, 3),
"drought_risk_confidence": round(drought_risk_metric * 100, 1),
"drought_risk_level": "HIGH" if drought_risk_metric >= 0.6 else "MEDIUM" if drought_risk_metric >= 0.3 else "LOW",
},
"risks": get_detailed_risks(current_risk_scores),

"forecast": forecast,

Expand Down
55 changes: 55 additions & 0 deletions backend/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pytest
from backend.alertsystem import app

@pytest.fixture
def client():
app.config["TESTING"] = True
with app.test_client() as client:
yield client

@pytest.fixture
def mock_openweather_geo():
return [
{
"name": "Delhi",
"lat": 28.6139,
"lon": 77.2090,
"country": "IN",
"state": "Delhi"
}
]

@pytest.fixture
def mock_openweather_weather():
return {
"main": {
"temp": 35.0,
"humidity": 40
},
"wind": {
"speed": 5.0
},
"rain": {
"1h": 0
}
}

@pytest.fixture
def mock_openweather_forecast():
return {
"list": [
{
"dt_txt": "2023-10-01 12:00:00",
"main": {
"temp": 36.0,
"humidity": 45
},
"wind": {
"speed": 4.5
},
"rain": {
"3h": 0
}
}
] * 40 # Just enough items to simulate API response
}
46 changes: 46 additions & 0 deletions backend/risk_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
def calculate_risk_scores(temp_val, humid_val, wind_val, rain_val):
flood_risk_metric = round(min(1.0, (rain_val * 0.6 + humid_val * 0.3 + wind_val * 0.1) / 100), 3)
heat_risk_metric = round(min(1.0, (max(temp_val - 25, 0) * 2 + humid_val * 0.3) / 100), 3)
wildfire_risk_metric = round(min(1.0, (max(temp_val - 32, 0) * 1.5 + (100 - humid_val) * 0.5 + wind_val * 0.2) / 100), 3)
cyclone_risk_metric = round(min(1.0, (wind_val * 1.5 + rain_val * 0.5) / 100), 3)
drought_risk_metric = round(min(1.0, (max(temp_val - 28, 0) + (100 - humid_val)) / 100), 3)

return {
"flood_risk": flood_risk_metric,
"heat_risk": heat_risk_metric,
"wildfire_risk": wildfire_risk_metric,
"cyclone_risk": cyclone_risk_metric,
"drought_risk": drought_risk_metric,
}

def get_risk_level(metric):
if metric >= 0.6:
return "HIGH"
elif metric >= 0.3:
return "MEDIUM"
return "LOW"

def get_detailed_risks(scores):
details = {}
for risk_type, metric in scores.items():
details[risk_type] = metric
details[f"{risk_type}_confidence"] = round(metric * 100, 1)
details[f"{risk_type}_level"] = get_risk_level(metric)
return details

def generate_alerts(scores):
alerts = []
if scores["flood_risk"] >= 0.6:
alerts.append("⚠ High Flood Risk Detected")
if scores["heat_risk"] >= 0.6:
alerts.append("🔥 Heatwave Conditions Possible")
if scores["wildfire_risk"] >= 0.6:
alerts.append("🌲 Elevated Wildfire Risk")
if scores["cyclone_risk"] >= 0.6:
alerts.append("🌀 Cyclone Risk Detected")
if scores["drought_risk"] >= 0.6:
alerts.append("☀ Drought Conditions Possible")

if not alerts:
alerts.append("✅ No major climate threats detected.")
return alerts
61 changes: 61 additions & 0 deletions backend/test_chatbot_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import pytest
import json

def test_chatbot_api_missing_message(client):
response = client.post("/chatbot", json={})
assert response.status_code == 400
data = response.get_json()
assert data["success"] is False
assert "Please provide a message" in data["message"]

def test_chatbot_api_greeting(client):
response = client.post("/chatbot", json={"message": "hello"})
assert response.status_code == 200
data = response.get_json()
assert data["success"] is True
assert "response" in data
assert any(phrase in data["response"] for phrase in ["Hello", "Hi", "Greetings"])

def test_chatbot_api_knowledge_base(client):
response = client.post("/chatbot", json={"message": "what is a flood?"})
assert response.status_code == 200
data = response.get_json()
assert data["success"] is True
assert "flood" in data["response"].lower() or "submerge" in data["response"].lower()

def test_chatbot_api_with_context(client):
payload = {
"message": "is it safe?",
"context": {
"location": {"city": "Mumbai", "state": "MH", "country": "IN"},
"weather": {"temperature": 32, "humidity": 85, "rainfall": 120, "wind_speed": 15},
"risks": {
"flood_risk": 0.85,
"heat_risk": 0.2,
"wildfire_risk": 0.05,
"cyclone_risk": 0.3,
"drought_risk": 0.1
}
}
}
response = client.post("/chatbot", json=payload)
assert response.status_code == 200
data = response.get_json()
assert data["success"] is True
# The chatbot should mention the highest risk (Flood) based on context
assert "Flood" in data["response"]
assert "0.850" in data["response"]

def test_chatbot_api_unknown_topic(client):
response = client.post("/chatbot", json={"message": "how do I cook pasta?"})
assert response.status_code == 200
data = response.get_json()
assert data["success"] is True
# The chatbot should gracefully handle unknown topic
assert isinstance(data["response"], str) and len(data["response"]) > 0

def test_chatbot_api_empty_payload(client):
response = client.post("/chatbot", data="")
assert response.status_code == 400
data = response.get_json()
assert data["success"] is False
Loading