Skip to content

Commit

Permalink
PEP8 conformance #2714
Browse files Browse the repository at this point in the history
  • Loading branch information
iamleeg committed Jun 20, 2022
1 parent 6568e33 commit 2087c4e
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '0.1.0'
__version__ = "0.1.0"

from .model.case import Case
from .controller.case_controller import CaseController
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from flask import jsonify


class CaseController:
"""Implements CRUD operations on cases. Uses an external store
class to actually work with the cases collection, so that different
storage technology can be chosen.
All methods return a tuple of (response, HTTP status code)"""

def __init__(self, store):
self._store = store

Expand Down
18 changes: 11 additions & 7 deletions data-serving/reusable-data-service/reusable_data_service/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,30 @@
import logging

app = Flask(__name__)
case_controller = None # Will be set up in main()
case_controller = None # Will be set up in main()

@app.route('/api/case/<id>')

@app.route("/api/case/<id>")
def get_case(id):
return case_controller.get_case(id)


def set_up_controllers():
global case_controller
store_options = { 'mongodb': MongoStore.setup }
if store_choice := os.environ.get('DATA_STORAGE_BACKEND'):
store_options = {"mongodb": MongoStore.setup}
if store_choice := os.environ.get("DATA_STORAGE_BACKEND"):
try:
store = store_options[store_choice]()
except KeyError:
logging.exception(f"Cannot configure backend data store {store_choice}")
raise
case_controller = CaseController(store)


def main():
set_up_controllers()
app.run(host='0.0.0.0', port=8080)
app.run(host="0.0.0.0", port=8080)


if __name__ == '__main__':
main()
if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@
import datetime
import json


@dataclasses.dataclass()
class DayZeroCase:
"""This class implements the "day-zero" data schema for Global.health.
At the beginning of an outbreak, we want to collect at least this much
information about an individual case for the line list.
Parameters here are defined to be keyword-only and not set in the
initialiser, so that clients can use Builder to populate them. Use
the validate() method to determine whether an instance is in a
consistent state (this also means we can add custom validation logic
to that function)."""

_: dataclasses.KW_ONLY
confirmation_date: datetime.datetime = dataclasses.field(init=False)

Expand All @@ -22,9 +24,9 @@ def from_json(cls, obj: str) -> type:
case = cls()
source = json.loads(obj)
for key in source:
if key in ['confirmation_date']:
if key in ["confirmation_date"]:
# parse as an ISO 8601 date
date = datetime.datetime.strptime(source[key], '%Y-%m-%dT%H:%M:%S.%fZ')
date = datetime.datetime.strptime(source[key], "%Y-%m-%dT%H:%M:%S.%fZ")
setattr(case, key, date)
else:
setattr(case, key, source[key])
Expand All @@ -33,11 +35,12 @@ def from_json(cls, obj: str) -> type:

def validate(self):
"""Check whether I am consistent. Raise ValueError if not."""
if not hasattr(self, 'confirmation_date'):
if not hasattr(self, "confirmation_date"):
raise ValueError("Confirmation Date is mandatory")
elif self.confirmation_date is None:
raise ValueError("Confirmation Date must have a value")


# Actually we want to capture extra fields which can be specified dynamically:
# so Case is the class that you should use.
Case = dataclasses.make_dataclass('Case', fields=[], bases=(DayZeroCase,))
Case = dataclasses.make_dataclass("Case", fields=[], bases=(DayZeroCase,))
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
from bson.json_util import dumps
from bson.objectid import ObjectId


class MongoStore:
"""A line list store backed by mongodb."""
def __init__(self, connection_string: str, database_name: str, collection_name: str):

def __init__(
self, connection_string: str, database_name: str, collection_name: str
):
self.client = pymongo.MongoClient(connection_string)
self.database_name = database_name
self.collection_name = collection_name

def get_client(self):
return self.client

def get_database(self):
return self.get_client()[self.database_name]

Expand All @@ -24,12 +28,14 @@ def case_by_id(self, id: str):
case = self.get_case_collection().find_one({"_id": ObjectId(id)})
# case includes BSON fields like ObjectID - convert into JSON for use by the app
return loads(dumps(case))

