def __init__(self, db): QObject.__init__(self) self.app = AppInterface() self.logger = Logger() self.__db = db self.__tr_dict = TranslatableConfigStrings( ).get_translatable_config_strings() # Hierarquical dict of qrs and qr groups self.__qrs_tree_data = dict() # {type: {qr_key1: qr_obj1, ...}, ...} # Hierarquical dict of qrs and qr groups with general results self.__general_results_tree_data = dict( ) # {type: {qr_obj1: qr_results1, ...}, ...} # Hierarchical dict of qrs and their corresponding error instances # feature1: {uuids, rel_uuids, error_type, nombre_ili_obj, details, values, fixed, exception, geom_fks} self.__error_results_data = dict() # {qr_key1: {t_id1: feature1}} self.__qr_results_dir_path = '' # Dir path where results will be stored self.__selected_qrs = list() # QRs to be validated (at least 1) self.__selected_qr = None # QR selected by the user to show its corresponding errors (exactly 1) self.__qr_engine = None # Once set, we can reuse it self.__qrs_results = None # QualityRulesExecutionResult object # To cache layers from QR DB self.__error_layer = None self.__point_layer = None self.__line_layer = None self.__polygon_layer = None # Cache by t_id (built on demand): {t_id1: 'Error', t_id2: 'Corregido', t_id3: 'Exception'} self.__error_state_dict = dict()
def __init__(self, db, layers, editing_layer): self._db = db self.__layers = layers self.__translatable_config_strings = TranslatableConfigStrings() self.width_line = 1.0 self._editing_layer = editing_layer self.app = None
def decorated_function(*args, **kwargs): inst = args[0] if type( args[0]).__name__ == 'AsistenteLADMCOLPlugin' else args[0].ladmcol context = args[1] for db_source in context.get_db_sources(): db = inst.conn_manager.get_db_connector_from_source( db_source=db_source) db.test_connection() if not db.cadastral_cartography_model_exists(): widget = inst.iface.messageBar().createMessage( "Asistente LADM-COL", QCoreApplication.translate( "AsistenteLADMCOLPlugin", "Check your {} database connection. The '{} {}' model is required for this functionality, but could not be found in your current database. Click the button to go to Settings." ).format( Tr.tr_db_source(db_source), LADMColModelRegistry().model( LADMNames.CADASTRAL_CARTOGRAPHY_MODEL_KEY).alias(), LADMColModelRegistry().model( LADMNames.CADASTRAL_CARTOGRAPHY_MODEL_KEY).alias()) ) button = QPushButton(widget) button.setText( QCoreApplication.translate("AsistenteLADMCOLPlugin", "Settings")) settings_context = SettingsContext(db_source) settings_context.required_models = [ LADMNames.CADASTRAL_CARTOGRAPHY_MODEL_KEY ] settings_context.tab_pages_list = [ SETTINGS_CONNECTION_TAB_INDEX ] settings_context.title = QCoreApplication.translate( "SettingsDialog", "{} Connection Settings").format( Tr.tr_db_source(db_source)) settings_context.tip = QCoreApplication.translate( "SettingsDialog", "Set a DB connection with the '{}' model.").format( LADMColModelRegistry().model( LADMNames.CADASTRAL_CARTOGRAPHY_MODEL_KEY).alias()) button.pressed.connect( partial(inst.show_settings_clear_message_bar, settings_context)) widget.layout().addWidget(button) inst.iface.messageBar().pushWidget(widget, Qgis.Warning, 15) inst.logger.warning( __name__, QCoreApplication.translate( "AsistenteLADMCOLPlugin", "A dialog/tool couldn't be opened/executed, connection to DB was not valid." )) return func_to_decorate(*args, **kwargs)
def __init__(self): self.logger = Logger() self.__quality_rules_data = QualityRuleConfig.get_quality_rules_config( ) self.__translated_strings = TranslatableConfigStrings( ).get_translatable_config_strings() self._quality_rule_groups = dict() self.__quality_rules = dict() self.role_registry = RoleRegistry() self._initialize_quality_rule_manager()
class RightOfWayManager: def __init__(self, db, layers, editing_layer): self._db = db self.__layers = layers self.__translatable_config_strings = TranslatableConfigStrings() self.width_line = 1.0 self._editing_layer = editing_layer self.app = None def finish_feature_creation(self, layerId, features): fid = features[0].id() is_valid = False feature_tid = None if self._editing_layer.getFeature(fid).isValid(): is_valid = True feature_tid = self._editing_layer.getFeature(fid)[self._db.names.T_ID_F] return FinishFeatureCreationArgs(is_valid, feature_tid) def get_memory_line_layer(self, base_layer): translated_strings = self.__translatable_config_strings.get_translatable_config_strings() # Add Memory line layer temporal_layer = QgsVectorLayer( "MultiLineString?crs={}".format(get_crs_authid(base_layer.sourceCrs())), translated_strings[RIGHT_OF_WAY_LINE_LAYER], "memory") return temporal_layer def get_feature_with_buffer_right_of_way(self, tmp_layer, layer): params = {'INPUT': tmp_layer, 'DISTANCE': self.width_line, 'SEGMENTS': 5, 'END_CAP_STYLE': 1, # Flat 'JOIN_STYLE': 2, 'MITER_LIMIT': 2, 'DISSOLVE': False, 'OUTPUT': 'memory:'} buffered_right_of_way_layer = processing.run("native:buffer", params)['OUTPUT'] buffer_geometry = buffered_right_of_way_layer.getFeature(1).geometry() feature = QgsVectorLayerUtils().createFeature(layer, buffer_geometry) tmp_layer.commitChanges() return feature def add_tmp_feature_to_layer(self, layer, tmp_feature): # Add temporal geometry create if not layer.isEditable(): layer.startEditing() self.app.core.suppress_form(layer, True) layer.addFeature(tmp_feature)
def get_quality_error_group_names(): root = QgsProject.instance().layerTreeRoot() prefix = TranslatableConfigStrings.get_translatable_config_strings( )[ERROR_LAYER_GROUP_PREFIX] error_group_names = list() for group in root.findGroups(False): if group.name().startswith(prefix): error_group_names.append(group.name()) return error_group_names
def get_quality_error_group(timestamp, create_if_non_existent=True): root = QgsProject.instance().layerTreeRoot() prefix = TranslatableConfigStrings.get_translatable_config_strings( )[ERROR_LAYER_GROUP_PREFIX] group_name = "{} {}".format(prefix, timestamp) group = root.findGroup(group_name) if group is None and create_if_non_existent: group = root.insertGroup(0, group_name) ilg = qgis.utils.plugins['InvisibleLayersAndGroups'] ilg.hideGroup(group) group.setExpanded(False) return group
def __init__(self, iface, db, qgis_utils, wizard_settings): super(AbsWizardFactory, self).__init__() self.iface = iface self._db = db self.qgis_utils = qgis_utils self.wizard_config = wizard_settings self.logger = Logger() self.names = self._db.names self.help_strings = HelpStrings() self.translatable_config_strings = TranslatableConfigStrings() load_ui(self.wizard_config[WIZARD_UI], self) self.WIZARD_FEATURE_NAME = self.wizard_config[WIZARD_FEATURE_NAME] self.WIZARD_TOOL_NAME = self.wizard_config[WIZARD_TOOL_NAME] self.EDITING_LAYER_NAME = self.wizard_config[WIZARD_EDITING_LAYER_NAME] self._layers = self.wizard_config[WIZARD_LAYERS] self.set_ready_only_field() self.init_gui()
def get_indicators_config(self, node_name, node_type): """ Gets the configuration of layer tree node indicators. Each node could have several indicators. :param node_name: Layer tree node name :param node_type: QgsLayerTreeNode.NodeType :return: List of indicators data. Each indicator config data is a dict, which has these mandatory keys: INDICATOR_TOOLTIP, INDICATOR_ICON, INDICATOR_SLOT """ indicators_config = [] translated_strings = TranslatableConfigStrings.get_translatable_config_strings( ) if node_type == QgsLayerTreeNode.NodeGroup: if node_name == translated_strings[ERROR_LAYER_GROUP]: indicators_config = [{ INDICATOR_TOOLTIP: QCoreApplication.translate( "LayerTreeIndicatorConfig", "<b>Export</b><br>Export quality errors to GeoPackage" ), INDICATOR_ICON: QIcon(":/Asistente-LADM-COL/resources/images/save.svg"), INDICATOR_SLOT: self._slot_caller.export_error_group }, { INDICATOR_TOOLTIP: QCoreApplication.translate( "LayerTreeIndicatorConfig", "<b>Export</b><br>Export quality errors to PDF"), INDICATOR_ICON: QIcon(":/Asistente-LADM-COL/resources/images/pdf.svg"), INDICATOR_SLOT: self._slot_caller.show_log_quality_dialog }] elif node_type == QgsLayerTreeNode.NodeLayer: pass return indicators_config
def get_error_layers_group(self): """ Get the topology errors group. If it exists but is placed in another position rather than the top, it moves the group to the top. """ root = QgsProject.instance().layerTreeRoot() translated_strings = TranslatableConfigStrings.get_translatable_config_strings( ) group = root.findGroup(translated_strings[ERROR_LAYER_GROUP]) if group is None: group = root.insertGroup(0, translated_strings[ERROR_LAYER_GROUP]) self.add_indicators_requested.emit( translated_strings[ERROR_LAYER_GROUP], QgsLayerTreeNode.NodeGroup) elif not self.iface.layerTreeView().layerTreeModel().node2index( group).row() == 0 or type(group.parent()) is QgsLayerTreeGroup: group_clone = group.clone() root.insertChildNode(0, group_clone) parent = group.parent() parent.removeChildNode(group) group = group_clone return group
def __init__(self, db, qgis_utils, quality, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self._db = db self.qgis_utils = qgis_utils self.quality = quality self.names = self._db.names self.translatable_config_strings = TranslatableConfigStrings() self.trw_quality_rules.setItemsExpandable(False) self.trw_quality_rules.itemSelectionChanged.connect( self.validate_selection_rules) self.trw_quality_rules.itemSelectionChanged.emit() # Set connections self.buttonBox.accepted.connect(self.accepted) self.buttonBox.rejected.connect(self.rejected) self.buttonBox.helpRequested.connect(self.show_help) self.btn_select_all.clicked.connect(self.select_all) self.btn_clear_selection.clicked.connect(self.clear_selection) translated_strings = self.translatable_config_strings.get_translatable_config_strings( ) self.items_dict = collections.OrderedDict() self.items_dict[QCoreApplication.translate( "QualityDialog", "Rules for Points" )] = { 'icon': 'points', 'rules': [{ 'id': 'check_overlaps_in_boundary_points', 'text': translated_strings[CHECK_OVERLAPS_IN_BOUNDARY_POINTS] }, { 'id': 'check_overlaps_in_control_points', 'text': translated_strings[CHECK_OVERLAPS_IN_CONTROL_POINTS] }, { 'id': 'check_boundary_points_covered_by_boundary_nodes', 'text': translated_strings[ CHECK_BOUNDARY_POINTS_COVERED_BY_BOUNDARY_NODES] }, { 'id': 'check_boundary_points_covered_by_plot_nodes', 'text': translated_strings[CHECK_BOUNDARY_POINTS_COVERED_BY_PLOT_NODES] }] } self.items_dict[QCoreApplication.translate( "QualityDialog", "Rules for Lines")] = { 'icon': 'lines', 'rules': [{ 'id': 'check_overlaps_in_boundaries', 'text': translated_strings[CHECK_OVERLAPS_IN_BOUNDARIES] }, { 'id': 'check_boundaries_are_not_split', 'text': translated_strings[CHECK_BOUNDARIES_ARE_NOT_SPLIT] }, { 'id': 'check_boundaries_covered_by_plots', 'text': translated_strings[CHECK_BOUNDARIES_COVERED_BY_PLOTS] }, { 'id': 'check_boundary_nodes_covered_by_boundary_points', 'text': translated_strings[ CHECK_BOUNDARY_NODES_COVERED_BY_BOUNDARY_POINTS] }, { 'id': 'check_dangles_in_boundaries', 'text': translated_strings[CHECK_DANGLES_IN_BOUNDARIES] }] } self.items_dict[QCoreApplication.translate( "QualityDialog", "Rules for Polygons" )] = { 'icon': 'polygons', 'rules': [ { 'id': 'check_overlaps_in_plots', 'text': translated_strings[CHECK_OVERLAPS_IN_PLOTS] }, { 'id': 'check_overlaps_in_buildings', 'text': translated_strings[CHECK_OVERLAPS_IN_BUILDINGS] }, { 'id': 'check_overlaps_in_rights_of_way', 'text': translated_strings[CHECK_OVERLAPS_IN_RIGHTS_OF_WAY] }, { 'id': 'check_plots_covered_by_boundaries', 'text': translated_strings[CHECK_PLOTS_COVERED_BY_BOUNDARIES] #}, { # 'id': 'check_missing_survey_points_in_buildings', # 'text': QCoreApplication.translate("QualityDialog", "Buildings nodes should be covered by Survey Points") }, { 'id': 'check_right_of_way_overlaps_buildings', 'text': translated_strings[CHECK_RIGHT_OF_WAY_OVERLAPS_BUILDINGS] }, { 'id': 'check_gaps_in_plots', 'text': translated_strings[CHECK_GAPS_IN_PLOTS] }, { 'id': 'check_multipart_in_right_of_way', 'text': translated_strings[CHECK_MULTIPART_IN_RIGHT_OF_WAY] }, { 'id': 'check_plot_nodes_covered_by_boundary_points', 'text': translated_strings[ CHECK_PLOT_NODES_COVERED_BY_BOUNDARY_POINTS] }, { 'id': 'check_buildings_should_be_within_plots', 'text': translated_strings[CHECK_BUILDING_WITHIN_PLOTS] }, { 'id': 'check_building_units_should_be_within_plots', 'text': translated_strings[CHECK_BUILDING_UNIT_WITHIN_PLOTS] } ] } self.items_dict[QCoreApplication.translate( "QualityDialog", "Logic consistency rules")] = { 'icon': 'tables', 'rules': [{ 'id': 'check_parcel_right_relationship', 'text': translated_strings[CHECK_PARCEL_RIGHT_RELATIONSHIP] }, { 'id': 'find_duplicate_records_in_a_table', 'text': translated_strings[FIND_DUPLICATE_RECORDS_IN_A_TABLE] }, { 'id': 'check_fraction_sum_for_party_groups', 'text': translated_strings[CHECK_FRACTION_SUM_FOR_PARTY_GROUPS] }, { 'id': 'check_department_code_has_two_numerical_characters', 'text': translated_strings[ CHECK_DEPARMENT_CODE_HAS_TWO_NUMERICAL_CHARACTERS] }, { 'id': 'check_municipality_code_has_three_numerical_characters', 'text': translated_strings[ CHECK_MUNICIPALITY_CODE_HAS_THREE_NUMERICAL_CHARACTERS] }, { 'id': 'check_parcel_number_has_30_numerical_characters', 'text': translated_strings[ CHECK_PARCEL_NUMBER_HAS_30_NUMERICAL_CHARACTERS] }, { 'id': 'check_parcel_number_before_has_20_numerical_characters', 'text': translated_strings[ CHECK_PARCEL_NUMBER_BEFORE_HAS_20_NUMERICAL_CHARACTERS] }, { 'id': 'check_col_party_natural_type', 'text': translated_strings[CHECK_COL_PARTY_NATURAL_TYPE] }, { 'id': 'check_col_party_legal_type', 'text': translated_strings[CHECK_COL_PARTY_LEGAL_TYPE] }, { 'id': 'check_parcel_type_and_22_position_of_parcel_number', 'text': translated_strings[ CHECK_PARCEL_TYPE_AND_22_POSITON_OF_PARCEL_NUMBER] }, { 'id': 'check_uebaunit_parcel', 'text': translated_strings[CHECK_UEBAUNIT_PARCEL] }] } self.load_items()
class QualityDialog(QDialog, DIALOG_UI): def __init__(self, db, qgis_utils, quality, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self._db = db self.qgis_utils = qgis_utils self.quality = quality self.names = self._db.names self.translatable_config_strings = TranslatableConfigStrings() self.trw_quality_rules.setItemsExpandable(False) self.trw_quality_rules.itemSelectionChanged.connect( self.validate_selection_rules) self.trw_quality_rules.itemSelectionChanged.emit() # Set connections self.buttonBox.accepted.connect(self.accepted) self.buttonBox.rejected.connect(self.rejected) self.buttonBox.helpRequested.connect(self.show_help) self.btn_select_all.clicked.connect(self.select_all) self.btn_clear_selection.clicked.connect(self.clear_selection) translated_strings = self.translatable_config_strings.get_translatable_config_strings( ) self.items_dict = collections.OrderedDict() self.items_dict[QCoreApplication.translate( "QualityDialog", "Rules for Points" )] = { 'icon': 'points', 'rules': [{ 'id': 'check_overlaps_in_boundary_points', 'text': translated_strings[CHECK_OVERLAPS_IN_BOUNDARY_POINTS] }, { 'id': 'check_overlaps_in_control_points', 'text': translated_strings[CHECK_OVERLAPS_IN_CONTROL_POINTS] }, { 'id': 'check_boundary_points_covered_by_boundary_nodes', 'text': translated_strings[ CHECK_BOUNDARY_POINTS_COVERED_BY_BOUNDARY_NODES] }, { 'id': 'check_boundary_points_covered_by_plot_nodes', 'text': translated_strings[CHECK_BOUNDARY_POINTS_COVERED_BY_PLOT_NODES] }] } self.items_dict[QCoreApplication.translate( "QualityDialog", "Rules for Lines")] = { 'icon': 'lines', 'rules': [{ 'id': 'check_overlaps_in_boundaries', 'text': translated_strings[CHECK_OVERLAPS_IN_BOUNDARIES] }, { 'id': 'check_boundaries_are_not_split', 'text': translated_strings[CHECK_BOUNDARIES_ARE_NOT_SPLIT] }, { 'id': 'check_boundaries_covered_by_plots', 'text': translated_strings[CHECK_BOUNDARIES_COVERED_BY_PLOTS] }, { 'id': 'check_boundary_nodes_covered_by_boundary_points', 'text': translated_strings[ CHECK_BOUNDARY_NODES_COVERED_BY_BOUNDARY_POINTS] }, { 'id': 'check_dangles_in_boundaries', 'text': translated_strings[CHECK_DANGLES_IN_BOUNDARIES] }] } self.items_dict[QCoreApplication.translate( "QualityDialog", "Rules for Polygons" )] = { 'icon': 'polygons', 'rules': [ { 'id': 'check_overlaps_in_plots', 'text': translated_strings[CHECK_OVERLAPS_IN_PLOTS] }, { 'id': 'check_overlaps_in_buildings', 'text': translated_strings[CHECK_OVERLAPS_IN_BUILDINGS] }, { 'id': 'check_overlaps_in_rights_of_way', 'text': translated_strings[CHECK_OVERLAPS_IN_RIGHTS_OF_WAY] }, { 'id': 'check_plots_covered_by_boundaries', 'text': translated_strings[CHECK_PLOTS_COVERED_BY_BOUNDARIES] #}, { # 'id': 'check_missing_survey_points_in_buildings', # 'text': QCoreApplication.translate("QualityDialog", "Buildings nodes should be covered by Survey Points") }, { 'id': 'check_right_of_way_overlaps_buildings', 'text': translated_strings[CHECK_RIGHT_OF_WAY_OVERLAPS_BUILDINGS] }, { 'id': 'check_gaps_in_plots', 'text': translated_strings[CHECK_GAPS_IN_PLOTS] }, { 'id': 'check_multipart_in_right_of_way', 'text': translated_strings[CHECK_MULTIPART_IN_RIGHT_OF_WAY] }, { 'id': 'check_plot_nodes_covered_by_boundary_points', 'text': translated_strings[ CHECK_PLOT_NODES_COVERED_BY_BOUNDARY_POINTS] }, { 'id': 'check_buildings_should_be_within_plots', 'text': translated_strings[CHECK_BUILDING_WITHIN_PLOTS] }, { 'id': 'check_building_units_should_be_within_plots', 'text': translated_strings[CHECK_BUILDING_UNIT_WITHIN_PLOTS] } ] } self.items_dict[QCoreApplication.translate( "QualityDialog", "Logic consistency rules")] = { 'icon': 'tables', 'rules': [{ 'id': 'check_parcel_right_relationship', 'text': translated_strings[CHECK_PARCEL_RIGHT_RELATIONSHIP] }, { 'id': 'find_duplicate_records_in_a_table', 'text': translated_strings[FIND_DUPLICATE_RECORDS_IN_A_TABLE] }, { 'id': 'check_fraction_sum_for_party_groups', 'text': translated_strings[CHECK_FRACTION_SUM_FOR_PARTY_GROUPS] }, { 'id': 'check_department_code_has_two_numerical_characters', 'text': translated_strings[ CHECK_DEPARMENT_CODE_HAS_TWO_NUMERICAL_CHARACTERS] }, { 'id': 'check_municipality_code_has_three_numerical_characters', 'text': translated_strings[ CHECK_MUNICIPALITY_CODE_HAS_THREE_NUMERICAL_CHARACTERS] }, { 'id': 'check_parcel_number_has_30_numerical_characters', 'text': translated_strings[ CHECK_PARCEL_NUMBER_HAS_30_NUMERICAL_CHARACTERS] }, { 'id': 'check_parcel_number_before_has_20_numerical_characters', 'text': translated_strings[ CHECK_PARCEL_NUMBER_BEFORE_HAS_20_NUMERICAL_CHARACTERS] }, { 'id': 'check_col_party_natural_type', 'text': translated_strings[CHECK_COL_PARTY_NATURAL_TYPE] }, { 'id': 'check_col_party_legal_type', 'text': translated_strings[CHECK_COL_PARTY_LEGAL_TYPE] }, { 'id': 'check_parcel_type_and_22_position_of_parcel_number', 'text': translated_strings[ CHECK_PARCEL_TYPE_AND_22_POSITON_OF_PARCEL_NUMBER] }, { 'id': 'check_uebaunit_parcel', 'text': translated_strings[CHECK_UEBAUNIT_PARCEL] }] } self.load_items() def validate_selection_rules(self): # At least one quality rule must have been selected self.buttonBox.button(QDialogButtonBox.Ok).setEnabled( bool(self.trw_quality_rules.selectedItems())) def load_items(self): self.trw_quality_rules.setUpdatesEnabled( False) # Don't render until we're ready self.trw_quality_rules.clear() font = QFont() font.setBold(True) for group, items in self.items_dict.items(): children = [] group_item = QTreeWidgetItem([group]) group_item.setData(0, Qt.BackgroundRole, QBrush(QColor(219, 219, 219, 255))) group_item.setData(0, Qt.FontRole, font) icon = QIcon(":/Asistente-LADM_COL/resources/images/{}.png".format( items['icon'])) group_item.setData(0, Qt.DecorationRole, icon) for rule in items['rules']: rule_item = QTreeWidgetItem([rule['text']]) rule_item.setData(0, Qt.UserRole, rule['id']) children.append(rule_item) group_item.addChildren(children) self.trw_quality_rules.addTopLevelItem(group_item) # Make group items non selectable and expanded for i in range(self.trw_quality_rules.topLevelItemCount()): self.trw_quality_rules.topLevelItem(i).setFlags( Qt.ItemIsEnabled) # Not selectable self.trw_quality_rules.topLevelItem(i).setExpanded(True) self.trw_quality_rules.setUpdatesEnabled(True) # Now render! def accepted(self): # we erase the group error layer every time it runs because we assume that data set changes. self.qgis_utils.remove_error_group_requested.emit() self.quality.initialize_log_dialog_quality() selected_count = len(self.trw_quality_rules.selectedItems()) if selected_count > 0: self.quality.set_count_topology_rules(selected_count) translated_strings = self.translatable_config_strings.get_translatable_config_strings( ) iterator = QTreeWidgetItemIterator(self.trw_quality_rules, QTreeWidgetItemIterator.Selectable) while iterator.value(): item = iterator.value() if item.isSelected(): id = item.data(0, Qt.UserRole) rule_name = item.text(0) # NOTE: Do not remove the named parameters, this is needed for making a decorator that thinks they are # optional happy! if id == 'check_overlaps_in_boundary_points': self.quality.check_overlapping_points( self._db, point_layer_name=self.names.OP_BOUNDARY_POINT_T, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_overlaps_in_control_points': self.quality.check_overlapping_points( self._db, point_layer_name=self.names.OP_CONTROL_POINT_T, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_boundary_points_covered_by_boundary_nodes': self.quality.check_boundary_points_covered_by_boundary_nodes( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_boundary_points_covered_by_plot_nodes': self.quality.check_boundary_points_covered_by_plot_nodes( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_overlaps_in_boundaries': self.quality.check_overlaps_in_boundaries( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_boundaries_are_not_split': self.quality.check_boundaries_are_not_split( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_boundaries_covered_by_plots': self.quality.check_boundaries_covered_by_plots( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_boundary_nodes_covered_by_boundary_points': self.quality.check_boundary_nodes_covered_by_boundary_points( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_dangles_in_boundaries': self.quality.check_dangles_in_boundaries( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_overlaps_in_plots': self.quality.check_overlapping_polygons( self._db, polygon_layer_name=self.names.OP_PLOT_T, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_overlaps_in_buildings': self.quality.check_overlapping_polygons( self._db, polygon_layer_name=self.names.OP_BUILDING_T, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_overlaps_in_rights_of_way': self.quality.check_overlapping_polygons( self._db, polygon_layer_name=self.names.OP_RIGHT_OF_WAY_T, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_plots_covered_by_boundaries': self.quality.check_plots_covered_by_boundaries( self._db, rule_name=rule_name, translated_strings=translated_strings) #elif id == 'check_missing_survey_points_in_buildings': # self.quality.check_missing_survey_points_in_buildings(self._db) elif id == 'check_right_of_way_overlaps_buildings': self.quality.check_right_of_way_overlaps_buildings( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_gaps_in_plots': self.quality.check_gaps_in_plots( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_multipart_in_right_of_way': self.quality.check_multiparts_in_right_of_way( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_plot_nodes_covered_by_boundary_points': self.quality.check_plot_nodes_covered_by_boundary_points( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_buildings_should_be_within_plots': self.quality.check_building_within_plots( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_building_units_should_be_within_plots': self.quality.check_building_unit_within_plots( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'check_parcel_right_relationship': self.quality.check_parcel_right_relationship( self._db, rule_name=rule_name, translated_strings=translated_strings) elif id == 'find_duplicate_records_in_a_table': self.quality.find_duplicate_records_in_a_table( self._db, rule_name=rule_name) elif id == 'check_fraction_sum_for_party_groups': self.quality.check_fraction_sum_for_party_groups( self._db, rule_name=rule_name) elif id == 'check_department_code_has_two_numerical_characters': self.quality.basic_logic_validations( self._db, rule='DEPARTMENT_CODE_VALIDATION', rule_name=rule_name) elif id == 'check_municipality_code_has_three_numerical_characters': self.quality.basic_logic_validations( self._db, rule='MUNICIPALITY_CODE_VALIDATION', rule_name=rule_name) elif id == 'check_parcel_number_has_30_numerical_characters': self.quality.basic_logic_validations( self._db, rule='PARCEL_NUMBER_VALIDATION', rule_name=rule_name) elif id == 'check_parcel_number_before_has_20_numerical_characters': self.quality.basic_logic_validations( self._db, rule='PARCEL_NUMBER_BEFORE_VALIDATION', rule_name=rule_name) elif id == 'check_col_party_natural_type': self.quality.advanced_logic_validations( self._db, rule='COL_PARTY_TYPE_NATURAL_VALIDATION', rule_name=rule_name) elif id == 'check_col_party_legal_type': self.quality.advanced_logic_validations( self._db, rule='COL_PARTY_TYPE_NO_NATURAL_VALIDATION', rule_name=rule_name) elif id == 'check_parcel_type_and_22_position_of_parcel_number': self.quality.advanced_logic_validations( self._db, rule= 'PARCEL_TYPE_AND_22_POSITION_OF_PARCEL_NUMBER_VALIDATION', rule_name=rule_name) elif id == 'check_uebaunit_parcel': self.quality.advanced_logic_validations( self._db, rule='UEBAUNIT_PARCEL_VALIDATION', rule_name=rule_name) iterator += 1 if selected_count > 0: self.quality.generate_log_button() if self.qgis_utils.error_group_exists(): group = self.qgis_utils.get_error_layers_group() # # Check if group layer is empty if group.findLayers(): self.qgis_utils.set_error_group_visibility(True) else: self.qgis_utils.remove_error_group_requested.emit() def rejected(self): pass def select_all(self): self.trw_quality_rules.selectAll() def clear_selection(self): self.trw_quality_rules.clearSelection() def show_help(self): self.qgis_utils.show_help("quality_rules")
class Symbology: ERROR_LAYER = 'error_layer' def __init__(self): self.translatable_config_strings = TranslatableConfigStrings() def get_default_style_group(self, names): return { names.OP_BOUNDARY_T: { QgsWkbTypes.LineGeometry: 'style_boundary' }, names.OP_BOUNDARY_POINT_T: { QgsWkbTypes.PointGeometry: 'style_boundary_point' }, names.OP_SURVEY_POINT_T: { QgsWkbTypes.PointGeometry: 'style_survey_point' }, names.OP_CONTROL_POINT_T: { QgsWkbTypes.PointGeometry: 'style_control_point' }, names.OP_PLOT_T: { QgsWkbTypes.PointGeometry: 'style_plot_point', QgsWkbTypes.PolygonGeometry: 'style_plot_polygon' }, names.OP_BUILDING_T: { QgsWkbTypes.PointGeometry: 'style_building_point', QgsWkbTypes.PolygonGeometry: 'style_building' }, names.OP_BUILDING_UNIT_T: { QgsWkbTypes.PointGeometry: 'style_building_unit_point', QgsWkbTypes.PolygonGeometry: 'style_building_unit_25' }, names.OP_RIGHT_OF_WAY_T: { QgsWkbTypes.PointGeometry: 'style_right_of_way_point', QgsWkbTypes.PolygonGeometry: 'style_right_of_way' }, self.ERROR_LAYER: { QgsWkbTypes.PointGeometry: 'style_point_error', QgsWkbTypes.LineGeometry: 'style_line_error', QgsWkbTypes.PolygonGeometry: 'style_polygon_error' } } def get_supplies_style_group(self, names): return { names.GC_PLOT_T: { QgsWkbTypes.PolygonGeometry: 'style_supplies_plot_polygon' } } def get_custom_error_layers(self): translated_strings = self.translatable_config_strings.get_translatable_config_strings() return { translated_strings[CHECK_BOUNDARIES_COVERED_BY_PLOTS]: { 'es': 'style_boundary_should_be_covered_by_plot_es', 'en': 'style_boundary_should_be_covered_by_plot_en' }, translated_strings[CHECK_PLOTS_COVERED_BY_BOUNDARIES]: { 'es': 'style_plot_should_be_covered_by_boundary_es', 'en': 'style_plot_should_be_covered_by_boundary_en' }, translated_strings[CHECK_BOUNDARY_POINTS_COVERED_BY_BOUNDARY_NODES]: { 'es': 'style_boundary_points_should_be_covered_by_boundary_nodes_es', 'en': 'style_boundary_points_should_be_covered_by_boundary_nodes_en' }, translated_strings[CHECK_BOUNDARY_NODES_COVERED_BY_BOUNDARY_POINTS]: { 'es': 'style_boundary_nodes_should_be_covered_by_boundary_points_es', 'en': 'style_boundary_nodes_should_be_covered_by_boundary_points_en' } } def get_error_layer_name(self): return self.ERROR_LAYER
def __init__(self): self.translatable_config_strings = TranslatableConfigStrings()
class QgisModelBakerUtils(QObject): def __init__(self): QObject.__init__(self) self.logger = Logger() from asistente_ladm_col.config.config_db_supported import ConfigDBsSupported self._dbs_supported = ConfigDBsSupported() self.translatable_config_strings = TranslatableConfigStrings() def get_generator(self, db): if 'QgisModelBaker' in qgis.utils.plugins: tool = self._dbs_supported.get_db_factory( db.engine).get_model_baker_db_ili_mode() QgisModelBaker = qgis.utils.plugins["QgisModelBaker"] generator = QgisModelBaker.get_generator()( tool, db.uri, "smart2", db.schema, pg_estimated_metadata=False) return generator else: self.logger.critical( __name__, QCoreApplication.translate( "AsistenteLADMCOLPlugin", "The QGIS Model Baker plugin is a prerequisite, install it before using LADM-COL Assistant." )) return None def get_model_baker_db_connection(self, db): generator = self.get_generator(db) if generator is not None: return generator._db_connector return None def load_layers(self, db, layer_list): """ Load a selected list of layers from qgis model baker. This call should configure relations and bag of enums between layers being loaded, but not when a layer already loaded has a relation or is part of a bag of enum. For that case, we use a cached set of relations and bags of enums that we get only once per session and configure in the Asistente LADM-COL. """ translated_strings = self.translatable_config_strings.get_translatable_config_strings( ) if 'QgisModelBaker' in qgis.utils.plugins: QgisModelBaker = qgis.utils.plugins["QgisModelBaker"] tool = self._dbs_supported.get_db_factory( db.engine).get_model_baker_db_ili_mode() generator = QgisModelBaker.get_generator()( tool, db.uri, "smart2", db.schema, pg_estimated_metadata=False) layers = generator.layers(layer_list) relations, bags_of_enum = generator.relations(layers, layer_list) legend = generator.legend( layers, ignore_node_names=[translated_strings[ERROR_LAYER_GROUP]]) QgisModelBaker.create_project(layers, relations, bags_of_enum, legend, auto_transaction=False) else: self.logger.critical( __name__, QCoreApplication.translate( "AsistenteLADMCOLPlugin", "The QGIS Model Baker plugin is a prerequisite, install it before using LADM-COL Assistant." )) def get_required_layers_without_load(self, layer_list, db): """ Gets a list of layers from a list of layer names using QGIS Model Baker. Layers are register in QgsProject, but not loaded to the canvas! :param layer_list: list of layers names (e.g., ['lc_terreno', 'lc_lindero']) :param db: db connection :return: list of QgsVectorLayers registered in the project """ layers = list() if 'QgisModelBaker' in qgis.utils.plugins: QgisModelBaker = qgis.utils.plugins["QgisModelBaker"] tool = self._dbs_supported.get_db_factory( db.engine).get_model_baker_db_ili_mode() generator = QgisModelBaker.get_generator()( tool, db.uri, "smart2", db.schema, pg_estimated_metadata=False) model_baker_layers = generator.layers(layer_list) for model_baker_layer in model_baker_layers: layer = model_baker_layer.create( ) # Convert Model Baker layer to QGIS layer QgsProject.instance().addMapLayer( layer, False) # Do not load it to canvas layers.append(layer) else: self.logger.critical( __name__, QCoreApplication.translate( "AsistenteLADMCOLPlugin", "The QGIS Model Baker plugin is a prerequisite, install it before using LADM-COL Assistant." )) return layers def get_layers_and_relations_info(self, db): """ Called once per session, this is used to get information of all relations and bags of enums in the DB and cache it in the Asistente LADM-COL. """ if 'QgisModelBaker' in qgis.utils.plugins: generator = self.get_generator(db) layers = generator.get_tables_info_without_ignored_tables() relations = [ relation for relation in generator.get_relations_info() ] self.logger.debug( __name__, "Relationships before filter: {}".format(len(relations))) self.filter_relations(relations) self.logger.debug( __name__, "Relationships after filter: {}".format(len(relations))) return (layers, relations, {}) else: self.logger.critical( __name__, QCoreApplication.translate( "AsistenteLADMCOLPlugin", "The QGIS Model Baker plugin is a prerequisite, install it before using LADM-COL Assistant." )) return (list(), list(), dict()) def filter_relations(self, relations): """ Modifies the input list of relations, removing elements that meet a condition. :param relations: List of a dict of relations. :return: Nothing, changes the input list of relations. """ to_delete = list() for relation in relations: if relation[QueryNames.REFERENCING_FIELD].startswith( 'uej2_') or relation[ QueryNames.REFERENCING_FIELD].startswith('ue_'): to_delete.append(relation) for idx in to_delete: relations.remove(idx) def get_tables_info_without_ignored_tables(self, db): if 'QgisModelBaker' in qgis.utils.plugins: generator = self.get_generator(db) return generator.get_tables_info_without_ignored_tables() else: self.logger.critical( __name__, QCoreApplication.translate( "AsistenteLADMCOLPlugin", "The QGIS Model Baker plugin is a prerequisite, install it before using LADM-COL Assistant." )) def get_first_index_for_layer_type( self, layer_type, group=QgsProject.instance().layerTreeRoot()): if 'QgisModelBaker' in qgis.utils.plugins: import QgisModelBaker return QgisModelBaker.utils.qgis_utils.get_first_index_for_layer_type( layer_type, group) return None @staticmethod def get_suggested_index_for_layer(layer, group): if 'QgisModelBaker' in qgis.utils.plugins: import QgisModelBaker return QgisModelBaker.utils.qgis_utils.get_suggested_index_for_layer( layer, group) return None
class QualityRuleManager(QObject, metaclass=SingletonQObject): def __init__(self): self.logger = Logger() self.__quality_rules_data = QualityRuleConfig.get_quality_rules_config( ) self.__translated_strings = TranslatableConfigStrings( ).get_translatable_config_strings() self._quality_rule_groups = dict() self.__quality_rules = dict() self.role_registry = RoleRegistry() self._initialize_quality_rule_manager() def _initialize_quality_rule_manager(self): for group_k, group_v in self.__quality_rules_data.items(): self._quality_rule_groups[group_k] = group_v[QUALITY_GROUP_NAME] for rule_k, rule_v in group_v[QUALITY_RULES].items(): self.__quality_rules[rule_k] = QualityRule(rule_v) self.logger.info( __name__, "{} quality rules registered!".format(len(self.__quality_rules))) def get_quality_rule(self, rule_key): """ Returns the QualityRule object corresponding to a rule code. :param rule_key: rule key :return: QualityRule """ return self.__quality_rules.get(rule_key) def get_quality_rule_group_name(self, group_key): """ Returns a quality rule group name. :param group_key: Group key :return: Group name if the group key is found. Otherwise, None. """ return self._quality_rule_groups.get(group_key) def get_quality_rules_by_group(self, enum_group=None): """ Returns all rules in a given group. If no enum_group is given, it returns the whole set of rules classified by group. :param enum_group: EnumQualityRule.Point|Line|Polygon|Logic :return: dict of rules """ quality_rules_group = dict() role_key = self.role_registry.get_active_role() role_quality_rules = self.role_registry.get_role_quality_rules( role_key) if enum_group: quality_rules_group = { k_rule: v_rule for k_rule, v_rule in self.__quality_rules.items() if k_rule in enum_group and k_rule in role_quality_rules } else: quality_rules_group[EnumQualityRule.Point] = dict() quality_rules_group[EnumQualityRule.Line] = dict() quality_rules_group[EnumQualityRule.Polygon] = dict() quality_rules_group[EnumQualityRule.Logic] = dict() for k_quality_rule, v_quality_rule in self.__quality_rules.items(): if k_quality_rule in role_quality_rules: if k_quality_rule in EnumQualityRule.Point: quality_rules_group[EnumQualityRule.Point][ k_quality_rule] = v_quality_rule elif k_quality_rule in EnumQualityRule.Line: quality_rules_group[EnumQualityRule.Line][ k_quality_rule] = v_quality_rule elif k_quality_rule in EnumQualityRule.Polygon: quality_rules_group[EnumQualityRule.Polygon][ k_quality_rule] = v_quality_rule elif k_quality_rule in EnumQualityRule.Logic: quality_rules_group[EnumQualityRule.Logic][ k_quality_rule] = v_quality_rule self.logger.debug( __name__, "Quality Rules for role '{}': {}".format( role_key, ", ".join([str(rqr.value) for rqr in role_quality_rules]))) return quality_rules_group def get_error_message(self, error_code): return self.__translated_strings.get(error_code)
class QualityRuleController(QObject): open_report_called = pyqtSignal(QualityRuleResultLog) # log result quality_rule_layer_removed = pyqtSignal() refresh_error_layer_symbology = pyqtSignal(QgsVectorLayer) total_progress_changed = pyqtSignal(int) # Progress value def __init__(self, db): QObject.__init__(self) self.app = AppInterface() self.logger = Logger() self.__db = db self.__tr_dict = TranslatableConfigStrings( ).get_translatable_config_strings() # Hierarquical dict of qrs and qr groups self.__qrs_tree_data = dict() # {type: {qr_key1: qr_obj1, ...}, ...} # Hierarquical dict of qrs and qr groups with general results self.__general_results_tree_data = dict( ) # {type: {qr_obj1: qr_results1, ...}, ...} # Hierarchical dict of qrs and their corresponding error instances # feature1: {uuids, rel_uuids, error_type, nombre_ili_obj, details, values, fixed, exception, geom_fks} self.__error_results_data = dict() # {qr_key1: {t_id1: feature1}} self.__qr_results_dir_path = '' # Dir path where results will be stored self.__selected_qrs = list() # QRs to be validated (at least 1) self.__selected_qr = None # QR selected by the user to show its corresponding errors (exactly 1) self.__qr_engine = None # Once set, we can reuse it self.__qrs_results = None # QualityRulesExecutionResult object # To cache layers from QR DB self.__error_layer = None self.__point_layer = None self.__line_layer = None self.__polygon_layer = None # Cache by t_id (built on demand): {t_id1: 'Error', t_id2: 'Corregido', t_id3: 'Exception'} self.__error_state_dict = dict() def get_tr_string(self, key): return self.__tr_dict.get(key, key) def validate_qrs(self): if self.__qr_engine is None: self.__qr_engine = QualityRuleEngine(self.__db, self.__selected_qrs, self.app.settings.tolerance, self.__qr_results_dir_path) self.__qr_engine.progress_changed.connect( self.total_progress_changed) else: self.__qr_engine.initialize(self.__db, self.__selected_qrs, self.app.settings.tolerance, self.__qr_results_dir_path) #self.__qr_engine.qr_logger.show_message_emitted.connect(self.show_log_quality_message) #self.__qr_engine.qr_logger.show_button_emitted.connect(self.show_log_quality_button) #self.__qr_engine.qr_logger.set_initial_progress_emitted.connect(self.set_log_quality_initial_progress) #self.__qr_engine.qr_logger.set_final_progress_emitted.connect(self.set_log_quality_final_progress) use_roads = bool(QSettings().value( 'Asistente-LADM-COL/quality/use_roads', DEFAULT_USE_ROADS_VALUE, bool)) options = {QR_IGACR3006: {'use_roads': use_roads}} res, msg, qrs_res = self.__qr_engine.validate_quality_rules(options) if not res: return res, msg, None self.__qrs_results = qrs_res self.__connect_layer_willbedeleted_signals( ) # Note: Call it after validate_quality_rules! res_u, msg_u, output_qr_dir = QualityErrorDBUtils.get_quality_validation_output_path( self.__qr_results_dir_path, self.__qr_engine.get_timestamp()) if len(self.__selected_qrs) == 1: pre_text = QCoreApplication.translate( "QualityRules", "The quality rule was checked!") else: pre_text = QCoreApplication.translate( "QualityRules", "All the {} quality rules were checked!").format( len(self.__selected_qrs)) post_text = QCoreApplication.translate( "QualityRules", "Both a PDF report and a GeoPackage database with errors can be found in <a href='file:///{}'>{}</a>." ).format(normalize_local_url(output_qr_dir), output_qr_dir) self.logger.success_msg(__name__, "{} {}".format(pre_text, post_text)) self.__emit_refresh_error_layer_symbology() return res, msg, self.__qrs_results def __connect_layer_willbedeleted_signals(self): """ Iterate QR DB layers from the layer tree and connect their layerwillberemoved signals. If a QR DB layer is removed, we'll react in the GUI. """ group = QualityErrorDBUtils.get_quality_error_group( self.__qr_engine.get_timestamp()) if group: for tree_layer in group.findLayers(): try: tree_layer.layer().willBeDeleted.disconnect( self.quality_rule_layer_removed) except: pass tree_layer.layer().willBeDeleted.connect( self.quality_rule_layer_removed) def disconnect_layer_willberemoved_signals(self): group = QualityErrorDBUtils.get_quality_error_group( self.__qr_engine.get_timestamp(), False) if group: for tree_layer in group.findLayers(): try: tree_layer.layer().willBeDeleted.disconnect( self.quality_rule_layer_removed) except: pass def get_qr_result(self, qr_key): """ Return the QRExecutionResult object for the given qr_key. It first attempts to find it in the __qrs_results dict, but, chances are, the whole set of QRs hasn't been validated when this method is called, so, as a last resort, we go for the tree_data, which is updated each time a QR gets its result. """ if self.__qrs_results is not None: return self.__qrs_results.result(qr_key) for type, qr_dict in self.__general_results_tree_data.items(): for k, v in qr_dict.items(): if k.id() == qr_key: return self.__general_results_tree_data[type][k] return None def __reset_qrs_results(self): # To be used when we are returning to select QRs (i.e., to the initial panel) self.__qrs_results = None def __get_qrs_per_role_and_models(self): return QualityRuleRegistry().get_qrs_per_role_and_models(self.__db) def load_tree_data(self, mode): """ Builds a hierarchical dict by qr type: {qr_type1: {qr_key1: qr_obj1, ...}, ...} Tree data for panel 1. :params mode: Value from EnumQualityRulePanelMode (either VALIDATE or READ). For VALIDATE we load QRs from registry (filtered by role and current db models). For READ we load QRs from the DB itself. """ if mode == EnumQualityRulePanelMode.VALIDATE: qrs = self.__get_qrs_per_role_and_models( ) # Dict of qr key and qr objects. else: qrs = dict() # TODO: Read QRs from the QR DB for qr_key, qr_obj in qrs.items(): type = qr_obj.type() if type not in self.__qrs_tree_data: self.__qrs_tree_data[type] = {qr_key: qr_obj} else: self.__qrs_tree_data[type][qr_key] = qr_obj def get_qrs_tree_data(self): return self.__qrs_tree_data def set_qr_dir_path(self, path): self.__qr_results_dir_path = path def set_selected_qrs(self, selected_qrs): # We sort them because the engine needs the QRs sorted for the PDF report for type, qr_dict in self.__qrs_tree_data.items(): for qr_key, qr_obj in qr_dict.items(): if qr_key in selected_qrs: self.__selected_qrs.append(qr_key) def get_selected_qrs(self): return self.__selected_qrs def __reset_selected_qrs(self): # To be used when we are returning to select QRs (i.e., to the initial panel) self.__selected_qrs = list() def reset_vars_for_general_results_panel(self): # Initialize variables when we leave the general results panel self.__reset_general_results_tree_data() self.__reset_selected_qrs() self.__reset_qrs_results() self.__reset_layers() # Call it before removing QR DB group to avoid triggering parent.layer_removed() slot again. self.disconnect_layer_willberemoved_signals() # When we leave the GRP, we remove the QR DB group from layer tree, # because we won't be working anymore with that QR DB QualityErrorDBUtils.remove_quality_error_group( self.__qr_engine.get_timestamp()) def reset_vars_for_error_results_panel(self): # Initialize variables when we leave the error results panel self.__reset_error_results_data() self.__reset_selected_qr() self.__reset_error_state_dict() self.__reset_layers() def load_general_results_tree_data(self): """ Builds a hierarchical dict by qr type: {type: {qr_obj1: qr_results1, ...}, ...} Tree data for panel 2. """ for type, qr_dict in self.__qrs_tree_data.items(): for qr_key, qr_obj in qr_dict.items(): if qr_key in self.__selected_qrs: if type not in self.__general_results_tree_data: self.__general_results_tree_data[type] = {qr_obj: None} else: self.__general_results_tree_data[type][qr_obj] = None def get_general_results_tree_data(self): return self.__general_results_tree_data def __reset_general_results_tree_data(self): # To be used when we are returning to select QRs (i.e., to the initial panel) self.__general_results_tree_data = dict() def set_qr_validation_result(self, qr, qr_result): """ When a QR has its validation result after validation, we can store it in our custom dict by using this method. """ for type, qr_dict in self.__general_results_tree_data.items(): for k, v in qr_dict.items(): if k == qr: self.__general_results_tree_data[type][k] = qr_result def open_report(self): if self.__qr_engine: log_result = self.__qr_engine.qr_logger.get_log_result() self.open_report_called.emit(log_result) def set_selected_qr(self, qr_key): self.__selected_qr = QualityRuleRegistry().get_quality_rule(qr_key) return self.__selected_qr is not None # We should not be able to continue if we don't find the QR def get_selected_qr(self): return self.__selected_qr def load_error_results_data(self): """ Go to table and bring data to the dict. We should keep this dict updated with changes from the user. From time to time we reflect this dict changes in the original data source. """ db = self.__qr_engine.get_db_quality() names = db.names layers = {names.ERR_QUALITY_ERROR_T: None, names.ERR_RULE_TYPE_T: None} self.app.core.get_layers(db, layers, load=False) if not layers: self.logger.critical( __name__, "Quality error layers ('{}') not found!".format(",".join( list(layers.keys())))) return # First go for the selected quality error's t_id features = LADMData.get_features_from_t_ids( layers[names.ERR_RULE_TYPE_T], names.ERR_RULE_TYPE_T_CODE_F, [self.__selected_qr.id()]) t_id = features[0][names.T_ID_F] if features else None if not t_id: self.logger.critical( __name__, "Quality error rule ('{}') not found!".format( self.__selected_qr.id())) return # Now go for all features that match the selected quality rule features = LADMData.get_features_from_t_ids( layers[names.ERR_QUALITY_ERROR_T], names.ERR_QUALITY_ERROR_T_RULE_TYPE_F, [t_id]) self.__error_results_data[self.__selected_qr.id()] = { feature[names.T_ID_F]: feature for feature in features } def get_error_results_data(self): # Get the subdict {t_id1: feature1, ...} corresponding to selected qr return self.__error_results_data.get( self.__selected_qr.id() if self.__selected_qr else '', dict()) def __reset_error_results_data(self): # To be used when we are returning to select QR results (i.e., to the general results panel) self.__error_results_data = dict() def error_t_id(self, feature): return feature[self.__qr_engine.get_db_quality().names.T_ID_F] def is_fixed_error(self, feature): db = self.__qr_engine.get_db_quality() state_t_id = feature[db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F] return self.__get_error_state_value( state_t_id) == LADMNames.ERR_ERROR_STATE_D_FIXED_V def is_error(self, feature): db = self.__qr_engine.get_db_quality() state_t_id = feature[db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F] return self.__get_error_state_value( state_t_id) == LADMNames.ERR_ERROR_STATE_D_ERROR_V def is_exception(self, feature): db = self.__qr_engine.get_db_quality() state_t_id = feature[db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F] return self.__get_error_state_value( state_t_id) == LADMNames.ERR_ERROR_STATE_D_EXCEPTION_V def uuid_objs(self, feature): return "\n".join(feature[self.__qr_engine.get_db_quality().names. ERR_QUALITY_ERROR_T_OBJECT_IDS_F]) def ili_obj_name(self, feature): ili_name = feature[self.__qr_engine.get_db_quality().names. ERR_QUALITY_ERROR_T_ILI_NAME_F] return ili_name.split(".")[-1] if ili_name else '' def error_type_code_and_display(self, feature): db = self.__qr_engine.get_db_quality() names = db.names layer = self.app.core.get_layer(db, names.ERR_ERROR_TYPE_T, load=False) features = LADMData.get_features_from_t_ids( layer, names.T_ID_F, [feature[db.names.ERR_QUALITY_ERROR_T_ERROR_TYPE_F]]) # tid return features[0][ names. ERR_ERROR_TYPE_T_CODE_F] if features else QCoreApplication.translate( "QualityRules", "No error type found!" ), features[0][ names. ERR_ERROR_TYPE_T_DESCRIPTION_F] if features else QCoreApplication.translate( "QualityRules", "No error description found!") def error_details_and_values(self, feature): res = "" db = self.__qr_engine.get_db_quality() details = feature[db.names.ERR_QUALITY_ERROR_T_DETAILS_F] values = feature[db.names.ERR_QUALITY_ERROR_T_VALUES_F] if details: res = details if values: try: res_values = json.loads(values) if type(res_values) is dict: items = "" for k, v in res_values.items(): items = res + "{}: {}\n".format(k, v) res_values = items.strip() else: res_values = str(res_values) except json.decoder.JSONDecodeError as e: res_values = values res = res_values if not res else "{}\n\n{}".format(res, res_values) return res def error_state(self, feature): db = self.__qr_engine.get_db_quality() state_t_id = feature[db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F] return self.__get_error_state_value(state_t_id) def __get_error_state_value(self, state_t_id): if state_t_id not in self.__error_state_dict: db = self.__qr_engine.get_db_quality() self.__error_state_dict[state_t_id] = LADMData( ).get_domain_value_from_code(db, db.names.ERR_ERROR_STATE_D, state_t_id) return self.__error_state_dict.get(state_t_id, "") def __get_error_state_t_id(self, state_value): # Use __error_state_dict to read cached values, but this time we have the value, # not the key, so check in dict values and if not found, go for its t_id if state_value not in self.__error_state_dict.values(): db = self.__qr_engine.get_db_quality() t_id = LADMData().get_domain_code_from_value( db, db.names.ERR_ERROR_STATE_D, state_value) self.__error_state_dict[t_id] = state_value # Get key by value in a dict: return next((k for k in self.__error_state_dict if self.__error_state_dict[k] == state_value), None) def __get_error_layer(self): if not self.__error_layer: db = self.__qr_engine.get_db_quality() self.__error_layer = self.app.core.get_layer( db, db.names.ERR_QUALITY_ERROR_T) return self.__error_layer def __get_point_error_layer(self): if not self.__point_layer: db = self.__qr_engine.get_db_quality() self.__point_layer = self.app.core.get_layer( db, db.names.ERR_POINT_T) return self.__point_layer def __get_line_error_layer(self): if not self.__line_layer: db = self.__qr_engine.get_db_quality() self.__line_layer = self.app.core.get_layer( db, db.names.ERR_LINE_T) return self.__line_layer def __get_polygon_error_layer(self): if not self.__polygon_layer: db = self.__qr_engine.get_db_quality() self.__polygon_layer = self.app.core.get_layer( db, db.names.ERR_POLYGON_T) return self.__polygon_layer def __reset_layers(self): # To be used when we are returning to select QR results (i.e., to the general results panel) self.__error_layer = None self.__point_layer = None self.__line_layer = None self.__polygon_layer = None def __reset_selected_qr(self): # To be used when we are returning to select QR results (i.e., to the general results panel) self.__selected_qr = None def __reset_error_state_dict(self): # To be used when we are returning to select QR results (i.e., to the general results panel) self.__error_state_dict = dict() def __error_related_geometries(self, error_t_ids): # Prefered geometry types are polygons, lines, points, in that order db = self.__qr_engine.get_db_quality() error_data = self.get_error_results_data() dict_layer_fids = dict() for error_t_id in error_t_ids: feature = error_data.get(error_t_id, None) if feature: polygon = feature[db.names.ERR_QUALITY_ERROR_T_POLYGON_F] line = feature[db.names.ERR_QUALITY_ERROR_T_LINE_F] point = feature[db.names.ERR_QUALITY_ERROR_T_POINT_F] if polygon: if 'polygon' in dict_layer_fids: dict_layer_fids['polygon']['fids'].append(polygon) else: dict_layer_fids['polygon'] = { 'layer': self.__get_polygon_error_layer(), 'fids': [polygon] } elif line: if 'line' in dict_layer_fids: dict_layer_fids['line']['fids'].append(line) else: dict_layer_fids['line'] = { 'layer': self.__get_line_error_layer(), 'fids': [line] } elif point: if 'point' in dict_layer_fids: dict_layer_fids['point']['fids'].append(point) else: dict_layer_fids['point'] = { 'layer': self.__get_point_error_layer(), 'fids': [point] } return dict_layer_fids def highlight_geometries(self, t_ids): res_geometries = self.__error_related_geometries(t_ids) if res_geometries: # First zoom to geometries if len(res_geometries) == 1: # Only one geometry type related for geom_type, dict_layer_fids in res_geometries.items( ): # We know this will be called just once self.app.gui.zoom_to_feature_ids(dict_layer_fids['layer'], dict_layer_fids['fids']) else: # Multiple geometry types were found, so combine the extents and then zoom to it combined_extent = QgsRectangle() for geom_type, dict_layer_fids in res_geometries.items(): combined_extent.combineExtentWith( self.app.core.get_extent_from_feature_ids( dict_layer_fids['layer'], dict_layer_fids['fids'])) self.app.gui.zoom_to_extent(combined_extent) # Now highlight geometries for geom_type, dict_layer_fids in res_geometries.items(): self.app.gui.flash_features(dict_layer_fids['layer'], dict_layer_fids['fids'], flashes=5) def get_uuids_display_name(self): names = self.__qr_engine.get_db_quality().names res = self.__selected_qr.field_mapping(names).get( names.ERR_QUALITY_ERROR_T_OBJECT_IDS_F, '') return res if res else QCoreApplication.translate( "QualityRules", "UUIDs") def set_fixed_error(self, error_t_id, fixed): # Save to the intermediate dict of data and to the underlying data source whether an error is fixed or not db = self.__qr_engine.get_db_quality() idx_state = self.__get_error_layer().fields().indexOf( db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F) value = LADMNames.ERR_ERROR_STATE_D_FIXED_V if fixed else LADMNames.ERR_ERROR_STATE_D_ERROR_V fixed_or_error_t_id = self.__get_error_state_t_id(value) if fixed_or_error_t_id is None: self.logger.critical( __name__, "The error state t_id couldn't be found for value '{}'!". format(value)) return # Save to dict self.get_error_results_data()[error_t_id].setAttribute( idx_state, fixed_or_error_t_id) fids = LADMData.get_fids_from_key_values(self.__get_error_layer(), db.names.T_ID_F, [error_t_id]) # Save to underlying data source if fids: res = self.__get_error_layer().dataProvider( ).changeAttributeValues( {fids[0]: { idx_state: fixed_or_error_t_id }}) if not res: self.logger.critical(__name__, "Error modifying the error state value!") else: self.logger.critical( __name__, "Error with t_id '' not found!".format(error_t_id)) def __emit_refresh_error_layer_symbology(self): if self.__get_point_error_layer().featureCount(): self.refresh_error_layer_symbology.emit( self.__get_point_error_layer()) if self.__get_line_error_layer().featureCount(): self.refresh_error_layer_symbology.emit( self.__get_line_error_layer()) if self.__get_polygon_error_layer().featureCount(): self.refresh_error_layer_symbology.emit( self.__get_polygon_error_layer())
def error_group_exists(self): root = QgsProject.instance().layerTreeRoot() translated_strings = TranslatableConfigStrings.get_translatable_config_strings( ) return root.findGroup( translated_strings[ERROR_LAYER_GROUP]) is not None
def __init__(self): QObject.__init__(self) self.logger = Logger() from asistente_ladm_col.config.config_db_supported import ConfigDBsSupported self._dbs_supported = ConfigDBsSupported() self.translatable_config_strings = TranslatableConfigStrings()
def decorated_function(*args, **kwargs): """ For all db_source in the context check: 1. that db connection is valid, otherwise show message with button that opens db connection setting dialog. 2. that db connection parameters are equal to db source connection parameters (in QSettings), otherwise show message with two buttons "Use DB from QSettings" and "Use the current connection". """ # Check if current connection is valid and disable access if not inst = args[0] context = args[1] db_connections_in_conflict = list() invalid_db_connections = list() for db_source in context.get_db_sources(): db = inst.conn_manager.get_db_connector_from_source(db_source) qsettings_db = inst.conn_manager.get_db_connection_from_qsettings( db_source) if db.equals(qsettings_db): res, code, msg = db.test_connection() if not res: invalid_db_connections.append(db_source) widget = inst.iface.messageBar().createMessage( "Asistente LADM-COL", QCoreApplication.translate( "AsistenteLADMCOLPlugin", "The {} DB connection is not valid. Details: {}"). format(Tr.tr_db_source(db_source), msg)) button = QPushButton(widget) button.setText( QCoreApplication.translate("AsistenteLADMCOLPlugin", "Settings")) settings_context = SettingsContext(db_source) settings_context.title = QCoreApplication.translate( "AsistenteLADMCOLPlugin", "{} Connection Settings").format( Tr.tr_db_source(db_source)) settings_context.tip = QCoreApplication.translate( "AsistenteLADMCOLPlugin", "Configure a valid DB connection for {} source." ).format(Tr.tr_db_source(db_source)) settings_context.tab_pages_list = [ SETTINGS_CONNECTION_TAB_INDEX ] button.pressed.connect( partial(inst.show_settings_clear_message_bar, settings_context)) widget.layout().addWidget(button) inst.iface.messageBar().pushWidget(widget, Qgis.Warning, 15) inst.logger.warning( __name__, QCoreApplication.translate( "AsistenteLADMCOLPlugin", "A dialog/tool couldn't be opened/executed, connection to DB was not valid." )) else: # Update cache if there is none and source is Collected if db_source == COLLECTED_DB_SOURCE: if not inst.app.core.get_cached_layers( ) and not inst.app.core.get_cached_relations(): inst.app.core.cache_layers_and_relations( db, ladm_col_db=True, db_source=None) else: db_connections_in_conflict.append(db_source) msg = QCoreApplication.translate( "AsistenteLADMCOLPlugin", "Your current {} DB does not match with the one registered in QSettings. Which connection would you like to use?" ).format(Tr.tr_db_source(db_source)) widget = inst.iface.messageBar().createMessage( "Asistente LADM-COL", msg) btn_current_connection = QPushButton(widget) btn_current_connection.setText( QCoreApplication.translate("AsistenteLADMCOLPlugin", "Use the current connection")) btn_current_connection.pressed.connect( partial(inst.use_current_db_connection, db_source)) btn_update_connection = QPushButton(widget) btn_update_connection.setText( QCoreApplication.translate("AsistenteLADMCOLPlugin", "Use DB from QSettings")) btn_update_connection.pressed.connect( partial(inst.update_db_connection_from_qsettings, db_source)) widget.layout().addWidget(btn_current_connection) widget.layout().addWidget(btn_update_connection) inst.iface.messageBar().pushWidget(widget, Qgis.Warning) inst.logger.warning(__name__, msg) if db_connections_in_conflict or invalid_db_connections: return # If any db connection changed or it's invalid, we don't return the decorated function else: func_to_decorate(*args, **kwargs)