Skip to content

Commit

Permalink
Merge f924dc4 into f90f8b2
Browse files Browse the repository at this point in the history
  • Loading branch information
exxamalte committed Nov 11, 2018
2 parents f90f8b2 + f924dc4 commit 5adbcb5
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 154 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,23 @@ the same parameters:
#### Feed

```python
import asyncio
from flightradar24_client.fr24_flights import Flightradar24FlightsFeed
# Home Coordinates: Latitude: -33.5, Longitude: 151.5
feed = Flightradar24FlightsFeed((-33.5, 151.5))
status, entries = feed.update()
LOOP = asyncio.get_event_loop()
status, entries = LOOP.run_until_complete(feed.update())
```

#### Feed Aggregator

```python
import asyncio
from flightradar24_client.fr24_flights import Flightradar24FlightsFeedAggregator
# Home Coordinates: Latitude: -33.5, Longitude: 151.5
feed = Flightradar24FlightsFeedAggregator((-33.5, 151.5))
status, entries = feed.update()
feed_aggregator = Flightradar24FlightsFeedAggregator((-33.5, 151.5))
LOOP = asyncio.get_event_loop()
status, entries = LOOP.run_until_complete(feed_aggregator.update())
```

### Dump1090 Feed
Expand All @@ -73,17 +77,21 @@ the same parameters:
#### Feed

```python
import asyncio
from flightradar24_client.dump1090_aircrafts import Dump1090AircraftsFeed
# Home Coordinates: Latitude: -33.5, Longitude: 151.5
feed = Dump1090AircraftsFeed((-33.5, 151.5))
status, entries = feed.update()
LOOP = asyncio.get_event_loop()
status, entries = LOOP.run_until_complete(feed.update())
```

#### Feed Aggregator

