kopia lustrzana https://github.com/glidernet/ogn-python
No more stats table -> functionality of TimescaleDB
Changed PK in tables device/receiver from id to name Altered FK in table takeoff_landings/logbook from device_id to address Updated Flarm release datespull/78/head
rodzic
695478654b
commit
a18e6aeab3
|
@ -22,7 +22,6 @@ CELERYBEAT_SCHEDULE = {
|
||||||
"update-takeoff-and-landing": {"task": "update_takeoff_landings", "schedule": timedelta(hours=1), "kwargs": {"last_minutes": 90}},
|
"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-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-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}},
|
"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}},
|
"purge_old_data": {"task": "purge_old_data", "schedule": timedelta(hours=1), "kwargs": {"max_hours": 48}},
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ from flask import request, render_template, send_file
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app import cache
|
from app import cache
|
||||||
from app.model import Airport, Country, Device, Logbook, Receiver, ReceiverStats
|
from app.model import Airport, Country, Device, Logbook, Receiver
|
||||||
|
|
||||||
from app.main import bp
|
from app.main import bp
|
||||||
|
|
||||||
|
@ -184,14 +184,3 @@ def download_flight():
|
||||||
buffer.seek(0)
|
buffer.seek(0)
|
||||||
|
|
||||||
return send_file(buffer, as_attachment=True, attachment_filename="wtf.igc", mimetype="text/plain")
|
return send_file(buffer, as_attachment=True, attachment_filename="wtf.igc", mimetype="text/plain")
|
||||||
|
|
||||||
|
|
||||||
@bp.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)
|
|
||||||
|
|
|
@ -2,20 +2,14 @@
|
||||||
from .aircraft_type import AircraftType
|
from .aircraft_type import AircraftType
|
||||||
from .beacon import Beacon
|
from .beacon import Beacon
|
||||||
from .country import Country
|
from .country import Country
|
||||||
from .country_stats import CountryStats
|
|
||||||
from .device import Device
|
from .device import Device
|
||||||
from .device_info import DeviceInfo
|
from .device_info import DeviceInfo
|
||||||
from .device_info_origin import DeviceInfoOrigin
|
from .device_info_origin import DeviceInfoOrigin
|
||||||
from .device_stats import DeviceStats
|
|
||||||
from .aircraft_beacon import AircraftBeacon
|
from .aircraft_beacon import AircraftBeacon
|
||||||
from .receiver_beacon import ReceiverBeacon
|
from .receiver_beacon import ReceiverBeacon
|
||||||
from .receiver import Receiver
|
from .receiver import Receiver
|
||||||
from .receiver_stats import ReceiverStats
|
|
||||||
from .takeoff_landing import TakeoffLanding
|
from .takeoff_landing import TakeoffLanding
|
||||||
from .airport import Airport
|
from .airport import Airport
|
||||||
from .logbook import Logbook
|
from .logbook import Logbook
|
||||||
from .receiver_coverage import ReceiverCoverage
|
|
||||||
from .relation_stats import RelationStats
|
|
||||||
from .flights2d import Flight2D
|
|
||||||
|
|
||||||
from .geo import Location
|
from .geo import Location
|
||||||
|
|
|
@ -30,8 +30,8 @@ class AircraftBeacon(Beacon):
|
||||||
distance = db.Column(db.Float(precision=2))
|
distance = db.Column(db.Float(precision=2))
|
||||||
radial = db.Column(db.SmallInteger)
|
radial = db.Column(db.SmallInteger)
|
||||||
quality = db.Column(db.Float(precision=2)) # signal quality normalized to 10km
|
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 = 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
|
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))
|
agl = db.Column(db.Float(precision=2))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -56,75 +56,5 @@ class AircraftBeacon(Beacon):
|
||||||
self.quality,
|
self.quality,
|
||||||
self.location_mgrs,
|
self.location_mgrs,
|
||||||
self.location_mgrs_short,
|
self.location_mgrs_short,
|
||||||
|
self.agl,
|
||||||
)
|
)
|
||||||
|
|
||||||
@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",
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_values(self):
|
|
||||||
return [
|
|
||||||
self.location_wkt,
|
|
||||||
int(self.altitude) if self.altitude else None,
|
|
||||||
self.name,
|
|
||||||
self.dstcall,
|
|
||||||
self.relay,
|
|
||||||
self.receiver_name,
|
|
||||||
self.timestamp,
|
|
||||||
self.track,
|
|
||||||
self.ground_speed,
|
|
||||||
# self.raw_message,
|
|
||||||
# self.reference_timestamp,
|
|
||||||
self.address_type,
|
|
||||||
self.aircraft_type,
|
|
||||||
self.stealth,
|
|
||||||
self.address,
|
|
||||||
self.climb_rate,
|
|
||||||
self.turn_rate,
|
|
||||||
self.signal_quality,
|
|
||||||
self.error_count,
|
|
||||||
self.frequency_offset,
|
|
||||||
self.gps_quality_horizontal,
|
|
||||||
self.gps_quality_vertical,
|
|
||||||
self.software_version,
|
|
||||||
self.hardware_version,
|
|
||||||
self.real_address,
|
|
||||||
self.signal_power,
|
|
||||||
self.distance,
|
|
||||||
self.radial,
|
|
||||||
self.quality,
|
|
||||||
self.location_mgrs,
|
|
||||||
self.location_mgrs_short,
|
|
||||||
]
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ from app import db
|
||||||
|
|
||||||
class Beacon(AbstractConcreteBase, db.Model):
|
class Beacon(AbstractConcreteBase, db.Model):
|
||||||
# APRS data
|
# APRS data
|
||||||
location_wkt = db.Column("location", Geometry("POINT", srid=4326))
|
location = db.Column("location", Geometry("POINT", srid=4326))
|
||||||
altitude = db.Column(db.Float(precision=2))
|
altitude = db.Column(db.Float(precision=2))
|
||||||
|
|
||||||
name = db.Column(db.String, primary_key=True, nullable=True)
|
name = db.Column(db.String, primary_key=True)
|
||||||
dstcall = db.Column(db.String)
|
dstcall = db.Column(db.String)
|
||||||
relay = db.Column(db.String)
|
relay = db.Column(db.String)
|
||||||
receiver_name = db.Column(db.String(9), primary_key=True, nullable=True)
|
receiver_name = db.Column(db.String(9), primary_key=True)
|
||||||
timestamp = db.Column(db.DateTime, primary_key=True, nullable=True)
|
timestamp = db.Column(db.DateTime, primary_key=True)
|
||||||
symboltable = None
|
symboltable = None
|
||||||
symbolcode = None
|
symbolcode = None
|
||||||
track = db.Column(db.SmallInteger)
|
track = db.Column(db.SmallInteger)
|
||||||
|
@ -31,15 +31,3 @@ class Beacon(AbstractConcreteBase, db.Model):
|
||||||
# Debug information
|
# Debug information
|
||||||
raw_message = None
|
raw_message = None
|
||||||
reference_timestamp = None
|
reference_timestamp = None
|
||||||
|
|
||||||
@hybrid_property
|
|
||||||
def location(self):
|
|
||||||
if self.location_wkt is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
coords = to_shape(self.location_wkt)
|
|
||||||
return Location(lat=coords.y, lon=coords.x)
|
|
||||||
|
|
||||||
@location.expression
|
|
||||||
def location(cls):
|
|
||||||
return cls.location_wkt
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
from app import db
|
|
||||||
|
|
||||||
|
|
||||||
class CountryStats(db.Model):
|
|
||||||
__tablename__ = "country_stats"
|
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
|
|
||||||
date = db.Column(db.Date)
|
|
||||||
|
|
||||||
# Static data
|
|
||||||
aircraft_beacon_count = db.Column(db.Integer)
|
|
||||||
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()"))
|
|
|
@ -10,9 +10,8 @@ from app.model.aircraft_type import AircraftType
|
||||||
class Device(db.Model):
|
class Device(db.Model):
|
||||||
__tablename__ = "devices"
|
__tablename__ = "devices"
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
name = db.Column(db.String, 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)
|
address = db.Column(db.String, index=True)
|
||||||
firstseen = db.Column(db.DateTime, index=True)
|
firstseen = db.Column(db.DateTime, index=True)
|
||||||
|
@ -38,6 +37,9 @@ class Device(db.Model):
|
||||||
return [info for info in query.all()]
|
return [info for info in query.all()]
|
||||||
|
|
||||||
EXPIRY_DATES = {
|
EXPIRY_DATES = {
|
||||||
|
7.0: datetime.date(2021, 10, 31),
|
||||||
|
6.83: datetime.date(2021, 10, 31),
|
||||||
|
6.82: datetime.date(2021, 5, 31),
|
||||||
6.81: datetime.date(2021, 1, 31),
|
6.81: datetime.date(2021, 1, 31),
|
||||||
6.80: datetime.date(2021, 1, 31),
|
6.80: datetime.date(2021, 1, 31),
|
||||||
6.67: datetime.date(2020, 10, 31),
|
6.67: datetime.date(2020, 10, 31),
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
from app import db
|
|
||||||
|
|
||||||
from .aircraft_type import AircraftType
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceStats(db.Model):
|
|
||||||
__tablename__ = "device_stats"
|
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
|
|
||||||
date = db.Column(db.Date)
|
|
||||||
|
|
||||||
# Static data
|
|
||||||
name = db.Column(db.String)
|
|
||||||
firstseen = db.Column(db.DateTime)
|
|
||||||
lastseen = db.Column(db.DateTime)
|
|
||||||
aircraft_type = db.Column(db.Enum(AircraftType), nullable=False, default=AircraftType.UNKNOWN)
|
|
||||||
stealth = db.Column(db.Boolean)
|
|
||||||
software_version = db.Column(db.Float(precision=2))
|
|
||||||
hardware_version = db.Column(db.SmallInteger)
|
|
||||||
real_address = db.Column(db.String(6))
|
|
||||||
|
|
||||||
# Statistic data
|
|
||||||
max_altitude = db.Column(db.Float(precision=2))
|
|
||||||
receiver_count = db.Column(db.SmallInteger)
|
|
||||||
aircraft_beacon_count = db.Column(db.Integer)
|
|
||||||
jumps = db.Column(db.SmallInteger)
|
|
||||||
ambiguous = db.Column(db.Boolean)
|
|
||||||
quality = db.Column(db.Float(precision=2))
|
|
||||||
|
|
||||||
# Relation statistic data
|
|
||||||
quality_offset = db.Column(db.Float(precision=2))
|
|
||||||
|
|
||||||
# Ranking data
|
|
||||||
max_altitude_ranking_worldwide = db.Column(db.Integer)
|
|
||||||
max_altitude_ranking_country = db.Column(db.Integer)
|
|
||||||
receiver_count_ranking_worldwide = db.Column(db.Integer)
|
|
||||||
receiver_count_ranking_country = db.Column(db.Integer)
|
|
||||||
aircraft_beacon_count_ranking_worldwide = db.Column(db.Integer)
|
|
||||||
aircraft_beacon_count_ranking_country = db.Column(db.Integer)
|
|
||||||
quality_ranking_worldwide = db.Column(db.Integer)
|
|
||||||
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()"))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<DeviceStats: %s,%s,%s,%s>" % (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)
|
|
|
@ -1,24 +0,0 @@
|
||||||
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 "<Flight %s: %s,%s>" % (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)
|
|
|
@ -9,6 +9,7 @@ class Logbook(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
|
||||||
reftime = db.Column(db.DateTime, index=True)
|
reftime = db.Column(db.DateTime, index=True)
|
||||||
|
address = db.Column(db.String, index=True)
|
||||||
takeoff_timestamp = db.Column(db.DateTime)
|
takeoff_timestamp = db.Column(db.DateTime)
|
||||||
takeoff_track = db.Column(db.SmallInteger)
|
takeoff_track = db.Column(db.SmallInteger)
|
||||||
landing_timestamp = db.Column(db.DateTime)
|
landing_timestamp = db.Column(db.DateTime)
|
||||||
|
@ -22,9 +23,6 @@ class Logbook(db.Model):
|
||||||
landing_airport_id = db.Column(db.Integer, db.ForeignKey("airports.id", ondelete="CASCADE"), index=True)
|
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 = 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"))
|
|
||||||
|
|
||||||
@hybrid_property
|
@hybrid_property
|
||||||
def duration(self):
|
def duration(self):
|
||||||
return None if (self.landing_timestamp is None or self.takeoff_timestamp is None) else self.landing_timestamp - self.takeoff_timestamp
|
return None if (self.landing_timestamp is None or self.takeoff_timestamp is None) else self.landing_timestamp - self.takeoff_timestamp
|
||||||
|
|
|
@ -9,14 +9,13 @@ from app import db
|
||||||
class Receiver(db.Model):
|
class Receiver(db.Model):
|
||||||
__tablename__ = "receivers"
|
__tablename__ = "receivers"
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
name = db.Column(db.String(9), 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))
|
altitude = db.Column(db.Float(precision=2))
|
||||||
|
|
||||||
name = db.Column(db.String(9), index=True)
|
|
||||||
firstseen = db.Column(db.DateTime, index=True)
|
firstseen = db.Column(db.DateTime, index=True)
|
||||||
lastseen = db.Column(db.DateTime, index=True)
|
lastseen = db.Column(db.DateTime, index=True)
|
||||||
|
timestamp = db.Column(db.DateTime, index=True)
|
||||||
version = db.Column(db.String)
|
version = db.Column(db.String)
|
||||||
platform = db.Column(db.String)
|
platform = db.Column(db.String)
|
||||||
|
|
||||||
|
|
|
@ -18,28 +18,3 @@ class ReceiverBeacon(Beacon):
|
||||||
self.receiver_name,
|
self.receiver_name,
|
||||||
self.timestamp,
|
self.timestamp,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_columns(self):
|
|
||||||
return [
|
|
||||||
"location",
|
|
||||||
"altitude",
|
|
||||||
"name",
|
|
||||||
"dstcall",
|
|
||||||
"receiver_name",
|
|
||||||
"timestamp",
|
|
||||||
# 'raw_message',
|
|
||||||
# 'reference_timestamp',
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_values(self):
|
|
||||||
return [
|
|
||||||
self.location_wkt,
|
|
||||||
int(self.altitude) if self.altitude else None,
|
|
||||||
self.name,
|
|
||||||
self.dstcall,
|
|
||||||
self.receiver_name,
|
|
||||||
self.timestamp,
|
|
||||||
# self.raw_message,
|
|
||||||
# self.reference_timestamp,
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
from app import db
|
|
||||||
|
|
||||||
|
|
||||||
class ReceiverCoverage(db.Model):
|
|
||||||
__tablename__ = "receiver_coverages"
|
|
||||||
|
|
||||||
location_mgrs_short = db.Column(db.String(9), primary_key=True)
|
|
||||||
date = db.Column(db.Date, primary_key=True)
|
|
||||||
|
|
||||||
max_signal_quality = db.Column(db.Float)
|
|
||||||
max_altitude = db.Column(db.Float(precision=2))
|
|
||||||
min_altitude = db.Column(db.Float(precision=2))
|
|
||||||
aircraft_beacon_count = db.Column(db.Integer)
|
|
||||||
|
|
||||||
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()"))
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
|
@ -1,41 +0,0 @@
|
||||||
from geoalchemy2.types import Geometry
|
|
||||||
|
|
||||||
from app import db
|
|
||||||
|
|
||||||
|
|
||||||
class ReceiverStats(db.Model):
|
|
||||||
__tablename__ = "receiver_stats"
|
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
|
|
||||||
date = db.Column(db.Date)
|
|
||||||
|
|
||||||
# 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))
|
|
||||||
altitude = db.Column(db.Float(precision=2))
|
|
||||||
version = db.Column(db.String)
|
|
||||||
platform = db.Column(db.String)
|
|
||||||
|
|
||||||
# Statistic data
|
|
||||||
aircraft_beacon_count = db.Column(db.Integer)
|
|
||||||
aircraft_count = db.Column(db.SmallInteger)
|
|
||||||
max_distance = db.Column(db.Float)
|
|
||||||
quality = db.Column(db.Float(precision=2))
|
|
||||||
|
|
||||||
# Relation statistic data
|
|
||||||
quality_offset = db.Column(db.Float(precision=2))
|
|
||||||
|
|
||||||
# Ranking data
|
|
||||||
aircraft_beacon_count_ranking = db.Column(db.SmallInteger)
|
|
||||||
aircraft_count_ranking = db.Column(db.SmallInteger)
|
|
||||||
max_distance_ranking = db.Column(db.SmallInteger)
|
|
||||||
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()"))
|
|
||||||
|
|
||||||
|
|
||||||
db.Index("ix_receiver_stats_date_receiver_id", ReceiverStats.date, ReceiverStats.receiver_id)
|
|
|
@ -1,26 +0,0 @@
|
||||||
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 "<RelationStats: %s,%s,%s>" % (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)
|
|
|
@ -4,7 +4,7 @@ from app import db
|
||||||
class TakeoffLanding(db.Model):
|
class TakeoffLanding(db.Model):
|
||||||
__tablename__ = "takeoff_landings"
|
__tablename__ = "takeoff_landings"
|
||||||
|
|
||||||
device_id = db.Column(db.Integer, db.ForeignKey("devices.id", ondelete="SET NULL"), primary_key=True)
|
address = db.Column(db.String, primary_key=True)
|
||||||
airport_id = db.Column(db.Integer, db.ForeignKey("airports.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)
|
timestamp = db.Column(db.DateTime, primary_key=True)
|
||||||
|
|
||||||
|
@ -13,4 +13,3 @@ class TakeoffLanding(db.Model):
|
||||||
|
|
||||||
# Relations
|
# Relations
|
||||||
airport = db.relationship("Airport", foreign_keys=[airport_id], backref="takeoff_landings")
|
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")
|
|
||||||
|
|
|
@ -1,199 +0,0 @@
|
||||||
from datetime import datetime, date
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from tests.base import TestBaseDB, db
|
|
||||||
|
|
||||||
from app.model import AircraftBeacon, ReceiverBeacon, Receiver, Device, DeviceStats
|
|
||||||
|
|
||||||
from app.collect.stats import create_device_stats
|
|
||||||
|
|
||||||
|
|
||||||
class TestStats(TestBaseDB):
|
|
||||||
def setUp(self):
|
|
||||||
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.rb01 = ReceiverBeacon(name="Koenigsdf", receiver_name="GLIDERN1", timestamp="2017-12-10 09:55:00", altitude=601)
|
|
||||||
self.rb02 = ReceiverBeacon(name="Koenigsdf", receiver_name="GLIDERN1", timestamp="2017-12-10 10:00:00", altitude=601)
|
|
||||||
self.rb03 = ReceiverBeacon(name="Koenigsdf", receiver_name="GLIDERN1", timestamp="2017-12-10 10:05:00", altitude=601)
|
|
||||||
|
|
||||||
self.r01 = Receiver(name="Koenigsdf")
|
|
||||||
self.r02 = Receiver(name="Bene")
|
|
||||||
|
|
||||||
self.d01 = Device(address="DD4711")
|
|
||||||
|
|
||||||
db.session.add(self.r01)
|
|
||||||
db.session.add(self.d01)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
@unittest.skip('stats will replaced by timescaledb aggregates')
|
|
||||||
def test_create_device_stats(self):
|
|
||||||
# Compute 1st beacon
|
|
||||||
self.ab01.device = self.d01
|
|
||||||
self.ab01.receiver = self.r01
|
|
||||||
db.session.add(self.ab01)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
today = date(2017, 12, 10)
|
|
||||||
create_device_stats(db.session, date=today)
|
|
||||||
|
|
||||||
devicestats = db.session.query(DeviceStats).all()
|
|
||||||
self.assertEqual(len(devicestats), 1)
|
|
||||||
self.assertEqual(devicestats[0].device, self.d01)
|
|
||||||
|
|
||||||
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].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)
|
|
||||||
self.assertEqual(devicestats[0].stealth, None)
|
|
||||||
self.assertEqual(devicestats[0].software_version, None)
|
|
||||||
self.assertEqual(devicestats[0].hardware_version, None)
|
|
||||||
self.assertEqual(devicestats[0].real_address, None)
|
|
||||||
|
|
||||||
# Compute 2nd beacon: set altitude, aircraft_type and stealth
|
|
||||||
self.ab02.device = self.d01
|
|
||||||
self.ab02.receiver = self.r01
|
|
||||||
self.ab02.altitude = 200
|
|
||||||
self.ab02.aircraft_type = 3
|
|
||||||
self.ab02.stealth = False
|
|
||||||
db.session.add(self.ab02)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
create_device_stats(db.session, date=today)
|
|
||||||
|
|
||||||
devicestats = db.session.query(DeviceStats).all()
|
|
||||||
self.assertEqual(len(devicestats), 1)
|
|
||||||
self.assertEqual(devicestats[0].device, self.d01)
|
|
||||||
|
|
||||||
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].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)
|
|
||||||
self.assertEqual(devicestats[0].stealth, False)
|
|
||||||
self.assertEqual(devicestats[0].software_version, None)
|
|
||||||
self.assertEqual(devicestats[0].hardware_version, None)
|
|
||||||
self.assertEqual(devicestats[0].real_address, None)
|
|
||||||
|
|
||||||
# Compute 3rd beacon: changed software version, but with error_count > 0
|
|
||||||
self.ab03.device = self.d01
|
|
||||||
self.ab03.receiver = self.r01
|
|
||||||
self.ab03.error_count = 1
|
|
||||||
self.ab03.software_version = 6.01
|
|
||||||
db.session.add(self.ab03)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
create_device_stats(db.session, date=today)
|
|
||||||
|
|
||||||
devicestats = db.session.query(DeviceStats).all()
|
|
||||||
self.assertEqual(len(devicestats), 1)
|
|
||||||
self.assertEqual(devicestats[0].device, self.d01)
|
|
||||||
|
|
||||||
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].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)
|
|
||||||
self.assertEqual(devicestats[0].stealth, False)
|
|
||||||
self.assertEqual(devicestats[0].software_version, None)
|
|
||||||
self.assertEqual(devicestats[0].hardware_version, None)
|
|
||||||
self.assertEqual(devicestats[0].real_address, None)
|
|
||||||
|
|
||||||
# Compute 4. beacon: another receiver, greater altitude, software_version, hardware_version, real_address
|
|
||||||
self.ab04.device = self.d01
|
|
||||||
self.ab04.receiver = self.r02
|
|
||||||
self.ab04.altitude = 250
|
|
||||||
self.ab04.software_version = 6.01
|
|
||||||
self.ab04.hardware_version = 15
|
|
||||||
self.ab04.real_address = "DDALFA"
|
|
||||||
db.session.add(self.ab04)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
create_device_stats(db.session, date=today)
|
|
||||||
|
|
||||||
devicestats = db.session.query(DeviceStats).all()
|
|
||||||
self.assertEqual(len(devicestats), 1)
|
|
||||||
self.assertEqual(devicestats[0].device, self.d01)
|
|
||||||
|
|
||||||
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].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")
|
|
||||||
|
|
||||||
# Compute 5. beacon: lower altitude, stealth
|
|
||||||
self.ab05.device = self.d01
|
|
||||||
self.ab05.receiver = self.r02
|
|
||||||
self.ab05.altitude = 100
|
|
||||||
self.ab05.stealth = True
|
|
||||||
db.session.add(self.ab05)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
create_device_stats(db.session, date=today)
|
|
||||||
|
|
||||||
devicestats = db.session.query(DeviceStats).all()
|
|
||||||
self.assertEqual(len(devicestats), 1)
|
|
||||||
self.assertEqual(devicestats[0].device, self.d01)
|
|
||||||
|
|
||||||
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].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")
|
|
||||||
|
|
||||||
# Compute 6. beacon: beacon from past, greater altitude, newer version
|
|
||||||
self.ab06.device = self.d01
|
|
||||||
self.ab06.receiver = self.r02
|
|
||||||
self.ab06.timestamp = datetime(2017, 12, 10, 9, 59, 50)
|
|
||||||
self.ab06.altitude = 300
|
|
||||||
self.ab06.software_version = 6.02
|
|
||||||
db.session.add(self.ab06)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
create_device_stats(db.session, date=today)
|
|
||||||
|
|
||||||
devicestats = db.session.query(DeviceStats).all()
|
|
||||||
self.assertEqual(len(devicestats), 1)
|
|
||||||
self.assertEqual(devicestats[0].device, self.d01)
|
|
||||||
|
|
||||||
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].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")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
Ładowanie…
Reference in New Issue