diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 4ecdd0b6..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,36 +0,0 @@ -# Contribution to Coronavirus Tracker API - -First off, thanks for taking the time to contribute! -Every commit supports the open source ecosystem in case of [COVID-19](https://en.wikipedia.org/wiki/2019%E2%80%9320_coronavirus_pandemic). - -## Testing - -We have a handful of unit tests to cover most of functions. -Please write new test cases for new code you create. - -## Submitting changes - -* If you're unable to find an open issue, [open a new one](https://github.com/ExpDev07/coronavirus-tracker-api/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible -* Open a new [GitHub Pull Request to coronavirus-tracker-api](https://github.com/ExpDev07/coronavirus-tracker-api/pulls) with a clear list of what you've done (read more about [pull requests](http://help.github.com/pull-requests/)). Include the relevant issue number if applicable. -* We will love you forever if you include unit tests. We can always use more test coverage -* If you have updated [Pipefile](./Pipfile), you have to update `Pipfile.lock`, `requirements.txt` and `requirements-dev.txt`. See section [Update requirements files](./README.md#update-requirements-files). - -## Your First Code Contribution - -Unsure where to begin contributing to coronavirus-tracker-api ? You can start by looking through these issues labels: - -* [Enhancement issues](https://github.com/ExpDev07/coronavirus-tracker-api/labels/enhancement) - issues for new feature or request -* [Help wanted issues](https://github.com/ExpDev07/coronavirus-tracker-api/labels/help%20wanted) - extra attention is needed -* [Documentation issues](https://github.com/ExpDev07/coronavirus-tracker-api/labels/documentation) - improvements or additions to documentation - -## Styleguide - -Please follow [PEP8](https://www.python.org/dev/peps/pep-0008/) guide. -See [Running Test](./README.md#running-tests), [Linting](./README.md#linting) and [Formatting](./README.md#formatting) sections for further instructions to validate your change. - - -We encourage you to pitch in and join the [Coronavirus Tracker API Team](https://github.com/ExpDev07/coronavirus-tracker-api#contributors-)! - -Thanks! :heart: :heart: :heart: - -[Coronavirus Tracker API Team](https://github.com/ExpDev07/coronavirus-tracker-api#contributors-) diff --git a/README.md b/README.md deleted file mode 100644 index 7355d0af..00000000 --- a/README.md +++ /dev/null @@ -1,529 +0,0 @@ -

- Coronavirus Tracker API -

