Skip to content

Commit

Permalink
Merge pull request #10 from flightaware/BCK-7005_display_alerts
Browse files Browse the repository at this point in the history
Display POSTed Alerts
  • Loading branch information
jacob-y-pan authored Jul 26, 2022
2 parents 7707a0e + d0cd396 commit 962189c
Showing 1 changed file with 108 additions and 45 deletions.
153 changes: 108 additions & 45 deletions alerts_backend/python/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,24 @@
import json
import requests
from flask import Flask, jsonify, Response, request
from flask.logging import create_logger
from flask_cors import CORS

from sqlalchemy import (exc, create_engine, MetaData, Table,
Column, Integer, Boolean, Text, insert, Date, DateTime)
from sqlalchemy.sql import func
from sqlalchemy import (
exc,
create_engine,
MetaData,
Table,
Column,
Integer,
Boolean,
Text,
insert,
Date,
select,
DateTime,
)

AEROAPI_BASE_URL = "https://aeroapi.flightaware.com/aeroapi"
AEROAPI_KEY = os.environ["AEROAPI_KEY"]
Expand All @@ -19,6 +32,7 @@

# pylint: disable=invalid-name
app = Flask(__name__)
logger = create_logger(app)
CORS(app)

# create the SQL engine using SQLite
Expand All @@ -34,41 +48,43 @@
metadata_obj = MetaData()
# Table for alert configurations
aeroapi_alert_configurations = Table(
"aeroapi_alert_configurations",
metadata_obj,
Column("fa_alert_id", Integer, primary_key=True),
Column("ident", Text),
Column("origin", Text),
Column("destination", Text),
Column("aircraft_type", Text),
Column("start_date", Date),
Column("end_date", Date),
Column("max_weekly", Integer),
Column("eta", Integer),
Column("arrival", Boolean),
Column("cancelled", Boolean),
Column("departure", Boolean),
Column("diverted", Boolean),
Column("filed", Boolean),
)
"aeroapi_alert_configurations",
metadata_obj,
Column("fa_alert_id", Integer, primary_key=True),
Column("ident", Text),
Column("origin", Text),
Column("destination", Text),
Column("aircraft_type", Text),
Column("start_date", Date),
Column("end_date", Date),
Column("max_weekly", Integer),
Column("eta", Integer),
Column("arrival", Boolean),
Column("cancelled", Boolean),
Column("departure", Boolean),
Column("diverted", Boolean),
Column("filed", Boolean),
)
# Table for POSTed alerts
aeroapi_alerts = Table(
"aeroapi_alerts",
metadata_obj,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("time_alert_received", DateTime(timezone=True), server_default=func.now()), # Store time in UTC that the alert was received
Column("long_description", Text),
Column("short_description", Text),
Column("summary", Text),
Column("event_code", Text),
Column("alert_id", Integer),
Column("fa_flight_id", Text),
Column("ident", Text),
Column("registration", Text),
Column("aircraft_type", Text),
Column("origin", Text),
Column("destination", Text)
)
"aeroapi_alerts",
metadata_obj,
Column("id", Integer, primary_key=True, autoincrement=True),
Column(
"time_alert_received", DateTime(timezone=True), server_default=func.now()
), # Store time in UTC that the alert was received
Column("long_description", Text),
Column("short_description", Text),
Column("summary", Text),
Column("event_code", Text),
Column("alert_id", Integer),
Column("fa_flight_id", Text),
Column("ident", Text),
Column("registration", Text),
Column("aircraft_type", Text),
Column("origin", Text),
Column("destination", Text),
)


def create_tables():
Expand All @@ -79,10 +95,12 @@ def create_tables():
try:
# Create the table(s) if they don't exist
metadata_obj.create_all(engine)
app.logger.info("Table(s) successfully created (if not already created)")
logger.info("Table(s) successfully created (if not already created)")
except exc.SQLAlchemyError as e:
# Since creation of table(s) is a critical error, raise exception
app.logger.error(f"SQL error occurred during creation of table(s) (CRITICAL - THROWING ERROR): {e}")
logger.error(
f"SQL error occurred during creation of table(s) (CRITICAL - THROWING ERROR): {e}"
)
raise e


