Begun adding tests.

portal
jprochazka 2024-07-17 17:30:22 -04:00
rodzic f2ae197d7a
commit ebe3ae8a13
22 zmienionych plików z 429 dodań i 227 usunięć

Wyświetl plik

@ -1,4 +1,6 @@
.vscode
.venv
__pycache__
instance
instance
.coverage
htmlcov

Wyświetl plik

@ -1,3 +1,5 @@
import os
from datetime import timedelta
from flask import Flask, render_template
from flask_apscheduler import APScheduler
@ -13,8 +15,19 @@ from backend.routes.settings import settings
from backend.routes.tokens import tokens
from backend.routes.users import users
def create_app():
def create_app(test_config=None):
app = Flask(__name__)
if test_config is None:
app.config.from_pyfile('config.py', silent=True)
else:
app.config.from_mapping(test_config)
try:
os.makedirs(app.instance_path)
except OSError:
pass
app.json.sort_keys = False
app.config["JWT_SECRET_KEY"] = "CHANGE_THIS_IN_PRODUCTION" # Change this!

Wyświetl plik

@ -1,47 +1,54 @@
import click
import MySQLdb
import os
import psycopg2
import sqlite3
import yaml
from flask import current_app, g
config=yaml.safe_load(open("config.yml"))
def create_connection():
match config['database']['use'].lower():
case 'mysql':
return MySQLdb.connect(
host=config['database']['mysql']['host'],
user=config['database']['mysql']['user'],
password=config['database']['mysql']['password'],
database=config['database']['mysql']['database']
)
case 'postgresql':
return psycopg2.connect(
host=config['database']['mysql']['host'],
user=config['database']['mysql']['user'],
password=config['database']['mysql']['password'],
database=config['database']['mysql']['database']
)
case 'sqlite':
return sqlite3.connect(config['database']['sqlite']['path'])
def init_app(app):
app.teardown_appcontext(close_db)
app.cli.add_command(init_db_command)
def get_db():
if 'db' not in g:
match config['database']['use'].lower():
case 'mysql':
g.db = MySQLdb.connect(
host=config['database']['mysql']['host'],
user=config['database']['mysql']['user'],
password=config['database']['mysql']['password'],
database=config['database']['mysql']['database']
)
case 'postgresql':
g.db = psycopg2.connect(
host=config['database']['mysql']['host'],
user=config['database']['mysql']['user'],
password=config['database']['mysql']['password'],
database=config['database']['mysql']['database']
)
case 'sqlite':
g.db = sqlite3.connect(os.path.join(current_app.instance_path, 'adsbportal.sqlite3'))
return g.db
def close_db(e=None):
db = g.pop('db', None)
if db is not None:
db.close()
def init_db():
connection = create_connection()
cursor=connection.cursor()
db = get_db()
sql_file = open(f"./backend/schemas/{config['database']['use'].lower()}.sql", "r")
sql_script = sql_file.read()
cursor.executescript(sql_script)
connection.commit()
connection.close()
sql_file = os.path.join("schemas", f"{config['database']['use'].lower()}.sql")
with current_app.open_resource(sql_file, "r") as f:
db.executescript(f.read())
@click.command('init-db')
def init_db_command():
init_db()
click.echo('Database inititalized')
click.echo('Database initialized')

Wyświetl plik

