@@ -93,13 +93,19 @@ def to_dict(self):
9393 }
9494
9595 @beartype .beartype
96- def update_from_dictionary (self , dictionary : dict , lower : bool = False ):
96+ def update_from_dictionary (self , dictionary : dict , lower : bool = True ):
9797 """
9898 Update the config dictionary from a provided dict
9999
100100 Args:
101101 dictionary (dict): The dictionary to update from
102102 """
103+ # make sure dictionary doesn't contain legacy keys
104+ self .check_for_legacy_keys (dictionary )
105+
106+ # make sure it has the minimum requirements
107+ self .validate_config_dictionary (dictionary )
108+
103109 if "structure" in dictionary :
104110 self .structure_config .update (dictionary ["structure" ])
105111 for key in dictionary ["structure" ].keys ():
@@ -108,6 +114,7 @@ def update_from_dictionary(self, dictionary: dict, lower: bool = False):
108114 f"Config dictionary structure segment contained { key } which is not used"
109115 )
110116 dictionary .pop ("structure" )
117+
111118 if "geology" in dictionary :
112119 self .geology_config .update (dictionary ["geology" ])
113120 for key in dictionary ["geology" ].keys ():
@@ -135,82 +142,19 @@ def update_from_dictionary(self, dictionary: dict, lower: bool = False):
135142 if len (dictionary ):
136143 logger .warning (f"Unused keys from config format { list (dictionary .keys ())} " )
137144
138- @beartype .beartype
139- def update_from_legacy_file (self , file_map : dict , lower : bool = False ):
140- """
141- Update the config dictionary from the provided old version dictionary
142-
143- Args:
144- file_map (dict): The old version dictionary to update from
145- """
146-
147- code_mapping = {
148- "otype" : (self .structure_config , "orientation_type" ),
149- "dd" : (self .structure_config , "dipdir_column" ),
150- "d" : (self .structure_config , "dip_column" ),
151- "sf" : (self .structure_config , "description_column" ),
152- "bedding" : (self .structure_config , "bedding_text" ),
153- "bo" : (self .structure_config , "overturned_column" ),
154- "btype" : (self .structure_config , "overturned_text" ),
155- "gi" : (self .structure_config , "objectid_column" ),
156- "c" : (self .geology_config , "unitname_column" ),
157- "u" : (self .geology_config , "alt_unitname_column" ),
158- "g" : (self .geology_config , "group_column" ),
159- "g2" : (self .geology_config , "supergroup_column" ),
160- "ds" : (self .geology_config , "description_column" ),
161- "min" : (self .geology_config , "minage_column" ),
162- "max" : (self .geology_config , "maxage_column" ),
163- "r1" : (self .geology_config , "rocktype_column" ),
164- "r2" : (self .geology_config , "alt_rocktype_column" ),
165- "sill" : (self .geology_config , "sill_text" ),
166- "intrusive" : (self .geology_config , "intrusive_text" ),
167- "volcanic" : (self .geology_config , "volcanic_text" ),
168- "f" : (self .fault_config , "structtype_column" ),
169- "fault" : (self .fault_config , "fault_text" ),
170- "fdipnull" : (self .fault_config , "dip_null_value" ),
171- "fdipdip_flag" : (self .fault_config , "dipdir_flag" ),
172- "fdipdir" : (self .fault_config , "dipdir_column" ),
173- "fdip" : (self .fault_config , "dip_column" ),
174- "fdipest" : (self .fault_config , "dipestimate_column" ),
175- "fdipest_vals" : (self .fault_config , "dipestimate_text" ),
176- "n" : (self .fault_config , "name_column" ),
177- "ff" : (self .fold_config , "structtype_column" ),
178- "fold" : (self .fold_config , "fold_text" ),
179- "t" : (self .fold_config , "description_column" ),
180- "syn" : (self .fold_config , "synform_text" ),
181- }
182- for code in code_mapping :
183- if code in file_map :
184- if lower is True :
185- file_map [code ] = str (file_map [code ]).lower ()
186- code_mapping [code ][0 ][code_mapping [code ][1 ]] = file_map [code ]
187- file_map .pop (code )
188-
189- if "o" in file_map :
190- self .structure_config ["objectid_column" ] = file_map ["o" ]
191- self .fault_config ["objectid_column" ] = file_map ["o" ]
192- self .fold_config ["objectid_column" ] = file_map ["o" ]
193- file_map .pop ("o" )
194-
195- if len (file_map ) > 0 :
196- logger .warning (f"Unused keys from legacy format { list (file_map .keys ())} " )
197145
198146 @beartype .beartype
199147 def update_from_file (
200- self , filename : Union [pathlib .Path , str ], legacy_format : bool = False , lower : bool = False
148+ self , filename : Union [pathlib .Path , str ], lower : bool = False
201149 ):
202150 """
203151 Update the config dictionary from the provided json filename or url
204152
205153 Args:
206154 filename (Union[pathlib.Path, str]): Filename or URL of the JSON config file
207- legacy_format (bool, optional): Whether the JSON is an old version. Defaults to False.
208155 lower (bool, optional): convert keys to lowercase. Defaults to False.
209156 """
210- if legacy_format :
211- func = self .update_from_legacy_file
212- else :
213- func = self .update_from_dictionary
157+ func = self .update_from_dictionary
214158
215159 try :
216160 filename = str (filename )
@@ -269,7 +213,60 @@ def update_from_file(
269213 err_string += "Please check the file is accessible online and then\n "
270214 else :
271215 err_string += "Please check the file exists and is accessible then\n "
272- if not legacy_format :
273- err_string += "Also check if this is a legacy config file and add clut_file_legacy=True to the Project function\n "
274216 err_string += "Check the contents for mismatched quotes or brackets!"
275217 raise Exception (err_string )
218+
219+ @beartype .beartype
220+ def validate_config_dictionary (self , config_dict : dict ) -> None :
221+ """
222+ Validate the structure and keys of the configuration dictionary.
223+
224+ Args:
225+ config_dict (dict): The config dictionary to validate.
226+
227+ Raises:
228+ ValueError: If the dictionary does not meet the minimum requirements for ma2p2loop.
229+ """
230+ required_keys = {
231+ "structure" : {"dipdir_column" , "dip_column" },
232+ "geology" : {"unitname_column" , "alt_unitname_column" },
233+ }
234+
235+ for section , keys in required_keys .items ():
236+ if section not in config_dict :
237+ logger .error (f"Missing required section '{ section } ' in config dictionary." )
238+ raise ValueError (f"Missing required section '{ section } ' in config dictionary." )
239+
240+ for key in keys :
241+ if key not in config_dict [section ]:
242+ logger .error (
243+ f"Missing required key '{ key } ' for '{ section } ' section of the config dictionary."
244+ )
245+ raise ValueError (
246+ f"Missing required key '{ key } ' for '{ section } ' section of the config dictionary."
247+ )
248+
249+ @beartype .beartype
250+ def check_for_legacy_keys (self , config_dict : dict ) -> None :
251+
252+ legacy_keys = {
253+ "otype" , "dd" , "d" , "sf" , "bedding" , "bo" , "btype" , "gi" , "c" , "u" ,
254+ "g" , "g2" , "ds" , "min" , "max" , "r1" , "r2" , "sill" , "intrusive" , "volcanic" ,
255+ "f" , "fdipnull" , "fdipdip_flag" , "fdipdir" , "fdip" , "fdipest" ,
256+ "fdipest_vals" , "n" , "ff" , "t" , "syn"
257+ }
258+
259+ # Recursively search for keys in the dictionary
260+ def check_keys (d : dict , parent_key = "" ):
261+ for key , value in d .items ():
262+ if key in legacy_keys :
263+ logger .error (
264+ f"Legacy key found in config - '{ key } ' at '{ parent_key + key } '. Please use the new config format. Use map2loop.utils.update_from_legacy_file to convert between the formats if needed"
265+ )
266+ raise ValueError (
267+ f"Legacy key found in config - '{ key } ' at '{ parent_key + key } '. Please use the new config format. Use map2loop.utils.update_from_legacy_file to convert between the formats if needed"
268+ )
269+ if isinstance (value , dict ):
270+ check_keys (value , parent_key = f"{ parent_key } { key } ." )
271+
272+ check_keys (config_dict )
0 commit comments