ogn-python/app/commands/export.py

183 wiersze
6.8 KiB
Python
Czysty Zwykły widok Historia

from flask.cli import AppGroup
import click
2017-06-23 06:14:42 +00:00
import datetime
import re
2019-01-31 20:26:20 +00:00
import csv
2020-10-27 19:46:14 +00:00
import os
2017-06-23 06:14:42 +00:00
2020-11-17 13:58:23 +00:00
from sqlalchemy.orm.exc import NoResultFound
2017-10-03 11:31:24 +00:00
from aerofiles.igc import Writer
2020-10-27 19:46:14 +00:00
from app.model import SenderPosition, Sender
2019-08-31 08:14:41 +00:00
from app import db
2017-10-03 11:31:24 +00:00
2019-08-31 08:14:41 +00:00
user_cli = AppGroup("export")
user_cli.help = "Export data in several file formats."
2017-06-23 06:14:42 +00:00
2020-11-22 07:55:19 +00:00
2020-10-27 19:46:14 +00:00
@user_cli.command("debug_sql")
@click.argument("start")
@click.argument("end")
@click.argument("name")
def debug_sql(start, end, name):
"""Export data (sender_positions and receivers) as sql for debugging (and/or creating test cases)."""
# First: get all the positions (and the receiver names for later)
sql_sender_positions = f"""
2020-11-22 07:55:19 +00:00
SELECT reference_timestamp, name, receiver_name, timestamp, location, track, ground_speed, altitude, aircraft_type, climb_rate, turn_rate, distance, bearing, agl
2020-10-27 19:46:14 +00:00
FROM sender_positions
WHERE reference_timestamp BETWEEN '{start}' AND '{end}' AND name = '{name}'
ORDER BY reference_timestamp;
"""
receiver_names = []
sender_position_values = []
results = db.session.execute(sql_sender_positions)
for row in results:
if row[2] not in receiver_names:
receiver_names.append("'" + row[2] + "'")
row = [f"'{r}'" if r else "DEFAULT" for r in row]
sender_position_values.append(f"({','.join(row)})")
2020-11-22 07:55:19 +00:00
2020-10-27 19:46:14 +00:00
# Second: get the receivers
sql_receivers = f"""
SELECT name, location
FROM receivers
WHERE name IN ({','.join(receiver_names)});
"""
receiver_values = []
results = db.session.execute(sql_receivers)
for row in results:
row = [f"'{r}'" if r else "DEFAULT" for r in row]
2020-11-22 07:55:19 +00:00
receiver_values.append(f"({','.join(row)})")
2020-10-27 19:46:14 +00:00
# Third: get the airports
sql_airports = f"""
SELECT DISTINCT a.name, a.location, a.altitude, a.style, a.border
FROM airports AS a, receivers AS r
WHERE
r.name IN ({','.join(receiver_names)})
AND ST_Within(r.location, ST_Buffer(a.location, 0.2))
AND a.style IN (2,4,5);
"""
airport_values = []
results = db.session.execute(sql_airports)
for row in results:
row = [f"'{r}'" if r else "DEFAULT" for r in row]
2020-11-22 07:55:19 +00:00
airport_values.append(f"({','.join(row)})")
2020-10-27 19:46:14 +00:00
# Last: write all into file
with open(f'{start}_{end}_{name}.sql', 'w') as file:
2020-11-22 07:55:19 +00:00
file.write('/*\n')
file.write('OGN Python SQL Export\n')
2020-10-27 19:46:14 +00:00
file.write(f'Created by: {os.getlogin()}\n')
file.write(f'Created at: {datetime.datetime.utcnow()}\n')
2020-11-22 07:55:19 +00:00
file.write('*/\n\n')
2020-10-27 19:46:14 +00:00
file.write("INSERT INTO airports(name, location, altitude, style, border) VALUES\n")
file.write(',\n'.join(airport_values) + ';\n\n')
file.write("INSERT INTO receivers(name, location) VALUES\n")
file.write(',\n'.join(receiver_values) + ';\n\n')
2020-11-22 07:55:19 +00:00
2020-10-27 19:46:14 +00:00
file.write("INSERT INTO sender_positions(reference_timestamp, name, receiver_name, timestamp, location, track, ground_speed, altitude, aircraft_type, climb_rate, turn_rate, distance, bearing, agl) VALUES\n")
file.write(',\n'.join(sender_position_values) + ';\n\n')
2017-06-23 06:14:42 +00:00
2019-08-31 08:14:41 +00:00
@user_cli.command("cup")
2019-01-31 20:26:20 +00:00
def cup():
"""Export receiver waypoints as '.cup'."""
sql = """
SELECT
'OGN-' || sq.name AS name,
sq.name AS code,
c.iso2 AS country,
CASE WHEN sq.lat_deg < 10 THEN '0' ELSE '' END || CAST((sq.lat_deg*100 + sq.lat_min) AS decimal(18, 5)) || sq.lat_sig AS lat,
CASE WHEN sq.lon_deg < 10 THEN '00' WHEN sq.lon_deg < 100 THEN '0' ELSE '' END || CAST(sq.lon_deg*100 + sq.lon_min AS decimal(18, 5)) || sq.lon_sig AS lon,
altitude || 'm' AS elev,
'8' AS style,
'' AS rwdir,
'' AS rwlen,
'' AS freq,
'lastseen: ' || sq.lastseen::date || ', version: ' || sq.version || ', platform: ' || sq.platform AS desc
FROM (
SELECT
st_y(location) as lat,
CASE WHEN ST_Y(location) > 0 THEN 'N' ELSE 'S' END AS lat_sig,
FLOOR(ABS(ST_Y(location))) AS lat_deg,
60*(ABS(ST_Y(location)) - FLOOR(ABS(ST_Y(location)))) AS lat_min,
st_x(location) AS lon,
CASE WHEN ST_X(location) > 0 THEN 'E' ELSE 'W' END AS lon_sig,
FLOOR(ABS(ST_X(location))) AS lon_deg,
60*(ABS(ST_X(location)) - FLOOR(ABS(ST_X(location)))) AS lon_min
, *
FROM receivers
WHERE lastseen - firstseen > INTERVAL'3 MONTH' AND lastseen > '2018-01-01 00:00:00' AND name NOT LIKE 'FNB%'
) sq
INNER JOIN countries c ON c.gid = sq.country_id
ORDER BY sq.name;
"""
results = db.session.execute(sql)
2019-01-31 20:26:20 +00:00
2019-08-31 08:14:41 +00:00
with open("receivers.cup", "w") as outfile:
2019-01-31 20:26:20 +00:00
outcsv = csv.writer(outfile)
outcsv.writerow(results.keys())
outcsv.writerows(results.fetchall())
2019-08-31 08:14:41 +00:00
@user_cli.command("igc")
@click.argument("address")
@click.argument("date")
2019-01-31 20:26:20 +00:00
def igc(address, date):
2017-06-23 06:14:42 +00:00
"""Export igc file for <address> at <date>."""
2020-11-17 13:58:23 +00:00
if not re.match("[0-9A-F]{6}", address):
print(f"Address '{address}' not valid.")
2017-06-23 06:14:42 +00:00
return
2020-11-17 13:58:23 +00:00
try:
2020-11-22 07:55:19 +00:00
sender = db.session.query(Sender).filter(Sender.address == address).one()
2020-11-17 13:58:23 +00:00
except NoResultFound as e:
print(f"No data for '{address}' in the DB")
2017-06-23 06:14:42 +00:00
return
2020-11-17 13:58:23 +00:00
if not re.match(r"\d{4}-\d{2}-\d{2}", date):
print(f"Date {date} not valid.")
2017-06-23 06:14:42 +00:00
return
2019-08-31 08:14:41 +00:00
with open("sample.igc", "wb") as fp:
2017-06-23 06:14:42 +00:00
writer = Writer(fp)
2019-08-31 08:14:41 +00:00
writer.write_headers(
{
"manufacturer_code": "OGN",
"logger_id": "OGN",
"date": datetime.date(1987, 2, 24),
"fix_accuracy": 50,
2020-11-17 13:58:23 +00:00
"pilot": "Unknown",
2019-08-31 08:14:41 +00:00
"copilot": "",
2020-11-17 13:58:23 +00:00
"glider_type": sender.infos[0].aircraft if len(sender.infos) > 0 else '',
"glider_id": sender.infos[0].registration if len(sender.infos) > 0 else '',
"firmware_version": sender.software_version,
"hardware_version": sender.hardware_version,
"logger_type": "OGN",
"gps_receiver": "unknown",
"pressure_sensor": "unknown",
"competition_id": sender.infos[0].competition if len(sender.infos) > 0 else '',
"competition_class": "unknown",
2019-08-31 08:14:41 +00:00
}
)
points = (
2020-10-27 19:46:14 +00:00
db.session.query(SenderPosition)
2020-11-22 07:55:19 +00:00
.filter(db.between(SenderPosition.reference_timestamp, f"{date} 00:00:00", f"{date} 23:59:59"))
.filter(SenderPosition.name == sender.name)
.order_by(SenderPosition.timestamp)
2019-08-31 08:14:41 +00:00
)
2017-06-23 06:14:42 +00:00
2020-11-17 13:58:23 +00:00
for point in points:
2019-08-31 08:14:41 +00:00
writer.write_fix(point.timestamp.time(), latitude=point.location.latitude, longitude=point.location.longitude, valid=True, pressure_alt=point.altitude, gps_alt=point.altitude)