diff --git a/Vagrantfile b/Vagrantfile
index 8ee8c2a..582d2c9 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -4,7 +4,7 @@ set -e
# install PostgreSQL and PostGIS
sudo apt-get update
-sudo apt-get install -y --no-install-recommends postgresql-9.4-postgis-2.1 libpq-dev
+sudo apt-get install -y postgresql-11-postgis-2.5
# create PostGIS database
sudo sudo -u postgres createuser -s vagrant
@@ -14,22 +14,18 @@ sudo sudo -u postgres psql -d ogn -c 'CREATE EXTENSION postgis;'
# install python requirements
cd /vagrant
-sudo apt-get install -y --no-install-recommends redis-server build-essential python3 python3-pip python3-dev libpq-dev libgeos-dev
-sudo -H pip3 install -r requirements.txt
+sudo apt-get install -y python3-pip redis-server
+sudo pip3 install -r requirements.txt
# # initialize database
-./manage.py db.init
+#./manage.py db.init
# # import registered devices from ddb
-./manage.py db.import_ddb
+#./manage.py db.import_ddb
SCRIPT
Vagrant.configure("2") do |config|
- config.vm.box = 'debian/jessie64'
-
- # Current version is broken
- config.vm.box_version = '8.5.2'
-
+ config.vm.box = 'debian/buster64'
config.vm.provision 'shell', inline: $script, privileged: false
end
diff --git a/ogn_python/__init__.py b/app/__init__.py
similarity index 69%
rename from ogn_python/__init__.py
rename to app/__init__.py
index ad25bd5..1ff0154 100644
--- a/ogn_python/__init__.py
+++ b/app/__init__.py
@@ -5,14 +5,14 @@ from flask_migrate import Migrate
from flask_caching import Cache
from celery import Celery
-from ogn_python.flask_celery import make_celery
+from app.flask_celery import make_celery
# Initialize Flask
app = Flask(__name__)
# Load the configuration
-#app.config.from_object('config.default')
-app.config.from_envvar('OGN_CONFIG_MODULE')
+app.config.from_object('app.config.default')
+app.config.from_envvar("OGN_CONFIG_MODULE", silent=True)
# Initialize other things
bootstrap = Bootstrap(app)
@@ -20,3 +20,5 @@ db = SQLAlchemy(app)
migrate = Migrate(app, db)
cache = Cache(app)
celery = make_celery(app)
+
+from app import routes, commands
diff --git a/ogn_python/backend/__init__.py b/app/backend/__init__.py
similarity index 100%
rename from ogn_python/backend/__init__.py
rename to app/backend/__init__.py
diff --git a/app/backend/liveglidernet.py b/app/backend/liveglidernet.py
new file mode 100644
index 0000000..e3c43ba
--- /dev/null
+++ b/app/backend/liveglidernet.py
@@ -0,0 +1,105 @@
+from datetime import datetime, timedelta, timezone, date
+
+from app.model import AircraftBeacon, Device, Receiver
+
+from app import db
+from app.model import ReceiverBeacon
+
+
+def utc_to_local(utc_dt):
+ return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
+
+
+def encode(address):
+ return "xx" + address
+
+
+def decode(code):
+ return code[2:9]
+
+
+def rec(min_timestamp, min_online_timestamp):
+ last_seen_query = (
+ db.session.query(ReceiverBeacon).filter(ReceiverBeacon.timestamp > min_timestamp).order_by(ReceiverBeacon.receiver_id, ReceiverBeacon.timestamp).distinct(ReceiverBeacon.receiver_id)
+ )
+
+ lines = []
+ lines.append('')
+ lines.append("")
+ lines.append('')
+ for receiver_beacon in last_seen_query:
+ if receiver_beacon.location == None or receiver_beacon.name.startswith("FNB"):
+ continue
+ lines.append(
+ ''.format(
+ receiver_beacon.name, receiver_beacon.location.latitude, receiver_beacon.location.longitude, receiver_beacon.timestamp < min_online_timestamp
+ )
+ )
+
+ lines.append("")
+ xml = "\n".join(lines)
+
+ return xml
+
+
+def lxml(show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_min=-180):
+
+ timestamp_range_filter = [db.between(AircraftBeacon.timestamp, datetime(2018, 7, 31, 11, 55, 0), datetime(2018, 7, 31, 12, 5, 0))]
+
+ last_seen_query = db.session.query(AircraftBeacon).filter(*timestamp_range_filter).order_by(AircraftBeacon.device_id, AircraftBeacon.timestamp).distinct(AircraftBeacon.device_id)
+ lines = list()
+ lines.append('')
+ lines.append("")
+
+ for aircraft_beacon in last_seen_query:
+ device = aircraft_beacon.device
+
+ code = encode(device.address)
+
+ if device.info:
+ if not device.info.tracked or not device.info.identified:
+ continue
+
+ if not device.info.competition:
+ competition = device.info.registration[-2:]
+ else:
+ competition = device.info.competition
+
+ if not device.info.registration:
+ registration = "???"
+ else:
+ registration = device.info.registration
+
+ address = device.address
+
+ else:
+ competition = ("_" + code[-2:]).lower()
+ registration = code
+ address = 0
+
+ elapsed_time = datetime.utcnow() - aircraft_beacon.timestamp
+ elapsed_seconds = int(elapsed_time.total_seconds())
+
+ lines.append(
+ ' '.format(
+ aircraft_beacon.location.latitude,
+ aircraft_beacon.location.longitude,
+ competition,
+ registration,
+ int(aircraft_beacon.altitude),
+ utc_to_local(aircraft_beacon.timestamp).strftime("%H:%M:%S"),
+ elapsed_seconds,
+ int(aircraft_beacon.track),
+ int(aircraft_beacon.ground_speed),
+ int(aircraft_beacon.climb_rate * 10) / 10,
+ aircraft_beacon.aircraft_type,
+ aircraft_beacon.receiver_name,
+ address,
+ code,
+ )
+ )
+
+ lines.append("")
+ xml = "\n".join(lines)
+
+ return xml
diff --git a/app/backend/ognrange.py b/app/backend/ognrange.py
new file mode 100644
index 0000000..cdef59b
--- /dev/null
+++ b/app/backend/ognrange.py
@@ -0,0 +1,54 @@
+import json
+from datetime import datetime, timedelta
+
+from sqlalchemy import func, case
+from sqlalchemy.sql.expression import label
+from app.model import Receiver, ReceiverCoverage
+
+from app import db
+
+
+def alchemyencoder(obj):
+ """JSON encoder function for SQLAlchemy special classes."""
+
+ import decimal
+ from datetime import datetime
+
+ if isinstance(obj, datetime):
+ return obj.strftime("%Y-%m-%d %H:%M")
+ elif isinstance(obj, decimal.Decimal):
+ return float(obj)
+
+
+def stations2_filtered_pl(start, end):
+ last_10_minutes = datetime.utcnow() - timedelta(minutes=10)
+
+ query = (
+ db.session.query(
+ Receiver.name.label("s"),
+ label("lt", func.round(func.ST_Y(Receiver.location_wkt) * 10000) / 10000),
+ label("lg", func.round(func.ST_X(Receiver.location_wkt) * 10000) / 10000),
+ case([(Receiver.lastseen > last_10_minutes, "U")], else_="D").label("u"),
+ Receiver.lastseen.label("ut"),
+ label("v", Receiver.version + "." + Receiver.platform),
+ )
+ .order_by(Receiver.lastseen)
+ .filter(db.or_(db.and_(start < Receiver.firstseen, end > Receiver.firstseen), db.and_(start < Receiver.lastseen, end > Receiver.lastseen)))
+ )
+
+ res = db.session.execute(query)
+ stations = json.dumps({"stations": [dict(r) for r in res]}, default=alchemyencoder)
+
+ return stations
+
+
+def max_tile_mgrs_pl(station, start, end, squares):
+ query = (
+ db.session.query(func.right(ReceiverCoverage.location_mgrs_short, 4), func.count(ReceiverCoverage.location_mgrs_short))
+ .filter(db.and_(Receiver.id == ReceiverCoverage.receiver_id, Receiver.name == station))
+ .filter(ReceiverCoverage.location_mgrs_short.like(squares + "%"))
+ .group_by(func.right(ReceiverCoverage.location_mgrs_short, 4))
+ )
+
+ res = {"t": squares, "p": ["{}/{}".format(r[0], r[1]) for r in query.all()]}
+ return json.dumps(res)
diff --git a/ogn_python/collect/__init__.py b/app/collect/__init__.py
similarity index 100%
rename from ogn_python/collect/__init__.py
rename to app/collect/__init__.py
diff --git a/ogn_python/collect/celery.py b/app/collect/celery.py
similarity index 63%
rename from ogn_python/collect/celery.py
rename to app/collect/celery.py
index 5bea387..1bb4c2f 100644
--- a/ogn_python/collect/celery.py
+++ b/app/collect/celery.py
@@ -2,26 +2,26 @@ import datetime
from celery.utils.log import get_task_logger
-from ogn_python.collect.takeoff_landings import update_entries as takeoff_update_entries
+from app.collect.takeoff_landings import update_entries as takeoff_update_entries
-from ogn_python.collect.logbook import update_entries as logbook_update_entries
-from ogn_python.collect.logbook import update_max_altitudes as logbook_update_max_altitudes
+from app.collect.logbook import update_entries as logbook_update_entries
+from app.collect.logbook import update_max_altitudes as logbook_update_max_altitudes
-from ogn_python.collect.database import import_ddb as device_infos_import_ddb
-from ogn_python.collect.database import update_country_code as receivers_update_country_code
+from app.collect.database import import_ddb as device_infos_import_ddb
+from app.collect.database import update_country_code as receivers_update_country_code
-from ogn_python.collect.stats import create_device_stats, update_device_stats_jumps, create_receiver_stats, create_relation_stats, update_qualities, update_receivers, update_devices
+from app.collect.stats import create_device_stats, update_device_stats_jumps, create_receiver_stats, create_relation_stats, update_qualities, update_receivers, update_devices
-from ogn_python.collect.ognrange import update_entries as receiver_coverage_update_entries
+from app.collect.ognrange import update_entries as receiver_coverage_update_entries
-from ogn_python import db
-from ogn_python import celery
+from app import db
+from app import celery
logger = get_task_logger(__name__)
-@celery.task(name='update_takeoff_landings')
+@celery.task(name="update_takeoff_landings")
def update_takeoff_landings(last_minutes):
"""Compute takeoffs and landings."""
@@ -31,7 +31,7 @@ def update_takeoff_landings(last_minutes):
return result
-@celery.task(name='update_logbook_entries')
+@celery.task(name="update_logbook_entries")
def update_logbook_entries(day_offset):
"""Add/update logbook entries."""
@@ -40,7 +40,7 @@ def update_logbook_entries(day_offset):
return result
-@celery.task(name='update_logbook_max_altitude')
+@celery.task(name="update_logbook_max_altitude")
def update_logbook_max_altitude(day_offset):
"""Add max altitudes in logbook when flight is complete (takeoff and landing)."""
@@ -49,7 +49,7 @@ def update_logbook_max_altitude(day_offset):
return result
-@celery.task(name='import_ddb')
+@celery.task(name="import_ddb")
def import_ddb():
"""Import registered devices from the DDB."""
@@ -57,7 +57,7 @@ def import_ddb():
return result
-@celery.task(name='update_receivers_country_code')
+@celery.task(name="update_receivers_country_code")
def update_receivers_country_code():
"""Update country code in receivers table if None."""
@@ -65,19 +65,16 @@ def update_receivers_country_code():
return result
-@celery.task(name='purge_old_data')
+@celery.task(name="purge_old_data")
def purge_old_data(max_hours):
"""Delete AircraftBeacons and ReceiverBeacons older than given 'age'."""
- from ogn_python.model import AircraftBeacon, ReceiverBeacon
- min_timestamp = datetime.datetime.utcnow() - datetime.timedelta(hours=max_hours)
- aircraft_beacons_deleted = db.session.query(AircraftBeacon) \
- .filter(AircraftBeacon.timestamp < min_timestamp) \
- .delete()
+ from app.model import AircraftBeacon, ReceiverBeacon
- receiver_beacons_deleted = db.session.query(ReceiverBeacon) \
- .filter(ReceiverBeacon.timestamp < min_timestamp) \
- .delete()
+ min_timestamp = datetime.datetime.utcnow() - datetime.timedelta(hours=max_hours)
+ aircraft_beacons_deleted = db.session.query(AircraftBeacon).filter(AircraftBeacon.timestamp < min_timestamp).delete()
+
+ receiver_beacons_deleted = db.session.query(ReceiverBeacon).filter(ReceiverBeacon.timestamp < min_timestamp).delete()
db.session.commit()
@@ -85,7 +82,7 @@ def purge_old_data(max_hours):
return result
-@celery.task(name='update_stats')
+@celery.task(name="update_stats")
def update_stats(day_offset):
"""Create stats and update receivers/devices with stats."""
@@ -100,7 +97,7 @@ def update_stats(day_offset):
update_devices(session=db.session)
-@celery.task(name='update_ognrange')
+@celery.task(name="update_ognrange")
def update_ognrange(day_offset):
"""Create receiver coverage stats for Melissas ognrange."""
diff --git a/ogn_python/collect/database.py b/app/collect/database.py
similarity index 72%
rename from ogn_python/collect/database.py
rename to app/collect/database.py
index 79dcc2f..63ccb58 100644
--- a/ogn_python/collect/database.py
+++ b/app/collect/database.py
@@ -3,10 +3,10 @@ from sqlalchemy.sql import null, and_, func, not_, case
from sqlalchemy.dialects import postgresql
from sqlalchemy.dialects.postgresql import insert
-from ogn_python.model import Country, DeviceInfo, DeviceInfoOrigin, AircraftBeacon, ReceiverBeacon, Device, Receiver
-from ogn_python.utils import get_ddb, get_flarmnet
+from app.model import Country, DeviceInfo, DeviceInfoOrigin, AircraftBeacon, ReceiverBeacon, Device, Receiver
+from app.utils import get_ddb, get_flarmnet
-from ogn_python import app
+from app import app
def upsert(session, model, rows, update_cols):
@@ -17,8 +17,7 @@ def upsert(session, model, rows, update_cols):
stmt = insert(table).values(rows)
on_conflict_stmt = stmt.on_conflict_do_update(
- index_elements=table.primary_key.columns,
- set_={k: case([(getattr(stmt.excluded, k) != null(), getattr(stmt.excluded, k))], else_=getattr(model, k)) for k in update_cols},
+ index_elements=table.primary_key.columns, set_={k: case([(getattr(stmt.excluded, k) != null(), getattr(stmt.excluded, k))], else_=getattr(model, k)) for k in update_cols}
)
# print(compile_query(on_conflict_stmt))
@@ -31,9 +30,7 @@ def update_device_infos(session, address_origin, path=None):
else:
device_infos = get_ddb(csv_file=path)
- session.query(DeviceInfo) \
- .filter(DeviceInfo.address_origin == address_origin) \
- .delete(synchronize_session='fetch')
+ session.query(DeviceInfo).filter(DeviceInfo.address_origin == address_origin).delete(synchronize_session="fetch")
session.commit()
for device_info in device_infos:
@@ -65,10 +62,11 @@ def update_country_code(session, logger=None):
if logger is None:
logger = app.logger
- update_receivers = session.query(Receiver) \
- .filter(and_(Receiver.country_id == null(), Receiver.location_wkt != null(), func.st_within(Receiver.location_wkt, Country.geom))) \
- .update({Receiver.country_id: Country.gid},
- synchronize_session='fetch')
+ update_receivers = (
+ session.query(Receiver)
+ .filter(and_(Receiver.country_id == null(), Receiver.location_wkt != null(), func.st_within(Receiver.location_wkt, Country.geom)))
+ .update({Receiver.country_id: Country.gid}, synchronize_session="fetch")
+ )
session.commit()
diff --git a/app/collect/logbook.py b/app/collect/logbook.py
new file mode 100644
index 0000000..ae59494
--- /dev/null
+++ b/app/collect/logbook.py
@@ -0,0 +1,200 @@
+from sqlalchemy import and_, or_, insert, update, exists, between
+from sqlalchemy.sql import func, null
+from sqlalchemy.sql.expression import true, false
+
+from app.model import TakeoffLanding, Logbook, AircraftBeacon
+from app.utils import date_to_timestamps
+
+from app import app
+
+
+def update_entries(session, date, logger=None):
+ """Add/update logbook entries."""
+
+ if logger is None:
+ logger = app.logger
+
+ logger.info("Compute logbook.")
+
+ # limit time range to given date and set window partition and window order
+ (start, end) = date_to_timestamps(date)
+ pa = TakeoffLanding.device_id
+ wo = and_(TakeoffLanding.device_id, TakeoffLanding.airport_id, TakeoffLanding.timestamp)
+
+ # make a query with current, previous and next "takeoff_landing" event, so we can find complete flights
+ sq = (
+ session.query(
+ TakeoffLanding.device_id,
+ func.lag(TakeoffLanding.device_id).over(partition_by=pa, order_by=wo).label("device_id_prev"),
+ func.lead(TakeoffLanding.device_id).over(partition_by=pa, order_by=wo).label("device_id_next"),
+ TakeoffLanding.timestamp,
+ func.lag(TakeoffLanding.timestamp).over(partition_by=pa, order_by=wo).label("timestamp_prev"),
+ func.lead(TakeoffLanding.timestamp).over(partition_by=pa, order_by=wo).label("timestamp_next"),
+ TakeoffLanding.track,
+ func.lag(TakeoffLanding.track).over(partition_by=pa, order_by=wo).label("track_prev"),
+ func.lead(TakeoffLanding.track).over(partition_by=pa, order_by=wo).label("track_next"),
+ TakeoffLanding.is_takeoff,
+ func.lag(TakeoffLanding.is_takeoff).over(partition_by=pa, order_by=wo).label("is_takeoff_prev"),
+ func.lead(TakeoffLanding.is_takeoff).over(partition_by=pa, order_by=wo).label("is_takeoff_next"),
+ TakeoffLanding.airport_id,
+ func.lag(TakeoffLanding.airport_id).over(partition_by=pa, order_by=wo).label("airport_id_prev"),
+ func.lead(TakeoffLanding.airport_id).over(partition_by=pa, order_by=wo).label("airport_id_next"),
+ )
+ .filter(between(TakeoffLanding.timestamp, start, end))
+ .subquery()
+ )
+
+ # find complete flights
+ complete_flight_query = session.query(
+ sq.c.timestamp.label("reftime"),
+ sq.c.device_id.label("device_id"),
+ sq.c.timestamp.label("takeoff_timestamp"),
+ sq.c.track.label("takeoff_track"),
+ sq.c.airport_id.label("takeoff_airport_id"),
+ sq.c.timestamp_next.label("landing_timestamp"),
+ sq.c.track_next.label("landing_track"),
+ sq.c.airport_id_next.label("landing_airport_id"),
+ ).filter(and_(sq.c.is_takeoff == true(), sq.c.is_takeoff_next == false()))
+
+ # find landings without start
+ only_landings_query = (
+ session.query(
+ sq.c.timestamp.label("reftime"),
+ sq.c.device_id.label("device_id"),
+ null().label("takeoff_timestamp"),
+ null().label("takeoff_track"),
+ null().label("takeoff_airport_id"),
+ sq.c.timestamp.label("landing_timestamp"),
+ sq.c.track.label("landing_track"),
+ sq.c.airport_id.label("landing_airport_id"),
+ )
+ .filter(sq.c.is_takeoff == false())
+ .filter(or_(sq.c.is_takeoff_prev == false(), sq.c.is_takeoff_prev == null()))
+ )
+
+ # find starts without landing
+ only_starts_query = (
+ session.query(
+ sq.c.timestamp.label("reftime"),
+ sq.c.device_id.label("device_id"),
+ sq.c.timestamp.label("takeoff_timestamp"),
+ sq.c.track.label("takeoff_track"),
+ sq.c.airport_id.label("takeoff_airport_id"),
+ null().label("landing_timestamp"),
+ null().label("landing_track"),
+ null().label("landing_airport_id"),
+ )
+ .filter(sq.c.is_takeoff == true())
+ .filter(or_(sq.c.is_takeoff_next == true(), sq.c.is_takeoff_next == null()))
+ )
+
+ # unite all computated flights
+ union_query = complete_flight_query.union(only_landings_query, only_starts_query).subquery()
+
+ # if a logbook entry exist --> update it
+ upd = (
+ update(Logbook)
+ .where(
+ and_(
+ Logbook.device_id == union_query.c.device_id,
+ union_query.c.takeoff_airport_id != null(),
+ union_query.c.landing_airport_id != null(),
+ or_(
+ and_(Logbook.takeoff_airport_id == union_query.c.takeoff_airport_id, Logbook.takeoff_timestamp == union_query.c.takeoff_timestamp, Logbook.landing_airport_id == null()),
+ and_(Logbook.takeoff_airport_id == null(), Logbook.landing_airport_id == union_query.c.landing_airport_id, Logbook.landing_timestamp == union_query.c.landing_timestamp),
+ ),
+ )
+ )
+ .values(
+ {
+ "reftime": union_query.c.reftime,
+ "takeoff_timestamp": union_query.c.takeoff_timestamp,
+ "takeoff_track": union_query.c.takeoff_track,
+ "takeoff_airport_id": union_query.c.takeoff_airport_id,
+ "landing_timestamp": union_query.c.landing_timestamp,
+ "landing_track": union_query.c.landing_track,
+ "landing_airport_id": union_query.c.landing_airport_id,
+ }
+ )
+ )
+
+ result = session.execute(upd)
+ update_counter = result.rowcount
+ session.commit()
+ logger.debug("Updated logbook entries: {}".format(update_counter))
+
+ # if a logbook entry doesnt exist --> insert it
+ new_logbook_entries = session.query(union_query).filter(
+ ~exists().where(
+ and_(
+ Logbook.device_id == union_query.c.device_id,
+ or_(
+ and_(Logbook.takeoff_airport_id == union_query.c.takeoff_airport_id, Logbook.takeoff_timestamp == union_query.c.takeoff_timestamp),
+ and_(Logbook.takeoff_airport_id == null(), union_query.c.takeoff_airport_id == null()),
+ ),
+ or_(
+ and_(Logbook.landing_airport_id == union_query.c.landing_airport_id, Logbook.landing_timestamp == union_query.c.landing_timestamp),
+ and_(Logbook.landing_airport_id == null(), union_query.c.landing_airport_id == null()),
+ ),
+ )
+ )
+ )
+
+ ins = insert(Logbook).from_select(
+ (
+ Logbook.reftime,
+ Logbook.device_id,
+ Logbook.takeoff_timestamp,
+ Logbook.takeoff_track,
+ Logbook.takeoff_airport_id,
+ Logbook.landing_timestamp,
+ Logbook.landing_track,
+ Logbook.landing_airport_id,
+ ),
+ new_logbook_entries,
+ )
+
+ result = session.execute(ins)
+ insert_counter = result.rowcount
+ session.commit()
+
+ finish_message = "Logbook: {} inserted, {} updated".format(insert_counter, update_counter)
+ logger.debug(finish_message)
+ return finish_message
+
+
+def update_max_altitudes(session, date, logger=None):
+ """Add max altitudes in logbook when flight is complete (takeoff and landing)."""
+
+ if logger is None:
+ logger = app.logger
+
+ logger.info("Update logbook max altitude.")
+
+ if session is None:
+ session = app.session
+
+ (start, end) = date_to_timestamps(date)
+
+ logbook_entries = (
+ session.query(Logbook.id)
+ .filter(and_(Logbook.takeoff_timestamp != null(), Logbook.landing_timestamp != null(), Logbook.max_altitude == null()))
+ .filter(between(Logbook.reftime, start, end))
+ .subquery()
+ )
+
+ max_altitudes = (
+ session.query(Logbook.id, func.max(AircraftBeacon.altitude).label("max_altitude"))
+ .filter(Logbook.id == logbook_entries.c.id)
+ .filter(and_(AircraftBeacon.device_id == Logbook.device_id, AircraftBeacon.timestamp >= Logbook.takeoff_timestamp, AircraftBeacon.timestamp <= Logbook.landing_timestamp))
+ .group_by(Logbook.id)
+ .subquery()
+ )
+
+ update_logbook = session.query(Logbook).filter(Logbook.id == max_altitudes.c.id).update({Logbook.max_altitude: max_altitudes.c.max_altitude}, synchronize_session="fetch")
+
+ session.commit()
+
+ finish_message = "Logbook (altitude): {} entries updated.".format(update_logbook)
+ logger.info(finish_message)
+ return finish_message
diff --git a/app/collect/ognrange.py b/app/collect/ognrange.py
new file mode 100644
index 0000000..a6a20f6
--- /dev/null
+++ b/app/collect/ognrange.py
@@ -0,0 +1,89 @@
+from sqlalchemy import Date
+from sqlalchemy import and_, insert, update, exists, between
+from sqlalchemy.sql import func, null
+
+from app.model import AircraftBeacon, ReceiverCoverage
+from app.utils import date_to_timestamps
+
+from app import app
+
+
+def update_entries(session, date, logger=None):
+ """Create receiver coverage stats for Melissas ognrange."""
+
+ if logger is None:
+ logger = app.logger
+
+ logger.info("Compute receiver coverages.")
+
+ (start, end) = date_to_timestamps(date)
+
+ # Filter aircraft beacons
+ sq = (
+ session.query(AircraftBeacon.location_mgrs_short, AircraftBeacon.receiver_id, AircraftBeacon.signal_quality, AircraftBeacon.altitude, AircraftBeacon.device_id)
+ .filter(and_(between(AircraftBeacon.timestamp, start, end), AircraftBeacon.location_mgrs_short != null(), AircraftBeacon.receiver_id != null(), AircraftBeacon.device_id != null()))
+ .subquery()
+ )
+
+ # ... and group them by reduced MGRS, receiver and date
+ query = (
+ session.query(
+ sq.c.location_mgrs_short,
+ sq.c.receiver_id,
+ func.cast(date, Date).label("date"),
+ func.max(sq.c.signal_quality).label("max_signal_quality"),
+ func.min(sq.c.altitude).label("min_altitude"),
+ func.max(sq.c.altitude).label("max_altitude"),
+ func.count(sq.c.altitude).label("aircraft_beacon_count"),
+ func.count(func.distinct(sq.c.device_id)).label("device_count"),
+ )
+ .group_by(sq.c.location_mgrs_short, sq.c.receiver_id)
+ .subquery()
+ )
+
+ # if a receiver coverage entry exist --> update it
+ upd = (
+ update(ReceiverCoverage)
+ .where(and_(ReceiverCoverage.location_mgrs_short == query.c.location_mgrs_short, ReceiverCoverage.receiver_id == query.c.receiver_id, ReceiverCoverage.date == date))
+ .values(
+ {
+ "max_signal_quality": query.c.max_signal_quality,
+ "min_altitude": query.c.min_altitude,
+ "max_altitude": query.c.max_altitude,
+ "aircraft_beacon_count": query.c.aircraft_beacon_count,
+ "device_count": query.c.device_count,
+ }
+ )
+ )
+
+ result = session.execute(upd)
+ update_counter = result.rowcount
+ session.commit()
+ logger.debug("Updated receiver coverage entries: {}".format(update_counter))
+
+ # if a receiver coverage entry doesnt exist --> insert it
+ new_coverage_entries = session.query(query).filter(
+ ~exists().where(and_(ReceiverCoverage.location_mgrs_short == query.c.location_mgrs_short, ReceiverCoverage.receiver_id == query.c.receiver_id, ReceiverCoverage.date == date))
+ )
+
+ ins = insert(ReceiverCoverage).from_select(
+ (
+ ReceiverCoverage.location_mgrs_short,
+ ReceiverCoverage.receiver_id,
+ ReceiverCoverage.date,
+ ReceiverCoverage.max_signal_quality,
+ ReceiverCoverage.min_altitude,
+ ReceiverCoverage.max_altitude,
+ ReceiverCoverage.aircraft_beacon_count,
+ ReceiverCoverage.device_count,
+ ),
+ new_coverage_entries,
+ )
+
+ result = session.execute(ins)
+ insert_counter = result.rowcount
+ session.commit()
+
+ finish_message = "ReceiverCoverage: {} inserted, {} updated".format(insert_counter, update_counter)
+ logger.debug(finish_message)
+ return finish_message
diff --git a/app/collect/stats.py b/app/collect/stats.py
new file mode 100644
index 0000000..2312820
--- /dev/null
+++ b/app/collect/stats.py
@@ -0,0 +1,451 @@
+from sqlalchemy import insert, distinct, between, literal
+from sqlalchemy.sql import null, and_, func, or_, update
+from sqlalchemy.sql.expression import case
+
+from app.model import AircraftBeacon, DeviceStats, Country, CountryStats, ReceiverStats, ReceiverBeacon, RelationStats, Receiver, Device
+
+from app.utils import date_to_timestamps
+
+from app import app
+
+# 40dB@10km is enough for 640km
+MAX_PLAUSIBLE_QUALITY = 40
+
+
+def create_device_stats(session, date, logger=None):
+ """Add/update device stats."""
+
+ if logger is None:
+ logger = app.logger
+
+ (start, end) = date_to_timestamps(date)
+
+ # First kill the stats for the selected date
+ deleted_counter = session.query(DeviceStats).filter(DeviceStats.date == date).delete()
+
+ # Since "distinct count" does not work in window functions we need a work-around for receiver counting
+ sq = (
+ session.query(AircraftBeacon, func.dense_rank().over(partition_by=AircraftBeacon.device_id, order_by=AircraftBeacon.receiver_id).label("dr"))
+ .filter(and_(between(AircraftBeacon.timestamp, start, end), AircraftBeacon.device_id != null()))
+ .filter(or_(AircraftBeacon.error_count == 0, AircraftBeacon.error_count == null()))
+ .subquery()
+ )
+
+ # Calculate stats, firstseen, lastseen and last values != NULL
+ device_stats = session.query(
+ distinct(sq.c.device_id).label("device_id"),
+ literal(date).label("date"),
+ func.max(sq.c.dr).over(partition_by=sq.c.device_id).label("receiver_count"),
+ func.max(sq.c.altitude).over(partition_by=sq.c.device_id).label("max_altitude"),
+ func.count(sq.c.device_id).over(partition_by=sq.c.device_id).label("aircraft_beacon_count"),
+ func.first_value(sq.c.name).over(partition_by=sq.c.device_id, order_by=case([(sq.c.name == null(), None)], else_=sq.c.timestamp).asc().nullslast()).label("name"),
+ func.first_value(sq.c.timestamp).over(partition_by=sq.c.device_id, order_by=case([(sq.c.timestamp == null(), None)], else_=sq.c.timestamp).asc().nullslast()).label("firstseen"),
+ func.first_value(sq.c.timestamp).over(partition_by=sq.c.device_id, order_by=case([(sq.c.timestamp == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("lastseen"),
+ func.first_value(sq.c.aircraft_type).over(partition_by=sq.c.device_id, order_by=case([(sq.c.aircraft_type == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("aircraft_type"),
+ func.first_value(sq.c.stealth).over(partition_by=sq.c.device_id, order_by=case([(sq.c.stealth == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("stealth"),
+ func.first_value(sq.c.software_version)
+ .over(partition_by=sq.c.device_id, order_by=case([(sq.c.software_version == null(), None)], else_=sq.c.timestamp).desc().nullslast())
+ .label("software_version"),
+ func.first_value(sq.c.hardware_version)
+ .over(partition_by=sq.c.device_id, order_by=case([(sq.c.hardware_version == null(), None)], else_=sq.c.timestamp).desc().nullslast())
+ .label("hardware_version"),
+ func.first_value(sq.c.real_address).over(partition_by=sq.c.device_id, order_by=case([(sq.c.real_address == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("real_address"),
+ ).subquery()
+
+ # And insert them
+ ins = insert(DeviceStats).from_select(
+ [
+ DeviceStats.device_id,
+ DeviceStats.date,
+ DeviceStats.receiver_count,
+ DeviceStats.max_altitude,
+ DeviceStats.aircraft_beacon_count,
+ DeviceStats.name,
+ DeviceStats.firstseen,
+ DeviceStats.lastseen,
+ DeviceStats.aircraft_type,
+ DeviceStats.stealth,
+ DeviceStats.software_version,
+ DeviceStats.hardware_version,
+ DeviceStats.real_address,
+ ],
+ device_stats,
+ )
+ res = session.execute(ins)
+ insert_counter = res.rowcount
+ session.commit()
+ logger.debug("DeviceStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter))
+
+ return "DeviceStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter)
+
+
+def create_receiver_stats(session, date, logger=None):
+ """Add/update receiver stats."""
+
+ if logger is None:
+ logger = app.logger
+
+ (start, end) = date_to_timestamps(date)
+
+ # First kill the stats for the selected date
+ deleted_counter = session.query(ReceiverStats).filter(ReceiverStats.date == date).delete()
+
+ # Select one day
+ sq = session.query(ReceiverBeacon).filter(between(ReceiverBeacon.timestamp, start, end)).subquery()
+
+ # Calculate stats, firstseen, lastseen and last values != NULL
+ receiver_stats = session.query(
+ distinct(sq.c.receiver_id).label("receiver_id"),
+ literal(date).label("date"),
+ func.first_value(sq.c.timestamp).over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.timestamp == null(), None)], else_=sq.c.timestamp).asc().nullslast()).label("firstseen"),
+ func.first_value(sq.c.timestamp).over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.timestamp == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("lastseen"),
+ func.first_value(sq.c.location).over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.location == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("location_wkt"),
+ func.first_value(sq.c.altitude).over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.altitude == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("altitude"),
+ func.first_value(sq.c.version).over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.version == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("version"),
+ func.first_value(sq.c.platform).over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.platform == null(), None)], else_=sq.c.timestamp).desc().nullslast()).label("platform"),
+ ).subquery()
+
+ # And insert them
+ ins = insert(ReceiverStats).from_select(
+ [
+ ReceiverStats.receiver_id,
+ ReceiverStats.date,
+ ReceiverStats.firstseen,
+ ReceiverStats.lastseen,
+ ReceiverStats.location_wkt,
+ ReceiverStats.altitude,
+ ReceiverStats.version,
+ ReceiverStats.platform,
+ ],
+ receiver_stats,
+ )
+ res = session.execute(ins)
+ insert_counter = res.rowcount
+ session.commit()
+ logger.warn("ReceiverStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter))
+
+ # Update aircraft_beacon_count, aircraft_count and max_distance
+ aircraft_beacon_stats = (
+ session.query(
+ AircraftBeacon.receiver_id,
+ func.count(AircraftBeacon.timestamp).label("aircraft_beacon_count"),
+ func.count(func.distinct(AircraftBeacon.device_id)).label("aircraft_count"),
+ func.max(AircraftBeacon.distance).label("max_distance"),
+ )
+ .filter(and_(between(AircraftBeacon.timestamp, start, end), AircraftBeacon.error_count == 0, AircraftBeacon.quality <= MAX_PLAUSIBLE_QUALITY, AircraftBeacon.relay == null()))
+ .group_by(AircraftBeacon.receiver_id)
+ .subquery()
+ )
+
+ upd = (
+ update(ReceiverStats)
+ .where(and_(ReceiverStats.date == date, ReceiverStats.receiver_id == aircraft_beacon_stats.c.receiver_id))
+ .values(
+ {"aircraft_beacon_count": aircraft_beacon_stats.c.aircraft_beacon_count, "aircraft_count": aircraft_beacon_stats.c.aircraft_count, "max_distance": aircraft_beacon_stats.c.max_distance}
+ )
+ )
+
+ result = session.execute(upd)
+ update_counter = result.rowcount
+ session.commit()
+ logger.warn("Updated {} ReceiverStats".format(update_counter))
+
+ return "ReceiverStats for {}: {} deleted, {} inserted, {} updated".format(date, deleted_counter, insert_counter, update_counter)
+
+
+def create_country_stats(session, date, logger=None):
+ if logger is None:
+ logger = app.logger
+
+ (start, end) = date_to_timestamps(date)
+
+ # First kill the stats for the selected date
+ deleted_counter = session.query(CountryStats).filter(CountryStats.date == date).delete()
+
+ country_stats = (
+ session.query(literal(date), Country.gid, func.count(AircraftBeacon.timestamp).label("aircraft_beacon_count"), func.count(func.distinct(AircraftBeacon.receiver_id)).label("device_count"))
+ .filter(between(AircraftBeacon.timestamp, start, end))
+ .filter(func.st_contains(Country.geom, AircraftBeacon.location))
+ .group_by(Country.gid)
+ .subquery()
+ )
+
+ # And insert them
+ ins = insert(CountryStats).from_select([CountryStats.date, CountryStats.country_id, CountryStats.aircraft_beacon_count, CountryStats.device_count], country_stats)
+ res = session.execute(ins)
+ insert_counter = res.rowcount
+ session.commit()
+
+
+def update_device_stats_jumps(session, date, logger=None):
+ """Update device stats jumps."""
+
+ if logger is None:
+ logger = app.logger
+
+ (start, end) = date_to_timestamps(date)
+
+ # speed limits in m/s (values above indicates a unplausible position / jump)
+ max_horizontal_speed = 1000
+ max_vertical_speed = 100
+ max_jumps = 10 # threshold for an 'ambiguous' device
+
+ # find consecutive positions for a device
+ sq = (
+ session.query(
+ AircraftBeacon.device_id,
+ AircraftBeacon.timestamp,
+ func.lead(AircraftBeacon.timestamp).over(partition_by=AircraftBeacon.device_id, order_by=AircraftBeacon.timestamp).label("timestamp_next"),
+ AircraftBeacon.location_wkt,
+ func.lead(AircraftBeacon.location_wkt).over(partition_by=AircraftBeacon.device_id, order_by=AircraftBeacon.timestamp).label("location_next"),
+ AircraftBeacon.altitude,
+ func.lead(AircraftBeacon.altitude).over(partition_by=AircraftBeacon.device_id, order_by=AircraftBeacon.timestamp).label("altitude_next"),
+ )
+ .filter(and_(between(AircraftBeacon.timestamp, start, end), AircraftBeacon.error_count == 0))
+ .subquery()
+ )
+
+ # calc vertial and horizontal speed between points
+ sq2 = (
+ session.query(
+ sq.c.device_id,
+ (func.st_distancesphere(sq.c.location_next, sq.c.location) / (func.extract("epoch", sq.c.timestamp_next) - func.extract("epoch", sq.c.timestamp))).label("horizontal_speed"),
+ ((sq.c.altitude_next - sq.c.altitude) / (func.extract("epoch", sq.c.timestamp_next) - func.extract("epoch", sq.c.timestamp))).label("vertical_speed"),
+ )
+ .filter(and_(sq.c.timestamp != null(), sq.c.timestamp_next != null(), sq.c.timestamp < sq.c.timestamp_next))
+ .subquery()
+ )
+
+ # ... and find and count 'jumps'
+ sq3 = (
+ session.query(sq2.c.device_id, func.sum(case([(or_(func.abs(sq2.c.horizontal_speed) > max_horizontal_speed, func.abs(sq2.c.vertical_speed) > max_vertical_speed), 1)], else_=0)).label("jumps"))
+ .group_by(sq2.c.device_id)
+ .subquery()
+ )
+
+ upd = update(DeviceStats).where(and_(DeviceStats.date == date, DeviceStats.device_id == sq3.c.device_id)).values({"ambiguous": sq3.c.jumps > max_jumps, "jumps": sq3.c.jumps})
+
+ result = session.execute(upd)
+ update_counter = result.rowcount
+ session.commit()
+ logger.warn("Updated {} DeviceStats jumps".format(update_counter))
+
+ return "DeviceStats jumps for {}: {} updated".format(date, update_counter)
+
+
+def create_relation_stats(session, date, logger=None):
+ """Add/update relation stats."""
+
+ if logger is None:
+ logger = app.logger
+
+ (start, end) = date_to_timestamps(date)
+
+ # First kill the stats for the selected date
+ deleted_counter = session.query(RelationStats).filter(RelationStats.date == date).delete()
+
+ # Calculate stats for selected day
+ relation_stats = (
+ session.query(literal(date), AircraftBeacon.device_id, AircraftBeacon.receiver_id, func.max(AircraftBeacon.quality), func.count(AircraftBeacon.timestamp))
+ .filter(
+ and_(
+ between(AircraftBeacon.timestamp, start, end),
+ AircraftBeacon.distance > 1000,
+ AircraftBeacon.error_count == 0,
+ AircraftBeacon.quality <= MAX_PLAUSIBLE_QUALITY,
+ AircraftBeacon.ground_speed > 10,
+ )
+ )
+ .group_by(literal(date), AircraftBeacon.device_id, AircraftBeacon.receiver_id)
+ .subquery()
+ )
+
+ # And insert them
+ ins = insert(RelationStats).from_select([RelationStats.date, RelationStats.device_id, RelationStats.receiver_id, RelationStats.quality, RelationStats.beacon_count], relation_stats)
+ res = session.execute(ins)
+ insert_counter = res.rowcount
+ session.commit()
+ logger.warn("RelationStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter))
+
+ return "RelationStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter)
+
+
+def update_qualities(session, date, logger=None):
+ """Calculate relative qualities of receivers and devices."""
+
+ if logger is None:
+ logger = app.logger
+
+ # Calculate avg quality of devices
+ dev_sq = session.query(RelationStats.device_id, func.avg(RelationStats.quality).label("quality")).filter(RelationStats.date == date).group_by(RelationStats.device_id).subquery()
+
+ dev_upd = update(DeviceStats).where(and_(DeviceStats.date == date, DeviceStats.device_id == dev_sq.c.device_id)).values({"quality": dev_sq.c.quality})
+
+ dev_result = session.execute(dev_upd)
+ dev_update_counter = dev_result.rowcount
+ session.commit()
+ logger.warn("Updated {} DeviceStats: quality".format(dev_update_counter))
+
+ # Calculate avg quality of receivers
+ rec_sq = session.query(RelationStats.receiver_id, func.avg(RelationStats.quality).label("quality")).filter(RelationStats.date == date).group_by(RelationStats.receiver_id).subquery()
+
+ rec_upd = update(ReceiverStats).where(and_(ReceiverStats.date == date, ReceiverStats.receiver_id == rec_sq.c.receiver_id)).values({"quality": rec_sq.c.quality})
+
+ rec_result = session.execute(rec_upd)
+ rec_update_counter = rec_result.rowcount
+ session.commit()
+ logger.warn("Updated {} ReceiverStats: quality".format(rec_update_counter))
+
+ # Calculate quality_offset of devices
+ dev_sq = (
+ session.query(
+ RelationStats.device_id, (func.sum(RelationStats.beacon_count * (RelationStats.quality - ReceiverStats.quality)) / (func.sum(RelationStats.beacon_count))).label("quality_offset")
+ )
+ .filter(RelationStats.date == date)
+ .filter(and_(RelationStats.receiver_id == ReceiverStats.receiver_id, RelationStats.date == ReceiverStats.date))
+ .group_by(RelationStats.device_id)
+ .subquery()
+ )
+
+ dev_upd = update(DeviceStats).where(and_(DeviceStats.date == date, DeviceStats.device_id == dev_sq.c.device_id)).values({"quality_offset": dev_sq.c.quality_offset})
+
+ dev_result = session.execute(dev_upd)
+ dev_update_counter = dev_result.rowcount
+ session.commit()
+ logger.warn("Updated {} DeviceStats: quality_offset".format(dev_update_counter))
+
+ # Calculate quality_offset of receivers
+ rec_sq = (
+ session.query(
+ RelationStats.receiver_id, (func.sum(RelationStats.beacon_count * (RelationStats.quality - DeviceStats.quality)) / (func.sum(RelationStats.beacon_count))).label("quality_offset")
+ )
+ .filter(RelationStats.date == date)
+ .filter(and_(RelationStats.device_id == DeviceStats.device_id, RelationStats.date == DeviceStats.date))
+ .group_by(RelationStats.receiver_id)
+ .subquery()
+ )
+
+ rec_upd = update(ReceiverStats).where(and_(ReceiverStats.date == date, ReceiverStats.receiver_id == rec_sq.c.receiver_id)).values({"quality_offset": rec_sq.c.quality_offset})
+
+ rec_result = session.execute(rec_upd)
+ rec_update_counter = rec_result.rowcount
+ session.commit()
+ logger.warn("Updated {} ReceiverStats: quality_offset".format(rec_update_counter))
+
+ return "Updated {} DeviceStats and {} ReceiverStats".format(dev_update_counter, rec_update_counter)
+
+
+def update_receivers(session, logger=None):
+ """Update receivers with stats."""
+
+ if logger is None:
+ logger = app.logger
+
+ receiver_stats = (
+ session.query(
+ distinct(ReceiverStats.receiver_id).label("receiver_id"),
+ func.first_value(ReceiverStats.firstseen)
+ .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.firstseen == null(), None)], else_=ReceiverStats.date).asc().nullslast())
+ .label("firstseen"),
+ func.first_value(ReceiverStats.lastseen)
+ .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.lastseen == null(), None)], else_=ReceiverStats.date).desc().nullslast())
+ .label("lastseen"),
+ func.first_value(ReceiverStats.location_wkt)
+ .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.location_wkt == null(), None)], else_=ReceiverStats.date).desc().nullslast())
+ .label("location_wkt"),
+ func.first_value(ReceiverStats.altitude)
+ .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.altitude == null(), None)], else_=ReceiverStats.date).desc().nullslast())
+ .label("altitude"),
+ func.first_value(ReceiverStats.version)
+ .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.version == null(), None)], else_=ReceiverStats.date).desc().nullslast())
+ .label("version"),
+ func.first_value(ReceiverStats.platform)
+ .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.platform == null(), None)], else_=ReceiverStats.date).desc().nullslast())
+ .label("platform"),
+ )
+ .order_by(ReceiverStats.receiver_id)
+ .subquery()
+ )
+
+ upd = (
+ update(Receiver)
+ .where(and_(Receiver.id == receiver_stats.c.receiver_id))
+ .values(
+ {
+ "firstseen": receiver_stats.c.firstseen,
+ "lastseen": receiver_stats.c.lastseen,
+ "location": receiver_stats.c.location_wkt,
+ "altitude": receiver_stats.c.altitude,
+ "version": receiver_stats.c.version,
+ "platform": receiver_stats.c.platform,
+ }
+ )
+ )
+
+ result = session.execute(upd)
+ update_counter = result.rowcount
+ session.commit()
+ logger.warn("Updated {} Receivers".format(update_counter))
+
+ return "Updated {} Receivers".format(update_counter)
+
+
+def update_devices(session, logger=None):
+ """Update devices with stats."""
+
+ if logger is None:
+ logger = app.logger
+
+ device_stats = (
+ session.query(
+ distinct(DeviceStats.device_id).label("device_id"),
+ func.first_value(DeviceStats.name).over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.name == null(), None)], else_=DeviceStats.date).desc().nullslast()).label("name"),
+ func.first_value(DeviceStats.firstseen)
+ .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.firstseen == null(), None)], else_=DeviceStats.date).asc().nullslast())
+ .label("firstseen"),
+ func.max(DeviceStats.lastseen)
+ .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.lastseen == null(), None)], else_=DeviceStats.date).desc().nullslast())
+ .label("lastseen"),
+ func.first_value(DeviceStats.aircraft_type)
+ .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.aircraft_type == null(), None)], else_=DeviceStats.date).desc().nullslast())
+ .label("aircraft_type"),
+ func.first_value(DeviceStats.stealth)
+ .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.stealth == null(), None)], else_=DeviceStats.date).desc().nullslast())
+ .label("stealth"),
+ func.first_value(DeviceStats.software_version)
+ .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.software_version == null(), None)], else_=DeviceStats.date).desc().nullslast())
+ .label("software_version"),
+ func.first_value(DeviceStats.hardware_version)
+ .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.hardware_version == null(), None)], else_=DeviceStats.date).desc().nullslast())
+ .label("hardware_version"),
+ func.first_value(DeviceStats.real_address)
+ .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.real_address == null(), None)], else_=DeviceStats.date).desc().nullslast())
+ .label("real_address"),
+ )
+ .order_by(DeviceStats.device_id)
+ .subquery()
+ )
+
+ upd = (
+ update(Device)
+ .where(and_(Device.id == device_stats.c.device_id))
+ .values(
+ {
+ "name": device_stats.c.name,
+ "firstseen": device_stats.c.firstseen,
+ "lastseen": device_stats.c.lastseen,
+ "aircraft_type": device_stats.c.aircraft_type,
+ "stealth": device_stats.c.stealth,
+ "software_version": device_stats.c.software_version,
+ "hardware_version": device_stats.c.hardware_version,
+ "real_address": device_stats.c.real_address,
+ }
+ )
+ )
+
+ result = session.execute(upd)
+ update_counter = result.rowcount
+ session.commit()
+ logger.warn("Updated {} Devices".format(update_counter))
+
+ return "Updated {} Devices".format(update_counter)
diff --git a/app/collect/takeoff_landings.py b/app/collect/takeoff_landings.py
new file mode 100644
index 0000000..a69078f
--- /dev/null
+++ b/app/collect/takeoff_landings.py
@@ -0,0 +1,144 @@
+from datetime import timedelta
+
+from sqlalchemy import and_, or_, insert, between, exists
+from sqlalchemy.sql import func, null
+from sqlalchemy.sql.expression import case
+
+from app.model import AircraftBeacon, TakeoffLanding, Airport
+
+from app import app
+
+
+def update_entries(session, start, end, logger=None):
+ """Compute takeoffs and landings."""
+
+ if logger is None:
+ logger = app.logger
+
+ logger.info("Compute takeoffs and landings.")
+
+ # considered time interval should not exceed a complete day
+ if end - start > timedelta(days=1):
+ abort_message = "TakeoffLanding: timeinterval start='{}' and end='{}' is too big.".format(start, end)
+ logger.warn(abort_message)
+ return abort_message
+
+ # check if we have any airport
+ airports_query = session.query(Airport).limit(1)
+ if not airports_query.all():
+ abort_message = "TakeoffLanding: Cannot calculate takeoff and landings without any airport! Please import airports first."
+ logger.warn(abort_message)
+ return abort_message
+
+ # takeoff / landing detection is based on 3 consecutive points all below a certain altitude AGL
+ takeoff_speed = 55 # takeoff detection: 1st point below, 2nd and 3rd above this limit
+ landing_speed = 40 # landing detection: 1st point above, 2nd and 3rd below this limit
+ min_takeoff_climb_rate = -5 # takeoff detection: glider should not sink too much
+ max_landing_climb_rate = 5 # landing detection: glider should not climb too much
+ duration = 100 # the points must not exceed this duration
+ radius = 5000 # the points must not exceed this radius around the 2nd point
+ max_agl = 200 # takeoff / landing must not exceed this altitude AGL
+
+ # get beacons for selected time range, one per device_id and timestamp
+ sq = (
+ session.query(AircraftBeacon)
+ .distinct(AircraftBeacon.device_id, AircraftBeacon.timestamp)
+ .order_by(AircraftBeacon.device_id, AircraftBeacon.timestamp, AircraftBeacon.error_count)
+ .filter(AircraftBeacon.agl < max_agl)
+ .filter(between(AircraftBeacon.timestamp, start, end))
+ .subquery()
+ )
+
+ # make a query with current, previous and next position
+ sq2 = session.query(
+ sq.c.device_id,
+ func.lag(sq.c.device_id).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("device_id_prev"),
+ func.lead(sq.c.device_id).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("device_id_next"),
+ sq.c.timestamp,
+ func.lag(sq.c.timestamp).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("timestamp_prev"),
+ func.lead(sq.c.timestamp).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("timestamp_next"),
+ sq.c.location,
+ func.lag(sq.c.location).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("location_wkt_prev"),
+ func.lead(sq.c.location).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("location_wkt_next"),
+ sq.c.track,
+ func.lag(sq.c.track).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("track_prev"),
+ func.lead(sq.c.track).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("track_next"),
+ sq.c.ground_speed,
+ func.lag(sq.c.ground_speed).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("ground_speed_prev"),
+ func.lead(sq.c.ground_speed).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("ground_speed_next"),
+ sq.c.altitude,
+ func.lag(sq.c.altitude).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("altitude_prev"),
+ func.lead(sq.c.altitude).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("altitude_next"),
+ sq.c.climb_rate,
+ func.lag(sq.c.climb_rate).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("climb_rate_prev"),
+ func.lead(sq.c.climb_rate).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label("climb_rate_next"),
+ ).subquery()
+
+ # consider only positions with predecessor and successor and limit distance and duration between points
+ sq3 = (
+ session.query(sq2)
+ .filter(and_(sq2.c.device_id_prev != null(), sq2.c.device_id_next != null()))
+ .filter(and_(func.ST_DistanceSphere(sq2.c.location, sq2.c.location_wkt_prev) < radius, func.ST_DistanceSphere(sq2.c.location, sq2.c.location_wkt_next) < radius))
+ .filter(sq2.c.timestamp_next - sq2.c.timestamp_prev < timedelta(seconds=duration))
+ .subquery()
+ )
+
+ # find possible takeoffs and landings
+ sq4 = (
+ session.query(
+ sq3.c.timestamp,
+ case(
+ [
+ (sq3.c.ground_speed > takeoff_speed, sq3.c.location_wkt_prev), # on takeoff we take the location from the previous fix because it is nearer to the airport
+ (sq3.c.ground_speed <= takeoff_speed, sq3.c.location),
+ ]
+ ).label("location"),
+ case([(sq3.c.ground_speed > landing_speed, sq3.c.track), (sq3.c.ground_speed <= landing_speed, sq3.c.track_prev)]).label(
+ "track"
+ ), # on landing we take the track from the previous fix because gliders tend to leave the runway quickly
+ sq3.c.ground_speed,
+ sq3.c.altitude,
+ case([(sq3.c.ground_speed > takeoff_speed, True), (sq3.c.ground_speed < landing_speed, False)]).label("is_takeoff"),
+ sq3.c.device_id,
+ )
+ .filter(
+ or_(
+ and_(sq3.c.ground_speed_prev < takeoff_speed, sq3.c.ground_speed > takeoff_speed, sq3.c.ground_speed_next > takeoff_speed, sq3.c.climb_rate > min_takeoff_climb_rate), # takeoff
+ and_(sq3.c.ground_speed_prev > landing_speed, sq3.c.ground_speed < landing_speed, sq3.c.ground_speed_next < landing_speed, sq3.c.climb_rate < max_landing_climb_rate), # landing
+ )
+ )
+ .subquery()
+ )
+
+ # consider them if the are near airports ...
+ sq5 = (
+ session.query(
+ sq4.c.timestamp, sq4.c.track, sq4.c.is_takeoff, sq4.c.device_id, Airport.id.label("airport_id"), func.ST_DistanceSphere(sq4.c.location, Airport.location_wkt).label("airport_distance")
+ )
+ .filter(and_(func.ST_Within(sq4.c.location, Airport.border), between(Airport.style, 2, 5)))
+ .subquery()
+ )
+
+ # ... and take the nearest airport
+ sq6 = (
+ session.query(sq5.c.timestamp, sq5.c.track, sq5.c.is_takeoff, sq5.c.device_id, sq5.c.airport_id)
+ .distinct(sq5.c.timestamp, sq5.c.track, sq5.c.is_takeoff, sq5.c.device_id)
+ .order_by(sq5.c.timestamp, sq5.c.track, sq5.c.is_takeoff, sq5.c.device_id, sq5.c.airport_distance)
+ .subquery()
+ )
+
+ # consider them only if they are not already existing in db
+ takeoff_landing_query = session.query(sq6).filter(
+ ~exists().where(and_(TakeoffLanding.timestamp == sq6.c.timestamp, TakeoffLanding.device_id == sq6.c.device_id, TakeoffLanding.airport_id == sq6.c.airport_id))
+ )
+
+ # ... and save them
+ ins = insert(TakeoffLanding).from_select((TakeoffLanding.timestamp, TakeoffLanding.track, TakeoffLanding.is_takeoff, TakeoffLanding.device_id, TakeoffLanding.airport_id), takeoff_landing_query)
+
+ result = session.execute(ins)
+ session.commit()
+ insert_counter = result.rowcount
+
+ finish_message = "TakeoffLandings: {} inserted".format(insert_counter)
+ logger.info(finish_message)
+ return finish_message
diff --git a/ogn_python/commands/__init__.py b/app/commands/__init__.py
similarity index 94%
rename from ogn_python/commands/__init__.py
rename to app/commands/__init__.py
index 469306a..ff3abc5 100644
--- a/ogn_python/commands/__init__.py
+++ b/app/commands/__init__.py
@@ -1,4 +1,4 @@
-from ogn_python import app
+from app import app
from .database import user_cli as database_cli
from .export import user_cli as export_cli
diff --git a/ogn_python/commands/database.py b/app/commands/database.py
similarity index 62%
rename from ogn_python/commands/database.py
rename to app/commands/database.py
index 9def6f8..0410f4e 100644
--- a/ogn_python/commands/database.py
+++ b/app/commands/database.py
@@ -4,14 +4,14 @@ import click
from datetime import datetime, timedelta
from sqlalchemy.sql import func
-from ogn_python.collect.database import update_device_infos, update_country_code
-from ogn_python.model import *
-from ogn_python.utils import get_airports, get_days
+from app.collect.database import update_device_infos, update_country_code
+from app.model import *
+from app.utils import get_airports, get_days
-from ogn_python import app
-from ogn_python import db
+from app import app
+from app import db
-user_cli = AppGroup('database')
+user_cli = AppGroup("database")
user_cli.help = "Database creation and handling."
@@ -22,7 +22,7 @@ def get_database_days(start, end):
"""Returns the first and the last day in aircraft_beacons table."""
if start is None and end is None:
- days_from_db = db.session.query(func.min(AircraftBeacon.timestamp).label('first_day'), func.max(AircraftBeacon.timestamp).label('last_day')).one()
+ days_from_db = db.session.query(func.min(AircraftBeacon.timestamp).label("first_day"), func.max(AircraftBeacon.timestamp).label("last_day")).one()
start = days_from_db[0].date()
end = days_from_db[1].date()
else:
@@ -34,40 +34,40 @@ def get_database_days(start, end):
return days
-@user_cli.command('info')
+@user_cli.command("info")
def info():
print(app.config)
- print(app.config['SQLALCHEMY_DATABASE_URI'])
+ print(app.config["SQLALCHEMY_DATABASE_URI"])
-@user_cli.command('init')
+@user_cli.command("init")
def init():
"""Initialize the database."""
from alembic.config import Config
from alembic import command
- db.session.execute('CREATE EXTENSION IF NOT EXISTS postgis;')
- db.session.execute('CREATE EXTENSION IF NOT EXISTS btree_gist;')
+ db.session.execute("CREATE EXTENSION IF NOT EXISTS postgis;")
+ db.session.execute("CREATE EXTENSION IF NOT EXISTS btree_gist;")
db.session.commit()
db.create_all()
- #alembic_cfg = Config(ALEMBIC_CONFIG_FILE)
- #command.stamp(alembic_cfg, "head")
+ # alembic_cfg = Config(ALEMBIC_CONFIG_FILE)
+ # command.stamp(alembic_cfg, "head")
print("Done.")
-@user_cli.command('init_timescaledb')
+@user_cli.command("init_timescaledb")
def init_timescaledb():
"""Initialize TimescaleDB features."""
- db.session.execute('CREATE EXTENSION IF NOT EXISTS timescaledb;')
+ db.session.execute("CREATE EXTENSION IF NOT EXISTS timescaledb;")
db.session.execute("SELECT create_hypertable('aircraft_beacons', 'timestamp', chunk_target_size => '2GB', if_not_exists => TRUE);")
db.session.execute("SELECT create_hypertable('receiver_beacons', 'timestamp', chunk_target_size => '2GB', if_not_exists => TRUE);")
db.session.commit()
-@user_cli.command('upgrade')
+@user_cli.command("upgrade")
def upgrade():
"""Upgrade database to the latest version."""
@@ -75,21 +75,21 @@ def upgrade():
from alembic import command
alembic_cfg = Config(ALEMBIC_CONFIG_FILE)
- command.upgrade(alembic_cfg, 'head')
+ command.upgrade(alembic_cfg, "head")
-@user_cli.command('drop')
-@click.option('--sure', default='n')
+@user_cli.command("drop")
+@click.option("--sure", default="n")
def drop(sure):
"""Drop all tables."""
- if sure == 'y':
+ if sure == "y":
db.drop_all()
- print('Dropped all tables.')
+ print("Dropped all tables.")
else:
print("Add argument '--sure y' to drop all tables.")
-@user_cli.command('import_ddb')
+@user_cli.command("import_ddb")
def import_ddb():
"""Import registered devices from the DDB."""
@@ -98,33 +98,29 @@ def import_ddb():
print("Imported %i devices." % counter)
-@user_cli.command('import_file')
-@click.argument('path')
-def import_file(path='tests/custom_ddb.txt'):
+@user_cli.command("import_file")
+@click.argument("path")
+def import_file(path="tests/custom_ddb.txt"):
"""Import registered devices from a local file."""
print("Import registered devices from '{}'...".format(path))
- counter = update_device_infos(db.session,
- DeviceInfoOrigin.user_defined,
- path=path)
+ counter = update_device_infos(db.session, DeviceInfoOrigin.user_defined, path=path)
print("Imported %i devices." % counter)
-@user_cli.command('import_flarmnet')
-@click.argument('path')
+@user_cli.command("import_flarmnet")
+@click.argument("path")
def import_flarmnet(path=None):
"""Import registered devices from a local file."""
print("Import registered devices from '{}'...".format("internet" if path is None else path))
- counter = update_device_infos(db.session,
- DeviceInfoOrigin.flarmnet,
- path=path)
+ counter = update_device_infos(db.session, DeviceInfoOrigin.flarmnet, path=path)
print("Imported %i devices." % counter)
-@user_cli.command('import_airports')
-@click.argument('path')
-def import_airports(path='tests/SeeYou.cup'):
+@user_cli.command("import_airports")
+@click.argument("path")
+def import_airports(path="tests/SeeYou.cup"):
"""Import airports from a ".cup" file"""
print("Import airports from '{}'...".format(path))
@@ -136,7 +132,7 @@ def import_airports(path='tests/SeeYou.cup'):
print("Imported {} airports.".format(len(airports)))
-@user_cli.command('update_country_codes')
+@user_cli.command("update_country_codes")
def update_country_codes():
"""Update country codes of all receivers."""
diff --git a/ogn_python/commands/export.py b/app/commands/export.py
similarity index 57%
rename from ogn_python/commands/export.py
rename to app/commands/export.py
index 6ce476c..86fcc30 100644
--- a/ogn_python/commands/export.py
+++ b/app/commands/export.py
@@ -6,14 +6,14 @@ import re
import csv
from aerofiles.igc import Writer
-from ogn_python.model import *
-from ogn_python import db
+from app.model import *
+from app import db
-user_cli = AppGroup('export')
+user_cli = AppGroup("export")
user_cli.help = "Export data in several file formats."
-@user_cli.command('cup')
+@user_cli.command("cup")
def cup():
"""Export receiver waypoints as '.cup'."""
@@ -49,65 +49,61 @@ def cup():
"""
results = db.session.execute(sql)
- with open('receivers.cup', 'w') as outfile:
+ with open("receivers.cup", "w") as outfile:
outcsv = csv.writer(outfile)
outcsv.writerow(results.keys())
outcsv.writerows(results.fetchall())
-@user_cli.command('igc')
-@click.argument('address')
-@click.argument('date')
+
+@user_cli.command("igc")
+@click.argument("address")
+@click.argument("date")
def igc(address, date):
"""Export igc file for
at ."""
- if not re.match('.{6}', address):
+ if not re.match(".{6}", address):
print("Address {} not valid.".format(address))
return
- if not re.match('\d{4}-\d{2}-\d{2}', date):
+ if not re.match("\d{4}-\d{2}-\d{2}", date):
print("Date {} not valid.".format(date))
return
- device_id = db.session.query(Device.id) \
- .filter(Device.address == address) \
- .first()
+ device_id = db.session.query(Device.id).filter(Device.address == address).first()
- if (device_id is None):
+ if device_id is None:
print("Device with address '{}' not found.".format(address))
return
- with open('sample.igc', 'wb') as fp:
+ with open("sample.igc", "wb") as fp:
writer = Writer(fp)
- writer.write_headers({
- 'manufacturer_code': 'OGN',
- 'logger_id': 'OGN',
- 'date': datetime.date(1987, 2, 24),
- 'fix_accuracy': 50,
- 'pilot': 'Konstantin Gruendger',
- 'copilot': '',
- 'glider_type': 'Duo Discus',
- 'glider_id': 'D-KKHH',
- 'firmware_version': '2.2',
- 'hardware_version': '2',
- 'logger_type': 'LXNAVIGATION,LX8000F',
- 'gps_receiver': 'uBLOX LEA-4S-2,16,max9000m',
- 'pressure_sensor': 'INTERSEMA,MS5534A,max10000m',
- 'competition_id': '2H',
- 'competition_class': 'Doubleseater',
- })
+ writer.write_headers(
+ {
+ "manufacturer_code": "OGN",
+ "logger_id": "OGN",
+ "date": datetime.date(1987, 2, 24),
+ "fix_accuracy": 50,
+ "pilot": "Konstantin Gruendger",
+ "copilot": "",
+ "glider_type": "Duo Discus",
+ "glider_id": "D-KKHH",
+ "firmware_version": "2.2",
+ "hardware_version": "2",
+ "logger_type": "LXNAVIGATION,LX8000F",
+ "gps_receiver": "uBLOX LEA-4S-2,16,max9000m",
+ "pressure_sensor": "INTERSEMA,MS5534A,max10000m",
+ "competition_id": "2H",
+ "competition_class": "Doubleseater",
+ }
+ )
- points = db.session.query(AircraftBeacon) \
- .filter(AircraftBeacon.device_id == device_id) \
- .filter(AircraftBeacon.timestamp > date + ' 00:00:00') \
- .filter(AircraftBeacon.timestamp < date + ' 23:59:59') \
+ points = (
+ db.session.query(AircraftBeacon)
+ .filter(AircraftBeacon.device_id == device_id)
+ .filter(AircraftBeacon.timestamp > date + " 00:00:00")
+ .filter(AircraftBeacon.timestamp < date + " 23:59:59")
.order_by(AircraftBeacon.timestamp)
+ )
for point in points.all():
- writer.write_fix(
- point.timestamp.time(),
- latitude=point.location.latitude,
- longitude=point.location.longitude,
- valid=True,
- pressure_alt=point.altitude,
- gps_alt=point.altitude,
- )
+ writer.write_fix(point.timestamp.time(), latitude=point.location.latitude, longitude=point.location.longitude, valid=True, pressure_alt=point.altitude, gps_alt=point.altitude)
diff --git a/ogn_python/commands/flights.py b/app/commands/flights.py
similarity index 89%
rename from ogn_python/commands/flights.py
rename to app/commands/flights.py
index c963297..5c81f63 100644
--- a/ogn_python/commands/flights.py
+++ b/app/commands/flights.py
@@ -4,15 +4,15 @@ import click
from datetime import datetime
from tqdm import tqdm
-from ogn_python.commands.database import get_database_days
-from ogn_python import db
+from app.commands.database import get_database_days
+from app import db
-user_cli = AppGroup('flights')
+user_cli = AppGroup("flights")
user_cli.help = "Create 2D flight paths from data."
-NOTHING = ''
-CONTEST_RELEVANT = 'AND agl < 1000'
-LOW_PASS = 'AND agl < 50 and ground_speed > 250'
+NOTHING = ""
+CONTEST_RELEVANT = "AND agl < 1000"
+LOW_PASS = "AND agl < 50 and ground_speed > 250"
def compute_gaps(session, date):
@@ -46,7 +46,9 @@ def compute_gaps(session, date):
) sq3
GROUP BY sq3.device_id
ON CONFLICT DO NOTHING;
- """.format(date=date.strftime('%Y-%m-%d'))
+ """.format(
+ date=date.strftime("%Y-%m-%d")
+ )
session.execute(query)
session.commit()
@@ -111,17 +113,17 @@ def compute_flights2d(session, date, flight_type):
) sq5
GROUP BY sq5.device_id
ON CONFLICT DO NOTHING;
- """.format(date=date.strftime('%Y-%m-%d'),
- flight_type=flight_type,
- filter=filter)
+ """.format(
+ date=date.strftime("%Y-%m-%d"), flight_type=flight_type, filter=filter
+ )
session.execute(query)
session.commit()
-@user_cli.command('create')
-@click.argument('start')
-@click.argument('end')
-@click.argument('flight_type', type=click.INT)
+@user_cli.command("create")
+@click.argument("start")
+@click.argument("end")
+@click.argument("flight_type", type=click.INT)
def create(start, end, flight_type):
"""Compute flights. Flight type: 0: all flights, 1: below 1000m AGL, 2: below 50m AGL + faster than 250 km/h, 3: inverse coverage'"""
@@ -129,7 +131,7 @@ def create(start, end, flight_type):
pbar = tqdm(days)
for single_date in pbar:
- pbar.set_description(datetime.strftime(single_date, '%Y-%m-%d'))
+ pbar.set_description(datetime.strftime(single_date, "%Y-%m-%d"))
if flight_type <= 2:
result = compute_flights2d(session=db.session, date=single_date, flight_type=flight_type)
else:
diff --git a/ogn_python/commands/gateway.py b/app/commands/gateway.py
similarity index 59%
rename from ogn_python/commands/gateway.py
rename to app/commands/gateway.py
index 2d1039c..d880808 100644
--- a/ogn_python/commands/gateway.py
+++ b/app/commands/gateway.py
@@ -2,33 +2,33 @@ from flask.cli import AppGroup
import click
from ogn.client import AprsClient
-from ogn_python.gateway.bulkimport import ContinuousDbFeeder
+from app.gateway.bulkimport import ContinuousDbFeeder
-from ogn_python import app
+from app import app
-user_cli = AppGroup('gateway')
+user_cli = AppGroup("gateway")
user_cli.help = "Connection to APRS servers."
-@user_cli.command('run')
-def run(aprs_user='anon-dev'):
+@user_cli.command("run")
+def run(aprs_user="anon-dev"):
"""Run the aprs client."""
saver = ContinuousDbFeeder()
# User input validation
if len(aprs_user) < 3 or len(aprs_user) > 9:
- print('aprs_user must be a string of 3-9 characters.')
+ print("aprs_user must be a string of 3-9 characters.")
return
- app.logger.warning('Start ogn gateway')
+ app.logger.warning("Start ogn gateway")
client = AprsClient(aprs_user)
client.connect()
try:
client.run(callback=saver.add, autoreconnect=True)
except KeyboardInterrupt:
- app.logger.warning('\nStop ogn gateway')
+ app.logger.warning("\nStop ogn gateway")
saver.flush()
client.disconnect()
diff --git a/app/commands/logbook.py b/app/commands/logbook.py
new file mode 100644
index 0000000..01d5e81
--- /dev/null
+++ b/app/commands/logbook.py
@@ -0,0 +1,119 @@
+from flask.cli import AppGroup
+import click
+
+from datetime import datetime
+
+from app.collect.logbook import update_entries as logbook_update_entries
+from app.collect.takeoff_landings import update_entries as takeoff_landings_update_entries
+from app.model import Airport, Logbook
+from sqlalchemy.sql import func
+from tqdm import tqdm
+from app.commands.database import get_database_days
+from app.utils import date_to_timestamps
+
+from app import db
+
+user_cli = AppGroup("logbook")
+user_cli.help = "Handling of logbook data."
+
+
+@user_cli.command("compute_takeoff_landing")
+@click.argument("start")
+@click.argument("end")
+def compute_takeoff_landing(start, end):
+ """Compute takeoffs and landings."""
+
+ days = get_database_days(start, end)
+
+ pbar = tqdm(days)
+ for single_date in pbar:
+ pbar.set_description(datetime.strftime(single_date, "%Y-%m-%d"))
+ (start, end) = date_to_timestamps(single_date)
+ result = takeoff_landings_update_entries(session=db.session, start=start, end=end)
+
+
+@user_cli.command("compute_logbook")
+@click.argument("start")
+@click.argument("end")
+def compute_logbook(start, end):
+ """Compute logbook."""
+
+ days = get_database_days(start, end)
+
+ pbar = tqdm(days)
+ for single_date in pbar:
+ pbar.set_description(single_date.strftime("%Y-%m-%d"))
+ result = logbook_update_entries(session=db.session, date=single_date)
+
+
+@user_cli.command("show")
+@click.argument("airport_name")
+@click.argument("date")
+def show(airport_name, date=None):
+ """Show a logbook for ."""
+ airport = db.session.query(Airport).filter(Airport.name == airport_name).first()
+
+ if airport is None:
+ print('Airport "{}" not found.'.format(airport_name))
+ return
+
+ or_args = []
+ if date is not None:
+ date = datetime.strptime(date, "%Y-%m-%d")
+ (start, end) = date_to_timestamps(date)
+ or_args = [db.between(Logbook.reftime, start, end)]
+
+ # get all logbook entries and add device and airport infos
+ logbook_query = (
+ db.session.query(func.row_number().over(order_by=Logbook.reftime).label("row_number"), Logbook)
+ .filter(*or_args)
+ .filter(db.or_(Logbook.takeoff_airport_id == airport.id, Logbook.landing_airport_id == airport.id))
+ .order_by(Logbook.reftime)
+ )
+
+ # ... and finally print out the logbook
+ print("--- Logbook ({}) ---".format(airport_name))
+
+ def none_datetime_replacer(datetime_object):
+ return "--:--:--" if datetime_object is None else datetime_object.time()
+
+ def none_track_replacer(track_object):
+ return "--" if track_object is None else round(track_object / 10.0)
+
+ def none_timedelta_replacer(timedelta_object):
+ return "--:--:--" if timedelta_object is None else timedelta_object
+
+ def none_registration_replacer(device_object):
+ return "[" + device_object.address + "]" if len(device_object.infos) == 0 else device_object.infos[0].registration
+
+ def none_aircraft_replacer(device_object):
+ return "(unknown)" if len(device_object.infos) == 0 else device_object.infos[0].aircraft
+
+ def airport_marker(logbook_object):
+ if logbook_object.takeoff_airport is not None and logbook_object.takeoff_airport.name is not airport.name:
+ return "FROM: {}".format(logbook_object.takeoff_airport.name)
+ elif logbook_object.landing_airport is not None and logbook_object.landing_airport.name is not airport.name:
+ return "TO: {}".format(logbook_object.landing_airport.name)
+ else:
+ return ""
+
+ def none_altitude_replacer(logbook_object):
+ return "?" if logbook_object.max_altitude is None else "{:5d}m ({:+5d}m)".format(logbook_object.max_altitude, logbook_object.max_altitude - logbook_object.takeoff_airport.altitude)
+
+ for [row_number, logbook] in logbook_query.all():
+ print(
+ "%3d. %10s %8s (%2s) %8s (%2s) %8s %15s %8s %17s %20s"
+ % (
+ row_number,
+ logbook.reftime.date(),
+ none_datetime_replacer(logbook.takeoff_timestamp),
+ none_track_replacer(logbook.takeoff_track),
+ none_datetime_replacer(logbook.landing_timestamp),
+ none_track_replacer(logbook.landing_track),
+ none_timedelta_replacer(logbook.duration),
+ none_altitude_replacer(logbook),
+ none_registration_replacer(logbook.device),
+ none_aircraft_replacer(logbook.device),
+ airport_marker(logbook),
+ )
+ )
diff --git a/ogn_python/commands/stats.py b/app/commands/stats.py
similarity index 59%
rename from ogn_python/commands/stats.py
rename to app/commands/stats.py
index c2af4ca..91ec5ee 100644
--- a/ogn_python/commands/stats.py
+++ b/app/commands/stats.py
@@ -4,24 +4,31 @@ import click
from datetime import datetime
from tqdm import tqdm
-from ogn_python.commands.database import get_database_days
+from app.commands.database import get_database_days
-from ogn_python.collect.stats import create_device_stats, create_receiver_stats, create_relation_stats, create_country_stats,\
- update_qualities, update_receivers as update_receivers_command, update_devices as update_devices_command,\
- update_device_stats_jumps
+from app.collect.stats import (
+ create_device_stats,
+ create_receiver_stats,
+ create_relation_stats,
+ create_country_stats,
+ update_qualities,
+ update_receivers as update_receivers_command,
+ update_devices as update_devices_command,
+ update_device_stats_jumps,
+)
-from ogn_python.collect.ognrange import update_entries as update_receiver_coverages
+from app.collect.ognrange import update_entries as update_receiver_coverages
-from ogn_python import db
+from app import db
-user_cli = AppGroup('stats')
+user_cli = AppGroup("stats")
user_cli.help = "Handling of statistical data."
-@user_cli.command('create')
-@click.argument('start')
-@click.argument('end')
+@user_cli.command("create")
+@click.argument("start")
+@click.argument("end")
def create(start, end):
"""Create DeviceStats, ReceiverStats and RelationStats."""
@@ -29,16 +36,17 @@ def create(start, end):
pbar = tqdm(days)
for single_date in pbar:
- pbar.set_description(datetime.strftime(single_date, '%Y-%m-%d'))
+ pbar.set_description(datetime.strftime(single_date, "%Y-%m-%d"))
result = create_device_stats(session=db.session, date=single_date)
result = update_device_stats_jumps(session=db.session, date=single_date)
result = create_receiver_stats(session=db.session, date=single_date)
result = create_relation_stats(session=db.session, date=single_date)
result = update_qualities(session=db.session, date=single_date)
-@user_cli.command('create_country')
-@click.argument('start')
-@click.argument('end')
+
+@user_cli.command("create_country")
+@click.argument("start")
+@click.argument("end")
def create_country(start, end):
"""Create CountryStats."""
@@ -46,22 +54,29 @@ def create_country(start, end):
pbar = tqdm(days)
for single_date in pbar:
- pbar.set_description(datetime.strftime(single_date, '%Y-%m-%d'))
+ pbar.set_description(datetime.strftime(single_date, "%Y-%m-%d"))
result = create_country_stats(session=db.session, date=single_date)
-from ogn_python.model import *
-@user_cli.command('update_devices_name')
+
+from app.model import *
+
+
+@user_cli.command("update_devices_name")
def update_devices_name():
"""Update Devices name."""
device_ids = db.session.query(Device.id).all()
for device_id in tqdm(device_ids):
- db.session.execute("update devices d set name = sq.name from ( select * from aircraft_beacons ab where ab.device_id = {} limit 1) sq where d.id = sq.device_id and d.name is null or d.name = 'ICA3D3CC4';".format(device_id[0]))
+ db.session.execute(
+ "update devices d set name = sq.name from ( select * from aircraft_beacons ab where ab.device_id = {} limit 1) sq where d.id = sq.device_id and d.name is null or d.name = 'ICA3D3CC4';".format(
+ device_id[0]
+ )
+ )
db.session.commit()
-@user_cli.command('update_receivers')
+@user_cli.command("update_receivers")
def update_receivers():
"""Update receivers with data from stats."""
@@ -69,7 +84,7 @@ def update_receivers():
print(result)
-@user_cli.command('update_devices')
+@user_cli.command("update_devices")
def update_devices():
"""Update devices with data from stats."""
@@ -77,9 +92,9 @@ def update_devices():
print(result)
-@user_cli.command('update_mgrs')
-@click.argument('start')
-@click.argument('end')
+@user_cli.command("update_mgrs")
+@click.argument("start")
+@click.argument("end")
def update_mgrs(start, end):
"""Create location_mgrs_short."""
@@ -87,23 +102,25 @@ def update_mgrs(start, end):
pbar = tqdm(days)
for single_date in pbar:
- datestr = datetime.strftime(single_date, '%Y-%m-%d')
+ datestr = datetime.strftime(single_date, "%Y-%m-%d")
pbar.set_description(datestr)
for pbar2 in tqdm(["{:02d}:{:02d}".format(hh, mm) for hh in range(0, 24) for mm in range(0, 60)]):
sql = """
UPDATE aircraft_beacons
SET location_mgrs_short = left(location_mgrs, 5) || substring(location_mgrs, 6, 2) || substring(location_mgrs, 11, 2)
WHERE timestamp BETWEEN '{0} {1}:00' and '{0} {1}:59' AND location_mgrs_short IS NULL;
- """.format(datestr, pbar2)
+ """.format(
+ datestr, pbar2
+ )
- #print(sql)
+ # print(sql)
db.session.execute(sql)
db.session.commit()
-@user_cli.command('create_ognrange')
-@click.argument('start')
-@click.argument('end')
+@user_cli.command("create_ognrange")
+@click.argument("start")
+@click.argument("end")
def create_ognrange(start=None, end=None):
"""Create receiver coverage stats for Melissas ognrange."""
@@ -111,5 +128,5 @@ def create_ognrange(start=None, end=None):
pbar = tqdm(days)
for single_date in pbar:
- pbar.set_description(datetime.strftime(single_date, '%Y-%m-%d'))
+ pbar.set_description(datetime.strftime(single_date, "%Y-%m-%d"))
result = update_receiver_coverages(session=db.session, date=single_date)
diff --git a/ogn_python/config/__init__.py b/app/config/__init__.py
similarity index 100%
rename from ogn_python/config/__init__.py
rename to app/config/__init__.py
diff --git a/app/config/default.py b/app/config/default.py
new file mode 100644
index 0000000..9ffe16d
--- /dev/null
+++ b/app/config/default.py
@@ -0,0 +1,27 @@
+SECRET_KEY = "i-like-ogn"
+
+SQLALCHEMY_DATABASE_URI = "postgresql://postgres@localhost:5432/ogn"
+SQLALCHEMY_TRACK_MODIFICATIONS = False
+
+# Flask-Cache stuff
+CACHE_TYPE = "simple"
+CACHE_DEFAULT_TIMEOUT = 300
+
+# Celery stuff
+CELERY_BROKER_URL = "redis://localhost:6379/0"
+CELERY_RESULT_BACKEND = "redis://localhost:6379/0"
+
+
+from celery.schedules import crontab
+from datetime import timedelta
+
+CELERYBEAT_SCHEDULE = {
+ "update-ddb": {"task": "import_ddb", "schedule": timedelta(hours=1)},
+ "update-country-codes": {"task": "update_receivers_country_code", "schedule": timedelta(days=1)},
+ "update-takeoff-and-landing": {"task": "update_takeoff_landings", "schedule": timedelta(hours=1), "kwargs": {"last_minutes": 90}},
+ "update-logbook": {"task": "update_logbook_entries", "schedule": timedelta(hours=2), "kwargs": {"day_offset": 0}},
+ "update-max-altitudes": {"task": "update_logbook_max_altitude", "schedule": timedelta(hours=1), "kwargs": {"day_offset": 0}},
+ "update-stats-daily": {"task": "update_stats", "schedule": crontab(hour=0, minute=5), "kwargs": {"day_offset": -1}},
+ "update-logbook-daily": {"task": "update_logbook_entries", "schedule": crontab(hour=1, minute=0), "kwargs": {"day_offset": -1}},
+ "purge_old_data": {"task": "purge_old_data", "schedule": timedelta(hours=1), "kwargs": {"max_hours": 48}},
+}
diff --git a/app/config/test.py b/app/config/test.py
new file mode 100644
index 0000000..8bea0a8
--- /dev/null
+++ b/app/config/test.py
@@ -0,0 +1,6 @@
+SQLALCHEMY_DATABASE_URI = "postgresql://postgres@localhost:5432/ogn_test"
+SQLALCHEMY_TRACK_MODIFICATIONS = False
+
+# Celery stuff
+CELERY_BROKER_URL = "redis://localhost:6379/0"
+CELERY_RESULT_BACKEND = "redis://localhost:6379/0"
diff --git a/ogn_python/flask_celery.py b/app/flask_celery.py
similarity index 62%
rename from ogn_python/flask_celery.py
rename to app/flask_celery.py
index e9cfba6..8fb150c 100644
--- a/ogn_python/flask_celery.py
+++ b/app/flask_celery.py
@@ -1,11 +1,8 @@
from celery import Celery
+
def make_celery(app):
- celery = Celery(
- app.import_name,
- backend=app.config['CELERY_RESULT_BACKEND'],
- broker=app.config['CELERY_BROKER_URL']
- )
+ celery = Celery(app.import_name, backend=app.config["CELERY_RESULT_BACKEND"], broker=app.config["CELERY_BROKER_URL"])
celery.conf.update(app.config)
class ContextTask(celery.Task):
@@ -14,4 +11,4 @@ def make_celery(app):
return self.run(*args, **kwargs)
celery.Task = ContextTask
- return celery
\ No newline at end of file
+ return celery
diff --git a/ogn_python/gateway/__init__.py b/app/gateway/__init__.py
similarity index 100%
rename from ogn_python/gateway/__init__.py
rename to app/gateway/__init__.py
diff --git a/app/gateway/bulkimport.py b/app/gateway/bulkimport.py
new file mode 100644
index 0000000..fc5e098
--- /dev/null
+++ b/app/gateway/bulkimport.py
@@ -0,0 +1,407 @@
+from datetime import datetime, timedelta
+from io import StringIO
+
+from flask.cli import AppGroup
+import click
+from tqdm import tqdm
+from mgrs import MGRS
+
+from ogn.parser import parse, ParseError
+
+from app.model import AircraftBeacon, ReceiverBeacon, Location
+from app.utils import open_file
+from app.gateway.process_tools import *
+
+from app import db
+from app import app
+
+user_cli = AppGroup("bulkimport")
+user_cli.help = "Tools for accelerated data import."
+
+
+# define message types we want to proceed
+AIRCRAFT_BEACON_TYPES = ["aprs_aircraft", "flarm", "tracker", "fanet", "lt24", "naviter", "skylines", "spider", "spot"]
+RECEIVER_BEACON_TYPES = ["aprs_receiver", "receiver"]
+
+# define fields we want to proceed
+BEACON_KEY_FIELDS = ["name", "receiver_name", "timestamp"]
+AIRCRAFT_BEACON_FIELDS = [
+ "location",
+ "altitude",
+ "dstcall",
+ "relay",
+ "track",
+ "ground_speed",
+ "address_type",
+ "aircraft_type",
+ "stealth",
+ "address",
+ "climb_rate",
+ "turn_rate",
+ "signal_quality",
+ "error_count",
+ "frequency_offset",
+ "gps_quality_horizontal",
+ "gps_quality_vertical",
+ "software_version",
+ "hardware_version",
+ "real_address",
+ "signal_power",
+ "distance",
+ "radial",
+ "quality",
+ "location_mgrs",
+ "location_mgrs_short",
+ "agl",
+ "receiver_id",
+ "device_id",
+]
+RECEIVER_BEACON_FIELDS = [
+ "location",
+ "altitude",
+ "dstcall",
+ "relay",
+ "version",
+ "platform",
+ "cpu_load",
+ "free_ram",
+ "total_ram",
+ "ntp_error",
+ "rt_crystal_correction",
+ "voltage",
+ "amperage",
+ "cpu_temp",
+ "senders_visible",
+ "senders_total",
+ "rec_input_noise",
+ "senders_signal",
+ "senders_messages",
+ "good_senders_signal",
+ "good_senders",
+ "good_and_bad_senders",
+]
+
+
+myMGRS = MGRS()
+
+
+def string_to_message(raw_string, reference_date):
+ global receivers
+
+ try:
+ message = parse(raw_string, reference_date)
+ except NotImplementedError as e:
+ app.logger.error("No parser implemented for message: {}".format(raw_string))
+ return None
+ except ParseError as e:
+ app.logger.error("Parsing error with message: {}".format(raw_string))
+ return None
+ except TypeError as e:
+ app.logger.error("TypeError with message: {}".format(raw_string))
+ return None
+ except Exception as e:
+ app.logger.error("Other Exception with string: {}".format(raw_string))
+ return None
+
+ # update reference receivers and distance to the receiver
+ if message["aprs_type"] == "position":
+ if message["beacon_type"] in AIRCRAFT_BEACON_TYPES + RECEIVER_BEACON_TYPES:
+ latitude = message["latitude"]
+ longitude = message["longitude"]
+
+ location = Location(longitude, latitude)
+ message["location"] = location.to_wkt()
+ location_mgrs = myMGRS.toMGRS(latitude, longitude).decode("utf-8")
+ message["location_mgrs"] = location_mgrs
+ message["location_mgrs_short"] = location_mgrs[0:5] + location_mgrs[5:7] + location_mgrs[10:12]
+
+ if message["beacon_type"] in AIRCRAFT_BEACON_TYPES and "gps_quality" in message:
+ if message["gps_quality"] is not None and "horizontal" in message["gps_quality"]:
+ message["gps_quality_horizontal"] = message["gps_quality"]["horizontal"]
+ message["gps_quality_vertical"] = message["gps_quality"]["vertical"]
+ del message["gps_quality"]
+
+ # TODO: Fix python-ogn-client 0.91
+ if "senders_messages" in message and message["senders_messages"] is not None:
+ message["senders_messages"] = int(message["senders_messages"])
+ if "good_senders" in message and message["good_senders"] is not None:
+ message["good_senders"] = int(message["good_senders"])
+ if "good_and_bad_senders" in message and message["good_and_bad_senders"] is not None:
+ message["good_and_bad_senders"] = int(message["good_and_bad_senders"])
+
+ return message
+
+
+class ContinuousDbFeeder:
+ def __init__(self,):
+ self.postfix = "continuous_import"
+ self.last_flush = datetime.utcnow()
+ self.last_add_missing = datetime.utcnow()
+ self.last_transfer = datetime.utcnow()
+
+ self.aircraft_buffer = StringIO()
+ self.receiver_buffer = StringIO()
+
+ create_tables(self.postfix)
+ create_indices(self.postfix)
+
+ def add(self, raw_string):
+ message = string_to_message(raw_string, reference_date=datetime.utcnow())
+
+ if message is None or ("raw_message" in message and message["raw_message"][0] == "#") or "beacon_type" not in message:
+ return
+
+ if message["beacon_type"] in AIRCRAFT_BEACON_TYPES:
+ complete_message = ",".join([str(message[k]) if k in message and message[k] is not None else "\\N" for k in BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS])
+ self.aircraft_buffer.write(complete_message)
+ self.aircraft_buffer.write("\n")
+ elif message["beacon_type"] in RECEIVER_BEACON_TYPES:
+ complete_message = ",".join([str(message[k]) if k in message and message[k] is not None else "\\N" for k in BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS])
+ self.receiver_buffer.write(complete_message)
+ self.receiver_buffer.write("\n")
+ else:
+ app.logger.error("Ignore beacon_type: {}".format(message["beacon_type"]))
+ return
+
+ if datetime.utcnow() - self.last_flush >= timedelta(seconds=20):
+ self.flush()
+ self.prepare()
+
+ self.aircraft_buffer = StringIO()
+ self.receiver_buffer = StringIO()
+
+ self.last_flush = datetime.utcnow()
+
+ if datetime.utcnow() - self.last_add_missing >= timedelta(seconds=60):
+ self.add_missing()
+ self.last_add_missing = datetime.utcnow()
+
+ if datetime.utcnow() - self.last_transfer >= timedelta(seconds=30):
+ self.transfer()
+ self.delete_beacons()
+ self.last_transfer = datetime.utcnow()
+
+ def flush(self):
+ self.aircraft_buffer.seek(0)
+ self.receiver_buffer.seek(0)
+
+ connection = db.engine.raw_connection()
+ cursor = connection.cursor()
+ cursor.copy_from(self.aircraft_buffer, "aircraft_beacons_{0}".format(self.postfix), sep=",", columns=BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS)
+ cursor.copy_from(self.receiver_buffer, "receiver_beacons_{0}".format(self.postfix), sep=",", columns=BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS)
+ connection.commit()
+
+ self.aircraft_buffer = StringIO()
+ self.receiver_buffer = StringIO()
+
+ def add_missing(self):
+ add_missing_receivers(self.postfix)
+ add_missing_devices(self.postfix)
+
+ def prepare(self):
+ # make receivers complete
+ update_receiver_beacons(self.postfix)
+ update_receiver_location(self.postfix)
+
+ # make devices complete
+ update_aircraft_beacons(self.postfix)
+
+ def transfer(self):
+ # tranfer beacons
+ transfer_aircraft_beacons(self.postfix)
+ transfer_receiver_beacons(self.postfix)
+
+ def delete_beacons(self):
+ # delete already transfered beacons
+ delete_receiver_beacons(self.postfix)
+ delete_aircraft_beacons(self.postfix)
+
+
+class FileDbFeeder:
+ def __init__(self):
+ self.postfix = "continuous_import"
+ self.last_flush = datetime.utcnow()
+
+ self.aircraft_buffer = StringIO()
+ self.receiver_buffer = StringIO()
+
+ create_tables(self.postfix)
+ create_indices(self.postfix)
+
+ def add(self, raw_string):
+ message = string_to_message(raw_string, reference_date=datetime.utcnow())
+
+ if message is None or ("raw_message" in message and message["raw_message"][0] == "#") or "beacon_type" not in message:
+ return
+
+ if message["beacon_type"] in AIRCRAFT_BEACON_TYPES:
+ complete_message = ",".join([str(message[k]) if k in message and message[k] is not None else "\\N" for k in BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS])
+ self.aircraft_buffer.write(complete_message)
+ self.aircraft_buffer.write("\n")
+ elif message["beacon_type"] in RECEIVER_BEACON_TYPES:
+ complete_message = ",".join([str(message[k]) if k in message and message[k] is not None else "\\N" for k in BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS])
+ self.receiver_buffer.write(complete_message)
+ self.receiver_buffer.write("\n")
+ else:
+ app.logger.error("Ignore beacon_type: {}".format(message["beacon_type"]))
+ return
+
+ def prepare(self):
+ # make receivers complete
+ add_missing_receivers(self.postfix)
+ update_receiver_location(self.postfix)
+
+ # make devices complete
+ add_missing_devices(self.postfix)
+
+ # prepare beacons for transfer
+ create_indices(self.postfix)
+ update_receiver_beacons_bigdata(self.postfix)
+ update_aircraft_beacons_bigdata(self.postfix)
+
+
+def get_aircraft_beacons_postfixes():
+ """Get the postfixes from imported aircraft_beacons logs."""
+
+ postfixes = db.session.execute(
+ """
+ SELECT DISTINCT(RIGHT(tablename, 8))
+ FROM pg_catalog.pg_tables
+ WHERE schemaname = 'public' AND tablename LIKE 'aircraft\_beacons\_20______'
+ ORDER BY RIGHT(tablename, 10);
+ """
+ ).fetchall()
+
+ return [postfix for postfix in postfixes]
+
+
+def export_to_path(postfix):
+ import os, gzip
+
+ aircraft_beacons_file = os.path.join(path, "aircraft_beacons_{0}.csv.gz".format(postfix))
+ with gzip.open(aircraft_beacons_file, "wt", encoding="utf-8") as gzip_file:
+ self.cur.copy_expert("COPY ({}) TO STDOUT WITH (DELIMITER ',', FORMAT CSV, HEADER, ENCODING 'UTF-8');".format(self.get_merged_aircraft_beacons_subquery()), gzip_file)
+ receiver_beacons_file = os.path.join(path, "receiver_beacons_{0}.csv.gz".format(postfix))
+ with gzip.open(receiver_beacons_file, "wt") as gzip_file:
+ self.cur.copy_expert("COPY ({}) TO STDOUT WITH (DELIMITER ',', FORMAT CSV, HEADER, ENCODING 'UTF-8');".format(self.get_merged_receiver_beacons_subquery()), gzip_file)
+
+
+def convert(sourcefile, datestr, saver):
+ from app.gateway.process import string_to_message
+ from app.gateway.process_tools import AIRCRAFT_BEACON_TYPES, RECEIVER_BEACON_TYPES
+ from datetime import datetime
+
+ fin = open_file(sourcefile)
+
+ # get total lines of the input file
+ total_lines = 0
+ for line in fin:
+ total_lines += 1
+ fin.seek(0)
+
+ current_line = 0
+ steps = 100000
+ reference_date = datetime.strptime(datestr + " 12:00:00", "%Y-%m-%d %H:%M:%S")
+
+ pbar = tqdm(fin, total=total_lines)
+ for line in pbar:
+ pbar.set_description("Importing {}".format(sourcefile))
+
+ current_line += 1
+ if current_line % steps == 0:
+ saver.flush()
+
+ message = string_to_message(line.strip(), reference_date=reference_date)
+ if message is None:
+ continue
+
+ dictfilt = lambda x, y: dict([(i, x[i]) for i in x if i in set(y)])
+
+ try:
+ if message["beacon_type"] in AIRCRAFT_BEACON_TYPES:
+ message = dictfilt(
+ message,
+ (
+ "beacon_type",
+ "aprs_type",
+ "location_wkt",
+ "altitude",
+ "name",
+ "dstcall",
+ "relay",
+ "receiver_name",
+ "timestamp",
+ "track",
+ "ground_speed",
+ "address_type",
+ "aircraft_type",
+ "stealth",
+ "address",
+ "climb_rate",
+ "turn_rate",
+ "signal_quality",
+ "error_count",
+ "frequency_offset",
+ "gps_quality_horizontal",
+ "gps_quality_vertical",
+ "software_version",
+ "hardware_version",
+ "real_address",
+ "signal_power",
+ "distance",
+ "radial",
+ "quality",
+ "agl",
+ "location_mgrs",
+ "location_mgrs_short",
+ "receiver_id",
+ "device_id",
+ ),
+ )
+
+ beacon = AircraftBeacon(**message)
+ elif message["beacon_type"] in RECEIVER_BEACON_TYPES:
+ if "rec_crystal_correction" in message:
+ del message["rec_crystal_correction"]
+ del message["rec_crystal_correction_fine"]
+ beacon = ReceiverBeacon(**message)
+ saver.add(beacon)
+ except Exception as e:
+ print(e)
+
+ saver.flush()
+ fin.close()
+
+
+@user_cli.command("file_import")
+@click.argument("path")
+def file_import(path):
+ """Import APRS logfiles into separate logfile tables."""
+
+ import os
+ import re
+
+ # Get Filepaths and dates to import
+ results = list()
+ for (root, dirs, files) in os.walk(path):
+ for file in sorted(files):
+ match = re.match("OGN_log\.txt_([0-9]{4}\-[0-9]{2}\-[0-9]{2})\.gz$", file)
+ if match:
+ results.append({"filepath": os.path.join(root, file), "datestr": match.group(1)})
+
+ with LogfileDbSaver() as saver:
+ already_imported = saver.get_datestrs()
+
+ results = list(filter(lambda x: x["datestr"] not in already_imported, results))
+
+ pbar = tqdm(results)
+ for result in pbar:
+ filepath = result["filepath"]
+ datestr = result["datestr"]
+ pbar.set_description("Importing data for {}".format(datestr))
+
+ saver.set_datestr(datestr)
+ saver.create_tables()
+ convert(filepath, datestr, saver)
+ saver.add_missing_devices()
+ saver.add_missing_receivers()
diff --git a/ogn_python/gateway/process_tools.py b/app/gateway/process_tools.py
similarity index 91%
rename from ogn_python/gateway/process_tools.py
rename to app/gateway/process_tools.py
index 35f0367..4e9d927 100644
--- a/ogn_python/gateway/process_tools.py
+++ b/app/gateway/process_tools.py
@@ -1,4 +1,5 @@
-from ogn_python import db
+from app import db
+
def create_tables(postfix):
"""Create tables for log file import."""
@@ -11,42 +12,55 @@ def create_tables(postfix):
def create_indices(postfix):
"""Creates indices for aircraft- and receiver-beacons."""
- db.session.execute("""
+ db.session.execute(
+ """
CREATE INDEX IF NOT EXISTS ix_aircraft_beacons_{0}_device_id ON "aircraft_beacons_{0}" (device_id NULLS FIRST);
CREATE INDEX IF NOT EXISTS ix_aircraft_beacons_{0}_receiver_id ON "aircraft_beacons_{0}" (receiver_id NULLS FIRST);
CREATE INDEX IF NOT EXISTS ix_aircraft_beacons_{0}_timestamp_name_receiver_name ON "aircraft_beacons_{0}" (timestamp, name, receiver_name);
CREATE INDEX IF NOT EXISTS ix_receiver_beacons_{0}_timestamp_name_receiver_name ON "receiver_beacons_{0}" (timestamp, name, receiver_name);
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
def create_indices_bigdata(postfix):
"""Creates indices for aircraft- and receiver-beacons."""
- db.session.execute("""
+ db.session.execute(
+ """
CREATE INDEX IF NOT EXISTS ix_aircraft_beacons_{0}_timestamp_name_receiver_name ON "aircraft_beacons_{0}" (timestamp, name, receiver_name);
CREATE INDEX IF NOT EXISTS ix_receiver_beacons_{0}_timestamp_name_receiver_name ON "receiver_beacons_{0}" (timestamp, name, receiver_name);
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
def add_missing_devices(postfix):
"""Add missing devices."""
- db.session.execute("""
+ db.session.execute(
+ """
INSERT INTO devices(address)
SELECT DISTINCT (ab.address)
FROM "aircraft_beacons_{0}" AS ab
WHERE ab.address IS NOT NULL AND NOT EXISTS (SELECT 1 FROM devices AS d WHERE d.address = ab.address)
ORDER BY ab.address;
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
def add_missing_receivers(postfix):
"""Add missing receivers."""
- db.session.execute("""
+ db.session.execute(
+ """
INSERT INTO receivers(name)
SELECT DISTINCT (rb.name)
FROM "receiver_beacons_{0}" AS rb
@@ -58,14 +72,18 @@ def add_missing_receivers(postfix):
FROM "aircraft_beacons_{0}" AS ab
WHERE NOT EXISTS (SELECT 1 FROM receivers AS r WHERE r.name = ab.receiver_name)
ORDER BY ab.receiver_name;
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
def update_receiver_location(postfix):
"""Updates the receiver location. We need this because we want the actual location for distance calculations."""
- db.session.execute("""
+ db.session.execute(
+ """
UPDATE receivers AS r
SET
location = sq.location,
@@ -77,19 +95,26 @@ def update_receiver_location(postfix):
ORDER BY rb.receiver_id, rb.timestamp
) AS sq
WHERE r.id = sq.receiver_id;
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
def update_receiver_beacons(postfix):
"""Updates the foreign keys."""
- db.session.execute("""
+ db.session.execute(
+ """
UPDATE receiver_beacons_{0} AS rb
SET receiver_id = r.id
FROM receivers AS r
WHERE rb.receiver_id IS NULL AND rb.name = r.name;
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
@@ -97,7 +122,8 @@ def update_receiver_beacons_bigdata(postfix):
"""Updates the foreign keys.
Due to performance reasons we use a new table instead of updating the old."""
- db.session.execute("""
+ db.session.execute(
+ """
SELECT
rb.location, rb.altitude, rb.name, rb.receiver_name, rb.dstcall, rb.timestamp,
@@ -112,7 +138,10 @@ def update_receiver_beacons_bigdata(postfix):
DROP TABLE IF EXISTS "receiver_beacons_{0}";
ALTER TABLE "receiver_beacons_{0}_temp" RENAME TO "receiver_beacons_{0}";
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
@@ -120,7 +149,8 @@ def update_aircraft_beacons(postfix):
"""Updates the foreign keys and calculates distance/radial and quality and computes the altitude above ground level.
Elevation data has to be in the table 'elevation' with srid 4326."""
- db.session.execute("""
+ db.session.execute(
+ """
UPDATE aircraft_beacons_{0} AS ab
SET
device_id = d.id,
@@ -135,7 +165,10 @@ def update_aircraft_beacons(postfix):
FROM devices AS d, receivers AS r, elevation AS e
WHERE ab.device_id IS NULL and ab.receiver_id IS NULL AND ab.address = d.address AND ab.receiver_name = r.name AND ST_Intersects(e.rast, ab.location);
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
@@ -144,7 +177,8 @@ def update_aircraft_beacons_bigdata(postfix):
Elevation data has to be in the table 'elevation' with srid 4326.
Due to performance reasons we use a new table instead of updating the old."""
- db.session.execute("""
+ db.session.execute(
+ """
SELECT
ab.location, ab.altitude, ab.name, ab.dstcall, ab.relay, ab.receiver_name, ab.timestamp, ab.track, ab.ground_speed,
@@ -170,14 +204,18 @@ def update_aircraft_beacons_bigdata(postfix):
DROP TABLE IF EXISTS "aircraft_beacons_{0}";
ALTER TABLE "aircraft_beacons_{0}_temp" RENAME TO "aircraft_beacons_{0}";
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
def delete_receiver_beacons(postfix):
"""Delete beacons from table."""
- db.session.execute("""
+ db.session.execute(
+ """
DELETE FROM receiver_beacons_continuous_import AS rb
USING (
SELECT name, receiver_name, timestamp
@@ -185,14 +223,18 @@ def delete_receiver_beacons(postfix):
WHERE receiver_id IS NOT NULL
) AS sq
WHERE rb.name = sq.name AND rb.receiver_name = sq.receiver_name AND rb.timestamp = sq.timestamp
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
def delete_aircraft_beacons(postfix):
"""Delete beacons from table."""
- db.session.execute("""
+ db.session.execute(
+ """
DELETE FROM aircraft_beacons_continuous_import AS ab
USING (
SELECT name, receiver_name, timestamp
@@ -200,7 +242,10 @@ def delete_aircraft_beacons(postfix):
WHERE receiver_id IS NOT NULL and device_id IS NOT NULL
) AS sq
WHERE ab.name = sq.name AND ab.receiver_name = sq.receiver_name AND ab.timestamp = sq.timestamp
- """.format(postfix))
+ """.format(
+ postfix
+ )
+ )
db.session.commit()
@@ -247,7 +292,9 @@ def get_merged_aircraft_beacons_subquery(postfix):
FROM "aircraft_beacons_{0}" AS ab
GROUP BY timestamp, name, receiver_name
ORDER BY timestamp, name, receiver_name
- """.format(postfix)
+ """.format(
+ postfix
+ )
def get_merged_receiver_beacons_subquery(postfix):
@@ -285,7 +332,9 @@ def get_merged_receiver_beacons_subquery(postfix):
FROM "receiver_beacons_{0}" AS rb
GROUP BY timestamp, name, receiver_name
ORDER BY timestamp, name, receiver_name
- """.format(postfix)
+ """.format(
+ postfix
+ )
def transfer_aircraft_beacons(postfix):
@@ -298,7 +347,9 @@ def transfer_aircraft_beacons(postfix):
FROM ({}) sq
WHERE sq.receiver_id IS NOT NULL AND sq.device_id IS NOT NULL
ON CONFLICT DO NOTHING;
- """.format(get_merged_aircraft_beacons_subquery(postfix))
+ """.format(
+ get_merged_aircraft_beacons_subquery(postfix)
+ )
db.session.execute(query)
db.session.commit()
@@ -317,7 +368,9 @@ def transfer_receiver_beacons(postfix):
FROM ({}) sq
WHERE sq.receiver_id IS NOT NULL
ON CONFLICT DO NOTHING;
- """.format(get_merged_receiver_beacons_subquery(postfix))
+ """.format(
+ get_merged_receiver_beacons_subquery(postfix)
+ )
db.session.execute(query)
db.session.commit()
diff --git a/app/live_routes.py b/app/live_routes.py
new file mode 100644
index 0000000..fad9d7a
--- /dev/null
+++ b/app/live_routes.py
@@ -0,0 +1,90 @@
+from flask import request, render_template, make_response, send_file
+from flask_cors import cross_origin
+
+from app.backend.liveglidernet import rec, lxml
+
+from app import app
+from app import db
+from app import cache
+
+
+@app.route("/live.html")
+@cross_origin()
+def live():
+ return render_template("ogn_live.html", host=request.host)
+
+
+@app.route("/rec.php")
+def rec_php():
+ a = request.args.get("a")
+ z = request.args.get("z")
+
+ xml = rec()
+ resp = app.make_response(xml)
+ resp.mimetype = "text/xml"
+ return resp
+
+
+@app.route("/lxml.php")
+def lxml_php():
+ a = request.args.get("a")
+ b = request.args.get("b")
+ c = request.args.get("c")
+ d = request.args.get("d")
+ e = request.args.get("e")
+ z = request.args.get("z")
+
+ xml = lxml()
+ resp = app.make_response(xml)
+ resp.mimetype = "text/xml"
+ return resp
+
+
+@app.route("/pict/")
+def pict(filename):
+ return app.send_static_file("ognlive/pict/" + filename)
+
+
+@app.route("/favicon.gif")
+def favicon_gif():
+ return app.send_static_file("ognlive/pict/favicon.gif")
+
+
+@app.route("/horizZoomControl.js")
+def horizZoomControl_js():
+ return app.send_static_file("ognlive/horizZoomControl.js")
+
+
+@app.route("/barogram.js")
+def barogram_js():
+ return app.send_static_file("ognlive/barogram.js")
+
+
+@app.route("/util.js")
+def util_js():
+ return app.send_static_file("ognlive/util.js")
+
+
+@app.route("/ogn.js")
+def ogn_js():
+ return app.send_static_file("ognlive/ogn.js")
+
+
+@app.route("/ol.js")
+def ol_js():
+ return app.send_static_file("ognlive/ol.js")
+
+
+@app.route("/osm.js")
+def osm_js():
+ return app.send_static_file("ognlive/osm.js")
+
+
+@app.route("/ol.css")
+def ol_css():
+ return app.send_static_file("ognlive/ol.css")
+
+
+@app.route("/osm.css")
+def osm_css():
+ return app.send_static_file("ognlive/osm.css")
diff --git a/ogn_python/model/__init__.py b/app/model/__init__.py
similarity index 100%
rename from ogn_python/model/__init__.py
rename to app/model/__init__.py
diff --git a/ogn_python/model/aircraft_beacon.py b/app/model/aircraft_beacon.py
similarity index 58%
rename from ogn_python/model/aircraft_beacon.py
rename to app/model/aircraft_beacon.py
index acf5858..313100f 100644
--- a/ogn_python/model/aircraft_beacon.py
+++ b/app/model/aircraft_beacon.py
@@ -1,7 +1,7 @@
from sqlalchemy.sql import func
from .beacon import Beacon
-from ogn_python import db
+from app import db
class AircraftBeacon(Beacon):
@@ -28,21 +28,21 @@ class AircraftBeacon(Beacon):
# Calculated values
distance = db.Column(db.Float(precision=2))
radial = db.Column(db.SmallInteger)
- quality = db.Column(db.Float(precision=2)) # signal quality normalized to 10km
- location_mgrs = db.Column(db.String(15)) # full mgrs (15 chars)
- location_mgrs_short = db.Column(db.String(9)) # reduced mgrs (9 chars), e.g. used for melissas range tool
+ quality = db.Column(db.Float(precision=2)) # signal quality normalized to 10km
+ location_mgrs = db.Column(db.String(15)) # full mgrs (15 chars)
+ location_mgrs_short = db.Column(db.String(9)) # reduced mgrs (9 chars), e.g. used for melissas range tool
agl = db.Column(db.Float(precision=2))
# Relations
- receiver_id = db.Column(db.Integer, db.ForeignKey('receivers.id', ondelete='SET NULL'))
- receiver = db.relationship('Receiver', foreign_keys=[receiver_id], backref='aircraft_beacons')
+ receiver_id = db.Column(db.Integer, db.ForeignKey("receivers.id", ondelete="SET NULL"))
+ receiver = db.relationship("Receiver", foreign_keys=[receiver_id], backref="aircraft_beacons")
- device_id = db.Column(db.Integer, db.ForeignKey('devices.id', ondelete='SET NULL'))
- device = db.relationship('Device', foreign_keys=[device_id], backref='aircraft_beacons')
+ device_id = db.Column(db.Integer, db.ForeignKey("devices.id", ondelete="SET NULL"))
+ device = db.relationship("Device", foreign_keys=[device_id], backref="aircraft_beacons")
# Multi-column indices
- db.Index('ix_aircraft_beacons_receiver_id_distance', 'receiver_id', 'distance')
- db.Index('ix_aircraft_beacons_device_id_timestamp', 'device_id', 'timestamp')
+ db.Index("ix_aircraft_beacons_receiver_id_distance", "receiver_id", "distance")
+ db.Index("ix_aircraft_beacons_device_id_timestamp", "device_id", "timestamp")
def __repr__(self):
return "" % (
@@ -61,49 +61,48 @@ class AircraftBeacon(Beacon):
self.hardware_version,
self.real_address,
self.signal_power,
-
self.distance,
self.radial,
self.quality,
self.location_mgrs,
- self.location_mgrs_short)
+ self.location_mgrs_short,
+ )
@classmethod
def get_columns(self):
- return ['location',
- 'altitude',
- 'name',
- 'dstcall',
- 'relay',
- 'receiver_name',
- 'timestamp',
- 'track',
- 'ground_speed',
-
- #'raw_message',
- #'reference_timestamp',
-
- 'address_type',
- 'aircraft_type',
- 'stealth',
- 'address',
- 'climb_rate',
- 'turn_rate',
- 'signal_quality',
- 'error_count',
- 'frequency_offset',
- 'gps_quality_horizontal',
- 'gps_quality_vertical',
- 'software_version',
- 'hardware_version',
- 'real_address',
- 'signal_power',
-
- 'distance',
- 'radial',
- 'quality',
- 'location_mgrs',
- 'location_mgrs_short']
+ return [
+ "location",
+ "altitude",
+ "name",
+ "dstcall",
+ "relay",
+ "receiver_name",
+ "timestamp",
+ "track",
+ "ground_speed",
+ #'raw_message',
+ #'reference_timestamp',
+ "address_type",
+ "aircraft_type",
+ "stealth",
+ "address",
+ "climb_rate",
+ "turn_rate",
+ "signal_quality",
+ "error_count",
+ "frequency_offset",
+ "gps_quality_horizontal",
+ "gps_quality_vertical",
+ "software_version",
+ "hardware_version",
+ "real_address",
+ "signal_power",
+ "distance",
+ "radial",
+ "quality",
+ "location_mgrs",
+ "location_mgrs_short",
+ ]
def get_values(self):
return [
@@ -116,10 +115,8 @@ class AircraftBeacon(Beacon):
self.timestamp,
self.track,
self.ground_speed,
-
- #self.raw_message,
- #self.reference_timestamp,
-
+ # self.raw_message,
+ # self.reference_timestamp,
self.address_type,
self.aircraft_type,
self.stealth,
@@ -135,13 +132,13 @@ class AircraftBeacon(Beacon):
self.hardware_version,
self.real_address,
self.signal_power,
-
self.distance,
self.radial,
self.quality,
self.location_mgrs,
- self.location_mgrs_short]
+ self.location_mgrs_short,
+ ]
-db.Index('ix_aircraft_beacons_date_device_id_address', func.date(AircraftBeacon.timestamp), AircraftBeacon.device_id, AircraftBeacon.address)
-db.Index('ix_aircraft_beacons_date_receiver_id_distance', func.date(AircraftBeacon.timestamp), AircraftBeacon.receiver_id, AircraftBeacon.distance)
+db.Index("ix_aircraft_beacons_date_device_id_address", func.date(AircraftBeacon.timestamp), AircraftBeacon.device_id, AircraftBeacon.address)
+db.Index("ix_aircraft_beacons_date_receiver_id_distance", func.date(AircraftBeacon.timestamp), AircraftBeacon.receiver_id, AircraftBeacon.distance)
diff --git a/ogn_python/model/aircraft_type.py b/app/model/aircraft_type.py
similarity index 100%
rename from ogn_python/model/aircraft_type.py
rename to app/model/aircraft_type.py
diff --git a/ogn_python/model/airport.py b/app/model/airport.py
similarity index 83%
rename from ogn_python/model/airport.py
rename to app/model/airport.py
index 866fd61..31453df 100644
--- a/ogn_python/model/airport.py
+++ b/app/model/airport.py
@@ -1,6 +1,6 @@
from geoalchemy2.types import Geometry
-from ogn_python import db
+from app import db
class Airport(db.Model):
@@ -8,7 +8,7 @@ class Airport(db.Model):
id = db.Column(db.Integer, primary_key=True)
- location_wkt = db.Column('location', Geometry('POINT', srid=4326))
+ location_wkt = db.Column("location", Geometry("POINT", srid=4326))
altitude = db.Column(db.Float(precision=2))
name = db.Column(db.String, index=True)
@@ -20,7 +20,7 @@ class Airport(db.Model):
runway_length = db.Column(db.SmallInteger)
frequency = db.Column(db.Float(precision=2))
- border = db.Column('border', Geometry('POLYGON', srid=4326))
+ border = db.Column("border", Geometry("POLYGON", srid=4326))
def __repr__(self):
return "" % (
@@ -34,4 +34,5 @@ class Airport(db.Model):
self.altitude,
self.runway_direction,
self.runway_length,
- self.frequency)
+ self.frequency,
+ )
diff --git a/ogn_python/model/beacon.py b/app/model/beacon.py
similarity index 93%
rename from ogn_python/model/beacon.py
rename to app/model/beacon.py
index a394f8c..c23ebeb 100644
--- a/ogn_python/model/beacon.py
+++ b/app/model/beacon.py
@@ -5,12 +5,12 @@ from sqlalchemy.ext.hybrid import hybrid_property
from .geo import Location
-from ogn_python import db
+from app import db
class Beacon(AbstractConcreteBase, db.Model):
# APRS data
- location_wkt = db.Column('location', Geometry('POINT', srid=4326))
+ location_wkt = db.Column("location", Geometry("POINT", srid=4326))
altitude = db.Column(db.Float(precision=2))
name = db.Column(db.String, primary_key=True, nullable=True)
diff --git a/ogn_python/model/country.py b/app/model/country.py
similarity index 64%
rename from ogn_python/model/country.py
rename to app/model/country.py
index 2b16304..1fce5c4 100644
--- a/ogn_python/model/country.py
+++ b/app/model/country.py
@@ -1,6 +1,6 @@
from geoalchemy2.types import Geometry
-from ogn_python import db
+from app import db
class Country(db.Model):
@@ -21,18 +21,7 @@ class Country(db.Model):
lon = db.Column(db.Float)
lat = db.Column(db.Float)
- geom = db.Column('geom', Geometry('MULTIPOLYGON', srid=4326))
+ geom = db.Column("geom", Geometry("MULTIPOLYGON", srid=4326))
def __repr__(self):
- return "" % (
- self.fips,
- self.iso2,
- self.iso3,
- self.un,
- self.name,
- self.area,
- self.pop2005,
- self.region,
- self.subregion,
- self.lon,
- self.lat)
+ return "" % (self.fips, self.iso2, self.iso3, self.un, self.name, self.area, self.pop2005, self.region, self.subregion, self.lon, self.lat)
diff --git a/ogn_python/model/country_stats.py b/app/model/country_stats.py
similarity index 51%
rename from ogn_python/model/country_stats.py
rename to app/model/country_stats.py
index c9e3ece..9f5c9bc 100644
--- a/ogn_python/model/country_stats.py
+++ b/app/model/country_stats.py
@@ -1,4 +1,4 @@
-from ogn_python import db
+from app import db
class CountryStats(db.Model):
@@ -13,6 +13,5 @@ class CountryStats(db.Model):
device_count = db.Column(db.Integer)
# Relations
- country_id = db.Column(db.Integer, db.ForeignKey('countries.gid', ondelete='SET NULL'), index=True)
- country = db.relationship('Country', foreign_keys=[country_id], backref=db.backref('stats', order_by='CountryStats.date.asc()'))
-
+ country_id = db.Column(db.Integer, db.ForeignKey("countries.gid", ondelete="SET NULL"), index=True)
+ country = db.relationship("Country", foreign_keys=[country_id], backref=db.backref("stats", order_by="CountryStats.date.asc()"))
diff --git a/ogn_python/model/device.py b/app/model/device.py
similarity index 69%
rename from ogn_python/model/device.py
rename to app/model/device.py
index e1317c8..7bcd098 100644
--- a/ogn_python/model/device.py
+++ b/app/model/device.py
@@ -2,17 +2,17 @@ import datetime
from sqlalchemy.ext.hybrid import hybrid_property
-from ogn_python import db
+from app import db
from .device_info import DeviceInfo
class Device(db.Model):
- __tablename__ = 'devices'
+ __tablename__ = "devices"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, index=True)
- #address = db.Column(db.String(6), index=True)
+ # address = db.Column(db.String(6), index=True)
address = db.Column(db.String, index=True)
firstseen = db.Column(db.DateTime, index=True)
lastseen = db.Column(db.DateTime, index=True)
@@ -23,26 +23,16 @@ class Device(db.Model):
real_address = db.Column(db.String(6))
def __repr__(self):
- return "" % (
- self.address,
- self.aircraft_type,
- self.stealth,
- self.software_version,
- self.hardware_version,
- self.real_address)
+ return "" % (self.address, self.aircraft_type, self.stealth, self.software_version, self.hardware_version, self.real_address)
@hybrid_property
def info(self):
- query = db.session.query(DeviceInfo) \
- .filter(DeviceInfo.address == self.address) \
- .order_by(DeviceInfo.address_origin)
+ query = db.session.query(DeviceInfo).filter(DeviceInfo.address == self.address).order_by(DeviceInfo.address_origin)
return query.first()
def get_infos(self):
- query = db.session.query(DeviceInfo) \
- .filter(DeviceInfo.address == self.address) \
- .order_by(DeviceInfo.address_origin)
+ query = db.session.query(DeviceInfo).filter(DeviceInfo.address == self.address).order_by(DeviceInfo.address_origin)
return [info for info in query.all()]
@@ -62,7 +52,7 @@ class Device(db.Model):
}
def expiry_date(self):
- if self.name.startswith('FLR'):
+ if self.name.startswith("FLR"):
if self.software_version in self.EXPIRY_DATES:
return self.EXPIRY_DATES[self.software_version]
else:
diff --git a/ogn_python/model/device_info.py b/app/model/device_info.py
similarity index 83%
rename from ogn_python/model/device_info.py
rename to app/model/device_info.py
index 88ed4d9..e0d134a 100644
--- a/ogn_python/model/device_info.py
+++ b/app/model/device_info.py
@@ -1,12 +1,12 @@
-from ogn_python import db
+from app import db
class DeviceInfo(db.Model):
- __tablename__ = 'device_infos'
+ __tablename__ = "device_infos"
id = db.Column(db.Integer, primary_key=True)
address_type = None
- #address = db.Column(db.String(6), index=True)
+ # address = db.Column(db.String(6), index=True)
address = db.Column(db.String, index=True)
aircraft = db.Column(db.String)
registration = db.Column(db.String(7))
@@ -27,4 +27,5 @@ class DeviceInfo(db.Model):
self.tracked,
self.identified,
self.aircraft_type,
- self.address_origin)
+ self.address_origin,
+ )
diff --git a/ogn_python/model/device_info_origin.py b/app/model/device_info_origin.py
similarity index 64%
rename from ogn_python/model/device_info_origin.py
rename to app/model/device_info_origin.py
index 9a3a565..db31572 100644
--- a/ogn_python/model/device_info_origin.py
+++ b/app/model/device_info_origin.py
@@ -8,15 +8,15 @@ class DeviceInfoOrigin:
if origin in [0, 1, 2, 3]:
self.origin = origin
else:
- raise ValueError('no address origin with id {} known'.format(origin))
+ raise ValueError("no address origin with id {} known".format(origin))
def name(self):
if self.origin == self.unknown:
- return 'unknown'
+ return "unknown"
elif self.origin == self.ogn_ddb:
- return 'OGN-DDB'
+ return "OGN-DDB"
elif self.origin == self.flarmnet:
- return 'FlarmNet'
+ return "FlarmNet"
elif self.origin == self.user_defined:
- return 'user-defined'
- return ''
+ return "user-defined"
+ return ""
diff --git a/ogn_python/model/device_stats.py b/app/model/device_stats.py
similarity index 74%
rename from ogn_python/model/device_stats.py
rename to app/model/device_stats.py
index 8865a7d..5c3ac00 100644
--- a/ogn_python/model/device_stats.py
+++ b/app/model/device_stats.py
@@ -1,4 +1,4 @@
-from ogn_python import db
+from app import db
class DeviceStats(db.Model):
@@ -40,15 +40,11 @@ class DeviceStats(db.Model):
quality_ranking_country = db.Column(db.Integer)
# Relations
- device_id = db.Column(db.Integer, db.ForeignKey('devices.id', ondelete='SET NULL'), index=True)
- device = db.relationship('Device', foreign_keys=[device_id], backref=db.backref('stats', order_by='DeviceStats.date.asc()'))
+ device_id = db.Column(db.Integer, db.ForeignKey("devices.id", ondelete="SET NULL"), index=True)
+ device = db.relationship("Device", foreign_keys=[device_id], backref=db.backref("stats", order_by="DeviceStats.date.asc()"))
def __repr__(self):
- return "" % (
- self.date,
- self.receiver_count,
- self.aircraft_beacon_count,
- self.max_altitude)
+ return "" % (self.date, self.receiver_count, self.aircraft_beacon_count, self.max_altitude)
-db.Index('ix_device_stats_date_device_id', DeviceStats.date, DeviceStats.device_id)
+db.Index("ix_device_stats_date_device_id", DeviceStats.date, DeviceStats.device_id)
diff --git a/app/model/flights2d.py b/app/model/flights2d.py
new file mode 100644
index 0000000..8f47f06
--- /dev/null
+++ b/app/model/flights2d.py
@@ -0,0 +1,24 @@
+from geoalchemy2.types import Geometry
+
+from app import db
+
+
+class Flight2D(db.Model):
+ __tablename__ = "flights2d"
+
+ date = db.Column(db.Date, primary_key=True)
+ flight_type = db.Column(db.SmallInteger, primary_key=True)
+
+ path_wkt = db.Column("path", Geometry("MULTILINESTRING", srid=4326))
+ path_simple_wkt = db.Column("path_simple", Geometry("MULTILINESTRING", srid=4326)) # this is the path simplified with ST_Simplify(path, 0.0001)
+
+ # Relations
+ device_id = db.Column(db.Integer, db.ForeignKey("devices.id", ondelete="SET NULL"), primary_key=True)
+ device = db.relationship("Device", foreign_keys=[device_id], backref="flights2d")
+
+ def __repr__(self):
+ return "" % (self.date, self.path_wkt, self.path_simple_wkt)
+
+
+db.Index("ix_flights2d_date_device_id", Flight2D.date, Flight2D.device_id)
+# db.Index('ix_flights2d_date_path', Flight2D.date, Flight2D.path_wkt) --> CREATE INDEX ix_flights2d_date_path ON flights2d USING GIST("date", path)
diff --git a/ogn_python/model/geo.py b/app/model/geo.py
similarity index 59%
rename from ogn_python/model/geo.py
rename to app/model/geo.py
index 69794d4..15fb615 100644
--- a/ogn_python/model/geo.py
+++ b/app/model/geo.py
@@ -6,10 +6,10 @@ class Location:
self.latitude = lat
def to_wkt(self):
- return 'SRID=4326;POINT({0} {1})'.format(self.longitude, self.latitude)
+ return "SRID=4326;POINT({0} {1})".format(self.longitude, self.latitude)
def __str__(self):
- return '{0: 7.4f}, {1:8.4f}'.format(self.latitude, self.longitude)
+ return "{0: 7.4f}, {1:8.4f}".format(self.latitude, self.longitude)
def as_dict(self):
- return {'latitude': round(self.latitude, 8), 'longitude': round(self.longitude, 8)}
+ return {"latitude": round(self.latitude, 8), "longitude": round(self.longitude, 8)}
diff --git a/ogn_python/model/logbook.py b/app/model/logbook.py
similarity index 58%
rename from ogn_python/model/logbook.py
rename to app/model/logbook.py
index 40eef16..ce1376d 100644
--- a/ogn_python/model/logbook.py
+++ b/app/model/logbook.py
@@ -1,10 +1,10 @@
from sqlalchemy.ext.hybrid import hybrid_property
-from ogn_python import db
+from app import db
class Logbook(db.Model):
- __tablename__ = 'logbook'
+ __tablename__ = "logbook"
id = db.Column(db.Integer, primary_key=True)
@@ -16,14 +16,14 @@ class Logbook(db.Model):
max_altitude = db.Column(db.Float(precision=2))
# Relations
- takeoff_airport_id = db.Column(db.Integer, db.ForeignKey('airports.id', ondelete='CASCADE'), index=True)
- takeoff_airport = db.relationship('Airport', foreign_keys=[takeoff_airport_id])
+ takeoff_airport_id = db.Column(db.Integer, db.ForeignKey("airports.id", ondelete="CASCADE"), index=True)
+ takeoff_airport = db.relationship("Airport", foreign_keys=[takeoff_airport_id])
- landing_airport_id = db.Column(db.Integer, db.ForeignKey('airports.id', ondelete='CASCADE'), index=True)
- landing_airport = db.relationship('Airport', foreign_keys=[landing_airport_id])
+ landing_airport_id = db.Column(db.Integer, db.ForeignKey("airports.id", ondelete="CASCADE"), index=True)
+ landing_airport = db.relationship("Airport", foreign_keys=[landing_airport_id])
- device_id = db.Column(db.Integer, db.ForeignKey('devices.id', ondelete='CASCADE'), index=True)
- device = db.relationship('Device', foreign_keys=[device_id], backref=db.backref('logbook', order_by='Logbook.reftime'))
+ device_id = db.Column(db.Integer, db.ForeignKey("devices.id", ondelete="CASCADE"), index=True)
+ device = db.relationship("Device", foreign_keys=[device_id], backref=db.backref("logbook", order_by="Logbook.reftime"))
@hybrid_property
def duration(self):
diff --git a/ogn_python/model/receiver.py b/app/model/receiver.py
similarity index 68%
rename from ogn_python/model/receiver.py
rename to app/model/receiver.py
index 97d5e71..cfae2a4 100644
--- a/ogn_python/model/receiver.py
+++ b/app/model/receiver.py
@@ -3,7 +3,7 @@ from geoalchemy2.types import Geometry
from .geo import Location
-from ogn_python import db
+from app import db
class Receiver(db.Model):
@@ -11,7 +11,7 @@ class Receiver(db.Model):
id = db.Column(db.Integer, primary_key=True)
- location_wkt = db.Column('location', Geometry('POINT', srid=4326))
+ location_wkt = db.Column("location", Geometry("POINT", srid=4326))
altitude = db.Column(db.Float(precision=2))
name = db.Column(db.String(9), index=True)
@@ -21,8 +21,8 @@ class Receiver(db.Model):
platform = db.Column(db.String)
# Relations
- country_id = db.Column(db.Integer, db.ForeignKey('countries.gid', ondelete='SET NULL'), index=True)
- country = db.relationship('Country', foreign_keys=[country_id], backref=db.backref('receivers', order_by='Receiver.name.asc()'))
+ country_id = db.Column(db.Integer, db.ForeignKey("countries.gid", ondelete="SET NULL"), index=True)
+ country = db.relationship("Country", foreign_keys=[country_id], backref=db.backref("receivers", order_by="Receiver.name.asc()"))
@property
def location(self):
diff --git a/ogn_python/model/receiver_beacon.py b/app/model/receiver_beacon.py
similarity index 71%
rename from ogn_python/model/receiver_beacon.py
rename to app/model/receiver_beacon.py
index e50fdb5..45f1901 100644
--- a/ogn_python/model/receiver_beacon.py
+++ b/app/model/receiver_beacon.py
@@ -1,7 +1,7 @@
from sqlalchemy.sql import func
from .beacon import Beacon
-from ogn_python import db
+from app import db
class ReceiverBeacon(Beacon):
@@ -35,11 +35,11 @@ class ReceiverBeacon(Beacon):
user_comment = None
# Relations
- receiver_id = db.Column(db.Integer, db.ForeignKey('receivers.id', ondelete='SET NULL'))
- receiver = db.relationship('Receiver', foreign_keys=[receiver_id], backref='receiver_beacons')
+ receiver_id = db.Column(db.Integer, db.ForeignKey("receivers.id", ondelete="SET NULL"))
+ receiver = db.relationship("Receiver", foreign_keys=[receiver_id], backref="receiver_beacons")
# Multi-column indices
- db.Index('ix_receiver_beacons_receiver_id_name', 'receiver_id', 'name')
+ db.Index("ix_receiver_beacons_receiver_id_name", "receiver_id", "name")
def __repr__(self):
return "" % (
@@ -60,38 +60,39 @@ class ReceiverBeacon(Beacon):
self.senders_messages,
self.good_senders_signal,
self.good_senders,
- self.good_and_bad_senders)
+ self.good_and_bad_senders,
+ )
@classmethod
def get_columns(self):
- return ['location',
- 'altitude',
- 'name',
- 'dstcall',
- 'receiver_name',
- 'timestamp',
-
- # 'raw_message',
- # 'reference_timestamp',
-
- 'version',
- 'platform',
- 'cpu_load',
- 'free_ram',
- 'total_ram',
- 'ntp_error',
- 'rt_crystal_correction',
- 'voltage',
- 'amperage',
- 'cpu_temp',
- 'senders_visible',
- 'senders_total',
- 'rec_input_noise',
- 'senders_signal',
- 'senders_messages',
- 'good_senders_signal',
- 'good_senders',
- 'good_and_bad_senders']
+ return [
+ "location",
+ "altitude",
+ "name",
+ "dstcall",
+ "receiver_name",
+ "timestamp",
+ # 'raw_message',
+ # 'reference_timestamp',
+ "version",
+ "platform",
+ "cpu_load",
+ "free_ram",
+ "total_ram",
+ "ntp_error",
+ "rt_crystal_correction",
+ "voltage",
+ "amperage",
+ "cpu_temp",
+ "senders_visible",
+ "senders_total",
+ "rec_input_noise",
+ "senders_signal",
+ "senders_messages",
+ "good_senders_signal",
+ "good_senders",
+ "good_and_bad_senders",
+ ]
def get_values(self):
return [
@@ -101,10 +102,8 @@ class ReceiverBeacon(Beacon):
self.dstcall,
self.receiver_name,
self.timestamp,
-
# self.raw_message,
# self.reference_timestamp,
-
self.version,
self.platform,
self.cpu_load,
@@ -122,7 +121,8 @@ class ReceiverBeacon(Beacon):
int(self.senders_messages) if self.senders_messages else None,
self.good_senders_signal,
int(self.good_senders) if self.good_senders else None,
- int(self.good_and_bad_senders) if self.good_and_bad_senders else None]
+ int(self.good_and_bad_senders) if self.good_and_bad_senders else None,
+ ]
-db.Index('ix_receiver_beacons_date_receiver_id', func.date(ReceiverBeacon.timestamp), ReceiverBeacon.receiver_id)
+db.Index("ix_receiver_beacons_date_receiver_id", func.date(ReceiverBeacon.timestamp), ReceiverBeacon.receiver_id)
diff --git a/ogn_python/model/receiver_coverage.py b/app/model/receiver_coverage.py
similarity index 56%
rename from ogn_python/model/receiver_coverage.py
rename to app/model/receiver_coverage.py
index 5883039..c0b543a 100644
--- a/ogn_python/model/receiver_coverage.py
+++ b/app/model/receiver_coverage.py
@@ -1,4 +1,4 @@
-from ogn_python import db
+from app import db
class ReceiverCoverage(db.Model):
@@ -15,9 +15,9 @@ class ReceiverCoverage(db.Model):
device_count = db.Column(db.SmallInteger)
# Relations
- receiver_id = db.Column(db.Integer, db.ForeignKey('receivers.id', ondelete='SET NULL'), primary_key=True)
- receiver = db.relationship('Receiver', foreign_keys=[receiver_id], backref=db.backref('receiver_coverages', order_by='ReceiverCoverage.date.asc()'))
+ receiver_id = db.Column(db.Integer, db.ForeignKey("receivers.id", ondelete="SET NULL"), primary_key=True)
+ receiver = db.relationship("Receiver", foreign_keys=[receiver_id], backref=db.backref("receiver_coverages", order_by="ReceiverCoverage.date.asc()"))
-db.Index('ix_receiver_coverages_date_receiver_id', ReceiverCoverage.date, ReceiverCoverage.receiver_id)
-db.Index('ix_receiver_coverages_receiver_id_date', ReceiverCoverage.receiver_id, ReceiverCoverage.date)
+db.Index("ix_receiver_coverages_date_receiver_id", ReceiverCoverage.date, ReceiverCoverage.receiver_id)
+db.Index("ix_receiver_coverages_receiver_id_date", ReceiverCoverage.receiver_id, ReceiverCoverage.date)
diff --git a/ogn_python/model/receiver_stats.py b/app/model/receiver_stats.py
similarity index 71%
rename from ogn_python/model/receiver_stats.py
rename to app/model/receiver_stats.py
index b66a795..6001075 100644
--- a/ogn_python/model/receiver_stats.py
+++ b/app/model/receiver_stats.py
@@ -1,6 +1,6 @@
from geoalchemy2.types import Geometry
-from ogn_python import db
+from app import db
class ReceiverStats(db.Model):
@@ -13,7 +13,7 @@ class ReceiverStats(db.Model):
# Static data
firstseen = db.Column(db.DateTime, index=True)
lastseen = db.Column(db.DateTime, index=True)
- location_wkt = db.Column('location', Geometry('POINT', srid=4326))
+ location_wkt = db.Column("location", Geometry("POINT", srid=4326))
altitude = db.Column(db.Float(precision=2))
version = db.Column(db.String)
platform = db.Column(db.String)
@@ -34,8 +34,8 @@ class ReceiverStats(db.Model):
quality_ranking = db.Column(db.Integer)
# Relations
- receiver_id = db.Column(db.Integer, db.ForeignKey('receivers.id', ondelete='SET NULL'), index=True)
- receiver = db.relationship('Receiver', foreign_keys=[receiver_id], backref=db.backref('stats', order_by='ReceiverStats.date.asc()'))
+ receiver_id = db.Column(db.Integer, db.ForeignKey("receivers.id", ondelete="SET NULL"), index=True)
+ receiver = db.relationship("Receiver", foreign_keys=[receiver_id], backref=db.backref("stats", order_by="ReceiverStats.date.asc()"))
-db.Index('ix_receiver_stats_date_receiver_id', ReceiverStats.date, ReceiverStats.receiver_id)
+db.Index("ix_receiver_stats_date_receiver_id", ReceiverStats.date, ReceiverStats.receiver_id)
diff --git a/app/model/relation_stats.py b/app/model/relation_stats.py
new file mode 100644
index 0000000..2bddebb
--- /dev/null
+++ b/app/model/relation_stats.py
@@ -0,0 +1,26 @@
+from app import db
+
+
+class RelationStats(db.Model):
+ __tablename__ = "relation_stats"
+
+ id = db.Column(db.Integer, primary_key=True)
+
+ date = db.Column(db.Date)
+
+ # Statistic data
+ quality = db.Column(db.Float(precision=2))
+ beacon_count = db.Column(db.Integer)
+
+ # Relations
+ device_id = db.Column(db.Integer, db.ForeignKey("devices.id", ondelete="SET NULL"), index=True)
+ device = db.relationship("Device", foreign_keys=[device_id], backref="relation_stats")
+ receiver_id = db.Column(db.Integer, db.ForeignKey("receivers.id", ondelete="SET NULL"), index=True)
+ receiver = db.relationship("Receiver", foreign_keys=[receiver_id], backref="relation_stats")
+
+ def __repr__(self):
+ return "" % (self.date, self.quality, self.beacon_count)
+
+
+db.Index("ix_relation_stats_date_device_id", RelationStats.date, RelationStats.device_id, RelationStats.receiver_id)
+db.Index("ix_relation_stats_date_receiver_id", RelationStats.date, RelationStats.receiver_id, RelationStats.device_id)
diff --git a/app/model/takeoff_landing.py b/app/model/takeoff_landing.py
new file mode 100644
index 0000000..3bfa966
--- /dev/null
+++ b/app/model/takeoff_landing.py
@@ -0,0 +1,16 @@
+from app import db
+
+
+class TakeoffLanding(db.Model):
+ __tablename__ = "takeoff_landings"
+
+ device_id = db.Column(db.Integer, db.ForeignKey("devices.id", ondelete="SET NULL"), primary_key=True)
+ airport_id = db.Column(db.Integer, db.ForeignKey("airports.id", ondelete="SET NULL"), primary_key=True)
+ timestamp = db.Column(db.DateTime, primary_key=True)
+
+ is_takeoff = db.Column(db.Boolean)
+ track = db.Column(db.SmallInteger)
+
+ # Relations
+ airport = db.relationship("Airport", foreign_keys=[airport_id], backref="takeoff_landings")
+ device = db.relationship("Device", foreign_keys=[device_id], backref="takeoff_landings", order_by="TakeoffLanding.timestamp")
diff --git a/app/routes.py b/app/routes.py
new file mode 100644
index 0000000..2256ec4
--- /dev/null
+++ b/app/routes.py
@@ -0,0 +1,197 @@
+import datetime
+
+from flask import request, render_template, send_file
+
+from app import app
+from app import db
+from app import cache
+
+from app.model import *
+
+
+@cache.cached(key_prefix="countries_in_receivers")
+def get_countries_in_receivers():
+ query = db.session.query(Country.iso2).filter(Country.gid == Receiver.country_id).order_by(Country.iso2).distinct(Country.iso2)
+
+ return [{"iso2": country[0]} for country in query.all()]
+
+
+@cache.cached(key_prefix="countries_in_logbook")
+def get_countries_in_logbook():
+ query = db.session.query(Country.iso2).filter(Country.iso2 == Airport.country_code).filter(Logbook.takeoff_airport_id == Airport.id).order_by(Country.iso2).distinct(Country.iso2)
+
+ return [{"iso2": country[0]} for country in query.all()]
+
+
+@cache.memoize()
+def get_airports_in_country(sel_country):
+ query = db.session.query(Airport.id, Airport.name).filter(Airport.country_code == sel_country).filter(Logbook.takeoff_airport_id == Airport.id).order_by(Airport.name).distinct(Airport.name)
+
+ return [{"id": airport[0], "name": airport[1]} for airport in query.all()]
+
+
+@cache.memoize()
+def get_dates_for_airport(sel_airport):
+ query = (
+ db.session.query(db.func.date(Logbook.reftime), db.func.count(Logbook.id).label("logbook_count"))
+ .filter(Airport.id == sel_airport)
+ .filter(db.or_(Airport.id == Logbook.takeoff_airport_id, Airport.id == Logbook.landing_airport_id))
+ .group_by(db.func.date(Logbook.reftime))
+ .order_by(db.func.date(Logbook.reftime).desc())
+ )
+
+ return [{"date": date, "logbook_count": logbook_count} for (date, logbook_count) in query.all()]
+
+
+@app.route("/")
+@app.route("/index.html")
+def index():
+ return render_template("base.html")
+
+
+@app.route("/devices.html", methods=["GET", "POST"])
+def devices():
+ devices = db.session.query(Device).order_by(Device.address).limit(100)
+ return render_template("devices.html", devices=devices)
+
+
+@app.route("/device_detail.html", methods=["GET", "POST"])
+def device_detail():
+ device_id = request.args.get("id")
+ device = db.session.query(Device).filter(Device.id == device_id).one()
+
+ return render_template("device_detail.html", title="Device", device=device)
+
+
+@app.route("/receivers.html")
+def receivers():
+ sel_country = request.args.get("country")
+
+ countries = get_countries_in_receivers()
+
+ # Get receiver selection list
+ if sel_country:
+ receivers = db.session.query(Receiver).filter(db.and_(Receiver.country_id == Country.gid, Country.iso2 == sel_country)).order_by(Receiver.name)
+ else:
+ receivers = db.session.query(Receiver).order_by(Receiver.name)
+
+ return render_template("receivers.html", title="Receivers", sel_country=sel_country, countries=countries, receivers=receivers)
+
+
+@app.route("/receiver_detail.html")
+def receiver_detail():
+ sel_receiver_id = request.args.get("receiver_id")
+
+ receiver = db.session.query(Receiver).filter(Receiver.id == sel_receiver_id).one()
+
+ airport = (
+ db.session.query(Airport)
+ .filter(
+ db.and_(
+ Receiver.id == sel_receiver_id,
+ db.func.st_contains(db.func.st_buffer(Receiver.location_wkt, 0.5), Airport.location_wkt),
+ db.func.st_distance_sphere(Airport.location_wkt, Receiver.location_wkt) < 1000,
+ )
+ )
+ .filter(Airport.style.in_((2, 4, 5)))
+ )
+ return render_template("receiver_detail.html", title="Receiver Detail", receiver=receiver, airport=airport.first())
+
+
+@app.route("/airports.html", methods=["GET", "POST"])
+def airports():
+ sel_country = request.args.get("country")
+
+ countries = get_countries_in_logbook()
+
+ if sel_country:
+ airports = get_airports_in_country(sel_country)
+ else:
+ airports = []
+
+ page = request.args.get("page", 1, type=int)
+
+ return render_template("airports.html", sel_country=sel_country, countries=countries, airports=airports)
+
+
+@app.route("/airport_detail.html")
+def airport_detail():
+ sel_airport = request.args.get("airport")
+
+ airport = db.session.query(Airport).filter(Airport.id == sel_airport)
+
+ devices = db.session.query(Device).join(Logbook).filter(Logbook.takeoff_airport_id == sel_airport).order_by(Device.address)
+
+ return render_template("airport_detail.html", title="Airport Detail", airport=airport.one(), devices=devices)
+
+
+@app.route("/logbook.html", methods=["GET", "POST"])
+def logbook():
+ sel_country = request.args.get("country")
+ sel_airport = request.args.get("airport")
+ sel_date = request.args.get("date")
+
+ sel_device_id = request.args.get("device_id")
+
+ countries = get_countries_in_logbook()
+
+ if sel_country:
+ airports = get_airports_in_country(sel_country)
+ else:
+ airports = []
+
+ if sel_airport:
+ sel_airport = int(sel_airport)
+ if sel_airport not in [airport["id"] for airport in airports]:
+ sel_airport = None
+ sel_date = None
+ dates = get_dates_for_airport(sel_airport)
+ else:
+ dates = []
+
+ if sel_date:
+ sel_date = datetime.datetime.strptime(sel_date, "%Y-%m-%d").date()
+ if sel_date not in [entry["date"] for entry in dates]:
+ sel_date = dates[0]["date"]
+ elif len(dates) > 0:
+ sel_date = dates[0]["date"]
+
+ # Get Logbook
+ filters = []
+ if sel_airport:
+ filters.append(db.or_(Logbook.takeoff_airport_id == sel_airport, Logbook.landing_airport_id == sel_airport))
+
+ if sel_date:
+ filters.append(db.func.date(Logbook.reftime) == sel_date)
+
+ if sel_device_id:
+ filters.append(Logbook.device_id == sel_device_id)
+
+ if len(filters) > 0:
+ logbook = db.session.query(Logbook).filter(*filters).order_by(Logbook.reftime)
+ else:
+ logbook = None
+
+ return render_template("logbook.html", title="Logbook", sel_country=sel_country, countries=countries, sel_airport=sel_airport, airports=airports, sel_date=sel_date, dates=dates, logbook=logbook)
+
+
+@app.route("/download.html")
+def download_flight():
+ from io import StringIO
+
+ buffer = StringIO()
+ buffer.write("Moin moin\nAlter Verwalter")
+ buffer.seek(0)
+
+ return send_file(buffer, as_attachment=True, attachment_filename="wtf.igc", mimetype="text/plain")
+
+
+@app.route("/statistics.html")
+def statistics():
+
+ today = datetime.date.today()
+ today = datetime.date(2018, 7, 31)
+
+ receiverstats = db.session.query(ReceiverStats).filter(ReceiverStats.date == today)
+
+ return render_template("statistics.html", title="Receiver Statistics", receiverstats=receiverstats)
diff --git a/ogn_python/static/css/flags/LICENSE b/app/static/css/flags/LICENSE
similarity index 100%
rename from ogn_python/static/css/flags/LICENSE
rename to app/static/css/flags/LICENSE
diff --git a/ogn_python/static/css/flags/flags.css b/app/static/css/flags/flags.css
similarity index 100%
rename from ogn_python/static/css/flags/flags.css
rename to app/static/css/flags/flags.css
diff --git a/ogn_python/static/css/flags/flags.png b/app/static/css/flags/flags.png
similarity index 100%
rename from ogn_python/static/css/flags/flags.png
rename to app/static/css/flags/flags.png
diff --git a/ogn_python/static/files/WineButton.png b/app/static/files/WineButton.png
similarity index 100%
rename from ogn_python/static/files/WineButton.png
rename to app/static/files/WineButton.png
diff --git a/ogn_python/static/files/bootstrap/LICENSE.md b/app/static/files/bootstrap/LICENSE.md
similarity index 100%
rename from ogn_python/static/files/bootstrap/LICENSE.md
rename to app/static/files/bootstrap/LICENSE.md
diff --git a/ogn_python/static/files/bootstrap/bootstrap.colorpickersliders.css b/app/static/files/bootstrap/bootstrap.colorpickersliders.css
similarity index 100%
rename from ogn_python/static/files/bootstrap/bootstrap.colorpickersliders.css
rename to app/static/files/bootstrap/bootstrap.colorpickersliders.css
diff --git a/ogn_python/static/files/bootstrap/bootstrap.colorpickersliders.js b/app/static/files/bootstrap/bootstrap.colorpickersliders.js
similarity index 100%
rename from ogn_python/static/files/bootstrap/bootstrap.colorpickersliders.js
rename to app/static/files/bootstrap/bootstrap.colorpickersliders.js
diff --git a/ogn_python/static/files/bootstrap/bootstrap.colorpickersliders.nocielch.js b/app/static/files/bootstrap/bootstrap.colorpickersliders.nocielch.js
similarity index 100%
rename from ogn_python/static/files/bootstrap/bootstrap.colorpickersliders.nocielch.js
rename to app/static/files/bootstrap/bootstrap.colorpickersliders.nocielch.js
diff --git a/ogn_python/static/files/bootstrap/typeahead.bundle.js b/app/static/files/bootstrap/typeahead.bundle.js
similarity index 100%
rename from ogn_python/static/files/bootstrap/typeahead.bundle.js
rename to app/static/files/bootstrap/typeahead.bundle.js
diff --git a/ogn_python/static/files/heatmap2.js b/app/static/files/heatmap2.js
similarity index 100%
rename from ogn_python/static/files/heatmap2.js
rename to app/static/files/heatmap2.js
diff --git a/ogn_python/static/files/maptiles2.js b/app/static/files/maptiles2.js
similarity index 100%
rename from ogn_python/static/files/maptiles2.js
rename to app/static/files/maptiles2.js
diff --git a/ogn_python/static/files/mgrs.min.js b/app/static/files/mgrs.min.js
similarity index 100%
rename from ogn_python/static/files/mgrs.min.js
rename to app/static/files/mgrs.min.js
diff --git a/ogn_python/static/files/ol/ol.css b/app/static/files/ol/ol.css
similarity index 100%
rename from ogn_python/static/files/ol/ol.css
rename to app/static/files/ol/ol.css
diff --git a/ogn_python/static/files/ol/ol.js b/app/static/files/ol/ol.js
similarity index 100%
rename from ogn_python/static/files/ol/ol.js
rename to app/static/files/ol/ol.js
diff --git a/ogn_python/static/files/style.css b/app/static/files/style.css
similarity index 100%
rename from ogn_python/static/files/style.css
rename to app/static/files/style.css
diff --git a/ogn_python/static/files/tinycolor.js b/app/static/files/tinycolor.js
similarity index 100%
rename from ogn_python/static/files/tinycolor.js
rename to app/static/files/tinycolor.js
diff --git a/ogn_python/static/files/url.js.sample b/app/static/files/url.js.sample
similarity index 100%
rename from ogn_python/static/files/url.js.sample
rename to app/static/files/url.js.sample
diff --git a/ogn_python/static/img/Blank.gif b/app/static/img/Blank.gif
similarity index 100%
rename from ogn_python/static/img/Blank.gif
rename to app/static/img/Blank.gif
diff --git a/ogn_python/static/img/Transparent.gif b/app/static/img/Transparent.gif
similarity index 100%
rename from ogn_python/static/img/Transparent.gif
rename to app/static/img/Transparent.gif
diff --git a/ogn_python/static/ognlive/barogram.js b/app/static/ognlive/barogram.js
similarity index 100%
rename from ogn_python/static/ognlive/barogram.js
rename to app/static/ognlive/barogram.js
diff --git a/ogn_python/static/ognlive/horizZoomControl.js b/app/static/ognlive/horizZoomControl.js
similarity index 100%
rename from ogn_python/static/ognlive/horizZoomControl.js
rename to app/static/ognlive/horizZoomControl.js
diff --git a/ogn_python/static/ognlive/ogn.js b/app/static/ognlive/ogn.js
similarity index 100%
rename from ogn_python/static/ognlive/ogn.js
rename to app/static/ognlive/ogn.js
diff --git a/ogn_python/static/ognlive/ol.css b/app/static/ognlive/ol.css
similarity index 100%
rename from ogn_python/static/ognlive/ol.css
rename to app/static/ognlive/ol.css
diff --git a/ogn_python/static/ognlive/ol.js b/app/static/ognlive/ol.js
similarity index 100%
rename from ogn_python/static/ognlive/ol.js
rename to app/static/ognlive/ol.js
diff --git a/ogn_python/static/ognlive/osm.css b/app/static/ognlive/osm.css
similarity index 100%
rename from ogn_python/static/ognlive/osm.css
rename to app/static/ognlive/osm.css
diff --git a/ogn_python/static/ognlive/pict/OGN.png b/app/static/ognlive/pict/OGN.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/OGN.png
rename to app/static/ognlive/pict/OGN.png
diff --git a/ogn_python/static/ognlive/pict/OGN_b.png b/app/static/ognlive/pict/OGN_b.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/OGN_b.png
rename to app/static/ognlive/pict/OGN_b.png
diff --git a/ogn_python/static/ognlive/pict/OGN_g.png b/app/static/ognlive/pict/OGN_g.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/OGN_g.png
rename to app/static/ognlive/pict/OGN_g.png
diff --git a/ogn_python/static/ognlive/pict/OGN_o.png b/app/static/ognlive/pict/OGN_o.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/OGN_o.png
rename to app/static/ognlive/pict/OGN_o.png
diff --git a/ogn_python/static/ognlive/pict/OGN_p.png b/app/static/ognlive/pict/OGN_p.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/OGN_p.png
rename to app/static/ognlive/pict/OGN_p.png
diff --git a/ogn_python/static/ognlive/pict/OGN_r.png b/app/static/ognlive/pict/OGN_r.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/OGN_r.png
rename to app/static/ognlive/pict/OGN_r.png
diff --git a/ogn_python/static/ognlive/pict/OpenPortGuideLogo_32.png b/app/static/ognlive/pict/OpenPortGuideLogo_32.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/OpenPortGuideLogo_32.png
rename to app/static/ognlive/pict/OpenPortGuideLogo_32.png
diff --git a/ogn_python/static/ognlive/pict/a.gif b/app/static/ognlive/pict/a.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/a.gif
rename to app/static/ognlive/pict/a.gif
diff --git a/ogn_python/static/ognlive/pict/bin.gif b/app/static/ognlive/pict/bin.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/bin.gif
rename to app/static/ognlive/pict/bin.gif
diff --git a/ogn_python/static/ognlive/pict/c1.gif b/app/static/ognlive/pict/c1.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/c1.gif
rename to app/static/ognlive/pict/c1.gif
diff --git a/ogn_python/static/ognlive/pict/c2.gif b/app/static/ognlive/pict/c2.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/c2.gif
rename to app/static/ognlive/pict/c2.gif
diff --git a/ogn_python/static/ognlive/pict/c3.gif b/app/static/ognlive/pict/c3.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/c3.gif
rename to app/static/ognlive/pict/c3.gif
diff --git a/ogn_python/static/ognlive/pict/c4.gif b/app/static/ognlive/pict/c4.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/c4.gif
rename to app/static/ognlive/pict/c4.gif
diff --git a/ogn_python/static/ognlive/pict/c5.gif b/app/static/ognlive/pict/c5.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/c5.gif
rename to app/static/ognlive/pict/c5.gif
diff --git a/ogn_python/static/ognlive/pict/cancel-5.png b/app/static/ognlive/pict/cancel-5.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/cancel-5.png
rename to app/static/ognlive/pict/cancel-5.png
diff --git a/ogn_python/static/ognlive/pict/close.png b/app/static/ognlive/pict/close.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/close.png
rename to app/static/ognlive/pict/close.png
diff --git a/ogn_python/static/ognlive/pict/cordon.gif b/app/static/ognlive/pict/cordon.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/cordon.gif
rename to app/static/ognlive/pict/cordon.gif
diff --git a/ogn_python/static/ognlive/pict/dbarrow.gif b/app/static/ognlive/pict/dbarrow.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/dbarrow.gif
rename to app/static/ognlive/pict/dbarrow.gif
diff --git a/ogn_python/static/ognlive/pict/drapd.gif b/app/static/ognlive/pict/drapd.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/drapd.gif
rename to app/static/ognlive/pict/drapd.gif
diff --git a/ogn_python/static/ognlive/pict/drape.gif b/app/static/ognlive/pict/drape.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/drape.gif
rename to app/static/ognlive/pict/drape.gif
diff --git a/ogn_python/static/ognlive/pict/drapf.gif b/app/static/ognlive/pict/drapf.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/drapf.gif
rename to app/static/ognlive/pict/drapf.gif
diff --git a/ogn_python/static/ognlive/pict/drapi.gif b/app/static/ognlive/pict/drapi.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/drapi.gif
rename to app/static/ognlive/pict/drapi.gif
diff --git a/ogn_python/static/ognlive/pict/drapn.gif b/app/static/ognlive/pict/drapn.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/drapn.gif
rename to app/static/ognlive/pict/drapn.gif
diff --git a/ogn_python/static/ognlive/pict/draps.gif b/app/static/ognlive/pict/draps.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/draps.gif
rename to app/static/ognlive/pict/draps.gif
diff --git a/ogn_python/static/ognlive/pict/eye.gif b/app/static/ognlive/pict/eye.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/eye.gif
rename to app/static/ognlive/pict/eye.gif
diff --git a/ogn_python/static/ognlive/pict/favicon.gif b/app/static/ognlive/pict/favicon.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/favicon.gif
rename to app/static/ognlive/pict/favicon.gif
diff --git a/ogn_python/static/ognlive/pict/h1.gif b/app/static/ognlive/pict/h1.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/h1.gif
rename to app/static/ognlive/pict/h1.gif
diff --git a/ogn_python/static/ognlive/pict/h2.gif b/app/static/ognlive/pict/h2.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/h2.gif
rename to app/static/ognlive/pict/h2.gif
diff --git a/ogn_python/static/ognlive/pict/h3.gif b/app/static/ognlive/pict/h3.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/h3.gif
rename to app/static/ognlive/pict/h3.gif
diff --git a/ogn_python/static/ognlive/pict/hel.png b/app/static/ognlive/pict/hel.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/hel.png
rename to app/static/ognlive/pict/hel.png
diff --git a/ogn_python/static/ognlive/pict/ico.gif b/app/static/ognlive/pict/ico.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/ico.gif
rename to app/static/ognlive/pict/ico.gif
diff --git a/ogn_python/static/ognlive/pict/ico.png b/app/static/ognlive/pict/ico.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/ico.png
rename to app/static/ognlive/pict/ico.png
diff --git a/ogn_python/static/ognlive/pict/l1.gif b/app/static/ognlive/pict/l1.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/l1.gif
rename to app/static/ognlive/pict/l1.gif
diff --git a/ogn_python/static/ognlive/pict/left-3.png b/app/static/ognlive/pict/left-3.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/left-3.png
rename to app/static/ognlive/pict/left-3.png
diff --git a/ogn_python/static/ognlive/pict/m.gif b/app/static/ognlive/pict/m.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/m.gif
rename to app/static/ognlive/pict/m.gif
diff --git a/ogn_python/static/ognlive/pict/min.png b/app/static/ognlive/pict/min.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/min.png
rename to app/static/ognlive/pict/min.png
diff --git a/ogn_python/static/ognlive/pict/mm.gif b/app/static/ognlive/pict/mm.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/mm.gif
rename to app/static/ognlive/pict/mm.gif
diff --git a/ogn_python/static/ognlive/pict/mmm.gif b/app/static/ognlive/pict/mmm.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/mmm.gif
rename to app/static/ognlive/pict/mmm.gif
diff --git a/ogn_python/static/ognlive/pict/mod.gif b/app/static/ognlive/pict/mod.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/mod.gif
rename to app/static/ognlive/pict/mod.gif
diff --git a/ogn_python/static/ognlive/pict/n.gif b/app/static/ognlive/pict/n.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/n.gif
rename to app/static/ognlive/pict/n.gif
diff --git a/ogn_python/static/ognlive/pict/ogn-logo-ani.gif b/app/static/ognlive/pict/ogn-logo-ani.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/ogn-logo-ani.gif
rename to app/static/ognlive/pict/ogn-logo-ani.gif
diff --git a/ogn_python/static/ognlive/pict/p.gif b/app/static/ognlive/pict/p.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/p.gif
rename to app/static/ognlive/pict/p.gif
diff --git a/ogn_python/static/ognlive/pict/plu.png b/app/static/ognlive/pict/plu.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/plu.png
rename to app/static/ognlive/pict/plu.png
diff --git a/ogn_python/static/ognlive/pict/pp.gif b/app/static/ognlive/pict/pp.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/pp.gif
rename to app/static/ognlive/pict/pp.gif
diff --git a/ogn_python/static/ognlive/pict/ppp.gif b/app/static/ognlive/pict/ppp.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/ppp.gif
rename to app/static/ognlive/pict/ppp.gif
diff --git a/ogn_python/static/ognlive/pict/rec.png b/app/static/ognlive/pict/rec.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/rec.png
rename to app/static/ognlive/pict/rec.png
diff --git a/ogn_python/static/ognlive/pict/rec0.png b/app/static/ognlive/pict/rec0.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/rec0.png
rename to app/static/ognlive/pict/rec0.png
diff --git a/ogn_python/static/ognlive/pict/rec1.png b/app/static/ognlive/pict/rec1.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/rec1.png
rename to app/static/ognlive/pict/rec1.png
diff --git a/ogn_python/static/ognlive/pict/recy.png b/app/static/ognlive/pict/recy.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/recy.png
rename to app/static/ognlive/pict/recy.png
diff --git a/ogn_python/static/ognlive/pict/redo-6.png b/app/static/ognlive/pict/redo-6.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/redo-6.png
rename to app/static/ognlive/pict/redo-6.png
diff --git a/ogn_python/static/ognlive/pict/right-3.png b/app/static/ognlive/pict/right-3.png
similarity index 100%
rename from ogn_python/static/ognlive/pict/right-3.png
rename to app/static/ognlive/pict/right-3.png
diff --git a/ogn_python/static/ognlive/pict/tra.gif b/app/static/ognlive/pict/tra.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/tra.gif
rename to app/static/ognlive/pict/tra.gif
diff --git a/ogn_python/static/ognlive/pict/yn0.gif b/app/static/ognlive/pict/yn0.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/yn0.gif
rename to app/static/ognlive/pict/yn0.gif
diff --git a/ogn_python/static/ognlive/pict/yn1.gif b/app/static/ognlive/pict/yn1.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/yn1.gif
rename to app/static/ognlive/pict/yn1.gif
diff --git a/ogn_python/static/ognlive/pict/z.gif b/app/static/ognlive/pict/z.gif
similarity index 100%
rename from ogn_python/static/ognlive/pict/z.gif
rename to app/static/ognlive/pict/z.gif
diff --git a/ogn_python/static/ognlive/util.js b/app/static/ognlive/util.js
similarity index 100%
rename from ogn_python/static/ognlive/util.js
rename to app/static/ognlive/util.js
diff --git a/ogn_python/templates/airport_detail.html b/app/templates/airport_detail.html
similarity index 100%
rename from ogn_python/templates/airport_detail.html
rename to app/templates/airport_detail.html
diff --git a/ogn_python/templates/airports.html b/app/templates/airports.html
similarity index 100%
rename from ogn_python/templates/airports.html
rename to app/templates/airports.html
diff --git a/ogn_python/templates/base.html b/app/templates/base.html
similarity index 72%
rename from ogn_python/templates/base.html
rename to app/templates/base.html
index 6e9e273..afa2f28 100644
--- a/ogn_python/templates/base.html
+++ b/app/templates/base.html
@@ -19,12 +19,12 @@
@@ -50,7 +50,7 @@
{% block app_content %}{% endblock %}
{%- block footer %}
-
+
{%- endblock footer %}
{% endblock %}
diff --git a/ogn_python/templates/device_detail.html b/app/templates/device_detail.html
similarity index 100%
rename from ogn_python/templates/device_detail.html
rename to app/templates/device_detail.html
diff --git a/ogn_python/templates/devices.html b/app/templates/devices.html
similarity index 100%
rename from ogn_python/templates/devices.html
rename to app/templates/devices.html
diff --git a/ogn_python/templates/logbook.html b/app/templates/logbook.html
similarity index 100%
rename from ogn_python/templates/logbook.html
rename to app/templates/logbook.html
diff --git a/ogn_python/templates/ogn_live.html b/app/templates/ogn_live.html
similarity index 100%
rename from ogn_python/templates/ogn_live.html
rename to app/templates/ogn_live.html
diff --git a/ogn_python/templates/ognrange.html b/app/templates/ognrange.html
similarity index 100%
rename from ogn_python/templates/ognrange.html
rename to app/templates/ognrange.html
diff --git a/ogn_python/templates/receiver_detail.html b/app/templates/receiver_detail.html
similarity index 100%
rename from ogn_python/templates/receiver_detail.html
rename to app/templates/receiver_detail.html
diff --git a/ogn_python/templates/receivers.html b/app/templates/receivers.html
similarity index 100%
rename from ogn_python/templates/receivers.html
rename to app/templates/receivers.html
diff --git a/ogn_python/templates/statistics.html b/app/templates/statistics.html
similarity index 100%
rename from ogn_python/templates/statistics.html
rename to app/templates/statistics.html
diff --git a/ogn_python/utils.py b/app/utils.py
similarity index 62%
rename from ogn_python/utils.py
rename to app/utils.py
index bd6f7e6..a96d33d 100644
--- a/ogn_python/utils.py
+++ b/app/utils.py
@@ -14,9 +14,7 @@ DDB_URL = "http://ddb.glidernet.org/download/?t=1"
FLARMNET_URL = "http://www.flarmnet.org/files/data.fln"
-address_prefixes = {'F': 'FLR',
- 'O': 'OGN',
- 'I': 'ICA'}
+address_prefixes = {"F": "FLR", "O": "OGN", "I": "ICA"}
nm2m = 1852
mi2m = 1609.34
@@ -36,10 +34,10 @@ def date_to_timestamps(date):
def get_ddb(csv_file=None, address_origin=DeviceInfoOrigin.unknown):
if csv_file is None:
r = requests.get(DDB_URL)
- rows = '\n'.join(i for i in r.text.splitlines() if i[0] != '#')
+ rows = "\n".join(i for i in r.text.splitlines() if i[0] != "#")
else:
- r = open(csv_file, 'r')
- rows = ''.join(i for i in r.readlines() if i[0] != '#')
+ r = open(csv_file, "r")
+ rows = "".join(i for i in r.readlines() if i[0] != "#")
data = csv.reader(StringIO(rows), quotechar="'", quoting=csv.QUOTE_ALL)
@@ -51,8 +49,8 @@ def get_ddb(csv_file=None, address_origin=DeviceInfoOrigin.unknown):
device_info.aircraft = row[2]
device_info.registration = row[3]
device_info.competition = row[4]
- device_info.tracked = row[5] == 'Y'
- device_info.identified = row[6] == 'Y'
+ device_info.tracked = row[5] == "Y"
+ device_info.identified = row[6] == "Y"
device_info.aircraft_type = int(row[7])
device_info.address_origin = address_origin
@@ -64,10 +62,10 @@ def get_ddb(csv_file=None, address_origin=DeviceInfoOrigin.unknown):
def get_flarmnet(fln_file=None, address_origin=DeviceInfoOrigin.flarmnet):
if fln_file is None:
r = requests.get(FLARMNET_URL)
- rows = [bytes.fromhex(line).decode('latin1') for line in r.text.split('\n') if len(line) == 172]
+ rows = [bytes.fromhex(line).decode("latin1") for line in r.text.split("\n") if len(line) == 172]
else:
- with open(fln_file, 'r') as file:
- rows = [bytes.fromhex(line.strip()).decode('latin1') for line in file.readlines() if len(line) == 172]
+ with open(fln_file, "r") as file:
+ rows = [bytes.fromhex(line.strip()).decode("latin1") for line in file.readlines() if len(line) == 172]
device_infos = list()
for row in rows:
@@ -96,43 +94,43 @@ def get_airports(cupfile):
for line in f:
try:
for waypoint in Reader([line]):
- if waypoint['style'] > 5: # reject unlandable places
+ if waypoint["style"] > 5: # reject unlandable places
continue
airport = Airport()
- airport.name = waypoint['name']
- airport.code = waypoint['code']
- airport.country_code = waypoint['country']
- airport.style = waypoint['style']
- airport.description = waypoint['description']
- location = Location(waypoint['longitude'], waypoint['latitude'])
+ airport.name = waypoint["name"]
+ airport.code = waypoint["code"]
+ airport.country_code = waypoint["country"]
+ airport.style = waypoint["style"]
+ airport.description = waypoint["description"]
+ location = Location(waypoint["longitude"], waypoint["latitude"])
airport.location_wkt = location.to_wkt()
- airport.altitude = waypoint['elevation']['value']
- if (waypoint['elevation']['unit'] == 'ft'):
+ airport.altitude = waypoint["elevation"]["value"]
+ if waypoint["elevation"]["unit"] == "ft":
airport.altitude = airport.altitude * FEETS_TO_METER
- airport.runway_direction = waypoint['runway_direction']
- airport.runway_length = waypoint['runway_length']['value']
- if (waypoint['runway_length']['unit'] == 'nm'):
+ airport.runway_direction = waypoint["runway_direction"]
+ airport.runway_length = waypoint["runway_length"]["value"]
+ if waypoint["runway_length"]["unit"] == "nm":
airport.altitude = airport.altitude * nm2m
- elif (waypoint['runway_length']['unit'] == 'ml'):
+ elif waypoint["runway_length"]["unit"] == "ml":
airport.altitude = airport.altitude * mi2m
- airport.frequency = waypoint['frequency']
+ airport.frequency = waypoint["frequency"]
airports.append(airport)
except AttributeError as e:
- print('Failed to parse line: {} {}'.format(line, e))
+ print("Failed to parse line: {} {}".format(line, e))
return airports
def open_file(filename):
"""Opens a regular or unzipped textfile for reading."""
- f = open(filename, 'rb')
+ f = open(filename, "rb")
a = f.read(2)
f.close()
- if (a == b'\x1f\x8b'):
- f = gzip.open(filename, 'rt', encoding="latin-1")
+ if a == b"\x1f\x8b":
+ f = gzip.open(filename, "rt", encoding="latin-1")
return f
else:
- f = open(filename, 'rt', encoding="latin-1")
+ f = open(filename, "rt", encoding="latin-1")
return f
diff --git a/ogn_python.py b/ogn_python.py
new file mode 100644
index 0000000..74929ed
--- /dev/null
+++ b/ogn_python.py
@@ -0,0 +1,5 @@
+from app import app
+
+
+if __name__ == '__main__':
+ app.run()
diff --git a/ogn_python/app.py b/ogn_python/app.py
deleted file mode 100644
index 92fb3a3..0000000
--- a/ogn_python/app.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from ogn_python import app
-from ogn_python import routes
-from ogn_python import commands
-
-if __name__ == '__main__':
- app.run()
diff --git a/ogn_python/backend/liveglidernet.py b/ogn_python/backend/liveglidernet.py
deleted file mode 100644
index 4d36552..0000000
--- a/ogn_python/backend/liveglidernet.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from datetime import datetime, timedelta, timezone, date
-
-from ogn_python.model import AircraftBeacon, Device, Receiver
-
-from ogn_python import db
-from ogn_python.model.receiver_beacon import ReceiverBeacon
-
-
-def utc_to_local(utc_dt):
- return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
-
-
-def encode(address):
- return 'xx' + address
-
-
-def decode(code):
- return code[2:9]
-
-
-def rec(min_timestamp, min_online_timestamp):
- last_seen_query = db.session.query(ReceiverBeacon) \
- .filter(ReceiverBeacon.timestamp > min_timestamp) \
- .order_by(ReceiverBeacon.receiver_id, ReceiverBeacon.timestamp) \
- .distinct(ReceiverBeacon.receiver_id)
-
- lines = []
- lines.append('')
- lines.append('')
- lines.append('')
- for receiver_beacon in last_seen_query:
- if receiver_beacon.location == None or receiver_beacon.name.startswith('FNB'):
- continue
- lines.append(''
- .format(receiver_beacon.name, receiver_beacon.location.latitude, receiver_beacon.location.longitude, receiver_beacon.timestamp < min_online_timestamp))
-
- lines.append('')
- xml = '\n'.join(lines)
-
- return xml
-
-
-def lxml(show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_min=-180):
-
- timestamp_range_filter = [db.between(AircraftBeacon.timestamp, datetime(2018, 7, 31, 11, 55, 0), datetime(2018, 7, 31, 12, 5, 0))]
-
- last_seen_query = db.session.query(AircraftBeacon) \
- .filter(*timestamp_range_filter) \
- .order_by(AircraftBeacon.device_id, AircraftBeacon.timestamp) \
- .distinct(AircraftBeacon.device_id) \
-
- lines = list()
- lines.append('')
- lines.append('')
-
- for aircraft_beacon in last_seen_query:
- device = aircraft_beacon.device
-
- code = encode(device.address)
-
- if device.info:
- if (not device.info.tracked or not device.info.identified):
- continue
-
- if not device.info.competition:
- competition = device.info.registration[-2:]
- else:
- competition = device.info.competition
-
- if not device.info.registration:
- registration = '???'
- else:
- registration = device.info.registration
-
- address = device.address
-
- else:
- competition = ('_' + code[-2:]).lower()
- registration = code
- address = 0
-
- elapsed_time = datetime.utcnow() - aircraft_beacon.timestamp
- elapsed_seconds = int(elapsed_time.total_seconds())
-
- lines.append(' '
- .format(aircraft_beacon.location.latitude,
- aircraft_beacon.location.longitude,
- competition,
- registration,
- int(aircraft_beacon.altitude),
- utc_to_local(aircraft_beacon.timestamp).strftime("%H:%M:%S"),
- elapsed_seconds,
- int(aircraft_beacon.track),
- int(aircraft_beacon.ground_speed),
- int(aircraft_beacon.climb_rate * 10) / 10,
- aircraft_beacon.aircraft_type,
- aircraft_beacon.receiver_name,
- address,
- code))
-
- lines.append('')
- xml = '\n'.join(lines)
-
- return xml
\ No newline at end of file
diff --git a/ogn_python/backend/ognrange.py b/ogn_python/backend/ognrange.py
deleted file mode 100644
index 31038b7..0000000
--- a/ogn_python/backend/ognrange.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import json
-from datetime import datetime, timedelta
-
-from sqlalchemy import func, case
-from sqlalchemy.sql.expression import label
-from ogn_python.model import Receiver, ReceiverCoverage
-
-from ogn_python import db
-
-
-def alchemyencoder(obj):
- """JSON encoder function for SQLAlchemy special classes."""
-
- import decimal
- from datetime import datetime
- if isinstance(obj, datetime):
- return obj.strftime('%Y-%m-%d %H:%M')
- elif isinstance(obj, decimal.Decimal):
- return float(obj)
-
-
-def stations2_filtered_pl(start, end):
- last_10_minutes = datetime.utcnow() - timedelta(minutes=10)
-
- query = db.session.query(
- Receiver.name.label('s'),
- label('lt', func.round(func.ST_Y(Receiver.location_wkt) * 10000) / 10000),
- label('lg', func.round(func.ST_X(Receiver.location_wkt) * 10000) / 10000),
- case([(Receiver.lastseen > last_10_minutes, "U")],
- else_="D").label('u'),
- Receiver.lastseen.label('ut'),
- label('v', Receiver.version + '.' + Receiver.platform)) \
- .order_by(Receiver.lastseen) \
- .filter(db.or_(db.and_(start < Receiver.firstseen, end > Receiver.firstseen),
- db.and_(start < Receiver.lastseen, end > Receiver.lastseen)))
-
- res = db.session.execute(query)
- stations = json.dumps({'stations': [dict(r) for r in res]}, default=alchemyencoder)
-
- return stations
-
-
-def max_tile_mgrs_pl(station, start, end, squares):
- query = db.session.query(
- func.right(ReceiverCoverage.location_mgrs_short, 4),
- func.count(ReceiverCoverage.location_mgrs_short)
- ) \
- .filter(db.and_(Receiver.id == ReceiverCoverage.receiver_id, Receiver.name == station)) \
- .filter(ReceiverCoverage.location_mgrs_short.like(squares + '%')) \
- .group_by(func.right(ReceiverCoverage.location_mgrs_short, 4))
-
- res = {'t': squares,
- 'p': ['{}/{}'.format(r[0], r[1]) for r in query.all()]}
- return json.dumps(res)
\ No newline at end of file
diff --git a/ogn_python/collect/logbook.py b/ogn_python/collect/logbook.py
deleted file mode 100644
index 70ae719..0000000
--- a/ogn_python/collect/logbook.py
+++ /dev/null
@@ -1,172 +0,0 @@
-from sqlalchemy import and_, or_, insert, update, exists, between
-from sqlalchemy.sql import func, null
-from sqlalchemy.sql.expression import true, false
-
-from ogn_python.model import TakeoffLanding, Logbook, AircraftBeacon
-from ogn_python.utils import date_to_timestamps
-
-from ogn_python import app
-
-
-def update_entries(session, date, logger=None):
- """Add/update logbook entries."""
-
- if logger is None:
- logger = app.logger
-
- logger.info("Compute logbook.")
-
- # limit time range to given date and set window partition and window order
- (start, end) = date_to_timestamps(date)
- pa = (TakeoffLanding.device_id)
- wo = and_(TakeoffLanding.device_id,
- TakeoffLanding.airport_id,
- TakeoffLanding.timestamp)
-
- # make a query with current, previous and next "takeoff_landing" event, so we can find complete flights
- sq = session.query(
- TakeoffLanding.device_id,
- func.lag(TakeoffLanding.device_id).over(partition_by=pa, order_by=wo).label('device_id_prev'),
- func.lead(TakeoffLanding.device_id).over(partition_by=pa, order_by=wo).label('device_id_next'),
- TakeoffLanding.timestamp,
- func.lag(TakeoffLanding.timestamp).over(partition_by=pa, order_by=wo).label('timestamp_prev'),
- func.lead(TakeoffLanding.timestamp).over(partition_by=pa, order_by=wo).label('timestamp_next'),
- TakeoffLanding.track,
- func.lag(TakeoffLanding.track).over(partition_by=pa, order_by=wo).label('track_prev'),
- func.lead(TakeoffLanding.track).over(partition_by=pa, order_by=wo).label('track_next'),
- TakeoffLanding.is_takeoff,
- func.lag(TakeoffLanding.is_takeoff).over(partition_by=pa, order_by=wo).label('is_takeoff_prev'),
- func.lead(TakeoffLanding.is_takeoff).over(partition_by=pa, order_by=wo).label('is_takeoff_next'),
- TakeoffLanding.airport_id,
- func.lag(TakeoffLanding.airport_id).over(partition_by=pa, order_by=wo).label('airport_id_prev'),
- func.lead(TakeoffLanding.airport_id).over(partition_by=pa, order_by=wo).label('airport_id_next')) \
- .filter(between(TakeoffLanding.timestamp, start, end)) \
- .subquery()
-
- # find complete flights
- complete_flight_query = session.query(
- sq.c.timestamp.label('reftime'),
- sq.c.device_id.label('device_id'),
- sq.c.timestamp.label('takeoff_timestamp'), sq.c.track.label('takeoff_track'), sq.c.airport_id.label('takeoff_airport_id'),
- sq.c.timestamp_next.label('landing_timestamp'), sq.c.track_next.label('landing_track'), sq.c.airport_id_next.label('landing_airport_id')) \
- .filter(and_(sq.c.is_takeoff == true(), sq.c.is_takeoff_next == false()))
-
- # find landings without start
- only_landings_query = session.query(
- sq.c.timestamp.label('reftime'),
- sq.c.device_id.label('device_id'),
- null().label('takeoff_timestamp'), null().label('takeoff_track'), null().label('takeoff_airport_id'),
- sq.c.timestamp.label('landing_timestamp'), sq.c.track.label('landing_track'), sq.c.airport_id.label('landing_airport_id')) \
- .filter(sq.c.is_takeoff == false()) \
- .filter(or_(sq.c.is_takeoff_prev == false(),
- sq.c.is_takeoff_prev == null()))
-
- # find starts without landing
- only_starts_query = session.query(
- sq.c.timestamp.label('reftime'),
- sq.c.device_id.label('device_id'),
- sq.c.timestamp.label('takeoff_timestamp'), sq.c.track.label('takeoff_track'), sq.c.airport_id.label('takeoff_airport_id'),
- null().label('landing_timestamp'), null().label('landing_track'), null().label('landing_airport_id')) \
- .filter(sq.c.is_takeoff == true()) \
- .filter(or_(sq.c.is_takeoff_next == true(),
- sq.c.is_takeoff_next == null()))
-
- # unite all computated flights
- union_query = complete_flight_query.union(
- only_landings_query,
- only_starts_query) \
- .subquery()
-
- # if a logbook entry exist --> update it
- upd = update(Logbook) \
- .where(and_(Logbook.device_id == union_query.c.device_id,
- union_query.c.takeoff_airport_id != null(),
- union_query.c.landing_airport_id != null(),
- or_(and_(Logbook.takeoff_airport_id == union_query.c.takeoff_airport_id,
- Logbook.takeoff_timestamp == union_query.c.takeoff_timestamp,
- Logbook.landing_airport_id == null()),
- and_(Logbook.takeoff_airport_id == null(),
- Logbook.landing_airport_id == union_query.c.landing_airport_id,
- Logbook.landing_timestamp == union_query.c.landing_timestamp)))) \
- .values({"reftime": union_query.c.reftime,
- "takeoff_timestamp": union_query.c.takeoff_timestamp,
- "takeoff_track": union_query.c.takeoff_track,
- "takeoff_airport_id": union_query.c.takeoff_airport_id,
- "landing_timestamp": union_query.c.landing_timestamp,
- "landing_track": union_query.c.landing_track,
- "landing_airport_id": union_query.c.landing_airport_id})
-
- result = session.execute(upd)
- update_counter = result.rowcount
- session.commit()
- logger.debug("Updated logbook entries: {}".format(update_counter))
-
- # if a logbook entry doesnt exist --> insert it
- new_logbook_entries = session.query(union_query) \
- .filter(~exists().where(
- and_(Logbook.device_id == union_query.c.device_id,
- or_(and_(Logbook.takeoff_airport_id == union_query.c.takeoff_airport_id,
- Logbook.takeoff_timestamp == union_query.c.takeoff_timestamp),
- and_(Logbook.takeoff_airport_id == null(),
- union_query.c.takeoff_airport_id == null())),
- or_(and_(Logbook.landing_airport_id == union_query.c.landing_airport_id,
- Logbook.landing_timestamp == union_query.c.landing_timestamp),
- and_(Logbook.landing_airport_id == null(),
- union_query.c.landing_airport_id == null())))))
-
- ins = insert(Logbook).from_select((Logbook.reftime,
- Logbook.device_id,
- Logbook.takeoff_timestamp,
- Logbook.takeoff_track,
- Logbook.takeoff_airport_id,
- Logbook.landing_timestamp,
- Logbook.landing_track,
- Logbook.landing_airport_id),
- new_logbook_entries)
-
- result = session.execute(ins)
- insert_counter = result.rowcount
- session.commit()
-
- finish_message = "Logbook: {} inserted, {} updated".format(insert_counter, update_counter)
- logger.debug(finish_message)
- return finish_message
-
-
-def update_max_altitudes(session, date, logger=None):
- """Add max altitudes in logbook when flight is complete (takeoff and landing)."""
-
- if logger is None:
- logger = app.logger
-
- logger.info("Update logbook max altitude.")
-
- if session is None:
- session = app.session
-
- (start, end) = date_to_timestamps(date)
-
- logbook_entries = session.query(Logbook.id) \
- .filter(and_(Logbook.takeoff_timestamp != null(), Logbook.landing_timestamp != null(), Logbook.max_altitude == null())) \
- .filter(between(Logbook.reftime, start, end)) \
- .subquery()
-
- max_altitudes = session.query(Logbook.id, func.max(AircraftBeacon.altitude).label('max_altitude')) \
- .filter(Logbook.id == logbook_entries.c.id) \
- .filter(and_(AircraftBeacon.device_id == Logbook.device_id,
- AircraftBeacon.timestamp >= Logbook.takeoff_timestamp,
- AircraftBeacon.timestamp <= Logbook.landing_timestamp)) \
- .group_by(Logbook.id) \
- .subquery()
-
- update_logbook = session.query(Logbook) \
- .filter(Logbook.id == max_altitudes.c.id) \
- .update({
- Logbook.max_altitude: max_altitudes.c.max_altitude},
- synchronize_session='fetch')
-
- session.commit()
-
- finish_message = "Logbook (altitude): {} entries updated.".format(update_logbook)
- logger.info(finish_message)
- return finish_message
diff --git a/ogn_python/collect/ognrange.py b/ogn_python/collect/ognrange.py
deleted file mode 100644
index ebddfd6..0000000
--- a/ogn_python/collect/ognrange.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from sqlalchemy import Date
-from sqlalchemy import and_, insert, update, exists, between
-from sqlalchemy.sql import func, null
-
-from ogn_python.model import AircraftBeacon, ReceiverCoverage
-from ogn_python.utils import date_to_timestamps
-
-from ogn_python import app
-
-
-def update_entries(session, date, logger=None):
- """Create receiver coverage stats for Melissas ognrange."""
-
- if logger is None:
- logger = app.logger
-
- logger.info("Compute receiver coverages.")
-
- (start, end) = date_to_timestamps(date)
-
- # Filter aircraft beacons
- sq = session.query(AircraftBeacon.location_mgrs_short,
- AircraftBeacon.receiver_id,
- AircraftBeacon.signal_quality,
- AircraftBeacon.altitude,
- AircraftBeacon.device_id) \
- .filter(and_(between(AircraftBeacon.timestamp, start, end),
- AircraftBeacon.location_mgrs_short != null(),
- AircraftBeacon.receiver_id != null(),
- AircraftBeacon.device_id != null())) \
- .subquery()
-
- # ... and group them by reduced MGRS, receiver and date
- query = session.query(sq.c.location_mgrs_short,
- sq.c.receiver_id,
- func.cast(date, Date).label('date'),
- func.max(sq.c.signal_quality).label('max_signal_quality'),
- func.min(sq.c.altitude).label('min_altitude'),
- func.max(sq.c.altitude).label('max_altitude'),
- func.count(sq.c.altitude).label('aircraft_beacon_count'),
- func.count(func.distinct(sq.c.device_id)).label('device_count')) \
- .group_by(sq.c.location_mgrs_short,
- sq.c.receiver_id) \
- .subquery()
-
- # if a receiver coverage entry exist --> update it
- upd = update(ReceiverCoverage) \
- .where(and_(ReceiverCoverage.location_mgrs_short == query.c.location_mgrs_short,
- ReceiverCoverage.receiver_id == query.c.receiver_id,
- ReceiverCoverage.date == date)) \
- .values({"max_signal_quality": query.c.max_signal_quality,
- "min_altitude": query.c.min_altitude,
- "max_altitude": query.c.max_altitude,
- "aircraft_beacon_count": query.c.aircraft_beacon_count,
- "device_count": query.c.device_count})
-
- result = session.execute(upd)
- update_counter = result.rowcount
- session.commit()
- logger.debug("Updated receiver coverage entries: {}".format(update_counter))
-
- # if a receiver coverage entry doesnt exist --> insert it
- new_coverage_entries = session.query(query) \
- .filter(~exists().where(
- and_(ReceiverCoverage.location_mgrs_short == query.c.location_mgrs_short,
- ReceiverCoverage.receiver_id == query.c.receiver_id,
- ReceiverCoverage.date == date)))
-
- ins = insert(ReceiverCoverage).from_select((
- ReceiverCoverage.location_mgrs_short,
- ReceiverCoverage.receiver_id,
- ReceiverCoverage.date,
- ReceiverCoverage.max_signal_quality,
- ReceiverCoverage.min_altitude,
- ReceiverCoverage.max_altitude,
- ReceiverCoverage.aircraft_beacon_count,
- ReceiverCoverage.device_count),
- new_coverage_entries)
-
- result = session.execute(ins)
- insert_counter = result.rowcount
- session.commit()
-
- finish_message = "ReceiverCoverage: {} inserted, {} updated".format(insert_counter, update_counter)
- logger.debug(finish_message)
- return finish_message
diff --git a/ogn_python/collect/stats.py b/ogn_python/collect/stats.py
deleted file mode 100644
index 6782148..0000000
--- a/ogn_python/collect/stats.py
+++ /dev/null
@@ -1,470 +0,0 @@
-from sqlalchemy import insert, distinct, between, literal
-from sqlalchemy.sql import null, and_, func, or_, update
-from sqlalchemy.sql.expression import case
-
-from ogn_python.model import AircraftBeacon, DeviceStats, Country, CountryStats, ReceiverStats, RelationStats, Receiver, Device
-
-from ogn_python.model.receiver_beacon import ReceiverBeacon
-from ogn_python.utils import date_to_timestamps
-
-from ogn_python import app
-
-# 40dB@10km is enough for 640km
-MAX_PLAUSIBLE_QUALITY = 40
-
-
-def create_device_stats(session, date, logger=None):
- """Add/update device stats."""
-
- if logger is None:
- logger = app.logger
-
- (start, end) = date_to_timestamps(date)
-
- # First kill the stats for the selected date
- deleted_counter = session.query(DeviceStats) \
- .filter(DeviceStats.date == date) \
- .delete()
-
- # Since "distinct count" does not work in window functions we need a work-around for receiver counting
- sq = session.query(AircraftBeacon,
- func.dense_rank()
- .over(partition_by=AircraftBeacon.device_id, order_by=AircraftBeacon.receiver_id)
- .label('dr')) \
- .filter(and_(between(AircraftBeacon.timestamp, start, end), AircraftBeacon.device_id != null())) \
- .filter(or_(AircraftBeacon.error_count == 0, AircraftBeacon.error_count == null())) \
- .subquery()
-
- # Calculate stats, firstseen, lastseen and last values != NULL
- device_stats = session.query(
- distinct(sq.c.device_id).label('device_id'),
- literal(date).label('date'),
- func.max(sq.c.dr)
- .over(partition_by=sq.c.device_id)
- .label('receiver_count'),
- func.max(sq.c.altitude)
- .over(partition_by=sq.c.device_id)
- .label('max_altitude'),
- func.count(sq.c.device_id)
- .over(partition_by=sq.c.device_id)
- .label('aircraft_beacon_count'),
- func.first_value(sq.c.name)
- .over(partition_by=sq.c.device_id, order_by=case([(sq.c.name == null(), None)], else_=sq.c.timestamp).asc().nullslast())
- .label('name'),
- func.first_value(sq.c.timestamp)
- .over(partition_by=sq.c.device_id, order_by=case([(sq.c.timestamp == null(), None)], else_=sq.c.timestamp).asc().nullslast())
- .label('firstseen'),
- func.first_value(sq.c.timestamp)
- .over(partition_by=sq.c.device_id, order_by=case([(sq.c.timestamp == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('lastseen'),
- func.first_value(sq.c.aircraft_type)
- .over(partition_by=sq.c.device_id, order_by=case([(sq.c.aircraft_type == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('aircraft_type'),
- func.first_value(sq.c.stealth)
- .over(partition_by=sq.c.device_id, order_by=case([(sq.c.stealth == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('stealth'),
- func.first_value(sq.c.software_version)
- .over(partition_by=sq.c.device_id, order_by=case([(sq.c.software_version == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('software_version'),
- func.first_value(sq.c.hardware_version)
- .over(partition_by=sq.c.device_id, order_by=case([(sq.c.hardware_version == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('hardware_version'),
- func.first_value(sq.c.real_address)
- .over(partition_by=sq.c.device_id, order_by=case([(sq.c.real_address == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('real_address')) \
- .subquery()
-
- # And insert them
- ins = insert(DeviceStats).from_select(
- [DeviceStats.device_id, DeviceStats.date, DeviceStats.receiver_count, DeviceStats.max_altitude, DeviceStats.aircraft_beacon_count, DeviceStats.name,
- DeviceStats.firstseen, DeviceStats.lastseen, DeviceStats.aircraft_type, DeviceStats.stealth,
- DeviceStats.software_version, DeviceStats.hardware_version, DeviceStats.real_address],
- device_stats)
- res = session.execute(ins)
- insert_counter = res.rowcount
- session.commit()
- logger.debug("DeviceStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter))
-
- return "DeviceStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter)
-
-
-def create_receiver_stats(session, date, logger=None):
- """Add/update receiver stats."""
-
- if logger is None:
- logger = app.logger
-
- (start, end) = date_to_timestamps(date)
-
- # First kill the stats for the selected date
- deleted_counter = session.query(ReceiverStats) \
- .filter(ReceiverStats.date == date) \
- .delete()
-
- # Select one day
- sq = session.query(ReceiverBeacon) \
- .filter(between(ReceiverBeacon.timestamp, start, end)) \
- .subquery()
-
- # Calculate stats, firstseen, lastseen and last values != NULL
- receiver_stats = session.query(
- distinct(sq.c.receiver_id).label('receiver_id'),
- literal(date).label('date'),
- func.first_value(sq.c.timestamp)
- .over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.timestamp == null(), None)], else_=sq.c.timestamp).asc().nullslast())
- .label('firstseen'),
- func.first_value(sq.c.timestamp)
- .over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.timestamp == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('lastseen'),
- func.first_value(sq.c.location)
- .over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.location == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('location_wkt'),
- func.first_value(sq.c.altitude)
- .over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.altitude == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('altitude'),
- func.first_value(sq.c.version)
- .over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.version == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('version'),
- func.first_value(sq.c.platform)
- .over(partition_by=sq.c.receiver_id, order_by=case([(sq.c.platform == null(), None)], else_=sq.c.timestamp).desc().nullslast())
- .label('platform')) \
- .subquery()
-
- # And insert them
- ins = insert(ReceiverStats).from_select(
- [ReceiverStats.receiver_id, ReceiverStats.date, ReceiverStats.firstseen, ReceiverStats.lastseen, ReceiverStats.location_wkt, ReceiverStats.altitude, ReceiverStats.version, ReceiverStats.platform],
- receiver_stats)
- res = session.execute(ins)
- insert_counter = res.rowcount
- session.commit()
- logger.warn("ReceiverStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter))
-
- # Update aircraft_beacon_count, aircraft_count and max_distance
- aircraft_beacon_stats = session.query(AircraftBeacon.receiver_id,
- func.count(AircraftBeacon.timestamp).label('aircraft_beacon_count'),
- func.count(func.distinct(AircraftBeacon.device_id)).label('aircraft_count'),
- func.max(AircraftBeacon.distance).label('max_distance')) \
- .filter(and_(between(AircraftBeacon.timestamp, start, end),
- AircraftBeacon.error_count == 0,
- AircraftBeacon.quality <= MAX_PLAUSIBLE_QUALITY,
- AircraftBeacon.relay == null())) \
- .group_by(AircraftBeacon.receiver_id) \
- .subquery()
-
- upd = update(ReceiverStats) \
- .where(and_(ReceiverStats.date == date,
- ReceiverStats.receiver_id == aircraft_beacon_stats.c.receiver_id)) \
- .values({'aircraft_beacon_count': aircraft_beacon_stats.c.aircraft_beacon_count,
- 'aircraft_count': aircraft_beacon_stats.c.aircraft_count,
- 'max_distance': aircraft_beacon_stats.c.max_distance})
-
- result = session.execute(upd)
- update_counter = result.rowcount
- session.commit()
- logger.warn("Updated {} ReceiverStats".format(update_counter))
-
- return "ReceiverStats for {}: {} deleted, {} inserted, {} updated".format(date, deleted_counter, insert_counter, update_counter)
-
-
-def create_country_stats(session, date, logger=None):
- if logger is None:
- logger = app.logger
-
- (start, end) = date_to_timestamps(date)
-
- # First kill the stats for the selected date
- deleted_counter = session.query(CountryStats) \
- .filter(CountryStats.date == date) \
- .delete()
-
- country_stats = session.query(literal(date), Country.gid,
- func.count(AircraftBeacon.timestamp).label('aircraft_beacon_count'), \
- func.count(func.distinct(AircraftBeacon.receiver_id)).label('device_count')) \
- .filter(between(AircraftBeacon.timestamp, start, end)) \
- .filter(func.st_contains(Country.geom, AircraftBeacon.location)) \
- .group_by(Country.gid) \
- .subquery()
-
- # And insert them
- ins = insert(CountryStats).from_select(
- [CountryStats.date, CountryStats.country_id, CountryStats.aircraft_beacon_count, CountryStats.device_count],
- country_stats)
- res = session.execute(ins)
- insert_counter = res.rowcount
- session.commit()
-
-
-def update_device_stats_jumps(session, date, logger=None):
- """Update device stats jumps."""
-
- if logger is None:
- logger = app.logger
-
- (start, end) = date_to_timestamps(date)
-
- # speed limits in m/s (values above indicates a unplausible position / jump)
- max_horizontal_speed = 1000
- max_vertical_speed = 100
- max_jumps = 10 # threshold for an 'ambiguous' device
-
- # find consecutive positions for a device
- sq = session.query(AircraftBeacon.device_id,
- AircraftBeacon.timestamp,
- func.lead(AircraftBeacon.timestamp).over(partition_by=AircraftBeacon.device_id, order_by=AircraftBeacon.timestamp).label('timestamp_next'),
- AircraftBeacon.location_wkt,
- func.lead(AircraftBeacon.location_wkt).over(partition_by=AircraftBeacon.device_id, order_by=AircraftBeacon.timestamp).label('location_next'),
- AircraftBeacon.altitude,
- func.lead(AircraftBeacon.altitude).over(partition_by=AircraftBeacon.device_id, order_by=AircraftBeacon.timestamp).label('altitude_next')) \
- .filter(and_(between(AircraftBeacon.timestamp, start, end),
- AircraftBeacon.error_count == 0)) \
- .subquery()
-
- # calc vertial and horizontal speed between points
- sq2 = session.query(sq.c.device_id,
- (func.st_distancesphere(sq.c.location_next, sq.c.location) / (func.extract('epoch', sq.c.timestamp_next) - func.extract('epoch', sq.c.timestamp))).label('horizontal_speed'),
- ((sq.c.altitude_next - sq.c.altitude) / (func.extract('epoch', sq.c.timestamp_next) - func.extract('epoch', sq.c.timestamp))).label('vertical_speed')) \
- .filter(and_(sq.c.timestamp != null(),
- sq.c.timestamp_next != null(),
- sq.c.timestamp < sq.c.timestamp_next)) \
- .subquery()
-
- # ... and find and count 'jumps'
- sq3 = session.query(sq2.c.device_id,
- func.sum(case([(or_(func.abs(sq2.c.horizontal_speed) > max_horizontal_speed, func.abs(sq2.c.vertical_speed) > max_vertical_speed), 1)], else_=0)).label('jumps')) \
- .group_by(sq2.c.device_id) \
- .subquery()
-
- upd = update(DeviceStats) \
- .where(and_(DeviceStats.date == date,
- DeviceStats.device_id == sq3.c.device_id)) \
- .values({'ambiguous': sq3.c.jumps > max_jumps,
- 'jumps': sq3.c.jumps})
-
- result = session.execute(upd)
- update_counter = result.rowcount
- session.commit()
- logger.warn("Updated {} DeviceStats jumps".format(update_counter))
-
- return "DeviceStats jumps for {}: {} updated".format(date, update_counter)
-
-
-def create_relation_stats(session, date, logger=None):
- """Add/update relation stats."""
-
- if logger is None:
- logger = app.logger
-
- (start, end) = date_to_timestamps(date)
-
- # First kill the stats for the selected date
- deleted_counter = session.query(RelationStats) \
- .filter(RelationStats.date == date) \
- .delete()
-
- # Calculate stats for selected day
- relation_stats = session.query(
- literal(date),
- AircraftBeacon.device_id,
- AircraftBeacon.receiver_id,
- func.max(AircraftBeacon.quality),
- func.count(AircraftBeacon.timestamp)
- ) \
- .filter(and_(between(AircraftBeacon.timestamp, start, end),
- AircraftBeacon.distance > 1000,
- AircraftBeacon.error_count == 0,
- AircraftBeacon.quality <= MAX_PLAUSIBLE_QUALITY,
- AircraftBeacon.ground_speed > 10)) \
- .group_by(literal(date), AircraftBeacon.device_id, AircraftBeacon.receiver_id) \
- .subquery()
-
- # And insert them
- ins = insert(RelationStats).from_select(
- [RelationStats.date, RelationStats.device_id, RelationStats.receiver_id, RelationStats.quality, RelationStats.beacon_count],
- relation_stats)
- res = session.execute(ins)
- insert_counter = res.rowcount
- session.commit()
- logger.warn("RelationStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter))
-
- return "RelationStats for {}: {} deleted, {} inserted".format(date, deleted_counter, insert_counter)
-
-
-def update_qualities(session, date, logger=None):
- """Calculate relative qualities of receivers and devices."""
-
- if logger is None:
- logger = app.logger
-
- # Calculate avg quality of devices
- dev_sq = session.query(RelationStats.device_id,
- func.avg(RelationStats.quality).label('quality')) \
- .filter(RelationStats.date == date) \
- .group_by(RelationStats.device_id) \
- .subquery()
-
- dev_upd = update(DeviceStats) \
- .where(and_(DeviceStats.date == date,
- DeviceStats.device_id == dev_sq.c.device_id)) \
- .values({'quality': dev_sq.c.quality})
-
- dev_result = session.execute(dev_upd)
- dev_update_counter = dev_result.rowcount
- session.commit()
- logger.warn("Updated {} DeviceStats: quality".format(dev_update_counter))
-
- # Calculate avg quality of receivers
- rec_sq = session.query(RelationStats.receiver_id,
- func.avg(RelationStats.quality).label('quality')) \
- .filter(RelationStats.date == date) \
- .group_by(RelationStats.receiver_id) \
- .subquery()
-
- rec_upd = update(ReceiverStats) \
- .where(and_(ReceiverStats.date == date,
- ReceiverStats.receiver_id == rec_sq.c.receiver_id)) \
- .values({'quality': rec_sq.c.quality})
-
- rec_result = session.execute(rec_upd)
- rec_update_counter = rec_result.rowcount
- session.commit()
- logger.warn("Updated {} ReceiverStats: quality".format(rec_update_counter))
-
- # Calculate quality_offset of devices
- dev_sq = session.query(RelationStats.device_id,
- (func.sum(RelationStats.beacon_count * (RelationStats.quality - ReceiverStats.quality)) / (func.sum(RelationStats.beacon_count))).label('quality_offset')) \
- .filter(RelationStats.date == date) \
- .filter(and_(RelationStats.receiver_id == ReceiverStats.receiver_id,
- RelationStats.date == ReceiverStats.date)) \
- .group_by(RelationStats.device_id) \
- .subquery()
-
- dev_upd = update(DeviceStats) \
- .where(and_(DeviceStats.date == date,
- DeviceStats.device_id == dev_sq.c.device_id)) \
- .values({'quality_offset': dev_sq.c.quality_offset})
-
- dev_result = session.execute(dev_upd)
- dev_update_counter = dev_result.rowcount
- session.commit()
- logger.warn("Updated {} DeviceStats: quality_offset".format(dev_update_counter))
-
- # Calculate quality_offset of receivers
- rec_sq = session.query(RelationStats.receiver_id,
- (func.sum(RelationStats.beacon_count * (RelationStats.quality - DeviceStats.quality)) / (func.sum(RelationStats.beacon_count))).label('quality_offset')) \
- .filter(RelationStats.date == date) \
- .filter(and_(RelationStats.device_id == DeviceStats.device_id,
- RelationStats.date == DeviceStats.date)) \
- .group_by(RelationStats.receiver_id) \
- .subquery()
-
- rec_upd = update(ReceiverStats) \
- .where(and_(ReceiverStats.date == date,
- ReceiverStats.receiver_id == rec_sq.c.receiver_id)) \
- .values({'quality_offset': rec_sq.c.quality_offset})
-
- rec_result = session.execute(rec_upd)
- rec_update_counter = rec_result.rowcount
- session.commit()
- logger.warn("Updated {} ReceiverStats: quality_offset".format(rec_update_counter))
-
- return "Updated {} DeviceStats and {} ReceiverStats".format(dev_update_counter, rec_update_counter)
-
-
-def update_receivers(session, logger=None):
- """Update receivers with stats."""
-
- if logger is None:
- logger = app.logger
-
- receiver_stats = session.query(
- distinct(ReceiverStats.receiver_id).label('receiver_id'),
- func.first_value(ReceiverStats.firstseen)
- .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.firstseen == null(), None)], else_=ReceiverStats.date).asc().nullslast())
- .label('firstseen'),
- func.first_value(ReceiverStats.lastseen)
- .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.lastseen == null(), None)], else_=ReceiverStats.date).desc().nullslast())
- .label('lastseen'),
- func.first_value(ReceiverStats.location_wkt)
- .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.location_wkt == null(), None)], else_=ReceiverStats.date).desc().nullslast())
- .label('location_wkt'),
- func.first_value(ReceiverStats.altitude)
- .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.altitude == null(), None)], else_=ReceiverStats.date).desc().nullslast())
- .label('altitude'),
- func.first_value(ReceiverStats.version)
- .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.version == null(), None)], else_=ReceiverStats.date).desc().nullslast())
- .label('version'),
- func.first_value(ReceiverStats.platform)
- .over(partition_by=ReceiverStats.receiver_id, order_by=case([(ReceiverStats.platform == null(), None)], else_=ReceiverStats.date).desc().nullslast())
- .label('platform')) \
- .order_by(ReceiverStats.receiver_id) \
- .subquery()
-
- upd = update(Receiver) \
- .where(and_(Receiver.id == receiver_stats.c.receiver_id)) \
- .values({'firstseen': receiver_stats.c.firstseen,
- 'lastseen': receiver_stats.c.lastseen,
- 'location': receiver_stats.c.location_wkt,
- 'altitude': receiver_stats.c.altitude,
- 'version': receiver_stats.c.version,
- 'platform': receiver_stats.c.platform})
-
- result = session.execute(upd)
- update_counter = result.rowcount
- session.commit()
- logger.warn("Updated {} Receivers".format(update_counter))
-
- return "Updated {} Receivers".format(update_counter)
-
-
-def update_devices(session, logger=None):
- """Update devices with stats."""
-
- if logger is None:
- logger = app.logger
-
- device_stats = session.query(
- distinct(DeviceStats.device_id).label('device_id'),
- func.first_value(DeviceStats.name)
- .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.name == null(), None)], else_=DeviceStats.date).desc().nullslast())
- .label('name'),
- func.first_value(DeviceStats.firstseen)
- .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.firstseen == null(), None)], else_=DeviceStats.date).asc().nullslast())
- .label('firstseen'),
- func.max(DeviceStats.lastseen)
- .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.lastseen == null(), None)], else_=DeviceStats.date).desc().nullslast())
- .label('lastseen'),
- func.first_value(DeviceStats.aircraft_type)
- .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.aircraft_type == null(), None)], else_=DeviceStats.date).desc().nullslast())
- .label('aircraft_type'),
- func.first_value(DeviceStats.stealth)
- .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.stealth == null(), None)], else_=DeviceStats.date).desc().nullslast())
- .label('stealth'),
- func.first_value(DeviceStats.software_version)
- .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.software_version == null(), None)], else_=DeviceStats.date).desc().nullslast())
- .label('software_version'),
- func.first_value(DeviceStats.hardware_version)
- .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.hardware_version == null(), None)], else_=DeviceStats.date).desc().nullslast())
- .label('hardware_version'),
- func.first_value(DeviceStats.real_address)
- .over(partition_by=DeviceStats.device_id, order_by=case([(DeviceStats.real_address == null(), None)], else_=DeviceStats.date).desc().nullslast())
- .label('real_address')) \
- .order_by(DeviceStats.device_id) \
- .subquery()
-
- upd = update(Device) \
- .where(and_(Device.id == device_stats.c.device_id)) \
- .values({'name': device_stats.c.name,
- 'firstseen': device_stats.c.firstseen,
- 'lastseen': device_stats.c.lastseen,
- 'aircraft_type': device_stats.c.aircraft_type,
- 'stealth': device_stats.c.stealth,
- 'software_version': device_stats.c.software_version,
- 'hardware_version': device_stats.c.hardware_version,
- 'real_address': device_stats.c.real_address})
-
- result = session.execute(upd)
- update_counter = result.rowcount
- session.commit()
- logger.warn("Updated {} Devices".format(update_counter))
-
- return "Updated {} Devices".format(update_counter)
diff --git a/ogn_python/collect/takeoff_landings.py b/ogn_python/collect/takeoff_landings.py
deleted file mode 100644
index b994f72..0000000
--- a/ogn_python/collect/takeoff_landings.py
+++ /dev/null
@@ -1,145 +0,0 @@
-from datetime import timedelta
-
-from sqlalchemy import and_, or_, insert, between, exists
-from sqlalchemy.sql import func, null
-from sqlalchemy.sql.expression import case
-
-from ogn_python.model import AircraftBeacon, TakeoffLanding, Airport
-
-from ogn_python import app
-
-
-def update_entries(session, start, end, logger=None):
- """Compute takeoffs and landings."""
-
- if logger is None:
- logger = app.logger
-
- logger.info("Compute takeoffs and landings.")
-
- # considered time interval should not exceed a complete day
- if end - start > timedelta(days=1):
- abort_message = "TakeoffLanding: timeinterval start='{}' and end='{}' is too big.".format(start, end)
- logger.warn(abort_message)
- return abort_message
-
- # check if we have any airport
- airports_query = session.query(Airport).limit(1)
- if not airports_query.all():
- abort_message = "TakeoffLanding: Cannot calculate takeoff and landings without any airport! Please import airports first."
- logger.warn(abort_message)
- return abort_message
-
- # takeoff / landing detection is based on 3 consecutive points all below a certain altitude AGL
- takeoff_speed = 55 # takeoff detection: 1st point below, 2nd and 3rd above this limit
- landing_speed = 40 # landing detection: 1st point above, 2nd and 3rd below this limit
- min_takeoff_climb_rate = -5 # takeoff detection: glider should not sink too much
- max_landing_climb_rate = 5 # landing detection: glider should not climb too much
- duration = 100 # the points must not exceed this duration
- radius = 5000 # the points must not exceed this radius around the 2nd point
- max_agl = 200 # takeoff / landing must not exceed this altitude AGL
-
- # get beacons for selected time range, one per device_id and timestamp
- sq = session.query(AircraftBeacon) \
- .distinct(AircraftBeacon.device_id, AircraftBeacon.timestamp) \
- .order_by(AircraftBeacon.device_id, AircraftBeacon.timestamp, AircraftBeacon.error_count) \
- .filter(AircraftBeacon.agl < max_agl) \
- .filter(between(AircraftBeacon.timestamp, start, end)) \
- .subquery()
-
- # make a query with current, previous and next position
- sq2 = session.query(
- sq.c.device_id,
- func.lag(sq.c.device_id).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('device_id_prev'),
- func.lead(sq.c.device_id).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('device_id_next'),
- sq.c.timestamp,
- func.lag(sq.c.timestamp).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('timestamp_prev'),
- func.lead(sq.c.timestamp).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('timestamp_next'),
- sq.c.location,
- func.lag(sq.c.location).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('location_wkt_prev'),
- func.lead(sq.c.location).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('location_wkt_next'),
- sq.c.track,
- func.lag(sq.c.track).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('track_prev'),
- func.lead(sq.c.track).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('track_next'),
- sq.c.ground_speed,
- func.lag(sq.c.ground_speed).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('ground_speed_prev'),
- func.lead(sq.c.ground_speed).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('ground_speed_next'),
- sq.c.altitude,
- func.lag(sq.c.altitude).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('altitude_prev'),
- func.lead(sq.c.altitude).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('altitude_next'),
- sq.c.climb_rate,
- func.lag(sq.c.climb_rate).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('climb_rate_prev'),
- func.lead(sq.c.climb_rate).over(partition_by=sq.c.device_id, order_by=sq.c.timestamp).label('climb_rate_next')) \
- .subquery()
-
- # consider only positions with predecessor and successor and limit distance and duration between points
- sq3 = session.query(sq2) \
- .filter(and_(sq2.c.device_id_prev != null(),
- sq2.c.device_id_next != null())) \
- .filter(and_(func.ST_DistanceSphere(sq2.c.location, sq2.c.location_wkt_prev) < radius,
- func.ST_DistanceSphere(sq2.c.location, sq2.c.location_wkt_next) < radius)) \
- .filter(sq2.c.timestamp_next - sq2.c.timestamp_prev < timedelta(seconds=duration)) \
- .subquery()
-
- # find possible takeoffs and landings
- sq4 = session.query(
- sq3.c.timestamp,
- case([(sq3.c.ground_speed > takeoff_speed, sq3.c.location_wkt_prev), # on takeoff we take the location from the previous fix because it is nearer to the airport
- (sq3.c.ground_speed <= takeoff_speed, sq3.c.location)]).label('location'),
- case([(sq3.c.ground_speed > landing_speed, sq3.c.track),
- (sq3.c.ground_speed <= landing_speed, sq3.c.track_prev)]).label('track'), # on landing we take the track from the previous fix because gliders tend to leave the runway quickly
- sq3.c.ground_speed,
- sq3.c.altitude,
- case([(sq3.c.ground_speed > takeoff_speed, True),
- (sq3.c.ground_speed < landing_speed, False)]).label('is_takeoff'),
- sq3.c.device_id) \
- .filter(or_(and_(sq3.c.ground_speed_prev < takeoff_speed, # takeoff
- sq3.c.ground_speed > takeoff_speed,
- sq3.c.ground_speed_next > takeoff_speed,
- sq3.c.climb_rate > min_takeoff_climb_rate),
- and_(sq3.c.ground_speed_prev > landing_speed, # landing
- sq3.c.ground_speed < landing_speed,
- sq3.c.ground_speed_next < landing_speed,
- sq3.c.climb_rate < max_landing_climb_rate))) \
- .subquery()
-
- # consider them if the are near airports ...
- sq5 = session.query(
- sq4.c.timestamp,
- sq4.c.track,
- sq4.c.is_takeoff,
- sq4.c.device_id,
- Airport.id.label('airport_id'),
- func.ST_DistanceSphere(sq4.c.location, Airport.location_wkt).label('airport_distance')) \
- .filter(and_(func.ST_Within(sq4.c.location, Airport.border),
- between(Airport.style, 2, 5))) \
- .subquery()
-
- # ... and take the nearest airport
- sq6 = session.query(sq5.c.timestamp, sq5.c.track, sq5.c.is_takeoff, sq5.c.device_id, sq5.c.airport_id) \
- .distinct(sq5.c.timestamp, sq5.c.track, sq5.c.is_takeoff, sq5.c.device_id) \
- .order_by(sq5.c.timestamp, sq5.c.track, sq5.c.is_takeoff, sq5.c.device_id, sq5.c.airport_distance) \
- .subquery()
-
- # consider them only if they are not already existing in db
- takeoff_landing_query = session.query(sq6) \
- .filter(~exists().where(
- and_(TakeoffLanding.timestamp == sq6.c.timestamp,
- TakeoffLanding.device_id == sq6.c.device_id,
- TakeoffLanding.airport_id == sq6.c.airport_id)))
-
- # ... and save them
- ins = insert(TakeoffLanding).from_select((TakeoffLanding.timestamp,
- TakeoffLanding.track,
- TakeoffLanding.is_takeoff,
- TakeoffLanding.device_id,
- TakeoffLanding.airport_id),
- takeoff_landing_query)
-
- result = session.execute(ins)
- session.commit()
- insert_counter = result.rowcount
-
- finish_message = "TakeoffLandings: {} inserted".format(insert_counter)
- logger.info(finish_message)
- return finish_message
diff --git a/ogn_python/commands/logbook.py b/ogn_python/commands/logbook.py
deleted file mode 100644
index cef50ef..0000000
--- a/ogn_python/commands/logbook.py
+++ /dev/null
@@ -1,117 +0,0 @@
-from flask.cli import AppGroup
-import click
-
-from datetime import datetime
-
-from ogn_python.collect.logbook import update_entries as logbook_update_entries
-from ogn_python.collect.takeoff_landings import update_entries as takeoff_landings_update_entries
-from ogn_python.model import Airport, Logbook
-from sqlalchemy.sql import func
-from tqdm import tqdm
-from ogn_python.commands.database import get_database_days
-from ogn_python.utils import date_to_timestamps
-
-from ogn_python import db
-
-user_cli = AppGroup('logbook')
-user_cli.help = "Handling of logbook data."
-
-
-@user_cli.command('compute_takeoff_landing')
-@click.argument('start')
-@click.argument('end')
-def compute_takeoff_landing(start, end):
- """Compute takeoffs and landings."""
-
- days = get_database_days(start, end)
-
- pbar = tqdm(days)
- for single_date in pbar:
- pbar.set_description(datetime.strftime(single_date, '%Y-%m-%d'))
- (start, end) = date_to_timestamps(single_date)
- result = takeoff_landings_update_entries(session=db.session, start=start, end=end)
-
-
-@user_cli.command('compute_logbook')
-@click.argument('start')
-@click.argument('end')
-def compute_logbook(start, end):
- """Compute logbook."""
-
- days = get_database_days(start, end)
-
- pbar = tqdm(days)
- for single_date in pbar:
- pbar.set_description(single_date.strftime('%Y-%m-%d'))
- result = logbook_update_entries(session=db.session, date=single_date)
-
-
-@user_cli.command('show')
-@click.argument('airport_name')
-@click.argument('date')
-def show(airport_name, date=None):
- """Show a logbook for ."""
- airport = db.session.query(Airport) \
- .filter(Airport.name == airport_name) \
- .first()
-
- if (airport is None):
- print('Airport "{}" not found.'.format(airport_name))
- return
-
- or_args = []
- if date is not None:
- date = datetime.strptime(date, "%Y-%m-%d")
- (start, end) = date_to_timestamps(date)
- or_args = [db.between(Logbook.reftime, start, end)]
-
- # get all logbook entries and add device and airport infos
- logbook_query = db.session.query(func.row_number().over(order_by=Logbook.reftime).label('row_number'),
- Logbook) \
- .filter(*or_args) \
- .filter(db.or_(Logbook.takeoff_airport_id == airport.id,
- Logbook.landing_airport_id == airport.id)) \
- .order_by(Logbook.reftime)
-
- # ... and finally print out the logbook
- print('--- Logbook ({}) ---'.format(airport_name))
-
- def none_datetime_replacer(datetime_object):
- return '--:--:--' if datetime_object is None else datetime_object.time()
-
- def none_track_replacer(track_object):
- return '--' if track_object is None else round(track_object / 10.0)
-
- def none_timedelta_replacer(timedelta_object):
- return '--:--:--' if timedelta_object is None else timedelta_object
-
- def none_registration_replacer(device_object):
- return '[' + device_object.address + ']' if len(device_object.infos) == 0 else device_object.infos[0].registration
-
- def none_aircraft_replacer(device_object):
- return '(unknown)' if len(device_object.infos) == 0 else device_object.infos[0].aircraft
-
- def airport_marker(logbook_object):
- if logbook_object.takeoff_airport is not None and logbook_object.takeoff_airport.name is not airport.name:
- return ('FROM: {}'.format(logbook_object.takeoff_airport.name))
- elif logbook_object.landing_airport is not None and logbook_object.landing_airport.name is not airport.name:
- return ('TO: {}'.format(logbook_object.landing_airport.name))
- else:
- return ('')
-
- def none_altitude_replacer(logbook_object):
- return "?" if logbook_object.max_altitude is None else "{:5d}m ({:+5d}m)".format(logbook_object.max_altitude, logbook_object.max_altitude - logbook_object.takeoff_airport.altitude)
-
- for [row_number, logbook] in logbook_query.all():
- print('%3d. %10s %8s (%2s) %8s (%2s) %8s %15s %8s %17s %20s' % (
- row_number,
- logbook.reftime.date(),
- none_datetime_replacer(logbook.takeoff_timestamp),
- none_track_replacer(logbook.takeoff_track),
- none_datetime_replacer(logbook.landing_timestamp),
- none_track_replacer(logbook.landing_track),
- none_timedelta_replacer(logbook.duration),
- none_altitude_replacer(logbook),
- none_registration_replacer(logbook.device),
- none_aircraft_replacer(logbook.device),
- airport_marker(logbook)))
diff --git a/ogn_python/config/default.py b/ogn_python/config/default.py
deleted file mode 100644
index 503fd1e..0000000
--- a/ogn_python/config/default.py
+++ /dev/null
@@ -1,57 +0,0 @@
-SECRET_KEY = 'i-like-ogn'
-
-SQLALCHEMY_DATABASE_URI = 'postgresql://postgres@localhost:5432/ogn'
-SQLALCHEMY_TRACK_MODIFICATIONS = False
-
-# Flask-Cache stuff
-CACHE_TYPE = 'simple'
-CACHE_DEFAULT_TIMEOUT = 300
-
-# Celery stuff
-CELERY_BROKER_URL = 'redis://localhost:6379/0'
-CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
-
-
-from celery.schedules import crontab
-from datetime import timedelta
-
-CELERYBEAT_SCHEDULE = {
- 'update-ddb': {
- 'task': 'import_ddb',
- 'schedule': timedelta(hours=1),
- },
- 'update-country-codes': {
- 'task': 'update_receivers_country_code',
- 'schedule': timedelta(days=1),
- },
- 'update-takeoff-and-landing': {
- 'task': 'update_takeoff_landings',
- 'schedule': timedelta(hours=1),
- 'kwargs': {'last_minutes': 90},
- },
- 'update-logbook': {
- 'task': 'update_logbook_entries',
- 'schedule': timedelta(hours=2),
- 'kwargs': {'day_offset': 0},
- },
- 'update-max-altitudes': {
- 'task': 'update_logbook_max_altitude',
- 'schedule': timedelta(hours=1),
- 'kwargs': {'day_offset': 0},
- },
- 'update-stats-daily': {
- 'task': 'update_stats',
- 'schedule': crontab(hour=0, minute=5),
- 'kwargs': {'day_offset': -1},
- },
- 'update-logbook-daily': {
- 'task': 'update_logbook_entries',
- 'schedule': crontab(hour=1, minute=0),
- 'kwargs': {'day_offset': -1},
- },
- 'purge_old_data': {
- 'task': 'purge_old_data',
- 'schedule': timedelta(hours=1),
- 'kwargs': {'max_hours': 48}
- },
-}
diff --git a/ogn_python/config/test.py b/ogn_python/config/test.py
deleted file mode 100644
index a3afda1..0000000
--- a/ogn_python/config/test.py
+++ /dev/null
@@ -1,6 +0,0 @@
-SQLALCHEMY_DATABASE_URI = 'postgresql://postgres@localhost:5432/ogn_test'
-SQLALCHEMY_TRACK_MODIFICATIONS = False
-
-# Celery stuff
-CELERY_BROKER_URL = 'redis://localhost:6379/0'
-CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
diff --git a/ogn_python/gateway/bulkimport.py b/ogn_python/gateway/bulkimport.py
deleted file mode 100644
index e14426f..0000000
--- a/ogn_python/gateway/bulkimport.py
+++ /dev/null
@@ -1,322 +0,0 @@
-from datetime import datetime, timedelta
-from io import StringIO
-
-from flask.cli import AppGroup
-import click
-from tqdm import tqdm
-from mgrs import MGRS
-
-from ogn.parser import parse, ParseError
-
-from ogn_python.model import AircraftBeacon, ReceiverBeacon, Location
-from ogn_python.utils import open_file
-from ogn_python.gateway.process_tools import *
-
-from ogn_python import db
-from ogn_python import app
-
-user_cli = AppGroup('bulkimport')
-user_cli.help = "Tools for accelerated data import."
-
-
-# define message types we want to proceed
-AIRCRAFT_BEACON_TYPES = ['aprs_aircraft', 'flarm', 'tracker', 'fanet', 'lt24', 'naviter', 'skylines', 'spider', 'spot']
-RECEIVER_BEACON_TYPES = ['aprs_receiver', 'receiver']
-
-# define fields we want to proceed
-BEACON_KEY_FIELDS = ['name', 'receiver_name', 'timestamp']
-AIRCRAFT_BEACON_FIELDS = ['location', 'altitude', 'dstcall', 'relay', 'track', 'ground_speed', 'address_type', 'aircraft_type', 'stealth', 'address', 'climb_rate', 'turn_rate', 'signal_quality', 'error_count', 'frequency_offset', 'gps_quality_horizontal', 'gps_quality_vertical', 'software_version', 'hardware_version', 'real_address', 'signal_power', 'distance', 'radial', 'quality', 'location_mgrs', 'location_mgrs_short', 'agl', 'receiver_id', 'device_id']
-RECEIVER_BEACON_FIELDS = ['location', 'altitude', 'dstcall', 'relay', 'version', 'platform', 'cpu_load', 'free_ram', 'total_ram', 'ntp_error', 'rt_crystal_correction', 'voltage', 'amperage', 'cpu_temp', 'senders_visible', 'senders_total', 'rec_input_noise', 'senders_signal', 'senders_messages', 'good_senders_signal', 'good_senders', 'good_and_bad_senders']
-
-
-myMGRS = MGRS()
-
-
-def string_to_message(raw_string, reference_date):
- global receivers
-
- try:
- message = parse(raw_string, reference_date)
- except NotImplementedError as e:
- app.logger.error('No parser implemented for message: {}'.format(raw_string))
- return None
- except ParseError as e:
- app.logger.error('Parsing error with message: {}'.format(raw_string))
- return None
- except TypeError as e:
- app.logger.error('TypeError with message: {}'.format(raw_string))
- return None
- except Exception as e:
- app.logger.error('Other Exception with string: {}'.format(raw_string))
- return None
-
- # update reference receivers and distance to the receiver
- if message['aprs_type'] == 'position':
- if message['beacon_type'] in AIRCRAFT_BEACON_TYPES + RECEIVER_BEACON_TYPES:
- latitude = message['latitude']
- longitude = message['longitude']
-
- location = Location(longitude, latitude)
- message['location'] = location.to_wkt()
- location_mgrs = myMGRS.toMGRS(latitude, longitude).decode('utf-8')
- message['location_mgrs'] = location_mgrs
- message['location_mgrs_short'] = location_mgrs[0:5] + location_mgrs[5:7] + location_mgrs[10:12]
-
- if message['beacon_type'] in AIRCRAFT_BEACON_TYPES and 'gps_quality' in message:
- if message['gps_quality'] is not None and 'horizontal' in message['gps_quality']:
- message['gps_quality_horizontal'] = message['gps_quality']['horizontal']
- message['gps_quality_vertical'] = message['gps_quality']['vertical']
- del message['gps_quality']
-
- # TODO: Fix python-ogn-client 0.91
- if 'senders_messages' in message and message['senders_messages'] is not None:
- message['senders_messages'] = int(message['senders_messages'])
- if 'good_senders' in message and message['good_senders'] is not None:
- message['good_senders'] = int(message['good_senders'])
- if 'good_and_bad_senders' in message and message['good_and_bad_senders'] is not None:
- message['good_and_bad_senders'] = int(message['good_and_bad_senders'])
-
- return message
-
-
-class ContinuousDbFeeder:
- def __init__(self,):
- self.postfix = 'continuous_import'
- self.last_flush = datetime.utcnow()
- self.last_add_missing = datetime.utcnow()
- self.last_transfer = datetime.utcnow()
-
- self.aircraft_buffer = StringIO()
- self.receiver_buffer = StringIO()
-
- create_tables(self.postfix)
- create_indices(self.postfix)
-
- def add(self, raw_string):
- message = string_to_message(raw_string, reference_date=datetime.utcnow())
-
- if message is None or ('raw_message' in message and message['raw_message'][0] == '#') or 'beacon_type' not in message:
- return
-
- if message['beacon_type'] in AIRCRAFT_BEACON_TYPES:
- complete_message = ','.join([str(message[k]) if k in message and message[k] is not None else '\\N' for k in BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS])
- self.aircraft_buffer.write(complete_message)
- self.aircraft_buffer.write('\n')
- elif message['beacon_type'] in RECEIVER_BEACON_TYPES:
- complete_message = ','.join([str(message[k]) if k in message and message[k] is not None else '\\N' for k in BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS])
- self.receiver_buffer.write(complete_message)
- self.receiver_buffer.write('\n')
- else:
- app.logger.error("Ignore beacon_type: {}".format(message['beacon_type']))
- return
-
- if datetime.utcnow() - self.last_flush >= timedelta(seconds=20):
- self.flush()
- self.prepare()
-
- self.aircraft_buffer = StringIO()
- self.receiver_buffer = StringIO()
-
- self.last_flush = datetime.utcnow()
-
- if datetime.utcnow() - self.last_add_missing >= timedelta(seconds=60):
- self.add_missing()
- self.last_add_missing = datetime.utcnow()
-
- if datetime.utcnow() - self.last_transfer >= timedelta(seconds=30):
- self.transfer()
- self.delete_beacons()
- self.last_transfer = datetime.utcnow()
-
-
- def flush(self):
- self.aircraft_buffer.seek(0)
- self.receiver_buffer.seek(0)
-
- connection = db.engine.raw_connection()
- cursor = connection.cursor()
- cursor.copy_from(self.aircraft_buffer, 'aircraft_beacons_{0}'.format(self.postfix), sep=',', columns=BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS)
- cursor.copy_from(self.receiver_buffer, 'receiver_beacons_{0}'.format(self.postfix), sep=',', columns=BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS)
- connection.commit()
-
- self.aircraft_buffer = StringIO()
- self.receiver_buffer = StringIO()
-
- def add_missing(self):
- add_missing_receivers(self.postfix)
- add_missing_devices(self.postfix)
-
- def prepare(self):
- # make receivers complete
- update_receiver_beacons(self.postfix)
- update_receiver_location(self.postfix)
-
- # make devices complete
- update_aircraft_beacons(self.postfix)
-
- def transfer(self):
- # tranfer beacons
- transfer_aircraft_beacons(self.postfix)
- transfer_receiver_beacons(self.postfix)
-
- def delete_beacons(self):
- # delete already transfered beacons
- delete_receiver_beacons(self.postfix)
- delete_aircraft_beacons(self.postfix)
-
-
-class FileDbFeeder():
- def __init__(self):
- self.postfix = 'continuous_import'
- self.last_flush = datetime.utcnow()
-
- self.aircraft_buffer = StringIO()
- self.receiver_buffer = StringIO()
-
- create_tables(self.postfix)
- create_indices(self.postfix)
-
- def add(self, raw_string):
- message = string_to_message(raw_string, reference_date=datetime.utcnow())
-
- if message is None or ('raw_message' in message and message['raw_message'][0] == '#') or 'beacon_type' not in message:
- return
-
- if message['beacon_type'] in AIRCRAFT_BEACON_TYPES:
- complete_message = ','.join([str(message[k]) if k in message and message[k] is not None else '\\N' for k in BEACON_KEY_FIELDS + AIRCRAFT_BEACON_FIELDS])
- self.aircraft_buffer.write(complete_message)
- self.aircraft_buffer.write('\n')
- elif message['beacon_type'] in RECEIVER_BEACON_TYPES:
- complete_message = ','.join([str(message[k]) if k in message and message[k] is not None else '\\N' for k in BEACON_KEY_FIELDS + RECEIVER_BEACON_FIELDS])
- self.receiver_buffer.write(complete_message)
- self.receiver_buffer.write('\n')
- else:
- app.logger.error("Ignore beacon_type: {}".format(message['beacon_type']))
- return
-
- def prepare(self):
- # make receivers complete
- add_missing_receivers(self.postfix)
- update_receiver_location(self.postfix)
-
- # make devices complete
- add_missing_devices(self.postfix)
-
- # prepare beacons for transfer
- create_indices(self.postfix)
- update_receiver_beacons_bigdata(self.postfix)
- update_aircraft_beacons_bigdata(self.postfix)
-
-
-def get_aircraft_beacons_postfixes():
- """Get the postfixes from imported aircraft_beacons logs."""
-
- postfixes = db.session.execute("""
- SELECT DISTINCT(RIGHT(tablename, 8))
- FROM pg_catalog.pg_tables
- WHERE schemaname = 'public' AND tablename LIKE 'aircraft\_beacons\_20______'
- ORDER BY RIGHT(tablename, 10);
- """).fetchall()
-
- return [postfix for postfix in postfixes]
-
-
-
-
-
-def export_to_path(postfix):
- import os, gzip
- aircraft_beacons_file = os.path.join(path, 'aircraft_beacons_{0}.csv.gz'.format(postfix))
- with gzip.open(aircraft_beacons_file, 'wt', encoding='utf-8') as gzip_file:
- self.cur.copy_expert("COPY ({}) TO STDOUT WITH (DELIMITER ',', FORMAT CSV, HEADER, ENCODING 'UTF-8');".format(self.get_merged_aircraft_beacons_subquery()), gzip_file)
- receiver_beacons_file = os.path.join(path, 'receiver_beacons_{0}.csv.gz'.format(postfix))
- with gzip.open(receiver_beacons_file, 'wt') as gzip_file:
- self.cur.copy_expert("COPY ({}) TO STDOUT WITH (DELIMITER ',', FORMAT CSV, HEADER, ENCODING 'UTF-8');".format(self.get_merged_receiver_beacons_subquery()), gzip_file)
-
-
-
-def convert(sourcefile, datestr, saver):
- from ogn_python.gateway.process import string_to_message
- from ogn_python.gateway.process_tools import AIRCRAFT_BEACON_TYPES, RECEIVER_BEACON_TYPES
- from datetime import datetime
-
- fin = open_file(sourcefile)
-
- # get total lines of the input file
- total_lines = 0
- for line in fin:
- total_lines += 1
- fin.seek(0)
-
- current_line = 0
- steps = 100000
- reference_date = datetime.strptime(datestr + ' 12:00:00', '%Y-%m-%d %H:%M:%S')
-
- pbar = tqdm(fin, total=total_lines)
- for line in pbar:
- pbar.set_description('Importing {}'.format(sourcefile))
-
- current_line += 1
- if current_line % steps == 0:
- saver.flush()
-
- message = string_to_message(line.strip(), reference_date=reference_date)
- if message is None:
- continue
-
- dictfilt = lambda x, y: dict([(i, x[i]) for i in x if i in set(y)])
-
- try:
- if message['beacon_type'] in AIRCRAFT_BEACON_TYPES:
- message = dictfilt(message, ('beacon_type', 'aprs_type', 'location_wkt', 'altitude', 'name', 'dstcall', 'relay', 'receiver_name', 'timestamp', 'track', 'ground_speed',
- 'address_type', 'aircraft_type', 'stealth', 'address', 'climb_rate', 'turn_rate', 'signal_quality', 'error_count', 'frequency_offset', 'gps_quality_horizontal', 'gps_quality_vertical', 'software_version', 'hardware_version', 'real_address', 'signal_power',
- 'distance', 'radial', 'quality', 'agl', 'location_mgrs', 'location_mgrs_short',
- 'receiver_id', 'device_id'))
-
- beacon = AircraftBeacon(**message)
- elif message['beacon_type'] in RECEIVER_BEACON_TYPES:
- if 'rec_crystal_correction' in message:
- del message['rec_crystal_correction']
- del message['rec_crystal_correction_fine']
- beacon = ReceiverBeacon(**message)
- saver.add(beacon)
- except Exception as e:
- print(e)
-
- saver.flush()
- fin.close()
-
-
-@user_cli.command('file_import')
-@click.argument('path')
-def file_import(path):
- """Import APRS logfiles into separate logfile tables."""
-
- import os
- import re
-
- # Get Filepaths and dates to import
- results = list()
- for (root, dirs, files) in os.walk(path):
- for file in sorted(files):
- match = re.match('OGN_log\.txt_([0-9]{4}\-[0-9]{2}\-[0-9]{2})\.gz$', file)
- if match:
- results.append({'filepath': os.path.join(root, file),
- 'datestr': match.group(1)})
-
- with LogfileDbSaver() as saver:
- already_imported = saver.get_datestrs()
-
- results = list(filter(lambda x: x['datestr'] not in already_imported, results))
-
- pbar = tqdm(results)
- for result in pbar:
- filepath = result['filepath']
- datestr = result['datestr']
- pbar.set_description("Importing data for {}".format(datestr))
-
- saver.set_datestr(datestr)
- saver.create_tables()
- convert(filepath, datestr, saver)
- saver.add_missing_devices()
- saver.add_missing_receivers()
diff --git a/ogn_python/live_routes.py b/ogn_python/live_routes.py
deleted file mode 100644
index ecc1b4e..0000000
--- a/ogn_python/live_routes.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from flask import request, render_template, make_response, send_file
-from flask_cors import cross_origin
-
-from ogn_python.backend.liveglidernet import rec, lxml
-
-from ogn_python import app
-from ogn_python import db
-from ogn_python import cache
-
-
-@app.route('/live.html')
-@cross_origin()
-def live():
- return render_template('ogn_live.html', host=request.host)
-
-
-@app.route('/rec.php')
-def rec_php():
- a = request.args.get('a')
- z = request.args.get('z')
-
- xml = rec()
- resp = app.make_response(xml)
- resp.mimetype = "text/xml"
- return resp
-
-
-@app.route('/lxml.php')
-def lxml_php():
- a = request.args.get('a')
- b = request.args.get('b')
- c = request.args.get('c')
- d = request.args.get('d')
- e = request.args.get('e')
- z = request.args.get('z')
-
- xml = lxml()
- resp = app.make_response(xml)
- resp.mimetype = "text/xml"
- return resp
-
-
-@app.route('/pict/')
-def pict(filename):
- return app.send_static_file('ognlive/pict/' + filename)
-
-
-@app.route('/favicon.gif')
-def favicon_gif():
- return app.send_static_file('ognlive/pict/favicon.gif')
-
-
-@app.route('/horizZoomControl.js')
-def horizZoomControl_js():
- return app.send_static_file('ognlive/horizZoomControl.js')
-
-
-@app.route('/barogram.js')
-def barogram_js():
- return app.send_static_file('ognlive/barogram.js')
-
-
-@app.route('/util.js')
-def util_js():
- return app.send_static_file('ognlive/util.js')
-
-
-@app.route('/ogn.js')
-def ogn_js():
- return app.send_static_file('ognlive/ogn.js')
-
-
-@app.route('/ol.js')
-def ol_js():
- return app.send_static_file('ognlive/ol.js')
-
-
-@app.route('/osm.js')
-def osm_js():
- return app.send_static_file('ognlive/osm.js')
-
-
-@app.route('/ol.css')
-def ol_css():
- return app.send_static_file('ognlive/ol.css')
-
-
-@app.route('/osm.css')
-def osm_css():
- return app.send_static_file('ognlive/osm.css')
diff --git a/ogn_python/model/flights2d.py b/ogn_python/model/flights2d.py
deleted file mode 100644
index 2dc3128..0000000
--- a/ogn_python/model/flights2d.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from geoalchemy2.types import Geometry
-
-from ogn_python import db
-
-
-class Flight2D(db.Model):
- __tablename__ = "flights2d"
-
- date = db.Column(db.Date, primary_key=True)
- flight_type = db.Column(db.SmallInteger, primary_key=True)
-
- path_wkt = db.Column('path', Geometry('MULTILINESTRING', srid=4326))
- path_simple_wkt = db.Column('path_simple', Geometry('MULTILINESTRING', srid=4326)) # this is the path simplified with ST_Simplify(path, 0.0001)
-
- # Relations
- device_id = db.Column(db.Integer, db.ForeignKey('devices.id', ondelete='SET NULL'), primary_key=True)
- device = db.relationship('Device', foreign_keys=[device_id], backref='flights2d')
-
- def __repr__(self):
- return "" % (
- self.date,
- self.path_wkt,
- self.path_simple_wkt)
-
-
-db.Index('ix_flights2d_date_device_id', Flight2D.date, Flight2D.device_id)
-#db.Index('ix_flights2d_date_path', Flight2D.date, Flight2D.path_wkt) --> CREATE INDEX ix_flights2d_date_path ON flights2d USING GIST("date", path)
diff --git a/ogn_python/model/relation_stats.py b/ogn_python/model/relation_stats.py
deleted file mode 100644
index 413b755..0000000
--- a/ogn_python/model/relation_stats.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from ogn_python import db
-
-
-class RelationStats(db.Model):
- __tablename__ = "relation_stats"
-
- id = db.Column(db.Integer, primary_key=True)
-
- date = db.Column(db.Date)
-
- # Statistic data
- quality = db.Column(db.Float(precision=2))
- beacon_count = db.Column(db.Integer)
-
- # Relations
- device_id = db.Column(db.Integer, db.ForeignKey('devices.id', ondelete='SET NULL'), index=True)
- device = db.relationship('Device', foreign_keys=[device_id], backref='relation_stats')
- receiver_id = db.Column(db.Integer, db.ForeignKey('receivers.id', ondelete='SET NULL'), index=True)
- receiver = db.relationship('Receiver', foreign_keys=[receiver_id], backref='relation_stats')
-
- def __repr__(self):
- return "" % (
- self.date,
- self.quality,
- self.beacon_count)
-
-
-db.Index('ix_relation_stats_date_device_id', RelationStats.date, RelationStats.device_id, RelationStats.receiver_id)
-db.Index('ix_relation_stats_date_receiver_id', RelationStats.date, RelationStats.receiver_id, RelationStats.device_id)
diff --git a/ogn_python/model/takeoff_landing.py b/ogn_python/model/takeoff_landing.py
deleted file mode 100644
index a463c5c..0000000
--- a/ogn_python/model/takeoff_landing.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from ogn_python import db
-
-
-class TakeoffLanding(db.Model):
- __tablename__ = 'takeoff_landings'
-
- device_id = db.Column(db.Integer, db.ForeignKey('devices.id', ondelete='SET NULL'), primary_key=True)
- airport_id = db.Column(db.Integer, db.ForeignKey('airports.id', ondelete='SET NULL'), primary_key=True)
- timestamp = db.Column(db.DateTime, primary_key=True)
-
- is_takeoff = db.Column(db.Boolean)
- track = db.Column(db.SmallInteger)
-
- # Relations
- airport = db.relationship('Airport', foreign_keys=[airport_id], backref='takeoff_landings')
- device = db.relationship('Device', foreign_keys=[device_id], backref='takeoff_landings', order_by='TakeoffLanding.timestamp')
diff --git a/ogn_python/navigation.py b/ogn_python/navigation.py
deleted file mode 100644
index ec1ce83..0000000
--- a/ogn_python/navigation.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from flask_nav import Nav
-from flask_nav.elements import *
-
-nav = Nav()
-
-# registers the "top" menubar
-nav.register_element('top_menubar', Navbar(
- View('Home', 'index'),
- View('Devices', 'devices'),
- View('Receivers', 'receivers'),
- View('Airports', 'airports'),
- View('Logbook', 'logbook'),
- View('Statistics', 'statistics'),
-))
diff --git a/ogn_python/routes.py b/ogn_python/routes.py
deleted file mode 100644
index f9c688b..0000000
--- a/ogn_python/routes.py
+++ /dev/null
@@ -1,243 +0,0 @@
-import datetime
-
-from flask import request, render_template, send_file
-from flask_cors import cross_origin
-
-from ogn_python import app
-from ogn_python import db
-from ogn_python import cache
-
-from ogn_python.model import *
-
-
-@cache.cached(key_prefix='countries_in_receivers')
-def get_countries_in_receivers():
- query = db.session.query(Country.iso2) \
- .filter(Country.gid == Receiver.country_id) \
- .order_by(Country.iso2) \
- .distinct(Country.iso2)
-
- return [{'iso2': country[0]} for country in query.all()]
-
-
-@cache.cached(key_prefix='countries_in_logbook')
-def get_countries_in_logbook():
- query = db.session.query(Country.iso2) \
- .filter(Country.iso2 == Airport.country_code) \
- .filter(Logbook.takeoff_airport_id == Airport.id) \
- .order_by(Country.iso2) \
- .distinct(Country.iso2)
-
- return [{'iso2': country[0]} for country in query.all()]
-
-
-@cache.memoize()
-def get_airports_in_country(sel_country):
- query = db.session.query(Airport.id, Airport.name) \
- .filter(Airport.country_code == sel_country) \
- .filter(Logbook.takeoff_airport_id == Airport.id) \
- .order_by(Airport.name) \
- .distinct(Airport.name)
-
- return [{'id': airport[0], 'name': airport[1]} for airport in query.all()]
-
-
-@cache.memoize()
-def get_dates_for_airport(sel_airport):
- query = db.session.query(db.func.date(Logbook.reftime), db.func.count(Logbook.id).label('logbook_count')) \
- .filter(Airport.id == sel_airport) \
- .filter(db.or_(Airport.id == Logbook.takeoff_airport_id, Airport.id == Logbook.landing_airport_id)) \
- .group_by(db.func.date(Logbook.reftime)) \
- .order_by(db.func.date(Logbook.reftime).desc())
-
- return [{'date': date, 'logbook_count': logbook_count} for (date, logbook_count) in query.all()]
-
-
-@app.route('/')
-@app.route('/index.html')
-def index():
- return render_template('base.html')
-
-
-@app.route('/devices.html', methods=['GET', 'POST'])
-def devices():
- devices = db.session.query(Device) \
- .order_by(Device.address) \
- .limit(100)
- return render_template('devices.html', devices=devices)
-
-
-@app.route('/device_detail.html', methods=['GET', 'POST'])
-def device_detail():
- device_id = request.args.get('id')
- device = db.session.query(Device) \
- .filter(Device.id == device_id) \
- .one()
-
- return render_template('device_detail.html',
- title='Device',
- device=device)
-
-
-@app.route('/receivers.html')
-def receivers():
- sel_country = request.args.get('country')
-
- countries = get_countries_in_receivers()
-
- # Get receiver selection list
- if sel_country:
- receivers = db.session.query(Receiver) \
- .filter(db.and_(Receiver.country_id == Country.gid, Country.iso2 == sel_country)) \
- .order_by(Receiver.name)
- else:
- receivers = db.session.query(Receiver) \
- .order_by(Receiver.name)
-
- return render_template('receivers.html',
- title='Receivers',
- sel_country=sel_country,
- countries=countries,
- receivers=receivers)
-
-
-@app.route('/receiver_detail.html')
-def receiver_detail():
- sel_receiver_id = request.args.get('receiver_id')
-
- receiver = db.session.query(Receiver) \
- .filter(Receiver.id == sel_receiver_id) \
- .one()
-
- airport = db.session.query(Airport) \
- .filter(db.and_(Receiver.id == sel_receiver_id,
- db.func.st_contains(db.func.st_buffer(Receiver.location_wkt, 0.5), Airport.location_wkt),
- db.func.st_distance_sphere(Airport.location_wkt, Receiver.location_wkt) < 1000)) \
- .filter(Airport.style.in_((2,4,5))) \
-
- return render_template('receiver_detail.html',
- title='Receiver Detail',
- receiver=receiver,
- airport=airport.first())
-
-
-@app.route('/airports.html', methods=['GET', 'POST'])
-def airports():
- sel_country = request.args.get('country')
-
- countries = get_countries_in_logbook()
-
- if sel_country:
- airports = get_airports_in_country(sel_country)
- else:
- airports = []
-
- page = request.args.get('page', 1, type=int)
-
- return render_template('airports.html',
- sel_country=sel_country,
- countries=countries,
- airports=airports)
-
-
-@app.route('/airport_detail.html')
-def airport_detail():
- sel_airport = request.args.get('airport')
-
- airport = db.session.query(Airport) \
- .filter(Airport.id == sel_airport)
-
- devices = db.session.query(Device).join(Logbook) \
- .filter(Logbook.takeoff_airport_id == sel_airport) \
- .order_by(Device.address)
-
- return render_template('airport_detail.html',
- title='Airport Detail',
- airport=airport.one(),
- devices=devices)
-
-
-@app.route('/logbook.html', methods=['GET', 'POST'])
-def logbook():
- sel_country = request.args.get('country')
- sel_airport = request.args.get('airport')
- sel_date = request.args.get('date')
-
- sel_device_id = request.args.get('device_id')
-
- countries = get_countries_in_logbook()
-
- if sel_country:
- airports = get_airports_in_country(sel_country)
- else:
- airports = []
-
- if sel_airport:
- sel_airport = int(sel_airport)
- if sel_airport not in [airport['id'] for airport in airports]:
- sel_airport = None
- sel_date = None
- dates = get_dates_for_airport(sel_airport)
- else:
- dates = []
-
- if sel_date:
- sel_date = datetime.datetime.strptime(sel_date, '%Y-%m-%d').date()
- if sel_date not in [entry['date'] for entry in dates]:
- sel_date = dates[0]['date']
- elif len(dates) > 0:
- sel_date = dates[0]['date']
-
- # Get Logbook
- filters = []
- if sel_airport:
- filters.append(db.or_(Logbook.takeoff_airport_id == sel_airport, Logbook.landing_airport_id == sel_airport))
-
- if sel_date:
- filters.append(db.func.date(Logbook.reftime) == sel_date)
-
- if sel_device_id:
- filters.append(Logbook.device_id == sel_device_id)
-
- if len(filters) > 0:
- logbook = db.session.query(Logbook) \
- .filter(*filters) \
- .order_by(Logbook.reftime)
- else:
- logbook = None
-
- return render_template('logbook.html',
- title='Logbook',
- sel_country=sel_country,
- countries=countries,
- sel_airport=sel_airport,
- airports=airports,
- sel_date=sel_date,
- dates=dates,
- logbook=logbook)
-
-@app.route('/download.html')
-def download_flight():
- from io import StringIO
- buffer = StringIO()
- buffer.write('Moin moin\nAlter Verwalter')
- buffer.seek(0)
-
- return send_file(buffer,
- as_attachment=True,
- attachment_filename='wtf.igc',
- mimetype = 'text/plain')
-
-
-@app.route('/statistics.html')
-def statistics():
-
- today = datetime.date.today()
- today = datetime.date(2018, 7, 31)
-
- receiverstats = db.session.query(ReceiverStats) \
- .filter(ReceiverStats.date == today)
-
- return render_template('statistics.html',
- title='Receiver Statistics',
- receiverstats=receiverstats)
diff --git a/tests/backend/test_backends.py b/tests/backend/test_backends.py
index 51b6b13..2ab2b4a 100644
--- a/tests/backend/test_backends.py
+++ b/tests/backend/test_backends.py
@@ -8,10 +8,10 @@ from xmlunittest import XmlTestMixin
from tests.base import TestBaseDB, db
-from ogn_python.model import AircraftBeacon, AircraftType, Receiver, Device, DeviceInfo, ReceiverCoverage
+from app.model import AircraftBeacon, AircraftType, Receiver, Device, DeviceInfo, ReceiverCoverage
-from ogn_python.backend.liveglidernet import rec, lxml
-from ogn_python.backend.ognrange import stations2_filtered_pl, max_tile_mgrs_pl
+from app.backend.liveglidernet import rec, lxml
+from app.backend.ognrange import stations2_filtered_pl, max_tile_mgrs_pl
class TestDB(TestBaseDB, XmlTestMixin):
@@ -19,50 +19,100 @@ class TestDB(TestBaseDB, XmlTestMixin):
super().setUp()
# Prepare Beacons
- self.r01 = Receiver(name='Koenigsdf', location_wkt='0101000020E610000061E8FED7A6EE26407F20661C10EA4740', lastseen='2017-12-20 10:00:00', altitude=601, version='0.2.5', platform='ARM')
- self.r02 = Receiver(name='Bene', location_wkt='0101000020E6100000D5E76A2BF6C72640D4063A6DA0DB4740', lastseen='2017-12-20 09:45:00', altitude=609, version='0.2.7', platform='x64')
- self.r03 = Receiver(name='Ohlstadt', location_wkt='0101000020E6100000057E678EBF772640A142883E32D44740', lastseen='2017-12-20 10:05:00', altitude=655, version='0.2.6', platform='ARM')
+ self.r01 = Receiver(name="Koenigsdf", location_wkt="0101000020E610000061E8FED7A6EE26407F20661C10EA4740", lastseen="2017-12-20 10:00:00", altitude=601, version="0.2.5", platform="ARM")
+ self.r02 = Receiver(name="Bene", location_wkt="0101000020E6100000D5E76A2BF6C72640D4063A6DA0DB4740", lastseen="2017-12-20 09:45:00", altitude=609, version="0.2.7", platform="x64")
+ self.r03 = Receiver(name="Ohlstadt", location_wkt="0101000020E6100000057E678EBF772640A142883E32D44740", lastseen="2017-12-20 10:05:00", altitude=655, version="0.2.6", platform="ARM")
db.session.add(self.r01)
db.session.add(self.r02)
db.session.add(self.r03)
db.session.commit()
- self.d01 = Device(address='DD4711', lastseen='2017-12-20 10:00:02')
- self.d02 = Device(address='DD0815', lastseen='2017-12-20 09:56:00')
+ self.d01 = Device(address="DD4711", lastseen="2017-12-20 10:00:02")
+ self.d02 = Device(address="DD0815", lastseen="2017-12-20 09:56:00")
db.session.add(self.d01)
db.session.add(self.d02)
db.session.commit()
- self.di01 = DeviceInfo(registration='D-4711', competition='Hi', tracked=True, identified=True)
+ self.di01 = DeviceInfo(registration="D-4711", competition="Hi", tracked=True, identified=True)
db.session.add(self.di01)
db.session.commit()
- self.ab11 = AircraftBeacon(name='FLRDD4711', receiver_name='Koenigsdf', location_wkt='0101000020E6100000211FF46C56ED26402650D7EDC6E94740', aircraft_type=AircraftType.glider_or_motor_glider, timestamp='2017-12-20 10:00:01', track=105, ground_speed=57, climb_rate=-0.5, device_id=self.d01.id)
- self.ab12 = AircraftBeacon(name='FLRDD4711', receiver_name='Koenigsdf', location_wkt='0101000020E6100000806DEA295FED2640347D898BB6E94740', aircraft_type=AircraftType.glider_or_motor_glider, timestamp='2017-12-20 10:00:02', track=123, ground_speed=55, climb_rate=-0.4, altitude=209, device_id=self.d01.id)
- self.ab21 = AircraftBeacon(name='FLRDD0815', receiver_name='Koenigsdf', location_wkt='0101000020E6100000F38B25BF58F22640448B6CE7FBE94740', aircraft_type=AircraftType.powered_aircraft, timestamp='2017-12-20 09:54:30', track=280, ground_speed=80, climb_rate=-2.9, device_id=self.d02.id)
- self.ab22 = AircraftBeacon(name='FLRDD0815', receiver_name='Bene', location_wkt='0101000020E6100000A5E8482EFFF12640DC1EAA16FEE94740', aircraft_type=AircraftType.powered_aircraft, timestamp='2017-12-20 09:56:00', track=270, ground_speed=77, climb_rate=-1.5, altitude=543, device_id=self.d02.id)
+ self.ab11 = AircraftBeacon(
+ name="FLRDD4711",
+ receiver_name="Koenigsdf",
+ location_wkt="0101000020E6100000211FF46C56ED26402650D7EDC6E94740",
+ aircraft_type=AircraftType.glider_or_motor_glider,
+ timestamp="2017-12-20 10:00:01",
+ track=105,
+ ground_speed=57,
+ climb_rate=-0.5,
+ device_id=self.d01.id,
+ )
+ self.ab12 = AircraftBeacon(
+ name="FLRDD4711",
+ receiver_name="Koenigsdf",
+ location_wkt="0101000020E6100000806DEA295FED2640347D898BB6E94740",
+ aircraft_type=AircraftType.glider_or_motor_glider,
+ timestamp="2017-12-20 10:00:02",
+ track=123,
+ ground_speed=55,
+ climb_rate=-0.4,
+ altitude=209,
+ device_id=self.d01.id,
+ )
+ self.ab21 = AircraftBeacon(
+ name="FLRDD0815",
+ receiver_name="Koenigsdf",
+ location_wkt="0101000020E6100000F38B25BF58F22640448B6CE7FBE94740",
+ aircraft_type=AircraftType.powered_aircraft,
+ timestamp="2017-12-20 09:54:30",
+ track=280,
+ ground_speed=80,
+ climb_rate=-2.9,
+ device_id=self.d02.id,
+ )
+ self.ab22 = AircraftBeacon(
+ name="FLRDD0815",
+ receiver_name="Bene",
+ location_wkt="0101000020E6100000A5E8482EFFF12640DC1EAA16FEE94740",
+ aircraft_type=AircraftType.powered_aircraft,
+ timestamp="2017-12-20 09:56:00",
+ track=270,
+ ground_speed=77,
+ climb_rate=-1.5,
+ altitude=543,
+ device_id=self.d02.id,
+ )
db.session.add(self.ab11)
db.session.add(self.ab12)
db.session.add(self.ab21)
db.session.add(self.ab22)
db.session.commit()
- self.rc11 = ReceiverCoverage(location_mgrs_short='32TPU8312', date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01)
- self.rc12 = ReceiverCoverage(location_mgrs_short='32TPU8434', date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01)
- self.rc12 = ReceiverCoverage(location_mgrs_short='32TPU8434', date=date(2017, 12, 21), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01)
- self.rc21 = ReceiverCoverage(location_mgrs_short='32TPU8512', date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r02)
+ self.rc11 = ReceiverCoverage(
+ location_mgrs_short="32TPU8312", date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01
+ )
+ self.rc12 = ReceiverCoverage(
+ location_mgrs_short="32TPU8434", date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01
+ )
+ self.rc12 = ReceiverCoverage(
+ location_mgrs_short="32TPU8434", date=date(2017, 12, 21), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01
+ )
+ self.rc21 = ReceiverCoverage(
+ location_mgrs_short="32TPU8512", date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r02
+ )
db.session.add(self.rc11)
db.session.add(self.rc12)
db.session.add(self.rc21)
db.session.commit()
- @unittest.skip('broken')
+ @unittest.skip("broken")
def test_rec(self):
- data = rec(min_timestamp=datetime(2017, 12, 19, 10, 0), min_online_timestamp=datetime(2017, 12, 20, 10, 0)).encode(encoding='utf-8')
+ data = rec(min_timestamp=datetime(2017, 12, 19, 10, 0), min_online_timestamp=datetime(2017, 12, 20, 10, 0)).encode(encoding="utf-8")
# Check the document
root = self.assertXmlDocument(data)
- self.assertXmlNode(root, tag='markers')
+ self.assertXmlNode(root, tag="markers")
self.assertXpathsOnlyOne(root, ('./m[@a="Koenigsdf"]', './m[@a="Bene"]', './m[@a="Ohlstadt"]'))
# Check the complete document
@@ -73,13 +123,15 @@ class TestDB(TestBaseDB, XmlTestMixin):
- """.encode(encoding='utf-8')
+ """.encode(
+ encoding="utf-8"
+ )
self.assertXmlEquivalentOutputs(data, expected)
- @unittest.skip('broken')
+ @unittest.skip("broken")
def test_lxml(self):
- data = lxml().encode(encoding='utf-8')
+ data = lxml().encode(encoding="utf-8")
# Check the complete document
expected = """
@@ -87,11 +139,13 @@ class TestDB(TestBaseDB, XmlTestMixin):
- """.encode(encoding='utf-8')
+ """.encode(
+ encoding="utf-8"
+ )
self.assertXmlEquivalentOutputs(data, expected)
- @mock.patch('ogn_python.backend.ognrange.datetime')
+ @mock.patch("app.backend.ognrange.datetime")
def test_stations2_filtered_pl(self, datetime_mock):
datetime_mock.utcnow.return_value = datetime(2017, 12, 20, 10, 0)
@@ -105,7 +159,7 @@ class TestDB(TestBaseDB, XmlTestMixin):
s2 = stations[1]
s3 = stations[2]
- self.assertEqual(s1["s"], 'Bene')
+ self.assertEqual(s1["s"], "Bene")
self.assertEqual(s1["lt"], 47.7158)
self.assertEqual(s1["lg"], 11.3906)
self.assertEqual(s1["u"], "D") # Down, because last beacon > 10min. ago
@@ -113,7 +167,7 @@ class TestDB(TestBaseDB, XmlTestMixin):
# self.assertEqual(s1["b"], 0)
self.assertEqual(s1["v"], "0.2.7.x64")
- self.assertEqual(s2["s"], 'Koenigsdf')
+ self.assertEqual(s2["s"], "Koenigsdf")
self.assertEqual(s2["lt"], 47.8286)
self.assertEqual(s2["lg"], 11.4661)
self.assertEqual(s2["u"], "U")
@@ -121,24 +175,24 @@ class TestDB(TestBaseDB, XmlTestMixin):
# self.assertEqual(s2["b"], 0)
self.assertEqual(s2["v"], "0.2.5.ARM")
- self.assertEqual(s3["s"], 'Ohlstadt')
+ self.assertEqual(s3["s"], "Ohlstadt")
def test_max_tile_mgrs_pl(self):
- result = max_tile_mgrs_pl(station='Koenigsdf', start=date(2017, 12, 15), end=date(2017, 12, 25), squares='32TPU')
+ result = max_tile_mgrs_pl(station="Koenigsdf", start=date(2017, 12, 15), end=date(2017, 12, 25), squares="32TPU")
data = json.loads(result)
- self.assertEqual(data['t'], '32TPU')
- self.assertEqual(data['p'][0], '8312/1')
- self.assertEqual(data['p'][1], '8434/2')
+ self.assertEqual(data["t"], "32TPU")
+ self.assertEqual(data["p"][0], "8312/1")
+ self.assertEqual(data["p"][1], "8434/2")
- result = max_tile_mgrs_pl(station='Bene', start=date(2017, 12, 15), end=date(2017, 12, 25), squares='32TPU')
+ result = max_tile_mgrs_pl(station="Bene", start=date(2017, 12, 15), end=date(2017, 12, 25), squares="32TPU")
data = json.loads(result)
- self.assertEqual(data['t'], '32TPU')
- self.assertEqual(data['p'][0], '8512/1')
+ self.assertEqual(data["t"], "32TPU")
+ self.assertEqual(data["p"][0], "8512/1")
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/base.py b/tests/base.py
index 9ee150e..9d81e03 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -1,15 +1,15 @@
import unittest
import os
-os.environ['OGN_CONFIG_MODULE'] = 'config/test.py'
+os.environ["OGN_CONFIG_MODULE"] = "config/test.py"
-from ogn_python import db # noqa: E402
+from app import db # noqa: E402
class TestBaseDB(unittest.TestCase):
@classmethod
def setUpClass(cls):
- db.session.execute('CREATE EXTENSION IF NOT EXISTS postgis;')
+ db.session.execute("CREATE EXTENSION IF NOT EXISTS postgis;")
db.session.commit()
db.drop_all()
db.create_all()
@@ -18,7 +18,8 @@ class TestBaseDB(unittest.TestCase):
pass
def tearDown(self):
- db.session.execute("""
+ db.session.execute(
+ """
DELETE FROM aircraft_beacons;
DELETE FROM receiver_beacons;
DELETE FROM takeoff_landings;
@@ -28,8 +29,9 @@ class TestBaseDB(unittest.TestCase):
DELETE FROM receiver_stats;
DELETE FROM receivers;
DELETE FROM devices;
- """)
+ """
+ )
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/collect/test_database.py b/tests/collect/test_database.py
index b874ae8..4253a1f 100644
--- a/tests/collect/test_database.py
+++ b/tests/collect/test_database.py
@@ -2,26 +2,26 @@ import unittest
from tests.base import TestBaseDB, db
-from ogn_python.model import AircraftBeacon
-from ogn_python.collect.database import upsert
+from app.model import AircraftBeacon
+from app.collect.database import upsert
class TestDatabase(TestBaseDB):
- @unittest.skip('wip')
+ @unittest.skip("wip")
def test_insert_duplicate_beacons(self):
- row1 = {'name': 'FLRDD0815', 'receiver_name': 'Koenigsdf', 'timestamp': '2019-01-26 11:51:00', 'ground_speed': None}
- row2 = {'name': 'FLRDD0815', 'receiver_name': 'Koenigsdf', 'timestamp': '2019-01-26 11:52:00', 'ground_speed': 0}
- row3 = {'name': 'FLRDD0815', 'receiver_name': 'Koenigsdf', 'timestamp': '2019-01-26 11:53:00', 'ground_speed': 1}
- row4 = {'name': 'FLRDD0815', 'receiver_name': 'Koenigsdf', 'timestamp': '2019-01-26 11:54:00', 'ground_speed': None}
+ row1 = {"name": "FLRDD0815", "receiver_name": "Koenigsdf", "timestamp": "2019-01-26 11:51:00", "ground_speed": None}
+ row2 = {"name": "FLRDD0815", "receiver_name": "Koenigsdf", "timestamp": "2019-01-26 11:52:00", "ground_speed": 0}
+ row3 = {"name": "FLRDD0815", "receiver_name": "Koenigsdf", "timestamp": "2019-01-26 11:53:00", "ground_speed": 1}
+ row4 = {"name": "FLRDD0815", "receiver_name": "Koenigsdf", "timestamp": "2019-01-26 11:54:00", "ground_speed": None}
- upsert(session=db.session, model=AircraftBeacon, rows=[row1, row2, row3, row4], update_cols=['ground_speed'])
+ upsert(session=db.session, model=AircraftBeacon, rows=[row1, row2, row3, row4], update_cols=["ground_speed"])
- row5 = {'name': 'FLRDD0815', 'receiver_name': 'Koenigsdf', 'timestamp': '2019-01-26 11:51:00', 'ground_speed': 2}
- row6 = {'name': 'FLRDD0815', 'receiver_name': 'Koenigsdf', 'timestamp': '2019-01-26 11:52:00', 'ground_speed': 3}
- row7 = {'name': 'FLRDD0815', 'receiver_name': 'Koenigsdf', 'timestamp': '2019-01-26 11:53:00', 'ground_speed': None}
- row8 = {'name': 'FLRDD0815', 'receiver_name': 'Koenigsdf', 'timestamp': '2019-01-26 11:54:00', 'ground_speed': None}
+ row5 = {"name": "FLRDD0815", "receiver_name": "Koenigsdf", "timestamp": "2019-01-26 11:51:00", "ground_speed": 2}
+ row6 = {"name": "FLRDD0815", "receiver_name": "Koenigsdf", "timestamp": "2019-01-26 11:52:00", "ground_speed": 3}
+ row7 = {"name": "FLRDD0815", "receiver_name": "Koenigsdf", "timestamp": "2019-01-26 11:53:00", "ground_speed": None}
+ row8 = {"name": "FLRDD0815", "receiver_name": "Koenigsdf", "timestamp": "2019-01-26 11:54:00", "ground_speed": None}
- upsert(session=db.session, model=AircraftBeacon, rows=[row5, row6, row7, row8], update_cols=['ground_speed'])
+ upsert(session=db.session, model=AircraftBeacon, rows=[row5, row6, row7, row8], update_cols=["ground_speed"])
result = db.session.query(AircraftBeacon).order_by(AircraftBeacon.timestamp).all()
self.assertEqual(result[0].ground_speed, 2)
@@ -30,5 +30,5 @@ class TestDatabase(TestBaseDB):
self.assertEqual(result[3].ground_speed, None)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/collect/test_logbook.py b/tests/collect/test_logbook.py
index 83c1e62..62020cf 100644
--- a/tests/collect/test_logbook.py
+++ b/tests/collect/test_logbook.py
@@ -3,18 +3,18 @@ import unittest
from tests.base import TestBaseDB, db
-from ogn_python.model import Logbook, Airport, Device, TakeoffLanding
-from ogn_python.collect.logbook import update_entries
+from app.model import Logbook, Airport, Device, TakeoffLanding
+from app.collect.logbook import update_entries
class TestLogbook(TestBaseDB):
def setUp(self):
# Create basic data and insert
- self.dd0815 = Device(address='DD0815')
- self.dd4711 = Device(address='DD4711')
+ self.dd0815 = Device(address="DD0815")
+ self.dd4711 = Device(address="DD4711")
- self.koenigsdorf = Airport(name='Koenigsdorf')
- self.ohlstadt = Airport(name='Ohlstadt')
+ self.koenigsdorf = Airport(name="Koenigsdorf")
+ self.ohlstadt = Airport(name="Ohlstadt")
db.session.add(self.dd0815)
db.session.add(self.dd4711)
@@ -24,10 +24,10 @@ class TestLogbook(TestBaseDB):
db.session.commit()
# Prepare takeoff and landings
- self.takeoff_koenigsdorf_dd0815 = TakeoffLanding(is_takeoff=True, timestamp='2016-06-01 10:00:00', airport_id=self.koenigsdorf.id, device_id=self.dd0815.id)
- self.landing_koenigsdorf_dd0815 = TakeoffLanding(is_takeoff=False, timestamp='2016-06-01 10:05:00', airport_id=self.koenigsdorf.id, device_id=self.dd0815.id)
- self.landing_koenigsdorf_dd0815_later = TakeoffLanding(is_takeoff=False, timestamp='2016-06-02 10:05:00', airport_id=self.koenigsdorf.id, device_id=self.dd0815.id)
- self.takeoff_ohlstadt_dd4711 = TakeoffLanding(is_takeoff=True, timestamp='2016-06-01 10:00:00', airport_id=self.ohlstadt.id, device_id=self.dd4711.id)
+ self.takeoff_koenigsdorf_dd0815 = TakeoffLanding(is_takeoff=True, timestamp="2016-06-01 10:00:00", airport_id=self.koenigsdorf.id, device_id=self.dd0815.id)
+ self.landing_koenigsdorf_dd0815 = TakeoffLanding(is_takeoff=False, timestamp="2016-06-01 10:05:00", airport_id=self.koenigsdorf.id, device_id=self.dd0815.id)
+ self.landing_koenigsdorf_dd0815_later = TakeoffLanding(is_takeoff=False, timestamp="2016-06-02 10:05:00", airport_id=self.koenigsdorf.id, device_id=self.dd0815.id)
+ self.takeoff_ohlstadt_dd4711 = TakeoffLanding(is_takeoff=True, timestamp="2016-06-01 10:00:00", airport_id=self.ohlstadt.id, device_id=self.dd4711.id)
def get_logbook_entries(self):
return db.session.query(Logbook).order_by(Logbook.takeoff_airport_id, Logbook.reftime).all()
@@ -161,5 +161,5 @@ class TestLogbook(TestBaseDB):
self.assertEqual(entries, entries2)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/collect/test_ognrange.py b/tests/collect/test_ognrange.py
index 690361f..e01b001 100644
--- a/tests/collect/test_ognrange.py
+++ b/tests/collect/test_ognrange.py
@@ -3,8 +3,8 @@ import unittest
from tests.base import TestBaseDB, db
-from ogn_python.model import AircraftBeacon, Receiver, ReceiverCoverage, Device
-from ogn_python.collect.ognrange import update_entries
+from app.model import AircraftBeacon, Receiver, ReceiverCoverage, Device
+from app.collect.ognrange import update_entries
class TestOGNrange(TestBaseDB):
@@ -12,11 +12,11 @@ class TestOGNrange(TestBaseDB):
super().setUp()
# Create basic data and insert
- self.dd0815 = Device(address='DD0815')
- self.dd4711 = Device(address='DD4711')
+ self.dd0815 = Device(address="DD0815")
+ self.dd4711 = Device(address="DD4711")
- self.r01 = Receiver(name='Koenigsdf')
- self.r02 = Receiver(name='Bene')
+ self.r01 = Receiver(name="Koenigsdf")
+ self.r02 = Receiver(name="Bene")
db.session.add(self.dd0815)
db.session.add(self.dd4711)
@@ -26,8 +26,12 @@ class TestOGNrange(TestBaseDB):
db.session.commit()
# Create beacons and insert
- self.ab01 = AircraftBeacon(name='FLRDD0815', receiver_name='Koenigsdf', device_id=self.dd0815.id, receiver_id=self.r01.id, timestamp='2017-12-10 10:00:00', location_mgrs_short='89ABC1267', altitude=800)
- self.ab02 = AircraftBeacon(name='FLRDD0815', receiver_name='Koenigsdf', device_id=self.dd0815.id, receiver_id=self.r01.id, timestamp='2017-12-10 10:00:01', location_mgrs_short='89ABC1267', altitude=850)
+ self.ab01 = AircraftBeacon(
+ name="FLRDD0815", receiver_name="Koenigsdf", device_id=self.dd0815.id, receiver_id=self.r01.id, timestamp="2017-12-10 10:00:00", location_mgrs_short="89ABC1267", altitude=800
+ )
+ self.ab02 = AircraftBeacon(
+ name="FLRDD0815", receiver_name="Koenigsdf", device_id=self.dd0815.id, receiver_id=self.r01.id, timestamp="2017-12-10 10:00:01", location_mgrs_short="89ABC1267", altitude=850
+ )
db.session.add(self.ab01)
db.session.add(self.ab02)
db.session.commit()
@@ -38,11 +42,11 @@ class TestOGNrange(TestBaseDB):
coverages = db.session.query(ReceiverCoverage).all()
self.assertEqual(len(coverages), 1)
coverage = coverages[0]
- self.assertEqual(coverage.location_mgrs_short, '89ABC1267')
+ self.assertEqual(coverage.location_mgrs_short, "89ABC1267")
self.assertEqual(coverage.receiver_id, self.r01.id)
self.assertEqual(coverage.min_altitude, 800)
self.assertEqual(coverage.max_altitude, 850)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/collect/test_stats.py b/tests/collect/test_stats.py
index 2cfd541..a2215df 100644
--- a/tests/collect/test_stats.py
+++ b/tests/collect/test_stats.py
@@ -3,9 +3,9 @@ import unittest
from tests.base import TestBaseDB, db
-from ogn_python.model import AircraftBeacon, ReceiverBeacon, Receiver, Device, DeviceStats
+from app.model import AircraftBeacon, ReceiverBeacon, Receiver, Device, DeviceStats
-from ogn_python.collect.stats import create_device_stats
+from app.collect.stats import create_device_stats
class TestStats(TestBaseDB):
@@ -13,21 +13,21 @@ class TestStats(TestBaseDB):
super().setUp()
# Prepare Beacons
- self.ab01 = AircraftBeacon(name='FLRDD4711', receiver_name='Koenigsdf', timestamp='2017-12-10 10:00:01')
- self.ab02 = AircraftBeacon(name='FLRDD4711', receiver_name='Koenigsdf', timestamp='2017-12-10 10:00:02')
- self.ab03 = AircraftBeacon(name='FLRDD4711', receiver_name='Koenigsdf', timestamp='2017-12-10 10:00:03')
- self.ab04 = AircraftBeacon(name='FLRDD4711', receiver_name='Koenigsdf', timestamp='2017-12-10 10:00:04')
- self.ab05 = AircraftBeacon(name='FLRDD4711', receiver_name='Koenigsdf', timestamp='2017-12-10 10:00:05')
- self.ab06 = AircraftBeacon(name='FLRDD4711', receiver_name='Koenigsdf', timestamp='2017-12-10 10:00:05')
+ self.ab01 = AircraftBeacon(name="FLRDD4711", receiver_name="Koenigsdf", timestamp="2017-12-10 10:00:01")
+ self.ab02 = AircraftBeacon(name="FLRDD4711", receiver_name="Koenigsdf", timestamp="2017-12-10 10:00:02")
+ self.ab03 = AircraftBeacon(name="FLRDD4711", receiver_name="Koenigsdf", timestamp="2017-12-10 10:00:03")
+ self.ab04 = AircraftBeacon(name="FLRDD4711", receiver_name="Koenigsdf", timestamp="2017-12-10 10:00:04")
+ self.ab05 = AircraftBeacon(name="FLRDD4711", receiver_name="Koenigsdf", timestamp="2017-12-10 10:00:05")
+ self.ab06 = AircraftBeacon(name="FLRDD4711", receiver_name="Koenigsdf", timestamp="2017-12-10 10:00:05")
- self.rb01 = ReceiverBeacon(name='Koenigsdf', receiver_name='GLIDERN1', timestamp='2017-12-10 09:55:00', altitude=601, version='0.2.5', platform='ARM')
- self.rb02 = ReceiverBeacon(name='Koenigsdf', receiver_name='GLIDERN1', timestamp='2017-12-10 10:00:00', altitude=601, version='0.2.7', platform='ARM')
- self.rb03 = ReceiverBeacon(name='Koenigsdf', receiver_name='GLIDERN1', timestamp='2017-12-10 10:05:00', altitude=601, version='0.2.6', platform='ARM')
+ self.rb01 = ReceiverBeacon(name="Koenigsdf", receiver_name="GLIDERN1", timestamp="2017-12-10 09:55:00", altitude=601, version="0.2.5", platform="ARM")
+ self.rb02 = ReceiverBeacon(name="Koenigsdf", receiver_name="GLIDERN1", timestamp="2017-12-10 10:00:00", altitude=601, version="0.2.7", platform="ARM")
+ self.rb03 = ReceiverBeacon(name="Koenigsdf", receiver_name="GLIDERN1", timestamp="2017-12-10 10:05:00", altitude=601, version="0.2.6", platform="ARM")
- self.r01 = Receiver(name='Koenigsdf')
- self.r02 = Receiver(name='Bene')
+ self.r01 = Receiver(name="Koenigsdf")
+ self.r02 = Receiver(name="Bene")
- self.d01 = Device(address='DD4711')
+ self.d01 = Device(address="DD4711")
db.session.add(self.r01)
db.session.add(self.d01)
@@ -50,7 +50,7 @@ class TestStats(TestBaseDB):
self.assertEqual(devicestats[0].max_altitude, None)
self.assertEqual(devicestats[0].receiver_count, 1)
self.assertEqual(devicestats[0].aircraft_beacon_count, 1)
- self.assertEqual(devicestats[0].date, datetime.strptime('2017-12-10', '%Y-%m-%d').date())
+ self.assertEqual(devicestats[0].date, datetime.strptime("2017-12-10", "%Y-%m-%d").date())
self.assertEqual(devicestats[0].firstseen, datetime(2017, 12, 10, 10, 0, 1))
self.assertEqual(devicestats[0].lastseen, datetime(2017, 12, 10, 10, 0, 1))
self.assertEqual(devicestats[0].aircraft_type, None)
@@ -77,7 +77,7 @@ class TestStats(TestBaseDB):
self.assertEqual(devicestats[0].max_altitude, 200)
self.assertEqual(devicestats[0].receiver_count, 1)
self.assertEqual(devicestats[0].aircraft_beacon_count, 2)
- self.assertEqual(devicestats[0].date, datetime.strptime('2017-12-10', '%Y-%m-%d').date())
+ self.assertEqual(devicestats[0].date, datetime.strptime("2017-12-10", "%Y-%m-%d").date())
self.assertEqual(devicestats[0].firstseen, datetime(2017, 12, 10, 10, 0, 1))
self.assertEqual(devicestats[0].lastseen, datetime(2017, 12, 10, 10, 0, 2))
self.assertEqual(devicestats[0].aircraft_type, 3)
@@ -103,7 +103,7 @@ class TestStats(TestBaseDB):
self.assertEqual(devicestats[0].max_altitude, 200)
self.assertEqual(devicestats[0].receiver_count, 1)
self.assertEqual(devicestats[0].aircraft_beacon_count, 2)
- self.assertEqual(devicestats[0].date, datetime.strptime('2017-12-10', '%Y-%m-%d').date())
+ self.assertEqual(devicestats[0].date, datetime.strptime("2017-12-10", "%Y-%m-%d").date())
self.assertEqual(devicestats[0].firstseen, datetime(2017, 12, 10, 10, 0, 1))
self.assertEqual(devicestats[0].lastseen, datetime(2017, 12, 10, 10, 0, 2))
self.assertEqual(devicestats[0].aircraft_type, 3)
@@ -118,7 +118,7 @@ class TestStats(TestBaseDB):
self.ab04.altitude = 250
self.ab04.software_version = 6.01
self.ab04.hardware_version = 15
- self.ab04.real_address = 'DDALFA'
+ self.ab04.real_address = "DDALFA"
db.session.add(self.ab04)
db.session.commit()
@@ -131,14 +131,14 @@ class TestStats(TestBaseDB):
self.assertEqual(devicestats[0].max_altitude, 250)
self.assertEqual(devicestats[0].receiver_count, 2)
self.assertEqual(devicestats[0].aircraft_beacon_count, 3)
- self.assertEqual(devicestats[0].date, datetime.strptime('2017-12-10', '%Y-%m-%d').date())
+ self.assertEqual(devicestats[0].date, datetime.strptime("2017-12-10", "%Y-%m-%d").date())
self.assertEqual(devicestats[0].firstseen, datetime(2017, 12, 10, 10, 0, 1))
self.assertEqual(devicestats[0].lastseen, datetime(2017, 12, 10, 10, 0, 4))
self.assertEqual(devicestats[0].aircraft_type, 3)
self.assertEqual(devicestats[0].stealth, False)
self.assertEqual(devicestats[0].software_version, 6.01)
self.assertEqual(devicestats[0].hardware_version, 15)
- self.assertEqual(devicestats[0].real_address, 'DDALFA')
+ self.assertEqual(devicestats[0].real_address, "DDALFA")
# Compute 5. beacon: lower altitude, stealth
self.ab05.device = self.d01
@@ -157,14 +157,14 @@ class TestStats(TestBaseDB):
self.assertEqual(devicestats[0].max_altitude, 250)
self.assertEqual(devicestats[0].receiver_count, 2)
self.assertEqual(devicestats[0].aircraft_beacon_count, 4)
- self.assertEqual(devicestats[0].date, datetime.strptime('2017-12-10', '%Y-%m-%d').date())
+ self.assertEqual(devicestats[0].date, datetime.strptime("2017-12-10", "%Y-%m-%d").date())
self.assertEqual(devicestats[0].firstseen, datetime(2017, 12, 10, 10, 0, 1))
self.assertEqual(devicestats[0].lastseen, datetime(2017, 12, 10, 10, 0, 5))
self.assertEqual(devicestats[0].aircraft_type, 3)
self.assertEqual(devicestats[0].stealth, True)
self.assertEqual(devicestats[0].software_version, 6.01)
self.assertEqual(devicestats[0].hardware_version, 15)
- self.assertEqual(devicestats[0].real_address, 'DDALFA')
+ self.assertEqual(devicestats[0].real_address, "DDALFA")
# Compute 6. beacon: beacon from past, greater altitude, newer version
self.ab06.device = self.d01
@@ -184,15 +184,15 @@ class TestStats(TestBaseDB):
self.assertEqual(devicestats[0].max_altitude, 300)
self.assertEqual(devicestats[0].receiver_count, 2)
self.assertEqual(devicestats[0].aircraft_beacon_count, 5)
- self.assertEqual(devicestats[0].date, datetime.strptime('2017-12-10', '%Y-%m-%d').date())
+ self.assertEqual(devicestats[0].date, datetime.strptime("2017-12-10", "%Y-%m-%d").date())
self.assertEqual(devicestats[0].firstseen, datetime(2017, 12, 10, 9, 59, 50))
self.assertEqual(devicestats[0].lastseen, datetime(2017, 12, 10, 10, 0, 5))
self.assertEqual(devicestats[0].aircraft_type, 3)
self.assertEqual(devicestats[0].stealth, True)
self.assertEqual(devicestats[0].software_version, 6.01)
self.assertEqual(devicestats[0].hardware_version, 15)
- self.assertEqual(devicestats[0].real_address, 'DDALFA')
+ self.assertEqual(devicestats[0].real_address, "DDALFA")
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/collect/test_takeoff_landing.py b/tests/collect/test_takeoff_landing.py
index 63b54e3..d099d7e 100644
--- a/tests/collect/test_takeoff_landing.py
+++ b/tests/collect/test_takeoff_landing.py
@@ -3,9 +3,9 @@ import unittest
from tests.base import TestBaseDB, db
-from ogn_python.model import TakeoffLanding
+from app.model import TakeoffLanding
-from ogn_python.collect.takeoff_landings import update_entries
+from app.collect.takeoff_landings import update_entries
class TestTakeoffLanding(TestBaseDB):
@@ -24,63 +24,154 @@ class TestTakeoffLanding(TestBaseDB):
def test_broken_rope(self):
"""Fill the db with a winch launch where the rope breaks. The algorithm should detect one takeoff and one landing."""
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009668B61829F12640330E0887F1E94740',604,'2016-07-02 10:47:12',0,0,0,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009668B61829F12640330E0887F1E94740',605,'2016-07-02 10:47:32',0,0,-0.096520193,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009668B61829F12640330E0887F1E94740',606,'2016-07-02 10:47:52',0,0,-0.096520193,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009668B61829F12640330E0887F1E94740',606,'2016-07-02 10:48:12',0,0,-0.096520193,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000001B2FDD2406F12640E53C762AF3E94740',606,'2016-07-02 10:48:24',284,51.85598112,0.299720599,0.1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000F594AFDEBBF02640623583E5F5E94740',610,'2016-07-02 10:48:26',282,88.89596764,4.729489459,-0.2)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000001C0DE02D90F026401564F188F7E94740',619,'2016-07-02 10:48:27',281,94.45196562,10.66294133,-0.3)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000ABF1D24D62F02640E12D90A0F8E94740',632,'2016-07-02 10:48:28',278,88.89596764,15.59055118,-0.7)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000069FD40CC38F02640C7925F2CF9E94740',650,'2016-07-02 10:48:29',273,83.33996966,18.90779782,-0.7)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000002709AF4A0FF02640C7925F2CF9E94740',670,'2016-07-02 10:48:30',272,79.63597101,20.72136144,-0.3)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000007AA85AF8E7EF2640C7925F2CF9E94740',691,'2016-07-02 10:48:31',269,79.63597101,21.02108204,-0.4)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000068DB43D5C2EF2640E12D90A0F8E94740',712,'2016-07-02 10:48:32',267,74.07997303,21.62560325,-0.5)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000EDA16AE19FEF2640FBC8C014F8E94740',728,'2016-07-02 10:48:33',266,68.52397506,12.36982474,-0.1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000000AFCCE1C7FEF26401564F188F7E94740',733,'2016-07-02 10:48:34',266,68.52397506,2.21488443,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000275633585EEF26402FFF21FDF6E94740',731,'2016-07-02 10:48:35',267,68.52397506,-3.916687833,0.2)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000015891C3539EF26402FFF21FDF6E94740',726,'2016-07-02 10:48:36',270,74.07997303,-6.329692659,1.1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000E63FA4DFBEEE264078C1CDCFFAE94740',712,'2016-07-02 10:48:39',280,88.89596764,-2.611125222,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000004FF9EABD0BEE2640448B6CE7FBE94740',706,'2016-07-02 10:48:43',256,90.74796697,-0.198120396,-2.5)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000046B921B3A0ED264003E78C28EDE94740',706,'2016-07-02 10:48:46',218,92.59996629,-0.198120396,-1.6)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000005C58F3177ED2640900C4C81DFE94740',703,'2016-07-02 10:48:48',202,96.30396495,-1.402082804,-1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000211FF46C56ED26402650D7EDC6E94740',702,'2016-07-02 10:48:51',188,100.0079636,0.502921006,-1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000806DEA295FED2640347D898BB6E94740',704,'2016-07-02 10:48:53',166,100.0079636,0.802641605,-2)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000337D898BB6ED26401383C0CAA1E94740',703,'2016-07-02 10:48:56',133,101.8599629,-1.803403607,-1.7)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000000C05593CE2ED2640FDF675E09CE94740',700,'2016-07-02 10:48:57',123,103.7119622,-2.611125222,-1.4)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000F0CCF1F778EE26409FA87F2394E94740',693,'2016-07-02 10:49:00',105,111.1199596,-2.809245618,-0.6)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000C9073D9B55EF2640BD5296218EE94740',687,'2016-07-02 10:49:04',97,112.9719589,-1.605283211,-0.1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000006F8104C5EF26400C24287E8CE94740',682,'2016-07-02 10:49:06',97,114.8239582,-2.407924816,-0.2)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000A0648535A8F02640F597DD9387E94740',676,'2016-07-02 10:49:10',97,118.5279569,-1.402082804,0.1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000D70FC48C03F22640621386EE7FE94740',672,'2016-07-02 10:49:16',97,116.6759575,-1.000762002,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000A72C431CEBF22640CB7F48BF7DE94740',666,'2016-07-02 10:49:20',84,114.8239582,-1.605283211,-1.5)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000BFCAA145B6F32640BD5296218EE94740',662,'2016-07-02 10:49:24',49,111.1199596,-1.203962408,-1.5)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000074DA40A70DF4264077E09C11A5E94740',659,'2016-07-02 10:49:27',23,107.4159609,-1.402082804,-1.4)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009AE3EFF11CF42640347D898BB6E94740',656,'2016-07-02 10:49:29',4,101.8599629,-0.797561595,-1.8)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000074DA40A70DF426402650D7EDC6E94740',654,'2016-07-02 10:49:31',347,101.8599629,-1.706883414,-1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000156A4DF38EF3264086EE7F6DEAE94740',649,'2016-07-02 10:49:36',312,98.15596427,-1.503683007,-1.4)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000FAEDEBC039F32640E53C762AF3E94740',644,'2016-07-02 10:49:38',295,96.30396495,-3.012446025,-1.2)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000B04A0F30E0F22640FBC8C014F8E94740',635,'2016-07-02 10:49:40',284,94.45196562,-5.125730251,-0.7)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000F38B25BF58F22640448B6CE7FBE94740',623,'2016-07-02 10:49:43',279,92.59996629,-2.809245618,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000A5E8482EFFF12640DC1EAA16FEE94740',617,'2016-07-02 10:49:45',279,88.89596764,-3.312166624,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009F17012859F12640F0AAF40003EA4740',607,'2016-07-02 10:49:49',279,81.48797034,-1.300482601,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000004B5658830AF12640873E323005EA4740',607,'2016-07-02 10:49:51',278,74.07997303,-0.294640589,-0.1)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000A0648535A8F0264006373FEB07EA4740',605,'2016-07-02 10:49:54',280,61.11597775,-0.096520193,0.5)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000C74B378941F02640E88C28ED0DEA4740',604,'2016-07-02 10:49:58',292,48.15198247,0.101600203,0.4)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000001B5A643BDFEF264045DB1EAA16EA4740',604,'2016-07-02 10:50:04',302,25.92799056,0.203200406,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000042D2948AB3EF264074029A081BEA4740',604,'2016-07-02 10:50:10',300,5.555997978,0.101600203,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000013AB192CAFEF264074029A081BEA4740',603,'2016-07-02 10:50:16',0,0,-0.096520193,0)")
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009668B61829F12640330E0887F1E94740',604,'2016-07-02 10:47:12',0,0,0,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009668B61829F12640330E0887F1E94740',605,'2016-07-02 10:47:32',0,0,-0.096520193,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009668B61829F12640330E0887F1E94740',606,'2016-07-02 10:47:52',0,0,-0.096520193,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009668B61829F12640330E0887F1E94740',606,'2016-07-02 10:48:12',0,0,-0.096520193,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000001B2FDD2406F12640E53C762AF3E94740',606,'2016-07-02 10:48:24',284,51.85598112,0.299720599,0.1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000F594AFDEBBF02640623583E5F5E94740',610,'2016-07-02 10:48:26',282,88.89596764,4.729489459,-0.2)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000001C0DE02D90F026401564F188F7E94740',619,'2016-07-02 10:48:27',281,94.45196562,10.66294133,-0.3)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000ABF1D24D62F02640E12D90A0F8E94740',632,'2016-07-02 10:48:28',278,88.89596764,15.59055118,-0.7)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000069FD40CC38F02640C7925F2CF9E94740',650,'2016-07-02 10:48:29',273,83.33996966,18.90779782,-0.7)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000002709AF4A0FF02640C7925F2CF9E94740',670,'2016-07-02 10:48:30',272,79.63597101,20.72136144,-0.3)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000007AA85AF8E7EF2640C7925F2CF9E94740',691,'2016-07-02 10:48:31',269,79.63597101,21.02108204,-0.4)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000068DB43D5C2EF2640E12D90A0F8E94740',712,'2016-07-02 10:48:32',267,74.07997303,21.62560325,-0.5)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000EDA16AE19FEF2640FBC8C014F8E94740',728,'2016-07-02 10:48:33',266,68.52397506,12.36982474,-0.1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000000AFCCE1C7FEF26401564F188F7E94740',733,'2016-07-02 10:48:34',266,68.52397506,2.21488443,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000275633585EEF26402FFF21FDF6E94740',731,'2016-07-02 10:48:35',267,68.52397506,-3.916687833,0.2)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000015891C3539EF26402FFF21FDF6E94740',726,'2016-07-02 10:48:36',270,74.07997303,-6.329692659,1.1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000E63FA4DFBEEE264078C1CDCFFAE94740',712,'2016-07-02 10:48:39',280,88.89596764,-2.611125222,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000004FF9EABD0BEE2640448B6CE7FBE94740',706,'2016-07-02 10:48:43',256,90.74796697,-0.198120396,-2.5)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000046B921B3A0ED264003E78C28EDE94740',706,'2016-07-02 10:48:46',218,92.59996629,-0.198120396,-1.6)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000005C58F3177ED2640900C4C81DFE94740',703,'2016-07-02 10:48:48',202,96.30396495,-1.402082804,-1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000211FF46C56ED26402650D7EDC6E94740',702,'2016-07-02 10:48:51',188,100.0079636,0.502921006,-1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000806DEA295FED2640347D898BB6E94740',704,'2016-07-02 10:48:53',166,100.0079636,0.802641605,-2)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000337D898BB6ED26401383C0CAA1E94740',703,'2016-07-02 10:48:56',133,101.8599629,-1.803403607,-1.7)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000000C05593CE2ED2640FDF675E09CE94740',700,'2016-07-02 10:48:57',123,103.7119622,-2.611125222,-1.4)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000F0CCF1F778EE26409FA87F2394E94740',693,'2016-07-02 10:49:00',105,111.1199596,-2.809245618,-0.6)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000C9073D9B55EF2640BD5296218EE94740',687,'2016-07-02 10:49:04',97,112.9719589,-1.605283211,-0.1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000006F8104C5EF26400C24287E8CE94740',682,'2016-07-02 10:49:06',97,114.8239582,-2.407924816,-0.2)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000A0648535A8F02640F597DD9387E94740',676,'2016-07-02 10:49:10',97,118.5279569,-1.402082804,0.1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000D70FC48C03F22640621386EE7FE94740',672,'2016-07-02 10:49:16',97,116.6759575,-1.000762002,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000A72C431CEBF22640CB7F48BF7DE94740',666,'2016-07-02 10:49:20',84,114.8239582,-1.605283211,-1.5)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000BFCAA145B6F32640BD5296218EE94740',662,'2016-07-02 10:49:24',49,111.1199596,-1.203962408,-1.5)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000074DA40A70DF4264077E09C11A5E94740',659,'2016-07-02 10:49:27',23,107.4159609,-1.402082804,-1.4)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009AE3EFF11CF42640347D898BB6E94740',656,'2016-07-02 10:49:29',4,101.8599629,-0.797561595,-1.8)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000074DA40A70DF426402650D7EDC6E94740',654,'2016-07-02 10:49:31',347,101.8599629,-1.706883414,-1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000156A4DF38EF3264086EE7F6DEAE94740',649,'2016-07-02 10:49:36',312,98.15596427,-1.503683007,-1.4)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000FAEDEBC039F32640E53C762AF3E94740',644,'2016-07-02 10:49:38',295,96.30396495,-3.012446025,-1.2)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000B04A0F30E0F22640FBC8C014F8E94740',635,'2016-07-02 10:49:40',284,94.45196562,-5.125730251,-0.7)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000F38B25BF58F22640448B6CE7FBE94740',623,'2016-07-02 10:49:43',279,92.59996629,-2.809245618,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000A5E8482EFFF12640DC1EAA16FEE94740',617,'2016-07-02 10:49:45',279,88.89596764,-3.312166624,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000009F17012859F12640F0AAF40003EA4740',607,'2016-07-02 10:49:49',279,81.48797034,-1.300482601,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000004B5658830AF12640873E323005EA4740',607,'2016-07-02 10:49:51',278,74.07997303,-0.294640589,-0.1)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000A0648535A8F0264006373FEB07EA4740',605,'2016-07-02 10:49:54',280,61.11597775,-0.096520193,0.5)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E6100000C74B378941F02640E88C28ED0DEA4740',604,'2016-07-02 10:49:58',292,48.15198247,0.101600203,0.4)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E61000001B5A643BDFEF264045DB1EAA16EA4740',604,'2016-07-02 10:50:04',302,25.92799056,0.203200406,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000042D2948AB3EF264074029A081BEA4740',604,'2016-07-02 10:50:10',300,5.555997978,0.101600203,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDEFF7', 'Koenigsdf', 'DDEFF7','0101000020E610000013AB192CAFEF264074029A081BEA4740',603,'2016-07-02 10:50:16',0,0,-0.096520193,0)"
+ )
db.session.execute("UPDATE aircraft_beacons SET device_id = d.id, agl = altitude - 600 FROM devices d WHERE d.address='DDEFF7'")
db.session.commit()
# find the takeoff and the landing
update_entries(db.session, start=datetime.datetime(2016, 7, 2, 0, 0, 0), end=datetime.datetime(2016, 7, 2, 23, 59, 59))
- takeoff_landing_query = db.session.query(TakeoffLanding) \
- .filter(db.between(TakeoffLanding.timestamp, datetime.datetime(2016, 7, 2, 0, 0, 0), datetime.datetime(2016, 7, 2, 23, 59, 59)))
+ takeoff_landing_query = db.session.query(TakeoffLanding).filter(db.between(TakeoffLanding.timestamp, datetime.datetime(2016, 7, 2, 0, 0, 0), datetime.datetime(2016, 7, 2, 23, 59, 59)))
self.assertEqual(len(takeoff_landing_query.all()), 2)
for entry in takeoff_landing_query.all():
- self.assertEqual(entry.airport.name, 'Koenigsdorf')
+ self.assertEqual(entry.airport.name, "Koenigsdorf")
# we should not find the takeoff and the landing again
update_entries(db.session, start=datetime.datetime(2016, 7, 2, 0, 0, 0), end=datetime.datetime(2016, 7, 2, 23, 59, 59))
@@ -89,88 +180,224 @@ class TestTakeoffLanding(TestBaseDB):
def test_broken_rope_with_stall(self):
"""Here we have a broken rope where the glider passes again the threshold for take off."""
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',597,'2019-04-13 09:20:14',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',595,'2019-04-13 09:20:23',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',595,'2019-04-13 09:20:29',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',591,'2019-04-13 09:21:01',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',591,'2019-04-13 09:21:02',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',589,'2019-04-13 09:21:13',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',589,'2019-04-13 09:21:29',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',590,'2019-04-13 09:21:48',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',591,'2019-04-13 09:22:02',0,0,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',592,'2019-04-13 09:22:22',0,0,0.1016,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000ED0DBE3099EA2640CA32C4B12EEA4740',593,'2019-04-13 09:22:40',102,25.925552,0.2032,0.60000002)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000026E4839ECDEA26401904560E2DEA4740',594,'2019-04-13 09:22:42',100,68.517532,0.2032,-0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D044D8F0F4EA2640513AB7F62BEA4740',595,'2019-04-13 09:22:43',101,81.480309,1.91008,-0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000025396A721EEB2640A00B49532AEA4740',600,'2019-04-13 09:22:44',100,90.739433,5.6337199,-0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000009E8B4814EEB2640CA41AA3B29EA4740',608,'2019-04-13 09:22:45',100,88.887611,9.2557602,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000087084327AEB264019133C9827EA4740',620,'2019-04-13 09:22:46',99,87.035782,12.3698,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000246416B4A3EB264052499D8026EA4740',634,'2019-04-13 09:22:47',97,83.33213,15.2908,-0.89999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000007958A835CDEB264067E4CDF425EA4740',650,'2019-04-13 09:22:48',94,79.628487,16.093439,-2.0999999)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CE4C3AB7F6EB264067E4CDF425EA4740',667,'2019-04-13 09:22:49',91,75.924835,16.89608,-0.89999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000248613AB19EC264067E4CDF425EA4740',684,'2019-04-13 09:22:50',91,72.221184,17.20088,-0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005C532ACE3EEC264067E4CDF425EA4740',701,'2019-04-13 09:22:51',90,68.517532,16.89608,-0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000003FF9C5925FEC264067E4CDF425EA4740',718,'2019-04-13 09:22:52',91,68.517532,16.19504,-0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000229F615780EC264052499D8026EA4740',733,'2019-04-13 09:22:53',89,59.258408,14.28496,-1.5)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B11D82BD9CEC264052499D8026EA4740',741,'2019-04-13 09:22:54',89,57.406582,3.62204,0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000789CA223B9EC264067E4CDF425EA4740',736,'2019-04-13 09:22:55',88,53.70293,-8.3413601,0.89999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B0AE00B9D7EC264052499D8026EA4740',724,'2019-04-13 09:22:56',89,62.962055,-14.5796,0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000E97B17DCFCEC264052499D8026EA4740',710,'2019-04-13 09:22:57',92,85.18396,-12.1666,1.8)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CC2A62EB2CED26408A7FFE6825EA4740',703,'2019-04-13 09:22:58',96,99.998558,-5.92836,2.0999999)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000936DEA295FED2640B5B55F5124EA4740',701,'2019-04-13 09:22:59',102,99.998558,0.40132001,2.4000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CB10C7BAB8ED2640D95F764F1EEA4740',704,'2019-04-13 09:23:01',116,92.591263,2.21488,5.6999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000002005593CE2ED2640AE38FBF019EA4740',707,'2019-04-13 09:23:02',133,88.887611,2.8143201,7.5)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000925CFE43FAED2640E77D426313EA4740',709,'2019-04-13 09:23:03',147,88.887611,1.50876,6.9000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CA65AD8E09EE26404BF9EABD0BEA4740',710,'2019-04-13 09:23:04',159,88.887611,0.60452002,6.9000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000003DF9EABD0BEE2640448B6CE7FBE94740',709,'2019-04-13 09:23:06',183,92.591263,-0.79755998,5.4000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005917B7D100EE2640CBA145B6F3E94740',707,'2019-04-13 09:23:07',192,94.443085,-2.1082001,3.3)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000076711B0DE0ED2640A098966BE4E94740',701,'2019-04-13 09:23:09',196,99.998558,-2.61112,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000AF25E4839EED2640E08D2B1BC3E94740',695,'2019-04-13 09:23:13',202,105.55404,0.1016,1.5)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000002152DD4931ED2640AF16FEF9A3E94740',696,'2019-04-13 09:23:17',214,103.70221,-0.39624,2.4000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000EA62C92F96EC264021BF58F28BE94740',696,'2019-04-13 09:23:21',236,105.55404,0.1016,2.4000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005CC2ABD203EC26404478557A80E94740',694,'2019-04-13 09:23:24',249,107.40586,-1.2039599,2.0999999)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000004182E2C798EB26402FEC0A907BE94740',690,'2019-04-13 09:23:26',256,111.10951,-2.2098,2.4000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000098A1F63EEEA26407DBD9CEC79E94740',685,'2019-04-13 09:23:29',268,114.81316,-1.00076,1.8)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D1915CFE43EA2640E11A79337DE94740',684,'2019-04-13 09:23:32',277,112.96133,-0.79755998,0.89999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000044BE55C4D6E926404478557A80E94740',682,'2019-04-13 09:23:34',280,114.81316,-2.0065999,0.60000002)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000029ED0DBE30E92640932B1BC389E94740',675,'2019-04-13 09:23:37',292,118.51682,-1.2039599,2.4000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D467FD40CCE826409AA87F2394E94740',675,'2019-04-13 09:23:39',307,114.81316,0.80264002,4.1999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D49AE61DA7E826404BC8073D9BE94740',677,'2019-04-13 09:23:40',316,112.96133,2.0116799,5.4000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000009CC420B072E826403D9B559FABE94740',680,'2019-04-13 09:23:42',339,103.70221,1.0058399,5.4000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000002A762AF369E8264019D3728DBCE94740',681,'2019-04-13 09:23:44',358,96.294907,0.2032,4.1999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000F1F44A5986E82640992A1895D4E94740',679,'2019-04-13 09:23:47',10,94.443085,-2.2098,0.89999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000007F2E244DA9E826401982BD9CECE94740',671,'2019-04-13 09:23:50',14,96.294907,-2.2098,0.60000002)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D3EFCCF1F7E8264099ACB00615EA4740',662,'2019-04-13 09:23:55',21,103.70221,-2.7127199,1.2)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000028B1759646E92640513AB7F62BEA4740',655,'2019-04-13 09:23:58',40,103.70221,-1.905,4.1999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000009A99999999E9264059B71B5736EA4740',652,'2019-04-13 09:24:00',60,99.998558,-1.2039599,5.0999999)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000448B6CE7FBE9264091DE96B53AEA4740',649,'2019-04-13 09:24:02',78,98.146736,-2.5095201,4.1999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000000AA4BA9362EA264091DE96B53AEA4740',643,'2019-04-13 09:24:04',93,98.146736,-2.8092401,3)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B4958DE1C4EA26402E81BA6E37EA4740',636,'2019-04-13 09:24:06',100,98.146736,-3.71856,1.2)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005D6DC5FEB2EB2640B597933D2FEA4740',619,'2019-04-13 09:24:11',100,94.443085,-3.71856,-0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005BB1BFEC9EEC2640EEDCDAAF28EA4740',602,'2019-04-13 09:24:16',98,96.294907,-2.7127199,0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B003E78C28ED2640A01A2FDD24EA4740',598,'2019-04-13 09:24:19',98,88.887611,-0.70104003,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000009298966BE4ED26408A8EE4F21FEA4740',597,'2019-04-13 09:24:24',100,59.258408,-0.096519999,0.30000001)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000075C601E130EE2640EEFAA6C31DEA4740',596,'2019-04-13 09:24:28',86,25.925552,0,-4.1999998)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000091B1E4174BEE26408A8EE4F21FEA4740',597,'2019-04-13 09:24:31',66,14.814602,-0.096519999,-3)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000001F27563358EE26402722222222EA4740',597,'2019-04-13 09:24:38',0,0,0.1016,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CAFFDAD453EE26402722222222EA4740',598,'2019-04-13 09:24:58',0,0,0.1016,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000586C9DA551EE26402722222222EA4740',597,'2019-04-13 09:25:18',0,0,0.1016,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000003098A1F63EE2640EEEBC03923EA4740',596,'2019-04-13 09:25:36',54,1.8518252,0.1016,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000003CDF1F778EE2640A01A2FDD24EA4740',594,'2019-04-13 09:25:48',76,1.8518252,-0.096519999,0)")
- db.session.execute("INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000001FF46C567DEE2640A01A2FDD24EA4740',593,'2019-04-13 09:25:59',0,0,-0.096519999,0)")
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',597,'2019-04-13 09:20:14',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',595,'2019-04-13 09:20:23',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',595,'2019-04-13 09:20:29',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',591,'2019-04-13 09:21:01',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',591,'2019-04-13 09:21:02',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',589,'2019-04-13 09:21:13',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',589,'2019-04-13 09:21:29',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',590,'2019-04-13 09:21:48',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',591,'2019-04-13 09:22:02',0,0,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B5040FE689EA264091FC62C92FEA4740',592,'2019-04-13 09:22:22',0,0,0.1016,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000ED0DBE3099EA2640CA32C4B12EEA4740',593,'2019-04-13 09:22:40',102,25.925552,0.2032,0.60000002)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000026E4839ECDEA26401904560E2DEA4740',594,'2019-04-13 09:22:42',100,68.517532,0.2032,-0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D044D8F0F4EA2640513AB7F62BEA4740',595,'2019-04-13 09:22:43',101,81.480309,1.91008,-0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000025396A721EEB2640A00B49532AEA4740',600,'2019-04-13 09:22:44',100,90.739433,5.6337199,-0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000009E8B4814EEB2640CA41AA3B29EA4740',608,'2019-04-13 09:22:45',100,88.887611,9.2557602,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000087084327AEB264019133C9827EA4740',620,'2019-04-13 09:22:46',99,87.035782,12.3698,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000246416B4A3EB264052499D8026EA4740',634,'2019-04-13 09:22:47',97,83.33213,15.2908,-0.89999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000007958A835CDEB264067E4CDF425EA4740',650,'2019-04-13 09:22:48',94,79.628487,16.093439,-2.0999999)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CE4C3AB7F6EB264067E4CDF425EA4740',667,'2019-04-13 09:22:49',91,75.924835,16.89608,-0.89999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000248613AB19EC264067E4CDF425EA4740',684,'2019-04-13 09:22:50',91,72.221184,17.20088,-0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005C532ACE3EEC264067E4CDF425EA4740',701,'2019-04-13 09:22:51',90,68.517532,16.89608,-0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000003FF9C5925FEC264067E4CDF425EA4740',718,'2019-04-13 09:22:52',91,68.517532,16.19504,-0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000229F615780EC264052499D8026EA4740',733,'2019-04-13 09:22:53',89,59.258408,14.28496,-1.5)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B11D82BD9CEC264052499D8026EA4740',741,'2019-04-13 09:22:54',89,57.406582,3.62204,0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000789CA223B9EC264067E4CDF425EA4740',736,'2019-04-13 09:22:55',88,53.70293,-8.3413601,0.89999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B0AE00B9D7EC264052499D8026EA4740',724,'2019-04-13 09:22:56',89,62.962055,-14.5796,0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000E97B17DCFCEC264052499D8026EA4740',710,'2019-04-13 09:22:57',92,85.18396,-12.1666,1.8)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CC2A62EB2CED26408A7FFE6825EA4740',703,'2019-04-13 09:22:58',96,99.998558,-5.92836,2.0999999)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000936DEA295FED2640B5B55F5124EA4740',701,'2019-04-13 09:22:59',102,99.998558,0.40132001,2.4000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CB10C7BAB8ED2640D95F764F1EEA4740',704,'2019-04-13 09:23:01',116,92.591263,2.21488,5.6999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000002005593CE2ED2640AE38FBF019EA4740',707,'2019-04-13 09:23:02',133,88.887611,2.8143201,7.5)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000925CFE43FAED2640E77D426313EA4740',709,'2019-04-13 09:23:03',147,88.887611,1.50876,6.9000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CA65AD8E09EE26404BF9EABD0BEA4740',710,'2019-04-13 09:23:04',159,88.887611,0.60452002,6.9000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000003DF9EABD0BEE2640448B6CE7FBE94740',709,'2019-04-13 09:23:06',183,92.591263,-0.79755998,5.4000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005917B7D100EE2640CBA145B6F3E94740',707,'2019-04-13 09:23:07',192,94.443085,-2.1082001,3.3)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000076711B0DE0ED2640A098966BE4E94740',701,'2019-04-13 09:23:09',196,99.998558,-2.61112,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000AF25E4839EED2640E08D2B1BC3E94740',695,'2019-04-13 09:23:13',202,105.55404,0.1016,1.5)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000002152DD4931ED2640AF16FEF9A3E94740',696,'2019-04-13 09:23:17',214,103.70221,-0.39624,2.4000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000EA62C92F96EC264021BF58F28BE94740',696,'2019-04-13 09:23:21',236,105.55404,0.1016,2.4000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005CC2ABD203EC26404478557A80E94740',694,'2019-04-13 09:23:24',249,107.40586,-1.2039599,2.0999999)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000004182E2C798EB26402FEC0A907BE94740',690,'2019-04-13 09:23:26',256,111.10951,-2.2098,2.4000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000098A1F63EEEA26407DBD9CEC79E94740',685,'2019-04-13 09:23:29',268,114.81316,-1.00076,1.8)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D1915CFE43EA2640E11A79337DE94740',684,'2019-04-13 09:23:32',277,112.96133,-0.79755998,0.89999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000044BE55C4D6E926404478557A80E94740',682,'2019-04-13 09:23:34',280,114.81316,-2.0065999,0.60000002)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000029ED0DBE30E92640932B1BC389E94740',675,'2019-04-13 09:23:37',292,118.51682,-1.2039599,2.4000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D467FD40CCE826409AA87F2394E94740',675,'2019-04-13 09:23:39',307,114.81316,0.80264002,4.1999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D49AE61DA7E826404BC8073D9BE94740',677,'2019-04-13 09:23:40',316,112.96133,2.0116799,5.4000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000009CC420B072E826403D9B559FABE94740',680,'2019-04-13 09:23:42',339,103.70221,1.0058399,5.4000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000002A762AF369E8264019D3728DBCE94740',681,'2019-04-13 09:23:44',358,96.294907,0.2032,4.1999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000F1F44A5986E82640992A1895D4E94740',679,'2019-04-13 09:23:47',10,94.443085,-2.2098,0.89999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000007F2E244DA9E826401982BD9CECE94740',671,'2019-04-13 09:23:50',14,96.294907,-2.2098,0.60000002)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000D3EFCCF1F7E8264099ACB00615EA4740',662,'2019-04-13 09:23:55',21,103.70221,-2.7127199,1.2)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000028B1759646E92640513AB7F62BEA4740',655,'2019-04-13 09:23:58',40,103.70221,-1.905,4.1999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000009A99999999E9264059B71B5736EA4740',652,'2019-04-13 09:24:00',60,99.998558,-1.2039599,5.0999999)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000448B6CE7FBE9264091DE96B53AEA4740',649,'2019-04-13 09:24:02',78,98.146736,-2.5095201,4.1999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000000AA4BA9362EA264091DE96B53AEA4740',643,'2019-04-13 09:24:04',93,98.146736,-2.8092401,3)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B4958DE1C4EA26402E81BA6E37EA4740',636,'2019-04-13 09:24:06',100,98.146736,-3.71856,1.2)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005D6DC5FEB2EB2640B597933D2FEA4740',619,'2019-04-13 09:24:11',100,94.443085,-3.71856,-0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000005BB1BFEC9EEC2640EEDCDAAF28EA4740',602,'2019-04-13 09:24:16',98,96.294907,-2.7127199,0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000B003E78C28ED2640A01A2FDD24EA4740',598,'2019-04-13 09:24:19',98,88.887611,-0.70104003,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000009298966BE4ED26408A8EE4F21FEA4740',597,'2019-04-13 09:24:24',100,59.258408,-0.096519999,0.30000001)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000075C601E130EE2640EEFAA6C31DEA4740',596,'2019-04-13 09:24:28',86,25.925552,0,-4.1999998)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000091B1E4174BEE26408A8EE4F21FEA4740',597,'2019-04-13 09:24:31',66,14.814602,-0.096519999,-3)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000001F27563358EE26402722222222EA4740',597,'2019-04-13 09:24:38',0,0,0.1016,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000CAFFDAD453EE26402722222222EA4740',598,'2019-04-13 09:24:58',0,0,0.1016,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E6100000586C9DA551EE26402722222222EA4740',597,'2019-04-13 09:25:18',0,0,0.1016,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000003098A1F63EE2640EEEBC03923EA4740',596,'2019-04-13 09:25:36',54,1.8518252,0.1016,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E610000003CDF1F778EE2640A01A2FDD24EA4740',594,'2019-04-13 09:25:48',76,1.8518252,-0.096519999,0)"
+ )
+ db.session.execute(
+ "INSERT INTO aircraft_beacons(name, receiver_name, address, location, altitude, timestamp, track, ground_speed, climb_rate, turn_rate) VALUES('FLRDDAC7C','Koenigsd2','DDAC7C','0101000020E61000001FF46C567DEE2640A01A2FDD24EA4740',593,'2019-04-13 09:25:59',0,0,-0.096519999,0)"
+ )
db.session.execute("UPDATE aircraft_beacons SET device_id = d.id, agl = altitude - 600 FROM devices d WHERE d.address='DDAC7C'")
db.session.commit()
# find the takeoff and the landing
update_entries(db.session, start=datetime.datetime(2019, 4, 13, 0, 0, 0), end=datetime.datetime(2019, 4, 13, 23, 59, 59))
- takeoff_landings = db.session.query(TakeoffLanding) \
- .filter(db.between(TakeoffLanding.timestamp, datetime.datetime(2019, 4, 13, 0, 0, 0), datetime.datetime(2019, 4, 13, 23, 59, 59))) \
- .all()
+ takeoff_landings = db.session.query(TakeoffLanding).filter(db.between(TakeoffLanding.timestamp, datetime.datetime(2019, 4, 13, 0, 0, 0), datetime.datetime(2019, 4, 13, 23, 59, 59))).all()
self.assertEqual(len(takeoff_landings), 2)
for entry in takeoff_landings:
- self.assertEqual(entry.airport.name, 'Koenigsdorf')
+ self.assertEqual(entry.airport.name, "Koenigsdorf")
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/commands/test_database.py b/tests/commands/test_database.py
index 91c6c18..f82e39d 100644
--- a/tests/commands/test_database.py
+++ b/tests/commands/test_database.py
@@ -3,21 +3,21 @@ import os
from tests.base import TestBaseDB, db
-from ogn_python.model import DeviceInfo
-from ogn_python.commands.database import import_file
+from app.model import DeviceInfo
+from app.commands.database import import_file
-from ogn_python import app
+from app import app
class TestDatabase(TestBaseDB):
def test_import_ddb_file(self):
runner = app.test_cli_runner()
- result = runner.invoke(import_file, [os.path.dirname(__file__) + '/../custom_ddb.txt'])
+ result = runner.invoke(import_file, [os.path.dirname(__file__) + "/../custom_ddb.txt"])
self.assertEqual(result.exit_code, 0)
device_infos = db.session.query(DeviceInfo).all()
self.assertEqual(len(device_infos), 6)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/model/all_classes.py b/tests/model/all_classes.py
index 350e18e..09af68a 100644
--- a/tests/model/all_classes.py
+++ b/tests/model/all_classes.py
@@ -3,16 +3,16 @@ import os
import unittest
import inspect
-os.environ['OGN_CONFIG_MODULE'] = 'config/test.py'
+os.environ["OGN_CONFIG_MODULE"] = "config/test.py"
-import ogn_python.model # noqa: E402
+import app.model # noqa: E402
class TestStringMethods(unittest.TestCase):
def test_string(self):
try:
- for name, obj in inspect.getmembers(ogn_python.model):
+ for name, obj in inspect.getmembers(app.model):
print("Testing: {}".format(name))
if inspect.isclass(obj):
print(obj())
@@ -20,5 +20,5 @@ class TestStringMethods(unittest.TestCase):
raise AssertionError(e)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/model/test_device.py b/tests/model/test_device.py
index e6ec6f7..436589f 100644
--- a/tests/model/test_device.py
+++ b/tests/model/test_device.py
@@ -3,14 +3,14 @@ import datetime
import unittest
from tests.base import TestBaseDB, db
-from ogn_python.model import Device, DeviceInfo
+from app.model import Device, DeviceInfo
class TestStringMethods(TestBaseDB):
def test_device_info(self):
- device = Device(name='FLRDD0815', address='DD0815')
- device_info1 = DeviceInfo(address='DD0815', address_origin=1, registration='D-0815')
- device_info2 = DeviceInfo(address='DD0815', address_origin=2, registration='15')
+ device = Device(name="FLRDD0815", address="DD0815")
+ device_info1 = DeviceInfo(address="DD0815", address_origin=1, registration="D-0815")
+ device_info2 = DeviceInfo(address="DD0815", address_origin=2, registration="15")
db.session.add(device)
db.session.add(device_info1)
@@ -20,10 +20,10 @@ class TestStringMethods(TestBaseDB):
self.assertEqual(device.info, device_info1)
def test_expiry_date(self):
- device = Device(name='FLRDD0815', address='DD0815', software_version=6.42)
+ device = Device(name="FLRDD0815", address="DD0815", software_version=6.42)
self.assertEqual(device.expiry_date(), datetime.date(2019, 10, 31))
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 166cf26..30af1b6 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -2,8 +2,8 @@ import os
import unittest
from datetime import date
-from ogn_python.model import AircraftType
-from ogn_python.utils import get_days, get_ddb, get_trackable, get_airports
+from app.model import AircraftType
+from app.utils import get_days, get_ddb, get_trackable, get_airports
class TestStringMethods(unittest.TestCase):
@@ -18,27 +18,27 @@ class TestStringMethods(unittest.TestCase):
self.assertGreater(len(devices), 1000)
def test_get_ddb_from_file(self):
- devices = get_ddb(os.path.dirname(__file__) + '/custom_ddb.txt')
+ devices = get_ddb(os.path.dirname(__file__) + "/custom_ddb.txt")
self.assertEqual(len(devices), 6)
device = devices[0]
- self.assertEqual(device.address, 'DD4711')
- self.assertEqual(device.aircraft, 'HK36 TTC')
- self.assertEqual(device.registration, 'D-EULE')
- self.assertEqual(device.competition, 'CU')
+ self.assertEqual(device.address, "DD4711")
+ self.assertEqual(device.aircraft, "HK36 TTC")
+ self.assertEqual(device.registration, "D-EULE")
+ self.assertEqual(device.competition, "CU")
self.assertTrue(device.tracked)
self.assertTrue(device.identified)
self.assertEqual(device.aircraft_type, AircraftType.glider_or_motor_glider)
def test_get_trackable(self):
- devices = get_ddb(os.path.dirname(__file__) + '/custom_ddb.txt')
+ devices = get_ddb(os.path.dirname(__file__) + "/custom_ddb.txt")
trackable = get_trackable(devices)
self.assertEqual(len(trackable), 4)
- self.assertIn('FLRDD4711', trackable)
- self.assertIn('FLRDD0815', trackable)
- self.assertIn('OGNDEADBE', trackable)
- self.assertIn('ICA999999', trackable)
+ self.assertIn("FLRDD4711", trackable)
+ self.assertIn("FLRDD0815", trackable)
+ self.assertIn("OGNDEADBE", trackable)
+ self.assertIn("ICA999999", trackable)
def test_get_airports(self):
- airports = get_airports(os.path.dirname(__file__) + '/SeeYou.cup')
+ airports = get_airports(os.path.dirname(__file__) + "/SeeYou.cup")
self.assertGreater(len(airports), 1000)