@@ -108,7 +108,8 @@ class Forecast:
108108
109109 def __init__ (self , frequency , api_data , convert_weather_code ):
110110 """
111- :param frequency: Frequency of forecast: 'hourly', 'three-hourly' or 'daily'
111+ :param frequency: Frequency of forecast: 'hourly', 'three-hourly',
112+ 'twice-daily', 'daily'
112113 :param api_data: Data returned from API call
113114 :param: convert_weather_code: Convert numeric weather codes to string description
114115 :type frequency: string
@@ -149,14 +150,14 @@ def __init__(self, frequency, api_data, convert_weather_code):
149150
150151 forecasts = api_data ["features" ][0 ]["properties" ]["timeSeries" ]
151152 parameters = api_data ["parameters" ][0 ]
152- if frequency == "daily" :
153- self .timesteps = self ._build_timesteps_from_daily (forecasts , parameters )
153+ if frequency == "twice- daily" :
154+ self .timesteps = self ._build_twice_daily_timesteps (forecasts , parameters )
154155 else :
155156 self .timesteps = []
156157 for forecast in forecasts :
157158 self .timesteps .append (self ._build_timestep (forecast , parameters ))
158159
159- def _build_timesteps_from_daily (self , forecasts , parameters ):
160+ def _build_twice_daily_timesteps (self , forecasts , parameters ):
160161 """Build individual timesteps from forecasts and metadata
161162
162163 Take the forecast data from DataHub and combine with unit information
@@ -188,38 +189,25 @@ def _build_timesteps_from_daily(self, forecasts, parameters):
188189
189190 for element , value in forecast .items ():
190191 if element .startswith ("midday" ):
191- trimmed_element = element .replace ("midday" , "" )
192- case_corrected_element = (
193- trimmed_element [0 ].lower () + trimmed_element [1 :]
194- )
195- day_step [case_corrected_element ] = {
192+ day_step [element ] = {
196193 "value" : value ,
197194 "description" : parameters [element ]["description" ],
198195 "unit_name" : parameters [element ]["unit" ]["label" ],
199196 "unit_symbol" : parameters [element ]["unit" ]["symbol" ]["type" ],
200197 }
201198 elif element .startswith ("midnight" ):
202- trimmed_element = element .replace ("midnight" , "" )
203- case_corrected_element = (
204- trimmed_element [0 ].lower () + trimmed_element [1 :]
205- )
206- night_step [case_corrected_element ] = {
199+ night_step [element ] = {
207200 "value" : value ,
208201 "description" : parameters [element ]["description" ],
209202 "unit_name" : parameters [element ]["unit" ]["label" ],
210203 "unit_symbol" : parameters [element ]["unit" ]["symbol" ]["type" ],
211204 }
212205 elif element .startswith ("day" ):
213- trimmed_element = element .replace ("day" , "" )
214- case_corrected_element = (
215- trimmed_element [0 ].lower () + trimmed_element [1 :]
216- )
217-
218206 if (
219- case_corrected_element == "significantWeatherCode "
207+ element == "daySignificantWeatherCode "
220208 and self .convert_weather_code
221209 ):
222- day_step [case_corrected_element ] = {
210+ day_step [element ] = {
223211 "value" : WEATHER_CODES [str (value )],
224212 "description" : parameters [element ]["description" ],
225213 "unit_name" : parameters [element ]["unit" ]["label" ],
@@ -229,7 +217,7 @@ def _build_timesteps_from_daily(self, forecasts, parameters):
229217 }
230218
231219 else :
232- day_step [case_corrected_element ] = {
220+ day_step [element ] = {
233221 "value" : value ,
234222 "description" : parameters [element ]["description" ],
235223 "unit_name" : parameters [element ]["unit" ]["label" ],
@@ -238,16 +226,11 @@ def _build_timesteps_from_daily(self, forecasts, parameters):
238226 ],
239227 }
240228 elif element .startswith ("night" ):
241- trimmed_element = element .replace ("night" , "" )
242- case_corrected_element = (
243- trimmed_element [0 ].lower () + trimmed_element [1 :]
244- )
245-
246229 if (
247- case_corrected_element == "significantWeatherCode "
230+ element == "nightSignificantWeatherCode "
248231 and self .convert_weather_code
249232 ):
250- night_step [case_corrected_element ] = {
233+ night_step [element ] = {
251234 "value" : WEATHER_CODES [str (value )],
252235 "description" : parameters [element ]["description" ],
253236 "unit_name" : parameters [element ]["unit" ]["label" ],
@@ -257,7 +240,7 @@ def _build_timesteps_from_daily(self, forecasts, parameters):
257240 }
258241
259242 else :
260- night_step [case_corrected_element ] = {
243+ night_step [element ] = {
261244 "value" : value ,
262245 "description" : parameters [element ]["description" ],
263246 "unit_name" : parameters [element ]["unit" ]["label" ],
@@ -305,7 +288,14 @@ def _build_timestep(self, forecast, parameters):
305288 forecast ["time" ], "%Y-%m-%dT%H:%M%z"
306289 )
307290
308- elif element == "significantWeatherCode" and self .convert_weather_code :
291+ elif (
292+ element
293+ in (
294+ "significantWeatherCode" ,
295+ "daySignificantWeatherCode" ,
296+ "nightSignificantWeatherCode" ,
297+ )
298+ ) and self .convert_weather_code :
309299 timestep [element ] = {
310300 "value" : WEATHER_CODES [str (value )],
311301 "description" : parameters [element ]["description" ],
@@ -366,6 +356,19 @@ def _check_requested_time(self, target):
366356
367357 raise APIException (err_str )
368358
359+ # If we have a twice-daily forecast, check that the requested time is
360+ # at most 6 hours before the first datetime we have a forecast for.
361+ if self .frequency == "twice-daily" and target < self .timesteps [0 ][
362+ "time"
363+ ] - datetime .timedelta (hours = 6 ):
364+ err_str = (
365+ "There is no forecast available for the requested time. "
366+ "The requested time is more than 6 hours before the first "
367+ "available forecast."
368+ )
369+
370+ raise APIException (err_str )
371+
369372 # If we have an hourly forecast, check that the requested time is at
370373 # most 30 minutes after the final datetime we have a forecast for
371374 if self .frequency == "hourly" and target > (
@@ -405,6 +408,19 @@ def _check_requested_time(self, target):
405408
406409 raise APIException (err_str )
407410
411+ # If we have a twice-daily forecast, then the target must be within 6 hours
412+ # of the last timestep
413+ if self .frequency == "twice-daily" and target > (
414+ self .timesteps [- 1 ]["time" ] + datetime .timedelta (hours = 6 )
415+ ):
416+ err_str = (
417+ "There is no forecast available for the requested time. The "
418+ "requested time is more than 6 hours after the first available "
419+ "forecast."
420+ )
421+
422+ raise APIException (err_str )
423+
408424 def at_datetime (self , target ):
409425 """Return the timestep closest to the target datetime
410426
0 commit comments