@@ -117,17 +117,23 @@ def _refine_data(options, data1):
117
117
data1 .sort_index (inplace = True )
118
118
return {"data" : data1 , "refine_logs" : refine_logs }
119
119
120
+ def _convert_local_to_utc (dte ):
121
+ # datetime obj is converted from local time zone to utc
122
+ local_timezone = datetime .now ().astimezone ().tzinfo
123
+ return pd .Timestamp (dte ,tz = local_timezone ).tz_convert ('UTC' )
120
124
121
125
def _entsoe_get_actual_generation (options = {"country" : "" , "start" : "" , "end" : "" }):
122
126
"""Fetches the aggregated actual generation per production type data (16.1.B&C) for the given country within the given start and end date
123
127
params: options = {country (2 letter country code),start,end} . Both the dates are in the YYYYMMDDhhmm format and the local time zone
124
128
returns : {"data":pd.DataFrame, "duration":duration (in min) of the time series data, "refine_logs":"notes on refinements made" }
125
129
"""
130
+ utc_start = _convert_local_to_utc (options ["start" ])
131
+ utc_end = _convert_local_to_utc (options ["end" ])
126
132
client1 = entsoePandas (api_key = _get_API_token ())
127
133
data1 = client1 .query_generation (
128
134
options ["country" ],
129
- start = pd . Timestamp ( options [ "start" ], tz = "UTC" ) ,
130
- end = pd . Timestamp ( options [ "end" ], tz = "UTC" ) ,
135
+ start = utc_start ,
136
+ end = utc_end ,
131
137
psr_type = None ,
132
138
)
133
139
# drop columns with actual consumption values (we want actual aggregated generation values)
@@ -159,8 +165,8 @@ def _entsoe_get_total_forecast(options={"country": "", "start": "", "end": ""}):
159
165
client = entsoePandas (api_key = _get_API_token ())
160
166
data = client .query_generation_forecast (
161
167
options ["country" ],
162
- start = pd . Timestamp (options ["start" ], tz = "UTC" ) ,
163
- end = pd . Timestamp (options ["end" ], tz = "UTC" ),
168
+ start = _convert_local_to_utc (options ["start" ]) ,
169
+ end = _convert_local_to_utc (options ["end" ])
164
170
)
165
171
# if the data is a series instead of a dataframe, it will be converted to a dataframe
166
172
if isinstance (data , pd .Series ):
@@ -188,8 +194,8 @@ def _entsoe_get_wind_solar_forecast(options={"country": "", "start": "", "end":
188
194
client = entsoePandas (api_key = _get_API_token ())
189
195
data = client .query_wind_and_solar_forecast (
190
196
options ["country" ],
191
- start = pd . Timestamp (options ["start" ], tz = "UTC" ) ,
192
- end = pd . Timestamp (options ["end" ], tz = "UTC" ),
197
+ start = _convert_local_to_utc (options ["start" ]) ,
198
+ end = _convert_local_to_utc (options ["end" ])
193
199
)
194
200
durationMin = (data .index [1 ] - data .index [0 ]).total_seconds () / 60
195
201
# refining the data
@@ -246,6 +252,7 @@ def _convert_to_60min_interval(rawData):
246
252
247
253
248
254
def _convert_date_to_entsoe_format (dt : datetime ):
255
+ """ rounds the date to nearest hour """
249
256
return dt .replace (minute = 0 , second = 0 , microsecond = 0 ).strftime ("%Y%m%d%H%M" )
250
257
251
258
@@ -260,6 +267,7 @@ def get_actual_production_percentage(country, start, end, interval60=False) -> d
260
267
:param str country: The 2 alphabet country code.
261
268
:param datetime start: The start date for data retrieval. A Datetime object. Note that this date will be rounded to the nearest hour.
262
269
:param datetime end: The end date for data retrieval. A datetime object. This date is also rounded to the nearest hour.
270
+ :param boolean interval60: To convert the data into 60 min time interval. False by default
263
271
:return: A DataFrame containing the hourly energy production mix and percentage of energy generated from renewable and non renewable sources.
264
272
:return: A dictionary containing:
265
273
- `error`: A string with an error message, empty if no errors.
@@ -269,6 +277,25 @@ def get_actual_production_percentage(country, start, end, interval60=False) -> d
269
277
:rtype: dict
270
278
"""
271
279
try :
280
+ if not isinstance (country , str ):
281
+ raise ValueError ("Invalid country" )
282
+ if not isinstance (start , datetime ):
283
+ raise ValueError ("Invalid start date" )
284
+ if not isinstance (end , datetime ):
285
+ raise ValueError ("Invalid end date" )
286
+
287
+ if start > datetime .now ():
288
+ raise ValueError ("Invalid start date. Generation data is only available for the past and not the future. Use the forecast API instead" )
289
+
290
+ if start > end :
291
+ raise ValueError ("Invalid date range. End date must be greater than the start date" )
292
+
293
+ # if end date is in the future and the start date is in the past , only data till the available moment will be returned.
294
+ if end > datetime .now ():
295
+ raise ValueError ("Invalid end date. Generation data is only available for the past and not the future. Use the forecast API instead" )
296
+ # this is not allowed because the entsoe-py returns error if it's greater than the present
297
+ #warnings.warn("End date is in the future. Will fetch data only till the present")
298
+
272
299
options = {
273
300
"country" : country ,
274
301
"start" : start ,
@@ -332,12 +359,12 @@ def get_actual_production_percentage(country, start, end, interval60=False) -> d
332
359
"time_interval" : duration ,
333
360
}
334
361
except Exception as e :
335
- print (e )
362
+ # print(e)
336
363
print (traceback .format_exc ())
337
364
return {
338
365
"data" : None ,
339
366
"data_available" : False ,
340
- "error" : Exception ,
367
+ "error" : e ,
341
368
"time_interval" : 0 ,
342
369
}
343
370
@@ -364,6 +391,13 @@ def get_forecast_percent_renewable(
364
391
"""
365
392
try :
366
393
# print(country,start,end)
394
+ if not isinstance (country , str ):
395
+ raise ValueError ("Invalid country" )
396
+ if not isinstance (start , datetime ):
397
+ raise ValueError ("Invalid start date" )
398
+ if not isinstance (end , datetime ):
399
+ raise ValueError ("Invalid end date" )
400
+
367
401
start = _convert_date_to_entsoe_format (start )
368
402
end = _convert_date_to_entsoe_format (end )
369
403
options = {"country" : country , "start" : start , "end" : end }
0 commit comments