diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3cb791c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +repos: + - repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + language_version: python3 diff --git a/aio_georss_client/consts.py b/aio_georss_client/consts.py index a590d5e..6d20870 100644 --- a/aio_georss_client/consts.py +++ b/aio_georss_client/consts.py @@ -1,63 +1,63 @@ """Constants for feeds and feed entries.""" -ATTR_ATTRIBUTION = 'attribution' -CUSTOM_ATTRIBUTE = 'custom_attribute' +ATTR_ATTRIBUTION = "attribution" +CUSTOM_ATTRIBUTE = "custom_attribute" DEFAULT_REQUEST_TIMEOUT = 10 -UPDATE_OK = 'OK' -UPDATE_OK_NO_DATA = 'OK_NO_DATA' -UPDATE_ERROR = 'ERROR' +UPDATE_OK = "OK" +UPDATE_OK_NO_DATA = "OK_NO_DATA" +UPDATE_ERROR = "ERROR" -XML_ATTR_HREF = '@href' -XML_ATTR_TERM = '@term' +XML_ATTR_HREF = "@href" +XML_ATTR_TERM = "@term" -XML_CDATA = '#text' +XML_CDATA = "#text" -XML_TAG_AUTHOR = 'author' -XML_TAG_CATEGORY = 'category' -XML_TAG_CHANNEL = 'channel' -XML_TAG_CONTENT = 'content' -XML_TAG_CONTRIBUTOR = 'contributor' -XML_TAG_COPYRIGHT = 'copyright' -XML_TAG_DC_DATE = 'dc:date' -XML_TAG_DESCRIPTION = 'description' -XML_TAG_DOCS = 'docs' -XML_TAG_ENTRY = 'entry' -XML_TAG_FEED = 'feed' -XML_TAG_GDACS_BBOX = 'gdacs:bbox' -XML_TAG_GENERATOR = 'generator' -XML_TAG_GEO_LAT = 'geo:lat' -XML_TAG_GEO_LONG = 'geo:long' -XML_TAG_GEO_POINT = 'geo:Point' -XML_TAG_GEORSS_POINT = 'georss:point' -XML_TAG_GEORSS_POLYGON = 'georss:polygon' -XML_TAG_GEORSS_WHERE = 'georss:where' -XML_TAG_GML_EXTERIOR = 'gml:exterior' -XML_TAG_GML_LINEAR_RING = 'gml:LinearRing' -XML_TAG_GML_POINT = 'gml:Point' -XML_TAG_GML_POLYGON = 'gml:Polygon' -XML_TAG_GML_POS = 'gml:pos' -XML_TAG_GML_POS_LIST = 'gml:posList' -XML_TAG_GUID = 'guid' -XML_TAG_HEIGHT = 'height' -XML_TAG_ID = 'id' -XML_TAG_IMAGE = 'image' -XML_TAG_ITEM = 'item' -XML_TAG_LANGUAGE = 'language' -XML_TAG_LAST_BUILD_DATE = 'lastBuildDate' -XML_TAG_LINK = 'link' -XML_TAG_MANAGING_EDITOR = 'managingEditor' -XML_TAG_NAME = 'name' -XML_TAG_PUB_DATE = 'pubDate' -XML_TAG_PUBLISHED = 'published' -XML_TAG_RIGHTS = 'rights' -XML_TAG_RSS = 'rss' -XML_TAG_SOURCE = 'source' -XML_TAG_SUBTITLE = 'subtitle' -XML_TAG_SUMMARY = 'summary' -XML_TAG_TITLE = 'title' -XML_TAG_TTL = 'ttl' -XML_TAG_UPDATED = 'updated' -XML_TAG_URL = 'url' -XML_TAG_WIDTH = 'width' +XML_TAG_AUTHOR = "author" +XML_TAG_CATEGORY = "category" +XML_TAG_CHANNEL = "channel" +XML_TAG_CONTENT = "content" +XML_TAG_CONTRIBUTOR = "contributor" +XML_TAG_COPYRIGHT = "copyright" +XML_TAG_DC_DATE = "dc:date" +XML_TAG_DESCRIPTION = "description" +XML_TAG_DOCS = "docs" +XML_TAG_ENTRY = "entry" +XML_TAG_FEED = "feed" +XML_TAG_GDACS_BBOX = "gdacs:bbox" +XML_TAG_GENERATOR = "generator" +XML_TAG_GEO_LAT = "geo:lat" +XML_TAG_GEO_LONG = "geo:long" +XML_TAG_GEO_POINT = "geo:Point" +XML_TAG_GEORSS_POINT = "georss:point" +XML_TAG_GEORSS_POLYGON = "georss:polygon" +XML_TAG_GEORSS_WHERE = "georss:where" +XML_TAG_GML_EXTERIOR = "gml:exterior" +XML_TAG_GML_LINEAR_RING = "gml:LinearRing" +XML_TAG_GML_POINT = "gml:Point" +XML_TAG_GML_POLYGON = "gml:Polygon" +XML_TAG_GML_POS = "gml:pos" +XML_TAG_GML_POS_LIST = "gml:posList" +XML_TAG_GUID = "guid" +XML_TAG_HEIGHT = "height" +XML_TAG_ID = "id" +XML_TAG_IMAGE = "image" +XML_TAG_ITEM = "item" +XML_TAG_LANGUAGE = "language" +XML_TAG_LAST_BUILD_DATE = "lastBuildDate" +XML_TAG_LINK = "link" +XML_TAG_MANAGING_EDITOR = "managingEditor" +XML_TAG_NAME = "name" +XML_TAG_PUB_DATE = "pubDate" +XML_TAG_PUBLISHED = "published" +XML_TAG_RIGHTS = "rights" +XML_TAG_RSS = "rss" +XML_TAG_SOURCE = "source" +XML_TAG_SUBTITLE = "subtitle" +XML_TAG_SUMMARY = "summary" +XML_TAG_TITLE = "title" +XML_TAG_TTL = "ttl" +XML_TAG_UPDATED = "updated" +XML_TAG_URL = "url" +XML_TAG_WIDTH = "width" diff --git a/aio_georss_client/feed.py b/aio_georss_client/feed.py index bdc3aa6..aa2729d 100644 --- a/aio_georss_client/feed.py +++ b/aio_georss_client/feed.py @@ -9,8 +9,13 @@ import aiohttp from aiohttp import ClientSession, client_exceptions -from .consts import (ATTR_ATTRIBUTION, DEFAULT_REQUEST_TIMEOUT, UPDATE_ERROR, - UPDATE_OK, UPDATE_OK_NO_DATA) +from .consts import ( + ATTR_ATTRIBUTION, + DEFAULT_REQUEST_TIMEOUT, + UPDATE_ERROR, + UPDATE_OK, + UPDATE_OK_NO_DATA, +) from .feed_entry import FeedEntry from .xml_parser import Feed, XmlParser from .xml_parser.feed_item import FeedItem @@ -23,12 +28,14 @@ class GeoRssFeed(Generic[T_FEED_ENTRY], ABC): """GeoRSS feed base class.""" - def __init__(self, - websession: ClientSession, - home_coordinates: Tuple[float, float], - url: str, - filter_radius: float = None, - filter_categories: List[str] = None): + def __init__( + self, + websession: ClientSession, + home_coordinates: Tuple[float, float], + url: str, + filter_radius: float = None, + filter_categories: List[str] = None, + ): """Initialise this service.""" self._websession = websession self._home_coordinates = home_coordinates @@ -39,15 +46,21 @@ def __init__(self, def __repr__(self): """Return string representation of this feed.""" - return '<{}(home={}, url={}, radius={}, categories={})>'.format( - self.__class__.__name__, self._home_coordinates, self._url, - self._filter_radius, self._filter_categories) + return "<{}(home={}, url={}, radius={}, categories={})>".format( + self.__class__.__name__, + self._home_coordinates, + self._url, + self._filter_radius, + self._filter_categories, + ) @abstractmethod - def _new_entry(self, - home_coordinates: Tuple[float, float], - rss_entry: FeedItem, - global_data: Dict) -> T_FEED_ENTRY: + def _new_entry( + self, + home_coordinates: Tuple[float, float], + rss_entry: FeedItem, + global_data: Dict, + ) -> T_FEED_ENTRY: """Generate a new entry.""" pass @@ -68,11 +81,11 @@ async def update(self) -> Tuple[str, Optional[List[T_FEED_ENTRY]]]: global_data = self._extract_from_feed(rss_data) # Extract data from feed entries. for rss_entry in rss_data.entries: - entries.append(self._new_entry(self._home_coordinates, - rss_entry, global_data)) + entries.append( + self._new_entry(self._home_coordinates, rss_entry, global_data) + ) filtered_entries = self._filter_entries(entries) - self._last_timestamp = self._extract_last_timestamp( - filtered_entries) + self._last_timestamp = self._extract_last_timestamp(filtered_entries) return UPDATE_OK, filtered_entries else: # Should not happen. @@ -85,17 +98,14 @@ async def update(self) -> Tuple[str, Optional[List[T_FEED_ENTRY]]]: self._last_timestamp = None return UPDATE_ERROR, None - async def _fetch(self, - method: str = "GET", - headers=None, - params=None) -> Tuple[str, Optional[Feed]]: + async def _fetch( + self, method: str = "GET", headers=None, params=None + ) -> Tuple[str, Optional[Feed]]: """Fetch GeoRSS data from external source.""" try: - timeout = aiohttp.ClientTimeout( - total=self._client_session_timeout()) + timeout = aiohttp.ClientTimeout(total=self._client_session_timeout()) async with self._websession.request( - method, self._url, headers=headers, params=params, - timeout=timeout + method, self._url, headers=headers, params=params, timeout=timeout ) as response: try: response.raise_for_status() @@ -106,17 +116,21 @@ async def _fetch(self, self.feed_data = feed_data return UPDATE_OK, feed_data except client_exceptions.ClientError as client_error: - _LOGGER.warning("Fetching data from %s failed with %s", - self._url, client_error) + _LOGGER.warning( + "Fetching data from %s failed with %s", self._url, client_error + ) return UPDATE_ERROR, None except client_exceptions.ClientError as client_error: - _LOGGER.warning("Requesting data from %s failed with " - "client error: %s", - self._url, client_error) + _LOGGER.warning( + "Requesting data from %s failed with " "client error: %s", + self._url, + client_error, + ) return UPDATE_ERROR, None except asyncio.TimeoutError: - _LOGGER.warning("Requesting data from %s failed with " - "timeout error", self._url) + _LOGGER.warning( + "Requesting data from %s failed with " "timeout error", self._url + ) return UPDATE_ERROR, None async def _read_response(self, response): @@ -125,7 +139,7 @@ async def _read_response(self, response): raw_response = await response.read() _LOGGER.debug("Response encoding %s", response.get_encoding()) if raw_response.startswith(codecs.BOM_UTF8): - return await response.text('utf-8-sig') + return await response.text("utf-8-sig") return await response.text() return None @@ -135,22 +149,31 @@ def _filter_entries(self, entries: List[T_FEED_ENTRY]): _LOGGER.debug("Entries before filtering %s", filtered_entries) # Always remove entries without geometry filtered_entries = list( - filter(lambda entry: - entry.geometries is not None and len(entry.geometries) >= 1, - filtered_entries)) + filter( + lambda entry: entry.geometries is not None + and len(entry.geometries) >= 1, + filtered_entries, + ) + ) # Filter by distance. if self._filter_radius: filtered_entries = list( - filter(lambda entry: - entry.distance_to_home <= self._filter_radius, - filtered_entries)) + filter( + lambda entry: entry.distance_to_home <= self._filter_radius, + filtered_entries, + ) + ) # Filter by category. if self._filter_categories: filtered_entries = list( - filter(lambda entry: - len({entry.category}.intersection( - self._filter_categories)) > 0, - filtered_entries)) + filter( + lambda entry: len( + {entry.category}.intersection(self._filter_categories) + ) + > 0, + filtered_entries, + ) + ) _LOGGER.debug("Entries after filtering %s", filtered_entries) return filtered_entries @@ -163,12 +186,14 @@ def _extract_from_feed(self, feed: Feed) -> Dict: return global_data def _extract_last_timestamp( - self, feed_entries: List[T_FEED_ENTRY]) -> Optional[datetime]: + self, feed_entries: List[T_FEED_ENTRY] + ) -> Optional[datetime]: """Determine latest (newest) entry from the filtered feed.""" if feed_entries: dates = sorted( [entry.published for entry in feed_entries if entry.published], - reverse=True) + reverse=True, + ) if dates: last_timestamp = dates[0] _LOGGER.debug("Last timestamp: %s", last_timestamp) diff --git a/aio_georss_client/feed_entry.py b/aio_georss_client/feed_entry.py index c588ca5..eb534ef 100644 --- a/aio_georss_client/feed_entry.py +++ b/aio_georss_client/feed_entry.py @@ -18,16 +18,14 @@ class FeedEntry(ABC): """Feed entry base class.""" - def __init__(self, - home_coordinates: Tuple[float, float], - rss_entry: FeedItem): + def __init__(self, home_coordinates: Tuple[float, float], rss_entry: FeedItem): """Initialise this feed entry.""" self._home_coordinates = home_coordinates self._rss_entry = rss_entry def __repr__(self): """Return string representation of this entry.""" - return '<{}(id={})>'.format(self.__class__.__name__, self.external_id) + return "<{}(id={})>".format(self.__class__.__name__, self.external_id) @property def features(self) -> List[Type[Geometry]]: @@ -39,8 +37,9 @@ def geometries(self) -> Optional[List[Geometry]]: """Return all geometries of this entry.""" if self._rss_entry: # Return all geometries that are of type defined in features. - return list(filter(lambda x: type(x) in self.features, - self._rss_entry.geometries)) + return list( + filter(lambda x: type(x) in self.features, self._rss_entry.geometries) + ) return None @property @@ -95,8 +94,11 @@ def _search_in_title(self, regexp): @property def category(self) -> Optional[str]: """Return the category of this entry.""" - if self._rss_entry and self._rss_entry.category \ - and isinstance(self._rss_entry.category, list): + if ( + self._rss_entry + and self._rss_entry.category + and isinstance(self._rss_entry.category, list) + ): # To keep this simple, just return the first category. return self._rss_entry.category[0] return None @@ -115,9 +117,12 @@ def distance_to_home(self) -> float: distance = float("inf") if self.geometries and len(self.geometries) >= 1: for geometry in self.geometries: - distance = min(distance, - GeoRssDistanceHelper.distance_to_geometry( - self._home_coordinates, geometry)) + distance = min( + distance, + GeoRssDistanceHelper.distance_to_geometry( + self._home_coordinates, geometry + ), + ) return distance @property @@ -152,5 +157,4 @@ def _search_in_description(self, regexp): @staticmethod def _string2boolean(value: str) -> bool: """Convert value to boolean.""" - return isinstance(value, str) \ - and value.strip().lower() in {'true', 'yes', '1'} + return isinstance(value, str) and value.strip().lower() in {"true", "yes", "1"} diff --git a/aio_georss_client/feed_manager.py b/aio_georss_client/feed_manager.py index c66b60b..de5d8d6 100644 --- a/aio_georss_client/feed_manager.py +++ b/aio_georss_client/feed_manager.py @@ -18,13 +18,14 @@ class FeedManagerBase: """Generic Feed manager.""" - def __init__(self, - feed: GeoRssFeed, - generate_async_callback: Callable[[str], Awaitable[None]], - update_async_callback: Callable[[str], Awaitable[None]], - remove_async_callback: Callable[[str], Awaitable[None]], - status_async_callback: Callable[[StatusUpdate], - Awaitable[None]] = None): + def __init__( + self, + feed: GeoRssFeed, + generate_async_callback: Callable[[str], Awaitable[None]], + update_async_callback: Callable[[str], Awaitable[None]], + remove_async_callback: Callable[[str], Awaitable[None]], + status_async_callback: Callable[[StatusUpdate], Awaitable[None]] = None, + ): """Initialise feed manager.""" self._feed = feed self.feed_entries = {} @@ -38,8 +39,7 @@ def __init__(self, def __repr__(self): """Return string representation of this feed.""" - return '<{}(feed={})>'.format( - self.__class__.__name__, self._feed) + return "<{}(feed={})>".format(self.__class__.__name__, self._feed) async def update(self): """Update the feed and then update connected entities.""" @@ -55,62 +55,50 @@ async def update(self): # Record current time of update. self._last_update_successful = self._last_update # For entity management the external ids from the feed are used. - feed_external_ids = set([entry.external_id - for entry in feed_entries]) - count_removed = await self._update_feed_remove_entries( - feed_external_ids) - count_updated = await self._update_feed_update_entries( - feed_external_ids) - count_created = await self._update_feed_create_entries( - feed_external_ids) + feed_external_ids = set([entry.external_id for entry in feed_entries]) + count_removed = await self._update_feed_remove_entries(feed_external_ids) + count_updated = await self._update_feed_update_entries(feed_external_ids) + count_created = await self._update_feed_create_entries(feed_external_ids) elif status == UPDATE_OK_NO_DATA: - _LOGGER.debug( - "Update successful, but no data received from %s", self._feed) + _LOGGER.debug("Update successful, but no data received from %s", self._feed) # Record current time of update. self._last_update_successful = self._last_update else: _LOGGER.warning( - "Update not successful, no data received from %s", self._feed) + "Update not successful, no data received from %s", self._feed + ) # Remove all entities. count_removed = await self._update_feed_remove_entries(set()) # Send status update to subscriber. - await self._status_update(status, count_created, count_updated, - count_removed) + await self._status_update(status, count_created, count_updated, count_removed) - async def _store_feed_entries(self, - status: str, - feed_entries: Optional[List[FeedEntry]]): + async def _store_feed_entries( + self, status: str, feed_entries: Optional[List[FeedEntry]] + ): """Keep a copy of all feed entries for future lookups.""" if feed_entries or status == UPDATE_OK_NO_DATA: if status == UPDATE_OK: - self.feed_entries = {entry.external_id: entry - for entry in feed_entries} + self.feed_entries = {entry.external_id: entry for entry in feed_entries} else: self.feed_entries.clear() - async def _update_feed_create_entries(self, - feed_external_ids: Set[str]) -> int: + async def _update_feed_create_entries(self, feed_external_ids: Set[str]) -> int: """Create entities after feed update.""" - create_external_ids = feed_external_ids.difference( - self._managed_external_ids) + create_external_ids = feed_external_ids.difference(self._managed_external_ids) count_created = len(create_external_ids) await self._generate_new_entities(create_external_ids) return count_created - async def _update_feed_update_entries(self, - feed_external_ids: Set[str]) -> int: + async def _update_feed_update_entries(self, feed_external_ids: Set[str]) -> int: """Update entities after feed update.""" - update_external_ids = self._managed_external_ids.intersection( - feed_external_ids) + update_external_ids = self._managed_external_ids.intersection(feed_external_ids) count_updated = len(update_external_ids) await self._update_entities(update_external_ids) return count_updated - async def _update_feed_remove_entries(self, - feed_external_ids: Set[str]) -> int: + async def _update_feed_remove_entries(self, feed_external_ids: Set[str]) -> int: """Remove entities after feed update.""" - remove_external_ids = self._managed_external_ids.difference( - feed_external_ids) + remove_external_ids = self._managed_external_ids.difference(feed_external_ids) count_removed = len(remove_external_ids) await self._remove_entities(remove_external_ids) return count_removed @@ -135,22 +123,23 @@ async def _remove_entities(self, external_ids: Set[str]): self._managed_external_ids.remove(external_id) await self._remove_async_callback(external_id) - async def _status_update(self, - status: str, - count_created: int, - count_updated: int, - count_removed: int): + async def _status_update( + self, status: str, count_created: int, count_updated: int, count_removed: int + ): """Provide status update.""" if self._status_async_callback: await self._status_async_callback( - StatusUpdate(status, - self.last_update, - self.last_update_successful, - self.last_timestamp, - len(self.feed_entries), - count_created, - count_updated, - count_removed)) + StatusUpdate( + status, + self.last_update, + self.last_update_successful, + self.last_timestamp, + len(self.feed_entries), + count_created, + count_updated, + count_removed, + ) + ) @property def last_timestamp(self) -> Optional[datetime]: diff --git a/aio_georss_client/geo_rss_distance_helper.py b/aio_georss_client/geo_rss_distance_helper.py index 406fead..d9acf09 100644 --- a/aio_georss_client/geo_rss_distance_helper.py +++ b/aio_georss_client/geo_rss_distance_helper.py @@ -17,8 +17,7 @@ def __init__(self): pass @staticmethod - def extract_coordinates(geometry: Geometry) \ - -> Optional[Tuple[float, float]]: + def extract_coordinates(geometry: Geometry) -> Optional[Tuple[float, float]]: """Extract the best coordinates from the feature for display.""" latitude = longitude = None if isinstance(geometry, Point): @@ -27,41 +26,47 @@ def extract_coordinates(geometry: Geometry) \ elif isinstance(geometry, (Polygon, BoundingBox)): centroid = geometry.centroid latitude, longitude = centroid.latitude, centroid.longitude - _LOGGER.debug("Centroid of %s is %s", geometry, - (latitude, longitude)) + _LOGGER.debug("Centroid of %s is %s", geometry, (latitude, longitude)) else: _LOGGER.debug("Not implemented: %s", type(geometry)) return latitude, longitude @staticmethod - def distance_to_geometry(home_coordinates: Tuple[float, float], - geometry: Geometry) -> float: + def distance_to_geometry( + home_coordinates: Tuple[float, float], geometry: Geometry + ) -> float: """Calculate the distance between home coordinates and geometry.""" distance = float("inf") if isinstance(geometry, Point): distance = GeoRssDistanceHelper._distance_to_point( - home_coordinates, geometry) + home_coordinates, geometry + ) elif isinstance(geometry, Polygon): distance = GeoRssDistanceHelper._distance_to_polygon( - home_coordinates, geometry) + home_coordinates, geometry + ) elif isinstance(geometry, BoundingBox): distance = GeoRssDistanceHelper._distance_to_bounding_box( - home_coordinates, geometry) + home_coordinates, geometry + ) else: _LOGGER.debug("Not implemented: %s", type(geometry)) return distance @staticmethod - def _distance_to_point(home_coordinates: Tuple[float, float], - point: Point) -> float: + def _distance_to_point( + home_coordinates: Tuple[float, float], point: Point + ) -> float: """Calculate the distance between home coordinates and the point.""" # Swap coordinates to match: (latitude, longitude). return GeoRssDistanceHelper._distance_to_coordinates( - home_coordinates, (point.latitude, point.longitude)) + home_coordinates, (point.latitude, point.longitude) + ) @staticmethod - def _distance_to_polygon(home_coordinates: Tuple[float, float], - polygon: Polygon) -> float: + def _distance_to_polygon( + home_coordinates: Tuple[float, float], polygon: Polygon + ) -> float: """Calculate the distance between home coordinates and the polygon.""" distance = float("inf") # Check if home is inside the polygon. @@ -70,21 +75,26 @@ def _distance_to_polygon(home_coordinates: Tuple[float, float], # Calculate distance from polygon by first calculating the distance # to each point of the polygon. for point in polygon.points: - distance = min(distance, - GeoRssDistanceHelper._distance_to_coordinates( - home_coordinates, - (point.latitude, point.longitude))) + distance = min( + distance, + GeoRssDistanceHelper._distance_to_coordinates( + home_coordinates, (point.latitude, point.longitude) + ), + ) # Next calculate the distance to each edge of the polygon. for edge in polygon.edges: - distance = min(distance, GeoRssDistanceHelper._distance_to_edge( - home_coordinates, edge)) - _LOGGER.debug("Distance between %s and %s: %s", - home_coordinates, polygon, distance) + distance = min( + distance, GeoRssDistanceHelper._distance_to_edge(home_coordinates, edge) + ) + _LOGGER.debug( + "Distance between %s and %s: %s", home_coordinates, polygon, distance + ) return distance @staticmethod - def _distance_to_bounding_box(home_coordinates: Tuple[float, float], - bbox: BoundingBox) -> float: + def _distance_to_bounding_box( + home_coordinates: Tuple[float, float], bbox: BoundingBox + ) -> float: """Calculate the distance between home coordinates and the bbox.""" distance = float("inf") # Check if home is inside the bounding box. @@ -105,25 +115,24 @@ def _distance_to_bounding_box(home_coordinates: Tuple[float, float], # 1 - above-left if transposed_point_longitude < bbox.bottom_left.longitude: # Calculate distance to top left point of bbox. - target_point = (bbox.top_right.latitude, - bbox.bottom_left.longitude) + target_point = (bbox.top_right.latitude, bbox.bottom_left.longitude) # 2 - above-centre - if bbox.bottom_left.longitude <= transposed_point_longitude \ - <= transposed_top_right_longitude: + if ( + bbox.bottom_left.longitude + <= transposed_point_longitude + <= transposed_top_right_longitude + ): # Calculate distance to top latitude of bbox. target_point = (bbox.top_right.latitude, home_coordinates[1]) # 3 - above-right if transposed_point_longitude > transposed_top_right_longitude: # Calculate distance to top right point of bbox. - target_point = (bbox.top_right.latitude, - bbox.top_right.longitude) - if bbox.top_right.latitude >= home_coordinates[0] \ - >= bbox.bottom_left.latitude: + target_point = (bbox.top_right.latitude, bbox.top_right.longitude) + if bbox.top_right.latitude >= home_coordinates[0] >= bbox.bottom_left.latitude: # 4 - left if transposed_point_longitude < bbox.bottom_left.longitude: # Calculate distance to left longitude of bbox. - target_point = (home_coordinates[0], - bbox.bottom_left.longitude) + target_point = (home_coordinates[0], bbox.bottom_left.longitude) # 5 - right if transposed_point_longitude > transposed_top_right_longitude: # Calculate distance to right longitude of bbox. @@ -132,54 +141,63 @@ def _distance_to_bounding_box(home_coordinates: Tuple[float, float], # 6 - below-left if transposed_point_longitude < bbox.bottom_left.longitude: # Calculate distance to bottom left point of bbox. - target_point = (bbox.bottom_left.latitude, - bbox.bottom_left.longitude) + target_point = (bbox.bottom_left.latitude, bbox.bottom_left.longitude) # 7 - below-centre - if bbox.bottom_left.longitude <= transposed_point_longitude \ - <= transposed_top_right_longitude: + if ( + bbox.bottom_left.longitude + <= transposed_point_longitude + <= transposed_top_right_longitude + ): # Calculate distance to bottom latitude of bbox. target_point = (bbox.bottom_left.latitude, home_coordinates[1]) # 8 - below-right if transposed_point_longitude > transposed_top_right_longitude: # Calculate distance to bottom right point of bbox. - target_point = (bbox.bottom_left.latitude, - bbox.top_right.longitude) + target_point = (bbox.bottom_left.latitude, bbox.top_right.longitude) if target_point: distance = GeoRssDistanceHelper._distance_to_coordinates( - home_coordinates, target_point) - _LOGGER.debug("Distance between %s and %s: %s", - home_coordinates, bbox, distance) + home_coordinates, target_point + ) + _LOGGER.debug( + "Distance between %s and %s: %s", home_coordinates, bbox, distance + ) return distance return distance @staticmethod - def _distance_to_coordinates(home_coordinates: Tuple[float, float], - coordinates: Tuple[float, float]) -> float: + def _distance_to_coordinates( + home_coordinates: Tuple[float, float], coordinates: Tuple[float, float] + ) -> float: """Calculate the distance between home coordinates and the coordinates.""" # Expecting coordinates in format: (latitude, longitude). return haversine(coordinates, home_coordinates) @staticmethod - def _distance_to_edge(home_coordinates: Tuple[float, float], - edge: Tuple[Point, Point]) -> float: + def _distance_to_edge( + home_coordinates: Tuple[float, float], edge: Tuple[Point, Point] + ) -> float: """Calculate distance between home coordinates and provided edge.""" perpendicular_point = GeoRssDistanceHelper._perpendicular_point( - edge, Point(home_coordinates[0], home_coordinates[1])) + edge, Point(home_coordinates[0], home_coordinates[1]) + ) # If there is a perpendicular point on the edge -> calculate distance. # If there isn't, then the distance to the end points of the edge will # need to be considered separately. if perpendicular_point: distance = GeoRssDistanceHelper._distance_to_point( - home_coordinates, perpendicular_point) - _LOGGER.debug("Distance between %s and %s: %s", - home_coordinates, edge, distance) + home_coordinates, perpendicular_point + ) + _LOGGER.debug( + "Distance between %s and %s: %s", home_coordinates, edge, distance + ) return distance return float("inf") @staticmethod - def _perpendicular_point(edge: Tuple[Point, Point], - point: Point) -> Optional[Point]: + def _perpendicular_point( + edge: Tuple[Point, Point], point: Point + ) -> Optional[Point]: """Find a perpendicular point on the edge to the provided point.""" a, b = edge # Safety check: a and b can't be an edge if they are the same point. @@ -202,8 +220,9 @@ def _perpendicular_point(edge: Tuple[Point, Point], ax, ay, bx, by = bx, by, ax, ay dx = abs(bx - ax) dy = abs(by - ay) - shortest_length = ((dx * (px - ax)) + (dy * (py - ay))) \ - / ((dx * dx) + (dy * dy)) + shortest_length = ((dx * (px - ax)) + (dy * (py - ay))) / ( + (dx * dx) + (dy * dy) + ) rx = ax + dx * shortest_length ry = ay + dy * shortest_length if bx >= rx >= ax and by >= ry >= ay: diff --git a/aio_georss_client/status_update.py b/aio_georss_client/status_update.py index f441916..c6238ff 100644 --- a/aio_georss_client/status_update.py +++ b/aio_georss_client/status_update.py @@ -6,15 +6,17 @@ class StatusUpdate: """Status Update class.""" - def __init__(self, - status: str, - last_update: Optional[datetime], - last_update_successful: Optional[datetime], - last_timestamp: Optional[datetime], - total: int, - created: int, - updated: int, - removed: int): + def __init__( + self, + status: str, + last_update: Optional[datetime], + last_update_successful: Optional[datetime], + last_timestamp: Optional[datetime], + total: int, + created: int, + updated: int, + removed: int, + ): """Initialise this status update.""" self._status = status self._last_update = last_update @@ -27,9 +29,9 @@ def __init__(self, def __repr__(self): """Return string representation of this entry.""" - return '<{}({}@{})>'.format(self.__class__.__name__, - self.status, - self.last_update) + return "<{}({}@{})>".format( + self.__class__.__name__, self.status, self.last_update + ) @property def status(self) -> str: diff --git a/aio_georss_client/xml_parser/__init__.py b/aio_georss_client/xml_parser/__init__.py index 7ed3705..dd394c0 100644 --- a/aio_georss_client/xml_parser/__init__.py +++ b/aio_georss_client/xml_parser/__init__.py @@ -5,33 +5,53 @@ import dateparser as dateparser import xmltodict -from aio_georss_client.consts import (XML_TAG_CHANNEL, XML_TAG_DC_DATE, - XML_TAG_FEED, XML_TAG_GDACS_BBOX, - XML_TAG_GEO_LAT, XML_TAG_GEO_LONG, - XML_TAG_GEORSS_POINT, - XML_TAG_GEORSS_POLYGON, XML_TAG_GML_POS, - XML_TAG_GML_POS_LIST, XML_TAG_HEIGHT, - XML_TAG_LAST_BUILD_DATE, - XML_TAG_PUB_DATE, XML_TAG_PUBLISHED, - XML_TAG_RSS, XML_TAG_TTL, - XML_TAG_UPDATED, XML_TAG_WIDTH) +from aio_georss_client.consts import ( + XML_TAG_CHANNEL, + XML_TAG_DC_DATE, + XML_TAG_FEED, + XML_TAG_GDACS_BBOX, + XML_TAG_GEO_LAT, + XML_TAG_GEO_LONG, + XML_TAG_GEORSS_POINT, + XML_TAG_GEORSS_POLYGON, + XML_TAG_GML_POS, + XML_TAG_GML_POS_LIST, + XML_TAG_HEIGHT, + XML_TAG_LAST_BUILD_DATE, + XML_TAG_PUB_DATE, + XML_TAG_PUBLISHED, + XML_TAG_RSS, + XML_TAG_TTL, + XML_TAG_UPDATED, + XML_TAG_WIDTH, +) from aio_georss_client.xml_parser.feed import Feed _LOGGER = logging.getLogger(__name__) DEFAULT_NAMESPACES = { - 'http://www.w3.org/2005/Atom': None, - 'http://purl.org/dc/elements/1.1/': 'dc', - 'http://www.georss.org/georss': 'georss', - 'http://www.w3.org/2003/01/geo/wgs84_pos#': 'geo', - 'http://www.opengis.net/gml': 'gml', - 'http://www.gdacs.org': 'gdacs', + "http://www.w3.org/2005/Atom": None, + "http://purl.org/dc/elements/1.1/": "dc", + "http://www.georss.org/georss": "georss", + "http://www.w3.org/2003/01/geo/wgs84_pos#": "geo", + "http://www.opengis.net/gml": "gml", + "http://www.gdacs.org": "gdacs", } -KEYS_DATE = [XML_TAG_DC_DATE, XML_TAG_LAST_BUILD_DATE, XML_TAG_PUB_DATE, - XML_TAG_PUBLISHED, XML_TAG_UPDATED] +KEYS_DATE = [ + XML_TAG_DC_DATE, + XML_TAG_LAST_BUILD_DATE, + XML_TAG_PUB_DATE, + XML_TAG_PUBLISHED, + XML_TAG_UPDATED, +] KEYS_FLOAT = [XML_TAG_GEO_LAT, XML_TAG_GEO_LONG] -KEYS_FLOAT_LIST = [XML_TAG_GEORSS_POLYGON, XML_TAG_GML_POS_LIST, - XML_TAG_GML_POS, XML_TAG_GEORSS_POINT, XML_TAG_GDACS_BBOX] +KEYS_FLOAT_LIST = [ + XML_TAG_GEORSS_POLYGON, + XML_TAG_GML_POS_LIST, + XML_TAG_GML_POS, + XML_TAG_GEORSS_POINT, + XML_TAG_GDACS_BBOX, +] KEYS_INT = [XML_TAG_HEIGHT, XML_TAG_TTL, XML_TAG_WIDTH] @@ -63,8 +83,7 @@ def postprocessor(path, key, value): if key in KEYS_INT and value: return key, int(value) except (ValueError, TypeError) as error: - _LOGGER.warning("Unable to process (%s/%s): %s", - key, value, error) + _LOGGER.warning("Unable to process (%s/%s): %s", key, value, error) return key, value @staticmethod @@ -73,16 +92,18 @@ def _process_coordinates(value): coordinate_values = value.split() point_coordinates = [] for i in range(0, len(coordinate_values)): - point_coordinates.append( - float(coordinate_values[i])) + point_coordinates.append(float(coordinate_values[i])) return point_coordinates def parse(self, xml) -> Optional[Feed]: """Parse the provided xml.""" if xml: parsed_dict = xmltodict.parse( - xml, process_namespaces=True, namespaces=self._namespaces, - postprocessor=XmlParser.postprocessor) + xml, + process_namespaces=True, + namespaces=self._namespaces, + postprocessor=XmlParser.postprocessor, + ) if XML_TAG_RSS in parsed_dict: return XmlParser._create_feed_from_rss(parsed_dict) if XML_TAG_FEED in parsed_dict: @@ -97,8 +118,9 @@ def _create_feed_from_rss(parsed_dict: Dict) -> Optional[Feed]: channel = rss.get(XML_TAG_CHANNEL) return Feed(channel) else: - _LOGGER.warning("Invalid structure: %s not followed by %s", - XML_TAG_RSS, XML_TAG_CHANNEL) + _LOGGER.warning( + "Invalid structure: %s not followed by %s", XML_TAG_RSS, XML_TAG_CHANNEL + ) return None @staticmethod diff --git a/aio_georss_client/xml_parser/feed.py b/aio_georss_client/xml_parser/feed.py index fbe1385..4483e5f 100644 --- a/aio_georss_client/xml_parser/feed.py +++ b/aio_georss_client/xml_parser/feed.py @@ -2,11 +2,18 @@ import logging from typing import List, Optional -from aio_georss_client.consts import (XML_TAG_COPYRIGHT, XML_TAG_DOCS, - XML_TAG_ENTRY, XML_TAG_GENERATOR, - XML_TAG_IMAGE, XML_TAG_ITEM, - XML_TAG_LANGUAGE, XML_TAG_RIGHTS, - XML_TAG_SUBTITLE, XML_TAG_TTL) +from aio_georss_client.consts import ( + XML_TAG_COPYRIGHT, + XML_TAG_DOCS, + XML_TAG_ENTRY, + XML_TAG_GENERATOR, + XML_TAG_IMAGE, + XML_TAG_ITEM, + XML_TAG_LANGUAGE, + XML_TAG_RIGHTS, + XML_TAG_SUBTITLE, + XML_TAG_TTL, +) from aio_georss_client.xml_parser.feed_image import FeedImage from aio_georss_client.xml_parser.feed_item import FeedItem from aio_georss_client.xml_parser.feed_or_feed_item import FeedOrFeedItem diff --git a/aio_georss_client/xml_parser/feed_dict_source.py b/aio_georss_client/xml_parser/feed_dict_source.py index 5f4518a..3287447 100644 --- a/aio_georss_client/xml_parser/feed_dict_source.py +++ b/aio_georss_client/xml_parser/feed_dict_source.py @@ -1,10 +1,15 @@ """GeoRSS feed dict source.""" from typing import List, Optional -from aio_georss_client.consts import (XML_ATTR_HREF, XML_CDATA, - XML_TAG_CONTENT, XML_TAG_DESCRIPTION, - XML_TAG_LINK, XML_TAG_SUMMARY, - XML_TAG_TITLE) +from aio_georss_client.consts import ( + XML_ATTR_HREF, + XML_CDATA, + XML_TAG_CONTENT, + XML_TAG_DESCRIPTION, + XML_TAG_LINK, + XML_TAG_SUMMARY, + XML_TAG_TITLE, +) class FeedDictSource: @@ -16,8 +21,7 @@ def __init__(self, source): def __repr__(self): """Return string representation of this feed item.""" - return '<{}({})>'.format( - self.__class__.__name__, self.link) + return "<{}({})>".format(self.__class__.__name__, self.link) def _attribute(self, names: List[str]) -> Optional: """Get an attribute from this feed or feed item.""" @@ -42,8 +46,11 @@ def _attribute_in_structure(obj, keys: List[str]) -> Optional: """Return the attribute found under the chain of keys.""" key = keys.pop(0) if key in obj: - return FeedDictSource._attribute_in_structure( - obj[key], keys) if keys else obj[key] + return ( + FeedDictSource._attribute_in_structure(obj[key], keys) + if keys + else obj[key] + ) @property def title(self) -> Optional[str]: @@ -53,9 +60,9 @@ def title(self) -> Optional[str]: @property def description(self) -> Optional[str]: """Return the description of this feed or feed item.""" - return self._attribute_with_text([XML_TAG_DESCRIPTION, - XML_TAG_SUMMARY, - XML_TAG_CONTENT]) + return self._attribute_with_text( + [XML_TAG_DESCRIPTION, XML_TAG_SUMMARY, XML_TAG_CONTENT] + ) @property def summary(self) -> Optional[str]: diff --git a/aio_georss_client/xml_parser/feed_item.py b/aio_georss_client/xml_parser/feed_item.py index e180d39..bd5ddf6 100644 --- a/aio_georss_client/xml_parser/feed_item.py +++ b/aio_georss_client/xml_parser/feed_item.py @@ -2,19 +2,26 @@ import logging from typing import List, Optional, Tuple -from aio_georss_client.consts import (XML_TAG_GDACS_BBOX, XML_TAG_GEO_LAT, - XML_TAG_GEO_LONG, XML_TAG_GEO_POINT, - XML_TAG_GEORSS_POINT, - XML_TAG_GEORSS_POLYGON, - XML_TAG_GEORSS_WHERE, - XML_TAG_GML_EXTERIOR, - XML_TAG_GML_LINEAR_RING, - XML_TAG_GML_POINT, XML_TAG_GML_POLYGON, - XML_TAG_GML_POS, XML_TAG_GML_POS_LIST, - XML_TAG_GUID, XML_TAG_ID, XML_TAG_SOURCE) +from aio_georss_client.consts import ( + XML_TAG_GDACS_BBOX, + XML_TAG_GEO_LAT, + XML_TAG_GEO_LONG, + XML_TAG_GEO_POINT, + XML_TAG_GEORSS_POINT, + XML_TAG_GEORSS_POLYGON, + XML_TAG_GEORSS_WHERE, + XML_TAG_GML_EXTERIOR, + XML_TAG_GML_LINEAR_RING, + XML_TAG_GML_POINT, + XML_TAG_GML_POLYGON, + XML_TAG_GML_POS, + XML_TAG_GML_POS_LIST, + XML_TAG_GUID, + XML_TAG_ID, + XML_TAG_SOURCE, +) from aio_georss_client.xml_parser.feed_or_feed_item import FeedOrFeedItem -from aio_georss_client.xml_parser.geometry import (BoundingBox, Geometry, - Point, Polygon) +from aio_georss_client.xml_parser.geometry import BoundingBox, Geometry, Point, Polygon _LOGGER = logging.getLogger(__name__) @@ -24,8 +31,7 @@ class FeedItem(FeedOrFeedItem): def __repr__(self): """Return string representation of this feed item.""" - return '<{}({})>'.format( - self.__class__.__name__, self.guid) + return "<{}({})>".format(self.__class__.__name__, self.guid) @property def guid(self) -> Optional[str]: @@ -46,12 +52,14 @@ def source(self) -> Optional[str]: def geometries(self) -> Optional[List[Geometry]]: """Return all geometries of this feed item.""" geometries = [] - for entry in [self._geometry_georss_point(), - self._geometry_georss_where(), - self._geometry_geo_point(), - self._geometry_geo_long_lat(), - self._geometry_georss_polygon(), - self._geometry_gdacs_bbox()]: + for entry in [ + self._geometry_georss_point(), + self._geometry_georss_where(), + self._geometry_geo_point(), + self._geometry_geo_long_lat(), + self._geometry_georss_polygon(), + self._geometry_gdacs_bbox(), + ]: if entry: geometries.extend(entry) # Filter out any duplicates. @@ -96,7 +104,8 @@ def _geometry_georss_where(self) -> Optional[List[Geometry]]: # # pos = self._attribute_in_structure( - where, [XML_TAG_GML_POINT, XML_TAG_GML_POS]) + where, [XML_TAG_GML_POINT, XML_TAG_GML_POS] + ) if pos: return [Point(pos[0], pos[1])] # Polygon: @@ -119,8 +128,14 @@ def _geometry_georss_where(self) -> Optional[List[Geometry]]: # # pos_list = self._attribute_in_structure( - where, [XML_TAG_GML_POLYGON, XML_TAG_GML_EXTERIOR, - XML_TAG_GML_LINEAR_RING, XML_TAG_GML_POS_LIST]) + where, + [ + XML_TAG_GML_POLYGON, + XML_TAG_GML_EXTERIOR, + XML_TAG_GML_LINEAR_RING, + XML_TAG_GML_POS_LIST, + ], + ) if pos_list: return self._create_polygon(pos_list) return None @@ -164,8 +179,7 @@ def _geometry_gdacs_bbox(self) -> Optional[List[BoundingBox]]: @staticmethod def _create_bbox_single(bbox: Tuple) -> List[BoundingBox]: """Create single bbox from provided tuple of coordinates.""" - return [BoundingBox(Point(bbox[2], bbox[0]), - Point(bbox[3], bbox[1]))] + return [BoundingBox(Point(bbox[2], bbox[0]), Point(bbox[3], bbox[1]))] @staticmethod def _create_bbox_multiple(bbox: List) -> List[BoundingBox]: @@ -173,12 +187,11 @@ def _create_bbox_multiple(bbox: List) -> List[BoundingBox]: bounding_boxes = [] for entry in bbox: if len(entry) == 4: - bounding_boxes.append(BoundingBox( - Point(entry[2], entry[0]), - Point(entry[3], entry[1]))) + bounding_boxes.append( + BoundingBox(Point(entry[2], entry[0]), Point(entry[3], entry[1])) + ) else: - _LOGGER.warning("Insufficient data for " - "bounding box: %s", entry) + _LOGGER.warning("Insufficient data for " "bounding box: %s", entry) return bounding_boxes def _geometry_georss_polygon(self) -> Optional[List[Polygon]]: @@ -212,7 +225,7 @@ def _create_polygon_single(polygon_data: Tuple) -> List[Polygon]: """Create polygon from provided tuple of coordinates.""" if len(polygon_data) % 2 != 0: # Not even number of coordinates - chop last entry. - polygon_data = polygon_data[0:len(polygon_data) - 1] + polygon_data = polygon_data[0 : len(polygon_data) - 1] points = [] for i in range(0, len(polygon_data), 2): points.append(Point(polygon_data[i], polygon_data[i + 1])) diff --git a/aio_georss_client/xml_parser/feed_or_feed_item.py b/aio_georss_client/xml_parser/feed_or_feed_item.py index 14de94f..8819819 100644 --- a/aio_georss_client/xml_parser/feed_or_feed_item.py +++ b/aio_georss_client/xml_parser/feed_or_feed_item.py @@ -2,12 +2,19 @@ import datetime from typing import List, Optional -from aio_georss_client.consts import (XML_ATTR_TERM, XML_TAG_AUTHOR, - XML_TAG_CATEGORY, XML_TAG_CONTRIBUTOR, - XML_TAG_DC_DATE, XML_TAG_LAST_BUILD_DATE, - XML_TAG_MANAGING_EDITOR, XML_TAG_NAME, - XML_TAG_PUB_DATE, XML_TAG_PUBLISHED, - XML_TAG_UPDATED) +from aio_georss_client.consts import ( + XML_ATTR_TERM, + XML_TAG_AUTHOR, + XML_TAG_CATEGORY, + XML_TAG_CONTRIBUTOR, + XML_TAG_DC_DATE, + XML_TAG_LAST_BUILD_DATE, + XML_TAG_MANAGING_EDITOR, + XML_TAG_NAME, + XML_TAG_PUB_DATE, + XML_TAG_PUBLISHED, + XML_TAG_UPDATED, +) from aio_georss_client.xml_parser.feed_dict_source import FeedDictSource @@ -39,9 +46,7 @@ def _create_categories(categories: list) -> List[str]: @property def published_date(self) -> Optional[datetime.datetime]: """Return the published date of this feed or feed item.""" - return self._attribute([XML_TAG_PUB_DATE, - XML_TAG_PUBLISHED, - XML_TAG_DC_DATE]) + return self._attribute([XML_TAG_PUB_DATE, XML_TAG_PUBLISHED, XML_TAG_DC_DATE]) @property def pub_date(self) -> Optional[datetime.datetime]: diff --git a/aio_georss_client/xml_parser/geometry.py b/aio_georss_client/xml_parser/geometry.py index 8b5c08b..1b4afc1 100644 --- a/aio_georss_client/xml_parser/geometry.py +++ b/aio_georss_client/xml_parser/geometry.py @@ -16,8 +16,9 @@ def __init__(self, latitude: float, longitude: float): def __repr__(self): """Return string representation of this point.""" - return '<{}(latitude={}, longitude={})>'.format( - self.__class__.__name__, self.latitude, self.longitude) + return "<{}(latitude={}, longitude={})>".format( + self.__class__.__name__, self.latitude, self.longitude + ) def __hash__(self) -> int: """Return unique hash of this""" @@ -26,10 +27,10 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: """Return if this object is equal to other object.""" return ( - self.__class__ == other.__class__ and - self.latitude == other.latitude and - self.longitude == other.longitude - ) + self.__class__ == other.__class__ + and self.latitude == other.latitude + and self.longitude == other.longitude + ) @property def latitude(self) -> Optional[float]: @@ -51,8 +52,7 @@ def __init__(self, points: List[Point]): def __repr__(self): """Return string representation of this polygon.""" - return '<{}(centroid={})>'.format( - self.__class__.__name__, self.centroid) + return "<{}(centroid={})>".format(self.__class__.__name__, self.centroid) def __hash__(self) -> int: """Return unique hash of this""" @@ -60,10 +60,7 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: """Return if this object is equal to other object.""" - return ( - self.__class__ == other.__class__ and - self.points == other.points - ) + return self.__class__ == other.__class__ and self.points == other.points @property def points(self) -> Optional[List]: @@ -75,7 +72,7 @@ def edges(self) -> List[Tuple[Point, Point]]: """Return all edges of this polygon.""" edges = [] for i in range(1, len(self.points)): - previous = self.points[i-1] + previous = self.points[i - 1] current = self.points[i] edges.append((previous, current)) return edges @@ -148,8 +145,9 @@ def __init__(self, bottom_left: Point, top_right: Point): def __repr__(self): """Return string representation of this bounding box.""" - return '<{}(bottom_left={}, top_right={})>'.format( - self.__class__.__name__, self._bottom_left, self._top_right) + return "<{}(bottom_left={}, top_right={})>".format( + self.__class__.__name__, self._bottom_left, self._top_right + ) def __hash__(self) -> int: """Return unique hash of this""" @@ -158,10 +156,10 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: """Return if this object is equal to other object.""" return ( - self.__class__ == other.__class__ and - self.bottom_left == other.bottom_left and - self.top_right == other.top_right - ) + self.__class__ == other.__class__ + and self.bottom_left == other.bottom_left + and self.top_right == other.top_right + ) @property def bottom_left(self) -> Point: @@ -180,8 +178,7 @@ def centroid(self) -> Point: if self._bottom_left.longitude > self._top_right.longitude: # bounding box spans across 180 degree longitude transposed_top_right_longitude = self._top_right.longitude + 360 - longitude = (self._bottom_left.longitude - + transposed_top_right_longitude) / 2 + longitude = (self._bottom_left.longitude + transposed_top_right_longitude) / 2 latitude = (self._bottom_left.latitude + self._top_right.latitude) / 2 return Point(latitude, longitude) @@ -192,11 +189,13 @@ def is_inside(self, point: Point) -> bool: transposed_top_right_longitude = self._top_right.longitude if self._bottom_left.longitude > self._top_right.longitude: # bounding box spans across 180 degree longitude - transposed_top_right_longitude = self._top_right.longitude \ - + 360 + transposed_top_right_longitude = self._top_right.longitude + 360 if point.longitude < 0: transposed_point_longitude += 360 - return (self._bottom_left.latitude <= point.latitude - <= self._top_right.latitude) and \ - (self._bottom_left.longitude <= transposed_point_longitude - <= transposed_top_right_longitude) + return ( + self._bottom_left.latitude <= point.latitude <= self._top_right.latitude + ) and ( + self._bottom_left.longitude + <= transposed_point_longitude + <= transposed_top_right_longitude + ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..36619cc --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.black] +target-version = ['py36', 'py37', 'py38'] diff --git a/tests/__init__.py b/tests/__init__.py index c2edede..b0d2d23 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -18,20 +18,23 @@ def attribution(self) -> Optional[str]: class MockGeoRssFeed(GeoRssFeed[MockFeedEntry]): - - def _new_entry(self, - home_coordinates: Tuple[float, float], - rss_entry: FeedItem, - global_data: Dict) -> MockFeedEntry: + def _new_entry( + self, + home_coordinates: Tuple[float, float], + rss_entry: FeedItem, + global_data: Dict, + ) -> MockFeedEntry: """Generate a new entry.""" return MockFeedEntry(home_coordinates, rss_entry) class MockSimpleFeedEntry(FeedEntry): - - def __init__(self, home_coordinates: Tuple[float, float], - rss_entry: FeedItem, - features: List[Type[Geometry]] = DEFAULT_FEATURES): + def __init__( + self, + home_coordinates: Tuple[float, float], + rss_entry: FeedItem, + features: List[Type[Geometry]] = DEFAULT_FEATURES, + ): super().__init__(home_coordinates, rss_entry) self._features = features @@ -45,7 +48,6 @@ def attribution(self) -> Optional[str]: class MockFeedItem(FeedItem): - def __init__(self, source, geometries): super().__init__(source) self._geometries = geometries diff --git a/tests/test_feed.py b/tests/test_feed.py index 1c1979c..3759b3c 100644 --- a/tests/test_feed.py +++ b/tests/test_feed.py @@ -20,17 +20,19 @@ async def test_update_ok(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_1.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_1.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/testpath") - assert repr(feed) == "" + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) + assert ( + repr(feed) == "" + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None @@ -72,14 +74,14 @@ async def test_update_ok_feed_2(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_2.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_2.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/testpath") + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None @@ -100,13 +102,13 @@ async def test_update_ok_feed_3(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_3.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_3.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/testpath") + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None @@ -116,8 +118,7 @@ async def test_update_ok_feed_3(aresponses, event_loop): assert feed_entry.external_id == "1234" assert len(feed_entry.geometries) == 1 assert isinstance(feed_entry.geometries[0], Polygon) - assert feed_entry.coordinates == (-34.93728111547821, - 148.59710883878262) + assert feed_entry.coordinates == (-34.93728111547821, 148.59710883878262) assert round(abs(feed_entry.distance_to_home - 491.7), 1) == 0 feed_entry = entries[1] @@ -133,8 +134,7 @@ async def test_update_ok_feed_3(aresponses, event_loop): assert len(feed_entry.geometries) == 2 assert isinstance(feed_entry.geometries[0], Polygon) assert isinstance(feed_entry.geometries[1], Polygon) - assert feed_entry.coordinates == (-29.962746645660683, - 152.43090880416074) + assert feed_entry.coordinates == (-29.962746645660683, 152.43090880416074) assert round(abs(feed_entry.distance_to_home - 176.5), 1) == 0 feed_entry = entries[3] @@ -161,13 +161,13 @@ async def test_update_ok_feed_6(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_6.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_6.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/testpath") + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None @@ -188,17 +188,19 @@ async def test_update_duplicate_geometries(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_7.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_7.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/testpath") - assert repr(feed) == "" + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) + assert ( + repr(feed) == "" + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None @@ -234,13 +236,16 @@ async def test_update_ok_with_radius_filtering(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_1.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_1.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_2, - "http://test.url/testpath", filter_radius=90.0) + feed = MockGeoRssFeed( + websession, + HOME_COORDINATES_2, + "http://test.url/testpath", + filter_radius=90.0, + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None @@ -251,21 +256,23 @@ async def test_update_ok_with_radius_filtering(aresponses, event_loop): @pytest.mark.asyncio -async def test_update_ok_with_radius_and_category_filtering(aresponses, - event_loop): +async def test_update_ok_with_radius_and_category_filtering(aresponses, event_loop): """Test updating feed is ok.""" aresponses.add( "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_1.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_1.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_2, - "http://test.url/testpath", filter_radius=90.0, - filter_categories=['Category 2']) + feed = MockGeoRssFeed( + websession, + HOME_COORDINATES_2, + "http://test.url/testpath", + filter_radius=90.0, + filter_categories=["Category 2"], + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None @@ -276,13 +283,16 @@ async def test_update_ok_with_radius_and_category_filtering(aresponses, "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_1.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_1.xml"), status=200), ) - feed = MockGeoRssFeed(websession, HOME_COORDINATES_2, - "http://test.url/testpath", filter_radius=90.0, - filter_categories=['Category 4']) + feed = MockGeoRssFeed( + websession, + HOME_COORDINATES_2, + "http://test.url/testpath", + filter_radius=90.0, + filter_categories=["Category 4"], + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None @@ -292,13 +302,10 @@ async def test_update_ok_with_radius_and_category_filtering(aresponses, @pytest.mark.asyncio async def test_update_error(aresponses, event_loop): """Test updating feed results in error.""" - aresponses.add( - "test.url", "/badpath", "get", aresponses.Response(status=404) - ) + aresponses.add("test.url", "/badpath", "get", aresponses.Response(status=404)) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/badpath") + feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, "http://test.url/badpath") status, entries = await feed.update() assert status == UPDATE_ERROR assert feed.last_timestamp is None @@ -311,26 +318,26 @@ async def test_update_ok_then_error(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_1.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_1.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/testpath") - assert repr(feed) == "" + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) + assert ( + repr(feed) == "" + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None assert len(entries) == 5 assert feed.last_timestamp is not None - aresponses.add( - "test.url", "/testpath", "get", aresponses.Response(status=404) - ) + aresponses.add("test.url", "/testpath", "get", aresponses.Response(status=404)) status, entries = await feed.update() assert status == UPDATE_ERROR @@ -340,13 +347,10 @@ async def test_update_ok_then_error(aresponses, event_loop): @pytest.mark.asyncio async def test_update_with_request_exception(aresponses, event_loop): """Test updating feed raises exception.""" - aresponses.add( - "test.url", "/badpath", "get", aresponses.Response(status=404) - ) + aresponses.add("test.url", "/badpath", "get", aresponses.Response(status=404)) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/badpath") + feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, "http://test.url/badpath") status, entries = await feed.update() assert status == UPDATE_ERROR assert entries is None @@ -356,24 +360,27 @@ async def test_update_with_request_exception(aresponses, event_loop): @pytest.mark.asyncio async def test_update_bom(aresponses, event_loop): """Test updating feed with BOM (byte order mark) is ok.""" - xml = "\xef\xbb\xbf" \ - "Title 1" \ - "" + xml = ( + "\xef\xbb\xbf" + "Title 1" + "" + ) aresponses.add( "test.url", "/testpath", "get", - aresponses.Response(text=xml, - charset='iso-8859-1', - status=200), + aresponses.Response(text=xml, charset="iso-8859-1", status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/testpath") - assert repr(feed) == "" + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) + assert ( + repr(feed) == "" + ) status, entries = await feed.update() assert status == UPDATE_OK assert entries is not None diff --git a/tests/test_feed_entry.py b/tests/test_feed_entry.py index 8cfd6b1..3238492 100644 --- a/tests/test_feed_entry.py +++ b/tests/test_feed_entry.py @@ -3,8 +3,7 @@ from unittest import mock from aio_georss_client.xml_parser.geometry import Point, BoundingBox, Polygon -from . import MockFeedEntry, MockSimpleFeedEntry, MOCK_HOME_COORDINATES, \ - MockFeedItem +from . import MockFeedEntry, MockSimpleFeedEntry, MOCK_HOME_COORDINATES, MockFeedItem def test_simple_feed_entry(): @@ -20,12 +19,15 @@ def test_simple_feed_entry(): assert feed_entry.description is None assert feed_entry.published is None assert feed_entry.updated is None - assert feed_entry._search_in_external_id( - r'External ID (?P.+)$') is None - assert feed_entry._search_in_title( - r'Title (?P.+)$') is None - assert feed_entry._search_in_description( - r'Description (?P.+)$') is None + assert ( + feed_entry._search_in_external_id(r"External ID (?P.+)$") + is None + ) + assert feed_entry._search_in_title(r"Title (?P.+)$") is None + assert ( + feed_entry._search_in_description(r"Description (?P.+)$") + is None + ) # Test _string2boolean assert not feed_entry._string2boolean("False") assert not feed_entry._string2boolean("no") @@ -38,11 +40,11 @@ def test_feed_entry_features(): point = Point(0.0, 0.0) polygon = Polygon([point, point]) bounding_box = BoundingBox(point, point) - feed_item = MockFeedItem(None, [point, polygon, point, polygon, polygon, - bounding_box]) + feed_item = MockFeedItem( + None, [point, polygon, point, polygon, polygon, bounding_box] + ) # 1. Include all - feed_entry = MockSimpleFeedEntry(None, feed_item, [Point, Polygon, - BoundingBox]) + feed_entry = MockSimpleFeedEntry(None, feed_item, [Point, Polygon, BoundingBox]) assert len(feed_entry.geometries) == 6 # 2. Exclude points feed_entry = MockSimpleFeedEntry(None, feed_item, [Polygon, BoundingBox]) @@ -63,23 +65,22 @@ def test_feed_entry_search_in_attributes(): rss_entry = mock.MagicMock() type(rss_entry).guid = mock.PropertyMock(return_value="Test 123") type(rss_entry).title = mock.PropertyMock(return_value="Title 123") - type(rss_entry).description = mock.PropertyMock( - return_value="Description 123") + type(rss_entry).description = mock.PropertyMock(return_value="Description 123") type(rss_entry).category = mock.PropertyMock( - return_value=["Category 1", "Category 2"]) - updated = datetime.datetime(2019, 4, 1, 8, 30, - tzinfo=datetime.timezone.utc) + return_value=["Category 1", "Category 2"] + ) + updated = datetime.datetime(2019, 4, 1, 8, 30, tzinfo=datetime.timezone.utc) type(rss_entry).updated_date = mock.PropertyMock(return_value=updated) feed_entry = MockFeedEntry(MOCK_HOME_COORDINATES, rss_entry) assert repr(feed_entry) == "" - assert feed_entry._search_in_external_id( - r'Test (?P.+)$') == "123" - assert feed_entry._search_in_title( - r'Title (?P.+)$') == "123" - assert feed_entry._search_in_description( - r'Description (?P.+)$') == "123" + assert feed_entry._search_in_external_id(r"Test (?P.+)$") == "123" + assert feed_entry._search_in_title(r"Title (?P.+)$") == "123" + assert ( + feed_entry._search_in_description(r"Description (?P.+)$") + == "123" + ) assert feed_entry.category == "Category 1" assert feed_entry.description == "Description 123" assert feed_entry.updated == updated diff --git a/tests/test_feed_manager.py b/tests/test_feed_manager.py index d685dea..5861469 100644 --- a/tests/test_feed_manager.py +++ b/tests/test_feed_manager.py @@ -22,14 +22,14 @@ async def test_feed_manager(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_1.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_1.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, HOME_COORDINATES_1, - "http://test.url/testpath") + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) # This will just record calls and keep track of external ids. generated_entity_external_ids = [] @@ -49,22 +49,21 @@ async def _remove_entity(entity_external_id: str) -> None: removed_entity_external_ids.append(entity_external_id) feed_manager = FeedManagerBase( - feed, - _generate_entity, - _update_entity, - _remove_entity) - assert repr(feed_manager) == ")>" + feed, _generate_entity, _update_entity, _remove_entity + ) + assert ( + repr(feed_manager) == ")>" + ) await feed_manager.update() entries = feed_manager.feed_entries assert entries is not None assert len(entries) == 5 assert feed_manager.last_update is not None - assert feed_manager.last_timestamp \ - == datetime.datetime(2018, 9, 23, 9, 10) + assert feed_manager.last_timestamp == datetime.datetime(2018, 9, 23, 9, 10) assert len(generated_entity_external_ids) == 5 assert len(updated_entity_external_ids) == 0 @@ -103,8 +102,7 @@ async def _remove_entity(entity_external_id: str) -> None: "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_4.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_4.xml"), status=200), ) await feed_manager.update() @@ -129,8 +127,9 @@ async def _remove_entity(entity_external_id: str) -> None: updated_entity_external_ids.clear() removed_entity_external_ids.clear() - with patch("aio_georss_client.feed.GeoRssFeed._fetch", - new_callable=CoroutineMock) as mock_fetch: + with patch( + "aio_georss_client.feed.GeoRssFeed._fetch", new_callable=CoroutineMock + ) as mock_fetch: mock_fetch.return_value = (UPDATE_OK_NO_DATA, None) await feed_manager.update() @@ -169,15 +168,14 @@ async def test_feed_manager_no_timestamp(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_5.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_5.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, - HOME_COORDINATES_1, - "http://test.url/testpath") + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) # This will just record calls and keep track of external ids. generated_entity_external_ids = [] @@ -197,15 +195,15 @@ async def _remove_entity(external_id: str) -> None: removed_entity_external_ids.append(external_id) feed_manager = FeedManagerBase( - feed, - _generate_entity, - _update_entity, - _remove_entity) - assert repr(feed_manager) == ")>" + feed, _generate_entity, _update_entity, _remove_entity + ) + assert ( + repr(feed_manager) == ")>" + ) await feed_manager.update() entries = feed_manager.feed_entries assert entries is not None @@ -220,15 +218,14 @@ async def test_feed_manager_with_status_callback(aresponses, event_loop): "test.url", "/testpath", "get", - aresponses.Response(text=load_fixture('generic_feed_1.xml'), - status=200), + aresponses.Response(text=load_fixture("generic_feed_1.xml"), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: - feed = MockGeoRssFeed(websession, - HOME_COORDINATES_1, - "http://test.url/testpath") + feed = MockGeoRssFeed( + websession, HOME_COORDINATES_1, "http://test.url/testpath" + ) # This will just record calls and keep track of external ids. generated_entity_external_ids = [] @@ -252,19 +249,21 @@ async def _status(status_details: StatusUpdate) -> None: """Capture status update details.""" status_update.append(status_details) - feed_manager = FeedManagerBase(feed, _generate_entity, _update_entity, - _remove_entity, _status) - assert repr(feed_manager) == ")>" + feed_manager = FeedManagerBase( + feed, _generate_entity, _update_entity, _remove_entity, _status + ) + assert ( + repr(feed_manager) == ")>" + ) await feed_manager.update() entries = feed_manager.feed_entries assert entries is not None assert len(entries) == 5 assert feed_manager.last_update is not None - assert feed_manager.last_timestamp \ - == datetime.datetime(2018, 9, 23, 9, 10) + assert feed_manager.last_timestamp == datetime.datetime(2018, 9, 23, 9, 10) assert len(generated_entity_external_ids) == 5 assert len(updated_entity_external_ids) == 0 @@ -274,14 +273,15 @@ async def _status(status_details: StatusUpdate) -> None: assert status_update[0].last_update is not None last_update_successful = status_update[0].last_update_successful assert status_update[0].last_update == last_update_successful - assert status_update[0].last_timestamp \ - == datetime.datetime(2018, 9, 23, 9, 10) + assert status_update[0].last_timestamp == datetime.datetime(2018, 9, 23, 9, 10) assert status_update[0].total == 5 assert status_update[0].created == 5 assert status_update[0].updated == 0 assert status_update[0].removed == 0 - assert repr(status_update[0]) == f"" + ) # Simulate an update with no data. generated_entity_external_ids.clear() @@ -307,6 +307,5 @@ async def _status(status_details: StatusUpdate) -> None: assert status_update[0].status == "ERROR" assert status_update[0].last_update is not None assert status_update[0].last_update_successful is not None - assert status_update[0].last_update_successful == \ - last_update_successful + assert status_update[0].last_update_successful == last_update_successful assert status_update[0].total == 0 diff --git a/tests/test_geometries.py b/tests/test_geometries.py index 0958a58..cd453a5 100644 --- a/tests/test_geometries.py +++ b/tests/test_geometries.py @@ -22,45 +22,50 @@ def test_point_equality(self): def test_polygon(self): """Test polygon.""" - polygon = Polygon([ - Point(-30.1, 150.1), - Point(-30.2, 150.2), - Point(-30.4, 150.4), - Point(-30.8, 150.8), - Point(-30.1, 150.1) - ]) + polygon = Polygon( + [ + Point(-30.1, 150.1), + Point(-30.2, 150.2), + Point(-30.4, 150.4), + Point(-30.8, 150.8), + Point(-30.1, 150.1), + ] + ) assert len(polygon.points) == 5 assert polygon.centroid.latitude == -30.32 assert polygon.centroid.longitude == 150.32 - assert repr(polygon) == ")>" + assert ( + repr(polygon) == ")>" + ) def test_polygon_equality(self): """Test points.""" - polygon1 = Polygon([ - Point(30.0, 30.0), - Point(30.0, 35.0), - Point(35.0, 35.0), - Point(35.0, 30.0), - Point(30.0, 30.0) - ]) - polygon2 = Polygon([ - Point(30.0, 30.0), - Point(30.0, 35.0), - Point(35.0, 35.0), - Point(35.0, 30.0), - Point(30.0, 30.0) - ]) + polygon1 = Polygon( + [ + Point(30.0, 30.0), + Point(30.0, 35.0), + Point(35.0, 35.0), + Point(35.0, 30.0), + Point(30.0, 30.0), + ] + ) + polygon2 = Polygon( + [ + Point(30.0, 30.0), + Point(30.0, 35.0), + Point(35.0, 35.0), + Point(35.0, 30.0), + Point(30.0, 30.0), + ] + ) assert polygon1 == polygon2 def test_point_in_polygon_1(self): """Test if point is in polygon.""" - polygon = Polygon([ - Point(30.0, 30.0), - Point(30.0, 35.0), - Point(35.0, 35.0), - Point(30.0, 30.0) - ]) + polygon = Polygon( + [Point(30.0, 30.0), Point(30.0, 35.0), Point(35.0, 35.0), Point(30.0, 30.0)] + ) # 1. Outside point = Point(20.0, 20.0) assert not polygon.is_inside(point) @@ -79,12 +84,14 @@ def test_point_in_polygon_1(self): def test_point_in_polygon_2(self): """Test if point is in polygon.""" - polygon = Polygon([ - Point(30.0, -30.0), - Point(30.0, -25.0), - Point(35.0, -25.0), - Point(30.0, -30.0) - ]) + polygon = Polygon( + [ + Point(30.0, -30.0), + Point(30.0, -25.0), + Point(35.0, -25.0), + Point(30.0, -30.0), + ] + ) # 1. Outside point = Point(20.0, -40.0) assert not polygon.is_inside(point) @@ -106,10 +113,12 @@ def test_point_in_polygon_2(self): def test_bounding_box_1(self): """Test bounding box.""" bbox = BoundingBox(Point(-30.0, 148.0), Point(-28.0, 150.0)) - assert repr(bbox) == ", " \ - "top_right=" \ - ")>" + assert ( + repr(bbox) == ", " + "top_right=" + ")>" + ) assert bbox.centroid.latitude == -29.0 assert bbox.centroid.longitude == 149.0 assert bbox.is_inside(Point(-29.5, 148.1)) @@ -118,10 +127,12 @@ def test_bounding_box_1(self): def test_bounding_box_2(self): """Test bounding box.""" bbox = BoundingBox(Point(-5.0, 175.0), Point(5.0, -175.0)) - assert repr(bbox) == ", " \ - "top_right=" \ - ")>" + assert ( + repr(bbox) == ", " + "top_right=" + ")>" + ) assert bbox.centroid.latitude == 0.0 assert bbox.centroid.longitude == 180.0 assert bbox.is_inside(Point(-2.5, 179.0)) @@ -130,10 +141,12 @@ def test_bounding_box_2(self): def test_bounding_box_3(self): """Test bounding box.""" bbox = BoundingBox(Point(-5.0, 175.0), Point(5.0, -175.0)) - assert repr(bbox) == ", " \ - "top_right=" \ - ")>" + assert ( + repr(bbox) == ", " + "top_right=" + ")>" + ) assert bbox.centroid.latitude == 0.0 assert bbox.centroid.longitude == 180.0 assert bbox.is_inside(Point(-2.5, -179.0)) diff --git a/tests/test_georss_distance_helper.py b/tests/test_georss_distance_helper.py index 254071e..c259b6d 100644 --- a/tests/test_georss_distance_helper.py +++ b/tests/test_georss_distance_helper.py @@ -12,28 +12,31 @@ class TestGeoRssDistanceHelper(unittest.TestCase): def test_extract_coordinates_from_point(self): """Test extracting coordinates from point.""" mock_point = Point(-30.0, 151.0) - latitude, longitude = GeoRssDistanceHelper.\ - extract_coordinates(mock_point) + latitude, longitude = GeoRssDistanceHelper.extract_coordinates(mock_point) assert latitude == -30.0 assert longitude == 151.0 def test_extract_coordinates_from_polygon(self): """Test extracting coordinates from polygon.""" - mock_polygon = Polygon([Point(-30.0, 151.0), - Point(-30.0, 151.5), - Point(-30.5, 151.5), - Point(-30.5, 151.0), - Point(-30.0, 151.0)]) - latitude, longitude = GeoRssDistanceHelper.\ - extract_coordinates(mock_polygon) + mock_polygon = Polygon( + [ + Point(-30.0, 151.0), + Point(-30.0, 151.5), + Point(-30.5, 151.5), + Point(-30.5, 151.0), + Point(-30.0, 151.0), + ] + ) + latitude, longitude = GeoRssDistanceHelper.extract_coordinates(mock_polygon) self.assertAlmostEqual(latitude, -30.2, 1) self.assertAlmostEqual(longitude, 151.2, 1) def test_extract_coordinates_from_unsupported_geometry(self): """Test extracting coordinates from unsupported geometry.""" mock_unsupported_geometry = MagicMock() - latitude, longitude = GeoRssDistanceHelper.\ - extract_coordinates(mock_unsupported_geometry) + latitude, longitude = GeoRssDistanceHelper.extract_coordinates( + mock_unsupported_geometry + ) self.assertIsNone(latitude) self.assertIsNone(longitude) @@ -41,195 +44,235 @@ def test_distance_to_point(self): """Test calculating distance to point.""" home_coordinates = (-31.0, 150.0) mock_point = Point(-30.0, 151.0) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_point) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_point + ) self.assertAlmostEqual(distance, 146.8, 1) def test_distance_to_polygon_1(self): """Test calculating distance to polygon.""" home_coordinates = (-31.0, 150.0) - mock_polygon = Polygon([Point(-30.0, 151.0), - Point(-30.0, 151.5), - Point(-30.5, 151.5), - Point(-30.5, 151.0), - Point(-30.0, 151.0)]) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + mock_polygon = Polygon( + [ + Point(-30.0, 151.0), + Point(-30.0, 151.5), + Point(-30.5, 151.5), + Point(-30.5, 151.0), + Point(-30.0, 151.0), + ] + ) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 110.6, 1) def test_distance_to_polygon_2(self): """Test calculating distance to polygon.""" home_coordinates = (-30.2, 151.2) - mock_polygon = Polygon([Point(-30.0, 151.0), - Point(-30.0, 151.5), - Point(-30.5, 151.5), - Point(-30.5, 151.0), - Point(-30.0, 151.0)]) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + mock_polygon = Polygon( + [ + Point(-30.0, 151.0), + Point(-30.0, 151.5), + Point(-30.5, 151.5), + Point(-30.5, 151.0), + Point(-30.0, 151.0), + ] + ) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 0.0, 1) def test_distance_to_polygon_3(self): """Test calculating distance to polygon.""" home_coordinates = (-29.0, 151.2) - mock_polygon = Polygon([Point(-30.0, 151.0), - Point(-30.0, 151.5), - Point(-30.5, 151.5), - Point(-30.5, 151.0), - Point(-30.0, 151.0)]) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + mock_polygon = Polygon( + [ + Point(-30.0, 151.0), + Point(-30.0, 151.5), + Point(-30.5, 151.5), + Point(-30.5, 151.0), + Point(-30.0, 151.0), + ] + ) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 111.2, 1) def test_distance_to_polygon_4(self): """Test calculating distance to polygon.""" home_coordinates = (30.0, 151.3) - mock_polygon = Polygon([Point(30.0, 151.0), - Point(30.0, 151.5), - Point(30.5, 151.5), - Point(30.5, 151.0), - Point(30.0, 151.0)]) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + mock_polygon = Polygon( + [ + Point(30.0, 151.0), + Point(30.0, 151.5), + Point(30.5, 151.5), + Point(30.5, 151.0), + Point(30.0, 151.0), + ] + ) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 0.0, 1) def test_distance_to_polygon_5(self): """Test calculating distance to polygon.""" - mock_polygon = Polygon([Point(30.0, 179.0), - Point(30.0, -179.5), - Point(30.5, -179.5), - Point(30.5, 179.0), - Point(30.0, 179.0)]) + mock_polygon = Polygon( + [ + Point(30.0, 179.0), + Point(30.0, -179.5), + Point(30.5, -179.5), + Point(30.5, 179.0), + Point(30.0, 179.0), + ] + ) home_coordinates = (30.2, -177.0) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 240.3, 1) home_coordinates = (30.1, 178.0) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 96.2, 1) home_coordinates = (31.0, -179.8) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 55.6, 1) home_coordinates = (31.0, 179.8) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 55.6, 1) def test_distance_to_polygon_6(self): """Test calculating distance to polygon.""" - mock_polygon = Polygon([Point(-30.0, 179.0), - Point(-30.0, -179.5), - Point(-29.5, -179.5), - Point(-29.5, 179.0), - Point(-30.0, 179.0)]) + mock_polygon = Polygon( + [ + Point(-30.0, 179.0), + Point(-30.0, -179.5), + Point(-29.5, -179.5), + Point(-29.5, 179.0), + Point(-30.0, 179.0), + ] + ) home_coordinates = (-29.8, -177.0) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 241.2, 1) home_coordinates = (-29.9, 178.0) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 96.4, 1) home_coordinates = (-29.0, -179.8) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 55.6, 1) home_coordinates = (-29.0, 179.8) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_polygon) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_polygon + ) self.assertAlmostEqual(distance, 55.6, 1) def test_distance_to_bbox_1(self): """Test calculating distance to bounding box.""" home_coordinates = (20.0, 20.0) # 1. inside - mock_bbox = BoundingBox(Point(10.0, 10.0), - Point(30.0, 30.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(10.0, 10.0), Point(30.0, 30.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 0.0, 1) # 2. above-left - mock_bbox = BoundingBox(Point(10.0, 25.0), - Point(15.0, 30.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(10.0, 25.0), Point(15.0, 30.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 768.1, 1) # 3. above - mock_bbox = BoundingBox(Point(10.0, 15.0), - Point(15.0, 25.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(10.0, 15.0), Point(15.0, 25.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 556.0, 1) # 4. above-right - mock_bbox = BoundingBox(Point(10.0, 10.0), - Point(15.0, 15.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(10.0, 10.0), Point(15.0, 15.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 768.1, 1) # 5. left - mock_bbox = BoundingBox(Point(15.0, 25.0), - Point(25.0, 30.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(15.0, 25.0), Point(25.0, 30.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 522.4, 1) # 6. right - mock_bbox = BoundingBox(Point(15.0, 10.0), - Point(25.0, 15.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(15.0, 10.0), Point(25.0, 15.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 522.4, 1) # 7. below-left - mock_bbox = BoundingBox(Point(25.0, 25.0), - Point(30.0, 30.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(25.0, 25.0), Point(30.0, 30.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 756.8, 1) # 8. below - mock_bbox = BoundingBox(Point(25.0, 15.0), - Point(30.0, 25.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(25.0, 15.0), Point(30.0, 25.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 556.0, 1) # 9. below-right - mock_bbox = BoundingBox(Point(25.0, 10.0), - Point(30.0, 15.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(25.0, 10.0), Point(30.0, 15.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 756.8, 1) # special case home_coordinates = (-20.0, -20.0) - mock_bbox = BoundingBox(Point(-30.0, -15.0), - Point(-25.0, -10.0)) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + mock_bbox = BoundingBox(Point(-30.0, -15.0), Point(-25.0, -10.0)) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 756.8, 1) def test_distance_to_bbox_2(self): """Test calculating distance to bounding box.""" - mock_bbox = BoundingBox(Point(5.0, 175.0), - Point(15.0, -175.0)) + mock_bbox = BoundingBox(Point(5.0, 175.0), Point(15.0, -175.0)) # 1. inside home_coordinates = (5.0, 176.0) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 0.0, 1) # 2. above-left home_coordinates = (20.0, 170.0) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 768.1, 1) # 3. above-right home_coordinates = (20.0, -170.0) - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_bbox) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_bbox + ) self.assertAlmostEqual(distance, 768.1, 1) def test_distance_to_unsupported_geometry(self): """Test calculating distance to unsupported geometry.""" home_coordinates = (-31.0, 150.0) mock_unsupported_geometry = MagicMock() - distance = GeoRssDistanceHelper.\ - distance_to_geometry(home_coordinates, mock_unsupported_geometry) + distance = GeoRssDistanceHelper.distance_to_geometry( + home_coordinates, mock_unsupported_geometry + ) assert distance == float("inf") diff --git a/tests/test_xml_parser.py b/tests/test_xml_parser.py index c1f2ac3..5f658cf 100644 --- a/tests/test_xml_parser.py +++ b/tests/test_xml_parser.py @@ -14,7 +14,7 @@ class TestXmlParser(unittest.TestCase): def test_simple_1(self): """Test parsing various actual XML files.""" xml_parser = XmlParser() - xml = load_fixture('xml_parser_simple_1.xml') + xml = load_fixture("xml_parser_simple_1.xml") feed = xml_parser.parse(xml) self.assertIsNotNone(feed) self.assertIsNotNone(feed.entries) @@ -23,7 +23,7 @@ def test_simple_1(self): def test_simple_2(self): """Test parsing various actual XML files.""" xml_parser = XmlParser() - xml = load_fixture('xml_parser_simple_2.xml') + xml = load_fixture("xml_parser_simple_2.xml") feed = xml_parser.parse(xml) self.assertIsNotNone(feed) self.assertIsNotNone(feed.entries) @@ -32,14 +32,14 @@ def test_simple_2(self): def test_simple_3(self): """Test parsing various actual XML files.""" xml_parser = XmlParser() - xml = load_fixture('xml_parser_simple_3.xml') + xml = load_fixture("xml_parser_simple_3.xml") feed = xml_parser.parse(xml) self.assertIsNone(feed) def test_complex_1(self): """Test parsing various actual XML files.""" xml_parser = XmlParser() - xml = load_fixture('xml_parser_complex_1.xml') + xml = load_fixture("xml_parser_complex_1.xml") feed = xml_parser.parse(xml) self.assertIsNotNone(feed) @@ -49,18 +49,18 @@ def test_complex_1(self): assert feed.summary == "Feed Description 1" assert feed.content == "Feed Description 1" assert feed.link == "Feed Link 1" - assert feed.published_date \ - == datetime.datetime(2018, 12, 9, 8, 30, - tzinfo=datetime.timezone.utc) - assert feed.pub_date \ - == datetime.datetime(2018, 12, 9, 8, 30, - tzinfo=datetime.timezone.utc) - assert feed.updated_date \ - == datetime.datetime(2018, 12, 9, 8, 45, - tzinfo=datetime.timezone.utc) - assert feed.last_build_date \ - == datetime.datetime(2018, 12, 9, 8, 45, - tzinfo=datetime.timezone.utc) + assert feed.published_date == datetime.datetime( + 2018, 12, 9, 8, 30, tzinfo=datetime.timezone.utc + ) + assert feed.pub_date == datetime.datetime( + 2018, 12, 9, 8, 30, tzinfo=datetime.timezone.utc + ) + assert feed.updated_date == datetime.datetime( + 2018, 12, 9, 8, 45, tzinfo=datetime.timezone.utc + ) + assert feed.last_build_date == datetime.datetime( + 2018, 12, 9, 8, 45, tzinfo=datetime.timezone.utc + ) assert feed.copyright == "Feed Copyright 1" assert feed.rights == "Feed Copyright 1" assert feed.generator == "Feed Generator 1" @@ -78,7 +78,7 @@ def test_complex_1(self): assert feed.image.description == "Image Description 1" assert feed.image.width == 123 assert feed.image.height == 234 - assert feed.get_additional_attribute('random') == "Feed Random 1" + assert feed.get_additional_attribute("random") == "Feed Random 1" assert repr(feed) == "" self.assertIsNotNone(feed.entries) @@ -88,12 +88,12 @@ def test_complex_1(self): assert feed_entry.title == "Title 1" assert feed_entry.description == "Description 1" assert feed_entry.link == "Link 1" - assert feed_entry.published_date \ - == datetime.datetime(2018, 12, 9, 7, 30, - tzinfo=datetime.timezone.utc) - assert feed_entry.updated_date \ - == datetime.datetime(2018, 12, 9, 7, 45, - tzinfo=datetime.timezone.utc) + assert feed_entry.published_date == datetime.datetime( + 2018, 12, 9, 7, 30, tzinfo=datetime.timezone.utc + ) + assert feed_entry.updated_date == datetime.datetime( + 2018, 12, 9, 7, 45, tzinfo=datetime.timezone.utc + ) assert feed_entry.guid == "GUID 1" assert feed_entry.id == "GUID 1" assert feed_entry.source == "Source 1" @@ -103,19 +103,19 @@ def test_complex_1(self): self.assertIsInstance(geometries[0], Point) assert geometries[0].latitude == -37.4567 assert geometries[0].longitude == 149.3456 - assert feed_entry.get_additional_attribute('random') == "Random 1" + assert feed_entry.get_additional_attribute("random") == "Random 1" assert repr(feed_entry) == "" feed_entry = feed.entries[1] assert feed_entry.title == "Title 2" assert feed_entry.description == "Description 2" assert feed_entry.link == "Link 2" - assert feed_entry.published_date \ - == datetime.datetime(2018, 12, 9, 7, 35, - tzinfo=datetime.timezone.utc) - assert feed_entry.updated_date \ - == datetime.datetime(2018, 12, 9, 7, 50, - tzinfo=datetime.timezone.utc) + assert feed_entry.published_date == datetime.datetime( + 2018, 12, 9, 7, 35, tzinfo=datetime.timezone.utc + ) + assert feed_entry.updated_date == datetime.datetime( + 2018, 12, 9, 7, 50, tzinfo=datetime.timezone.utc + ) assert feed_entry.guid == "GUID 2" assert feed_entry.category == ["Category 2"] geometries = feed_entry.geometries @@ -127,16 +127,14 @@ def test_complex_1(self): feed_entry = feed.entries[2] assert feed_entry.title == "Title 3" assert feed_entry.description == "Description 3" - assert feed_entry.published_date \ - == datetime.datetime(2018, 12, 9, 7, 40, - tzinfo=datetime.timezone.utc) - assert feed_entry.updated_date \ - == datetime.datetime(2018, 12, 9, 7, 55, - tzinfo=datetime.timezone.utc) + assert feed_entry.published_date == datetime.datetime( + 2018, 12, 9, 7, 40, tzinfo=datetime.timezone.utc + ) + assert feed_entry.updated_date == datetime.datetime( + 2018, 12, 9, 7, 55, tzinfo=datetime.timezone.utc + ) assert feed_entry.guid == "GUID 3" - assert feed_entry.category == ["Category 3A", - "Category 3B", - "Category 3C"] + assert feed_entry.category == ["Category 3A", "Category 3B", "Category 3C"] geometries = feed_entry.geometries assert len(geometries) == 1 self.assertIsInstance(geometries[0], Point) @@ -148,11 +146,16 @@ def test_complex_1(self): assert feed_entry.description == "Description 4" assert feed_entry.author == "Author 4" assert feed_entry.contributor == "Author 4" - assert feed_entry.category == ["Category 4A", - "Category 4B"] + assert feed_entry.category == ["Category 4A", "Category 4B"] assert feed_entry.published_date == datetime.datetime( - 2018, 9, 30, 21, 36, 48, - tzinfo=datetime.timezone(datetime.timedelta(hours=10), 'AEST')) + 2018, + 9, + 30, + 21, + 36, + 48, + tzinfo=datetime.timezone(datetime.timedelta(hours=10), "AEST"), + ) geometries = feed_entry.geometries assert len(geometries) == 1 self.assertIsInstance(geometries[0], Point) @@ -163,8 +166,14 @@ def test_complex_1(self): assert feed_entry.title == "Title 5" assert feed_entry.description == "Description 5" assert feed_entry.published_date == datetime.datetime( - 2018, 9, 20, 18, 1, 55, - tzinfo=datetime.timezone(datetime.timedelta(hours=2), 'CEST')) + 2018, + 9, + 20, + 18, + 1, + 55, + tzinfo=datetime.timezone(datetime.timedelta(hours=2), "CEST"), + ) geometries = feed_entry.geometries assert len(geometries) == 1 self.assertIsInstance(geometries[0], Polygon) @@ -175,8 +184,13 @@ def test_complex_1(self): assert feed_entry.title == "Title 6" assert feed_entry.description == "Description 6" assert feed_entry.published_date == datetime.datetime( - 2018, 10, 7, 19,52, - tzinfo=datetime.timezone(datetime.timedelta(hours=-7), 'PDT')) + 2018, + 10, + 7, + 19, + 52, + tzinfo=datetime.timezone(datetime.timedelta(hours=-7), "PDT"), + ) geometries = feed_entry.geometries assert len(geometries) == 1 self.assertIsInstance(geometries[0], Polygon) @@ -186,7 +200,7 @@ def test_complex_1(self): def test_complex_2(self): """Test parsing various actual XML files.""" xml_parser = XmlParser() - xml = load_fixture('xml_parser_complex_2.xml') + xml = load_fixture("xml_parser_complex_2.xml") feed = xml_parser.parse(xml) self.assertIsNotNone(feed) @@ -194,12 +208,12 @@ def test_complex_2(self): assert feed.subtitle == "Feed Subtitle 1" assert feed.ttl == "INVALID" assert feed.author == "Author 1" - assert feed.last_build_date \ - == datetime.datetime(2018, 12, 9, 9, 0, - tzinfo=datetime.timezone.utc) - assert feed.updated_date \ - == datetime.datetime(2018, 12, 9, 9, 0, - tzinfo=datetime.timezone.utc) + assert feed.last_build_date == datetime.datetime( + 2018, 12, 9, 9, 0, tzinfo=datetime.timezone.utc + ) + assert feed.updated_date == datetime.datetime( + 2018, 12, 9, 9, 0, tzinfo=datetime.timezone.utc + ) assert feed.copyright == "Feed Rights 1" assert feed.rights == "Feed Rights 1" assert feed.generator == "Feed Generator 1" @@ -222,7 +236,7 @@ def test_complex_2(self): def test_complex_3(self): """Test parsing various actual XML files.""" xml_parser = XmlParser() - xml = load_fixture('xml_parser_complex_3.xml') + xml = load_fixture("xml_parser_complex_3.xml") feed = xml_parser.parse(xml) self.assertIsNotNone(feed) @@ -251,7 +265,7 @@ def test_complex_3(self): def test_geometries_2(self): """Test parsing various geometries in entries.""" xml_parser = XmlParser() - xml = load_fixture('xml_parser_geometries_1.xml') + xml = load_fixture("xml_parser_geometries_1.xml") feed = xml_parser.parse(xml) self.assertIsNotNone(feed) @@ -303,9 +317,11 @@ def test_byte_order_mark(self): """Test parsing an XML file with byte order mark.""" xml_parser = XmlParser() # Create XML starting with byte order mark. - xml = "\xef\xbb\xbf" \ - "Title 1" \ - "" + xml = ( + "\xef\xbb\xbf" + "Title 1" + "" + ) # This will raise an error because the parser can't handle with self.assertRaises(ExpatError): xml_parser.parse(xml) diff --git a/tests/utils.py b/tests/utils.py index 0bba672..485a1b4 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,6 +4,6 @@ def load_fixture(filename): """Load a fixture.""" - path = os.path.join(os.path.dirname(__file__), 'fixtures', filename) - with open(path, encoding='utf-8') as fptr: + path = os.path.join(os.path.dirname(__file__), "fixtures", filename) + with open(path, encoding="utf-8") as fptr: return fptr.read()