99
1010import yaml
1111from pydantic import ValidationError
12+ from pydantic .error_wrappers import ErrorWrapper
1213from pydantic .fields import ModelField , UndefinedType
1314from rich .console import Console
1415
@@ -103,8 +104,7 @@ def construct_list(
103104 debug : bool = False ,
104105) -> list [Any ]:
105106 """
106- Helper function to facilitate interactive construction of a list to be stored
107- under the current parameter.
107+ Helper function to facilitate interactive construction of a list.
108108 """
109109 lst : list = []
110110 add_entry = ask_for_input (value_name , False )
@@ -269,7 +269,9 @@ def validate_value(value: Any, key: str, field: ModelField, debug: bool = False)
269269 """
270270 validated_value , errors = field .validate (value , {}, loc = key )
271271 if errors :
272- raise ValidationError (errors , MachineConfig )
272+ raise ValidationError (
273+ ([errors ] if isinstance (errors , ErrorWrapper ) else errors ), MachineConfig
274+ )
273275 console .print (f"{ key !r} validated successfully." , style = "bright_green" )
274276 if debug :
275277 console .print (f"Type: { type (validated_value )} " , style = "bright_green" )
@@ -314,6 +316,7 @@ def add_calibrations(
314316 """
315317 # Known calibrations and what to call their keys and values
316318 known_calibrations : dict [str , tuple [str , str ]] = {
319+ # Calibration type | Key name | Value name
317320 "magnification" : ("magnification" , "pixel size (in angstroms)" )
318321 }
319322
@@ -342,7 +345,7 @@ def add_calibrations(
342345 continue
343346 # Skip failed inputs
344347 calibration_values = construct_dict (
345- f"{ calibration_type } setting " ,
348+ f"{ calibration_type } calibration " ,
346349 known_calibrations [calibration_type ][0 ],
347350 known_calibrations [calibration_type ][1 ],
348351 allow_empty_key = False ,
@@ -445,56 +448,38 @@ def get_file_extension() -> str:
445448 style = "red" ,
446449 )
447450 return get_file_extension ()
448- if extension in unsorted_dict .keys ():
451+ if extension in extension_dict .keys ():
449452 console .print ("This extension has already been provided" )
450453 return ""
451454 return extension
452455
453- def get_file_substring () -> str :
454- message = (
455- "Please enter a keyword that will be present in files with this "
456- "extension. This field is case-sensitive."
457- )
458- substring = prompt (message , style = "yellow" ).strip ()
459- # Validate
460- if bool (re .fullmatch (r"[\w\s\-]*" , substring )) is False :
461- console .print (
462- "Unsafe characters are present in this substring. Please "
463- "try again. " ,
464- style = "red" ,
465- )
466- return get_file_substring ()
467- if substring in substrings :
468- console .print ("This substring has already been provided." )
469- return ""
470- return substring
471-
472456 """
473457 Start of get_extensions_and_substrings
474458 """
475- unsorted_dict : dict = {}
459+ extension_dict : dict = {}
476460 add_extension = ask_for_input ("file extension" , False )
477461 while add_extension is True :
478462 extension = get_file_extension ()
479463 if not extension :
480464 add_extension = ask_for_input ("file extension" , True )
481465 continue
482- substrings : list [str ] = []
483- add_substring = ask_for_input ( "file substring" , False )
484- while add_substring is True :
485- substring = get_file_substring ()
486- if not substring :
487- add_substring = ask_for_input ( "file substring" , True )
488- continue
489- substrings . append ( substring )
490- add_substring = ask_for_input ( "file substring" , True )
491- unsorted_dict [extension ] = sorted ( substrings )
466+ substrings : list [str ] = construct_list (
467+ "file substring" ,
468+ "Please enter a file substring associated with this extension" ,
469+ allow_empty = False ,
470+ allow_eval = False ,
471+ many_types = False ,
472+ restrict_to_types = str ,
473+ sort_values = True ,
474+ )
475+ extension_dict [extension ] = substrings
492476 add_extension = ask_for_input ("file extension" , True )
477+ continue
493478
494- sorted_dict : dict = {}
495- for key in sorted (unsorted_dict .keys ()):
496- sorted_dict [ key ] = unsorted_dict [ key ]
497- return sorted_dict
479+ extension_dict = {
480+ key : extension_dict [ key ] for key in sorted (extension_dict .keys ())
481+ }
482+ return extension_dict
498483
499484 """
500485 Start of add_software_packages
@@ -513,7 +498,7 @@ def get_file_substring() -> str:
513498 category = "software package"
514499 add_input = ask_for_input (category , again = False )
515500 while add_input :
516- # Collect inputs
501+ # Collect software name
517502 console .print (
518503 "Acquisition Software (acquisition_software)" ,
519504 style = "bold bright_cyan" ,
@@ -532,6 +517,7 @@ def get_file_substring() -> str:
532517 add_input = ask_for_input (category , False )
533518 continue
534519
520+ # Collect version info
535521 console .print (
536522 "Software Versions (software_versions)" ,
537523 style = "bold bright_cyan" ,
@@ -542,6 +528,7 @@ def get_file_substring() -> str:
542528 style = "yellow" ,
543529 )
544530
531+ # Collect settings files and modifications
545532 console .print (
546533 "Software Settings Output Directories (software_settings_output_directories)" ,
547534 style = "bold bright_cyan" ,
@@ -560,6 +547,7 @@ def get_file_substring() -> str:
560547 xml_file = None
561548 xml_tree_path = ""
562549
550+ # Collect extensions and filename substrings
563551 console .print (
564552 "Data Required Substrings (data_required_substrings)" ,
565553 style = "bold bright_cyan" ,
@@ -580,6 +568,7 @@ def get_file_substring() -> str:
580568 "extensions_and_substrings" : file_ext_ss ,
581569 }
582570 add_input = ask_for_input (category , again = True )
571+ continue
583572
584573 # Re-pack keys and values according to the current config field structures
585574 console .print ("Compiling and validating inputs..." )
@@ -627,53 +616,21 @@ def get_file_substring() -> str:
627616def add_data_directories (
628617 key : str , field : ModelField , debug : bool = False
629618) -> dict [str , str ]:
630- def get_directory () -> Optional [Path ]:
631- message = "What is the full file path to the data directory you wish to add?"
632- answer = prompt (message , style = "yellow" ).strip ()
633- # Convert "" into None
634- if not answer :
635- return None
636- return Path (answer )
637-
638- def get_directory_type ():
639- message = (
640- "What type of data is stored in this directory? Options: 'microscope', "
641- "'detector'"
642- )
643- answer = prompt (message , style = "yellow" ).lower ().strip ()
644- if answer not in ("microscope" , "detector" ):
645- console .print ("Invalid directory type." , style = "red" )
646- if ask_for_input ("directory type" , True ) is True :
647- return get_directory_type ()
648- return ""
649- return answer
650-
651619 """
652- Start of add_data_directories
620+ Function to facilitate populating the data_directories field.
653621 """
654622 print_field_info (field )
655- data_directories : dict [str , str ] = {}
656623 category = "data directory"
657- add_directory = ask_for_input (category , False )
658- while add_directory is True :
659- directory = get_directory ()
660- # Move on to next loop or exit if no directory provided
661- if not directory :
662- console .print ("No directory added" , style = "red" )
663- add_directory = ask_for_input (category , True )
664- continue
665-
666- # Get the directory type
667- directory_type = get_directory_type ()
668- if not directory_type :
669- console .print ("No directory type provided" , style = "red" )
670-
671- # Add to dictionary
672- data_directories [str (directory )] = directory_type
673-
674- # Check if more need to be added
675- add_directory = ask_for_input (category , True )
676- continue
624+ data_directories : dict [str , str ] = construct_dict (
625+ category ,
626+ "full file path to the data directory" ,
627+ "data type" ,
628+ allow_empty_key = False ,
629+ allow_empty_value = False ,
630+ allow_eval = False ,
631+ sort_keys = True ,
632+ restrict_to_types = str ,
633+ )
677634
678635 # Validate and return
679636 try :
0 commit comments