kopia lustrzana https://github.com/glidernet/ogn-python
Refactored Logbook
rodzic
2f577c8c29
commit
03cfd6136a
|
@ -1,9 +1,10 @@
|
|||
from sqlalchemy import and_, or_, insert, update, exists, between
|
||||
from sqlalchemy.sql import func, null
|
||||
from sqlalchemy.sql.expression import case, true, false
|
||||
from sqlalchemy.dialects.postgresql import insert # special insert for upsert ("ON CONFLICT ...")
|
||||
from flask import current_app
|
||||
|
||||
from app.model import Airport, SenderPosition, Sender, TakeoffLanding, Logbook
|
||||
from app.model import Airport, Country, SenderPosition, Sender, TakeoffLanding, Logbook
|
||||
from app.utils import date_to_timestamps
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
@ -39,12 +40,6 @@ def update_takeoff_landings(start, end):
|
|||
current_app.logger.warn(abort_message)
|
||||
return abort_message
|
||||
|
||||
# delete existing elements
|
||||
db.session.query(TakeoffLanding) \
|
||||
.filter(between(TakeoffLanding.timestamp, start, end))\
|
||||
.delete(synchronize_session='fetch')
|
||||
db.session.commit()
|
||||
|
||||
# get beacons for selected time range (+ buffer for duration), one per name and timestamp
|
||||
sq = (
|
||||
db.session.query(SenderPosition.name, SenderPosition.timestamp, SenderPosition.location, SenderPosition.track, db.func.coalesce(SenderPosition.ground_speed, 0.0).label("ground_speed"), SenderPosition.altitude, db.func.coalesce(SenderPosition.climb_rate, 0.0).label("climb_rate"))
|
||||
|
@ -129,15 +124,25 @@ def update_takeoff_landings(start, end):
|
|||
)
|
||||
|
||||
# ... and take the nearest airport
|
||||
takeoff_landing_query = (
|
||||
sq6 = (
|
||||
db.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()
|
||||
)
|
||||
|
||||
# ... add the country
|
||||
takeoff_landing_query = (
|
||||
db.session.query(sq6.c.timestamp, sq6.c.track, sq6.c.is_takeoff, sq6.c.device_id, sq6.c.airport_id, Country.gid)
|
||||
.join(Airport, sq6.c.airport_id==Airport.id)
|
||||
.join(Country, Airport.country_code==Country.iso2, isouter=True)
|
||||
.subquery()
|
||||
)
|
||||
|
||||
# ... and save them
|
||||
ins = insert(TakeoffLanding).from_select((TakeoffLanding.timestamp, TakeoffLanding.track, TakeoffLanding.is_takeoff, TakeoffLanding.sender_id, TakeoffLanding.airport_id), takeoff_landing_query)
|
||||
ins = insert(TakeoffLanding) \
|
||||
.from_select((TakeoffLanding.timestamp, TakeoffLanding.track, TakeoffLanding.is_takeoff, TakeoffLanding.sender_id, TakeoffLanding.airport_id, TakeoffLanding.country_id), takeoff_landing_query) \
|
||||
.on_conflict_do_nothing(index_elements=[TakeoffLanding.timestamp, TakeoffLanding.sender_id, TakeoffLanding.airport_id])
|
||||
|
||||
result = db.session.execute(ins)
|
||||
db.session.commit()
|
||||
|
@ -148,48 +153,92 @@ def update_takeoff_landings(start, end):
|
|||
return finish_message
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from app import create_app
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
result = update_takeoff_landings(start=datetime(2020, 11, 9, 10, 0, 0), end=datetime(2020, 11, 9, 10, 10, 0))
|
||||
print(result)
|
||||
|
||||
|
||||
def update_logbook(offset_days):
|
||||
def update_logbook(offset_days=None):
|
||||
"""Add/update logbook entries."""
|
||||
|
||||
current_app.logger.info("Compute logbook.")
|
||||
|
||||
# limit time range to given date and set window partition and window order
|
||||
if offset_days:
|
||||
(start, end) = date_to_timestamps(datetime.utcnow()-timedelta(days=offset_days))
|
||||
else:
|
||||
(start, end) = date_to_timestamps(datetime.utcnow().date())
|
||||
pa = TakeoffLanding.sender_id
|
||||
wo = and_(TakeoffLanding.sender_id, TakeoffLanding.timestamp, TakeoffLanding.airport_id)
|
||||
|
||||
# delete existing elements
|
||||
db.session.query(Logbook)\
|
||||
.filter(between(Logbook.reference, start, end))\
|
||||
.delete(synchronize_session='fetch')
|
||||
db.session.commit()
|
||||
|
||||
# make a query with current and next "takeoff_landing" event, so we can find complete flights
|
||||
# make a query with previous, current and next "takeoff_landing" event, so we can find complete flights
|
||||
sq = (
|
||||
db.session.query(
|
||||
TakeoffLanding.sender_id,
|
||||
func.lag(TakeoffLanding.sender_id).over(partition_by=pa, order_by=wo).label("sender_id_prev"),
|
||||
func.lead(TakeoffLanding.sender_id).over(partition_by=pa, order_by=wo).label("sender_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))
|
||||
#.filter(between(TakeoffLanding.timestamp, start, end))
|
||||
.subquery()
|
||||
)
|
||||
|
||||
# find (new) starts without landing
|
||||
only_starts_query = (
|
||||
db.session.query(
|
||||
sq.c.sender_id.label("sender_id"),
|
||||
sq.c.timestamp.label("takeoff_timestamp"),
|
||||
sq.c.track.label("takeoff_track"),
|
||||
sq.c.airport_id.label("takeoff_airport_id")
|
||||
)
|
||||
.filter(sq.c.is_takeoff == true())
|
||||
.filter(or_(sq.c.is_takeoff_next == true(), sq.c.is_takeoff_next == null()))
|
||||
.filter(~Logbook.query.filter(db.and_(Logbook.sender_id==sq.c.sender_id, Logbook.takeoff_timestamp==sq.c.timestamp, Logbook.takeoff_airport_id==sq.c.airport_id)).exists())
|
||||
)
|
||||
ins = insert(Logbook).from_select(
|
||||
(
|
||||
Logbook.sender_id,
|
||||
Logbook.takeoff_timestamp,
|
||||
Logbook.takeoff_track,
|
||||
Logbook.takeoff_airport_id
|
||||
),
|
||||
only_starts_query,
|
||||
)
|
||||
result = db.session.execute(ins)
|
||||
current_app.logger.debug(f"Added {result.rowcount} starts")
|
||||
db.session.commit()
|
||||
|
||||
# find (new) landings without start
|
||||
only_landings_query = (
|
||||
db.session.query(
|
||||
sq.c.sender_id.label("sender_id"),
|
||||
sq.c.timestamp.label("landing_timestamp"),
|
||||
sq.c.track.label("landing_track"),
|
||||
sq.c.airport_id.label("landing_airport_id"),
|
||||
)
|
||||
.filter(or_(sq.c.is_takeoff_prev == false(), sq.c.is_takeoff_prev == null()))
|
||||
.filter(sq.c.is_takeoff == false())
|
||||
.filter(~Logbook.query.filter(db.and_(Logbook.sender_id==sq.c.sender_id, Logbook.landing_timestamp==sq.c.timestamp, Logbook.landing_airport_id==sq.c.airport_id)).exists())
|
||||
)
|
||||
ins = insert(Logbook).from_select(
|
||||
(
|
||||
Logbook.sender_id,
|
||||
Logbook.landing_timestamp,
|
||||
Logbook.landing_track,
|
||||
Logbook.landing_airport_id
|
||||
),
|
||||
only_landings_query,
|
||||
)
|
||||
result = db.session.execute(ins)
|
||||
current_app.logger.debug(f"Added {result.rowcount} landings")
|
||||
db.session.commit()
|
||||
|
||||
# find complete flights
|
||||
complete_flight_query = (
|
||||
db.session.query(
|
||||
|
@ -203,42 +252,15 @@ def update_logbook(offset_days):
|
|||
)
|
||||
.filter(sq.c.is_takeoff == true())
|
||||
.filter(sq.c.is_takeoff_next == false())
|
||||
.subquery()
|
||||
)
|
||||
|
||||
# find landings without start
|
||||
only_landings_query = (
|
||||
db.session.query(
|
||||
sq.c.sender_id_next.label("sender_id"),
|
||||
null().label("takeoff_timestamp"),
|
||||
null().label("takeoff_track"),
|
||||
null().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"),
|
||||
# insert (new) flights
|
||||
new_flights_query = (
|
||||
db.session.query(complete_flight_query) \
|
||||
.filter(~Logbook.query.filter(db.and_(Logbook.sender_id==complete_flight_query.c.sender_id, Logbook.landing_timestamp==complete_flight_query.c.landing_timestamp, Logbook.landing_airport_id==complete_flight_query.c.landing_airport_id)).exists())
|
||||
.filter(~Logbook.query.filter(db.and_(Logbook.sender_id==complete_flight_query.c.sender_id, Logbook.takeoff_timestamp==complete_flight_query.c.takeoff_timestamp, Logbook.takeoff_airport_id==complete_flight_query.c.takeoff_airport_id)).exists())
|
||||
)
|
||||
.filter(or_(sq.c.is_takeoff == false(), sq.c.is_takeoff == null()))
|
||||
.filter(sq.c.is_takeoff_next == false())
|
||||
)
|
||||
|
||||
# find starts without landing
|
||||
only_starts_query = (
|
||||
db.session.query(
|
||||
sq.c.sender_id.label("sender_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
|
||||
logbook_entries = complete_flight_query.union(only_landings_query, only_starts_query).subquery()
|
||||
|
||||
# ... insert them into logbook
|
||||
ins = insert(Logbook).from_select(
|
||||
(
|
||||
Logbook.sender_id,
|
||||
|
@ -247,47 +269,121 @@ def update_logbook(offset_days):
|
|||
Logbook.takeoff_airport_id,
|
||||
Logbook.landing_timestamp,
|
||||
Logbook.landing_track,
|
||||
Logbook.landing_airport_id,
|
||||
Logbook.landing_airport_id
|
||||
),
|
||||
logbook_entries,
|
||||
new_flights_query
|
||||
)
|
||||
|
||||
result = db.session.execute(ins)
|
||||
insert_counter = result.rowcount
|
||||
current_app.logger.debug(f"Added {result.rowcount} complete flights")
|
||||
db.session.commit()
|
||||
|
||||
finish_message = "Logbook: {} inserted".format(insert_counter)
|
||||
return finish_message
|
||||
# update existing landing with takeoff from complete flight
|
||||
upd = update(Logbook) \
|
||||
.where(db.and_(
|
||||
Logbook.sender_id==complete_flight_query.c.sender_id,
|
||||
Logbook.takeoff_timestamp==null(),
|
||||
Logbook.takeoff_airport_id==null(),
|
||||
Logbook.landing_timestamp!=null(),
|
||||
Logbook.landing_timestamp==complete_flight_query.c.landing_timestamp,
|
||||
Logbook.landing_airport_id==complete_flight_query.c.landing_airport_id
|
||||
)) \
|
||||
.values(takeoff_timestamp=complete_flight_query.c.takeoff_timestamp,
|
||||
takeoff_airport_id=complete_flight_query.c.takeoff_airport_id
|
||||
)
|
||||
result = db.session.execute(upd)
|
||||
current_app.logger.debug(f"Updated {result.rowcount} takeoffs to complete flights")
|
||||
db.session.commit()
|
||||
|
||||
# update existing takeoff with landing from complete flight
|
||||
upd = update(Logbook) \
|
||||
.where(db.and_(
|
||||
Logbook.sender_id==complete_flight_query.c.sender_id,
|
||||
Logbook.takeoff_timestamp!=null(),
|
||||
Logbook.takeoff_timestamp==complete_flight_query.c.takeoff_timestamp,
|
||||
Logbook.takeoff_airport_id==complete_flight_query.c.takeoff_airport_id,
|
||||
Logbook.landing_timestamp==null(),
|
||||
Logbook.landing_airport_id==null()
|
||||
)) \
|
||||
.values(landing_timestamp=complete_flight_query.c.landing_timestamp,
|
||||
landing_airport_id=complete_flight_query.c.landing_airport_id
|
||||
)
|
||||
result = db.session.execute(upd)
|
||||
current_app.logger.debug(f"Updated {result.rowcount} landings to complete flights")
|
||||
db.session.commit()
|
||||
|
||||
return
|
||||
|
||||
|
||||
def update_max_altitudes(date, logger=None):
|
||||
|
||||
def update_max_altitudes(offset_days=100):
|
||||
MAX_UPDATES = 100
|
||||
|
||||
query = f"""
|
||||
UPDATE logbooks AS l
|
||||
SET max_altitude = sq2.max_altitude
|
||||
FROM (
|
||||
SELECT
|
||||
sq.logbook_id,
|
||||
MAX(sp.altitude) AS max_altitude
|
||||
FROM
|
||||
sender_positions AS sp,
|
||||
(
|
||||
SELECT
|
||||
l.id AS logbook_id, s.name, l.takeoff_timestamp, l.landing_timestamp
|
||||
FROM logbooks AS l
|
||||
INNER JOIN senders AS s ON l.sender_id = s.id
|
||||
WHERE
|
||||
l.takeoff_timestamp IS NOT NULL and l.landing_timestamp IS NOT NULL
|
||||
AND l.max_altitude IS NULL
|
||||
ORDER BY l.takeoff_timestamp
|
||||
LIMIT {MAX_UPDATES}
|
||||
) AS sq
|
||||
WHERE sq.name = sp.name AND sp.timestamp BETWEEN sq.takeoff_timestamp AND sq.landing_timestamp
|
||||
GROUP BY sq.logbook_id
|
||||
) AS sq2
|
||||
WHERE l.id = sq2.logbook_id;
|
||||
"""
|
||||
|
||||
result = db.session.execute(query)
|
||||
db.session.commit()
|
||||
|
||||
return result.rowcount
|
||||
|
||||
def update_max_altitudes_orm(offset_days):
|
||||
"""Add max altitudes in logbook when flight is complete (takeoff and landing)."""
|
||||
|
||||
if logger is None:
|
||||
logger = current_app.logger
|
||||
|
||||
current_app.logger.info("Update logbook max altitude.")
|
||||
|
||||
(start, end) = date_to_timestamps(date)
|
||||
(start, end) = date_to_timestamps(datetime.today() - timedelta(days=offset_days))
|
||||
|
||||
logbook_entries = (
|
||||
db.query(Logbook.id)
|
||||
db.session.query(Logbook.id, Sender.name)
|
||||
.filter(and_(Logbook.takeoff_timestamp != null(), Logbook.landing_timestamp != null(), Logbook.max_altitude == null()))
|
||||
.filter(between(Logbook.reference, start, end))
|
||||
#.filter(between(Logbook.reference, start, end))
|
||||
.filter(Logbook.sender_id == Sender.id)
|
||||
.limit(10)
|
||||
.subquery()
|
||||
)
|
||||
|
||||
max_altitudes = (
|
||||
db.query(Logbook.id, func.max(SenderPosition.altitude).label("max_altitude"))
|
||||
db.session.query(Logbook.id, func.max(SenderPosition.altitude).label("max_altitude"))
|
||||
.filter(and_(SenderPosition.name == logbook_entries.c.name, SenderPosition.timestamp >= Logbook.takeoff_timestamp, SenderPosition.timestamp <= Logbook.landing_timestamp))
|
||||
.filter(Logbook.id == logbook_entries.c.id)
|
||||
.filter(and_(SenderPosition.address == Logbook.address, SenderPosition.timestamp >= Logbook.takeoff_timestamp, SenderPosition.timestamp <= Logbook.landing_timestamp))
|
||||
.group_by(Logbook.id)
|
||||
.subquery()
|
||||
)
|
||||
|
||||
update_logbook = db.query(Logbook).filter(Logbook.id == max_altitudes.c.id).update({Logbook.max_altitude: max_altitudes.c.max_altitude}, synchronize_session="fetch")
|
||||
update_logbooks = db.session.query(Logbook).filter(Logbook.id == max_altitudes.c.id).update({Logbook.max_altitude: max_altitudes.c.max_altitude}, synchronize_session="fetch")
|
||||
|
||||
db.session.commit()
|
||||
|
||||
finish_message = "Logbook (altitude): {} entries updated.".format(update_logbook)
|
||||
finish_message = "Logbook (altitude): {} entries updated.".format(update_logbooks)
|
||||
return finish_message
|
||||
|
||||
if __name__ == '__main__':
|
||||
from app import create_app
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
#result = update_takeoff_landings(start=datetime(2020, 11, 9, 10, 0, 0), end=datetime(2020, 11, 9, 15, 30, 0))
|
||||
result = update_logbook(0)
|
||||
print(result)
|
||||
|
|
|
@ -17,26 +17,26 @@ def get_countries_in_receivers():
|
|||
return [{"iso2": country[0]} for country in query.all()]
|
||||
|
||||
|
||||
@cache.cached(key_prefix="countries_in_logbook")
|
||||
@cache.cached(key_prefix="countries_in_takeoff_landings")
|
||||
def get_used_countries():
|
||||
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)
|
||||
query = db.session.query(Country.iso2).filter(Country.gid == TakeoffLanding.country_id).order_by(Country.iso2).distinct(Country.iso2)
|
||||
return [{"iso2": country[0]} for country in query.all()]
|
||||
|
||||
|
||||
@cache.memoize()
|
||||
def get_used_airports_by_country(sel_country):
|
||||
query = db.session.query(Airport).filter(Airport.country_code == sel_country).filter(Logbook.takeoff_airport_id == Airport.id).order_by(Airport.name).distinct(Airport.name)
|
||||
query = db.session.query(Airport).filter(Airport.country_code == sel_country).filter(TakeoffLanding.airport_id==Airport.id).filter(TakeoffLanding.country_id == Country.gid).order_by(Airport.name).distinct(Airport.name)
|
||||
return [used_airport for used_airport in query]
|
||||
|
||||
|
||||
@cache.memoize()
|
||||
def get_dates_for_airport(sel_airport):
|
||||
query = (
|
||||
db.session.query(db.func.date(Logbook.reference), db.func.count(Logbook.id).label("logbook_count"))
|
||||
db.session.query(db.func.date(Logbook.reference_timestamp), 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.reference))
|
||||
.order_by(db.func.date(Logbook.reference).desc())
|
||||
.group_by(db.func.date(Logbook.reference_timestamp))
|
||||
.order_by(db.func.date(Logbook.reference_timestamp).desc())
|
||||
)
|
||||
|
||||
return [{"date": date, "logbook_count": logbook_count} for (date, logbook_count) in query.all()]
|
||||
|
@ -54,7 +54,7 @@ def index():
|
|||
sender_positions_today = db.session.query(db.func.sum(ReceiverStatistic.messages_count)).filter(ReceiverStatistic.date==date.today()).one()[0]
|
||||
sender_positions_total = db.session.query(db.func.sum(ReceiverStatistic.messages_count)).one()[0]
|
||||
|
||||
last_logbook_entries = db.session.query(Logbook).order_by(Logbook.reference.desc()).limit(10)
|
||||
last_logbook_entries = db.session.query(Logbook).order_by(Logbook.reference_timestamp.desc()).limit(10)
|
||||
return render_template("index.html",
|
||||
senders_today=senders_today,
|
||||
receivers_today=receivers_today,
|
||||
|
@ -150,8 +150,8 @@ def airport_detail():
|
|||
return render_template("airport_detail.html", title="Airport Detail", airport=airport.one(), senders=senders)
|
||||
|
||||
|
||||
@bp.route("/logbook.html", methods=["GET", "POST"])
|
||||
def logbook():
|
||||
@bp.route("/logbooks.html", methods=["GET", "POST"])
|
||||
def logbooks():
|
||||
sel_country = request.args.get("country")
|
||||
sel_airport_id = request.args.get("airport_id")
|
||||
sel_date = request.args.get("date")
|
||||
|
@ -187,17 +187,17 @@ def logbook():
|
|||
filters.append(db.or_(Logbook.takeoff_airport_id == sel_airport_id, Logbook.landing_airport_id == sel_airport_id))
|
||||
|
||||
if sel_date:
|
||||
filters.append(db.func.date(Logbook.reference) == sel_date)
|
||||
filters.append(db.func.date(Logbook.reference_timestamp) == sel_date)
|
||||
|
||||
if sel_sender_id:
|
||||
filters.append(Logbook.sender_id == sel_sender_id)
|
||||
|
||||
if len(filters) > 0:
|
||||
logbook = db.session.query(Logbook).filter(*filters).order_by(Logbook.reference)
|
||||
logbooks = db.session.query(Logbook).filter(*filters).order_by(Logbook.reference_timestamp).limit(100)
|
||||
else:
|
||||
logbook = None
|
||||
logbooks = None
|
||||
|
||||
return render_template("logbook.html", title="Logbook", sel_country=sel_country, countries=countries, sel_airport_id=sel_airport_id, airports=airports, sel_date=sel_date, dates=dates, logbook=logbook)
|
||||
return render_template("logbooks.html", title="Logbook", sel_country=sel_country, countries=countries, sel_airport_id=sel_airport_id, airports=airports, sel_date=sel_date, dates=dates, logbooks=logbooks)
|
||||
|
||||
|
||||
@bp.route("/download.html")
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.sql import null, case
|
||||
from sqlalchemy.orm import backref
|
||||
|
||||
from app import db
|
||||
from app.model import Sender
|
||||
|
||||
|
||||
class Logbook(db.Model):
|
||||
__tablename__ = "logbook"
|
||||
__tablename__ = "logbooks"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
|
@ -18,14 +18,19 @@ class Logbook(db.Model):
|
|||
|
||||
# Relations
|
||||
sender_id = db.Column(db.Integer, db.ForeignKey("senders.id", ondelete="CASCADE"), index=True)
|
||||
#sender = db.relationship("Sender", foreign_keys=[sender_id], backref=backref("logbook_entries", order_by=reference.desc()) # TODO: does not work...
|
||||
sender = db.relationship("Sender", foreign_keys=[sender_id], backref=backref("logbook_entries", order_by=case({True: takeoff_timestamp, False: landing_timestamp}, takeoff_timestamp != null()).desc()))
|
||||
|
||||
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 = db.relationship("Airport", foreign_keys=[takeoff_airport_id], backref=backref("logbook_entries_takeoff", order_by=case({True: takeoff_timestamp, False: landing_timestamp}, takeoff_timestamp != null()).desc()))
|
||||
|
||||
takeoff_country_id = db.Column(db.Integer, db.ForeignKey("countries.gid", ondelete="CASCADE"), index=True)
|
||||
takeoff_country = db.relationship("Country", foreign_keys=[takeoff_country_id], backref=backref("logbook_entries_takeoff", order_by=case({True: takeoff_timestamp, False: landing_timestamp}, takeoff_timestamp != null()).desc()))
|
||||
|
||||
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], backref=backref("logbook_entries_landing", order_by=case({True: takeoff_timestamp, False: landing_timestamp}, takeoff_timestamp != null()).desc()))
|
||||
|
||||
landing_country_id = db.Column(db.Integer, db.ForeignKey("countries.gid", ondelete="CASCADE"), index=True)
|
||||
landing_country = db.relationship("Country", foreign_keys=[landing_country_id], backref=backref("logbook_entries_landing", order_by=case({True: takeoff_timestamp, False: landing_timestamp}, takeoff_timestamp != null()).desc()))
|
||||
|
||||
@hybrid_property
|
||||
def duration(self):
|
||||
|
@ -36,9 +41,19 @@ class Logbook(db.Model):
|
|||
return case({False: None, True: cls.landing_timestamp - cls.takeoff_timestamp}, cls.landing_timestamp != null() and cls.takeoff_timestamp != null())
|
||||
|
||||
@hybrid_property
|
||||
def reference(self):
|
||||
def reference_timestamp(self):
|
||||
return self.takeoff_timestamp if self.takeoff_timestamp is not None else self.landing_timestamp
|
||||
|
||||
@reference.expression
|
||||
def reference(cls):
|
||||
@reference_timestamp.expression
|
||||
def reference_timestamp(cls):
|
||||
return case({True: cls.takeoff_timestamp, False: cls.landing_timestamp}, cls.takeoff_timestamp != null())
|
||||
|
||||
#__table_args__ = (db.Index('idx_logbook_reference_timestamp', case({True: takeoff_timestamp, False: landing_timestamp}, takeoff_timestamp != null())),)
|
||||
# FIXME: does not work...
|
||||
|
||||
# FIXME: this does not throw an error as the __table_args__ above, but there is no index created
|
||||
#_wrapped_case = f"({case(whens={True: Logbook.takeoff_timestamp, False: Logbook.landing_timestamp}, value=Logbook.takeoff_timestamp != null())})"
|
||||
#Index("idx_logbook_reference_timestamp", _wrapped_case)
|
||||
|
||||
# TODO:
|
||||
# so execute manually: CREATE INDEX IF NOT EXISTS idx_logbook_reference_timestamp ON logbooks ((CASE takeoff_timestamp IS NULL WHEN true THEN takeoff_timestamp WHEN false THEN landing_timestamp END));
|
||||
|
|
|
@ -18,4 +18,7 @@ class TakeoffLanding(db.Model):
|
|||
airport_id = db.Column(db.Integer, db.ForeignKey("airports.id", ondelete="SET NULL"))
|
||||
airport = db.relationship("Airport", foreign_keys=[airport_id], backref="takeoff_landings")
|
||||
|
||||
country_id = db.Column(db.Integer, db.ForeignKey("countries.gid", ondelete="CASCADE"), index=True)
|
||||
country = db.relationship("Country", foreign_keys=[country_id], backref="takeoff_landings")
|
||||
|
||||
__table_args__ = (Index('idx_takeoff_landings_uc', 'timestamp', 'sender_id', 'airport_id', unique=True), )
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block navbar %}
|
||||
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
|
@ -23,7 +23,7 @@
|
|||
<li><a href="{{ url_for('main.senders') }}">Senders</a></li>
|
||||
<li><a href="{{ url_for('main.receivers') }}">Receivers</a></li>
|
||||
<li><a href="{{ url_for('main.airports') }}">Airports</a></li>
|
||||
<li><a href="{{ url_for('main.logbook') }}">Logbook</a></li>
|
||||
<li><a href="{{ url_for('main.logbooks') }}">Logbook</a></li>
|
||||
<li><a href="{{ url_for('main.sender_ranking') }}">Sender Ranking</a></li>
|
||||
<li><a href="{{ url_for('main.receiver_ranking') }}">Receiver Ranking</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -60,8 +60,8 @@
|
|||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{% if ns.mydate != entry.reference.strftime('%Y-%m-%d') %}{% set ns.mydate = entry.reference.strftime('%Y-%m-%d') %}{{ ns.mydate }}{% endif %}</td>
|
||||
<td>{% if entry.takeoff_airport is not none %}<a href="{{ url_for('main.logbook', country=entry.takeoff_airport.country_code, airport_id=entry.takeoff_airport.id, date=entry.reference.strftime('%Y-%m-%d')) }}">{{ entry.takeoff_airport.name }}</a>{% endif %}</td>
|
||||
<td>{% if entry.landing_airport is not none %}<a href="{{ url_for('main.logbook', country=entry.landing_airport.country_code, airport_id=entry.landing_airport.id, date=entry.reference.strftime('%Y-%m-%d')) }}">{{ entry.landing_airport.name }}</a>{% endif %}</td>
|
||||
<td>{% if entry.takeoff_airport is not none %}<a href="{{ url_for('main.logbooks', country=entry.takeoff_airport.country_code, airport_id=entry.takeoff_airport.id, date=entry.reference.strftime('%Y-%m-%d')) }}">{{ entry.takeoff_airport.name }}</a>{% endif %}</td>
|
||||
<td>{% if entry.landing_airport is not none %}<a href="{{ url_for('main.logbooks', country=entry.landing_airport.country_code, airport_id=entry.landing_airport.id, date=entry.reference.strftime('%Y-%m-%d')) }}">{{ entry.landing_airport.name }}</a>{% endif %}</td>
|
||||
<td>{% if entry.takeoff_timestamp is not none %} {{ entry.takeoff_timestamp.strftime('%H:%M') }} {% endif %}</td>
|
||||
<td>{% if entry.landing_timestamp is not none %} {{ entry.landing_timestamp.strftime('%H:%M') }} {% endif %}</td>
|
||||
<td>{% if entry.duration is not none %}{{ entry.duration }}{% endif %}</td>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
</form>
|
||||
|
||||
|
||||
{% if logbook is not none %}
|
||||
{% if logbooks is not none %}
|
||||
<table class="datatable table table-striped table-bordered">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
|
@ -49,7 +49,7 @@
|
|||
<th>AGL</th>
|
||||
<th>Remark</th>
|
||||
</tr>
|
||||
{% for entry in logbook %}
|
||||
{% for entry in logbooks %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>{% set sender = entry.sender %}
|
||||
<td>{{ sender|to_html_link|safe }}</td>
|
||||
|
@ -59,10 +59,10 @@
|
|||
<td>{% if entry.landing_timestamp is not none and entry.landing_airport.id == sel_airport_id %} {{ entry.landing_timestamp.strftime('%H:%M') }} {% endif %}</td>
|
||||
<td>{% if entry.landing_track is not none and entry.landing_airport.id == sel_airport_id %} {{ '%02d' | format(entry.landing_track/10) }} {% endif %}</td>
|
||||
<td>{% if entry.duration is not none %}{{ entry.duration }}{% endif %}</td>
|
||||
<td>{% if entry.max_altitude is not none %}{{ '%0.1f'|format(entry.max_altitude - entry.takeoff_airport.altitude) }} m{% endif %}</td>
|
||||
<td>{% if entry.max_altitude is not none %}{{ '%d' | format(entry.max_altitude - entry.takeoff_airport.altitude) }} m{% endif %}</td>
|
||||
<td>
|
||||
{% if entry.takeoff_airport is not none and entry.takeoff_airport.id != sel_airport_id %}Take Off: <img src="{{ url_for('static', filename='img/Transparent.gif') }}" class="flag flag-{{ entry.takeoff_airport.country_code|lower }}" alt="{{ entry.takeoff_airport.country_code }}"/> <a href="{{ url_for('main.logbook', country=entry.takeoff_airport.country_code, airport_id=entry.takeoff_airport.id, date=sel_date) }}">{{ entry.takeoff_airport.name }}</a>
|
||||
{% elif entry.landing_airport is not none and entry.landing_airport.id != sel_airport_id %}Landing: <img src="{{ url_for('static', filename='img/Transparent.gif') }}" class="flag flag-{{ entry.landing_airport.country_code|lower }}" alt="{{ entry.landing_airport.country_code }}"/> <a href="{{ url_for('main.logbook', country=entry.takeoff_airport.country_code, airport_id=entry.landing_airport.id, date=sel_date) }}">{{ entry.landing_airport.name }}</a>
|
||||
{% if entry.takeoff_airport is not none and entry.takeoff_airport.id != sel_airport_id %}Take Off: <img src="{{ url_for('static', filename='img/Transparent.gif') }}" class="flag flag-{{ entry.takeoff_airport.country_code|lower }}" alt="{{ entry.takeoff_airport.country_code }}"/> <a href="{{ url_for('main.logbooks', country=entry.takeoff_airport.country_code, airport_id=entry.takeoff_airport.id, date=sel_date) }}">{{ entry.takeoff_airport.name }}</a>
|
||||
{% elif entry.landing_airport is not none and entry.landing_airport.id != sel_airport_id %}Landing: <img src="{{ url_for('static', filename='img/Transparent.gif') }}" class="flag flag-{{ entry.landing_airport.country_code|lower }}" alt="{{ entry.landing_airport.country_code }}"/> <a href="{{ url_for('main.logbooks', country=entry.takeoff_airport.country_code, airport_id=entry.landing_airport.id, date=sel_date) }}">{{ entry.landing_airport.name }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
Ładowanie…
Reference in New Issue