"""Represents the configuration of a multiple expression:author: Michel Bierlaire:date: Sun Apr 2 14:15:17 2023"""from__future__importannotationsimportloggingfromtypingimportIterable,NamedTupleimportbiogeme.exceptionsasexcepfrombiogeme.exceptionsimportBiogemeErrorlogger=logging.getLogger(__name__)
[docs]classConfiguration:"""Represents the configuration of a multiple expression. It is internally represented as a sorted list of tuples. """def__init__(self,selections:Iterable[SelectionTuple]|None=None):"""Ctor. :param selections: list of tuples, where each of them associates a controller name with the selected configuration :type selections: list(SelectionTuple) """ifselectionsisNone:self.__selections:list[SelectionTuple]|None=Noneelse:self.selections:list[SelectionTuple]=list(selections)@propertydefselections(self)->list[SelectionTuple]:returnself.__selections@selections.setterdefselections(self,the_list:list[SelectionTuple]):self.__selections=sorted(the_list)self.__check_list_validity()self.string_id=self.get_string_id()
[docs]@classmethoddeffrom_string(cls,string_id:str)->Configuration:"""Ctor from a string representation :param string_id: string ID :type string_id: str """ifnotstring_id:raiseBiogemeError('No string id has been provided')terms=string_id.split(SEPARATOR)the_config={}forterminterms:try:controller,selection=term.split(SELECTION_SEPARATOR)exceptValueErrorasexc:error_msg=(f'{exc}: Invalid syntax for ID {term}. Expecting a separator 'f'[{SELECTION_SEPARATOR}]')raiseexcep.BiogemeError(error_msg)the_config[controller]=selectionreturncls.from_dict(the_config)
[docs]@classmethoddeffrom_dict(cls,dict_of_selections:dict[str,str])->Configuration:"""Ctor from dict :param dict_of_selections: dict associating a catalog name with the selected configuration :type dict_of_selections: dict(str: str) """the_list=(SelectionTuple(controller=controller,selection=selection)forcontroller,selectionindict_of_selections.items())returncls(selections=the_list)
[docs]@classmethoddeffrom_tuple_of_configurations(cls,tuple_of_configurations:tuple[Configuration,...])->Configuration:"""Ctor from tuple of configurations that are merged together. In the presence of two different selections for the same catalog, the first one is selected, and the others ignored. :param tuple_of_configurations: tuple of configurations to merge :type tuple_of_configurations: tuple(Configuration) """known_controllers=set()selections=[]forconfigurationintuple_of_configurations:ifconfiguration.selectionsisnotNone:forselectioninconfiguration.selections:ifselection.controllernotinknown_controllers:selections.append(selection)known_controllers.add(selection.controller)returncls(selections)
[docs]defget_string_id(self)->str:"""The string ID is a unique string representation of the configuration :return: string ID :rtype: str """terms=[f'{selection.controller}{SELECTION_SEPARATOR}{selection.selection}'forselectioninself.selections]returnSEPARATOR.join(terms)
[docs]defget_selection(self,controller_name:str)->str|None:"""Retrieve the selection of a given controller :param controller_name: name of the controller :type controller_name: str :return: name of the selected config, or None if controller is not known :rtype: str """forselectioninself.selections:ifselection.controller==controller_name:returnselection.selectionreturnNone
def__check_list_validity(self)->None:"""Check the validity of the list. :raise BiogemeError: if the same catalog appears more than once in the list """unique_items=set()foriteminself.__selections:ifitem.controllerinunique_items:error_msg=(f'Controller {item.controller} appears more than once in the 'f'configuration: {self.__selections}')raiseexcep.BiogemeError(error_msg)unique_items.add(item.controller)