Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PREDICT: Enhance Forecast Responses #4090

Merged
merged 2 commits into from
Dec 15, 2024
Merged
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
142 changes: 104 additions & 38 deletions src/predict/api/prediction.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,88 @@

load_dotenv()

ml_app = Blueprint("ml_app", __name__)
# Centralized AQI Ranges Configuration
AQI_RANGES = {
"good": {
"min": 0,
"max": 9.1,
"label": "Good"
},
"moderate": {
"min": 9.101,
"max": 35.49,
"label": "Moderate"
},
"u4sg": {
"min": 35.491,
"max": 55.49,
"label": "Unhealthy for Sensitive Groups"
},
"unhealthy": {
"min": 55.491,
"max": 125.49,
"label": "Unhealthy"
},
"very_unhealthy": {
"min": 125.491,
"max": 225.49,
"label": "Very Unhealthy"
},
"hazardous": {
"min": 225.491,
"max": None,
"label": "Hazardous"
}
}

def get_aqi_category(value):
"""
Determine the AQI category for a given value.

Args:
value (float): The air quality value to categorize

Returns:
dict: The AQI category that matches the value
"""
for category, range_info in AQI_RANGES.items():
if range_info['max'] is None and value >= range_info['min']:
return {
'category': category,
'label': range_info['label'],
**range_info
}
elif range_info['max'] is not None and range_info['min'] <= value <= range_info['max']:
return {
'category': category,
'label': range_info['label'],
**range_info
}
return None

def enhance_forecast_response(result, language=''):
"""
Enhance forecast response with additional metadata.

Args:
result (dict): The original forecast result
language (str, optional): Language for health tips. Defaults to ''

Returns:
dict: Enhanced forecast result
"""
# Add health tips if possible
try:
add_forecast_health_tips(result, language=language)
except Exception as e:
current_app.logger.error(f"Error adding health tips: {str(e)}")

# Add AQI ranges to the response
result['aqi_ranges'] = AQI_RANGES

return result

ml_app = Blueprint("ml_app", __name__)

@ml_app.get(routes.route["fetch_faulty_devices"])
@cache.cached(timeout=Config.CACHE_TIMEOUT, key_prefix=get_faults_cache_key)
Expand Down Expand Up @@ -68,7 +148,6 @@ def fetch_faulty_devices():
500,
)


@ml_app.route(routes.route["next_24hr_forecasts"], methods=["GET"])
@cache.cached(timeout=Config.CACHE_TIMEOUT, key_prefix=hourly_forecasts_cache_key)
def get_next_24hr_forecasts():
Expand Down Expand Up @@ -99,28 +178,19 @@ def get_next_24hr_forecasts():
)
language = request.args.get("language", default="", type=str)
result = get_forecasts_1(**params, db_name="hourly_forecasts_1")

if result:
try:
add_forecast_health_tips(result, language=language)
except Exception as e:
current_app.logger.warning(
"Error adding health tips: ", str(e), exc_info=True
)
# response = {
# "success": True,
# "message": "Hourly forecasts successfully retrieved",
# "forecasts": result,
# }
result = enhance_forecast_response(result, language)
response = result
else:
response = {
"message": "forecasts for this site are not available",
"success": False,
}

data = jsonify(response)
return data, 200


@ml_app.route(routes.route["next_1_week_forecasts"], methods=["GET"])
@cache.cached(timeout=Config.CACHE_TIMEOUT, key_prefix=daily_forecasts_cache_key)
def get_next_1_week_forecasts():
Expand Down Expand Up @@ -152,27 +222,19 @@ def get_next_1_week_forecasts():
)
language = request.args.get("language", default="", type=str)
result = get_forecasts_1(**params, db_name="daily_forecasts_1")

if result:
try:
add_forecast_health_tips(result, language=language)

except Exception as e:
current_app.logger.error("Error adding health tips", str(e), exc_info=False)
# response = {
# "success": True,
# "message": "Daily forecasts successfully retrieved.",
# "forecasts": result,
# TODO delete once mobile app is updates
result = enhance_forecast_response(result, language)
response = result
else:
response = {
"message": "forecasts for this site are not available",
"success": False,
}

data = jsonify(response)
return data, 200


