Skip to content

Commit

Permalink
Merge pull request #86 from openclimatefix/issue/add-forecast-latest
Browse files Browse the repository at this point in the history
Issue/add forecast latest
  • Loading branch information
peterdudfield authored Apr 22, 2022
2 parents 2fc40cf + 795e8dd commit ed1a86f
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 7 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ uvicorn[standard]
pydantic
numpy
requests
nowcasting_datamodel==0.0.38
nowcasting_datamodel==0.0.39
nowcasting_dataset==3.1.59
sqlalchemy
psycopg2-binary
Expand Down
25 changes: 24 additions & 1 deletion src/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
from typing import List, Optional

from nowcasting_datamodel.connection import DatabaseConnection
from nowcasting_datamodel.models import Forecast, GSPYield, ManyForecasts
from nowcasting_datamodel.models import Forecast, ForecastValue, GSPYield, ManyForecasts
from nowcasting_datamodel.read.read import (
get_all_gsp_ids_latest_forecast,
get_forecast_values,
get_latest_forecast,
get_latest_national_forecast,
)
Expand Down Expand Up @@ -37,6 +38,28 @@ def get_forecasts_for_a_specific_gsp_from_database(session: Session, gsp_id) ->
return Forecast.from_orm(forecast)


def get_latest_forecast_values_for_a_specific_gsp_from_database(
session: Session, gsp_id
) -> List[ForecastValue]:
"""
Get the forecast values for yesterday and today for one gsp
:param session: sqlalchemy session
:param gsp_id: gsp id, 0 is national
:return: list of latest forecat values
"""

yesterday_start_datetime = datetime.now(tz=timezone.utc).date() - timedelta(days=1)
yesterday_start_datetime = datetime.combine(yesterday_start_datetime, datetime.min.time())

return get_forecast_values(
session=session,
gsp_id=gsp_id,
start_datetime=yesterday_start_datetime,
only_return_latest=True,
)


def get_session():
"""Get database settion"""
connection = DatabaseConnection(url=os.getenv("DB_URL", "not_set"))
Expand Down
18 changes: 16 additions & 2 deletions src/gsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

import geopandas as gpd
from fastapi import APIRouter, Depends
from nowcasting_datamodel.models import Forecast, GSPYield, ManyForecasts
from nowcasting_datamodel.models import Forecast, ForecastValue, GSPYield, ManyForecasts
from nowcasting_dataset.data_sources.gsp.eso import get_gsp_metadata_from_eso
from sqlalchemy.orm.session import Session

