Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor Timeline class to pydantic model #30

Merged
merged 6 commits into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 152 additions & 149 deletions Pipfile.lock

Large diffs are not rendered by default.

21 changes: 19 additions & 2 deletions app/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""app.models.py"""
from typing import Dict, List

from pydantic import BaseModel
from pydantic import BaseModel, validator


class Latest(BaseModel):
Expand All @@ -27,9 +27,26 @@ class Timeline(BaseModel):
Timeline model.
"""

latest: int
timeline: Dict[str, int] = {}

@validator("timeline")
@classmethod
def sort_timeline(cls, value):
"""Sort the timeline history before inserting into the model"""
return dict(sorted(value.items()))

@property
def latest(self):
"""Get latest available history value."""
return list(self.timeline.values())[-1] if self.timeline else 0

def serialize(self):
"""
Serialize the model into dict
TODO: override dict() instead of using serialize
"""
return {**self.dict(), "latest": self.latest}


class Timelines(BaseModel):
"""
Expand Down
10 changes: 5 additions & 5 deletions app/services/location/jhu.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ...caches import check_cache, load_cache
from ...coordinates import Coordinates
from ...location import TimelinedLocation
from ...timeline import Timeline
from ...models import Timeline
from ...utils import countries
from ...utils import date as date_util
from ...utils import httputils
Expand Down Expand Up @@ -178,25 +178,25 @@ async def get_locations():
location["country"],
location["province"],
# Coordinates.
Coordinates(coordinates["lat"], coordinates["long"]),
Coordinates(latitude=coordinates["lat"], longitude=coordinates["long"]),
# Last update.
datetime.utcnow().isoformat() + "Z",
# Timelines (parse dates as ISO).
{
"confirmed": Timeline(
{
timeline={
datetime.strptime(date, "%m/%d/%y").isoformat() + "Z": amount
for date, amount in timelines["confirmed"].items()
}
),
"deaths": Timeline(
{
timeline={
datetime.strptime(date, "%m/%d/%y").isoformat() + "Z": amount
for date, amount in timelines["deaths"].items()
}
),
"recovered": Timeline(
{
timeline={
datetime.strptime(date, "%m/%d/%y").isoformat() + "Z": amount
for date, amount in timelines["recovered"].items()
}
Expand Down
8 changes: 4 additions & 4 deletions app/services/location/nyt.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from ...caches import check_cache, load_cache
from ...coordinates import Coordinates
from ...location.nyt import NYTLocation
from ...timeline import Timeline
from ...models import Timeline
from ...utils import httputils
from . import LocationService

Expand Down Expand Up @@ -119,18 +119,18 @@ async def get_locations():
last_updated=datetime.utcnow().isoformat() + "Z", # since last request
timelines={
"confirmed": Timeline(
{
timeline={
datetime.strptime(date, "%Y-%m-%d").isoformat() + "Z": amount
for date, amount in confirmed_history.items()
}
),
"deaths": Timeline(
{
timeline={
datetime.strptime(date, "%Y-%m-%d").isoformat() + "Z": amount
for date, amount in deaths_history.items()
}
),
"recovered": Timeline({}),
"recovered": Timeline(),
},
)
)
Expand Down
42 changes: 0 additions & 42 deletions app/timeline.py

This file was deleted.

24 changes: 12 additions & 12 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,44 @@ astroid==2.4.2
async-asgi-testclient==1.4.4
async-generator==1.10
asyncmock==0.4.2
attrs==19.3.0
attrs==20.2.0
bandit==1.6.2
black==19.10b0
certifi==2020.6.20
chardet==3.0.4
click==7.1.2
coverage==5.2.1
coveralls==2.1.1
coverage==5.3
coveralls==2.1.2
docopt==0.6.2
gitdb==4.0.5
gitpython==3.1.7
gitpython==3.1.8
idna==2.10
importlib-metadata==1.7.0 ; python_version < '3.8'
iniconfig==1.0.1
invoke==1.4.1
isort==4.3.21
isort==5.5.2
lazy-object-proxy==1.4.3
mccabe==0.6.1
mock==4.0.2
more-itertools==8.4.0
more-itertools==8.5.0
multidict==4.7.6
packaging==20.4
pathspec==0.8.0
pbr==5.4.5
pbr==5.5.0
pluggy==0.13.1
py==1.9.0
pylint==2.5.3
pylint==2.6.0
pyparsing==2.4.7
pytest-asyncio==0.14.0
pytest-cov==2.10.0
pytest==6.0.1
pytest-cov==2.10.1
pytest==6.0.2
pyyaml==5.3.1
regex==2020.7.14
requests==2.24.0
responses==0.10.15
responses==0.12.0
six==1.15.0
smmap==3.0.4
stevedore==3.2.0
stevedore==3.2.2
toml==0.10.1
typed-ast==1.4.1
urllib3[secure]==1.25.10 ; python_version >= '3.5'
Expand Down
12 changes: 6 additions & 6 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ aioredis==1.3.1
asgiref==3.2.10 ; python_version >= '3.5'
async-timeout==3.0.1
asyncache==0.1.1
attrs==19.3.0
attrs==20.2.0
cachetools==4.1.1
certifi==2020.6.20
cffi==1.14.1
cffi==1.14.2
chardet==3.0.4
click==7.1.2
cryptography==3.0
cryptography==3.1
dataclasses==0.6 ; python_version < '3.7'
fastapi==0.60.1
fastapi==0.61.1
gunicorn==20.0.4
h11==0.9.0
hiredis==1.1.0
Expand All @@ -29,8 +29,8 @@ pyopenssl==19.1.0
python-dateutil==2.8.1
python-dotenv==0.14.0
requests==2.24.0
scout-apm==2.15.2
sentry-sdk==0.16.2
scout-apm==2.16.1
sentry-sdk==0.17.4
six==1.15.0
starlette==0.13.6
urllib3[secure]==1.25.10 ; python_version >= '3.5'
Expand Down
4 changes: 2 additions & 2 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
def sort(ctx, targets="."):
"""Sort module imports."""
print("sorting imports ...")
args = ["isort", "-rc", "--atomic", targets]
args = ["isort", "--atomic", targets]
ctx.run(" ".join(args))


Expand All @@ -40,7 +40,7 @@ def check(ctx, fmt=False, sort=False, diff=False): # pylint: disable=redefined-
sort = True

fmt_args = ["black", "--check", "."]
sort_args = ["isort", "-rc", "--check", "."]
sort_args = ["isort", "--check", "."]

if diff:
fmt_args.append("--diff")
Expand Down
1 change: 0 additions & 1 deletion tests/test_countries.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from app.utils import countries


"""
Todo:
* Test cases for capturing of stdout/stderr
Expand Down
12 changes: 6 additions & 6 deletions tests/test_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import pytest