@ml_app.route(routes.route["all_1_week_forecasts"], methods=["GET"])
@cache.cached(timeout=Config.CACHE_TIMEOUT, key_prefix=all_daily_forecasts_cache_key)
def get_all_daily_forecasts():
Expand All @@ -182,11 +244,9 @@ def get_all_daily_forecasts():
language = request.args.get("language", default="", type=str)
result = get_forecasts_2(db_name="daily_forecasts_1", all_forecasts=True)
current_app.logger.info(f"result: result retriece", exc_info=True)

if result:
try:
add_forecast_health_tips(result, language=language)
except Exception as e:
current_app.logger.error("Error adding health tips: ", str(e))
result = enhance_forecast_response(result, language)
response = {
"success": True,
"message": "All daily forecasts successfully retrieved.",
Expand All @@ -199,7 +259,6 @@ def get_all_daily_forecasts():
}
return jsonify(response), 200


@ml_app.route(routes.route["all_24hr_forecasts"], methods=["GET"])
@cache.cached(timeout=Config.CACHE_TIMEOUT, key_prefix=all_hourly_forecasts_cache_key)
def get_all_hourly_forecasts():
Expand All @@ -208,11 +267,9 @@ def get_all_hourly_forecasts():
"""
language = request.args.get("language", default="", type=str)
result = get_forecasts_2(db_name="hourly_forecasts_1", all_forecasts=True)

if result:
try:
add_forecast_health_tips(result, language=language)
except Exception as e:
current_app.logger.error("Error adding health tips: ", str(e))
result = enhance_forecast_response(result, language)
response = {
"success": True,
"message": "All hourly forecasts successfully retrieved.",
Expand All @@ -225,7 +282,6 @@ def get_all_hourly_forecasts():
}
return jsonify(response), 200


@ml_app.route(routes.route["predict_for_heatmap"], methods=["GET"])
@cache.cached(timeout=Config.CACHE_TIMEOUT, key_prefix=heatmap_cache_key)
def predictions_for_heatmap():
Expand All @@ -242,6 +298,7 @@ def predictions_for_heatmap():
response["total"] = total
response["pages"] = (total // limit) + (1 if total % limit else 0)
response["page"] = page
response["aqi_ranges"] = AQI_RANGES # Add AQI ranges to heatmap response
if airqloud:
response["airqloud"] = airqloud
status_code = 200
Expand All @@ -255,7 +312,6 @@ def predictions_for_heatmap():
finally:
return jsonify(response), status_code


@ml_app.route(routes.route["search_predictions"], methods=["GET"])
@cache.cached(timeout=Config.CACHE_TIMEOUT, key_prefix=geo_coordinates_cache_key)
def search_predictions():
Expand All @@ -264,6 +320,7 @@ def search_predictions():
longitude = float(request.args.get("longitude"))
source = str(request.args.get("source", "parishes")).lower()
distance_in_metres = int(request.args.get("distance", 100))

if source == "parishes":
data = get_predictions_by_geo_coordinates_v2(
latitude=latitude,
Expand All @@ -275,9 +332,16 @@ def search_predictions():
longitude=longitude,
distance_in_metres=distance_in_metres,
)

if data:
health_tips = get_health_tips()
pm2_5 = data["pm2_5"]

# Use the new get_aqi_category function
aqi_category = get_aqi_category(pm2_5)

data["aqi_category"] = aqi_category
data['aqi_ranges'] = AQI_RANGES
data["health_tips"] = list(
filter(
lambda x: x["aqi_category"]["max"]
Expand All @@ -294,7 +358,6 @@ def search_predictions():
traceback.print_exc()
return {"message": "Please contact support", "success": False}, 500


@ml_app.route(routes.route["parish_predictions"], methods=["GET"])
def parish_predictions():
try:
Expand All @@ -310,15 +373,18 @@ def parish_predictions():
data, total_pages = get_parish_predictions(
parish=parish, district=district, page_size=page_size, offset=offset
)

# Add AQI ranges to the response
return {
"success": True,
"page": page,
"page_size": page_size,
"total_pages": total_pages,
"page_limit": limit,
"aqi_ranges": AQI_RANGES,
"data": data,
}, 200
except Exception as ex:
print(ex)
traceback.print_exc()
return {"message": "Please contact support", "success": False}, 500
return {"message": "Please contact support", "success": False}, 500
Loading