Add whatsapp feature
This commit is contained in:
@@ -110,6 +110,89 @@ class PredictionService:
|
||||
error=str(e))
|
||||
# Features dict will use defaults (0.0) from _prepare_prophet_features
|
||||
|
||||
# CRITICAL FIX: Fetch POI (Point of Interest) features from external service
|
||||
# Prophet models trained with POI features REQUIRE them during prediction
|
||||
# This prevents "Regressor 'poi_retail_total_count' missing" errors
|
||||
if 'tenant_id' in features:
|
||||
try:
|
||||
from shared.clients.external_client import ExternalServiceClient
|
||||
from app.core.config import settings
|
||||
|
||||
external_client = ExternalServiceClient(settings, "forecasting-service")
|
||||
poi_data = await external_client.get_poi_context(features['tenant_id'])
|
||||
|
||||
if poi_data and 'ml_features' in poi_data:
|
||||
# Add all POI ML features to prediction features
|
||||
poi_features = poi_data['ml_features']
|
||||
features.update(poi_features)
|
||||
logger.info("POI features enriched",
|
||||
tenant_id=features['tenant_id'],
|
||||
poi_feature_count=len(poi_features))
|
||||
else:
|
||||
logger.warning("No POI data available for tenant, using default POI features",
|
||||
tenant_id=features['tenant_id'])
|
||||
# Provide default POI features to prevent model errors
|
||||
# These match ALL features generated by POI detection service
|
||||
# Format: poi_{category}_{feature_name}
|
||||
default_poi_features = {}
|
||||
|
||||
# POI categories from external service POI_CATEGORIES configuration
|
||||
# These match the categories in services/external/app/core/poi_config.py
|
||||
poi_categories = [
|
||||
'schools', 'offices', 'gyms_sports', 'residential', 'tourism',
|
||||
'competitors', 'transport_hubs', 'coworking', 'retail'
|
||||
]
|
||||
|
||||
for category in poi_categories:
|
||||
default_poi_features.update({
|
||||
f'poi_{category}_proximity_score': 0.0,
|
||||
f'poi_{category}_weighted_proximity_score': 0.0,
|
||||
f'poi_{category}_count_0_100m': 0,
|
||||
f'poi_{category}_count_100_300m': 0,
|
||||
f'poi_{category}_count_300_500m': 0,
|
||||
f'poi_{category}_count_500_1000m': 0,
|
||||
f'poi_{category}_total_count': 0,
|
||||
f'poi_{category}_distance_to_nearest_m': 9999.0,
|
||||
f'poi_{category}_has_within_100m': 0,
|
||||
f'poi_{category}_has_within_300m': 0,
|
||||
f'poi_{category}_has_within_500m': 0,
|
||||
})
|
||||
|
||||
features.update(default_poi_features)
|
||||
logger.info("Using default POI features",
|
||||
tenant_id=features['tenant_id'],
|
||||
default_feature_count=len(default_poi_features))
|
||||
except Exception as e:
|
||||
logger.error("Failed to fetch POI features, using defaults",
|
||||
error=str(e),
|
||||
tenant_id=features.get('tenant_id'))
|
||||
# On error, still provide default POI features to prevent prediction failures
|
||||
default_poi_features = {}
|
||||
|
||||
# POI categories from external service POI_CATEGORIES configuration
|
||||
# These match the categories in services/external/app/core/poi_config.py
|
||||
poi_categories = [
|
||||
'schools', 'offices', 'gyms_sports', 'residential', 'tourism',
|
||||
'competitors', 'transport_hubs', 'coworking', 'retail'
|
||||
]
|
||||
|
||||
for category in poi_categories:
|
||||
default_poi_features.update({
|
||||
f'poi_{category}_proximity_score': 0.0,
|
||||
f'poi_{category}_weighted_proximity_score': 0.0,
|
||||
f'poi_{category}_count_0_100m': 0,
|
||||
f'poi_{category}_count_100_300m': 0,
|
||||
f'poi_{category}_count_300_500m': 0,
|
||||
f'poi_{category}_count_500_1000m': 0,
|
||||
f'poi_{category}_total_count': 0,
|
||||
f'poi_{category}_distance_to_nearest_m': 9999.0,
|
||||
f'poi_{category}_has_within_100m': 0,
|
||||
f'poi_{category}_has_within_300m': 0,
|
||||
f'poi_{category}_has_within_500m': 0,
|
||||
})
|
||||
|
||||
features.update(default_poi_features)
|
||||
|
||||
# Prepare features for Prophet model
|
||||
prophet_df = self._prepare_prophet_features(features)
|
||||
|
||||
@@ -925,21 +1008,34 @@ class PredictionService:
|
||||
'congestion_weekend_interaction': congestion * is_weekend
|
||||
}
|
||||
|
||||
# CRITICAL FIX: Extract POI (Point of Interest) features from the features dict
|
||||
# POI features start with 'poi_' prefix and must be included for models trained with them
|
||||
# This prevents "Regressor 'poi_retail_total_count' missing" errors
|
||||
poi_features = {}
|
||||
for key, value in features.items():
|
||||
if key.startswith('poi_'):
|
||||
# Ensure POI features are numeric (float or int)
|
||||
try:
|
||||
poi_features[key] = float(value) if isinstance(value, (int, float, str)) else 0.0
|
||||
except (ValueError, TypeError):
|
||||
poi_features[key] = 0.0
|
||||
|
||||
# Combine all features
|
||||
all_new_features = {**new_features, **interaction_features}
|
||||
|
||||
all_new_features = {**new_features, **interaction_features, **poi_features}
|
||||
|
||||
# Add all features at once using pd.concat to avoid fragmentation
|
||||
new_feature_df = pd.DataFrame([all_new_features])
|
||||
df = pd.concat([df, new_feature_df], axis=1)
|
||||
|
||||
logger.debug("Complete Prophet features prepared",
|
||||
|
||||
logger.debug("Complete Prophet features prepared",
|
||||
feature_count=len(df.columns),
|
||||
date=features['date'],
|
||||
season=df['season'].iloc[0],
|
||||
traffic_volume=df['traffic_volume'].iloc[0],
|
||||
average_speed=df['average_speed'].iloc[0],
|
||||
pedestrian_count=df['pedestrian_count'].iloc[0])
|
||||
|
||||
pedestrian_count=df['pedestrian_count'].iloc[0],
|
||||
poi_feature_count=len(poi_features))
|
||||
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user