diff --git a/app/app.py b/app/app.py index 3b7568b..86b4671 100644 --- a/app/app.py +++ b/app/app.py @@ -20,6 +20,9 @@ metrics.init_app(app) app.config["SESSION_COOKIE_SAMESITE"] = "Lax" app.config["SESSION_COOKIE_SECURE"] = env_SECURE +with app.app_context(): + db.create_all() + logger.info("Worker ready") if __name__ == "__main__": diff --git a/app/config.py b/app/config.py index 673e0c7..011f48d 100644 --- a/app/config.py +++ b/app/config.py @@ -6,6 +6,7 @@ env_DEBUG = os.environ.get("DEBUG", "").lower() == "true" env_SECURE = os.environ.get("SECURE", "").lower() == "true" env_OWM_KEY = os.environ.get("OWM_API_KEY", "") env_OWM_UNITS = os.environ.get("OWM_UNITS", "standard") +env_CACHE_TIME = int(os.environ.get("CACHE_TIME", 5)) env_AUTHORIZED_CALLERS = list(os.environ.get("AUTHORIZED_CALLERS", "")) env_SECRET_KEY = os.environ.get("SECRET_KEY", os.urandom(24)) if not env_SECRET_KEY: diff --git a/app/docker-compose.yml b/app/docker-compose.yml index ea6f507..ee90b50 100644 --- a/app/docker-compose.yml +++ b/app/docker-compose.yml @@ -14,5 +14,6 @@ services: - SECRET_KEY= # Should be a long random value, randomly regenerated every launch if not specified - SECURE=FALSE # Set to True when using HTTPS - OWM_API_KEY= # API key from OpenWeatherMap (One Call 3.0 and Geocoding) - - OWM_UNITS= # Units for OpenWeatherMap (Standard, Metric, Imperial) + - OWM_UNITS= # Units for OpenWeatherMap (Standard, Metric, Imperial) + - CACHE_TIME= # Time (in minutes) that weather data is cached - AUTHORIZED_CALLERS= # Comma seperated list of authorized phone numbers, eg +13365550916,+13365553721 diff --git a/app/models.py b/app/models.py index 9ca8c99..d28c7ab 100644 --- a/app/models.py +++ b/app/models.py @@ -1,5 +1,34 @@ from datetime import datetime, timezone - from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() + + +class Zipcode(db.Model): + __tablename__ = "zipcodes" + zip = db.Column(db.Integer, primary_key=True) + results = db.Column(db.JSON, nullable=False) + timestamp = db.Column( + db.DateTime(timezone=True), default=datetime.now(timezone.utc), nullable=False + ) + + +class Weather(db.Model): + __tablename__ = "weather" + lat_long = db.Column(db.String(100), primary_key=True) + results = db.Column(db.JSON, nullable=False) + last_timestamp = db.Column( + db.DateTime(timezone=True), + default=datetime.now(timezone.utc), + onupdate=datetime.now(timezone.utc), + nullable=False, + ) + + +class Stats(db.Model): + __tablename__ = "stats" + id = db.Column(db.Integer, autoincrement=True, primary_key=True) + lat_long = db.Column(db.String(100), nullable=False) + timestamp = db.Column( + db.DateTime(timezone=True), default=datetime.now(timezone.utc), nullable=False + ) diff --git a/app/utils.py b/app/utils.py index 9287e6b..a39bae9 100644 --- a/app/utils.py +++ b/app/utils.py @@ -7,8 +7,8 @@ import typing as t import requests import traceback -import models -from config import env_OWM_KEY, env_OWM_UNITS +from models import db, Stats, Weather, Zipcode +from config import env_OWM_KEY, env_OWM_UNITS, env_CACHE_TIME logger = logging.getLogger("gunicorn.error") weather_template = "The current temperature is: {0}. The real feel temperature is: {1}. The high is: {2}. The low is: {3}. The current humidity is: {4} percent. The summary for today is: {5}." @@ -73,6 +73,10 @@ def _get_weather(lat, long): round(weather_json["current"]["humidity"]), weather_json["daily"][0]["summary"], ) + lat_long = str(lat) + "," + str(long) + s = Stats(lat_long=lat_long) + db.session.add(s) + db.session.commit() return weather except Exception as e: logger.error("Error in _get_weather: " + str(e)) @@ -84,12 +88,27 @@ def _get_weather_json(lat, long): url = "https://api.openweathermap.org/data/3.0/onecall?lat={0}&lon={1}&exclude=alerts,minutely,hourly&units={2}&appid={3}".format( lat, long, env_OWM_UNITS, env_OWM_KEY ) + lat_long = str(lat) + "," + str(long) + current_time = datetime.now(timezone.utc) try: + w = Weather.query.filter(Weather.lat_long == lat_long).first() + if w and w.last_timestamp.replace(tzinfo=timezone.utc) >= current_time - timedelta(minutes=env_CACHE_TIME): + logger.info("Weather cache hit!") + return w.results + logger.info("Weather cache miss!") + response = requests.get(url) if response.status_code == 200: weather = response.json() + if w: + w.results = weather + w.last_timestamp = current_time + else: + w = Weather(lat_long=lat_long, results=weather) + db.session.add(w) + db.session.commit() return weather else: logger.error("Error in _get_weather_json: " + str(response.status_code)) @@ -106,11 +125,20 @@ def _get_cords(zipcode): ) try: + z = Zipcode.query.filter_by(zip=zipcode).first() + if z: + logger.info("Zipcode cache hit!") + return z.results["lat"], z.results["lon"] + logger.info("Zipcode cache miss!") + response = requests.get(url) if response.status_code == 200: locale = response.json() logger.info(locale) + new_z = Zipcode(zip=zipcode, results=locale) + db.session.add(new_z) + db.session.commit() return locale["lat"], locale["lon"] else: logger.error("Error in _get_cords: " + str(response.status_code))