@staticmethod
def setup():
"""Configure a store instance from the environment."""
mongo_connection_string = os.environ.get('MONGO_CONNECTION')
mongo_database = os.environ.get('MONGO_DB')
mongo_collection = os.environ.get('MONGO_CASE_COLLECTION')
mongo_store = MongoStore(mongo_connection_string, mongo_database, mongo_collection)
mongo_connection_string = os.environ.get("MONGO_CONNECTION")
mongo_database = os.environ.get("MONGO_DB")
mongo_collection = os.environ.get("MONGO_CASE_COLLECTION")
mongo_store = MongoStore(
mongo_connection_string, mongo_database, mongo_collection
)
return mongo_store
14 changes: 8 additions & 6 deletions data-serving/reusable-data-service/tests/test_case_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@

class MemoryStore:
"""Simple dictionary-based store for cases."""

def __init__(self):
self.cases = dict()

def case_by_id(self, id: str):
return self.cases.get(id)

def put_case(self, id: str, case: Case):
self.cases[id] = case

Expand All @@ -23,17 +24,18 @@ def app_context():

def test_one_present_item_should_return_200_OK(app_context):
store = MemoryStore()
with open('./tests/data/case.minimal.json', 'r') as minimal_file:
with open("./tests/data/case.minimal.json", "r") as minimal_file:
case = Case.from_json(minimal_file.read())
store.put_case('foo', case)
store.put_case("foo", case)
controller = CaseController(store)
(response, status) = controller.get_case('foo')
(response, status) = controller.get_case("foo")
assert status == 200
assert response is not None


def test_one_absent_item_should_return_400_not_found(app_context):
store = MemoryStore()
controller = CaseController(store)
(response, status) = controller.get_case('foo')
(response, status) = controller.get_case("foo")
assert status == 404
assert response == "No case with ID foo"
30 changes: 20 additions & 10 deletions data-serving/reusable-data-service/tests/test_case_end_to_end.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,38 @@

from reusable_data_service import app, set_up_controllers


@pytest.fixture
def client_with_patched_mongo(monkeypatch):
# configure controllers
monkeypatch.setenv('DATA_STORAGE_BACKEND', 'mongodb')
monkeypatch.setenv('MONGO_CONNECTION_STRING', 'mongodb://localhost/27017') # will be unused
monkeypatch.setenv('MONGO_DB', 'outbreak')
monkeypatch.setenv('MONGO_CASE_COLLECTION', 'cases')
monkeypatch.setenv("DATA_STORAGE_BACKEND", "mongodb")
monkeypatch.setenv(
"MONGO_CONNECTION_STRING", "mongodb://localhost/27017"
) # will be unused
monkeypatch.setenv("MONGO_DB", "outbreak")
monkeypatch.setenv("MONGO_CASE_COLLECTION", "cases")
db = mongomock.MongoClient()

def fake_mongo(connection_string):
return db
monkeypatch.setattr('pymongo.MongoClient', fake_mongo)

monkeypatch.setattr("pymongo.MongoClient", fake_mongo)
set_up_controllers()
app.config['TESTING'] = True
app.config["TESTING"] = True
client = app.test_client()
yield client


def test_get_case_with_known_id(client_with_patched_mongo):
# insert a case
db = pymongo.MongoClient('mongodb://localhost/27017')
case_id = db['outbreak']['cases'].insert_one({"confirmation_date": "2021-12-31T01:23:45.678Z"}).inserted_id
response = client_with_patched_mongo.get(f'/api/case/{str(case_id)}')
db = pymongo.MongoClient("mongodb://localhost/27017")
case_id = (
db["outbreak"]["cases"]
.insert_one({"confirmation_date": "2021-12-31T01:23:45.678Z"})
.inserted_id
)
response = client_with_patched_mongo.get(f"/api/case/{str(case_id)}")
result = response.get_json()
assert response.status_code == 200
assert result is not None
assert result['confirmation_date'] == '2021-12-31T01:23:45.678Z'
assert result["confirmation_date"] == "2021-12-31T01:23:45.678Z"
8 changes: 5 additions & 3 deletions data-serving/reusable-data-service/tests/test_case_model.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import pytest
from reusable_data_service import Case


def test_instantiating_case_from_empty_json_is_error():
with pytest.raises(ValueError):
case = Case.from_json('{}')
case = Case.from_json("{}")


def test_case_from_minimal_json_is_valid():
with open('./tests/data/case.minimal.json', 'r') as minimal_file:
with open("./tests/data/case.minimal.json", "r") as minimal_file:
case = Case.from_json(minimal_file.read())
assert case is not None
assert case is not None
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@


def test_version():
assert __version__ == '0.1.0'
assert __version__ == "0.1.0"

0 comments on commit 2087c4e

Please sign in to comment.