Add backend for live.glidernet.org and ognrange.onglide.com (WIP)

pull/68/head
Konstantin Gründger 2017-12-21 22:43:22 +01:00
rodzic 347927ba39
commit 7729dca315
5 zmienionych plików z 261 dodań i 1 usunięć

Wyświetl plik

Wyświetl plik

@ -0,0 +1,93 @@
from datetime import datetime, timedelta, date
import os
from sqlalchemy import func, and_, between, case, null
from ogn.model import AircraftBeacon, DeviceInfo, Device, Receiver
def rec(session):
last_10_minutes = datetime.utcnow() - timedelta(minutes=10)
receiver_query = session.query(Receiver,
case([(Receiver.lastseen > last_10_minutes, True)],
else_=False).label('is_online')) \
.order_by(Receiver.name)
lines = list()
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
lines.append('<markers>')
lines.append('<m e="0"/>')
for [receiver, is_online] in receiver_query.all():
lines.append('<m a="{0}" b="{1:.7f}" c="{2:.7f}" d="{3:1d}"/>'
.format(receiver.name, receiver.location.latitude, receiver.location.longitude, is_online))
lines.append('</markers>')
xml = '\n'.join(lines)
return xml
def lxml(session, show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_min=-180):
if show_offline:
observation_start = date.today()
else:
observation_start = datetime.utcnow() - timedelta(minutes=5)
position_query = session.query(Device, AircraftBeacon, DeviceInfo) \
.filter(and_(between(func.ST_Y(AircraftBeacon.location_wkt), lat_min, lat_max),
between(func.ST_X(AircraftBeacon.location_wkt), lon_min, lon_max))) \
.filter(Device.lastseen > observation_start) \
.filter(Device.last_position_beacon_id == AircraftBeacon.id) \
.outerjoin(DeviceInfo, DeviceInfo.device_id == Device.id)
lines = list()
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
lines.append('<markers>')
for [aircraft_beacon, device_info] in position_query.all():
if device_info and (not device_info.tracked or not device_info.identified):
continue
code = encode(aircraft_beacon.address)
if device_info is None:
competition = ('_' + code[-2:]).lower()
registration = code
address = 0
else:
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_info.address
elapsed_time = datetime.utcnow() - aircraft_beacon.timestamp
elapsed_seconds = int(elapsed_time.total_seconds())
lines.append('<m a="{0:.7f},{1:.7f},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13}"/>'
.format(aircraft_beacon.location.latitude,
aircraft_beacon.location.longitude,
competition,
registration,
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('</markers>')
xml = '\n'.join(lines)
return xml

Wyświetl plik

@ -0,0 +1,35 @@
import json
from datetime import datetime, timedelta
from sqlalchemy import func, case
from sqlalchemy.sql.expression import label
from ogn.model import Receiver
def alchemyencoder(obj):
import decimal
from datetime import datetime
"""JSON encoder function for SQLAlchemy special classes."""
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M')
elif isinstance(obj, decimal.Decimal):
return float(obj)
def stations2_filtered_pl(session):
last_10_minutes = datetime.utcnow() - timedelta(minutes=10)
query = 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)
res = session.execute(query)
stations = json.dumps({"stations": [dict(r) for r in res]}, default=alchemyencoder)
return stations

Wyświetl plik

@ -47,7 +47,8 @@ setup(
'dev': [
'nose==1.3.7',
'coveralls==1.2',
'flake8==3.5.0'
'flake8==3.5.0',
'xmlunittest==0.4.0'
]
},
zip_safe=False

Wyświetl plik

@ -0,0 +1,131 @@
import unittest
from unittest import mock
import os
from datetime import datetime
from xmlunittest import XmlTestMixin
from ogn.model import Receiver, Device
from ogn.backend.liveglidernet import rec, lxml
from ogn.backend.ognrange import stations2_filtered_pl
class TestDB(unittest.TestCase, XmlTestMixin):
session = None
engine = None
app = None
def setUp(self):
os.environ['OGN_CONFIG_MODULE'] = 'config.test'
from ogn.commands.dbutils import engine, session
self.session = session
self.engine = engine
from ogn.commands.database import init
init()
# 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.d01 = Device(address='DD4711')
self.d02 = Device(address='DD0815')
session.add(self.r01)
session.add(self.r02)
session.add(self.r03)
session.add(self.d01)
session.add(self.d02)
session.commit()
def tearDown(self):
session = self.session
session.execute("DELETE FROM receiver")
session.commit()
@mock.patch('ogn.backend.liveglidernet.datetime')
def test_rec(self, datetime_mock):
session = self.session
datetime_mock.utcnow.return_value = datetime(2017, 12, 20, 10, 0)
data = rec(session).encode(encoding='utf-8')
# Check the document
root = self.assertXmlDocument(data)
self.assertXmlNode(root, tag='markers')
self.assertXpathsOnlyOne(root, ('./m[@a="Koenigsdf"]', './m[@a="Bene"]', './m[@a="Ohlstadt"]'))
expected = """<?xml version="1.0" encoding="UTF-8"?>
<markers>
<m e="0"/>
<m a="Bene" b="47.7158333" c="11.3905500" d="0"/>
<m a="Koenigsdf" b="47.8286167" c="11.4661167" d="1"/>
<m a="Ohlstadt" b="47.6577833" c="11.2338833" d="1"/>
</markers>
""".encode(encoding='utf-8')
# Check the complete document
self.assertXmlEquivalentOutputs(data, expected)
print(data)
@unittest.skip("not finished yet")
@mock.patch('ogn.backend.liveglidernet.datetime')
def test_lxml(self, datetime_mock):
session = self.session
datetime_mock.utcnow.return_value = datetime(2017, 12, 20, 10, 0)
data = lxml(session).encode(encoding='utf-8')
# Check the document
root = self.assertXmlDocument(data)
self.assertXmlNode(root, tag='markers')
self.assertXpathsOnlyOne(root, ('./m[@a="Koenigsdf"]', './m[@a="Bene"]', './m[@a="Ohlstadt"]'))
print(data)
@mock.patch('ogn.backend.ognrange.datetime')
def test_stations2_filtered_pl(self, datetime_mock):
session = self.session
datetime_mock.utcnow.return_value = datetime(2017, 12, 20, 10, 0)
import json
result = stations2_filtered_pl(session)
print(result)
data = json.loads(result)
stations = data["stations"]
self.assertEqual(len(stations), 3)
s1 = stations[0]
s2 = stations[1]
s3 = stations[2]
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
self.assertEqual(s1["ut"], "2017-12-20 09:45")
# self.assertEqual(s1["b"], 0)
self.assertEqual(s1["v"], "0.2.7.x64")
self.assertEqual(s2["s"], 'Koenigsdf')
self.assertEqual(s2["lt"], 47.8286)
self.assertEqual(s2["lg"], 11.4661)
self.assertEqual(s2["u"], "U")
self.assertEqual(s2["ut"], "2017-12-20 10:00")
# self.assertEqual(s2["b"], 0)
self.assertEqual(s2["v"], "0.2.5.ARM")
self.assertEqual(s3["s"], 'Ohlstadt')
if __name__ == '__main__':
unittest.main()