```python
import asyncio
from flightradar24_client.dump1090_aircrafts import Dump1090AircraftsFeedAggregator
# Home Coordinates: Latitude: -33.5, Longitude: 151.5
feed = Dump1090AircraftsFeedAggregator((-33.5, 151.5))
status, entries = feed.update()
feed_aggregator = Dump1090AircraftsFeedAggregator((-33.5, 151.5))
LOOP = asyncio.get_event_loop()
status, entries = LOOP.run_until_complete(feed_aggregator.update())
```
61 changes: 37 additions & 24 deletions flightradar24_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
Format-independent base classes for flights feeds.
"""
import aiohttp
import async_timeout
import asyncio
import collections
import datetime

from typing import Optional

import logging
import requests
from haversine import haversine
from json import JSONDecodeError

from flightradar24_client.consts import UPDATE_OK, UPDATE_ERROR, \
ATTR_LATITUDE, ATTR_LONGITUDE, ATTR_MODE_S, ATTR_ALTITUDE, \
Expand All @@ -32,8 +33,8 @@ def __init__(self, filter_radius=None):
self._filter_radius = filter_radius
self._stack = collections.deque(DEFAULT_AGGREGATOR_STACK_SIZE * [[]],
DEFAULT_AGGREGATOR_STACK_SIZE)
self._callsigns = FixedSizeDict(max=500)
self._coordinates = FixedSizeDict(max=500)
self._callsigns = FixedSizeDict(max=250)
self._coordinates = FixedSizeDict(max=250)

def __repr__(self):
"""Return string representation of this feed aggregator."""
Expand All @@ -45,10 +46,10 @@ def feed(self):
"""Return the external feed access."""
return None

def update(self):
async def update(self):
"""Update from external source, aggregate with previous data and
return filtered entries."""
status, data = self.feed.update()
status, data = await self.feed.update()
if status == UPDATE_OK:
self._stack.pop()
self._stack.appendleft(data)
Expand All @@ -60,13 +61,14 @@ def update(self):
# Fill in callsign from previous update if currently missing.
if not data[key].callsign and key in self._callsigns:
data[key].override(ATTR_CALLSIGN, self._callsigns[key])
# Keep record of coordinates.
# Keep record of latest coordinates.
# Here we are considering (lat=0, lon=0) as unwanted coordinates,
# despite the fact that they are valid. Typically, coordinates
# (0, 0) indicate that the correct coordinates have not been
# received.
if key not in self._coordinates and data[key].coordinates \
and data[key].coordinates is not INVALID_COORDINATES:
if data[key].coordinates \
and data[key].coordinates != INVALID_COORDINATES \
and data[key].coordinates != NONE_COORDINATES:
self._coordinates[key] = data[key].coordinates
# Fill in missing coordinates.
if (not data[key].coordinates
Expand All @@ -91,7 +93,8 @@ def _filter_entries(self, entries):
filtered_entries = list(
filter(lambda entry:
(entry.coordinates is not None) and
(entry.coordinates != (None, None)),
(entry.coordinates != INVALID_COORDINATES) and
(entry.coordinates != NONE_COORDINATES),
filtered_entries))
# Always remove entries on the ground (altitude: 0).
filtered_entries = list(
Expand All @@ -111,13 +114,15 @@ class Feed:
"""Data format independent feed."""

def __init__(self, home_coordinates, apply_filters=True,
filter_radius=None, hostname=None, port=None):
filter_radius=None, hostname=None, port=None, loop=None,
session=None):
"""Initialise feed."""
self._home_coordinates = home_coordinates
self._apply_filters = apply_filters
self._filter_radius = filter_radius
self._loop = loop
self._session = session
self._url = self._create_url(hostname, port)
self._request = requests.Request(method="GET", url=self._url).prepare()

def __repr__(self):
"""Return string representation of this feed."""
Expand All @@ -137,9 +142,9 @@ def _parse(self, json_string):
"""Parse the provided JSON data."""
pass

def update(self):
async def update(self):
"""Update from external source and return filtered entries."""
status, data = self._fetch()
status, data = await self._fetch()
if status == UPDATE_OK:
if data:
feed_entries = []
Expand All @@ -160,26 +165,34 @@ def update(self):
# Error happened while fetching the feed.
return UPDATE_ERROR, None

def _fetch(self):
async def _fetch(self):
"""Fetch JSON data from external source."""
if self._session is None:
self._session = aiohttp.ClientSession()
try:
with requests.Session() as session:
response = session.send(self._request, timeout=10)
if response.ok:
entries = self._parse(response.text)
async with async_timeout.timeout(10, loop=self._loop):
response = await self._session.get(self._url)
# TODO: check more status codes
if response.status == 200:
data = await response.json()
entries = self._parse(data)
return UPDATE_OK, entries
else:
_LOGGER.warning(
"Fetching data from %s failed with status %s",
self._request.url, response.status_code)
self._url, response.status)
return UPDATE_ERROR, None
except requests.exceptions.RequestException as request_ex:
except aiohttp.ClientError as client_error:
_LOGGER.warning("Fetching data from %s failed with %s",
self._request.url, request_ex)
self._url, client_error)
return UPDATE_ERROR, None
except JSONDecodeError as decode_ex:
except asyncio.TimeoutError as timeout_error:
_LOGGER.warning("Fetching data from %s failed with %s",
self._url, timeout_error)
return UPDATE_ERROR, None
except aiohttp.ContentTypeError as decode_ex:
_LOGGER.warning("Unable to parse JSON from %s: %s",
self._request.url, decode_ex)
self._url, decode_ex)
return UPDATE_ERROR, None

def _filter_entries(self, entries):
Expand Down
14 changes: 7 additions & 7 deletions flightradar24_client/dump1090_aircrafts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
Fetches JSON feed from a local Dump1090 aircrafts feed.
"""
import json
import logging

from flightradar24_client import Feed, FeedEntry, FeedAggregator
Expand All @@ -24,11 +23,13 @@ class Dump1090AircraftsFeedAggregator(FeedAggregator):
"""Aggregates date received from the feed over a period of time."""

