mirror of
https://github.com/thecyberlearn/quantum-ai-v3.git
synced 2026-08-18 15:32:57 +00:00
- Reduce template from 2024+ lines to 769 lines (62% reduction) - Replace extensive inline CSS with template prototype framework - Modernize JavaScript from SocialAdsModule to SocialAdsUtils pattern - Convert to component-based architecture using template includes - Remove duplicate processing view and clean up unused imports - Enhance results display with rich social ad formatting - Maintain all existing functionality and user experience 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
199 lines
8.1 KiB
Python
199 lines
8.1 KiB
Python
from agent_base.processors import StandardAPIProcessor
|
|
from django.utils import timezone
|
|
from .models import WeatherReporterRequest, WeatherReporterResponse
|
|
import json
|
|
import re
|
|
import urllib.parse
|
|
|
|
|
|
class WeatherReporterProcessor(StandardAPIProcessor):
|
|
"""API processor for Weather Reporter agent"""
|
|
|
|
agent_slug = 'weather-reporter'
|
|
api_base_url = 'https://api.openweathermap.org/data/2.5/weather'
|
|
api_key_env = 'OPENWEATHER_API_KEY'
|
|
auth_method = 'query'
|
|
|
|
def sanitize_location(self, location):
|
|
"""Sanitize and validate location input"""
|
|
if not location:
|
|
return 'London' # Default fallback
|
|
|
|
# Remove leading/trailing whitespace
|
|
location = location.strip()
|
|
|
|
# Length validation (max 100 characters)
|
|
if len(location) > 100:
|
|
location = location[:100]
|
|
|
|
# Character whitelist: letters, numbers, spaces, hyphens, commas, periods, apostrophes
|
|
# This allows city names like "New York", "São Paulo", "O'Connor", etc.
|
|
allowed_pattern = re.compile(r'^[a-zA-Z0-9\s\-,.\'\u00C0-\u017F]+$')
|
|
|
|
if not allowed_pattern.match(location):
|
|
# Remove disallowed characters (keep only safe characters)
|
|
location = re.sub(r'[^a-zA-Z0-9\s\-,.\'\u00C0-\u017F]', '', location)
|
|
|
|
# Remove multiple spaces and clean up
|
|
location = re.sub(r'\s+', ' ', location).strip()
|
|
|
|
# Final validation - must have at least one alphanumeric character
|
|
if not re.search(r'[a-zA-Z0-9]', location):
|
|
return 'London' # Fallback if no valid characters remain
|
|
|
|
return location
|
|
|
|
def get_endpoint(self, **kwargs):
|
|
"""Get the OpenWeather API endpoint with location"""
|
|
location = kwargs.get('location', 'London')
|
|
location = self.sanitize_location(location)
|
|
|
|
# URL encode the location to prevent injection
|
|
location_encoded = urllib.parse.quote(location)
|
|
return f"{self.api_base_url}?q={location_encoded}&units=metric"
|
|
|
|
def prepare_request_data(self, **kwargs):
|
|
"""Prepare API request data"""
|
|
location = self.sanitize_location(kwargs.get('location', 'London'))
|
|
report_type = kwargs.get('report_type', 'current')
|
|
|
|
# Validate report_type
|
|
valid_report_types = ['current', 'detailed', 'forecast']
|
|
if report_type not in valid_report_types:
|
|
report_type = 'current'
|
|
|
|
return {
|
|
'location': location,
|
|
'report_type': report_type,
|
|
}
|
|
|
|
def should_use_get(self, **kwargs):
|
|
"""Use GET for weather API"""
|
|
return True
|
|
|
|
def format_weather_report(self, weather_data, report_type):
|
|
"""Format weather data into readable report"""
|
|
if not weather_data or 'main' not in weather_data:
|
|
return "Weather data unavailable"
|
|
|
|
location = weather_data.get('name', 'Unknown')
|
|
country = weather_data.get('sys', {}).get('country', '')
|
|
temp = weather_data.get('main', {}).get('temp', 0)
|
|
feels_like = weather_data.get('main', {}).get('feels_like', 0)
|
|
humidity = weather_data.get('main', {}).get('humidity', 0)
|
|
pressure = weather_data.get('main', {}).get('pressure', 0)
|
|
description = weather_data.get('weather', [{}])[0].get('description', 'Unknown')
|
|
wind_speed = weather_data.get('wind', {}).get('speed', 0)
|
|
wind_deg = weather_data.get('wind', {}).get('deg', 0)
|
|
|
|
if report_type == 'detailed':
|
|
report = f"""🌤️ Weather Report for {location}, {country}
|
|
|
|
🌡️ Temperature: {temp}°C (feels like {feels_like}°C)
|
|
☁️ Conditions: {description.title()}
|
|
💨 Wind: {wind_speed} m/s at {wind_deg}°
|
|
💧 Humidity: {humidity}%
|
|
🔽 Pressure: {pressure} hPa
|
|
|
|
Weather data provided by OpenWeatherMap"""
|
|
else:
|
|
report = f"🌤️ {location}: {temp}°C, {description.title()}, {humidity}% humidity"
|
|
|
|
return report
|
|
|
|
def process_response(self, response_data, request_obj):
|
|
"""Process the weather API response"""
|
|
try:
|
|
# Update request status
|
|
request_obj.status = 'processing'
|
|
request_obj.save()
|
|
|
|
# Check if response already exists (to avoid duplicate creation)
|
|
if hasattr(request_obj, 'response'):
|
|
print(f"{self.agent_slug}: Response already exists for request {request_obj.id}")
|
|
return request_obj.response
|
|
|
|
# Extract weather data
|
|
weather_data = response_data.copy()
|
|
if 'processing_time' in weather_data:
|
|
del weather_data['processing_time']
|
|
if 'success' in weather_data:
|
|
del weather_data['success']
|
|
|
|
# Extract specific fields
|
|
temperature = None
|
|
description = ""
|
|
humidity = None
|
|
wind_speed = None
|
|
|
|
if 'main' in weather_data:
|
|
temperature = weather_data['main'].get('temp')
|
|
humidity = weather_data['main'].get('humidity')
|
|
|
|
if 'weather' in weather_data and len(weather_data['weather']) > 0:
|
|
description = weather_data['weather'][0].get('description', '')
|
|
|
|
if 'wind' in weather_data:
|
|
wind_speed = weather_data['wind'].get('speed')
|
|
|
|
# Format report
|
|
formatted_report = self.format_weather_report(weather_data, request_obj.report_type)
|
|
|
|
# Determine success
|
|
success = response_data.get('success', True) and bool(weather_data.get('main'))
|
|
|
|
# Create response object
|
|
response_obj = WeatherReporterResponse.objects.create(
|
|
request=request_obj,
|
|
success=success,
|
|
processing_time=response_data.get('processing_time', 0),
|
|
weather_data=weather_data,
|
|
temperature=temperature,
|
|
description=description,
|
|
humidity=humidity,
|
|
wind_speed=wind_speed,
|
|
formatted_report=formatted_report,
|
|
)
|
|
|
|
# Only deduct wallet balance after successful processing
|
|
if success:
|
|
request_obj.user.deduct_balance(
|
|
request_obj.cost,
|
|
f"Weather Reporter - {request_obj.location}",
|
|
'weather-reporter'
|
|
)
|
|
print(f"{self.agent_slug}: Wallet deducted {request_obj.cost} AED for successful processing")
|
|
|
|
# Update request as completed
|
|
request_obj.status = 'completed' if success else 'failed'
|
|
request_obj.processed_at = timezone.now()
|
|
request_obj.save()
|
|
|
|
return response_obj
|
|
|
|
except Exception as e:
|
|
# Handle error
|
|
request_obj.status = 'failed'
|
|
request_obj.save()
|
|
|
|
# Check if response already exists (to avoid duplicate creation in error handling)
|
|
if hasattr(request_obj, 'response'):
|
|
print(f"{self.agent_slug}: Error occurred but response already exists for request {request_obj.id}")
|
|
request_obj.response.success = False
|
|
request_obj.response.error_message = str(e)
|
|
request_obj.response.save()
|
|
return request_obj.response
|
|
|
|
# Create error response only if one doesn't exist
|
|
try:
|
|
error_response = WeatherReporterResponse.objects.create(
|
|
request=request_obj,
|
|
success=False,
|
|
error_message=str(e),
|
|
processing_time=response_data.get('processing_time', 0)
|
|
)
|
|
return error_response
|
|
except Exception as create_error:
|
|
print(f"{self.agent_slug}: Could not create error response: {create_error}")
|
|
# Return None or re-raise the original error
|
|
raise Exception(f"Failed to process Weather Reporter response: {e}") |