diff --git a/ogn/backend/liveglidernet.py b/ogn/backend/liveglidernet.py index e4d4e11..1b14481 100644 --- a/ogn/backend/liveglidernet.py +++ b/ogn/backend/liveglidernet.py @@ -1,9 +1,20 @@ -from datetime import datetime, timedelta, date -import os +from datetime import datetime, timedelta, timezone, date -from sqlalchemy import func, and_, between, case, null +from sqlalchemy import func, and_, between, case -from ogn.model import AircraftBeacon, DeviceInfo, Device, Receiver +from ogn.model import AircraftBeacon, Device, Receiver + + +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(session): @@ -34,28 +45,25 @@ def lxml(session, show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_ else: observation_start = datetime.utcnow() - timedelta(minutes=5) - position_query = session.query(Device, AircraftBeacon, DeviceInfo) \ + position_query = session.query(AircraftBeacon, Device) \ .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) + .filter(Device.lastseen == AircraftBeacon.timestamp) \ + .filter(Device.id == AircraftBeacon.device_id) lines = list() lines.append('') lines.append('') - for [aircraft_beacon, device_info] in position_query.all(): - if device_info and (not device_info.tracked or not device_info.identified): - continue + for [aircraft_beacon, device] in position_query.all(): + code = encode(device.address) - code = encode(aircraft_beacon.address) + if len(device.informations) > 0: + device_info = device.informations[0] + if device_info and (not device_info.tracked or not device_info.identified): + continue - 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: @@ -66,12 +74,18 @@ def lxml(session, show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_ else: registration = device_info.registration - address = device_info.address + address = device.address + + else: + device_info = None + competition = ('_' + code[-2:]).lower() + registration = code + address = 0 elapsed_time = datetime.utcnow() - aircraft_beacon.timestamp elapsed_seconds = int(elapsed_time.total_seconds()) - lines.append('' + lines.append(' ' .format(aircraft_beacon.location.latitude, aircraft_beacon.location.longitude, competition, @@ -81,7 +95,7 @@ def lxml(session, show_offline=False, lat_max=90, lat_min=-90, lon_max=180, lon_ elapsed_seconds, int(aircraft_beacon.track), int(aircraft_beacon.ground_speed), - int(aircraft_beacon.climb_rate*10)/10, + int(aircraft_beacon.climb_rate * 10) / 10, aircraft_beacon.aircraft_type, aircraft_beacon.receiver_name, address, diff --git a/ogn/backend/ognrange.py b/ogn/backend/ognrange.py index 48c86ed..b499652 100644 --- a/ogn/backend/ognrange.py +++ b/ogn/backend/ognrange.py @@ -21,8 +21,8 @@ def stations2_filtered_pl(session): 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), + 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'), @@ -30,6 +30,6 @@ def stations2_filtered_pl(session): .order_by(Receiver.lastseen) res = session.execute(query) - stations = json.dumps({"stations": [dict(r) for r in res]}, default=alchemyencoder) + stations = json.dumps({'stations': [dict(r) for r in res]}, default=alchemyencoder) return stations diff --git a/ogn/model/device.py b/ogn/model/device.py index 8bbe02b..b12825e 100644 --- a/ogn/model/device.py +++ b/ogn/model/device.py @@ -19,6 +19,7 @@ class Device(Base): # Relations aircraft_beacons = relationship('AircraftBeacon') + informations = relationship('DeviceInfo', backref="device") def __repr__(self): return "" % ( diff --git a/ogn/model/device_info.py b/ogn/model/device_info.py index a00e2c4..95ad4af 100644 --- a/ogn/model/device_info.py +++ b/ogn/model/device_info.py @@ -21,7 +21,6 @@ class DeviceInfo(Base): # Relations device_id = Column(Integer, ForeignKey('device.id', ondelete='SET NULL'), index=True) - device = relationship('Device', foreign_keys=[device_id]) def __repr__(self): return "" % ( diff --git a/tests/backend/__init__.py b/tests/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/backend/test_backends.py b/tests/backend/test_backends.py index d3ed434..3d93645 100644 --- a/tests/backend/test_backends.py +++ b/tests/backend/test_backends.py @@ -5,10 +5,11 @@ from datetime import datetime from xmlunittest import XmlTestMixin -from ogn.model import Receiver, Device +from ogn.model import AircraftBeacon, Receiver, Device, DeviceInfo from ogn.backend.liveglidernet import rec, lxml from ogn.backend.ognrange import stations2_filtered_pl +from ogn.model.aircraft_type import AircraftType class TestDB(unittest.TestCase, XmlTestMixin): @@ -29,21 +30,37 @@ class TestDB(unittest.TestCase, XmlTestMixin): 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.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') session.add(self.d01) session.add(self.d02) session.commit() + self.di01 = DeviceInfo(registration='D-4711', competition='Hi', tracked=True, identified=True, device_id=self.d01.id) + session.add(self.di01) + session.commit() + + self.ab11 = AircraftBeacon(location_wkt='0101000020E6100000211FF46C56ED26402650D7EDC6E94740', aircraft_type=AircraftType.glider_or_motor_glider, receiver_name='Koenigsdf', timestamp='2017-12-20 10:00:01', track=105, ground_speed=57, climb_rate=-0.5, device_id=self.d01.id) + self.ab12 = AircraftBeacon(location_wkt='0101000020E6100000806DEA295FED2640347D898BB6E94740', aircraft_type=AircraftType.glider_or_motor_glider, receiver_name='Koenigsdf', 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(location_wkt='0101000020E6100000F38B25BF58F22640448B6CE7FBE94740', aircraft_type=AircraftType.powered_aircraft, receiver_name='Koenigsdf', timestamp='2017-12-20 09:54:30', track=280, ground_speed=80, climb_rate=-2.9, device_id=self.d02.id) + self.ab22 = AircraftBeacon(location_wkt='0101000020E6100000A5E8482EFFF12640DC1EAA16FEE94740', aircraft_type=AircraftType.powered_aircraft, receiver_name='Bene', timestamp='2017-12-20 09:56:00', track=270, ground_speed=77, climb_rate=-1.5, altitude=543, device_id=self.d02.id) + session.add(self.ab11) + session.add(self.ab12) + session.add(self.ab21) + session.add(self.ab22) + session.commit() + def tearDown(self): session = self.session + session.execute("DELETE FROM device_info") session.execute("DELETE FROM receiver") + session.execute("DELETE FROM device") + session.execute("DELETE FROM aircraft_beacon") session.commit() @mock.patch('ogn.backend.liveglidernet.datetime') @@ -59,6 +76,7 @@ class TestDB(unittest.TestCase, XmlTestMixin): self.assertXmlNode(root, tag='markers') self.assertXpathsOnlyOne(root, ('./m[@a="Koenigsdf"]', './m[@a="Bene"]', './m[@a="Ohlstadt"]')) + # Check the complete document expected = """ @@ -68,26 +86,26 @@ class TestDB(unittest.TestCase, XmlTestMixin): """.encode(encoding='utf-8') - # Check the complete document self.assertXmlEquivalentOutputs(data, expected) - print(data) - - @unittest.skip("not finished yet") + @mock.patch('ogn.backend.liveglidernet.utc_to_local', side_effect=lambda x: x) @mock.patch('ogn.backend.liveglidernet.datetime') - def test_lxml(self, datetime_mock): + def test_lxml(self, datetime_mock, utc_to_local_mock): session = self.session - datetime_mock.utcnow.return_value = datetime(2017, 12, 20, 10, 0) + datetime_mock.utcnow.return_value = datetime(2017, 12, 20, 10, 0, 5) 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"]')) + # Check the complete document + expected = """ + + + + + """.encode(encoding='utf-8') - print(data) + self.assertXmlEquivalentOutputs(data, expected) @mock.patch('ogn.backend.ognrange.datetime') def test_stations2_filtered_pl(self, datetime_mock): @@ -98,7 +116,6 @@ class TestDB(unittest.TestCase, XmlTestMixin): import json result = stations2_filtered_pl(session) - print(result) data = json.loads(result)