88
99import json
1010from enum import Enum , auto
11- from typing import TYPE_CHECKING , Any , Dict , FrozenSet , List , Optional
11+ from typing import TYPE_CHECKING , Any , Dict , FrozenSet , List
1212
1313import numpy as np
1414import pandas as pd
@@ -120,6 +120,14 @@ def __repr__(self) -> str:
120120
121121class Parameter (Specifiable ):
122122 """Used to specify parameters for disease modules etc."""
123+ def __init__ (self ,
124+ type_ : Types ,
125+ description : str ,
126+ categories : List [str ] = None ,
127+ * ,
128+ metadata : Optional [Dict [str , Any ]] = None ):
129+ super ().__init__ (type_ , description , categories )
130+ self .metadata = metadata or {}
123131
124132
125133class Property (Specifiable ):
@@ -321,27 +329,41 @@ def load_parameters_from_dataframe(self, resource: pd.DataFrame) -> None:
321329
322330 :param DataFrame resource: DataFrame with a column of the parameter_name and a column of `value`
323331 """
332+
324333 resource .set_index ('parameter_name' , inplace = True )
325334 skipped_data_types = ('DATA_FRAME' , 'SERIES' )
335+ acceptable_labels = ['unassigned' , 'undetermined' , 'universal' , 'local' , 'scenario' ]
336+ param_defaults = {'param_label' : 'unassigned' , 'prior_min' : None , 'prior_max' : None }
337+
338+ for _col in param_defaults .keys ():
339+ if _col not in resource .columns :
340+ resource [_col ] = param_defaults [_col ]
326341 # for each supported parameter, convert to the correct type
327342 for parameter_name in resource .index [resource .index .notnull ()]:
328343 parameter_definition = self .PARAMETERS [parameter_name ]
329-
330344 if parameter_definition .type_ .name in skipped_data_types :
331345 continue
332346
333347 # For each parameter, raise error if the value can't be coerced
334- parameter_value = resource .at [parameter_name , 'value' ]
348+ parameter_value , prior_min , prior_max = resource .loc [parameter_name , ['value' , 'prior_min' , 'prior_max' ]]
349+ parameter_label = resource .at [parameter_name , 'param_label' ]
350+ assert parameter_label in acceptable_labels , f'unrecognised parameter label { parameter_label } '
351+
335352 error_message = (
336- f"The value of '{ parameter_value } ' for parameter '{ parameter_name } ' "
337- f"could not be parsed as a { parameter_definition .type_ .name } data type"
353+ f"some values are not of type { parameter_definition .type_ .name } and "
354+ f"could not be parsed as a { parameter_definition .type_ .name } data type. "
355+ f"parameter name is { parameter_name } , values { [parameter_value , prior_min , prior_max ]} "
338356 )
339357 if parameter_definition .python_type is list :
340358 try :
341359 # chose json.loads instead of save_eval
342360 # because it raises error instead of joining two strings without a comma
343361 parameter_value = json .loads (parameter_value )
344362 assert isinstance (parameter_value , list )
363+ if pd .notnull (prior_min ):
364+ assert isinstance (json .loads (prior_min ), list )
365+ if pd .notnull (prior_max ):
366+ assert isinstance (json .loads (prior_max ), list )
345367 except (json .decoder .JSONDecodeError , TypeError , AssertionError ) as exception :
346368 raise ValueError (error_message ) from exception
347369 elif parameter_definition .python_type == pd .Categorical :
@@ -358,11 +380,22 @@ def load_parameters_from_dataframe(self, resource: pd.DataFrame) -> None:
358380 # All other data types, assign to the python_type defined in Parameter class
359381 try :
360382 parameter_value = parameter_definition .python_type (parameter_value )
383+ if not isinstance (parameter_definition .python_type , pd .Timestamp ):
384+ if pd .notnull (prior_min ):
385+ parameter_definition .python_type (prior_min )
386+ if pd .notnull (prior_max ):
387+ parameter_definition .python_type (prior_max )
361388 except Exception as exception :
362389 raise ValueError (error_message ) from exception
363390
364391 # Save the values to the parameters
365392 self .parameters [parameter_name ] = parameter_value
393+ # Assign metadata to the Parameter object
394+ parameter_definition .metadata .update (
395+ param_label = parameter_label ,
396+ prior_min = prior_min ,
397+ prior_max = prior_max
398+ )
366399
367400 def read_parameters (self , data_folder : str | Path ) -> None :
368401 """Read parameter values from file, if required.
0 commit comments