Added flask server for API endpoints for MongoDB Agent and LLM Summarization
This commit is contained in:
Binary file not shown.
Binary file not shown.
354
llm/api/INTEGRATION_GUIDE.md
Normal file
354
llm/api/INTEGRATION_GUIDE.md
Normal file
@@ -0,0 +1,354 @@
|
||||
# Flask API Integration Guide for Next.js
|
||||
|
||||
## 🚀 Flask API Server
|
||||
|
||||
Your Flask API server is now ready and running on **`http://localhost:5001`**
|
||||
|
||||
### 🔌 Available Endpoints
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/health` | Health check endpoint |
|
||||
| `GET` | `/api/weather?lat=X&lon=Y` | Get weather conditions |
|
||||
| `POST` | `/api/analyze-crashes` | Analyze crash patterns at location |
|
||||
| `POST` | `/api/find-safe-route` | Find safest route between points |
|
||||
| `POST` | `/api/get-single-route` | Get single route with safety analysis |
|
||||
|
||||
---
|
||||
|
||||
## 📦 Starting the Server
|
||||
|
||||
```bash
|
||||
cd /path/to/VTHacks13/llm
|
||||
python api/flask_server.py
|
||||
```
|
||||
|
||||
The server will start on `http://localhost:5001` with the following services:
|
||||
- ✅ MongoDB connection to crash database
|
||||
- ✅ Route safety analysis
|
||||
- ✅ Weather API integration (Open-Meteo)
|
||||
- ✅ Gemini AI for safety recommendations
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Next.js Integration
|
||||
|
||||
### 1. Install Dependencies
|
||||
|
||||
```bash
|
||||
npm install axios # or use fetch API
|
||||
```
|
||||
|
||||
### 2. Create API Client
|
||||
|
||||
Create `lib/api-client.js`:
|
||||
|
||||
```javascript
|
||||
const API_BASE_URL = 'http://localhost:5001/api';
|
||||
|
||||
// Health Check
|
||||
export async function checkAPIHealth() {
|
||||
const response = await fetch(`${API_BASE_URL}/health`);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Weather API
|
||||
export async function getWeather(lat, lon) {
|
||||
const response = await fetch(`${API_BASE_URL}/weather?lat=${lat}&lon=${lon}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to fetch weather');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Crash Analysis
|
||||
export async function analyzeCrashes(lat, lon, radius = 1.0) {
|
||||
const response = await fetch(`${API_BASE_URL}/analyze-crashes`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ lat, lon, radius })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to analyze crashes');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Safe Route Finding
|
||||
export async function findSafeRoute(startLat, startLon, endLat, endLon) {
|
||||
const response = await fetch(`${API_BASE_URL}/find-safe-route`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
start_lat: startLat,
|
||||
start_lon: startLon,
|
||||
end_lat: endLat,
|
||||
end_lon: endLon
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to find safe route');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Single Route
|
||||
export async function getSingleRoute(startLat, startLon, endLat, endLon, profile = 'driving') {
|
||||
const response = await fetch(`${API_BASE_URL}/get-single-route`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
start_lat: startLat,
|
||||
start_lon: startLon,
|
||||
end_lat: endLat,
|
||||
end_lon: endLon,
|
||||
profile
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to get route');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Example React Component
|
||||
|
||||
Create `components/SafetyAnalysis.jsx`:
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react';
|
||||
import { analyzeCrashes, findSafeRoute, getWeather } from '../lib/api-client';
|
||||
|
||||
export default function SafetyAnalysis() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [results, setResults] = useState(null);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const handleAnalyze = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// Example: Analyze crashes around Virginia Tech
|
||||
const lat = 37.2284;
|
||||
const lon = -80.4234;
|
||||
const radius = 2.0;
|
||||
|
||||
// Get crash analysis
|
||||
const crashData = await analyzeCrashes(lat, lon, radius);
|
||||
|
||||
// Get safe route (Virginia Tech to Downtown Blacksburg)
|
||||
const routeData = await findSafeRoute(lat, lon, 37.2297, -80.4139);
|
||||
|
||||
// Get weather
|
||||
const weatherData = await getWeather(lat, lon);
|
||||
|
||||
setResults({
|
||||
crashes: crashData,
|
||||
route: routeData,
|
||||
weather: weatherData
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h2 className="text-2xl font-bold mb-4">Safety Analysis</h2>
|
||||
|
||||
<button
|
||||
onClick={handleAnalyze}
|
||||
disabled={loading}
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Analyzing...' : 'Analyze Safety'}
|
||||
</button>
|
||||
|
||||
{error && (
|
||||
<div className="mt-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded">
|
||||
Error: {error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{results && (
|
||||
<div className="mt-6 space-y-4">
|
||||
{/* Crash Analysis Results */}
|
||||
<div className="p-4 bg-gray-100 rounded">
|
||||
<h3 className="font-bold text-lg">Crash Analysis</h3>
|
||||
<p>Total crashes: {results.crashes.crash_summary.total_crashes}</p>
|
||||
<p>Total casualties: {results.crashes.crash_summary.total_casualties}</p>
|
||||
<p>Weather: {results.crashes.weather.summary}</p>
|
||||
</div>
|
||||
|
||||
{/* Route Results */}
|
||||
<div className="p-4 bg-blue-100 rounded">
|
||||
<h3 className="font-bold text-lg">Safe Route</h3>
|
||||
<p>Distance: {results.route.recommended_route.distance_km.toFixed(1)} km</p>
|
||||
<p>Duration: {results.route.recommended_route.duration_min.toFixed(0)} minutes</p>
|
||||
<p>Crashes nearby: {results.route.recommended_route.crashes_nearby}</p>
|
||||
<p>Safety score: {results.route.recommended_route.safety_score.toFixed(3)}</p>
|
||||
</div>
|
||||
|
||||
{/* Weather Results */}
|
||||
<div className="p-4 bg-green-100 rounded">
|
||||
<h3 className="font-bold text-lg">Current Weather</h3>
|
||||
<p>{results.weather.summary}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Mapbox Integration
|
||||
|
||||
For route visualization:
|
||||
|
||||
```jsx
|
||||
import { useEffect, useRef } from 'react';
|
||||
import mapboxgl from 'mapbox-gl';
|
||||
|
||||
export default function RouteMap({ routeData }) {
|
||||
const mapContainer = useRef(null);
|
||||
const map = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!routeData || map.current) return;
|
||||
|
||||
map.current = new mapboxgl.Map({
|
||||
container: mapContainer.current,
|
||||
style: 'mapbox://styles/mapbox/streets-v12',
|
||||
center: [routeData.recommended_route.coordinates[0][0],
|
||||
routeData.recommended_route.coordinates[0][1]],
|
||||
zoom: 13
|
||||
});
|
||||
|
||||
// Add route to map
|
||||
map.current.on('load', () => {
|
||||
map.current.addSource('route', {
|
||||
'type': 'geojson',
|
||||
'data': {
|
||||
'type': 'Feature',
|
||||
'properties': {},
|
||||
'geometry': routeData.recommended_route.geometry
|
||||
}
|
||||
});
|
||||
|
||||
map.current.addLayer({
|
||||
'id': 'route',
|
||||
'type': 'line',
|
||||
'source': 'route',
|
||||
'layout': {
|
||||
'line-join': 'round',
|
||||
'line-cap': 'round'
|
||||
},
|
||||
'paint': {
|
||||
'line-color': '#3887be',
|
||||
'line-width': 5,
|
||||
'line-opacity': 0.75
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [routeData]);
|
||||
|
||||
return <div ref={mapContainer} className="w-full h-96" />;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Response Formats
|
||||
|
||||
### Crash Analysis Response
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"location": {"lat": 37.2284, "lon": -80.4234},
|
||||
"radius_km": 2.0,
|
||||
"crash_summary": {
|
||||
"total_crashes": 5,
|
||||
"avg_distance_km": 1.2,
|
||||
"severity_breakdown": {"Minor": 3, "Major": 2},
|
||||
"total_casualties": 8
|
||||
},
|
||||
"weather": {
|
||||
"summary": "Clear sky, precipitation 0.0mm/h, wind 5.2 km/h, day"
|
||||
},
|
||||
"safety_analysis": "AI-generated safety report..."
|
||||
}
|
||||
```
|
||||
|
||||
### Safe Route Response
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"recommended_route": {
|
||||
"coordinates": [[lon, lat], [lon, lat], ...],
|
||||
"distance_km": 1.5,
|
||||
"duration_min": 4.2,
|
||||
"geometry": {...}, // GeoJSON for Mapbox
|
||||
"safety_score": 0.234,
|
||||
"crashes_nearby": 2
|
||||
},
|
||||
"safety_analysis": "AI-generated route safety report...",
|
||||
"weather_summary": "Current weather conditions...",
|
||||
"alternative_routes": [...]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing the API
|
||||
|
||||
Run the test script:
|
||||
```bash
|
||||
cd llm/api
|
||||
python test_api.py
|
||||
```
|
||||
|
||||
Or test individual endpoints with curl:
|
||||
```bash
|
||||
# Health check
|
||||
curl http://localhost:5001/api/health
|
||||
|
||||
# Weather
|
||||
curl "http://localhost:5001/api/weather?lat=37.2284&lon=-80.4234"
|
||||
|
||||
# Crash analysis
|
||||
curl -X POST http://localhost:5001/api/analyze-crashes \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"lat": 37.2284, "lon": -80.4234, "radius": 1.0}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
1. **Start Flask Server**: `cd llm && python api/flask_server.py`
|
||||
2. **Test Endpoints**: Use the provided test scripts
|
||||
3. **Integrate with Next.js**: Use the API client code above
|
||||
4. **Add to Your Components**: Import and use the API functions
|
||||
5. **Visualize Routes**: Use Mapbox with the route coordinates
|
||||
|
||||
Your Flask API is ready to bridge your Python AI/safety analysis with your Next.js frontend! 🚀
|
||||
369
llm/api/flask_server.py
Normal file
369
llm/api/flask_server.py
Normal file
@@ -0,0 +1,369 @@
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
import json
|
||||
from bson import ObjectId
|
||||
|
||||
# Since we're now in llm/api/, we need to add the parent directory (llm) to Python path
|
||||
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
# Import our existing modules from the same llm directory
|
||||
try:
|
||||
from gemini_mongo_mateo import (
|
||||
connect_to_mongodb,
|
||||
get_crashes_within_radius_mongodb,
|
||||
analyze_mongodb_crash_patterns,
|
||||
get_current_weather
|
||||
)
|
||||
from gemini_reroute_mateo import SafeRouteAnalyzer, MONGO_URI
|
||||
print("✅ Successfully imported Python modules")
|
||||
except ImportError as e:
|
||||
print(f"❌ Failed to import modules: {e}")
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
def serialize_mongodb_doc(doc):
|
||||
"""Convert MongoDB document to JSON-serializable format"""
|
||||
if doc is None:
|
||||
return None
|
||||
|
||||
if isinstance(doc, list):
|
||||
return [serialize_mongodb_doc(item) for item in doc]
|
||||
|
||||
if isinstance(doc, dict):
|
||||
serialized = {}
|
||||
for key, value in doc.items():
|
||||
if isinstance(value, ObjectId):
|
||||
serialized[key] = str(value)
|
||||
elif isinstance(value, dict):
|
||||
serialized[key] = serialize_mongodb_doc(value)
|
||||
elif isinstance(value, list):
|
||||
serialized[key] = serialize_mongodb_doc(value)
|
||||
else:
|
||||
serialized[key] = value
|
||||
return serialized
|
||||
|
||||
return doc
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app) # Enable CORS for all routes
|
||||
|
||||
# Initialize the route analyzer
|
||||
route_analyzer = SafeRouteAnalyzer(MONGO_URI)
|
||||
|
||||
# Initialize MongoDB connection for crash analysis
|
||||
mongo_collection = connect_to_mongodb()
|
||||
|
||||
@app.route('/api/health', methods=['GET'])
|
||||
def health_check():
|
||||
"""Health check endpoint to verify API is running."""
|
||||
return jsonify({
|
||||
'status': 'healthy',
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'mongodb_connected': mongo_collection is not None,
|
||||
'route_analyzer_ready': route_analyzer.collection is not None
|
||||
})
|
||||
|
||||
@app.route('/api/weather', methods=['GET'])
|
||||
def get_weather_endpoint():
|
||||
"""Get current weather conditions for given coordinates."""
|
||||
try:
|
||||
lat = float(request.args.get('lat'))
|
||||
lon = float(request.args.get('lon'))
|
||||
|
||||
weather_data, weather_summary = get_current_weather(lat, lon)
|
||||
|
||||
if weather_data is None:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': weather_summary
|
||||
}), 400
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'weather_data': weather_data,
|
||||
'summary': weather_summary,
|
||||
'coordinates': {'lat': lat, 'lon': lon}
|
||||
})
|
||||
|
||||
except (TypeError, ValueError) as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Invalid latitude or longitude provided'
|
||||
}), 400
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}), 500
|
||||
|
||||
@app.route('/api/analyze-crashes', methods=['POST'])
|
||||
def analyze_crashes_endpoint():
|
||||
"""Analyze crash patterns and safety for a specific location."""
|
||||
try:
|
||||
if mongo_collection is None:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Database connection not available'
|
||||
}), 500
|
||||
|
||||
# More robust JSON parsing
|
||||
data = request.get_json(force=True)
|
||||
if not data:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'No valid JSON data provided'
|
||||
}), 400
|
||||
|
||||
try:
|
||||
lat = float(data.get('lat'))
|
||||
lon = float(data.get('lon'))
|
||||
radius_km = float(data.get('radius', 1.0))
|
||||
except (TypeError, ValueError) as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Invalid coordinates: lat={data.get("lat")}, lon={data.get("lon")}, radius={data.get("radius", 1.0)}'
|
||||
}), 400
|
||||
|
||||
print(f"🔍 Analyzing crashes at ({lat:.4f}, {lon:.4f}) within {radius_km}km...")
|
||||
|
||||
# Get crashes within radius
|
||||
crashes = get_crashes_within_radius_mongodb(mongo_collection, lat, lon, radius_km)
|
||||
|
||||
# Serialize MongoDB documents to handle ObjectId
|
||||
crashes_serialized = serialize_mongodb_doc(crashes)
|
||||
|
||||
# Get current weather
|
||||
weather_data, weather_summary = get_current_weather(lat, lon)
|
||||
|
||||
# Generate safety analysis using LLM
|
||||
safety_analysis = analyze_mongodb_crash_patterns(
|
||||
crashes, lat, lon, radius_km, weather_summary
|
||||
)
|
||||
|
||||
# Calculate some basic statistics
|
||||
total_crashes = len(crashes)
|
||||
avg_distance = sum(crash.get('distance_km', 0) for crash in crashes) / total_crashes if crashes else 0
|
||||
|
||||
# Extract crash summary stats
|
||||
severity_counts = {}
|
||||
total_casualties = 0
|
||||
for crash in crashes:
|
||||
severity = crash.get('severity', 'Unknown')
|
||||
severity_counts[severity] = severity_counts.get(severity, 0) + 1
|
||||
|
||||
casualties = crash.get('casualties', {})
|
||||
for category in ['bicyclists', 'drivers', 'pedestrians', 'passengers']:
|
||||
if category in casualties:
|
||||
cat_data = casualties[category]
|
||||
total_casualties += (cat_data.get('fatal', 0) +
|
||||
cat_data.get('major_injuries', 0) +
|
||||
cat_data.get('minor_injuries', 0))
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'location': {'lat': lat, 'lon': lon},
|
||||
'radius_km': radius_km,
|
||||
'crash_summary': {
|
||||
'total_crashes': total_crashes,
|
||||
'avg_distance_km': round(avg_distance, 3),
|
||||
'severity_breakdown': severity_counts,
|
||||
'total_casualties': total_casualties
|
||||
},
|
||||
'weather': {
|
||||
'summary': weather_summary,
|
||||
'data': weather_data
|
||||
},
|
||||
'safety_analysis': safety_analysis,
|
||||
'raw_crashes': crashes_serialized[:10] if crashes_serialized else [] # Return first 10 for reference
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error in crash analysis: {e}")
|
||||
traceback.print_exc()
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}), 500
|
||||
|
||||
@app.route('/api/find-safe-route', methods=['POST'])
|
||||
def find_safe_route_endpoint():
|
||||
"""Find the safest route between two points with crash analysis."""
|
||||
try:
|
||||
if route_analyzer.collection is None:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Route analyzer not available'
|
||||
}), 500
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'No route data provided'
|
||||
}), 400
|
||||
|
||||
start_lat = float(data.get('start_lat'))
|
||||
start_lon = float(data.get('start_lon'))
|
||||
end_lat = float(data.get('end_lat'))
|
||||
end_lon = float(data.get('end_lon'))
|
||||
|
||||
print(f"🛣️ Finding safe route from ({start_lat:.4f}, {start_lon:.4f}) to ({end_lat:.4f}, {end_lon:.4f})...")
|
||||
|
||||
# Find the safest route
|
||||
results = route_analyzer.find_safer_route(start_lat, start_lon, end_lat, end_lon)
|
||||
|
||||
if 'error' in results:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': results['error']
|
||||
}), 500
|
||||
|
||||
# Extract key information for frontend
|
||||
recommended_route = results['recommended_route']
|
||||
route_data = recommended_route['route_data']
|
||||
safety_data = recommended_route['safety_analysis']
|
||||
|
||||
# Prepare response
|
||||
response_data = {
|
||||
'success': True,
|
||||
'start_coordinates': {'lat': start_lat, 'lon': start_lon},
|
||||
'end_coordinates': {'lat': end_lat, 'lon': end_lon},
|
||||
'recommended_route': {
|
||||
'coordinates': route_data['coordinates'], # For Mapbox visualization
|
||||
'distance_km': route_data['distance_km'],
|
||||
'duration_min': route_data['duration_min'],
|
||||
'geometry': route_data.get('geometry'), # GeoJSON for Mapbox
|
||||
'safety_score': safety_data['average_safety_score'],
|
||||
'crashes_nearby': safety_data['total_crashes_near_route'],
|
||||
'max_danger_score': safety_data['max_danger_score']
|
||||
},
|
||||
'safety_analysis': results['safety_report'],
|
||||
'weather_summary': results.get('weather_summary'),
|
||||
'route_comparison': results.get('route_comparison'),
|
||||
'alternative_routes': []
|
||||
}
|
||||
|
||||
# Add alternative routes if available
|
||||
for alt_route in results.get('alternative_routes', []):
|
||||
alt_data = alt_route['route_data']
|
||||
alt_safety = alt_route['safety_analysis']
|
||||
response_data['alternative_routes'].append({
|
||||
'route_id': alt_data['route_id'],
|
||||
'coordinates': alt_data['coordinates'],
|
||||
'distance_km': alt_data['distance_km'],
|
||||
'duration_min': alt_data['duration_min'],
|
||||
'geometry': alt_data.get('geometry'),
|
||||
'safety_score': alt_safety['average_safety_score'],
|
||||
'crashes_nearby': alt_safety['total_crashes_near_route']
|
||||
})
|
||||
|
||||
return jsonify(response_data)
|
||||
|
||||
except (TypeError, ValueError) as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Invalid route coordinates provided'
|
||||
}), 400
|
||||
except Exception as e:
|
||||
print(f"❌ Error in route finding: {e}")
|
||||
traceback.print_exc()
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}), 500
|
||||
|
||||
@app.route('/api/get-single-route', methods=['POST'])
|
||||
def get_single_route_endpoint():
|
||||
"""Get a single route with safety analysis (simpler version)."""
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'No data provided'
|
||||
}), 400
|
||||
|
||||
start_lat = float(data.get('start_lat'))
|
||||
start_lon = float(data.get('start_lon'))
|
||||
end_lat = float(data.get('end_lat'))
|
||||
end_lon = float(data.get('end_lon'))
|
||||
profile = data.get('profile', 'driving') # driving, walking, cycling
|
||||
|
||||
# Get route from Mapbox
|
||||
route_result = route_analyzer.get_route_from_mapbox(
|
||||
start_lat, start_lon, end_lat, end_lon, profile
|
||||
)
|
||||
|
||||
if not route_result.get('success'):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': route_result.get('error', 'Failed to get route')
|
||||
}), 500
|
||||
|
||||
# Analyze route safety
|
||||
safety_analysis = route_analyzer.analyze_route_safety(route_result['coordinates'])
|
||||
|
||||
if 'error' in safety_analysis:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': safety_analysis['error']
|
||||
}), 500
|
||||
|
||||
# Get weather
|
||||
weather_data, weather_summary = route_analyzer.get_current_weather(start_lat, start_lon)
|
||||
|
||||
# Generate safety report
|
||||
safety_report = route_analyzer.generate_safety_report_with_llm(
|
||||
safety_analysis, route_result, weather_summary
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'route': {
|
||||
'coordinates': route_result['coordinates'],
|
||||
'distance_km': route_result['distance_km'],
|
||||
'duration_min': route_result['duration_min'],
|
||||
'geometry': route_result.get('geometry'),
|
||||
'profile': profile
|
||||
},
|
||||
'safety': {
|
||||
'total_crashes_nearby': safety_analysis['total_crashes_near_route'],
|
||||
'average_safety_score': safety_analysis['average_safety_score'],
|
||||
'max_danger_score': safety_analysis['max_danger_score'],
|
||||
'safety_points': safety_analysis['safety_points']
|
||||
},
|
||||
'safety_report': safety_report,
|
||||
'weather_summary': weather_summary
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error getting single route: {e}")
|
||||
traceback.print_exc()
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e)
|
||||
}), 500
|
||||
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
return jsonify({'success': False, 'error': 'Endpoint not found'}), 404
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_error(error):
|
||||
return jsonify({'success': False, 'error': 'Internal server error'}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("🚀 Starting Flask API Server...")
|
||||
print("📡 Endpoints available:")
|
||||
print(" - GET /api/health")
|
||||
print(" - GET /api/weather?lat=X&lon=Y")
|
||||
print(" - POST /api/analyze-crashes")
|
||||
print(" - POST /api/find-safe-route")
|
||||
print(" - POST /api/get-single-route")
|
||||
print("\n🌐 Server running on http://localhost:5001")
|
||||
|
||||
app.run(debug=True, host='0.0.0.0', port=5001)
|
||||
6
llm/api/requirements.txt
Normal file
6
llm/api/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Flask==3.0.0
|
||||
Flask-CORS==4.0.0
|
||||
pymongo==4.6.0
|
||||
requests==2.31.0
|
||||
langchain-google-genai==1.0.7
|
||||
python-dotenv==1.0.0
|
||||
203
llm/api/test_api.py
Normal file
203
llm/api/test_api.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for Flask API endpoints.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
|
||||
API_BASE = "http://localhost:5001/api"
|
||||
|
||||
def test_health():
|
||||
"""Test health endpoint"""
|
||||
print("🔍 Testing health endpoint...")
|
||||
try:
|
||||
response = requests.get(f"{API_BASE}/health")
|
||||
print(f"Status: {response.status_code}")
|
||||
data = response.json()
|
||||
print(f"Response: {json.dumps(data, indent=2)}")
|
||||
|
||||
if data.get('status') == 'healthy':
|
||||
print("✅ Health check passed!")
|
||||
else:
|
||||
print("⚠️ Health check shows issues")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Health check failed: {e}")
|
||||
return False
|
||||
finally:
|
||||
print()
|
||||
|
||||
def test_weather():
|
||||
"""Test weather endpoint"""
|
||||
print("🌤️ Testing weather endpoint...")
|
||||
try:
|
||||
# Test with Virginia Tech coordinates
|
||||
response = requests.get(f"{API_BASE}/weather", params={'lat': 37.2284, 'lon': -80.4234})
|
||||
print(f"Status: {response.status_code}")
|
||||
data = response.json()
|
||||
|
||||
if data.get('success'):
|
||||
print(f"✅ Weather: {data['summary']}")
|
||||
print(f" Coordinates: ({data['coordinates']['lat']}, {data['coordinates']['lon']})")
|
||||
else:
|
||||
print(f"❌ Weather API error: {data.get('error')}")
|
||||
return data.get('success', False)
|
||||
except Exception as e:
|
||||
print(f"❌ Weather test failed: {e}")
|
||||
return False
|
||||
finally:
|
||||
print()
|
||||
|
||||
def test_crash_analysis():
|
||||
"""Test crash analysis endpoint"""
|
||||
print("🚨 Testing crash analysis endpoint...")
|
||||
try:
|
||||
payload = {
|
||||
'lat': 37.2284, # Virginia Tech
|
||||
'lon': -80.4234,
|
||||
'radius': 2.0
|
||||
}
|
||||
response = requests.post(f"{API_BASE}/analyze-crashes", json=payload)
|
||||
print(f"Status: {response.status_code}")
|
||||
data = response.json()
|
||||
|
||||
if data.get('success'):
|
||||
print("✅ Crash analysis successful!")
|
||||
crash_summary = data['crash_summary']
|
||||
print(f" Total crashes: {crash_summary['total_crashes']}")
|
||||
print(f" Total casualties: {crash_summary['total_casualties']}")
|
||||
print(f" Average distance: {crash_summary['avg_distance_km']} km")
|
||||
print(f" Weather: {data['weather']['summary']}")
|
||||
|
||||
if crash_summary['severity_breakdown']:
|
||||
print(" Severity breakdown:")
|
||||
for severity, count in crash_summary['severity_breakdown'].items():
|
||||
print(f" - {severity}: {count}")
|
||||
else:
|
||||
print(f"❌ Crash analysis error: {data.get('error')}")
|
||||
return data.get('success', False)
|
||||
except Exception as e:
|
||||
print(f"❌ Crash analysis test failed: {e}")
|
||||
return False
|
||||
finally:
|
||||
print()
|
||||
|
||||
def test_route_finding():
|
||||
"""Test safe route finding endpoint"""
|
||||
print("🛣️ Testing route finding endpoint...")
|
||||
try:
|
||||
payload = {
|
||||
'start_lat': 37.2284, # Virginia Tech
|
||||
'start_lon': -80.4234,
|
||||
'end_lat': 37.2297, # Downtown Blacksburg
|
||||
'end_lon': -80.4139
|
||||
}
|
||||
response = requests.post(f"{API_BASE}/find-safe-route", json=payload)
|
||||
print(f"Status: {response.status_code}")
|
||||
data = response.json()
|
||||
|
||||
if data.get('success'):
|
||||
print("✅ Route finding successful!")
|
||||
route = data['recommended_route']
|
||||
print(f" Distance: {route['distance_km']:.2f} km")
|
||||
print(f" Duration: {route['duration_min']:.1f} minutes")
|
||||
print(f" Crashes nearby: {route['crashes_nearby']}")
|
||||
print(f" Safety score: {route['safety_score']:.3f}")
|
||||
print(f" Coordinate points: {len(route['coordinates'])}")
|
||||
|
||||
if data.get('weather_summary'):
|
||||
print(f" Weather: {data['weather_summary']}")
|
||||
|
||||
print(f" Alternative routes: {len(data['alternative_routes'])}")
|
||||
else:
|
||||
print(f"❌ Route finding error: {data.get('error')}")
|
||||
return data.get('success', False)
|
||||
except Exception as e:
|
||||
print(f"❌ Route finding test failed: {e}")
|
||||
return False
|
||||
finally:
|
||||
print()
|
||||
|
||||
def test_single_route():
|
||||
"""Test single route endpoint"""
|
||||
print("🗺️ Testing single route endpoint...")
|
||||
try:
|
||||
payload = {
|
||||
'start_lat': 37.2284, # Virginia Tech
|
||||
'start_lon': -80.4234,
|
||||
'end_lat': 37.2297, # Downtown Blacksburg
|
||||
'end_lon': -80.4139,
|
||||
'profile': 'driving'
|
||||
}
|
||||
response = requests.post(f"{API_BASE}/get-single-route", json=payload)
|
||||
print(f"Status: {response.status_code}")
|
||||
data = response.json()
|
||||
|
||||
if data.get('success'):
|
||||
print("✅ Single route successful!")
|
||||
route = data['route']
|
||||
safety = data['safety']
|
||||
print(f" Distance: {route['distance_km']:.2f} km")
|
||||
print(f" Duration: {route['duration_min']:.1f} minutes")
|
||||
print(f" Profile: {route['profile']}")
|
||||
print(f" Total crashes nearby: {safety['total_crashes_nearby']}")
|
||||
print(f" Safety score: {safety['average_safety_score']:.3f}")
|
||||
print(f" Max danger score: {safety['max_danger_score']:.3f}")
|
||||
|
||||
if data.get('weather_summary'):
|
||||
print(f" Weather: {data['weather_summary']}")
|
||||
else:
|
||||
print(f"❌ Single route error: {data.get('error')}")
|
||||
return data.get('success', False)
|
||||
except Exception as e:
|
||||
print(f"❌ Single route test failed: {e}")
|
||||
return False
|
||||
finally:
|
||||
print()
|
||||
|
||||
def main():
|
||||
print("🧪 Testing Flask API Endpoints")
|
||||
print("=" * 50)
|
||||
|
||||
# Check if server is running
|
||||
try:
|
||||
requests.get(f"{API_BASE}/health", timeout=5)
|
||||
except requests.exceptions.ConnectionError:
|
||||
print("❌ Could not connect to Flask server.")
|
||||
print(" Make sure the server is running: python api/flask_server.py")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ Error connecting to server: {e}")
|
||||
return
|
||||
|
||||
# Run all tests
|
||||
results = []
|
||||
results.append(("Health Check", test_health()))
|
||||
results.append(("Weather API", test_weather()))
|
||||
results.append(("Crash Analysis", test_crash_analysis()))
|
||||
results.append(("Route Finding", test_route_finding()))
|
||||
results.append(("Single Route", test_single_route()))
|
||||
|
||||
# Summary
|
||||
print("=" * 50)
|
||||
print("🎯 TEST SUMMARY:")
|
||||
print("=" * 50)
|
||||
|
||||
passed = 0
|
||||
for test_name, result in results:
|
||||
status = "✅ PASSED" if result else "❌ FAILED"
|
||||
print(f" {test_name:<20} {status}")
|
||||
if result:
|
||||
passed += 1
|
||||
|
||||
print(f"\nResults: {passed}/{len(results)} tests passed")
|
||||
|
||||
if passed == len(results):
|
||||
print("🎉 All tests passed! Flask API is ready for Next.js integration!")
|
||||
else:
|
||||
print("⚠️ Some tests failed. Please check the server logs.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
126
llm/api/test_crash_endpoint.py
Normal file
126
llm/api/test_crash_endpoint.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick test script for the crash analysis endpoint
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
def test_crash_analysis():
|
||||
"""Test the crash analysis endpoint"""
|
||||
|
||||
url = "http://localhost:5001/api/analyze-crashes"
|
||||
|
||||
# Test data - Washington DC coordinates
|
||||
payload = {
|
||||
"lat": 38.9072,
|
||||
"lon": -77.0369,
|
||||
"radius": 1.0
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
print("🧪 Testing Crash Analysis Endpoint")
|
||||
print("=" * 50)
|
||||
print(f"URL: {url}")
|
||||
print(f"Payload: {json.dumps(payload, indent=2)}")
|
||||
print(f"Headers: {headers}")
|
||||
print()
|
||||
|
||||
try:
|
||||
print("📡 Sending request...")
|
||||
response = requests.post(url, json=payload, headers=headers, timeout=30)
|
||||
|
||||
print(f"📊 Response Status: {response.status_code}")
|
||||
print(f"📋 Response Headers: {dict(response.headers)}")
|
||||
print()
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get('success'):
|
||||
print("✅ SUCCESS! Crash analysis endpoint is working!")
|
||||
print()
|
||||
print("📈 Results Summary:")
|
||||
crash_summary = data.get('crash_summary', {})
|
||||
print(f" • Total crashes: {crash_summary.get('total_crashes', 'N/A')}")
|
||||
print(f" • Average distance: {crash_summary.get('avg_distance_km', 'N/A')} km")
|
||||
print(f" • Total casualties: {crash_summary.get('total_casualties', 'N/A')}")
|
||||
|
||||
weather = data.get('weather', {})
|
||||
print(f" • Weather: {weather.get('summary', 'N/A')}")
|
||||
|
||||
safety_analysis = data.get('safety_analysis', '')
|
||||
print(f" • Safety analysis length: {len(safety_analysis)} characters")
|
||||
|
||||
raw_crashes = data.get('raw_crashes', [])
|
||||
print(f" • Sample crashes returned: {len(raw_crashes)}")
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"❌ API returned success=False: {data.get('error', 'Unknown error')}")
|
||||
return False
|
||||
|
||||
else:
|
||||
print(f"❌ HTTP Error {response.status_code}")
|
||||
try:
|
||||
error_data = response.json()
|
||||
print(f"Error details: {error_data}")
|
||||
except:
|
||||
print(f"Response text: {response.text}")
|
||||
return False
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print("❌ Connection Error: Cannot connect to Flask server")
|
||||
print(" Make sure the Flask server is running on http://localhost:5001")
|
||||
print(" Start it with: cd llm && python api/flask_server.py")
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print("❌ Timeout Error: Request took too long (>30 seconds)")
|
||||
print(" This might be normal for the first request as it loads data")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Unexpected Error: {e}")
|
||||
return False
|
||||
|
||||
def test_health_first():
|
||||
"""Test health endpoint first to make sure server is running"""
|
||||
try:
|
||||
response = requests.get("http://localhost:5001/api/health", timeout=5)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print("✅ Health check passed")
|
||||
print(f" • MongoDB connected: {data.get('mongodb_connected')}")
|
||||
print(f" • Route analyzer ready: {data.get('route_analyzer_ready')}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Health check failed: {response.status_code}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Health check error: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🚀 Flask Server Crash Analysis Test")
|
||||
print("=" * 60)
|
||||
|
||||
# Test health first
|
||||
if not test_health_first():
|
||||
print("\n💡 Server not responding. Start it with:")
|
||||
print(" cd /Users/shivapochampally/Documents/competitions/VTHacks13/llm")
|
||||
print(" python api/flask_server.py")
|
||||
exit(1)
|
||||
|
||||
print()
|
||||
|
||||
# Test crash analysis
|
||||
success = test_crash_analysis()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
if success:
|
||||
print("🎉 All tests passed! Your crash analysis endpoint is working perfectly!")
|
||||
else:
|
||||
print("❌ Test failed. Check the error messages above.")
|
||||
50
llm/flask_server.log
Normal file
50
llm/flask_server.log
Normal file
@@ -0,0 +1,50 @@
|
||||
✅ Successfully imported Python modules
|
||||
✅ Connected to MongoDB for route safety analysis
|
||||
Connecting to MongoDB...
|
||||
✅ Successfully connected to MongoDB!
|
||||
📊 Found 337,113 total crash records in database
|
||||
📅 Found 112,964 crash records from 2020 onward
|
||||
🚀 Starting Flask API Server...
|
||||
📡 Endpoints available:
|
||||
- GET /api/health
|
||||
- GET /api/weather?lat=X&lon=Y
|
||||
- POST /api/analyze-crashes
|
||||
- POST /api/find-safe-route
|
||||
- POST /api/get-single-route
|
||||
|
||||
🌐 Server running on http://localhost:5001
|
||||
* Serving Flask app 'flask_server'
|
||||
* Debug mode: on
|
||||
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
|
||||
* Running on all addresses (0.0.0.0)
|
||||
* Running on http://127.0.0.1:5001
|
||||
* Running on http://100.110.69.195:5001
|
||||
Press CTRL+C to quit
|
||||
* Restarting with watchdog (fsevents)
|
||||
* Debugger is active!
|
||||
* Debugger PIN: 649-846-112
|
||||
127.0.0.1 - - [28/Sep/2025 01:55:17] "GET /api/health HTTP/1.1" 200 -
|
||||
127.0.0.1 - - [28/Sep/2025 01:55:22] "POST /api/analyze-crashes HTTP/1.1" 400 -
|
||||
* Detected change in '/Users/shivapochampally/Documents/competitions/VTHacks13/llm/api/flask_server.py', reloading
|
||||
* Detected change in '/Users/shivapochampally/Documents/competitions/VTHacks13/llm/api/flask_server.py', reloading
|
||||
✅ Successfully imported Python modules
|
||||
✅ Connected to MongoDB for route safety analysis
|
||||
Connecting to MongoDB...
|
||||
✅ Successfully connected to MongoDB!
|
||||
📊 Found 337,113 total crash records in database
|
||||
📅 Found 112,964 crash records from 2020 onward
|
||||
🚀 Starting Flask API Server...
|
||||
📡 Endpoints available:
|
||||
- GET /api/health
|
||||
- GET /api/weather?lat=X&lon=Y
|
||||
- POST /api/analyze-crashes
|
||||
- POST /api/find-safe-route
|
||||
- POST /api/get-single-route
|
||||
|
||||
🌐 Server running on http://localhost:5001
|
||||
🔍 Analyzing crashes at (38.9072, -77.0369) within 1.0km...
|
||||
🔍 Querying crashes within 1.0km of (38.907200, -77.036900) from 2020 onward...
|
||||
📍 Found 5944 crashes within 1.0km radius (from 2020 onward)
|
||||
* Restarting with watchdog (fsevents)
|
||||
* Debugger is active!
|
||||
* Debugger PIN: 649-846-112
|
||||
@@ -12,7 +12,7 @@ from math import radians, sin, cos, sqrt, atan2
|
||||
GEMINI_API_KEY = "AIzaSyBCbEOo4aK72507hqvpYkE9zXUe-z5aSXA"
|
||||
MONGO_URI = "mongodb+srv://Admin:HelloKitty420@geobase.tyxsoir.mongodb.net/crashes"
|
||||
|
||||
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash-lite", api_key=GEMINI_API_KEY)
|
||||
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash-lite", google_api_key=GEMINI_API_KEY)
|
||||
|
||||
def connect_to_mongodb():
|
||||
"""
|
||||
|
||||
@@ -15,7 +15,7 @@ GEMINI_API_KEY = "AIzaSyBCbEOo4aK72507hqvpYkE9zXUe-z5aSXA"
|
||||
MONGO_URI = "mongodb+srv://Admin:HelloKitty420@geobase.tyxsoir.mongodb.net/crashes"
|
||||
MAPBOX_API_KEY = "pk.eyJ1IjoicGllbG9yZDc1NyIsImEiOiJjbWcxdTd6c3AwMXU1MmtxMDh6b2l5amVrIn0.5Es0azrah23GX1e9tmbjGw"
|
||||
|
||||
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash-lite", api_key=GEMINI_API_KEY)
|
||||
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash-lite", google_api_key=GEMINI_API_KEY)
|
||||
|
||||
class SafeRouteAnalyzer:
|
||||
def __init__(self, mongo_uri: str):
|
||||
|
||||
Reference in New Issue
Block a user