Skip to content

Commit 69c2d02

Browse files
Kilo59deepsourcebottanwinncodedawisourcery-ai[bot]
authored
cache & refactor updates (#310)
* reduce cache size (#21) * formatting (120 length -> 100) * csbs to/from Redis * nyt to/from Redis * ignore locustfile * unused coordinates * cache redis json serialization error * fix nyt redis serialization error * refactor Timeline class to pydantic model * partial recovery fix (#31) * fix jhu timeline init call * update requirements * update isort usage Co-authored-by: DeepSource Bot <[email protected]> Co-authored-by: Thanh Nguyen <[email protected]> Co-authored-by: codedawi <[email protected]> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Sourcery AI <[email protected]>
1 parent 1508dce commit 69c2d02

25 files changed

+504
-388
lines changed

.deepsource.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version = 1
2+
3+
test_patterns = ["tests/**"]
4+
5+
[[analyzers]]
6+
name = "python"
7+
enabled = true
8+
9+
[analyzers.meta]
10+
runtime_version = "3.x.x"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ htmlcov/
5151
nosetests.xml
5252
coverage.xml
5353
*,cover
54+
locustfile.py
5455

5556
# Translations
5657
*.mo

Pipfile.lock

Lines changed: 152 additions & 149 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/data/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
from ..services.location.nyt import NYTLocationService
55

66
# Mapping of services to data-sources.
7-
DATA_SOURCES = {"jhu": JhuLocationService(), "csbs": CSBSLocationService(), "nyt": NYTLocationService()}
7+
DATA_SOURCES = {
8+
"jhu": JhuLocationService(),
9+
"csbs": CSBSLocationService(),
10+
"nyt": NYTLocationService(),
11+
}
812

913

1014
def data_source(source):

app/io.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010

1111

1212
def save(
13-
name: str, content: Union[str, Dict, List], write_mode: str = "w", indent: int = 2, **json_dumps_kwargs
13+
name: str,
14+
content: Union[str, Dict, List],
15+
write_mode: str = "w",
16+
indent: int = 2,
17+
**json_dumps_kwargs,
1418
) -> pathlib.Path:
1519
"""Save content to a file. If content is a dictionary, use json.dumps()."""
1620
path = DATA / name
@@ -35,7 +39,12 @@ class AIO:
3539

3640
@classmethod
3741
async def save(
38-
cls, name: str, content: Union[str, Dict, List], write_mode: str = "w", indent: int = 2, **json_dumps_kwargs
42+
cls,
43+
name: str,
44+
content: Union[str, Dict, List],
45+
write_mode: str = "w",
46+
indent: int = 2,
47+
**json_dumps_kwargs,
3948
):
4049
"""Save content to a file. If content is a dictionary, use json.dumps()."""
4150
path = DATA / name

app/location/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Location: # pylint: disable=too-many-instance-attributes
1111
"""
1212

1313
def __init__(
14-
self, id, country, province, coordinates, last_updated, confirmed, deaths, recovered
14+
self, id, country, province, coordinates, last_updated, confirmed, deaths, recovered,
1515
): # pylint: disable=too-many-arguments
1616
# General info.
1717
self.id = id
@@ -66,7 +66,11 @@ def serialize(self):
6666
# Last updated.
6767
"last_updated": self.last_updated,
6868
# Latest data (statistics).
69-
"latest": {"confirmed": self.confirmed, "deaths": self.deaths, "recovered": self.recovered},
69+
"latest": {
70+
"confirmed": self.confirmed,
71+
"deaths": self.deaths,
72+
"recovered": self.recovered,
73+
},
7074
}
7175

7276

app/main.py

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
import pydantic
77
import sentry_sdk
88
import uvicorn
9-
from fastapi import FastAPI, Request, Response, openapi
9+
from fastapi import FastAPI, Request, Response
1010
from fastapi.middleware.cors import CORSMiddleware
1111
from fastapi.middleware.gzip import GZipMiddleware
1212
from fastapi.responses import JSONResponse
13-
from fastapi.staticfiles import StaticFiles
1413
from scout_apm.async_.starlette import ScoutMiddleware
1514
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
1615

@@ -35,9 +34,9 @@
3534
"API for tracking the global coronavirus (COVID-19, SARS-CoV-2) outbreak."
3635
" Project page: https://github.com/ExpDev07/coronavirus-tracker-api."
3736
),
38-
version="2.0.3",
39-
docs_url=None,
40-
redoc_url=None,
37+
version="2.0.4",
38+
docs_url="/",
39+
redoc_url="/docs",
4140
on_startup=[setup_client_session],
4241
on_shutdown=[teardown_client_session],
4342
)
@@ -60,7 +59,11 @@
6059

6160
# Enable CORS.
6261
APP.add_middleware(
63-
CORSMiddleware, allow_credentials=True, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"],
62+
CORSMiddleware,
63+
allow_credentials=True,
64+
allow_origins=["*"],
65+
allow_methods=["*"],
66+
allow_headers=["*"],
6467
)
6568
APP.add_middleware(GZipMiddleware, minimum_size=1000)
6669

@@ -109,31 +112,6 @@ async def handle_validation_error(
109112
# Include routers.
110113
APP.include_router(V1, prefix="", tags=["v1"])
111114
APP.include_router(V2, prefix="/v2", tags=["v2"])
112-
APP.mount("/static", StaticFiles(directory="static"), name="static")
113-
114-
# ##############
115-
# Swagger/Redocs
116-
# ##############
117-
118-
119-
@APP.get("/", include_in_schema=False)
120-
async def custom_swagger_ui_html():
121-
"""Serve Swagger UI."""
122-
return openapi.docs.get_swagger_ui_html(
123-
openapi_url=APP.openapi_url,
124-
title=f"{APP.title} - Swagger UI",
125-
oauth2_redirect_url=APP.swagger_ui_oauth2_redirect_url,
126-
swagger_js_url="/static/swagger-ui-bundle.js",
127-
swagger_css_url="/static/swagger-ui.css",
128-
)
129-
130-
131-
@APP.get("/docs", include_in_schema=False)
132-
async def redoc_html():
133-
"""Serve ReDoc UI."""
134-
return openapi.docs.get_redoc_html(
135-
openapi_url=APP.openapi_url, title=f"{APP.title} - ReDoc", redoc_js_url="/static/redoc.standalone.js",
136-
)
137115

138116

139117
# Running of app.

app/models.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""app.models.py"""
22
from typing import Dict, List
33

4-
from pydantic import BaseModel
4+
from pydantic import BaseModel, validator
55

66

77
class Latest(BaseModel):
@@ -27,9 +27,26 @@ class Timeline(BaseModel):
2727
Timeline model.
2828
"""
2929

30-
latest: int
3130
timeline: Dict[str, int] = {}
3231

32+
@validator("timeline")
33+
@classmethod
34+
def sort_timeline(cls, value):
35+
"""Sort the timeline history before inserting into the model"""
36+
return dict(sorted(value.items()))
37+
38+
@property
39+
def latest(self):
40+
"""Get latest available history value."""
41+
return list(self.timeline.values())[-1] if self.timeline else 0
42+
43+
def serialize(self):
44+
"""
45+
Serialize the model into dict
46+
TODO: override dict() instead of using serialize
47+
"""
48+
return {**self.dict(), "latest": self.latest}
49+
3350

3451
class Timelines(BaseModel):
3552
"""

app/routers/v1.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ async def all_categories():
1919
"deaths": deaths,
2020
"recovered": recovered,
2121
# Latest.
22-
"latest": {"confirmed": confirmed["latest"], "deaths": deaths["latest"], "recovered": recovered["latest"],},
22+
"latest": {
23+
"confirmed": confirmed["latest"],
24+
"deaths": deaths["latest"],
25+
"recovered": recovered["latest"],
26+
},
2327
}
2428

2529

app/routers/v2.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,17 @@ async def get_locations(
6565

6666
# Do filtering.
6767
try:
68-
locations = [location for location in locations if str(getattr(location, key)).lower() == str(value)]
68+
locations = [
69+
location
70+
for location in locations
71+
if str(getattr(location, key)).lower() == str(value)
72+
]
6973
except AttributeError:
7074
pass
7175
if not locations:
72-
raise HTTPException(404, detail=f"Source `{source}` does not have the desired location data.")
76+
raise HTTPException(
77+
404, detail=f"Source `{source}` does not have the desired location data.",
78+
)
7379

7480
# Return final serialized data.
7581
return {
@@ -84,7 +90,9 @@ async def get_locations(
8490

8591
# pylint: disable=invalid-name
8692
@V2.get("/locations/{id}", response_model=LocationResponse)
87-
async def get_location_by_id(request: Request, id: int, source: Sources = "jhu", timelines: bool = True):
93+
async def get_location_by_id(
94+
request: Request, id: int, source: Sources = "jhu", timelines: bool = True
95+
):
8896
"""
8997
Getting specific location by id.
9098
"""

0 commit comments

Comments
 (0)