from database import (
get_forecasts_for_a_specific_gsp_from_database,
get_forecasts_from_database,
get_latest_forecast_values_for_a_specific_gsp_from_database,
get_latest_national_forecast_from_database,
get_session,
get_truth_values_for_a_specific_gsp_from_database,
Expand Down Expand Up @@ -40,7 +41,7 @@ def get_gsp_boundaries_from_eso_wgs84() -> gpd.GeoDataFrame:

@router.get("/forecast/one_gsp/{gsp_id}", response_model=Forecast)
async def get_forecasts_for_a_specific_gsp(
gsp_id, session: Session = Depends(get_session)
gsp_id: int, session: Session = Depends(get_session)
) -> Forecast:
"""Get one forecast for a specific GSP id"""

Expand All @@ -49,6 +50,19 @@ async def get_forecasts_for_a_specific_gsp(
return get_forecasts_for_a_specific_gsp_from_database(session=session, gsp_id=gsp_id)


@router.get("/forecast/latest/{gsp_id}", response_model=List[ForecastValue])
async def get_latest_forecasts_for_a_specific_gsp(
gsp_id: int, session: Session = Depends(get_session)
) -> List[ForecastValue]:
"""Get the latest forecasts for a specific GSP id for today and yesterday"""

logger.info(f"Get forecasts for gsp id {gsp_id}")

return get_latest_forecast_values_for_a_specific_gsp_from_database(
session=session, gsp_id=gsp_id
)


@router.get("/truth/one_gsp/{gsp_id}/", response_model=List[GSPYield])
async def get_truths_for_a_specific_gsp(
gsp_id: int, regime: Optional[str] = None, session: Session = Depends(get_session)
Expand Down
1 change: 1 addition & 0 deletions src/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def db_connection():
"""Pytest fixture for a database connection"""
with tempfile.NamedTemporaryFile(suffix="db") as tmp:
# set url option to not check same thread, this solves an error seen in testing

url = f"sqlite:///{tmp.name}.db?check_same_thread=False"
os.environ["DB_URL"] = url
os.environ["DB_URL_PV"] = url
Expand Down
60 changes: 58 additions & 2 deletions src/tests/test_gsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,19 @@

from fastapi.testclient import TestClient
from freezegun import freeze_time
from nowcasting_datamodel.fake import make_fake_forecasts, make_fake_national_forecast
from nowcasting_datamodel.models import Forecast, GSPYield, Location, LocationSQL, ManyForecasts
from nowcasting_datamodel.fake import (
make_fake_forecast,
make_fake_forecasts,
make_fake_national_forecast,
)
from nowcasting_datamodel.models import (
Forecast,
ForecastValue,
GSPYield,
Location,
LocationSQL,
ManyForecasts,
)

from database import get_session
from main import app
Expand Down Expand Up @@ -100,3 +111,48 @@ def test_read_truth_one_gsp(db_session):
r_json = response.json()
assert len(r_json) == 2
_ = [GSPYield(**gsp_yield) for gsp_yield in r_json]


@freeze_time("2022-06-01")
def test_read_forecast_one_gsp(db_session):
"""Check main GB/pv/gsp/{gsp_id} route works"""

forecast_value_1 = ForecastValue(
target_time=datetime(2022, 6, 2), expected_power_generation_megawatts=1
)
forecast_value_1_sql = forecast_value_1.to_orm()

forecast_value_2 = ForecastValue(
target_time=datetime(2022, 6, 1, 1), expected_power_generation_megawatts=2
)
forecast_value_2_sql = forecast_value_2.to_orm()

forecast_value_3 = ForecastValue(
target_time=datetime(2022, 6, 1), expected_power_generation_megawatts=3
)
forecast_value_3_sql = forecast_value_3.to_orm()

forecast = make_fake_forecast(
gsp_id=1, session=db_session, t0_datetime_utc=datetime(2020, 1, 1)
)
forecast.forecast_values.append(forecast_value_1_sql)
forecast.forecast_values.append(forecast_value_2_sql)
forecast.forecast_values.append(forecast_value_3_sql)

# add to database
db_session.add_all([forecast])

app.dependency_overrides[get_session] = lambda: db_session

response = client.get("/v0/GB/solar/gsp/forecast/latest/1")
assert response.status_code == 200

r_json = response.json()
# no forecast are from 'make_fake_forecast', as these are for 2020
# the two are 'forecast_value_1' and 'forecast_value_3'
# 'forecast_value_2' is not included as it has the same target time as
# 'forecast_value_3'
for i in r_json:
print(i)
assert len(r_json) == 3
_ = [ForecastValue(**forecast_value) for forecast_value in r_json]
3 changes: 2 additions & 1 deletion test-docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3"

services:
postgres:
image: postgres:13.4
image: postgres:14.2
restart: always
environment:
- POSTGRES_USER=postgres
Expand All @@ -18,6 +18,7 @@ services:
TESTING: 1
environment:
- DB_URL=postgresql://postgres:postgres@postgres:5432/postgres
- DB_URL_PV=postgresql://postgres:postgres@postgres:5432/postgres
- GIT_PYTHON_REFRESH=quiet
- LOG_LEVEL=DEBUG
command: >
Expand Down

0 comments on commit ed1a86f

Please sign in to comment.