@ -4,11 +4,11 @@ import logging
from datetime import datetime
from flask_apscheduler import APScheduler
from urllib.request import urlopen
from backend.db import create_connection
from backend.db import get_db
scheduler = APScheduler()
connection = None
cursor = None
db = None
now = None
class DataProcessor(object):
@ -42,8 +42,8 @@ class DataProcessor(object):
for aircraft in aircraft_data:
self.process_aircraft(aircraft)
connection.close()
db.commit()
return
# Process the aircraft
@ -66,7 +66,6 @@ class DataProcessor(object):
"UPDATE adsb_aircraft SET last_seen = %s WHERE icao = %s",
(now, aircraft["hex"])
)
connection.commit()
cursor.execute(
"SELECT id FROM adsb_aircraft WHERE icao = %s",
(aircraft["hex"],)
@ -82,7 +81,6 @@ class DataProcessor(object):
"INSERT INTO adsb_aircraft (icao, firstSeen, last_seen) VALUES (%s, %s, %s)",
(aircraft["hex"], now, now)
)
connection.commit()
aircraft_id = cursor.lastrowid
except Exception as ex:
logging.error(f'Error encountered while trying to insert aircraft {aircraft["hex"]}', exc_info=ex)
@ -116,7 +114,6 @@ class DataProcessor(object):
"UPDATE adsb_flights SET last_seen = %s WHERE flight = %s",
(now, flight)
)
connection.commit()
cursor.execute(
"SELECT id FROM adsb_flights WHERE flight = %s",
(flight,)
@ -132,7 +129,6 @@ class DataProcessor(object):
"INSERT INTO adsb_flights (aircraft, flight, firstSeen, last_seen) VALUES (%s, %s, %s, %s)",
(aircraft_id, flight, now, now)
)
connection.commit()
flight_id = cursor.lastrowid
except Exception as ex:
logging.error(f'Error encountered while trying to insert flight {flight}', exc_info=ex)
@ -180,7 +176,6 @@ class DataProcessor(object):
"INSERT INTO adsb_positions (flight, time, message, squawk, latitude, longitude, track, altitude, verticleRate, speed, aircraft) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
(flight_id, now, aircraft["messages"], squawk, aircraft["lat"], aircraft["lon"], aircraft["track"], altitude, aircraft["geom_rate"], aircraft["gs"], aircraft_id)
)
connection.commit()
except Exception as ex:
logging.error(f'Error encountered while inserting position data for message ID {aircraft["messages"]} related to flight {flight_id}', exc_info=ex)
return
@ -195,8 +190,8 @@ def data_collection_job():
# Setup and begin the data collection job
processor.log("-- BEGINING FLIGHT RECORDER JOB")
connection = create_connection()
cursor = connection.cursor()
now = datetime.now()
db=get_db()
cursor=db.cursor()
now=datetime.now()
processor.process_all_aircraft()
processor.log("-- FLIGHT RECORD JOB COMPLETE")

Wyświetl plik

@ -2,11 +2,11 @@ import logging
from datetime import datetime, timedelta
from flask_apscheduler import APScheduler
from backend.db import create_connection
from backend.db import get_db
scheduler = APScheduler()
connection = None
cursor = None
db = None
now = None
class MaintenanceProcessor(object):
@ -39,14 +39,11 @@ class MaintenanceProcessor(object):
cutoff_date = datetime.now() - timedelta(days = days_to_save)
self.purge_aircraft(cutoff_date)
self.purge_positions(cutoff_date)
db.commit()
else:
self.log("Maintenance is disabled")
connection.commit()
connection.close()
return
# Remove aircraft not seen since the specified date
@ -141,7 +138,7 @@ def maintenance_job():
# Setup and begin the maintenance job
processor.log("-- BEGINING PORTAL MAINTENANCE JOB")
connection = create_connection()
cursor = connection.cursor()
db=get_db()
cursor=db.cursor()
processor.begin_maintenance()
processor.log("-- PORTAL MAINTENANCE JOB COMPLETE")

Wyświetl plik