- -Provides up-to-date data about Coronavirus outbreak. Includes numbers about confirmed cases, deaths and recovered. -Support multiple data-sources. - -![Travis build](https://api.travis-ci.com/ExpDev07/coronavirus-tracker-api.svg?branch=master) -[![Coverage Status](https://coveralls.io/repos/github/ExpDev07/coronavirus-tracker-api/badge.svg?branch=master)](https://coveralls.io/github/ExpDev07/coronavirus-tracker-api?branch=master) -[![License](https://img.shields.io/github/license/ExpDev07/coronavirus-tracker-api)](LICENSE.md) -[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-) -[![GitHub stars](https://img.shields.io/github/stars/ExpDev07/coronavirus-tracker-api)](https://github.com/ExpDev07/coronavirus-tracker-api/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/ExpDev07/coronavirus-tracker-api)](https://github.com/ExpDev07/coronavirus-tracker-api/network/members) -[![GitHub last commit](https://img.shields.io/github/last-commit/ExpDev07/coronavirus-tracker-api)](https://github.com/ExpDev07/coronavirus-tracker-api/commits/master) -[![GitHub pull requests](https://img.shields.io/github/issues-pr/ExpDev07/coronavirus-tracker-api)](https://github.com/ExpDev07/coronavirus-tracker-api/pulls) -[![GitHub issues](https://img.shields.io/github/issues/ExpDev07/coronavirus-tracker-api)](https://github.com/ExpDev07/coronavirus-tracker-api/issues) -[![Total alerts](https://img.shields.io/lgtm/alerts/g/ExpDev07/coronavirus-tracker-api.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/ExpDev07/coronavirus-tracker-api/alerts/) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![Tweet](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2FExpDev07%2Fcoronavirus-tracker-api)](https://twitter.com/intent/tweet?text=COVID19%20Live%20Tracking%20API:%20&url=https%3A%2F%2Fgithub.com%2FExpDev07%2Fcoronavirus-tracker-api) - -**Live global stats (provided by [fight-covid19/bagdes](https://github.com/fight-covid19/bagdes)) from this API:** - -![Covid-19 Confirmed](https://covid19-badges.herokuapp.com/confirmed/latest) -![Covid-19 Recovered](https://covid19-badges.herokuapp.com/recovered/latest) -![Covid-19 Deaths](https://covid19-badges.herokuapp.com/deaths/latest) - -## New York Times is now available as a source! - -**Specify source parameter with ?source=nyt. NYT also provides a timeseries! To view timelines of cases by US counties use ?source=nyt&timelines=true** - -## Recovered cases showing 0 - -**JHU (our main data provider) [no longer provides data for amount of recoveries](https://github.com/CSSEGISandData/COVID-19/issues/1250), and as a result, the API will be showing 0 for this statistic. Apologies for any inconvenience. Hopefully we'll be able to find an alternative data-source that offers this.** - -## Available data-sources: - -Currently 3 different data-sources are available to retrieve the data: - -* **jhu** - https://github.com/CSSEGISandData/COVID-19 - Worldwide Data repository operated by the Johns Hopkins University Center for Systems Science and Engineering (JHU CSSE). - -* **csbs** - https://www.csbs.org/information-covid-19-coronavirus - U.S. County data that comes from the Conference of State Bank Supervisors. - -* **nyt** - https://github.com/nytimes/covid-19-data - The New York Times is releasing a series of data files with cumulative counts of coronavirus cases in the United States. This API provides the timeseries at the US county level. - -__jhu__ data-source will be used as a default source if you don't specify a *source parameter* in your request. - -## API Reference - -All endpoints are located at ``coronavirus-tracker-api.herokuapp.com/v2/`` and are accessible via https. For instance: you can get data per location by using this URL: -*[https://coronavirus-tracker-api.herokuapp.com/v2/locations](https://coronavirus-tracker-api.herokuapp.com/v2/locations)* - -You can open the URL in your browser to further inspect the response. Or you can make this curl call in your terminal to see the prettified response: - -``` -curl https://coronavirus-tracker-api.herokuapp.com/v2/locations | json_pp -``` - -### Swagger/OpenAPI - -Consume our API through [our super awesome and interactive SwaggerUI](https://coronavirus-tracker-api.herokuapp.com/) (on mobile, use the [mobile friendly ReDocs](https://coronavirus-tracker-api.herokuapp.com/docs) instead for the best experience). - - -The [OpenAPI](https://swagger.io/docs/specification/about/) json definition can be downloaded at https://coronavirus-tracker-api.herokuapp.com/openapi.json - -## API Endpoints - -### Sources Endpoint - -Getting the data-sources that are currently available to Coronavirus Tracker API to retrieve the data of the pandemic. - -```http -GET /v2/sources -``` - -__Sample response__ -```json -{ - "sources": [ - "jhu", - "csbs", - "nyt" - ] -} -``` - -### Latest Endpoint - -Getting latest amount of total confirmed cases, deaths, and recovered. - -```http -GET /v2/latest -``` - -__Query String Parameters__ -| __Query string parameter__ | __Description__ | __Type__ | -| -------------------------- | -------------------------------------------------------------------------------- | -------- | -| source | The data-source where data will be retrieved from *(jhu/csbs/nyt)*. Default is *jhu* | String | - -__Sample response__ -```json -{ - "latest": { - "confirmed": 197146, - "deaths": 7905, - "recovered": 80840 - } -} -``` - -### Locations Endpoint - -Getting latest amount of confirmed cases, deaths, and recovered per location. - -#### The Location Object -```http -GET /v2/locations/:id -``` - -__Path Parameters__ -| __Path parameter__ | __Required/Optional__ | __Description__ | __Type__ | -| ------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | -| id | OPTIONAL | The unique location id for which you want to call the Locations Endpoint. The list of valid location IDs (:id) can be found in the locations response: ``/v2/locations`` | Integer | - -__Query String Parameters__ -| __Query string parameter__ | __Description__ | __Type__ | -| -------------------------- | -------------------------------------------------------------------------------- | -------- | -| source | The data-source where data will be retrieved from *(jhu/csbs/nyt)*. Default is *jhu* | String | - -#### Example Request -```http -GET /v2/locations/39 -``` - -__Sample response__ -```json -{ - "location": { - "id": 39, - "country": "Norway", - "country_code": "NO", - "country_population": 5009150, - "province": "", - "county": "", - "last_updated": "2020-03-21T06:59:11.315422Z", - "coordinates": { }, - "latest": { }, - "timelines": { - "confirmed": { - "latest": 1463, - "timeline": { - "2020-03-16T00:00:00Z": 1333, - "2020-03-17T00:00:00Z": 1463 - } - }, - "deaths": { }, - "recovered": { } - } - } -} -``` - -#### List of all locations -```http -GET /v2/locations -``` - -__Query String Parameters__ -| __Query string parameter__ | __Description__ | __Type__ | -| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | -| source | The data-source where data will be retrieved from.
__Value__ can be: *jhu/csbs/nyt*. __Default__ is *jhu* | String | -| country_code | The ISO ([alpha-2 country_code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)) to the Country/Province for which you're calling the Endpoint | String | -| timelines | To set the visibility of timelines (*daily tracking*).
__Value__ can be: *0/1*. __Default__ is *0* (timelines are not visible) | Integer | - -__Sample response__ -```json -{ - "latest": { - "confirmed": 272166, - "deaths": 11299, - "recovered": 87256 - }, - "locations": [ - { - "id": 0, - "country": "Thailand", - "country_code": "TH", - "country_population": 67089500, - "province": "", - "county": "", - "last_updated": "2020-03-21T06:59:11.315422Z", - "coordinates": { - "latitude": "15", - "longitude": "101" - }, - "latest": { - "confirmed": 177, - "deaths": 1, - "recovered": 41 - } - }, - { - "id": 39, - "country": "Norway", - "country_code": "NO", - "province": "", - "county": "", - "last_updated": "2020-03-21T06:59:11.315422Z", - "coordinates": { - "latitude": "60.472", - "longitude": "8.4689" - }, - "latest": { - "confirmed": 1463, - "deaths": 3, - "recovered": 1 - } - } - ] -} -``` - -__Response definitions__ -| __Response Item__ | __Description__ | __Type__ | -| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | -| {latest} | The total amount of confirmed cases, deaths and recovered for all the locations | Object | -| {latest}/confirmed | The up-to-date total number of confirmed cases for all the locations within the data-source | Integer | -| {latest}/deaths | The up-to-date total amount of deaths for all the locations within the data-source | Integer | -| {latest}/recovered | The up-to-date total amount of recovered for all the locations within the data-source | Integer | -| {locations} | The collection of locations contained within the data-source | Object | -| {location} | Information that identifies a location | Object | -| {latest} | The amount of confirmed cases, deaths and recovered related to the specific location | Object | -| {locations}/{location}/{latest}/confirmed | The up-to-date number of confirmed cases related to the specific location | Integer | -| {locations}/{location}/{latest}/deaths | The up-to-date number of deaths related to the specific location | Integer | -| {locations}/{location}/{latest}/recovered | The up-to-date number of recovered related to the specific location | Integer | -| {locations}/{location}/id | The location id. This unique id is assigned to the location by the data-source. | Integer | -| {locations}/{location}/country | The Country name | String | -| {locations}/{location}/country_code | The [ISO alpha-2 country_code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) Country code for the location. | String | -| {locations}/{location}/province | The province where the location belongs to. (Used for US locations coming from __csbs data-source__.
__Empty__ when *jhu data-source* is used | String | -| {locations}/{location}/{coordinates}/latitude | The location latitude | Float | -| {locations}/{location}/{coordinates}/longitude | The location longitude | Float | - - -### Example Requests with parameters - -__Parameter: country_code__ - -Getting data for the Country specified by the *country_code parameter*, in this case Italy - IT - -```http -GET /v2/locations?country_code=IT -``` - -__Sample Response__ -```json -{ - "latest": { - "confirmed": 59138, - "deaths": 5476, - "recovered": 7024 - }, - "locations": [ - { - "id": 16, - "country": "Italy", - "country_code": "IT", - "country_population": 60340328, - "province": "", - "county": "", - "last_updated": "2020-03-23T13:32:23.913872Z", - "coordinates": { - "latitude": "43", - "longitude": "12" - }, - "latest": { - "confirmed": 59138, - "deaths": 5476, - "recovered": 7024 - } - } - ] -} -``` - -__Parameter: source__ - -Getting the data from the data-source specified by the *source parameter*, in this case [csbs](https://www.csbs.org/information-covid-19-coronavirus) - - -```http -GET /v2/locations?source=csbs -``` - -__Sample Response__ -```json -{ - "latest": { - "confirmed": 7596, - "deaths": 43, - "recovered": 0 - }, - "locations": [ - { - "id": 0, - "country": "US", - "country_code": "US", - "country_population": 310232863, - "province": "New York", - "state": "New York", - "county": "New York", - "last_updated": "2020-03-21T14:00:00Z", - "coordinates": { - "latitude": 40.71455, - "longitude": -74.00714 - }, - "latest": { - "confirmed": 6211, - "deaths": 43, - "recovered": 0 - } - }, - { - "id": 1, - "country": "US", - "country_code": "US", - "country_population": 310232863, - "province": "New York", - "state": "New York", - "county": "Westchester", - "last_updated": "2020-03-21T14:00:00Z", - "coordinates": { - "latitude": 41.16319759, - "longitude": -73.7560629 - }, - "latest": { - "confirmed": 1385, - "deaths": 0, - "recovered": 0 - }, - } - ] -} -``` - -__Parameter: timelines__ - -Getting the data for all the locations including the daily tracking of confirmed cases, deaths and recovered per location. - -```http -GET /v2/locations?timelines=1 -``` -Explore the response by opening the URL in your browser [https://coronavirus-tracker-api.herokuapp.com/v2/locations?timelines=1](https://coronavirus-tracker-api.herokuapp.com/v2/locations?timelines=1) or make the following curl call in your terminal: - -``` -curl https://coronavirus-tracker-api.herokuapp.com/v2/locations?timelines=1 | json_pp -``` - -__NOTE:__ Timelines tracking starts from day 22nd January 2020 and ends to the last available day in the data-source. - - - -## Wrappers - -These are the available API wrappers created by the community. They are not necessarily maintained by any of this project's authors or contributors. - -### PHP - -* [CovidPHP by @o-ba](https://github.com/o-ba/covid-php). - -### Golang - -* [Go-corona by @itsksaurabh](https://github.com/itsksaurabh/go-corona). - -### C# - -* [CovidSharp by @Abdirahiim](https://github.com/Abdirahiim/covidtrackerapiwrapper) -* [Covid19Tracker.NET by @egbakou](https://github.com/egbakou/Covid19Tracker.NET) -* [CovidDotNet by @degant](https://github.com/degant/CovidDotNet) - -### Python - -* [COVID19Py by @Kamaropoulos](https://github.com/Kamaropoulos/COVID19Py). - -### Java - -* [Coronavirus by @mew](https://github.com/mew/Coronavirus). - -### Node.js - -* [jhucsse.covid by @Sem1084](https://www.npmjs.com/package/jhucsse.covid). - -### Ruby - -* [covid19-data-ruby by @jaerodyne](https://github.com/jaerodyne/covid19-data-ruby). - -### Lua - -* [lua-covid-data by @imolein](https://codeberg.org/imo/lua-covid-data). - -## Prerequisites - -You will need the following things properly installed on your computer. - -* [Python 3](https://www.python.org/downloads/) (with pip) -* [pipenv](https://pypi.org/project/pipenv/) - -## Installation - -* `git clone https://github.com/ExpDev07/coronavirus-tracker-api.git` -* `cd coronavirus-tracker-api` - -1. Make sure you have [`python3.8` installed and on your `PATH`](https://docs.python-guide.org/starting/installation/). -2. [Install the `pipenv` dependency manager](https://pipenv.readthedocs.io/en/latest/install/#installing-pipenv) - * with [pipx](https://pipxproject.github.io/pipx/) `$ pipx install pipenv` - * with [Homebrew/Linuxbrew](https://pipenv.readthedocs.io/en/latest/install/#homebrew-installation-of-pipenv) `$ brew install pipenv` - * with [pip/pip3 directly](https://pipenv.readthedocs.io/en/latest/install/#pragmatic-installation-of-pipenv) `$ pip install --user pipenv` -3. Create virtual environment and install all dependencies `$ pipenv sync --dev` -4. Activate/enter the virtual environment `$ pipenv shell` - -And don't despair if don't get the python setup working on the first try. No one did. Guido got pretty close... once. But that's another story. Good luck. - -## Running / Development - -For a live reloading on code changes. - -* `pipenv run dev` - -Without live reloading. - -* `pipenv run start` - -Visit your app at [http://localhost:8000](http://localhost:8000). - -Alternatively run our API with Docker. - -### Running Tests -> [pytest](https://docs.pytest.org/en/latest/) - -```bash -pipenv run test -``` - - -### Linting -> [pylint](https://www.pylint.org/) - -```bash -pipenv run lint -``` - -### Formatting -> [black](https://black.readthedocs.io/en/stable/) - -```bash -pipenv run fmt -``` - -### Update requirements files - -```bash -invoke generate-reqs -``` - -[Pipfile.lock](./Pipfile.lock) will be automatically updated during `pipenv install`. - -### Docker - -Our Docker image is based on [tiangolo/uvicorn-gunicorn-fastapi/](https://hub.docker.com/r/tiangolo/uvicorn-gunicorn-fastapi/). - -```bash -invoke docker --build -``` - -Run with `docker run` or `docker-compose` - -#### Alternate Docker images - -If a full `gunicorn` deployment is unnecessary or [impractical on your hardware](https://fastapi.tiangolo.com/deployment/#raspberry-pi-and-other-architectures) consider using our single instance [`Uvicorn`](https://www.uvicorn.org/) based [Dockerfile](uvicorn.Dockerfile). - - -### Invoke - -Additional developer commands can be run by calling them with the [python `invoke` task runner](http://www.pyinvoke.org/). -```bash -invoke --list -``` - -### Deploying - -## Contributors ✨ - -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ExpDev

πŸ’» πŸ“– 🚧

bjarkimg

πŸ’¬

Bost

πŸ“–

GRIBOK

πŸ’» ⚠️

Oliver Thamm

πŸ“–

Mauro M.

πŸ“–

JKSenthil

πŸ’» πŸ“– ⚠️

SeanCena

πŸ’» πŸ“– ⚠️

Abdirahiim Yassin

πŸ“– πŸ”§ πŸ“¦

DarΓ­o HereΓ±ΓΊ

πŸ“–

Oliver

πŸ“–

carmelag

πŸ“–

Gabriel

πŸ’» πŸš‡ ⚠️ πŸ“–

Kodjo Laurent Egbakou

πŸ“– πŸ”§ πŸ“¦

Turreted

πŸ’»

Ibtida Bhuiyan

πŸ’» πŸ“–

James Gray

πŸ’»

Nischal Shankar

πŸ’» πŸ“–
- - - - - -## License - -See [LICENSE.md](LICENSE.md) for the license. Please link to this repo somewhere in your project :). diff --git a/app/data/__init__.py b/app/data/__init__.py index 60a75dac..8a04dbd8 100644 --- a/app/data/__init__.py +++ b/app/data/__init__.py @@ -1,21 +1,38 @@ """app.data""" -from ..services.location.csbs import CSBSLocationService -from ..services.location.jhu import JhuLocationService -from ..services.location.nyt import NYTLocationService +from ..services.location.csbs import CSBSLocationServicee +from ..services.location.jhu import JhuLocationServicee +from ..services.location.nyt import NYTLocationServicee +from app.services.location.__init__ import LocationServicer -# Mapping of services to data-sources. -DATA_SOURCES = { - "jhu": JhuLocationService(), - "csbs": CSBSLocationService(), - "nyt": NYTLocationService(), -} +class DataSourceSingletonMeta(type): + """ + access point to the DataSource + """ + _instances = {} -def data_source(source): - """ - Retrieves the provided data-source service. + def __call__(cls, *args, **kwargs): + """ + Possible changes to the value of the `__init__` argument do not affect + the returned instance. + """ + if cls not in cls._instances: + instance = super().__call__(*args, **kwargs) + cls._instances[cls] = instance + return cls._instances[cls] - :returns: The service. - :rtype: LocationService - """ - return DATA_SOURCES.get(source.lower()) + +class DataSourceSingleton(metaclass=DataSourceSingletonMeta): + DATA_SOURCES = { + "jhu": LocationServicer(JhuLocationServicee()), + "csbs": LocationServicer(CSBSLocationServicee()), + "nyt": LocationServicer(NYTLocationServicee()), + } + def get_data_source(self, dataSource): + return self.DATA_SOURCES.get(dataSource.lower()) + ... + + def get_data_source_list(self): + return self.DATA_SOURCES + + diff --git a/app/main.py b/app/main.py deleted file mode 100644 index b9aff949..00000000 --- a/app/main.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -app.main.py -""" -import logging - -import pydantic -import sentry_sdk -import uvicorn -from fastapi import FastAPI, Request, Response -from fastapi.middleware.cors import CORSMiddleware -from fastapi.middleware.gzip import GZipMiddleware -from fastapi.responses import JSONResponse -from scout_apm.async_.starlette import ScoutMiddleware -from sentry_sdk.integrations.asgi import SentryAsgiMiddleware - -from .config import get_settings -from .data import data_source -from .routers import V1, V2 -from .utils.httputils import setup_client_session, teardown_client_session - -# ############ -# FastAPI App -# ############ -LOGGER = logging.getLogger("api") - -SETTINGS = get_settings() - -if SETTINGS.sentry_dsn: # pragma: no cover - sentry_sdk.init(dsn=SETTINGS.sentry_dsn) - -APP = FastAPI( - title="Coronavirus Tracker", - description=( - "API for tracking the global coronavirus (COVID-19, SARS-CoV-2) outbreak." - " Project page: https://github.com/ExpDev07/coronavirus-tracker-api." - ), - version="2.0.4", - docs_url="/", - redoc_url="/docs", - on_startup=[setup_client_session], - on_shutdown=[teardown_client_session], -) - -# ##################### -# Middleware -####################### - -# Scout APM -if SETTINGS.scout_name: # pragma: no cover - LOGGER.info(f"Adding Scout APM middleware for `{SETTINGS.scout_name}`") - APP.add_middleware(ScoutMiddleware) -else: - LOGGER.debug("No SCOUT_NAME config") - -# Sentry Error Tracking -if SETTINGS.sentry_dsn: # pragma: no cover - LOGGER.info("Adding Sentry middleware") - APP.add_middleware(SentryAsgiMiddleware) - -# Enable CORS. -APP.add_middleware( - CORSMiddleware, - allow_credentials=True, - allow_origins=["*"], - allow_methods=["*"], - allow_headers=["*"], -) -APP.add_middleware(GZipMiddleware, minimum_size=1000) - - -@APP.middleware("http") -async def add_datasource(request: Request, call_next): - """ - Attach the data source to the request.state. - """ - # Retrieve the datas ource from query param. - source = data_source(request.query_params.get("source", default="jhu")) - - # Abort with 404 if source cannot be found. - if not source: - return Response("The provided data-source was not found.", status_code=404) - - # Attach source to request. - request.state.source = source - - # Move on... - LOGGER.debug(f"source provided: {source.__class__.__name__}") - response = await call_next(request) - return response - - -# ################ -# Exception Handler -# ################ - - -@APP.exception_handler(pydantic.error_wrappers.ValidationError) -async def handle_validation_error( - request: Request, exc: pydantic.error_wrappers.ValidationError -): # pylint: disable=unused-argument - """ - Handles validation errors. - """ - return JSONResponse({"message": exc.errors()}, status_code=422) - - -# ################ -# Routing -# ################ - - -# Include routers. -APP.include_router(V1, prefix="", tags=["v1"]) -APP.include_router(V2, prefix="/v2", tags=["v2"]) - - -# Running of app. -if __name__ == "__main__": - uvicorn.run( - "app.main:APP", host="127.0.0.1", port=SETTINGS.port, log_level="info", - ) diff --git a/app/routers/v2.py b/app/routers/v2.py deleted file mode 100644 index 31eb408c..00000000 --- a/app/routers/v2.py +++ /dev/null @@ -1,110 +0,0 @@ -"""app.routers.v2""" -import enum - -from fastapi import APIRouter, HTTPException, Request - -from ..data import DATA_SOURCES -from ..models import LatestResponse, LocationResponse, LocationsResponse - -V2 = APIRouter() - - -class Sources(str, enum.Enum): - """ - A source available for retrieving data. - """ - - JHU = "jhu" - CSBS = "csbs" - NYT = "nyt" - - -@V2.get("/latest", response_model=LatestResponse) -async def get_latest( - request: Request, source: Sources = Sources.JHU -): # pylint: disable=unused-argument - """ - Getting latest amount of total confirmed cases, deaths, and recoveries. - """ - locations = await request.state.source.get_all() - return { - "latest": { - "confirmed": sum(map(lambda location: location.confirmed, locations)), - "deaths": sum(map(lambda location: location.deaths, locations)), - "recovered": sum(map(lambda location: location.recovered, locations)), - } - } - - -# pylint: disable=unused-argument,too-many-arguments,redefined-builtin -@V2.get("/locations", response_model=LocationsResponse, response_model_exclude_unset=True) -async def get_locations( - request: Request, - source: Sources = "jhu", - country_code: str = None, - province: str = None, - county: str = None, - timelines: bool = False, -): - """ - Getting the locations. - """ - # All query paramameters. - params = dict(request.query_params) - - # Remove reserved params. - params.pop("source", None) - params.pop("timelines", None) - - # Retrieve all the locations. - locations = await request.state.source.get_all() - - # Attempt to filter out locations with properties matching the provided query params. - for key, value in params.items(): - # Clean keys for security purposes. - key = key.lower() - value = value.lower().strip("__") - - # Do filtering. - try: - locations = [ - location - for location in locations - if str(getattr(location, key)).lower() == str(value) - ] - except AttributeError: - pass - if not locations: - raise HTTPException( - 404, detail=f"Source `{source}` does not have the desired location data.", - ) - - # Return final serialized data. - return { - "latest": { - "confirmed": sum(map(lambda location: location.confirmed, locations)), - "deaths": sum(map(lambda location: location.deaths, locations)), - "recovered": sum(map(lambda location: location.recovered, locations)), - }, - "locations": [location.serialize(timelines) for location in locations], - } - - -# pylint: disable=invalid-name -@V2.get("/locations/{id}", response_model=LocationResponse) -async def get_location_by_id( - request: Request, id: int, source: Sources = Sources.JHU, timelines: bool = True -): - """ - Getting specific location by id. - """ - location = await request.state.source.get(id) - return {"location": location.serialize(timelines)} - - -@V2.get("/sources") -async def sources(): - """ - Retrieves a list of data-sources that are availble to use. - """ - return {"sources": list(DATA_SOURCES.keys())} diff --git a/app/services/location/__init__.py b/app/services/location/__init__.py index 6d292b54..b22fb02b 100644 --- a/app/services/location/__init__.py +++ b/app/services/location/__init__.py @@ -2,27 +2,34 @@ from abc import ABC, abstractmethod -class LocationService(ABC): +class LocationServicer: # The 'Remote' of the Pattern """ - Service for retrieving locations. + Abstraction class handling the 'Front End' portion of the project """ - @abstractmethod - async def get_all(self): - """ - Gets and returns all of the locations. + def __init__(self, implementation): + self.implementation = implementation + + @abstractmethod + def all_locations(self): + return self.implementation.get_all() + + @abstractmethod + def locations(self): + return self.implementation.get() + +class LocationServicee: # The 'Device' with the implmentation + """ + Implementation Interface + """ + + def get_all(self): + # No implementation - interface method + pass + + def get(self): + # No implementation - interface method + pass + - :returns: The locations. - :rtype: List[Location] - """ - raise NotImplementedError - @abstractmethod - async def get(self, id): # pylint: disable=redefined-builtin,invalid-name - """ - Gets and returns location with the provided id. - - :returns: The location. - :rtype: Location - """ - raise NotImplementedError diff --git a/app/services/location/csbs.py b/app/services/location/csbs.py index 444ebad6..f931e95e 100644 --- a/app/services/location/csbs.py +++ b/app/services/location/csbs.py @@ -10,12 +10,12 @@ from ...coordinates import Coordinates from ...location.csbs import CSBSLocation from ...utils import httputils -from . import LocationService +from . import LocationServicee LOGGER = logging.getLogger("services.location.csbs") -class CSBSLocationService(LocationService): +class CSBSLocationServicee(LocationServicee): """ Service for retrieving locations from csbs """ @@ -30,7 +30,6 @@ async def get(self, loc_id): # pylint: disable=arguments-differ locations = await self.get_all() return locations[loc_id] - # Base URL for fetching data BASE_URL = "https://facts.csbs.org/covid-19/covid19_county.csv" diff --git a/app/services/location/jhu.py b/app/services/location/jhu.py index ebed3960..75ee37fd 100644 --- a/app/services/location/jhu.py +++ b/app/services/location/jhu.py @@ -15,13 +15,13 @@ from ...utils import countries from ...utils import date as date_util from ...utils import httputils -from . import LocationService +from . import LocationServicee LOGGER = logging.getLogger("services.location.jhu") PID = os.getpid() -class JhuLocationService(LocationService): +class JhuLocationServicee(LocationServicee): """ Service for retrieving locations from Johns Hopkins CSSE (https://github.com/CSSEGISandData/COVID-19). """ diff --git a/app/services/location/nyt.py b/app/services/location/nyt.py index 1f25ec34..5d4874a1 100644 --- a/app/services/location/nyt.py +++ b/app/services/location/nyt.py @@ -11,12 +11,12 @@ from ...location.nyt import NYTLocation from ...models import Timeline from ...utils import httputils -from . import LocationService +from . import LocationServicee LOGGER = logging.getLogger("services.location.nyt") -class NYTLocationService(LocationService): +class NYTLocationServicee(LocationServicee): """ Service for retrieving locations from New York Times (https://github.com/nytimes/covid-19-data). """