Skip to content

Commit 67c6ecd

Browse files
Merge branch 'main' into feature/redoc-documentation
2 parents 1d3f1d5 + b6a5175 commit 67c6ecd

File tree

5 files changed

+113
-15
lines changed

5 files changed

+113
-15
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[bumpversion]
22
commit = True
33
tag = True
4-
current_version = 0.1.53
4+
current_version = 0.1.54
55
message = Bump version: {current_version} → {new_version}
66

77
[bumpversion:file:src/india_api/internal/service/server.py]
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import logging
2+
3+
import pandas as pd
4+
from fastapi import HTTPException
5+
import pytest
6+
7+
from india_api.internal import PredictedPower, ActualPower, SiteProperties
8+
9+
from pvsite_datamodel.sqlmodels import APIRequestSQL
10+
11+
from .client import Client
12+
from .conftest import forecast_values
13+
from ...models import ForecastHorizon
14+
from ...service.csv import format_csv_and_created_time
15+
16+
log = logging.getLogger(__name__)
17+
18+
# TODO add list of test that are here
19+
20+
21+
@pytest.fixture()
22+
def client(engine, db_session):
23+
"""Hooks Client into pytest db_session fixture"""
24+
client = Client(database_url=str(engine.url))
25+
client.session = db_session
26+
27+
return client
28+
29+
# Skip for now
30+
@pytest.mark.skip(reason="Not finished yet")
31+
class TestCsvExport:
32+
def test_format_csv_and_created_time(self, client, forecast_values_wind) -> None:
33+
"""Test the format_csv_and_created_time function."""
34+
forecast_values_wind = client.get_predicted_wind_power_production_for_location(
35+
location="testID"
36+
)
37+
assert forecast_values_wind is not None
38+
assert len(forecast_values_wind) > 0
39+
assert isinstance(forecast_values_wind[0], PredictedPower)
40+
41+
result = format_csv_and_created_time(
42+
forecast_values_wind,
43+
ForecastHorizon.latest,
44+
)
45+
assert isinstance(result, tuple)
46+
assert isinstance(result[0], pd.DataFrame)
47+
assert isinstance(result[1], pd.Timestamp)
48+
logging.info(f"CSV created at: {result[1]}")
49+
logging.info(f"CSV content: {result[0].head()}")
50+
# Check the shape of the DataFrame
51+
# The shape should match the number of forecast values
52+
# and the number of columns in the DataFrame
53+
# The DataFrame should have 3 columns: Date [IST], Time, PowerMW
54+
assert result[0].shape[1] == 3
55+
# Check the first row of the DataFrame
56+
# The date of the first row should be the nearest rounded 15min from now
57+
rounded_15_min = pd.Timestamp.now(tz="Asia/Kolkata").round("15min")
58+
assert result[0].iloc[0]["Time"] == rounded_15_min.strftime("%H:%M")
59+
# Check the number of rows in the DataFrame
60+
# For the latest forecast, it should be the number of
61+
# forecast values after now
62+
forecast_values_from_now = [
63+
value for value in forecast_values_wind if value.Time >= rounded_15_min
64+
]
65+
assert result[0].shape[0] == len(forecast_values_from_now)
66+
# Check the column names
67+
assert list(result[0].columns) == ["Date [IST]", "Time", "PowerMW"]
68+
# Check the data types of the columns
69+
assert result[0]["Date [IST]"].dtype == "datetime64[ns, Asia/Kolkata]"
70+
assert result[0]["Time"].dtype == "object"
71+
assert result[0]["PowerMW"].dtype == "float64"

src/india_api/internal/service/csv.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
from datetime import datetime
33

44
from india_api.internal import PredictedPower
5+
from india_api.internal.models import ForecastHorizon
56

67

