Skip to content

Commit

Permalink
Refactoring crime incidents, moved former unit test into integration …
Browse files Browse the repository at this point in the history
…tests

  Closes codeforboston#194
  • Loading branch information
wesdrew committed Mar 12, 2019
1 parent 4dd4a8d commit ca6ca9e
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import unittest
from mycity.utilities.crime_incidents_api_utils import get_crime_incident_response


class CrimeIncidentsAPIUtilitiesTestCase(unittest.TestCase):

def test_get_crime_incident_response_returns_success_if_query_is_successful(self):
test_address = "46 Everdean St Boston, MA"
result = get_crime_incident_response(test_address)
self.assertEqual(True, result['success'])
57 changes: 34 additions & 23 deletions mycity/mycity/test/unit_tests/test_crime_incidents_api_utils.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import unittest
import unittest.mock as mock
import mycity.test.test_constants as test_constants
import mycity.test.unit_tests.base as base
from mycity.utilities.crime_incidents_api_utils import \
get_crime_incident_response

class CrimeIncidentsAPIUtilitiesTestCase(base.BaseTestCase):

@mock.patch(
'mycity.utilities.gis_utils.geocode_address',
return_value=test_constants.GEOCODE_ADDRESS_MOCK
)
@mock.patch('requests.get')
def test_get_crime_incident_response(self, mock_geocode_address, mock_get):
mock_resp = self._mock_response(status=200,
json_data=test_constants.GET_CRIME_INCIDENTS_API_MOCK)
mock_get.return_value = mock_resp

test_address = "46 Everdean St Boston, MA"
result = get_crime_incident_response(test_address)
self.assertEqual(
True,
result['success']
)
import requests
import typing
from mycity.utilities.crime_incidents_api_utils import get_crime_incident_response


class ResponseStub:

def __init__(self, status_code: int, response_data: typing.Dict = {}):
self.status_code = status_code
self.response_data = response_data

def json(self):
return self.response_data


class CrimeIncidentsAPIUtilitiesTestCase(unittest.TestCase):

def test_get_crime_incident_response_returns_empty_dict_if_request_fails(self):
requests_stub = _build_requests_stub(requests.codes.bad)
to_test = get_crime_incident_response('46 Everdean St.', requests_stub)
self.assertEquals({}, to_test)

def test_get_crime_incident_response_returns_json_if_request_succeeds(self):
expected = {'test': 'yes, it\'s a test'}
requests_stub = _build_requests_stub(requests.codes.ok, expected)
to_test = get_crime_incident_response('46 Everdean St.', requests_stub)
self.assertEquals(expected, to_test)


def _build_requests_stub(status_code: int, data_from_service: typing.Dict = {}):
requests_stub = requests
requests_stub.Session.get = mock.MagicMock(return_value = ResponseStub(status_code, data_from_service))
return requests_stub
61 changes: 36 additions & 25 deletions mycity/mycity/utilities/crime_incidents_api_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,71 @@
"""

import requests
from mycity.utilities.gis_utils import geocode_address
import typing
import logging
from mycity.utilities.gis_utils import geocode_address


RESOURCEID = "12cb3883-56f5-47de-afa5-3b1cf61b257b"
RESOURCE_ID = "12cb3883-56f5-47de-afa5-3b1cf61b257b"
QUERY_LIMIT = 5
CRIME_INCIDENTS_SQL_URL = \
"https://data.boston.gov/api/3/action/datastore_search_sql"
LONG_INDEX = 0
LAT_INDEX = 1

logger = logging.getLogger(__name__)


def get_crime_incident_response(address):
def get_crime_incident_response(address: str, _requests: typing.ClassVar = requests):
"""
Executes and returns the crime incident request response
:param address: address to query
:param address: address to query
:param _requests: Injectable request class
:return: the raw json response
"""
url_parameters = {"sql": _build_query_string(address)}
logger.debug("Finding crime incidents information for {} using query {}"
.format(address, url_parameters))
with requests.Session() as session:
logger.debug("Finding crime incidents information for {} using query {}".format(address, url_parameters))

with _requests.Session() as session:
response = session.get(CRIME_INCIDENTS_SQL_URL, params=url_parameters)

if response.status_code == requests.codes.ok:
if response.status_code == _requests.codes.ok:
return response.json()
return {}


def _build_query_string(address):
def _get_coordinates_for_address(
address: str,
_geocode_address: typing.Callable[[str], list] = geocode_address)-> tuple:
"""
Builds the SQL query given an address
Populates the GPS coordinates for the provided address
:param address: address to query
:return: a SQL query string
:param address: address to query
:param _geocode_address: injectable function for test
:return: a tuple of the form (lat, long)
"""
coordinates = _get_coordinates_for_address(address)
return """SELECT * FROM "{}" WHERE "lat" LIKE '{}%' AND \
"long" LIKE '{}%' LIMIT {}""" \
.format(RESOURCEID, coordinates[0], coordinates[1], QUERY_LIMIT)
coordinates = _geocode_address(address)
logger.debug("Got coordinates: {}".format(coordinates))
_lat = "{:.2f}".format(float(coordinates[LAT_INDEX]))
_long = "{:.2f}".format(float(coordinates[LONG_INDEX]))
return _lat, _long


def _get_coordinates_for_address(address):
def _build_query_string(
address: str,
_get_coordinates_for_address: typing.Callable[[str], list] = _get_coordinates_for_address)-> str:
"""
Populates the GPS coordinates for the provided address
Builds the SQL query given an address
:param address: address to query
:return: a tuple of the form (lat, long)
:param address: address to query
:param _get_coordinates_for_address: injectable function for test
:return: a SQL query string
"""
coordinates = geocode_address(address)
logger.debug("Got coordinates: {}".format(coordinates))
_lat = "{:.2f}".format(float(coordinates[1]))
_long = "{:.2f}".format(float(coordinates[0]))
return (_lat, _long)
coordinates = _get_coordinates_for_address(address)
return """SELECT * FROM "{}" WHERE "lat" LIKE '{}%' AND \
"long" LIKE '{}%' LIMIT {}""" \
.format(RESOURCE_ID, coordinates[0], coordinates[1], QUERY_LIMIT)

0 comments on commit ca6ca9e

Please sign in to comment.