66import json
77import requests
88from flask import Flask , jsonify , Response , request
9+ from flask .logging import create_logger
910from flask_cors import CORS
1011
11- from sqlalchemy import (exc , create_engine , MetaData , Table ,
12- Column , Integer , Boolean , Text , insert , Date , DateTime )
1312from sqlalchemy .sql import func
13+ from sqlalchemy import (
14+ exc ,
15+ create_engine ,
16+ MetaData ,
17+ Table ,
18+ Column ,
19+ Integer ,
20+ Boolean ,
21+ Text ,
22+ insert ,
23+ Date ,
24+ select ,
25+ DateTime ,
26+ )
1427
1528AEROAPI_BASE_URL = "https://aeroapi.flightaware.com/aeroapi"
1629AEROAPI_KEY = os .environ ["AEROAPI_KEY" ]
1932
2033# pylint: disable=invalid-name
2134app = Flask (__name__ )
35+ logger = create_logger (app )
2236CORS (app )
2337
2438# create the SQL engine using SQLite
3448metadata_obj = MetaData ()
3549# Table for alert configurations
3650aeroapi_alert_configurations = Table (
37- "aeroapi_alert_configurations" ,
38- metadata_obj ,
39- Column ("fa_alert_id" , Integer , primary_key = True ),
40- Column ("ident" , Text ),
41- Column ("origin" , Text ),
42- Column ("destination" , Text ),
43- Column ("aircraft_type" , Text ),
44- Column ("start_date" , Date ),
45- Column ("end_date" , Date ),
46- Column ("max_weekly" , Integer ),
47- Column ("eta" , Integer ),
48- Column ("arrival" , Boolean ),
49- Column ("cancelled" , Boolean ),
50- Column ("departure" , Boolean ),
51- Column ("diverted" , Boolean ),
52- Column ("filed" , Boolean ),
53- )
51+ "aeroapi_alert_configurations" ,
52+ metadata_obj ,
53+ Column ("fa_alert_id" , Integer , primary_key = True ),
54+ Column ("ident" , Text ),
55+ Column ("origin" , Text ),
56+ Column ("destination" , Text ),
57+ Column ("aircraft_type" , Text ),
58+ Column ("start_date" , Date ),
59+ Column ("end_date" , Date ),
60+ Column ("max_weekly" , Integer ),
61+ Column ("eta" , Integer ),
62+ Column ("arrival" , Boolean ),
63+ Column ("cancelled" , Boolean ),
64+ Column ("departure" , Boolean ),
65+ Column ("diverted" , Boolean ),
66+ Column ("filed" , Boolean ),
67+ )
5468# Table for POSTed alerts
5569aeroapi_alerts = Table (
56- "aeroapi_alerts" ,
57- metadata_obj ,
58- Column ("id" , Integer , primary_key = True , autoincrement = True ),
59- Column ("time_alert_received" , DateTime (timezone = True ), server_default = func .now ()), # Store time in UTC that the alert was received
60- Column ("long_description" , Text ),
61- Column ("short_description" , Text ),
62- Column ("summary" , Text ),
63- Column ("event_code" , Text ),
64- Column ("alert_id" , Integer ),
65- Column ("fa_flight_id" , Text ),
66- Column ("ident" , Text ),
67- Column ("registration" , Text ),
68- Column ("aircraft_type" , Text ),
69- Column ("origin" , Text ),
70- Column ("destination" , Text )
71- )
70+ "aeroapi_alerts" ,
71+ metadata_obj ,
72+ Column ("id" , Integer , primary_key = True , autoincrement = True ),
73+ Column (
74+ "time_alert_received" , DateTime (timezone = True ), server_default = func .now ()
75+ ), # Store time in UTC that the alert was received
76+ Column ("long_description" , Text ),
77+ Column ("short_description" , Text ),
78+ Column ("summary" , Text ),
79+ Column ("event_code" , Text ),
80+ Column ("alert_id" , Integer ),
81+ Column ("fa_flight_id" , Text ),
82+ Column ("ident" , Text ),
83+ Column ("registration" , Text ),
84+ Column ("aircraft_type" , Text ),
85+ Column ("origin" , Text ),
86+ Column ("destination" , Text ),
87+ )
7288
7389
7490def create_tables ():
@@ -79,10 +95,12 @@ def create_tables():
7995 try :
8096 # Create the table(s) if they don't exist
8197 metadata_obj .create_all (engine )
82- app . logger .info ("Table(s) successfully created (if not already created)" )
98+ logger .info ("Table(s) successfully created (if not already created)" )
8399 except exc .SQLAlchemyError as e :
84100 # Since creation of table(s) is a critical error, raise exception
85- app .logger .error (f"SQL error occurred during creation of table(s) (CRITICAL - THROWING ERROR): { e } " )
101+ logger .error (
102+ f"SQL error occurred during creation of table(s) (CRITICAL - THROWING ERROR): { e } "
103+ )
86104 raise e
87105
88106
@@ -99,13 +117,47 @@ def insert_into_table(data_to_insert: Dict[str, Any], table: Table) -> int:
99117 stmt = insert (table )
100118 conn .execute (stmt , data_to_insert )
101119 conn .commit ()
102- app . logger .info (f"Data successfully inserted into table { table .name } " )
120+ logger .info (f"Data successfully inserted into table { table .name } " )
103121 except exc .SQLAlchemyError as e :
104- app . logger .error (f"SQL error occurred during insertion into table { table .name } : { e } " )
122+ logger .error (f"SQL error occurred during insertion into table { table .name } : { e } " )
105123 return - 1
106124 return 0
107125
108126
127+ @app .route ("/posted_alerts" )
128+ def get_posted_alerts () -> Response :
129+ """
130+ Function to return all the triggered POSTed alerts via the SQL table.
131+ Returns a JSON payload of all the POSTed alerts.
132+ """
133+ data : Dict [str , Any ] = {"posted_alerts" : []}
134+ with engine .connect () as conn :
135+ stmt = select (aeroapi_alerts )
136+ result = conn .execute (stmt )
137+ conn .commit ()
138+ for row in result :
139+ data ["posted_alerts" ].append (dict (row ))
140+
141+ return jsonify (data )
142+
143+
144+ @app .route ("/alert_configs" )
145+ def get_alert_configs () -> Response :
146+ """
147+ Function to return all the alerts that are currently configured
148+ via the SQL table. Returns a JSON payload of all the configured alerts.
149+ """
150+ data : Dict [str , Any ] = {"alert_configurations" : []}
151+ with engine .connect () as conn :
152+ stmt = select (aeroapi_alert_configurations )
153+ result = conn .execute (stmt )
154+ conn .commit ()
155+ for row in result :
156+ data ["alert_configurations" ].append (dict (row ))
157+
158+ return jsonify (data )
159+
160+
109161@app .route ("/post" , methods = ["POST" ])
110162def handle_alert () -> Tuple [Response , int ]:
111163 """
@@ -146,7 +198,9 @@ def handle_alert() -> Tuple[Response, int]:
146198 r_status = 200
147199 except KeyError as e :
148200 # If value doesn't exist, do not insert into table and produce error
149- app .logger .error (f"Alert POST request did not have one or more keys with data. Will process but will return 400: { e } " )
201+ logger .error (
202+ f"Alert POST request did not have one or more keys with data. Will process but will return 400: { e } "
203+ )
150204 r_title = "Missing info in request"
151205 r_detail = "At least one value to insert in the database is missing in the post request"
152206 r_status = 400
@@ -166,7 +220,7 @@ def create_alert() -> Response:
166220 # initialize response headers
167221 r_alert_id : int = - 1
168222 r_success : bool = False
169- r_description : str = ''
223+ r_description : str = ""
170224 # Process json
171225 content_type = request .headers .get ("Content-Type" )
172226 data : Dict [str , Any ]
@@ -190,15 +244,21 @@ def create_alert() -> Response:
190244 if "max_weekly" not in data :
191245 data ["max_weekly" ] = 1000
192246
193- app . logger .info (f"Making AeroAPI request to POST { api_resource } " )
247+ logger .info (f"Making AeroAPI request to POST { api_resource } " )
194248 result = AEROAPI .post (f"{ AEROAPI_BASE_URL } { api_resource } " , json = data )
195249 if result .status_code != 201 :
196250 # return to front end the error, decode and clean the response
197251 try :
198252 processed_json = result .json ()
199- r_description = f"Error code { result .status_code } with the following description: { processed_json ['detail' ]} "
253+ r_description = (
254+ f"Error code { result .status_code } with the following "
255+ f"description: { processed_json ['detail' ]} "
256+ )
200257 except json .decoder .JSONDecodeError :
201- r_description = f"Error code { result .status_code } could not be parsed into JSON. The following is the HTML response given: { result .text } "
258+ r_description = (
259+ f"Error code { result .status_code } could not be parsed into JSON. "
260+ f"The following is the HTML response given: { result .text } "
261+ )
202262 else :
203263 # Package created alert and put into database
204264 fa_alert_id = int (result .headers ["Location" ][8 :])
@@ -228,7 +288,10 @@ def create_alert() -> Response:
228288 data ["fa_alert_id" ] = fa_alert_id
229289
230290 if insert_into_table (data , aeroapi_alert_configurations ) == - 1 :
231- r_description = f"Database insertion error, check your database configuration. Alert has still been configured with alert id { r_alert_id } "
291+ r_description = (
292+ f"Database insertion error, check your database configuration. "
293+ f"Alert has still been configured with alert id { r_alert_id } "
294+ )
232295 else :
233296 r_success = True
234297 r_description = f"Request sent successfully with alert id { r_alert_id } "
0 commit comments