@ -1,8 +1,7 @@
import logging
from flask import abort, Blueprint, jsonify, request
from flask_jwt_extended import jwt_required
from backend.db import create_connection
from backend.db import get_db
aircraft = Blueprint('aircraft', __name__)
@ -12,9 +11,12 @@ def get_aircraft_by_icao(icao):
data=[]
try:
connection = create_connection()
cursor=connection.cursor()
cursor.execute("SELECT * FROM aircraft WHERE icao = %s", (icao,))
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM aircraft WHERE icao = ?", (icao,))
#cursor.execute("SELECT * FROM aircraft WHERE icao = %s", (icao,))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
for result in result:
@ -22,8 +24,6 @@ def get_aircraft_by_icao(icao):
except Exception as ex:
logging.error(f"Error encountered while trying to get aircraft using ICAO {icao}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
if not data:
abort(404, description="Not Found")
@ -40,11 +40,23 @@ def get_aircraft_positions(icao):
positions=[]
try:
connection = create_connection()
cursor=connection.cursor()
cursor.execute("SELECT id FROM aircraft WHERE icao = %s", (icao,))
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM aircraft WHERE icao = ?", (icao,))
#cursor.execute("SELECT COUNT(*) FROM aircraft WHERE icao = %s", (icao,))
if cursor.fetchone()[0] == 0:
return "Not Found", 404
cursor.execute("SELECT id FROM aircraft WHERE icao = ?", (icao,))
#cursor.execute("SELECT id FROM aircraft WHERE icao = %s", (icao,))
aircraft_id = cursor.fetchone()[0]
cursor.execute("SELECT * FROM positions WHERE aircraft = %s ORDER BY time LIMIT %s, %s", (aircraft_id, offset, limit))
cursor.execute("SELECT * FROM positions WHERE aircraft = ? ORDER BY time LIMIT ?, ?", (aircraft_id, offset, limit))
#cursor.execute("SELECT * FROM positions WHERE aircraft = %s ORDER BY time LIMIT %s, %s", (aircraft_id, offset, limit))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
for result in result:
@ -52,8 +64,6 @@ def get_aircraft_positions(icao):
except Exception as ex:
logging.error(f"Error encountered while trying to get flight positions for aircraft ICAO {icao}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
data={}
data['offset'] = offset
@ -67,15 +77,19 @@ def get_aircraft_positions(icao):
def get_aircraft():
offset = request.args.get('offset', default=0, type=int)
limit = request.args.get('limit', default=50, type=int)
if offset < 0 or limit < 1 or limit > 100:
abort(400, description="Bad Request")
aircraft_data=[]
try:
connection = create_connection()
cursor=connection.cursor()
cursor.execute("SELECT * FROM aircraft ORDER BY last_seen DESC, icao LIMIT %s, %s", (offset, limit))
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM aircraft ORDER BY last_seen DESC, icao LIMIT ?, ?", (offset, limit))
#cursor.execute("SELECT * FROM aircraft ORDER BY last_seen DESC, icao LIMIT %s, %s", (offset, limit))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
for result in result:
@ -83,8 +97,6 @@ def get_aircraft():
except Exception as ex:
logging.error('Error encountered while trying to get aircraft', exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
data={}
data['offset'] = offset
@ -99,15 +111,13 @@ def get_aircraft():
@aircraft.route('/api/aircraft/count', methods=['GET'])
def get_aircraft_count():
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM aircraft")
count=cursor.fetchone()[0]
count = cursor.fetchone()[0]
except Exception as ex:
logging.error('Error encountered while trying to get aircraft count', exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
response = jsonify(aircraft=count)
response.headers.add('Access-Control-Allow-Origin', '*')

Wyświetl plik

@ -4,7 +4,7 @@ from datetime import datetime
from flask import abort, Blueprint, jsonify, request
from flask_jwt_extended import jwt_required
from marshmallow import Schema, fields, ValidationError
from backend.db import create_connection
from backend.db import get_db
blog = Blueprint('blog', __name__)
@ -28,18 +28,16 @@ def post_blog_post():
return jsonify(err.messages), 400
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute(
"INSERT INTO blog_posts (date, title, author, content) VALUES (%s, %s, %s, %s)",
(datetime.now(), payload['title'], payload['author'], payload['content'])
)
connection.commit()
db.commit()
except Exception as ex:
logging.error('Error encountered while trying to add post blog post', exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "Created", 201
@ -47,19 +45,17 @@ def post_blog_post():
@jwt_required()
def delete_blog_post(blog_post_id):
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM blog_posts WHERE id = %s", (blog_post_id,))
if cursor.fetchone()[0] == 0:
return "Not Found", 404
else:
cursor.execute("DELETE FROM blog_posts WHERE id = %s", (blog_post_id,))
connection.commit()
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to delete blog post id {blog_post_id}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "No Content", 204
@ -68,8 +64,8 @@ def get_blog_post(blog_post_id):
data=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM blog_posts WHERE id = %s", (blog_post_id,))
columns=[x[0] for x in cursor.description]
results = cursor.fetchall()
@ -78,8 +74,6 @@ def get_blog_post(blog_post_id):
except Exception as ex:
logging.error(f"Error encountered while trying to get blog post id {blog_post_id,}", blog_post_id, exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
if not data:
abort(404, description="Not Found")
@ -97,8 +91,8 @@ def put_blog_post(blog_post_id):
return jsonify(err.messages), 400
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM blog_posts WHERE id = %s", (blog_post_id,))
if cursor.fetchone()[0] == 0:
return "Not Found", 404
@ -107,12 +101,10 @@ def put_blog_post(blog_post_id):
"UPDATE blog_posts SET date = %s, title = %s, content = %s WHERE id = %s",
(datetime.now(), payload['title'], payload['content'], blog_post_id)
)
connection.commit()
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to put blog post id {blog_post_id}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "No Content", 204
@ -126,8 +118,8 @@ def get_blog_posts():
blog_posts=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM blog_posts ORDER BY date DESC LIMIT %s, %s", (offset, limit))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
@ -136,8 +128,6 @@ def get_blog_posts():
except Exception as ex:
logging.error('Error encountered while trying to get blog posts', exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
data={}
data['offset'] = offset

Wyświetl plik

@ -2,7 +2,7 @@ import logging
from flask import abort, Blueprint, jsonify, request
from flask_jwt_extended import jwt_required
from backend.db import create_connection
from backend.db import get_db
flights = Blueprint('flights', __name__)
@ -12,8 +12,8 @@ def get_flight(flight):
data=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM flights WHERE flight = %s", (flight,))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
@ -22,8 +22,6 @@ def get_flight(flight):
except Exception as ex:
logging.error(f"Error encountered while trying to get flight {flight}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
if not data:
abort(404, description="Not Found")
@ -40,8 +38,8 @@ def get_flight_positions(flight):
positions=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM positions WHERE flight = %s ORDER BY time LIMIT %s, %s", (flight, offset, limit))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
@ -50,8 +48,6 @@ def get_flight_positions(flight):
except Exception as ex:
logging.error(f"Error encountered while trying to get flight positions for flight {flight}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
data={}
data['offset'] = offset
@ -71,8 +67,8 @@ def get_flights():
flights=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM flights ORDER BY last_seen DESC, flight LIMIT %s, %s", (offset, limit))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
@ -81,8 +77,6 @@ def get_flights():
except Exception as ex:
logging.error('Error encountered while trying to get flights', exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
data={}
data['offset'] = offset
@ -97,15 +91,13 @@ def get_flights():
@flights.route('/api/flights/count', methods=['GET'])
def get_flights_count():
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM flights")
count=cursor.fetchone()[0]
except Exception as ex:
logging.error('Error encountered while trying to get flight count', exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
response = jsonify(flights=count)
response.headers.add('Access-Control-Allow-Origin', '*')

Wyświetl plik

@ -3,7 +3,7 @@ import logging
from flask import abort, Blueprint, jsonify, request
from flask_jwt_extended import jwt_required
from marshmallow import Schema, fields, ValidationError
from backend.db import create_connection
from backend.db import get_db
links = Blueprint('links', __name__)
@ -26,18 +26,16 @@ def post_link():
return jsonify(err.messages), 400
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute(
"INSERT INTO links (name, address) VALUES (%s, %s)",
(payload['name'], payload['address'])
)
connection.commit()
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to post link", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "Created", 201
@ -45,19 +43,17 @@ def post_link():
@jwt_required()
def delete_link(link_id):
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM links WHERE id = %s", (link_id,))
if cursor.fetchone()[0] == 0:
return "Not Found", 404
else:
cursor.execute("DELETE FROM links WHERE id = %s", (link_id,))
connection.commit()
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to delete link id {link_id}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "No Content", 204
@ -67,8 +63,8 @@ def get_link(link_id):
data=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM links WHERE id = %s", (link_id,))
columns=[x[0] for x in cursor.description]
results = cursor.fetchall()
@ -77,8 +73,6 @@ def get_link(link_id):
except Exception as ex:
logging.error(f"Error encountered while trying to get link id {link_id}", link_id, exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
if not data:
abort(404, description="Not Found")
@ -97,8 +91,8 @@ def put_link(id):
return jsonify(err.messages), 400
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM links WHERE id = %s", (id))
if cursor.fetchone()[0] == 0:
abort(404, description="Not Found")
@ -107,11 +101,10 @@ def put_link(id):
"UPDATE links SET name = %s, address = %s WHERE id = %s",
(payload_object['name'], payload_object['address'], id)
)
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to put link id {id}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "No Content", 204
@ -125,8 +118,8 @@ def get_links():
links=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM links ORDER BY name LIMIT %s, %s", (offset, limit))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
@ -135,8 +128,6 @@ def get_links():
except Exception as ex:
logging.error(f"Error encountered while trying to get links", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
data={}
data['offset'] = offset

Wyświetl plik

@ -2,7 +2,7 @@ import logging
from flask import abort, Blueprint, jsonify, request
from flask_jwt_extended import jwt_required
from backend.db import create_connection
from backend.db import get_db
notifications = Blueprint('notifications', __name__)
@ -11,19 +11,17 @@ notifications = Blueprint('notifications', __name__)
@jwt_required()
def delete_notification(flight):
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM notifications WHERE flight = %s", (flight,))
if cursor.fetchone()[0] == 0:
return "Not Found", 404
else:
cursor.execute("DELETE FROM notifications WHERE flight = %s", (flight,))
connection.commit()
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to delete blog post id {flight}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "No Content", 204
@ -31,8 +29,8 @@ def delete_notification(flight):
@jwt_required()
def post_notification(flight):
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM notifications WHERE flight = %s", (flight,))
if cursor.fetchone()[0] > 0:
return "Bad Request", 400
@ -41,12 +39,10 @@ def post_notification(flight):
"INSERT INTO notifications (flight) VALUES (%s)",
(flight,)
)
connection.commit()
db.commit()
except Exception as ex:
logging.error('Error encountered while trying to add notification', exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "Created", 201
@ -61,8 +57,8 @@ def get_notifications():
notifications=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM notifications ORDER BY flight LIMIT %s, %s", (offset, limit))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
@ -71,8 +67,6 @@ def get_notifications():
except Exception as ex:
logging.error(f"Error encountered while trying to get notifications", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
data={}
data['offset'] = offset

Wyświetl plik

@ -3,7 +3,7 @@ import logging
from flask import abort, Blueprint, jsonify, request
from flask_jwt_extended import jwt_required
from marshmallow import Schema, fields, ValidationError
from backend.db import create_connection
from backend.db import get_db
settings = Blueprint('settings', __name__)
@ -22,8 +22,8 @@ def put_setting():
return jsonify(err.messages), 400
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM settings WHERE name = %s", (payload['name'],))
if cursor.fetchone()[0] == 0:
abort(404, description="Not Found")
@ -32,12 +32,10 @@ def put_setting():
"UPDATE settings SET value = %s WHERE name = %s",
(payload['value'], payload['name'])
)
connection.commit()
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to put setting named {payload['name']}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "No Content", 204
@ -47,8 +45,8 @@ def get_setting(name):
data=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM settings WHERE name = %s", (name,))
columns=[x[0] for x in cursor.description]
results = cursor.fetchall()
@ -57,8 +55,6 @@ def get_setting(name):
except Exception as ex:
logging.error(f"Error encountered while trying to get setting named {name}", id, exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
if not data:
abort(404, description="Not Found")
@ -71,8 +67,8 @@ def get_settings():
settings=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM settings ORDER BY name")
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
@ -81,7 +77,5 @@ def get_settings():
except Exception as ex:
logging.error(f"Error encountered while trying to get settings", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return jsonify(settings), 200

Wyświetl plik

@ -3,7 +3,7 @@ import logging
from flask import abort, Blueprint, jsonify, request
from flask_jwt_extended import jwt_required
from marshmallow import Schema, fields, ValidationError
from backend.db import create_connection
from backend.db import get_db
users = Blueprint('users', __name__)
@ -29,18 +29,16 @@ def post_user():
return jsonify(err.messages), 400
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute(
"INSERT INTO users (name, email, password, administrator) VALUES (%s, %s, %s, %s)",
(payload['name'], payload['email'], payload['password'], payload['administrator'])
)
connection.commit()
db.commit()
except Exception as ex:
logging.error('Error encountered while trying to post user', exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "Created", 201
@ -48,19 +46,17 @@ def post_user():
@jwt_required()
def delete_user(email):
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM users WHERE email = %s", (email,))
if cursor.fetchone()[0] == 0:
return "Not Found", 404
else:
cursor.execute("DELETE FROM users WHERE email = %s", (email,))
connection.commit()
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to delete user related to email {email}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "No Content", 204
@ -70,8 +66,8 @@ def get_user(email):
data=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
columns=[x[0] for x in cursor.description]
results = cursor.fetchall()
@ -80,8 +76,6 @@ def get_user(email):
except Exception as ex:
logging.error(f"Error encountered while trying to get user related to email {email,}", email, exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
if not data:
abort(404, description="Not Found")
@ -97,8 +91,8 @@ def put_user(email):
return jsonify(err.messages), 400
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT COUNT(*) FROM users WHERE email = %s", (email,))
if cursor.fetchone()[0] == 0:
return "Not Found", 404
@ -107,12 +101,10 @@ def put_user(email):
"UPDATE users SET name = %s, password = %s, administrator = %s WHERE email = %s",
(payload['name'], payload['password'], payload['administrator'], email)
)
connection.commit()
db.commit()
except Exception as ex:
logging.error(f"Error encountered while trying to put user related to email {email}", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
return "No Content", 204
@ -127,8 +119,8 @@ def get_users():
users=[]
try:
connection = create_connection()
cursor=connection.cursor()
db=get_db()
cursor=db.cursor()
cursor.execute("SELECT * FROM users ORDER BY name LIMIT %s, %s", (offset, limit))
columns=[x[0] for x in cursor.description]
result=cursor.fetchall()
@ -137,8 +129,6 @@ def get_users():
except Exception as ex:
logging.error(f"Error encountered while trying to get users", exc_info=ex)
abort(500, description="Internal Server Error")
finally:
connection.close()
data={}
data['offset'] = offset

Wyświetl plik

@ -7,16 +7,6 @@ DROP TABLE IF EXISTS links;
DROP TABLE IF EXISTS positions;
DROP TABLE IF EXISTS settings;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`email` varchar(75) NOT NULL,
`login` varchar(25) NOT NULL,
`password` varchar(255) NOT NULL,
`administrator` bit DEFAULT 0,
PRIMARY KEY (`id`)
);
CREATE TABLE `aircraft` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`icao` varchar(24) NOT NULL,
@ -80,4 +70,14 @@ CREATE TABLE `settings` (
`name` varchar(50) NOT NULL,
`value` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`email` varchar(75) NOT NULL,
`login` varchar(25) NOT NULL,
`password` varchar(255) NOT NULL,
`administrator` bit DEFAULT 0,
PRIMARY KEY (`id`)
);

Wyświetl plik

@ -7,16 +7,6 @@ DROP TABLE IF EXISTS links;
DROP TABLE IF EXISTS positions;
DROP TABLE IF EXISTS settings;
CREATE TABLE users (
`id` int NOT NULL GENERATED ALWAYS AS IDENTITY,
`name` varchar(100) NOT NULL,
`email` varchar(75) NOT NULL,
`login` varchar(25) NOT NULL,
`password` varchar(255) NOT NULL,
`administrator` bit DEFAULT 0,
PRIMARY KEY (id)
);
CREATE TABLE aircraft (
`id` int NOT NULL GENERATED ALWAYS AS IDENTITY,
`icao` varchar(24) NOT NULL,
@ -66,7 +56,7 @@ CREATE TABLE positions (
`squawk` int DEFAULT NULL,
`latitude` double precision NOT NULL,
`longitude` double precision NOT NULL,
`track int` NOT NULL,
`track` int NOT NULL,
`altitude` int NOT NULL,
`verticle_rate` int NOT NULL,
`speed` int DEFAULT NULL,
@ -80,4 +70,14 @@ CREATE TABLE settings (
`name` varchar(50) NOT NULL,
`value` varchar(100) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE users (
`id` int NOT NULL GENERATED ALWAYS AS IDENTITY,
`name` varchar(100) NOT NULL,
`email` varchar(75) NOT NULL,
`login` varchar(25) NOT NULL,
`password` varchar(255) NOT NULL,
`administrator` bit DEFAULT 0,
PRIMARY KEY (id)
);

Wyświetl plik

@ -7,19 +7,11 @@ DROP TABLE IF EXISTS links;
DROP TABLE IF EXISTS positions;
DROP TABLE IF EXISTS settings;
CREATE TABLE users (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT NOT NULL,
`email` TEXT NOT NULL,
`password` TEXT,
`token` TEXT
);
CREATE TABLE aircraft (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`icao` TEXT NOT NULL,
`firstSeen` TEXT NOT NULL,
`lastSeen` TEXT
`first_seen` TEXT NOT NULL,
`last_seen` TEXT
);
CREATE TABLE blog_posts (
@ -71,4 +63,13 @@ CREATE TABLE settings (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT NOT NULL,
`value` TEXT NOT NULL
);
CREATE TABLE users (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT NOT NULL,
`email` TEXT NOT NULL,
`login` TEXT NOT NULL,
`password` TEXT,
`administrator` INTEGER DEFAULT 0
);

Wyświetl plik

@ -1,7 +1,5 @@
database:
use: "mysql"
sqlite:
path: "adsbportal.sqlite3"
use: "sqlite"
mysql:
host: "127.0.0.1"
user: "portaluser"

Wyświetl plik

@ -0,0 +1,6 @@
[tool.pytest.ini_options]
testpaths = ["tests"]
[tool.coverage.run]
branch = true
source = ["backend"]

Wyświetl plik

@ -0,0 +1,36 @@
import os
import pytest
import tempfile
from backend import create_app
from backend.db import get_db, init_db
with open(os.path.join(os.path.dirname(__file__), 'data.sql'), 'rb') as f:
_data_sql = f.read().decode('utf8')
@pytest.fixture
def app():
db_fd, db_path = tempfile.mkstemp()
app = create_app({
'TESTING': True,
'DATABASE': db_path,
})
with app.app_context():
init_db()
get_db().executescript(_data_sql)
yield app
os.close(db_fd)
os.unlink(db_path)
@pytest.fixture
def client(app):
return app.test_client()
@pytest.fixture
def runner(app):
return app.test_cli_runner()

Wyświetl plik

@ -0,0 +1,31 @@
INSERT INTO aircraft (`icao`, `first_seen`, `last_seen`)
VALUES
('icao01', '2024-07-17 01:10:11', '2024-06-17 01:11:01'),
('icao02', '2024-07-17 02:20:22', '2024-06-17 02:22:02'),
('icao03', '2024-07-17 03:30:33', '2024-06-17 03:33:03'),
('icao04', '2024-07-17 04:40:44', '2024-06-17 04:44:04'),
('icao05', '2024-07-17 05:50:55', '2024-06-17 05:55:05');
INSERT INTO positions (`flight`, `aircraft`, `time`, `message`, `squawk`, `latitude`, `longitude`, `track`, `altitude`, `verticle_rate`, `speed`)
VALUES
(, 1, '2024-06-17 01:11:01', , , , , 5, , , )
(, 1, '2024-06-17 01:11:46', , , , , 4, , , )
(, 1, '2024-06-17 01:11:31', , , , , 3, , , )
(, 1, '2024-06-17 01:11:16', , , , , 2, , , )
(, 1, '2024-06-17 01:10:01', , , , , 1, , , )
(, 1, '2024-07-17 01:11:11', , , , , 5, , , )
(, 1, '2024-07-17 01:10:56', , , , , 4, , , )
(, 1, '2024-07-17 01:10:41', , , , , 3, , , )
(, 1, '2024-07-17 01:10:26', , , , , 2, , , )
(, 1, '2024-07-17 01:10:11', , , , , 1, , , )
(, 5, '2024-06-17 05:55:05', , , , , 4, , , )
(, 5, '2024-07-17 05:50:20', , , , , 3, , , )
(, 5, '2024-07-17 05:50:35', , , , , 2, , , )
(, 5, '2024-07-17 05:50:55', , , , , 1, , , )
INSERT INTO users (`name`, `email`, `login`, `password`, `administrator`)
VALUES
('User One', 'noreply@adsbportal.com', 'login_one', '$2y$0htWdxS7PxTvIwJNo2COJ7Rywgif4En0TmJbDvrjLRfWZOBX526yJUKW', 1),
('User Two', 'noreply@adsbreceiver.net', 'login_two', '$2y$ui7QK047JldTekx828J2rfSVQ7N5yo6ETQIYGoBqpfFRbNr3EvWzQzt6', 0);

Wyświetl plik

@ -0,0 +1,26 @@
import pytest
import sqlite3
from backend.db import get_db
def test_get_close_db(app):
with app.app_context():
db=get_db()
assert db is get_db()
with pytest.raises(sqlite3.ProgrammingError) as e:
db.execute('SELECT 1')
assert 'closed' in str(e.value)
def test_init_db_command(runner, monkeypatch):
class Recorder(object):
called = False
def fake_init_db():
Recorder.called = True
monkeypatch.setattr('backend.db.init_db', fake_init_db)
result = runner.invoke(args=['init-db'])
assert 'initialized' in result.output
assert Recorder.called

Wyświetl plik

@ -0,0 +1,9 @@
from backend import create_app
def test_config():
assert not create_app().testing
assert create_app({'TESTING': True}).testing
def test_api_docs(client):
response = client.get('/api/docs')
assert b'adsb_receiver_api_v1_oas3.yaml' in response.data

Wyświetl plik

@ -0,0 +1,120 @@
# GET /api/aircraft/{icao}
def test_get_aircraft_200(client):
response = client.get('/api/aircraft/icao01')
assert response.status_code == 200
assert response.json['aircraft']['id'] == 1
assert response.json['aircraft']['icao'] == "icao01"
assert response.json['aircraft']['first_seen'] == "2024-07-17 01:10:11"
assert response.json['aircraft']['last_seen'] == "2024-06-17 01:11:01"
def test_get_aircraft_404(client):
response = client.get('/api/aircraft/icao00')
assert response.status_code == 404
# GET /api/aircraft/{icao}/positions
def test_get_aircraft_200(client):
response = client.get('/api/aircraft/icao00/positions')
assert response.status_code == 200
def test_get_aircraft_404(client):
response = client.get('/api/aircraft/icao00/positions')
assert response.status_code == 404
# GET /api/aircraft
def test_get_aircraft_200(client):
response = client.get('/api/aircraft')
assert response.status_code == 200
assert response.json['offset'] == 0
assert response.json['limit'] == 50
assert response.json['count'] == 5
assert response.json['aircraft'][0]['id'] == 5
assert response.json['aircraft'][0]['icao'] == "icao05"
assert response.json['aircraft'][0]['first_seen'] == "2024-07-17 05:50:55"
assert response.json['aircraft'][0]['last_seen'] == "2024-06-17 05:55:05"
assert response.json['aircraft'][1]['id'] == 4
assert response.json['aircraft'][1]['icao'] == "icao04"
assert response.json['aircraft'][1]['first_seen'] == "2024-07-17 04:40:44"
assert response.json['aircraft'][1]['last_seen'] == "2024-06-17 04:44:04"
assert response.json['aircraft'][2]['id'] == 3
assert response.json['aircraft'][2]['icao'] == "icao03"
assert response.json['aircraft'][2]['first_seen'] == "2024-07-17 03:30:33"
assert response.json['aircraft'][2]['last_seen'] == "2024-06-17 03:33:03"
assert response.json['aircraft'][3]['id'] == 2
assert response.json['aircraft'][3]['icao'] == "icao02"
assert response.json['aircraft'][3]['first_seen'] == "2024-07-17 02:20:22"
assert response.json['aircraft'][3]['last_seen'] == "2024-06-17 02:22:02"
assert response.json['aircraft'][4]['id'] == 1
assert response.json['aircraft'][4]['icao'] == "icao01"
assert response.json['aircraft'][4]['first_seen'] == "2024-07-17 01:10:11"
assert response.json['aircraft'][4]['last_seen'] == "2024-06-17 01:11:01"
def test_get_aircraft_200_offset(client):
response = client.get('/api/aircraft?offset=2')
assert response.status_code == 200
assert response.json['offset'] == 2
assert response.json['limit'] == 50
assert response.json['count'] == 3
assert response.json['aircraft'][0]['id'] == 3
assert response.json['aircraft'][0]['icao'] == "icao03"
assert response.json['aircraft'][0]['first_seen'] == "2024-07-17 03:30:33"
assert response.json['aircraft'][0]['last_seen'] == "2024-06-17 03:33:03"
assert response.json['aircraft'][1]['id'] == 2
assert response.json['aircraft'][1]['icao'] == "icao02"
assert response.json['aircraft'][1]['first_seen'] == "2024-07-17 02:20:22"
assert response.json['aircraft'][1]['last_seen'] == "2024-06-17 02:22:02"
assert response.json['aircraft'][2]['id'] == 1
assert response.json['aircraft'][2]['icao'] == "icao01"
assert response.json['aircraft'][2]['first_seen'] == "2024-07-17 01:10:11"
assert response.json['aircraft'][2]['last_seen'] == "2024-06-17 01:11:01"
def test_get_aircraft_200_limit(client):
response = client.get('/api/aircraft?limit=2')
assert response.status_code == 200
assert response.json['offset'] == 0
assert response.json['limit'] == 2
assert response.json['count'] == 2
assert response.json['aircraft'][0]['id'] == 5
assert response.json['aircraft'][0]['icao'] == "icao05"
assert response.json['aircraft'][0]['first_seen'] == "2024-07-17 05:50:55"
assert response.json['aircraft'][0]['last_seen'] == "2024-06-17 05:55:05"
assert response.json['aircraft'][1]['id'] == 4
assert response.json['aircraft'][1]['icao'] == "icao04"
assert response.json['aircraft'][1]['first_seen'] == "2024-07-17 04:40:44"
assert response.json['aircraft'][1]['last_seen'] == "2024-06-17 04:44:04"
def test_get_aircraft_200_offset_and_limit(client):
response = client.get('/api/aircraft?offset=1&limit=2')
assert response.status_code == 200
assert response.json['offset'] == 1
assert response.json['limit'] == 2
assert response.json['count'] == 2
assert response.json['aircraft'][0]['id'] == 4
assert response.json['aircraft'][0]['icao'] == "icao04"
assert response.json['aircraft'][0]['first_seen'] == "2024-07-17 04:40:44"
assert response.json['aircraft'][0]['last_seen'] == "2024-06-17 04:44:04"
assert response.json['aircraft'][1]['id'] == 3
assert response.json['aircraft'][1]['icao'] == "icao03"
assert response.json['aircraft'][1]['first_seen'] == "2024-07-17 03:30:33"
assert response.json['aircraft'][1]['last_seen'] == "2024-06-17 03:33:03"
def test_get_aircraft_400_offset_less_than_0(client):
response = client.get('/api/aircraft?offset=-1')
assert response.status_code == 400
def test_get_aircraft_400_limit_less_than_0(client):
response = client.get('/api/aircraft?limit=-1')
assert response.status_code == 400
def test_get_aircraft_400_limit_greater_than_100(client):
response = client.get('/api/aircraft?limit=101')
assert response.status_code == 400
# GET /api/aircraft/count
def test_get_aircraft_count(client):
response = client.get('/api/aircraft/count')
assert response.status_code == 200
assert response.json["aircraft"] == 5