Expand All @@ -99,13 +117,47 @@ def insert_into_table(data_to_insert: Dict[str, Any], table: Table) -> int:
stmt = insert(table)
conn.execute(stmt, data_to_insert)
conn.commit()
app.logger.info(f"Data successfully inserted into table {table.name}")
logger.info(f"Data successfully inserted into table {table.name}")
except exc.SQLAlchemyError as e:
app.logger.error(f"SQL error occurred during insertion into table {table.name}: {e}")
logger.error(f"SQL error occurred during insertion into table {table.name}: {e}")
return -1
return 0


@app.route("/posted_alerts")
def get_posted_alerts() -> Response:
"""
Function to return all the triggered POSTed alerts via the SQL table.
Returns a JSON payload of all the POSTed alerts.
"""
data: Dict[str, Any] = {"posted_alerts": []}
with engine.connect() as conn:
stmt = select(aeroapi_alerts)
result = conn.execute(stmt)
conn.commit()
for row in result:
data["posted_alerts"].append(dict(row))

return jsonify(data)


@app.route("/alert_configs")
def get_alert_configs() -> Response:
"""
Function to return all the alerts that are currently configured
via the SQL table. Returns a JSON payload of all the configured alerts.
"""
data: Dict[str, Any] = {"alert_configurations": []}
with engine.connect() as conn:
stmt = select(aeroapi_alert_configurations)
result = conn.execute(stmt)
conn.commit()
for row in result:
data["alert_configurations"].append(dict(row))

return jsonify(data)


@app.route("/post", methods=["POST"])
def handle_alert() -> Tuple[Response, int]:
"""
Expand Down Expand Up @@ -146,7 +198,9 @@ def handle_alert() -> Tuple[Response, int]:
r_status = 200
except KeyError as e:
# If value doesn't exist, do not insert into table and produce error
app.logger.error(f"Alert POST request did not have one or more keys with data. Will process but will return 400: {e}")
logger.error(
f"Alert POST request did not have one or more keys with data. Will process but will return 400: {e}"
)
r_title = "Missing info in request"
r_detail = "At least one value to insert in the database is missing in the post request"
r_status = 400
Expand All @@ -166,7 +220,7 @@ def create_alert() -> Response:
# initialize response headers
r_alert_id: int = -1
r_success: bool = False
r_description: str = ''
r_description: str = ""
# Process json
content_type = request.headers.get("Content-Type")
data: Dict[str, Any]
Expand All @@ -190,15 +244,21 @@ def create_alert() -> Response:
if "max_weekly" not in data:
data["max_weekly"] = 1000

app.logger.info(f"Making AeroAPI request to POST {api_resource}")
logger.info(f"Making AeroAPI request to POST {api_resource}")
result = AEROAPI.post(f"{AEROAPI_BASE_URL}{api_resource}", json=data)
if result.status_code != 201:
# return to front end the error, decode and clean the response
try:
processed_json = result.json()
r_description = f"Error code {result.status_code} with the following description: {processed_json['detail']}"
r_description = (
f"Error code {result.status_code} with the following "
f"description: {processed_json['detail']}"
)
except json.decoder.JSONDecodeError:
r_description = f"Error code {result.status_code} could not be parsed into JSON. The following is the HTML response given: {result.text}"
r_description = (
f"Error code {result.status_code} could not be parsed into JSON. "
f"The following is the HTML response given: {result.text}"
)
else:
# Package created alert and put into database
fa_alert_id = int(result.headers["Location"][8:])
Expand Down Expand Up @@ -228,7 +288,10 @@ def create_alert() -> Response:
data["fa_alert_id"] = fa_alert_id

if insert_into_table(data, aeroapi_alert_configurations) == -1:
r_description = f"Database insertion error, check your database configuration. Alert has still been configured with alert id {r_alert_id}"
r_description = (
f"Database insertion error, check your database configuration. "
f"Alert has still been configured with alert id {r_alert_id}"
)
else:
r_success = True
r_description = f"Request sent successfully with alert id {r_alert_id}"
Expand Down

0 comments on commit 962189c

Please sign in to comment.