Skip to content

Commit

Permalink
refactor: fix python warnings and reformat files
Browse files Browse the repository at this point in the history
  • Loading branch information
SlowMo24 authored Nov 16, 2023
1 parent b7f5b5b commit ebfcbe8
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 51 deletions.
60 changes: 36 additions & 24 deletions ohsome/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""OhsomeClient classes to build and handle requests to ohsome API"""
import json
import os
import urllib
from urllib.parse import urljoin

import requests
from requests.adapters import HTTPAdapter
Expand Down Expand Up @@ -152,7 +152,6 @@ def metadata(self):
def _query_metadata(self):
"""
Send ohsome GET request
:param params: parameters of the request as in ohsome documentation
:return:
"""
self._url = self._base_api_url + "metadata"
Expand Down Expand Up @@ -225,10 +224,10 @@ def post(
Sends request to ohsome API
:param bboxes: Bounding boxes given as
- str e.g. "lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|… or id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|…
- str e.g. lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|… or id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|…
- list e.g. [[id1:lon1,lat1,lon2,lat2],[id2:lon1,lat1,lon2,lat2],...]
- pandas.DataFrame with columns minx, miny, maxx, maxy. These columns can be created from a GeoDataFrame using the
'GeoDataFrame.bounds' method.
- pandas.DataFrame with columns minx, miny, maxx, maxy. These columns can be created from a GeoDataFrame using
the 'GeoDataFrame.bounds' method.
:param bcircles: Coordinate pair and radius in meters to define a circular polygon given as
- str e.g. lon,lat,radius|lon,lat,radius|… or id1:lon,lat,radius|id2:lon,lat,radius|…
Expand All @@ -245,18 +244,18 @@ def post(
:param filter: (str) Combines several attributive filters: OSM type, geometry (simple feature) type, OSM tag
:param filter2: (str) Combines several attributive filters. Only for ratio queries.
:param format: (str) ‘json’ or ‘csv’; default: ‘json’
:param showMetadata (str, bool) add additional metadata information to the response: ‘true’, ‘false’, ‘yes’,
:param showMetadata: (str, bool) add additional metadata information to the response: ‘true’, ‘false’, ‘yes’,
‘no’; default: ‘false’
:param timeout (str, int) custom timeout to limit the processing time in seconds; default: dependent on server
:param timeout: (str, int) custom timeout to limit the processing time in seconds; default: dependent on server
settings, retrievable via the /metadata request
:param filter2: (str) Combines several attributive filters. Only for ratio queries.
:param groupByKey: (str) OSM key, no default value (only one groupByKey can be defined),
non matching objects (if any) will be summarised in a ‘remainder’ category. Only for groupByKey queries.
non-matching objects (if any) will be summarised in a ‘remainder’ category. Only for groupByKey queries.
:param groupByKeys: (str, list) OSM key(s) given as a list and combined with the ‘AND’ operator; default: empty;
Expand Down Expand Up @@ -285,24 +284,27 @@ def _handle_request(self) -> OhsomeResponse:
:return:
"""
ohsome_exception = None
response = None

try:
response = self._session().post(url=self._url, data=self._parameters)
response.raise_for_status()
response.json()

except requests.exceptions.HTTPError as e:
try:
error_message = e.response.json()["message"]
except json.decoder.JSONDecodeError:
error_message = f"Invalid URL: Is {self._url} valid?"
finally:
ohsome_exception = OhsomeException(
message=error_message,
url=self._url,
params=self._parameters,
error_code=e.response.status_code,
response=e.response,
)

ohsome_exception = OhsomeException(
message=error_message,
url=self._url,
params=self._parameters,
error_code=e.response.status_code,
response=e.response,
)

except requests.exceptions.ConnectionError as e:
ohsome_exception = OhsomeException(
message="Connection Error: Query could not be sent. Make sure there are no network "
Expand All @@ -311,29 +313,39 @@ def _handle_request(self) -> OhsomeResponse:
params=self._parameters,
response=e.response,
)

except requests.exceptions.RequestException as e:
ohsome_exception = OhsomeException(
message=str(e),
url=self._url,
params=self._parameters,
response=e.response,
)

