def check_existence_of_pedigree_matrix(self, pedigree_matrix: str, pedigree: str, subrow=None) -> NoReturn: # Check existence of PedigreeMatrix, if used if pedigree_matrix and pedigree: pm = self._glb_idx.get( PedigreeMatrix.partial_key(name=pedigree_matrix)) if len(pm) == 0: raise CommandExecutionError( "Could not find Pedigree Matrix '" + pedigree_matrix + "'" + subrow_issue_message(subrow)) else: try: lst = pm[0].get_modes_for_code(pedigree) except: raise CommandExecutionError("Could not decode Pedigree '" + pedigree + "' for Pedigree Matrix '" + pedigree_matrix + "'" + subrow_issue_message(subrow)) elif pedigree and not pedigree_matrix: raise CommandExecutionError( "Pedigree specified without accompanying Pedigree Matrix" + subrow_issue_message(subrow))
def get_source(self, reference_name, subrow) -> Any: reference = None if reference_name: try: ast = parser_field_parsers.string_to_ast( parser_field_parsers.reference, reference_name) ref_id = ast["ref_id"] references = self._glb_idx.get( ProvenanceReference.partial_key(ref_id)) if len(references) == 1: reference = references[0] else: references = self._glb_idx.get( BibliographicReference.partial_key(ref_id)) if len(references) == 1: reference = references[0] else: raise CommandExecutionError( f"Reference '{reference_name}' not found" + subrow_issue_message(subrow)) except: # TODO Change when Ref* are implemented reference = reference_name + " (not found)" return reference
def _check_fields(self, relation_class: RelationClassType, source_processor: Processor, target_processor: Processor, subrow=None): # Use of column BackInterface is only allowed in some relation types back_allowed_classes = [ RelationClassType.ff_directed_flow, RelationClassType.ff_reverse_directed_flow, RelationClassType.ff_directed_flow_back ] if self._fields[ "back_interface"] and relation_class not in back_allowed_classes: raise CommandExecutionError( f"Column 'BackInterface' is only allowed in relations of type: " f"{back_allowed_classes}" + subrow_issue_message(subrow)) # Use of column Weight is only allowed in some relation types weight_allowed_classes = [ RelationClassType.ff_directed_flow, RelationClassType.ff_reverse_directed_flow, RelationClassType.ff_directed_flow_back, RelationClassType.ff_scale ] if self._fields[ "flow_weight"] and relation_class not in weight_allowed_classes: raise CommandExecutionError( f"Column 'Weight' is only allowed in relations of type: " f"{weight_allowed_classes}") # Processors should be the same when relation is "Scale Change" if relation_class == RelationClassType.ff_scale_change and source_processor.name != target_processor.name: raise CommandExecutionError( f"Source and target processors should be the same for a relation of type" f" 'Scale Change': '{source_processor.name}' != '{target_processor.name}'" )
def find_processor(self, processor_name, subrow) -> Processor: # Find Processor # TODO Allow creating a basic Processor if it is not found? processors = find_processors_matching_name(processor_name, self._glb_idx) # p = find_observable_by_name(processor_name, self._glb_idx) # p = self._glb_idx.get(Processor.partial_key(processor_name)) if len(processors) == 0: raise CommandExecutionError("Processor '" + processor_name + "' not declared previously" + subrow_issue_message(subrow)) elif len(processors) > 1: raise CommandExecutionError( f"Processor '{processor_name}' declared previously {len(processors)} times" + subrow_issue_message(subrow)) return processors[0]
def _process_row(self, fields: Dict[str, Any], subrow=None) -> None: """ :param fields: :param subrow: :return: """ # InterfaceType must exist try: self._get_factor_type_from_field(None, "interface") except CommandExecutionError as e: self._add_issue(IType.ERROR, str(e)) # (LCIA) Indicator must exist indicator = self._glb_idx.get( Indicator.partial_key(fields["lcia_indicator"])) if len(indicator) == 1: pass elif len(indicator) == 0: self._add_issue( IType.ERROR, f"Indicator with name '{fields['lcia_indicator']}' not found" + subrow_issue_message(subrow)) return else: self._add_issue( IType.WARNING, f"Indicator with name '{fields['lcia_indicator']}' found {len(indicator)} times" + subrow_issue_message(subrow)) return # Store LCIA Methods as a new variable. # TODO Use it to prepare a pd.DataFrame previous to calculating Indicators (after solving). Use "to_pickable" lcia_methods = self._state.get("_lcia_methods") if not lcia_methods: lcia_methods = PartialRetrievalDictionary() self._state.set("_lcia_methods", lcia_methods) _ = dict(m=fields["lcia_method"], d=fields["lcia_indicator"], h=fields["lcia_horizon"], i=fields["interface"]) lcia_methods.put( _, (fields["interface_unit"], fields["lcia_coefficient"]))
def transform_text_attributes_into_dictionary(self, text_attributes: str, subrow) -> Dict: dictionary_attributes = {} if text_attributes: try: dictionary_attributes = dictionary_from_key_value_list( text_attributes, self._glb_idx) except Exception as e: raise CommandExecutionError( str(e) + subrow_issue_message(subrow)) return dictionary_attributes
def _process_row(self, fields: Dict[str, Any], subrow=None) -> None: """ Create and register Benchmark object :param fields: """ name = fields["benchmark"] benchmark_group = fields["benchmark_group"] stakeholders = fields["stakeholders"] b = self._glb_idx.get(Benchmark.partial_key(name=name)) if len(b) == 1: b = b[0] elif len(b) == 0: b = Benchmark(name, benchmark_group, stakeholders.split(",") if stakeholders else []) self._glb_idx.put(b.key(), b) else: self._add_issue( IType.ERROR, f"There are {len(b)} instances of the Benchmark '{name}'" + subrow_issue_message(subrow)) return # Add range, if not repeated category = fields["category"] if category not in b.ranges: b.ranges[category] = create_dictionary( data=dict(range=fields["range"], unit=fields["unit"], category=category, label=fields["label"], description=fields["description"])) else: self._add_issue( IType.WARNING, f"Range with category '{category}' repeated" + subrow_issue_message(subrow))
def get_location(self, reference_name, subrow) -> Any: reference = None if reference_name: try: # TODO Change to parser for Location (includes references, but also Codes) ast = parser_field_parsers.string_to_ast( parser_field_parsers.reference, reference_name) ref_id = ast["ref_id"] references = self._glb_idx.get( GeographicReference.partial_key(ref_id)) if len(references) == 1: reference = references[0] else: raise CommandExecutionError( f"Reference '{reference_name}' not found" + subrow_issue_message(subrow)) except: reference = reference_name return reference
def _process_row(self, fields: Dict[str, Any], subrow=None) -> None: scaling_type = fields["scaling_type"] scale: str = fields["scale"] # Find processors invoking_processor = self._get_processor_from_field( "invoking_processor") requested_processor = self._get_processor_from_field( "requested_processor") if invoking_processor == requested_processor: raise CommandExecutionError( f"Invoking and Requested processors cannot be the same '{invoking_processor.name}'. " f"Use the 'relative_to' attribute in 'Interfaces' command instead." ) invoking_interface_name: str = fields["invoking_interface"] requested_interface_name: str = fields["requested_interface"] requested_new_processor_name: str = fields["new_processor_name"] ## # Transform text of "attributes" into a dictionary if fields.get("attributes"): try: fields["attributes"] = dictionary_from_key_value_list( fields["attributes"], self._glb_idx) except Exception as e: self._add_issue(IType.ERROR, str(e) + subrow_issue_message(subrow)) return else: fields["attributes"] = {} # Process specific fields # Obtain the parent: it must exist. It could be created dynamically but it's important to specify attributes if fields.get("parent_processor"): try: parent_processor = self._get_processor_from_field( "parent_processor") except CommandExecutionError: self._add_issue( IType.ERROR, f"Specified parent processor, '{fields.get('parent_processor')}', does not exist" + subrow_issue_message(subrow)) return else: parent_processor = None # Get internal and user-defined attributes in one dictionary attributes = { c.name: fields[c.name] for c in self._command_fields if c.attribute_of == Processor and fields[c.name] is not None } # print(f"Invoking: {invoking_processor.name}:{invoking_interface_name}, Requested: {requested_processor.name}:{requested_interface_name}") requested_processor_clone = None if strcmp(scaling_type, "CloneAndScale") or strcmp( scaling_type, "Clone"): # TODO: check “RequestedProcessor” must be an archetype # 1. Clones “RequestedProcessor” as a child of “InvokingProcessor” requested_processor_clone = self._clone_processor_as_child( processor=requested_processor, parent_processor=invoking_processor if not parent_processor else parent_processor, name=requested_new_processor_name, other_attributes=attributes) if strcmp(scaling_type, "CloneAndScale"): # 2. Constrains the value of “RequestedInterface” to the value of “InvokingInterface”, scaled by “Scale” try: self._constrains_interface( scale=scale, invoking_interface_name=invoking_interface_name, requested_interface_name=requested_interface_name, parent_processor=invoking_processor, child_processor=requested_processor_clone) except Exception as e: self._add_issue(IType.ERROR, str(e) + subrow_issue_message(subrow)) return elif strcmp(scaling_type, "Scale"): # Processors must be of same type (archetype or instance) if not strcmp(invoking_processor.instance_or_archetype, requested_processor.instance_or_archetype): raise CommandExecutionError( "Requested and invoking processors should be of the same type " "(both instance or_archetype)") # 1. Constrains the value of “RequestedInterface” to the value of “InvokingInterface”, scaled by “Scale” try: self._constrains_interface( scale=scale, invoking_interface_name=invoking_interface_name, requested_interface_name=requested_interface_name, parent_processor=invoking_processor, child_processor=requested_processor) except Exception as e: self._add_issue(IType.ERROR, str(e) + subrow_issue_message(subrow)) return elif strcmp(scaling_type, "CloneScaled"): # “RequestedProcessor” must be an archetype # if not strcmp(requested_processor.instance_or_archetype, "archetype"): # raise CommandExecutionError(f"Requested processor '{requested_processor.name}' should be of type 'archetype'") # “InvokingProcessor” must be an instance # if not strcmp(invoking_processor.instance_or_archetype, "instance"): # raise CommandExecutionError(f"Invoking processor '{invoking_processor.name}' should be of type 'instance'") # 1. Clones “RequestedProcessor” as a child of “InvokingProcessor” # 2. Scales the new processor using “Scale” as the value of “RequestedInterface” requested_processor_clone = self._clone_processor_as_child( processor=requested_processor, parent_processor=invoking_processor if not parent_processor else parent_processor, other_attributes=attributes) # Value Scale, which can be an expression, should be evaluated (ast) because we need a final float number scale_value = self._get_scale_value(scale) # In the cloned processor search in all interfaces if there are Observations relative_to RequestedInterface # and multiply the observation by the computed scale. self._scale_observations_relative_to_interface( processor=requested_processor_clone, interface_name=requested_interface_name, scale=scale_value) if requested_processor_clone: # Find or create processor and REGISTER it in "glb_idx" # Add to ProcessorsGroup, if specified field_val = fields.get("processor_group") if field_val: p_set = self._p_sets.get(field_val, ProcessorsSet(field_val)) self._p_sets[field_val] = p_set if p_set.append( requested_processor_clone, self._glb_idx ): # Appends codes to the pset if the processor was not member of the pset p_set.append_attributes_codes(fields["attributes"])
def _process_row(self, field_values: Dict[str, Any], subrow=None) -> None: """ Process a dictionary representing a row of the Interfaces command. The dictionary can come directly from the worksheet or from a dataset. :param field_values: dictionary """ # f_processor_name -> p # f_interface_type_name -> it # f_interface_name -> i # # IF NOT i AND it AND p => i_name = it.name => get or create "i" # IF i AND it AND p => get or create "i", IF "i" exists, i.it MUST BE equal to "it" (IF NOT, error) # IF i AND p AND NOT it => get "i" (MUST EXIST) f_interface_type_name = field_values.get("interface_type") f_interface_name = field_values.get("interface") if not f_interface_name: if not f_interface_type_name: raise CommandExecutionError( "At least one of InterfaceType or Interface must be defined" + subrow_issue_message(subrow)) f_interface_name = f_interface_type_name processor = self.find_processor(field_values.get("processor"), subrow) # Try to find Interface f_orientation = field_values.get("orientation") interface_type: Optional[FactorType] = None interface: Optional[Factor] = None interfaces: Sequence[Factor] = self._glb_idx.get( Factor.partial_key(processor=processor, name=f_interface_name)) if len(interfaces) == 1: interface = interfaces[0] print(f"DEBUG - Interface '{interface.name}' found") interface_type = interface.taxon if f_interface_type_name and not strcmp(interface_type.name, f_interface_type_name): self._add_issue( IType.WARNING, f"The existing Interface '{interface.name}' has the InterfaceType " f"'{interface_type.name}' which is different from the specified " f"InterfaceType '{f_interface_type_name}'. Record skipped." + subrow_issue_message(subrow)) return elif len(interfaces) > 1: raise CommandExecutionError( f"Interface '{f_interface_name}' found {str(len(interfaces))} times. " f"It must be uniquely identified." + subrow_issue_message(subrow)) elif len(interfaces) == 0: # The interface does not exist, create it below if not f_orientation: raise CommandExecutionError( f"Orientation must be defined for new Interfaces." + subrow_issue_message(subrow)) # InterfaceType still not found if not interface_type: interface_type_name = ifnull(f_interface_type_name, f_interface_name) # Find FactorType # TODO Allow creating a basic FactorType if it is not found? interface_types: Sequence[FactorType] = self._glb_idx.get( FactorType.partial_key(interface_type_name)) if len(interface_types) == 0: raise CommandExecutionError( f"InterfaceType '{interface_type_name}' not declared previously" + subrow_issue_message(subrow)) elif len(interface_types) > 1: raise CommandExecutionError( f"InterfaceType '{interface_type_name}' found {str(len(interface_types))} times. " f"It must be uniquely identified." + subrow_issue_message(subrow)) else: interface_type = interface_types[0] # Get attributes default values taken from Interface Type or Processor attributes # Rows : value of (source) "processor.subsystem_type" # Columns: value of (target) "interface_type.opposite_processor_type" # Cells : CORRECTED value of "opposite_processor_type" # +--------+-------+--------+-------+---------+ # | | Local | Env | Ext | ExtEnv | # +--------+-------+--------+-------+---------+ # | Local | Local | Env | Ext | ExtEnv | # | Env | Local | Env | Ext | ExtEnv? | # | Ext | Ext | ExtEnv | Local | Env | # | ExtEnv | Ext | ExtEnv | Local | Env? | # +--------+-------+--------+-------+---------+ if interface_type.opposite_processor_type: tmp = interface_type.opposite_processor_type.lower() if processor.subsystem_type.lower() in ["local", "environment" ]: # First two rows opposite_processor_type = tmp else: opposite_processor_type = InterfacesAndQualifiedQuantitiesCommand.invert[ tmp] # TODO in doubt. Maybe these are undefined (values with question mark in the table) # if tmp == "externalenvironment" and processor.subsystem_type.lower() in ["environment", "externalenvironment"]: # pass else: opposite_processor_type = None interface_type_values = { "sphere": interface_type.sphere, "roegen_type": interface_type.roegen_type, "opposite_processor_type": opposite_processor_type } # Get internal and user-defined attributes in one dictionary # Use: value specified in Interfaces ELSE value specified in InterfaceTypes ELSE first value of allowed values attributes = { c.name: ifnull( field_values[c.name], ifnull(interface_type_values.get(c.name), head(c.allowed_values))) for c in self._command_fields if c.attribute_of == Factor } if not interface: # f_list: Sequence[Factor] = self._glb_idx.get( # Factor.partial_key(processor=p, factor_type=ft, orientation=f_orientation)) # # if len(f_list) > 0: # raise CommandExecutionError(f"An interface called '{f_list[0].name}' for Processor '{f_processor_name}'" # f" with InterfaceType '{f_interface_type_name}' and orientation " # f"'{f_orientation}' already exists"+subrow_issue_message(subrow)) # Transform text of "interface_attributes" into a dictionary interface_attributes = self.transform_text_attributes_into_dictionary( field_values.get("interface_attributes"), subrow) attributes.update(interface_attributes) location = self.get_location(field_values.get("location"), subrow) interface = Factor.create_and_append( f_interface_name, processor, in_processor_type=FactorInProcessorType(external=False, incoming=False), taxon=interface_type, geolocation=location, tags=None, attributes=attributes) self._glb_idx.put(interface.key(), interface) print(f"DEBUG - Interface '{interface.name}' created") elif not interface.compare_attributes(attributes): initial = ', '.join( [f"{k}: {interface.get_attribute(k)}" for k in attributes]) new = ', '.join([f"{k}: {attributes[k]}" for k in attributes]) name = interface.processor.full_hierarchy_names( self._glb_idx)[0] + ":" + interface.name raise CommandExecutionError( f"The same interface '{name}', is being redeclared with different properties. " f"INITIAL: {initial}; NEW: {new}." + subrow_issue_message(subrow)) f_unit = field_values.get("unit") if not f_unit: f_unit = interface_type.unit # Unify unit (it must be done before considering RelativeTo -below-, because it adds a transformation to "f_unit") f_value = field_values.get("value") if f_value is not None and f_unit != interface_type.unit: try: f_value = UnitConversion.convert(f_value, f_unit, interface_type.unit) except DimensionalityError: raise CommandExecutionError( f"Dimensions of units in InterfaceType ({interface_type.unit}) and specified ({f_unit}) are not convertible" + subrow_issue_message(subrow)) f_unit = interface_type.unit # Search for a relative_to interface f_relative_to = field_values.get("relative_to") relative_to_interface: Optional[Factor] = None if f_relative_to: try: ast = parser_field_parsers.string_to_ast( parser_field_parsers.factor_unit, f_relative_to) except: raise CommandExecutionError( f"Could not parse the RelativeTo column, value {str(f_relative_to)}. " + subrow_issue_message(subrow)) relative_to_interface_name = ast_to_string(ast["factor"]) # rel_unit_name = ast["unparsed_unit"] # try: # f_unit = str((ureg(f_unit) / ureg(rel_unit_name)).units) # except (UndefinedUnitError, AttributeError) as ex: # raise CommandExecutionError(f"The final unit could not be computed, interface '{f_unit}' / " # f"relative_to '{rel_unit_name}': {str(ex)}"+subrow_issue_message(subrow)) relative_to_interface = first( interface.processor.factors, lambda ifc: strcmp(ifc.name, relative_to_interface_name)) if not relative_to_interface: raise CommandExecutionError( f"Interface specified in 'relative_to' column " f"'{relative_to_interface_name}' has not been found." + subrow_issue_message(subrow)) if f_value is None and relative_to_interface is not None: # Search for a Interface Type Conversion defined in the ScaleChangeMap command interface_types_transforms: List[FactorTypesRelationUnidirectionalLinearTransformObservation] = \ find_factor_types_transform_relation(self._glb_idx, relative_to_interface.taxon, interface.taxon, processor, processor) # Overwrite any specified unit, it doesn't make sense without a value, i.e. it cannot be used for conversion f_unit = interface.taxon.unit if len(interface_types_transforms) == 1: f_value = interface_types_transforms[0].scaled_weight else: interface_types_transforms_message = "an interface type conversion doesn't exist" \ if (len(interface_types_transforms) == 0) \ else f"{len(interface_types_transforms)} interface type conversions exist" f_value = "0" self._add_issue( IType.WARNING, f"Field 'value' should be defined for interfaces having a " f"'RelativeTo' interface, and {interface_types_transforms_message}. " f"Using value '0'." + subrow_issue_message(subrow)) # Create quantitative observation if f_value is not None: f_uncertainty = field_values.get("uncertainty") f_assessment = field_values.get("assessment") f_pedigree_matrix = field_values.get("pedigree_matrix") f_pedigree = field_values.get("pedigree") f_time = field_values.get("time") f_comments = field_values.get("comments") f_source = field_values.get("qq_source") # TODO: source is not being used source = self.get_source(f_source, subrow) # Find Observer observer: Optional[Observer] = None if f_source: observer = self._glb_idx.get_one( Observer.partial_key(f_source)) if not observer: self._add_issue( IType.WARNING, f"Observer '{f_source}' has not been found." + subrow_issue_message(subrow)) # If an observation exists then "time" is mandatory if not f_time: raise CommandExecutionError( f"Field 'time' needs to be specified for the given observation." + subrow_issue_message(subrow)) # An interface can have multiple observations if each of them have a different [time, observer] combination for observation in interface.quantitative_observations: observer_name = observation.observer.name if observation.observer else None if strcmp(observation.attributes["time"], f_time) and strcmp( observer_name, f_source): raise CommandExecutionError( f"The interface '{interface.name}' in processor '{interface.processor.name}' already has an " f"observation with time '{f_time}' and source '{f_source}'." ) self.check_existence_of_pedigree_matrix(f_pedigree_matrix, f_pedigree, subrow) # Transform text of "number_attributes" into a dictionary number_attributes = self.transform_text_attributes_into_dictionary( field_values.get("number_attributes"), subrow) o = _create_or_append_quantitative_observation( interface, f_value, f_unit, f_uncertainty, f_assessment, f_pedigree, f_pedigree_matrix, observer, relative_to_interface, f_time, None, f_comments, None, number_attributes)
def _process_row(self, field_values: Dict[str, Any], subrow=None) -> None: """ Process a dictionary representing a row of the InterfaceTypes command. The dictionary can come directly from the worksheet or from a dataset. :param field_values: dictionary """ # Read variables ft_h_name = field_values.get( "interface_type_hierarchy", "_default") # "_default" InterfaceType Hierarchy NAME <<<<<< ft_name = field_values.get("interface_type") ft_sphere = field_values.get("sphere") ft_roegen_type = field_values.get("roegen_type") ft_parent = field_values.get("parent_interface_type") ft_formula = field_values.get("formula") ft_description = field_values.get("description") ft_unit = field_values.get("unit") ft_opposite_processor_type = field_values.get( "opposite_processor_type") ft_level = field_values.get("level") ft_attributes = field_values.get("attributes", {}) print(str(type(ft_attributes))) if ft_attributes: try: ft_attributes = dictionary_from_key_value_list( ft_attributes, self._glb_idx) except Exception as e: self._add_issue(IType.ERROR, str(e) + subrow_issue_message(subrow)) return else: ft_attributes = {} # Process # Mandatory fields if not ft_h_name: self._add_issue( IType.WARNING, "Empty interface type hierarchy name. It is recommended to specify one, assuming '_default'." + subrow_issue_message(subrow)) ft_h_name = "_default" if not ft_name: self._add_issue( IType.ERROR, "Empty interface type name. Skipped." + subrow_issue_message(subrow)) return # Check if a hierarchy of interface types by the name <ft_h_name> exists, if not, create it and register it hie = self._glb_idx.get(Hierarchy.partial_key(name=ft_h_name)) if not hie: hie = Hierarchy(name=ft_h_name, type_name="interfacetype") self._glb_idx.put(hie.key(), hie) else: hie = hie[0] # If parent defined, check if it exists # (it must be registered both in the global registry AND in the hierarchy) if ft_parent: parent = self._glb_idx.get(FactorType.partial_key(ft_parent)) if len(parent) > 0: for p in parent: if p.hierarchy == hie: parent = p break if not isinstance(parent, FactorType): self._add_issue( IType.ERROR, f"Parent interface type name '{ft_parent}' not found in hierarchy '{ft_h_name}" + subrow_issue_message(subrow)) return else: self._add_issue( IType.ERROR, f"Parent interface type name '{ft_parent}' not found" + subrow_issue_message(subrow)) return # Double check, it must be defined in "hie" if ft_parent not in hie.codes: self._add_issue( IType.ERROR, f"Parent interface type name '{ft_parent}' not registered in the hierarchy '{ft_h_name}'" + subrow_issue_message(subrow)) return else: parent = None # Check if FactorType exists ft = self._glb_idx.get(FactorType.partial_key(ft_name)) if len(ft) == 0: # TODO Compile and CONSIDER attributes (on the FactorType side) roegen_type = None if ft_roegen_type: roegen_type = FlowFundRoegenType.flow if strcmp( ft_roegen_type, "flow") else FlowFundRoegenType.fund ft = FactorType( ft_name, parent=parent, hierarchy=hie, roegen_type=roegen_type, tags=None, # No tags attributes=dict(unit=ft_unit, description=ft_description, level=ft_level, **ft_attributes), expression=ft_formula, sphere=ft_sphere, opposite_processor_type=ft_opposite_processor_type) # Simple name self._glb_idx.put(FactorType.partial_key(ft_name, ft.ident), ft) if not strcmp(ft_name, ft.full_hierarchy_name()): self._glb_idx.put( FactorType.partial_key(ft.full_hierarchy_name(), ft.ident), ft) else: self._add_issue( IType.WARNING, f"Interface type name '{ft_name}' already registered" + subrow_issue_message(subrow)) return
def _process_row(self, field_values: Dict[str, Any], subrow=None) -> None: # Transform text of "attributes" into a dictionary if field_values.get("attributes"): try: field_values["attributes"] = dictionary_from_key_value_list( field_values["attributes"], self._glb_idx) except Exception as e: self._add_issue(IType.ERROR, str(e) + subrow_issue_message(subrow)) return else: field_values["attributes"] = {} # Process specific fields # Obtain the parent: it must exist. It could be created dynamically but it's important to specify attributes if field_values.get("parent_processor"): try: parent_processor = self._get_processor_from_field( "parent_processor") # parents = find_processors_matching_name(parent_processor) # if len(parents) > 1: # self._add_issue(IType.WARNING, # f"Parent processor '{parent_processor}' not unique. Matches: {', '.join(p.hierarchical_names[0] for p in parents)}. Skipped." + subrow_issue_message(subrow)) # return except CommandExecutionError: self._add_issue( IType.ERROR, f"Specified parent processor, '{field_values.get('parent_processor')}', does not exist" + subrow_issue_message(subrow)) return else: parent_processor = None behave_as_processor: Optional[Processor] = None if field_values.get("behave_as_processor"): try: behave_as_processor = self._get_processor_from_field( "behave_as_processor") except CommandExecutionError: self._add_issue( IType.WARNING, f"Specified 'behave as' processor, '{field_values.get('behave_as_processor')}', does not exist, value ignored" + subrow_issue_message(subrow)) # Find or create processor and REGISTER it in "glb_idx" # TODO Now, only Simple name allowed # TODO Improve allowing hierarchical names, and hierarchical names with wildcards pgroup = field_values.get("processor_group") # Get internal and user-defined attributes in one dictionary attributes = { c.name: field_values[c.name] for c in self._command_fields if c.attribute_of == Processor } attributes.update(field_values["attributes"]) attributes["processor_group"] = pgroup # Needed to support the new name of the field, "Accounted" (previously it was "InstanceOrArchetype") # (internally the values have the same meaning, "Instance" for a processor which has to be accounted, # "Archetype" for a processor which hasn't) v = attributes.get("instance_or_archetype", None) if strcmp(v, "Yes"): v = "Instance" elif strcmp(v, "No"): v = "Archetype" if v: attributes["instance_or_archetype"] = v name = field_values["processor"] p_names, _ = obtain_name_parts(name) geolocation = Geolocation.create(field_values["geolocation_ref"], field_values["geolocation_code"]) ps = find_processors_matching_name(name, self._glb_idx) more_than_one = len(ps) > 1 simple = len(p_names) == 1 exists = True if len(ps) == 1 else False # SIMPLE? EXISTS? PARENT? ACTION: # Yes Yes Yes NEW; HANG FROM PARENT # Yes Yes No Warning: repeated # Yes No Yes NEW; HANG FROM PARENT # Yes No No NEW # No Yes Yes Warning: cannot hang from parent # No Yes No Warning: repeated AND not simple not allowed # No No Yes Warning: cannot create more than one processor AND not simple not allowed # No No No Warning: cannot create more than one processor AND not simple not allowed create_new = False if not simple: if not parent_processor: self._add_issue( IType.WARNING, f"When a processor does not have parent, the name must be simple. Skipped." + subrow_issue_message(subrow)) return else: if exists and not parent_processor: self._add_issue( IType.WARNING, f"Repeated declaration of {name}. Skipped." + subrow_issue_message(subrow)) return create_new = True if create_new: p = find_or_create_processor(state=self._glb_idx, name=name, proc_attributes=attributes, proc_location=geolocation) else: if exists: p = ps[0] # Add to ProcessorsGroup, if specified if pgroup: p_set = self._p_sets.get(pgroup, ProcessorsSet(pgroup)) self._p_sets[pgroup] = p_set if p_set.append( p, self._glb_idx ): # Appends codes to the pset if the processor was not member of the pset p_set.append_attributes_codes(field_values["attributes"]) # If geolocation specified, check if it exists # Inside it, check it the code exists if p.geolocation and p.geolocation.reference: # Geographical reference gr = self._glb_idx.get( GeographicReference.partial_key(name=p.geolocation.reference)) if len(gr) == 0: self._add_issue( IType.ERROR, f"Geographical reference {p.geolocation.reference} not found " + subrow_issue_message(subrow)) return if p.geolocation.reference and not p.geolocation.code: self._add_issue( IType.ERROR, f"Geographical reference was specified but not the code in it " + subrow_issue_message(subrow)) return geo_id = p.geolocation.code try: url = gr[0].attributes["data_location"] except: self._add_issue( IType.ERROR, f"URL not found in geographical reference {p.geolocation.reference} " + subrow_issue_message(subrow)) return try: j, ids = read_geojson( url ) # READ the file!! (or get it from cache). Could take some time... except: self._add_issue( IType.ERROR, f"URL {url} in reference {p.geolocation.reference} could not be read " + subrow_issue_message(subrow)) return if geo_id not in ids: self._add_issue( IType.WARNING, f"Could not find code {geo_id} in file {url}, geographical reference {p.geolocation.reference} " + subrow_issue_message(subrow)) # Add Relationship "part-of" if parent was specified # The processor may have previously other parent processors that will keep its parentship if parent_processor: # Create "part-of" relationship if len( self._glb_idx.get( ProcessorsRelationPartOfObservation.partial_key( parent_processor, p))) > 0: self._add_issue( IType.WARNING, f"{p.name} is already part-of {parent_processor.name}. Skipped." + subrow_issue_message(subrow)) return o1 = ProcessorsRelationPartOfObservation.create_and_append( parent_processor, p, None, behave_as=behave_as_processor, weight=field_values.get("parent_processor_weight")) # Part-of self._glb_idx.put(o1.key(), o1) for hname in parent_processor.full_hierarchy_names(self._glb_idx): p_key = Processor.partial_key(f"{hname}.{p.name}", p.ident) if attributes: p_key.update({ k: ("" if v is None else v) for k, v in attributes.items() }) self._glb_idx.put(p_key, p)
def _process_row(self, fields: Dict[str, Any], subrow=None) -> None: origin_interface_types = self._get_factor_types_from_field("source_hierarchy", "source_interface_type") destination_interface_types = self._get_factor_types_from_field("target_hierarchy", "target_interface_type") origin_processor: Optional[Processor] = None if fields["source_context"]: origin_processor = self._get_processor_from_field("source_context") destination_processor: Optional[Processor] = None if fields["target_context"]: destination_processor = self._get_processor_from_field("target_context") # Check that the interface types are from different hierarchies (warn if not; not error) for origin_interface_type in origin_interface_types: for destination_interface_type in destination_interface_types: if origin_interface_type.hierarchy == destination_interface_type.hierarchy: self._add_issue(IType.WARNING, f"The interface types '{origin_interface_type.name}' and " f"'{destination_interface_type.name}' are in the same hierarchy"+subrow_issue_message(subrow)) observer = find_or_create_observer(Observer.no_observer_specified, self._glb_idx) # Check existence of ScaleChange key = FactorTypesRelationUnidirectionalLinearTransformObservation.partial_key(origin_interface_type, destination_interface_type, origin_processor, destination_processor, observer) tmp = self._glb_idx.get(key) if len(tmp) == 0: # Create the directed Scale (Linear "Transformation") Relationship o = FactorTypesRelationUnidirectionalLinearTransformObservation.create_and_append( origin_interface_type, destination_interface_type, fields["scale"], origin_processor, destination_processor, fields["source_unit"], fields["target_unit"], observer) self._glb_idx.put(o.key(), o) elif len(tmp) > 0: self._add_issue(IType.WARNING, f"The ScaleChange {tmp[0]} has already been defined with weight {tmp[0]._weight}")