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

Issue/add forecast latest #86

Merged
merged 10 commits into from
Apr 22, 2022
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