From d68fcf1ca946afe464214bc1b3f95f38dad91752 Mon Sep 17 00:00:00 2001 From: braddf Date: Wed, 17 Jul 2024 17:26:25 +0100 Subject: [PATCH 1/3] Add dummy db class; update README --- README.md | 9 ++++++-- requirements.txt | 2 +- src/database.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++- src/national.py | 25 ++++++++++++++++++++ 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 99205f3c..e2312b73 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,12 @@ cd src && uvicorn main:app --reload You may need to run the following additional installation `pip install git+https://github.com/SheffieldSolar/PV_Live-API#pvlive_api` for `pvlive-api`, as in the Dockerfile. -## Docker +> If you don't have a local database set up, you can leave the `DB_URL` string empty (default not set) and the API will still run and return routes such as `http://localhost:8000/` for API info and any other non-DB routes with DB dependencies e.g. session/caching commented out. +> +> You will not be able to access any routes using the DB client / database, but for local development of new routes this should work for now, until we reinstate dynamic fake data as a dependable dev tool. + +## [ Docker ] +### 🛑 Currently non-functional, needs updating to migrate database to match datamodel 1. Make sure docker is installed on your system. 2. Use `docker-compose up` @@ -62,7 +67,7 @@ as Actions when code is pushed to GitHub. You can run the formatters and linters locally. To do that: 1. [Install pre-commit](https://pre-commit.com/#install) -2. Check the install worked via `pre-commit --version` +2. Check the install worked via `pre-commit --v` 3. Install the git hooks script via `pre-commit install` # Deployment diff --git a/requirements.txt b/requirements.txt index b5c23b32..55aafc53 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ opencv-contrib-python-headless fastapi uvicorn[standard] pydantic -numpy +numpy==1.26.4 requests nowcasting_datamodel==1.5.52 sqlalchemy diff --git a/src/database.py b/src/database.py index a02cb1c4..59cb3b9f 100644 --- a/src/database.py +++ b/src/database.py @@ -1,5 +1,6 @@ """ Functions to read from the database and format """ +import abc import os from datetime import datetime, timedelta, timezone from typing import List, Optional, Union @@ -44,7 +45,64 @@ ) from utils import filter_forecast_values, floor_30_minutes_dt, get_start_datetime -db_conn = DatabaseConnection(url=os.getenv("DB_URL", "not_set")) + +class BaseDBClient(abc.ABC): + """This is a base class for database clients with one static method get_client(). + + Methods + ------- + get_client : static method + It gets the database client. If a valid Postgresql database URL is set in + the "DB_URL" environment variable, get_client returns an instance + of DatabaseConnection(). If not, it returns an instance of the DummyDBClient. + """ + + @staticmethod + def get_client(): + """ + Get the database client. + """ + db_url = os.getenv("DB_URL") + if db_url and db_url.find("postgresql") != -1: + return DatabaseConnection(url=db_url) + else: + return DummyDBClient() + + +class DummyDBClient(BaseDBClient): + """The DummyDBClient serves as a placeholder database client + + This might be useful when a valid Postgresql + database connection is not available. in testing or development environments. + Feel free to improve on this implementation and submit a pull request! + A better example can be found in the [india-api]( + https://github.com/openclimatefix/india-api/blob/f4e3b776194290d78e1c9702b792cbb88edf4b90/src/india_api/cmd/main.py#L12 + ) repository. + + It inherits from the BaseDBClient class and implements the methods accordingly. + + Methods + ---------- + get_session: + Returns None for now, but should mock the session if possible, + if we keep the current DB/datamodel implementation. + """ + + def __init__(self): + """Initializes the DummyDBClient""" + pass + + def get_session(self): + """Returns None for now, but mock the session if we keep the current implementation.""" + return None + + +def get_db_client() -> BaseDBClient: + """Return either the datamodel client or a dummy client""" + return BaseDBClient.get_client() + + +db_conn = get_db_client() logger = structlog.stdlib.get_logger() diff --git a/src/national.py b/src/national.py index 09457c6f..b73da1fd 100644 --- a/src/national.py +++ b/src/national.py @@ -189,3 +189,28 @@ def get_national_pvlive( return get_truth_values_for_a_specific_gsp_from_database( session=session, gsp_id=0, regime=regime ) + + +@router.get( + "/bmrs", + response_model=dict, + # dependencies=[Depends(get_auth_implicit_scheme())], + summary="Get BMRS Forecast", +) +# @cache_response +@limiter.limit(f"{N_CALLS_PER_HOUR}/hour") +def get_bmrs_forecast( + request: Request, + # session: Session = Depends(get_session), + # user: Auth0User = Security(get_user()), +) -> dict: + """ + + This route returns the most recent BMRS forecast for each _target_time_. + + #### Parameters + + """ + logger.debug("Get bmrs forecast") + + return {"message": "This route is not yet implemented. Please check back later."} From 8fbaa78df64b5ab14931c494efdd535f1fe18193 Mon Sep 17 00:00:00 2001 From: peterdudfield Date: Wed, 17 Jul 2024 17:48:03 +0100 Subject: [PATCH 2/3] change back to numpy==2.0.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 55aafc53..b5c23b32 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ opencv-contrib-python-headless fastapi uvicorn[standard] pydantic -numpy==1.26.4 +numpy requests nowcasting_datamodel==1.5.52 sqlalchemy From 5cc0dec4395a480546d20d6bee98f6c7cada806d Mon Sep 17 00:00:00 2001 From: braddf Date: Wed, 17 Jul 2024 22:21:32 +0100 Subject: [PATCH 3/3] Update client names to connection --- src/database.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/database.py b/src/database.py index 59cb3b9f..472810ba 100644 --- a/src/database.py +++ b/src/database.py @@ -46,31 +46,31 @@ from utils import filter_forecast_values, floor_30_minutes_dt, get_start_datetime -class BaseDBClient(abc.ABC): - """This is a base class for database clients with one static method get_client(). +class BaseDBConnection(abc.ABC): + """This is a base class for database connections with one static method get_connection(). Methods ------- - get_client : static method - It gets the database client. If a valid Postgresql database URL is set in - the "DB_URL" environment variable, get_client returns an instance - of DatabaseConnection(). If not, it returns an instance of the DummyDBClient. + get_connection : static method + It gets the database connection. If a valid Postgresql database URL is set in + the "DB_URL" environment variable, get_connection returns an instance + of DatabaseConnection(). If not, it returns an instance of the DummyDBConnection. """ @staticmethod - def get_client(): + def get_connection(): """ - Get the database client. + Get the database connection. """ db_url = os.getenv("DB_URL") if db_url and db_url.find("postgresql") != -1: return DatabaseConnection(url=db_url) else: - return DummyDBClient() + return DummyDBConnection() -class DummyDBClient(BaseDBClient): - """The DummyDBClient serves as a placeholder database client +class DummyDBConnection(BaseDBConnection): + """The DummyDBConnection serves as a placeholder database connection This might be useful when a valid Postgresql database connection is not available. in testing or development environments. @@ -79,7 +79,7 @@ class DummyDBClient(BaseDBClient): https://github.com/openclimatefix/india-api/blob/f4e3b776194290d78e1c9702b792cbb88edf4b90/src/india_api/cmd/main.py#L12 ) repository. - It inherits from the BaseDBClient class and implements the methods accordingly. + It inherits from the BaseDBConnection class and implements the methods accordingly. Methods ---------- @@ -89,7 +89,7 @@ class DummyDBClient(BaseDBClient): """ def __init__(self): - """Initializes the DummyDBClient""" + """Initializes the DummyDBConnection""" pass def get_session(self): @@ -97,12 +97,12 @@ def get_session(self): return None -def get_db_client() -> BaseDBClient: - """Return either the datamodel client or a dummy client""" - return BaseDBClient.get_client() +def get_db_connection() -> BaseDBConnection: + """Return either the datamodel connection or a dummy connection""" + return BaseDBConnection.get_connection() -db_conn = get_db_client() +db_conn = get_db_connection() logger = structlog.stdlib.get_logger()