def __init__(self, home_coordinates, filter_radius=None,
hostname=DEFAULT_HOSTNAME, port=DEFAULT_PORT):
hostname=DEFAULT_HOSTNAME, port=DEFAULT_PORT, loop=None,
session=None):
"""Initialise feed aggregator."""
super().__init__(filter_radius)
self._feed = Dump1090AircraftsFeed(home_coordinates, False,
filter_radius, hostname, port)
filter_radius, hostname, port,
loop, session)

@property
def feed(self):
Expand All @@ -41,9 +42,9 @@ class Dump1090AircraftsFeed(Feed):

def __init__(self, home_coordinates, apply_filters=True,
filter_radius=None, hostname=DEFAULT_HOSTNAME,
port=DEFAULT_PORT):
port=DEFAULT_PORT, loop=None, session=None):
super().__init__(home_coordinates, apply_filters, filter_radius,
hostname, port)
hostname, port, loop, session)

def _create_url(self, hostname, port):
"""Generate the url to retrieve data from."""
Expand All @@ -53,10 +54,9 @@ def _new_entry(self, home_coordinates, feed_data):
"""Generate a new entry."""
return FeedEntry(home_coordinates, feed_data)

def _parse(self, json_string):
def _parse(self, parsed_json):
"""Parse the provided JSON data."""
result = []
parsed_json = json.loads(json_string)
timestamp = None if 'now' not in parsed_json else parsed_json['now']
if 'aircraft' in parsed_json:
aircrafts = parsed_json['aircraft']
Expand Down
15 changes: 8 additions & 7 deletions flightradar24_client/fr24_flights.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
Fetches JSON feed from a local Flightradar24 flights feed.
"""
import json
import logging

from flightradar24_client import Feed, FeedEntry, FeedAggregator
Expand All @@ -23,11 +22,13 @@ class Flightradar24FlightsFeedAggregator(FeedAggregator):
"""Aggregates date received from the feed over a period of time."""

def __init__(self, home_coordinates, filter_radius=None,
hostname=DEFAULT_HOSTNAME, port=DEFAULT_PORT):
hostname=DEFAULT_HOSTNAME, port=DEFAULT_PORT, loop=None,
session=None):
"""Initialise feed aggregator."""
super().__init__(filter_radius)
self._feed = Flightradar24FlightsFeed(home_coordinates, False,
filter_radius, hostname, port)
filter_radius, hostname, port,
loop, session)

@property
def feed(self):
Expand All @@ -40,9 +41,9 @@ class Flightradar24FlightsFeed(Feed):

def __init__(self, home_coordinates, apply_filters=True,
filter_radius=None, hostname=DEFAULT_HOSTNAME,
port=DEFAULT_PORT):
port=DEFAULT_PORT, loop=None, session=None):
super().__init__(home_coordinates, apply_filters, filter_radius,
hostname, port)
hostname, port, loop, session)

def _create_url(self, hostname, port):
"""Generate the url to retrieve data from."""
Expand All @@ -52,10 +53,9 @@ def _new_entry(self, home_coordinates, feed_data):
"""Generate a new entry."""
return FeedEntry(home_coordinates, feed_data)

def _parse(self, json_string):
def _parse(self, parsed_json):
"""Parse the provided JSON data."""
result = []
parsed_json = json.loads(json_string)
for key in parsed_json:
data_entry = parsed_json[key]
result.append({
Expand All @@ -70,4 +70,5 @@ def _parse(self, json_string):
ATTR_VERT_RATE: data_entry[15],
ATTR_CALLSIGN: data_entry[16],
})
_LOGGER.debug("Parser result = %s", result)
return result
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

REQUIRES = [
'haversine>=1.0.1',
'requests>=2.20.0',
'aiohttp',
'async_timeout'
]

setup(
Expand Down

0 comments on commit 5adbcb5

Please sign in to comment.