diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index e76103fc..4e8e66de 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -27,6 +27,7 @@ jobs: echo "REACT_APP_BACKEND_URL=${{ secrets.REACT_APP_BACKEND_URL }}" > ./recommender-back/.env echo "MONGO_URI=${{ secrets.MONGO_URI }}" >> ./recommender-back/.env echo "FLASK_DEBUG=False" >> ./recommender-back/.env + echo "ENVIRONMENT=production" >> ./recommender-back/.env - name: Build and push frontend uses: docker/build-push-action@v2 with: diff --git a/recommender-back/src/apis/current.py b/recommender-back/src/apis/current.py index 53cf4b04..d463c0c7 100644 --- a/recommender-back/src/apis/current.py +++ b/recommender-back/src/apis/current.py @@ -1,9 +1,11 @@ import copy import math +import os from .poi import PointOfInterest from ..services.data_fetcher import DataFetcher from ..config import Config +ENVIRONMENT = os.environ.get('ENVIRONMENT') class Current: def __init__(self, fetcher: DataFetcher): @@ -11,7 +13,9 @@ def __init__(self, fetcher: DataFetcher): self.weather = None self.aqi = None self.get_current_weather() - self.get_current_air_quality() + + if ENVIRONMENT == "production": + self.get_current_air_quality() def get_current_weather(self): ''' @@ -146,10 +150,10 @@ def find_nearest_stations_weather_data(self, poi: PointOfInterest): missing_fields.remove(key) if not missing_fields or not weather: smallest, nearest = float('inf'), '' - if len(aqi) > 0: + if aqi != None: nearest = self.find_nearest_stations_aqi(aqi, lat, lon) returned.setdefault( - 'Air quality', aqi[nearest]['Air quality']) + 'Air quality', aqi[nearest]['Air quality']) break del weather[nearest] poi.weather['Current'] = returned diff --git a/recommender-back/src/apis/manager.py b/recommender-back/src/apis/manager.py index 1006a7a3..2c67232a 100644 --- a/recommender-back/src/apis/manager.py +++ b/recommender-back/src/apis/manager.py @@ -1,4 +1,5 @@ import json +import os from requests import Timeout from .current import Current from .poi import PointOfInterest @@ -6,6 +7,8 @@ from ..services.api_fetcher import InternalApiService from ..db.db import get_collection +ENVIRONMENT = os.environ.get('ENVIRONMENT') + def get_simulated_pois_as_json(air_temperature, wind_speed, humidity, precipitation, cloud_amount, air_quality, current_time, sunrise, sunset): """ @@ -47,9 +50,10 @@ def get_pois_as_json(category="All"): weather_fetcher = DataFetcher() current = Current(weather_fetcher) forecast_data = InternalApiService.fetch_forecast() - aqi_data = InternalApiService.fetch_aqi() - aqi_data = _replace_datetime_in_aqi_data(forecast_data, aqi_data) - forecast_data = _add_aqi_to_forecast(forecast_data, aqi_data) + if ENVIRONMENT == "production": + aqi_data = InternalApiService.fetch_aqi() + aqi_data = _replace_datetime_in_aqi_data(forecast_data, aqi_data) + forecast_data = _add_aqi_to_forecast(forecast_data, aqi_data) updated_data = [] for poi in pois: if category not in poi.categories: diff --git a/recommender-back/src/apis/poi.py b/recommender-back/src/apis/poi.py index 20d34bf6..f6d81508 100644 --- a/recommender-back/src/apis/poi.py +++ b/recommender-back/src/apis/poi.py @@ -25,14 +25,16 @@ def __init__(self, name=None, latitude=None, longitude=None, not_accessible_for= } def _extract_weather_data(self, data): - return { - 'wind_speed': float(data.get('Wind speed').split(' ')[0]), - 'precipitation': float(data.get('Precipitation').split(' ')[0]), - 'clouds': float(data.get('Cloud amount').split(' ')[0]) * 0.01, - 'temperature': float(data.get('Air temperature').split(' ')[0]), - 'humidity': float(data.get('Humidity').split(' ')[0]) * 0.01, - 'air_quality': float(data.get('Air quality').split(' ')[0]), - } + extracted_weather = {'wind_speed': float(data.get('Wind speed').split(' ')[0]), + 'precipitation': float(data.get('Precipitation').split(' ')[0]), + 'clouds': float(data.get('Cloud amount').split(' ')[0]) * 0.01, + 'temperature': float(data.get('Air temperature').split(' ')[0]), + 'humidity': float(data.get('Humidity').split(' ')[0]) * 0.01} + + if data.get('Air quality'): + extracted_weather['air_quality'] = float(data.get('Air quality').split(' ')[0]) + + return extracted_weather def calculate_score(self, cur_time=None, sunrise=None, sunset=None): """ @@ -72,12 +74,20 @@ def calculate_score(self, cur_time=None, sunrise=None, sunset=None): scorer = self.scorers["Indoor"] if scorer: - data['Score'] = scorer.score( - weather_data['temperature'], weather_data['wind_speed'], - weather_data['humidity'], weather_data['precipitation'], - weather_data['clouds'], weather_data['air_quality'], - sunrise_time, sunset_time, current_time - ) + if weather_data.get('air_quality'): + data['Score'] = scorer.score( + weather_data['temperature'], weather_data['wind_speed'], + weather_data['humidity'], weather_data['precipitation'], + weather_data['clouds'], weather_data['air_quality'], + sunrise_time, sunset_time, current_time + ) + else: + data['Score'] = scorer.score( + weather_data['temperature'], weather_data['wind_speed'], + weather_data['humidity'], weather_data['precipitation'], + weather_data['clouds'], 0, + sunrise_time, sunset_time, current_time + ) def set_simulated_weather(self, air_temperature, wind_speed, humidity, precipitation, cloud_amount, air_quality): @@ -95,7 +105,6 @@ def set_simulated_weather(self, air_temperature, wind_speed, humidity, This method sets simulated weather data for the Point of Interest (POI) to facilitate testing the score calculation functionality. """ - self.weather = { "Weather": { "Air temperature": f"{air_temperature} °C", diff --git a/recommender-back/src/services/scoring/indoor_scorer.py b/recommender-back/src/services/scoring/indoor_scorer.py index 5f6229cc..77f430e5 100644 --- a/recommender-back/src/services/scoring/indoor_scorer.py +++ b/recommender-back/src/services/scoring/indoor_scorer.py @@ -21,7 +21,7 @@ def temperature_score(temperature): return 1 - math.exp(-0.04 * (IndoorScorer.OPTIMAL_TEMPERATURE_LOW - temperature)) else: return 1 - math.exp(0.2 * (IndoorScorer.OPTIMAL_TEMPERATURE_HIGH - temperature)) - + @staticmethod def day_time_score(current_time, sunrise_time, sunset_time): if sunrise_time <= current_time <= sunset_time: @@ -40,6 +40,9 @@ def score(self, temperature, wind_speed, humidity, precipitation, clouds, air_qu self.day_time_score(current_time, sunrise_time, sunset_time) + IndoorScorer.CLOUDS_WEIGHT * (1 - math.exp(-3 * clouds)) + IndoorScorer.WIND_SPEED_WEIGHT * (1 - math.exp(-0.3 * wind_speed)) + - self.humidity_score(humidity) + - IndoorScorer.AIR_QUALITY_WEIGHT * math.exp(0.5 * (1 - air_quality))) + self.humidity_score(humidity)) + + if air_quality != 0: + score += IndoorScorer.AIR_QUALITY_WEIGHT * math.exp(0.5 * (1 - air_quality)) + return round(score, 2) diff --git a/recommender-back/src/services/scoring/outdoor_scorer.py b/recommender-back/src/services/scoring/outdoor_scorer.py index cb05767b..5d80ee98 100644 --- a/recommender-back/src/services/scoring/outdoor_scorer.py +++ b/recommender-back/src/services/scoring/outdoor_scorer.py @@ -40,6 +40,9 @@ def score(self, temperature, wind_speed, humidity, precipitation, clouds, air_qu self.day_time_score(current_time, sunrise_time, sunset_time) + OutdoorScorer.CLOUDS_WEIGHT * math.exp(-clouds) + OutdoorScorer.WIND_SPEED_WEIGHT * math.exp(-wind_speed) + - self.humidity_score(humidity) + - OutdoorScorer.AIR_QUALITY_WEIGHT * math.exp(1 - air_quality)) + self.humidity_score(humidity)) + + if air_quality != 0: + score += OutdoorScorer.AIR_QUALITY_WEIGHT * math.exp(1 - air_quality) + return round(score, 2)