From f1f1cf0eb26841fdcb2bcd2271db9cd08b7b8200 Mon Sep 17 00:00:00 2001 From: Ronald Diaz Date: Wed, 20 Apr 2022 09:16:11 -0400 Subject: [PATCH] completed assessment --- .gitignore | 3 + RUNTIME_INSTRUCTIONS.md | 55 +++++++++++ app.py | 67 ++++++++++++++ database.db | Bin 0 -> 8192 bytes database_connection_manager.py | 55 +++++++++++ requirements.txt | 30 ++++++ settings.txt | 12 +-- templates/base.html | 52 +++++++++++ templates/data.html | 90 ++++++++++++++++++ templates/settings.html | 48 ++++++++++ tests/__init__.py | 0 tests/test_database.py | 34 +++++++ tests/test_utils.py | 20 ++++ utils.py | 134 +++++++++++++++++++++++++++ weather_alert.log | 163 +++++++++++++++++++++++++++++++++ weather_alert_logging.py | 8 ++ 16 files changed, 765 insertions(+), 6 deletions(-) create mode 100644 .gitignore create mode 100644 RUNTIME_INSTRUCTIONS.md create mode 100644 app.py create mode 100644 database.db create mode 100644 database_connection_manager.py create mode 100644 requirements.txt create mode 100644 templates/base.html create mode 100644 templates/data.html create mode 100644 templates/settings.html create mode 100644 tests/__init__.py create mode 100644 tests/test_database.py create mode 100644 tests/test_utils.py create mode 100644 utils.py create mode 100644 weather_alert.log create mode 100644 weather_alert_logging.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49afcad --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +cache.sqlite +.env \ No newline at end of file diff --git a/RUNTIME_INSTRUCTIONS.md b/RUNTIME_INSTRUCTIONS.md new file mode 100644 index 0000000..a60d82f --- /dev/null +++ b/RUNTIME_INSTRUCTIONS.md @@ -0,0 +1,55 @@ +# Instructions for installing and running the project: + +1- Install requiraments in the requirements.txt file: + +``` +pip install -r requirements.txt +``` + +2- Set the enviroment variable ALERTUS_CREDENTIALS in a file called .env: + +``` +ALERTUS_CREDENTIALS=:> +``` + +3- Run Flask server + +``` +flask run --port 8000 +``` + +4- Run Tests + +``` +pytest +``` + +### Github Link: + +https://github.com/iamrdr97 + +### Explaning Challenges and Highlights: + +One of the biggest challenges I faced doing this project was implementing the user interface, since I had never used +technologies like jinja2 or bootstrap, and it had been a long time since I used JavaScript and jQuery. + +Another important challenge was accessing weather.gov and alertus services through their APIs, but using their +documentation allowed me to achieve my goal. + +What I am most proud of is having completed the project in the established time, despite the fact that to do, so I used +tools and frameworks that I do not usually use, such as flask and the ones mentioned above for the user interface. + +### List of relevant libraries used: + +#### requests: + +**requests** allows to send HTTP requests in an easy way + +#### requests-cache: + +**requests-cache** is a persistent cache that provides an easy way to get better performance with the python requests +library. + +#### pytest: + +**pytest** is a testing framework that allows to test the code in a simple way. diff --git a/app.py b/app.py new file mode 100644 index 0000000..6391a10 --- /dev/null +++ b/app.py @@ -0,0 +1,67 @@ +import json + +import requests_cache +from flask import Flask, render_template, request + +import database_connection_manager +from utils import load_settings_from_settings_txt_file, update_settings_file, get_new_forecast_and_store_it +from weather_alert_logging import get_logger + +app = Flask(__name__) + +requests_cache.install_cache('cache', backend='sqlite', expire_after=600) + +app.logger = get_logger() + + +@app.route('/') +def index(): + """ + This is the main page of the app. + Here the user can see the last ten forecasts saved in the database. + :return: The rendered data template. + """ + forecasts = database_connection_manager.get_last_ten_forecasts() + if forecasts is None: + app.logger.error('Could not fetch forecasts from database') + return {'error': 'Could not fetch the forecasts from database.'}, 500 + settings = load_settings_from_settings_txt_file() + return render_template('data.html', forecasts=forecasts, settings=settings) + + +@app.route('/settings/', methods=['GET', 'POST']) +def settings_view(): + """ + This is the settings page of the app. + Here the user can change the settings of the app. + :return: The rendered settings template. + """ + if request.method == 'POST': + settings = request.form + update_settings_file(settings) + app.logger.info(f'Settings updated successfully, new settings: {settings}') + else: + settings = load_settings_from_settings_txt_file() + return render_template('settings.html', settings=settings) + + +@app.route('/forecast/', methods=['GET']) +def forecast_view(): + """ + Endpoint to fetch the forecast for the next three hours and store it in the database. + :return: The forecast as a json object with the fields used to store them in the database. + """ + new_forecast = get_new_forecast_and_store_it() + if new_forecast is None: + app.logger.error('Could not fetch new forecast') + return {'error': 'Could not fetch the forecast.'}, 500 + forecasts = {"timestamp": new_forecast['timestamp'], "longitude": new_forecast['longitude'], + "latitude": new_forecast['latitude'], "first_forecast": new_forecast['first_forecast'], + "second_forecast": new_forecast['second_forecast'], "third_forecast": new_forecast['third_forecast'], + 'alert_generated': new_forecast['alert_generated'], 'alert_id': new_forecast['alert_id']} + app.logger.info(f'New forecast fetched successfully, new forecast: {forecasts}') + return json.dumps(forecasts) + + +if __name__ == '__main__': + app.run(debug=True, host='localhost', port=8000) diff --git a/database.db b/database.db new file mode 100644 index 0000000000000000000000000000000000000000..07ffbedeaea34da59dc77868e1d4ae1c1345210f GIT binary patch literal 8192 zcmeI#KTE?v7zXfr+d^7V;^x>Jgtia`(LuqXQG)-1U<^8z*j{5GNhP_#RVTlJ;_e4< z)6Kbvb8&L^OX!7|qEK*h5T2JDxxDvWhTrxc9@v2tygx`HPx56p%ZON;bH-Rjt&&uJPndbeJ>P=?D!%Ui6=!b`%8WyKM2Mh-pXe4vVO(%SN&eS zP!NCs1Rwwb2tWV=5P$##AOHaf{GmXjT+=j-h-zhXbVJF*!qJmkbsneATkW;C+m+#T jb9c|Cm*+<5dyr8au1^dO=jkh0W#;Jm<9+q6Jt6oB`Vex% literal 0 HcmV?d00001 diff --git a/database_connection_manager.py b/database_connection_manager.py new file mode 100644 index 0000000..f2f5a20 --- /dev/null +++ b/database_connection_manager.py @@ -0,0 +1,55 @@ +import sqlite3 + +from weather_alert_logging import get_logger + +logger = get_logger() + + +def check_table_exists(connection: sqlite3.Connection): + try: + connection.execute("SELECT * FROM forecasts;") + except sqlite3.OperationalError as e: + if 'no such table' in e.args[0]: + connection.execute( + "CREATE TABLE forecasts (timestamp REAL, long REAL, lat REAL, first_forecast INT, second_forecast " + "INTEGER, third_forecast INTEGER, alert_generated INTEGER, alert_id INTEGER );") + connection.commit() + + +def get_connection(): + try: + connection = sqlite3.connect('database.db') + check_table_exists(connection) + return connection + except sqlite3.Error as e: + logger.error(e) + return None + + +def get_last_ten_forecasts(): + try: + connection = get_connection() + cursor = connection.execute("SELECT * FROM forecasts ORDER BY timestamp DESC LIMIT 10;") + forecasts = cursor.fetchall() + except sqlite3.Error as e: + logger.error(e) + return None + + forecasts = [ + {'timestamp': forecast[0], 'longitude': forecast[1], 'latitude': forecast[2], 'first_forecast': forecast[3], + 'second_forecast': forecast[4], 'third_forecast': forecast[5], 'alert_generated': forecast[6], + 'alert_id': forecast[7] if forecast[7] else "NULL"} for forecast in forecasts] + + return forecasts + + +def insert_forecast(timestamp, long, lat, first_forecast, + second_forecast, third_forecast, alert_generated=0, alert_id=None): + try: + connection = get_connection() + connection.execute("INSERT INTO forecasts VALUES (?, ?, ?, ?, ?, ?, ?, ?);", + (timestamp, long, lat, first_forecast, second_forecast, third_forecast, alert_generated, + alert_id)) + connection.commit() + except sqlite3.Error as e: + logger.error(e) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4591289 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,30 @@ +appdirs==1.4.4 +attrs==21.4.0 +autopep8==1.6.0 +cattrs==1.10.0 +certifi==2021.10.8 +charset-normalizer==2.0.12 +click==8.1.2 +Flask==2.1.1 +idna==3.3 +importlib-metadata==4.11.3 +iniconfig==1.1.1 +itsdangerous==2.1.2 +Jinja2==3.1.1 +MarkupSafe==2.1.1 +packaging==21.3 +pluggy==1.0.0 +py==1.11.0 +pycodestyle==2.8.0 +pyparsing==3.0.8 +pytest==7.1.1 +python-dotenv==0.20.0 +requests==2.27.1 +requests-cache==0.9.3 +six==1.16.0 +toml==0.10.2 +tomli==2.0.1 +url-normalize==1.4.3 +urllib3==1.26.9 +Werkzeug==2.1.1 +zipp==3.8.0 diff --git a/settings.txt b/settings.txt index 1b8d204..65f0614 100644 --- a/settings.txt +++ b/settings.txt @@ -1,7 +1,7 @@ -# Location Information -latitude = 39.7456 -longitude = -97.0892 +#Location Information +latitude = 25.877353 +longitude = -80.130049 -# Application Settings -threshold_value = 80 -check_in_frequency = 30 +#Application Settings +threshold_value = 70 +check_in_frequency = 60 diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..988be55 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,52 @@ + + + + + + {% block title %} {% endblock %} Weather Alert + + + + + + +
{% block content %} {% endblock %}
+ + diff --git a/templates/data.html b/templates/data.html new file mode 100644 index 0000000..4d356a1 --- /dev/null +++ b/templates/data.html @@ -0,0 +1,90 @@ +{% extends 'base.html' %} {% block title %} Data {% endblock %} {% block content +%} + + + + + + + + + + + + + + + + {% for forecast in forecasts %} + + + + + + + + + + + + + + {% endfor %} + +
TimestampLongitueLatitudeFirst ForecastSecond ForecastThird ForecastAlert GeneratedAlert ID
{{ forecast.timestamp }}{{ forecast.longitude }}{{ forecast.latitude }}{{ forecast.first_forecast }}{{ forecast.second_forecast }}{{ forecast.third_forecast }}{{ forecast.alert_generated }}{{ forecast.alert_id }}
+ + + +{% endblock %} diff --git a/templates/settings.html b/templates/settings.html new file mode 100644 index 0000000..b5f7416 --- /dev/null +++ b/templates/settings.html @@ -0,0 +1,48 @@ +{% extends 'base.html' %} {% block title %} Settings {% endblock %} {% block +content %} + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +{% endblock %} diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_database.py b/tests/test_database.py new file mode 100644 index 0000000..c89f970 --- /dev/null +++ b/tests/test_database.py @@ -0,0 +1,34 @@ +from datetime import datetime + +import database_connection_manager + + +def test_sqlite_database_connection(): + """ + Test if the database connection is working + """ + connection = database_connection_manager.get_connection() + assert connection is not None + + +def test_get_last_ten_forecasts(): + """ + Test if the last ten forecasts are returned + """ + forecasts = database_connection_manager.get_last_ten_forecasts() + assert forecasts and len(forecasts) <= 10 + + +def test_insert_forecast(): + """ + Test if a forecast is inserted into the database + """ + timestamp = datetime.timestamp(datetime.now()) + database_connection_manager.insert_forecast(timestamp, 25.877369, -80.343601, + 80, 81, 82, 0, None) + forecasts = database_connection_manager.get_last_ten_forecasts() + + connection = database_connection_manager.get_connection() + cursor = connection.execute("SELECT * FROM forecasts ORDER BY timestamp DESC LIMIT 1") + latest_forecast = cursor.fetchone() + assert latest_forecast[0] == timestamp diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..8b000f2 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,20 @@ +from utils import * + + +def test_getting_new_forecasts(): + settings = { + 'latitude': 25.877369, + 'longitude': -80.343601, + 'threshold_value': 80, + 'check_in_frequency': 60, + } + forecasts = get_updated_forecasts_for_the_next_three_hours(settings) + assert len(forecasts) == 3 + + +def test_load_settings_txt_file(): + settings = load_settings_from_settings_txt_file() + assert settings['latitude'] == 25.877369 + assert settings['longitude'] == -80.343601 + assert settings['threshold_value'] == 80 + assert settings['check_in_frequency'] == 60 diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..b68c8cf --- /dev/null +++ b/utils.py @@ -0,0 +1,134 @@ +import os +import re +import xml.etree.ElementTree as ET +from datetime import datetime + +import requests + +import database_connection_manager +from weather_alert_logging import get_logger + +logger = get_logger() + + +def load_settings_from_settings_txt_file(): + settings = {} + try: + with open('settings.txt', 'r') as f: + file_lines = f.readlines() + for line in file_lines: + line = line.strip() + if line.startswith('#') or line == '': + continue + else: + key, value = line.split('=') + settings[key.strip()] = value.strip() + settings = { + 'latitude': float(settings['latitude']), + 'longitude': float(settings['longitude']), + 'threshold_value': int(settings['threshold_value']), + 'check_in_frequency': int(settings['check_in_frequency']) + } + except Exception as e: + if isinstance(e, FileNotFoundError): + logger.warning('settings.txt file not found. Creating new file.') + else: + logger.warning('Invalid settings.txt file. Creating new file.') + settings = { + 'latitude': 25.877353, + 'longitude': -80.130049, + 'threshold_value': 80, + 'check_in_frequency': 60 + } + update_settings_file(settings) + return settings + + +def update_settings_file(settings): + with open('settings.txt', 'w+') as f: + f.writelines([ + '#Location Information\n', + f"latitude = {settings['latitude']}\n", + f"longitude = {settings['longitude']}\n", + '\n', + '#Application Settings\n', + f"threshold_value = {settings['threshold_value']}\n", + f"check_in_frequency = {settings['check_in_frequency']}\n", + ]) + + +def get_updated_forecasts_for_the_next_three_hours(settings): + try: + headers = { + 'accept': 'application/vnd.noaa.dwml+xml', + } + response = requests.get( + "https://api.weather.gov/points/{},{}/".format(settings['latitude'], settings['longitude']), + headers=headers) + + if response.status_code != 200: + raise Exception(f'Invalid response code from NOAA API.{response.status_code}. details: {response.text}') + response = response.json() + response = response['properties']['forecastHourly'] + response = requests.get(response, headers=headers) + + if response.status_code != 200: + raise Exception(f'Invalid response code from NOAA API.{response.status_code}, details: {response.text}') + + root = ET.fromstring(response.text) + root = root[1][5][5] + forecasts = [root[1], root[2], root[3]] + for i, r in enumerate(forecasts): + forecasts[i] = int(re.search(r'\d+', r.text).group(0)) + except Exception as e: + logger.error(f'Error while getting forecasts from NOAA API. {e}') + return None + + return forecasts + + +def generate_alert(settings): + headers = { + 'accept': 'application/json; charset=UTF-8', + 'Content-Type': 'application/com.alertus-v1.0+json', + 'Authorization': f'Basic {os.environ.get("ALERTUS_CREDENTIALS")}' + } + + data = '{"durationSeconds": 10,"presetName": "Dev Candidate - Evacuate","alertProfileId": 402,"presetId": 2206,' \ + '"clientVersion": "3.15.220207","alertProfileName": "Demo Alert Profile - Evacuate","sender": ' \ + '"devcandidate","clientName": "alertus","priority": 0,"text": "Temperature is going to be higher than ' + \ + str(int(settings['threshold_value'])) + ' ' \ + 'degrees. Evacuate immediately."}' + + response = requests.post("https://demo.alertus.com/alertusmw/services/rest/activation/preset", headers=headers, + data=data) + + return int(response.text) + + +def get_new_forecast_and_store_it(): + settings = load_settings_from_settings_txt_file() + forecasts = get_updated_forecasts_for_the_next_three_hours(settings) + if forecasts is None: + return None + alert_generated = False + alert_id = None + if any(forecast > settings['threshold_value'] for forecast in forecasts): + alert_id = generate_alert(settings) + alert_generated = True + timestamp, long, lat, first_forecast, second_forecast, third_forecast, alert_generated, alert_id = datetime.timestamp( + datetime.now()), settings['longitude'], settings['latitude'], forecasts[0], forecasts[1], forecasts[ + 2], alert_generated, alert_id + database_connection_manager.insert_forecast(timestamp, long, lat, first_forecast, second_forecast, + third_forecast, alert_generated, alert_id) + + return { + 'timestamp': timestamp, + 'longitude': long, + 'latitude': lat, + 'first_forecast': first_forecast, + 'second_forecast': second_forecast, + 'third_forecast': third_forecast, + 'alert_generated': alert_generated, + 'alert_id': alert_id + } diff --git a/weather_alert.log b/weather_alert.log new file mode 100644 index 0000000..5a02c2f --- /dev/null +++ b/weather_alert.log @@ -0,0 +1,163 @@ +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:41:27] "GET / HTTP/1.1" 200 - +ERROR:weather_alert:Error while getting forecasts from NOAA API. Invalid response code from NOAA API.500, details: { + "correlationId": "90f794d", + "title": "Unexpected Problem", + "type": "https://api.weather.gov/problems/UnexpectedProblem", + "status": 500, + "detail": "An unexpected problem has occurred.", + "instance": "https://api.weather.gov/requests/90f794d" +} +ERROR:weather_alert:Could not fetch new forecast +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:41:39] "GET /forecast/ HTTP/1.1" 500 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426110.550919, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118437} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:41:50] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426118.985449, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118438} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:41:58] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:42:02] "GET /settings/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:42:05] "GET / HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:42:06] "GET /settings/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:42:07] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426137.996276, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118439} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:42:18] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426148.023619, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118440} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:42:28] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:42:31] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426162.57375, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118441} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:42:42] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:43:08] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426199.570653, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118442} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:43:19] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426209.166179, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118443} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:43:29] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:43:30] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426220.941236, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118444} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:43:40] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426230.866471, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118445} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:43:50] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:45:43] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426355.364548, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118446} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:45:55] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426364.321452, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118447} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:46:04] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:46:24] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426395.318124, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118448} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:46:35] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426405.354707, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118449} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:46:45] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650426415.280141, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118450} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:46:55] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:48:08] "GET / HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:56:55] "GET / HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:58:38] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650427129.822405, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118452} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:58:49] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650427139.35994, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118453} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:58:59] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650427149.298064, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118454} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:59:09] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650427159.234771, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 74, 'second_forecast': 74, 'third_forecast': 73, 'alert_generated': True, 'alert_id': 118455} +INFO:werkzeug:127.0.0.1 - - [19/Apr/2022 23:59:19] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 07:30:08] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650454221.567568, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 71, 'second_forecast': 73, 'third_forecast': 75, 'alert_generated': True, 'alert_id': 118471} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 07:30:21] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650454229.142556, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 71, 'second_forecast': 73, 'third_forecast': 75, 'alert_generated': True, 'alert_id': 118472} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 07:30:29] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650454239.174681, 'longitude': -80.343601, 'latitude': 25.877369, 'first_forecast': 71, 'second_forecast': 73, 'third_forecast': 75, 'alert_generated': True, 'alert_id': 118473} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 07:30:39] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +ERROR:weather_alert:Error while getting forecasts from NOAA API. Invalid response code from NOAA API.500, details: { + "correlationId": "141e4c7c", + "title": "Unexpected Problem", + "type": "https://api.weather.gov/problems/UnexpectedProblem", + "status": 500, + "detail": "An unexpected problem has occurred.", + "instance": "https://api.weather.gov/requests/141e4c7c" +} +ERROR:weather_alert:Could not fetch new forecast +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:38:23] "GET /forecast/ HTTP/1.1" 500 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:38:23] "GET / HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:38:47] "GET /settings/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:38:49] "GET / HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:39:22] "GET /settings/ HTTP/1.1" 200 - +INFO:weather_alert:Settings updated successfully, new settings: ImmutableMultiDict([('latitude', '25.877353'), ('longitude', '-80.130049'), ('threshold_value', '80'), ('check_in_frequency', '10')]) +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:39:26] "POST /settings/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:39:27] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650458379.01588, 'longitude': -80.130049, 'latitude': 25.877353, 'first_forecast': 74, 'second_forecast': 75, 'third_forecast': 76, 'alert_generated': False, 'alert_id': None} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:39:39] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650458387.601904, 'longitude': -80.130049, 'latitude': 25.877353, 'first_forecast': 74, 'second_forecast': 75, 'third_forecast': 76, 'alert_generated': False, 'alert_id': None} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:39:47] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650458397.618505, 'longitude': -80.130049, 'latitude': 25.877353, 'first_forecast': 74, 'second_forecast': 75, 'third_forecast': 76, 'alert_generated': False, 'alert_id': None} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:39:57] "GET /forecast/ HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650458407.616536, 'longitude': -80.130049, 'latitude': 25.877353, 'first_forecast': 74, 'second_forecast': 75, 'third_forecast': 76, 'alert_generated': False, 'alert_id': None} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:40:07] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:40:12] "GET /settings/ HTTP/1.1" 200 - +INFO:weather_alert:Settings updated successfully, new settings: ImmutableMultiDict([('latitude', '25.877353'), ('longitude', '-80.130049'), ('threshold_value', '80'), ('check_in_frequency', '10')]) +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:40:17] "POST /settings/ HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +WARNING:weather_alert:settings.txt file not found. Creating new file. +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:41:16] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650458536.885086, 'longitude': -80.130049, 'latitude': 25.877353, 'first_forecast': 74, 'second_forecast': 75, 'third_forecast': 76, 'alert_generated': False, 'alert_id': None} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:42:16] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:42:41] "GET /settings/ HTTP/1.1" 200 - +INFO:weather_alert:Settings updated successfully, new settings: ImmutableMultiDict([('latitude', '25.877353'), ('longitude', '-80.130049'), ('threshold_value', '70'), ('check_in_frequency', '60')]) +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:42:46] "POST /settings/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:42:48] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650458629.725356, 'longitude': -80.130049, 'latitude': 25.877353, 'first_forecast': 74, 'second_forecast': 75, 'third_forecast': 76, 'alert_generated': True, 'alert_id': 118474} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:43:49] "GET /forecast/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:43:57] "GET /settings/ HTTP/1.1" 200 - +INFO:weather_alert:Settings updated successfully, new settings: ImmutableMultiDict([('latitude', '25.877353'), ('longitude', '-80.130049'), ('threshold_value', '70'), ('check_in_frequency', '60')]) +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:44:07] "POST /settings/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 08:44:09] "GET / HTTP/1.1" 200 - +INFO:werkzeug: * Running on http://localhost:8000 (Press CTRL+C to quit) +INFO:werkzeug: * Restarting with stat +WARNING:werkzeug: * Debugger is active! +INFO:werkzeug: * Debugger PIN: 194-019-107 +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 09:07:16] "GET / HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 09:07:20] "GET /settings/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 09:07:21] "GET / HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 09:07:23] "GET /settings/ HTTP/1.1" 200 - +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 09:07:25] "GET / HTTP/1.1" 200 - +INFO:weather_alert:New forecast fetched successfully, new forecast: {'timestamp': 1650460106.877767, 'longitude': -80.130049, 'latitude': 25.877353, 'first_forecast': 74, 'second_forecast': 75, 'third_forecast': 76, 'alert_generated': True, 'alert_id': 118476} +INFO:werkzeug:127.0.0.1 - - [20/Apr/2022 09:08:26] "GET /forecast/ HTTP/1.1" 200 - diff --git a/weather_alert_logging.py b/weather_alert_logging.py new file mode 100644 index 0000000..e9fa1ec --- /dev/null +++ b/weather_alert_logging.py @@ -0,0 +1,8 @@ +import logging + +logging.basicConfig(filename='weather_alert.log', level=logging.INFO) +logger = logging.getLogger("weather_alert") + + +def get_logger(): + return logger