from app import coordinates, location, timeline
from app import coordinates, location, models


def mocked_timeline(*args, **kwargs):
Expand All @@ -22,7 +22,7 @@ def __init__(self, latest):
(2, "Cruise Ship", "XX", "", 15, 100, 1000, 1111, 22222),
],
)
@mock.patch("app.timeline.Timeline", side_effect=mocked_timeline)
@mock.patch("app.models.Timeline", side_effect=mocked_timeline)
def test_location_class(
mocked_timeline,
test_id,
Expand All @@ -39,9 +39,9 @@ def test_location_class(
coords = coordinates.Coordinates(latitude=latitude, longitude=longitude)

# Timelines
confirmed = timeline.Timeline(confirmed_latest)
deaths = timeline.Timeline(deaths_latest)
recovered = timeline.Timeline(recovered_latest)
confirmed = models.Timeline(confirmed_latest)
deaths = models.Timeline(deaths_latest)
recovered = models.Timeline(recovered_latest)

# Date now.
now = datetime.utcnow().isoformat() + "Z"
Expand All @@ -57,4 +57,4 @@ def test_location_class(
)

assert location_obj.country_code == country_code
assert not location_obj.serialize() == None
assert location_obj.serialize() is not None
4 changes: 2 additions & 2 deletions tests/test_timeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import pytest

from app import timeline
from app import models


def test_timeline_class():
Expand All @@ -15,7 +15,7 @@ def test_timeline_class():
"1/23/20": 3,
}

history_data = timeline.Timeline(history=timeseries)
history_data = models.Timeline(timeline=timeseries)

# validate last value
assert history_data.latest == 7
Expand Down