except KeyboardInterrupt:
ohsome_exception = OhsomeException(
message="Keyboard Interrupt: Query was interrupted by the user.",
url=self._url,
params=self._parameters,
error_code=440,
)
except ValueError:
error_code, message = extract_error_message_from_invalid_json(response.text)

except ValueError as e:
if response:
error_code, message = extract_error_message_from_invalid_json(
response.text
)
else:
message = str(e)
error_code = None
ohsome_exception = OhsomeException(
message=message,
url=self._url,
error_code=error_code,
params=self._parameters,
response=response,
)

except AttributeError:
ohsome_exception = OhsomeException(
message=f"Seems like {self._url} is not a valid endpoint.",
Expand Down Expand Up @@ -374,9 +386,9 @@ def _construct_resource_url(self, endpoint=None):
:return:
"""
if endpoint:
self._url = urllib.parse.urljoin(self._base_api_url, endpoint.strip("/"))
self._url = urljoin(self._base_api_url, endpoint.strip("/"))
else:
self._url = urllib.parse.urljoin(self._base_api_url, "/".join(self._cache))
self._url = urljoin(self._base_api_url, "/".join(self._cache))

def _(self, name):
# Enables method chaining
Expand Down Expand Up @@ -599,7 +611,7 @@ def groupByBoundary(self):


class _OhsomeClientElementsFullHistory(_OhsomeBaseClient):
"""Subclass of _OhsomePostClient to define endpoints of ohsome API"""
"""Subclass of _OhsomeBaseClient to define endpoints of ohsome API for full history analyses."""

@property
def bbox(self):
Expand Down Expand Up @@ -663,7 +675,7 @@ def density(self):


class _OhsomeClientContributionsLatest(_OhsomePostClient):
"""Subclass of _OhsomePostClient to define endpoints of ohsome API"""
"""Subclass of _OhsomePostClient to define endpoints of ohsome API for contribution analyses."""

@property
def bbox(self):
Expand Down
19 changes: 9 additions & 10 deletions ohsome/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import datetime
import re
from typing import Tuple

import geopandas as gpd
import numpy as np
Expand Down Expand Up @@ -97,8 +98,8 @@ def format_bcircles(bcircles):
elif isinstance(bcircles, dict):
return "|".join(
[
f"{id}:" + ",".join([str(c) for c in coords])
for id, coords in bcircles.items()
f"{bcircle_id}:" + ",".join([str(c) for c in coords])
for bcircle_id, coords in bcircles.items()
]
)
elif isinstance(bcircles, gpd.GeoDataFrame):
Expand Down Expand Up @@ -133,8 +134,8 @@ def format_bboxes(bboxes):
bboxes: Bounding boxes given as
string: lon1,lat1,lon2,lat2|lon1,lat1,lon2,lat2|… or id1:lon1,lat1,lon2,lat2|id2:lon1,lat1,lon2,lat2|…
list: [[id1,lon1,lat1,lon2,lat2],[id2,lon1,lat1,lon2,lat2],...] or [lon1,lat1,lon2,lat2] if it's just one box
pandas.DataFrame: with columns minx, miny, maxx, maxy. These columns can be created from a GeoDataFrame using the
'GeoDataFrame.bounds' method.
pandas.DataFrame: with columns minx, miny, maxx, maxy. These columns can be created from a GeoDataFrame using
the 'GeoDataFrame.bounds' method.
:return: Bounding boxes formatted as a string compliant with ohsome API
"""
if isinstance(bboxes, list) or isinstance(bboxes, tuple):
Expand All @@ -149,8 +150,8 @@ def format_bboxes(bboxes):
elif isinstance(bboxes, dict):
return "|".join(
[
f"{id}:" + ",".join([str(c) for c in coords])
for id, coords in bboxes.items()
f"{bbox_id}:" + ",".join([str(c) for c in coords])
for bbox_id, coords in bboxes.items()
]
)
elif isinstance(bboxes, str):
Expand Down Expand Up @@ -207,16 +208,14 @@ def find_groupby_names(url):
return [name.strip("/") for name in url.split("groupBy")[1:]]


def extract_error_message_from_invalid_json(responsetext):
def extract_error_message_from_invalid_json(responsetext) -> Tuple[int, str]:
"""
Extract error code and error message from invalid json returned from ohsome API
Otherwise throws OhsomeException.
:param response:
:param responsetext:
:return:
"""
# responsetext = response.text

message = "A broken response has been received"

m = re.search('"message" : "(.*)"', responsetext)
Expand Down
14 changes: 9 additions & 5 deletions ohsome/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@

"""Class for ohsome API response"""

import json

import geopandas as gpd
import pandas as pd
import json
from pandas import DataFrame

from ohsome.helper import find_groupby_names


Expand Down Expand Up @@ -138,10 +141,11 @@ def _set_index(self, result_df, groupby_names):
[*groupby_names, "fromTimestamp", "toTimestamp"], inplace=True
)