7-
def format_csv_and_created_time(values: list[PredictedPower]) -> (pd.DataFrame, datetime):
8+
def format_csv_and_created_time(values: list[PredictedPower], forecast_horizon: ForecastHorizon) -> (pd.DataFrame, datetime):
89
"""
910
Format the predicted power values into a pandas dataframe ready for CSV export.
1011
@@ -26,16 +27,20 @@ def format_csv_and_created_time(values: list[PredictedPower]) -> (pd.DataFrame,
2627
df["Date [IST]"] = df["Time"].dt.date
2728
# create start and end time column and only show HH:MM
2829
df["Start Time [IST]"] = df["Time"].dt.strftime("%H:%M")
29-
df["End Time [IST]"] = (df["Time"] + pd.to_timedelta("15T")).dt.strftime("%H:%M")
30+
df["End Time [IST]"] = (df["Time"] + pd.to_timedelta("15min")).dt.strftime("%H:%M")
31+
32+
now_ist = pd.Timestamp.now(tz="Asia/Kolkata")
33+
if forecast_horizon == ForecastHorizon.day_ahead:
34+
# only get tomorrow's results, for IST time.
35+
tomorrow = now_ist + pd.Timedelta(days=1)
36+
df = df[df["Date [IST]"] == tomorrow.date()]
37+
elif forecast_horizon == ForecastHorizon.latest:
38+
# only get results from now onwards, for IST time.
39+
df = df[df["Time"] >= now_ist]
3040

3141
# combine start and end times
3242
df["Time"] = df["Start Time [IST]"].astype(str) + " - " + df["End Time [IST]"].astype(str)
3343

34-
# only get tomorrows results. This is for IST time.
35-
now_ist = pd.Timestamp.now(tz="Asia/Kolkata")
36-
tomorrow = now_ist + pd.Timedelta(days=1)
37-
df = df[df["Date [IST]"] == tomorrow.date()]
38-
3944
# get the max created time
4045
created_time = df["CreatedTime"].max()
4146

src/india_api/internal/service/regions.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,35 +186,57 @@ def get_forecast_timeseries_route(
186186
response_class=FileResponse,
187187
include_in_schema=False,
188188
)
189-
def get_forecast_da_csv(
189+
def get_forecast_csv(
190190
source: ValidSourceDependency,
191191
region: str,
192192
db: DBClientDependency,
193193
auth: dict = Depends(auth),
194+
forecast_horizon: Optional[ForecastHorizon] = ForecastHorizon.latest,
194195
):
195196
"""
196197
Route to get the day ahead forecast as a CSV file.
198+
By default, the CSV file will be for the latest forecast, from now forwards.
199+
The forecast_horizon can be set to 'latest' or 'day_ahead'.
200+
- latest: The latest forecast, from now forwards.
201+
- day_ahead: The forecast for the next day, from 00:00.
197202
"""
198203

199-
forcasts: GetForecastGenerationResponse = get_forecast_timeseries_route(
204+
if forecast_horizon is not None:
205+
if forecast_horizon not in [ForecastHorizon.latest, ForecastHorizon.day_ahead]:
206+
raise HTTPException(
207+
status_code=status.HTTP_400_BAD_REQUEST,
208+
detail=f"Invalid forecast_horizon {forecast_horizon}. Must be 'latest' or 'day_ahead'.",
209+
)
210+
211+
forecasts: GetForecastGenerationResponse = get_forecast_timeseries_route(
200212
source=source,
201213
region=region,
202214
db=db,
203215
auth=auth,
204-
forecast_horizon=ForecastHorizon.day_ahead,
216+
forecast_horizon=forecast_horizon,
205217
smooth_flag=False,
206218
)
207219

208220
# format to dataframe
209-
df, created_time = format_csv_and_created_time(forcasts.values)
221+
df, created_time = format_csv_and_created_time(forecasts.values, forecast_horizon=forecast_horizon)
210222

211223
# make file format
212224
now_ist = pd.Timestamp.now(tz="Asia/Kolkata")
213225
tomorrow_ist = df["Date [IST]"].iloc[0]
214-
csv_file_path = f"{region}_{source}_da_{tomorrow_ist}.csv"
226+
match forecast_horizon:
227+
case ForecastHorizon.latest:
228+
forecast_type = "intraday"
229+
case ForecastHorizon.day_ahead:
230+
forecast_type = "da"
231+
case _:
232+
raise HTTPException(
233+
status_code=status.HTTP_400_BAD_REQUEST,
234+
detail=f"Invalid forecast_horizon {forecast_horizon}. Must be 'latest' or 'day_ahead'.",
235+
)
236+
csv_file_path = f"{region}_{source}_{forecast_type}_{tomorrow_ist}.csv"
215237

216238
description = (
217-
f"Forecast for {region} for {source} for {tomorrow_ist}. "
239+
f"Forecast for {region} for {source}, {forecast_type}, for {tomorrow_ist}. "
218240
f"The Forecast was created at {created_time} and downloaded at {now_ist}"
219241
)
220242

src/india_api/internal/service/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
2020
log = logging.getLogger(__name__)
2121
folder = os.path.dirname(os.path.abspath(__file__))
22-
version = "0.1.53"
22+
version = "0.1.54"
2323

2424

2525
tags_metadata = [

0 commit comments

Comments
 (0)