def _create_groupby_dataframe(self, data, groupby_names):
def _create_groupby_dataframe(self, data, groupby_names) -> DataFrame:
"""
Formats groupby results
:param result_df:
:param data:
:param groupby_names:
:return:
"""
keys = list(data[0].keys())
Expand All @@ -162,10 +166,10 @@ def _create_groupby_dataframe(self, data, groupby_names):
record_dfs.extend(record_result)
return pd.DataFrame().from_records(record_dfs)

def _format_timestamp(self, result_df):
def _format_timestamp(self, result_df: DataFrame) -> None:
"""
Format timestamp column as datetime
:param dresult_dff:
:param result_df:
:return:
"""
if "timestamp" in result_df.columns:
Expand Down
17 changes: 9 additions & 8 deletions ohsome/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
# -*- coding: utf-8 -*-
"""Test OhsomeExceptions"""

import logging
import os
from pathlib import Path
from unittest.mock import patch, MagicMock

import pytest
import logging
import geopandas as gpd
import ohsome
from unittest.mock import patch, MagicMock
import pytest
from requests.exceptions import RequestException

import ohsome

script_path = os.path.dirname(os.path.realpath(__file__))
logger = logging.getLogger(__name__)

Expand All @@ -33,8 +34,8 @@ def test_timeout_error(base_client):
bboxes=bboxes, time=time, filter=fltr, timeout=timeout
)
assert (
"The given query is too large in respect to the given timeout. Please use a smaller region and/or coarser time period."
in e_info.value.message
"The given query is too large in respect to the given timeout. Please use a smaller region and/or coarser "
"time period." in e_info.value.message
)


Expand Down Expand Up @@ -135,7 +136,7 @@ def test_metadata_invalid_baseurl(custom_client_with_wrong_url):
def test_exception_invalid_parameters(base_client):
"""
Test whether error message from ohsome API is forwarded to user
:param custom_client:
:param base_client:
:return:
"""
bboxes = [8.6577, 49.3958, 8.7122, 49.4296]
Expand All @@ -152,7 +153,7 @@ def test_exception_invalid_parameters(base_client):
def test_exception_connection_reset(base_client):
"""
Test whether error without response (e.g. connection reset) is handled correctly
:param custom_client:
:param base_client:
:return:
"""

Expand Down
2 changes: 1 addition & 1 deletion ohsome/test/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def test_extract_error_message_from_invalid_json():

def test_extract_error_message_from_invalid_json_no_message():
"""
Test whether error code and message are extracted correctly if theres is no message in the json response
Test whether error code and message are extracted correctly if there is no message in the json response
:return:
"""

Expand Down
4 changes: 2 additions & 2 deletions ohsome/test/test_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
# -*- coding: utf-8 -*-
"""Tests for ohsome API response"""
import warnings
import pytest

import geopandas as gpd
import pandas as pd
import pytest


@pytest.mark.vcr
def test_elements_count(base_client):
"""
Tests whether the result of elements.count is formatted correctly as a pandas.DataFrame. If this works
.area, .length and .permiter should work as well.
.area, .length and .perimeter should work as well.
:return:
"""
bboxes = "8.6933,49.40893,8.69797,49.41106"
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ requests = "^2.25.1"
pandas = "^2.1.3"
numpy = "^1.20.0"
geopandas = "^0.14.1"
pyproj = "^3.0.0"
urllib3 = "^2.1.0"

[tool.poetry.group.test.dependencies]
Expand Down

0 comments on commit ebfcbe8

Please sign in to comment.