Пример #1
0
    def add_spatial_unit_layer(self):
        """
        Adds spatial unit layers on the map canvas when
        View Social Tenure is launched.
        :return: None
        :rtype: NoneType
        """
        sp_unit_manager = SpatialUnitManagerDockWidget(self._plugin.iface,
                                                       self._plugin)
        spatial_unit_lyr = sp_unit_manager.entity_layer_names(
            self.spatial_unit)

        for lyr in spatial_unit_lyr:
            sp_unit_manager.add_layer_by_name(lyr)
Пример #2
0
    def draw_spatial_unit(self, spatial_unit, model):
        """
        Draw geometry of the given model in the respective local and web views.
        :param model: Source model whose geometry will be drawn.
        :type model: object
        :param clear_existing: Clears any existing features prior to adding the
        new features.
        :type clear_existing: bool
        """
        if model is None:
            msg = QApplication.translate(
                "SpatialPreview", "Data model is empty, the spatial "
                "unit cannot be rendered.")
            QMessageBox.critical(
                self,
                QApplication.translate("SpatialPreview",
                                       "Spatial Unit Preview"), msg)

            return

        table_name = spatial_unit.name
        if not pg_table_exists(table_name):
            msg = QApplication.translate(
                "SpatialPreview", "The spatial unit data source could "
                "not be retrieved, the feature cannot "
                "be rendered.")
            QMessageBox.critical(
                self,
                QApplication.translate("SpatialPreview",
                                       "Spatial Unit Preview"), msg)

            return

        sp_unit_manager = SpatialUnitManagerDockWidget(self.iface())
        spatial_cols = sp_unit_manager.geom_columns(spatial_unit)

        geom, geom_col = None, ""
        sc_obj = None
        for sc in spatial_cols:

            db_geom = getattr(model, sc.name)
            #Use the first non-empty geometry
            # value in the collection
            if not db_geom is None:
                sc_obj = sc
                geom_col = sc.name
                geom = db_geom
        QApplication.processEvents()

        lyr = sp_unit_manager.geom_col_layer_name(table_name, sc_obj)

        sp_unit_manager.add_layer_by_name(lyr)

        if geom is not None:

            self.highlight_spatial_unit(spatial_unit, geom,
                                        self.local_map.canvas)
            self._web_spatial_loader.add_overlay(model, geom_col)
Пример #3
0
    def draw_spatial_unit(self, spatial_unit, model):
        """
        Draw geometry of the given model in the respective local and web views.
        :param model: Source model whose geometry will be drawn.
        :type model: object
        :param clear_existing: Clears any existing features prior to adding the
        new features.
        :type clear_existing: bool
        """
        if model is None:
            msg = QApplication.translate("SpatialPreview",
                                         "Data model is empty, the spatial "
                                         "unit cannot be rendered.")
            QMessageBox.critical(self,
                                 QApplication.translate(
                                    "SpatialPreview",
                                    "Spatial Unit Preview"),
                                 msg)

            return

        table_name = spatial_unit.name
        if not pg_table_exists(table_name):
            msg = QApplication.translate("SpatialPreview",
                                         "The spatial unit data source could "
                                         "not be retrieved, the feature cannot "
                                         "be rendered.")
            QMessageBox.critical(
                self,
                QApplication.translate(
                    "SpatialPreview",
                    "Spatial Unit Preview"),
                msg
            )

            return

        sp_unit_manager = SpatialUnitManagerDockWidget(self.iface())
        spatial_cols = sp_unit_manager.geom_columns(spatial_unit)

        geom, geom_col = None, ""
        sc_obj = None
        for sc in spatial_cols:

            db_geom = getattr(model, sc.name)
            #Use the first non-empty geometry
            # value in the collection
            if not db_geom is None:
                sc_obj = sc
                geom_col = sc.name
                geom = db_geom
        QApplication.processEvents()

        lyr = sp_unit_manager.geom_col_layer_name(
            table_name, sc_obj
        )

        sp_unit_manager.add_layer_by_name(lyr)

        if geom is not None:

            self.highlight_spatial_unit(
                spatial_unit, geom, self.local_map.canvas
            )
            self._web_spatial_loader.add_overlay(
                model, geom_col
            )
Пример #4
0
    def __init__(self, plugin):
        QMainWindow.__init__(self, plugin.iface.mainWindow())
        self.setupUi(self)

        self.btnSearch.setIcon(GuiUtils.get_icon('search.png'))
        self.btnClearSearch.setIcon(GuiUtils.get_icon('reset.png'))

        self._plugin = plugin

        self.search_done = False
        # self.tbPropertyPreview.set_iface(self._plugin.iface)
        QTimer.singleShot(
            100, lambda: self.tbPropertyPreview.set_iface(self._plugin.iface))

        self.curr_profile = current_profile()

        self.spatial_units = self.curr_profile.social_tenure.spatial_units
        # Center me
        self.move(QDesktopWidget().availableGeometry().center() -
                  self.frameGeometry().center())
        self.sp_unit_manager = SpatialUnitManagerDockWidget(
            self._plugin.iface, self._plugin)
        self.geom_cols = []
        for spatial_unit in self.spatial_units:
            each_geom_col = self.sp_unit_manager.geom_columns(spatial_unit)
            self.geom_cols.extend(each_geom_col)

        # Configure notification bar
        self._notif_search_config = NotificationBar(self.vl_notification)

        # set whether currently logged in user has
        # permissions to edit existing STR records
        self._can_edit = self._plugin.STRCntGroup.canUpdate()
        self._can_delete = self._plugin.STRCntGroup.canDelete()
        self._can_create = self._plugin.STRCntGroup.canCreate()
        # Variable used to store a reference to the
        # currently selected social tenure relationship
        # when displaying documents in the supporting documents tab window.
        # This ensures that there are no duplicates
        # when the same item is selected over and over again.

        self._strID = None
        self.removed_docs = None
        # Used to store the root hash of the currently selected node.
        self._curr_rootnode_hash = ""

        self.str_model, self.str_doc_model = entity_model(
            self.curr_profile.social_tenure, False, True)

        self._source_doc_manager = SourceDocumentManager(
            self.curr_profile.social_tenure.supporting_doc, self.str_doc_model,
            self)

        self._source_doc_manager.documentRemoved.connect(
            self.onSourceDocumentRemoved)

        self._source_doc_manager.setEditPermissions(False)

        self.initGui()
        self.add_spatial_unit_layer()
        self.details_tree_view = DetailsTreeView(iface, self._plugin, self)
        # else:
        #     self.details_tree_view = self._plugin.details_tree_view
        self.details_tree_view.activate_feature_details(True)
        self.details_tree_view.add_tree_view()
        self.details_tree_view.model.clear()

        count = pg_table_count(self.curr_profile.social_tenure.name)
        self.setWindowTitle(
            self.tr('{}{}'.format(self.windowTitle(),
                                  '- ' + str(count) + ' rows')))

        self.active_spu_id = -1

        self.toolBox.setStyleSheet('''
            QToolBox::tab {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #EDEDED, stop: 0.4 #EDEDED,
                    stop: 0.5 #EDEDED, stop: 1.0 #D3D3D3
                );
                border-radius: 2px;
                border-style: outset;
                border-width: 2px;
                height: 100px;
                border-color: #C3C3C3;
            }

            QToolBox::tab:selected {
                font: italic;
            }
            ''')

        self.details_tree_view.view.setStyleSheet('''
            QTreeView:!active {
                selection-background-color: #72a6d9;
            }
            ''')
Пример #5
0
class ViewSTRWidget(WIDGET, BASE):
    """
    Search and browse the social tenure relationship
    of all participating entities.
    """
    def __init__(self, plugin):
        QMainWindow.__init__(self, plugin.iface.mainWindow())
        self.setupUi(self)

        self.btnSearch.setIcon(GuiUtils.get_icon('search.png'))
        self.btnClearSearch.setIcon(GuiUtils.get_icon('reset.png'))

        self._plugin = plugin

        self.search_done = False
        # self.tbPropertyPreview.set_iface(self._plugin.iface)
        QTimer.singleShot(
            100, lambda: self.tbPropertyPreview.set_iface(self._plugin.iface))

        self.curr_profile = current_profile()

        self.spatial_units = self.curr_profile.social_tenure.spatial_units
        # Center me
        self.move(QDesktopWidget().availableGeometry().center() -
                  self.frameGeometry().center())
        self.sp_unit_manager = SpatialUnitManagerDockWidget(
            self._plugin.iface, self._plugin)
        self.geom_cols = []
        for spatial_unit in self.spatial_units:
            each_geom_col = self.sp_unit_manager.geom_columns(spatial_unit)
            self.geom_cols.extend(each_geom_col)

        # Configure notification bar
        self._notif_search_config = NotificationBar(self.vl_notification)

        # set whether currently logged in user has
        # permissions to edit existing STR records
        self._can_edit = self._plugin.STRCntGroup.canUpdate()
        self._can_delete = self._plugin.STRCntGroup.canDelete()
        self._can_create = self._plugin.STRCntGroup.canCreate()
        # Variable used to store a reference to the
        # currently selected social tenure relationship
        # when displaying documents in the supporting documents tab window.
        # This ensures that there are no duplicates
        # when the same item is selected over and over again.

        self._strID = None
        self.removed_docs = None
        # Used to store the root hash of the currently selected node.
        self._curr_rootnode_hash = ""

        self.str_model, self.str_doc_model = entity_model(
            self.curr_profile.social_tenure, False, True)

        self._source_doc_manager = SourceDocumentManager(
            self.curr_profile.social_tenure.supporting_doc, self.str_doc_model,
            self)

        self._source_doc_manager.documentRemoved.connect(
            self.onSourceDocumentRemoved)

        self._source_doc_manager.setEditPermissions(False)

        self.initGui()
        self.add_spatial_unit_layer()
        self.details_tree_view = DetailsTreeView(iface, self._plugin, self)
        # else:
        #     self.details_tree_view = self._plugin.details_tree_view
        self.details_tree_view.activate_feature_details(True)
        self.details_tree_view.add_tree_view()
        self.details_tree_view.model.clear()

        count = pg_table_count(self.curr_profile.social_tenure.name)
        self.setWindowTitle(
            self.tr('{}{}'.format(self.windowTitle(),
                                  '- ' + str(count) + ' rows')))

        self.active_spu_id = -1

        self.toolBox.setStyleSheet('''
            QToolBox::tab {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #EDEDED, stop: 0.4 #EDEDED,
                    stop: 0.5 #EDEDED, stop: 1.0 #D3D3D3
                );
                border-radius: 2px;
                border-style: outset;
                border-width: 2px;
                height: 100px;
                border-color: #C3C3C3;
            }

            QToolBox::tab:selected {
                font: italic;
            }
            ''')

        self.details_tree_view.view.setStyleSheet('''
            QTreeView:!active {
                selection-background-color: #72a6d9;
            }
            ''')

    def add_tool_buttons(self):
        """
        Add toolbar buttons of add, edit and delete buttons.
        :return: None
        :rtype: NoneType
        """
        tool_buttons = QToolBar()
        tool_buttons.setObjectName('form_toolbar')
        tool_buttons.setIconSize(QSize(16, 16))

        self.addSTR = QAction(GuiUtils.get_icon('add.png'),
                              QApplication.translate('ViewSTRWidget', 'Add'),
                              self)

        self.editSTR = QAction(GuiUtils.get_icon('edit.png'),
                               QApplication.translate('ViewSTRWidget', 'Edit'),
                               self)

        self.deleteSTR = QAction(
            GuiUtils.get_icon('remove.png'),
            QApplication.translate('ViewSTRWidget', 'Remove'), self)

        tool_buttons.addAction(self.addSTR)
        tool_buttons.addAction(self.editSTR)
        tool_buttons.addAction(self.deleteSTR)

        self.toolbarVBox.addWidget(tool_buttons)

    def initGui(self):
        """
        Initialize widget
        """
        self.tb_actions.setVisible(False)
        self._load_entity_configurations()

        self.add_tool_buttons()

        # Connect signals
        self.tbSTREntity.currentChanged.connect(self.entityTabIndexChanged)
        self.btnSearch.clicked.connect(self.searchEntityRelations)
        self.btnClearSearch.clicked.connect(self.clearSearch)
        # self.tvSTRResults.expanded.connect(self.onTreeViewItemExpanded)

        # Set the results treeview to accept requests for context menus
        # self.tvSTRResults.setContextMenuPolicy(Qt.CustomContextMenu)
        # self.tvSTRResults.customContextMenuRequested.connect(
        #     self.onResultsContextMenuRequested
        # )

        if not self._can_create:
            self.addSTR.hide()

        if not self._can_edit:
            self.editSTR.hide()
        else:
            self.editSTR.setDisabled(True)
        if not self._can_delete:
            self.deleteSTR.hide()
        else:
            self.deleteSTR.setDisabled(True)

        self.addSTR.triggered.connect(self.load_new_str_editor)

        self.deleteSTR.triggered.connect(self.delete_str)

        self.editSTR.triggered.connect(self.load_edit_str_editor)

        # Load async for the current widget
        self.entityTabIndexChanged(0)

    def init_progress_dialog(self):
        """
        Initializes the progress dialog.
        """
        self.progress = QProgressBar(self)
        self.progress.resize(self.width(), 10)
        self.progress.setTextVisible(False)

    def add_spatial_unit_layer(self):
        """
        Add the spatial unit layer into the map canvas for later use.
        """
        # Used for startup of view STR, just add the first geom layer.
        if len(self.geom_cols) > 0:
            for spatial_unit in self.spatial_units:
                layer_name_item = self.sp_unit_manager.geom_col_layer_name(
                    spatial_unit.name, self.geom_cols[0])
                self.sp_unit_manager.add_layer_by_name(layer_name_item)

    def _check_permissions(self):
        """
        Enable/disable actions based on the
        permissions defined in the content
        group.
        """
        if self._can_edit:
            self.tb_actions.addAction(self._new_str_action)
        else:
            self.tb_actions.removeAction(self._new_str_action)

        if len(self.tb_actions.actions()) == 0:
            self.tb_actions.setVisible(False)

        else:
            self.tb_actions.setVisible(True)

    def _load_entity_configurations(self):
        """
        Specify the entity configurations.
        """
        try:
            self.parties = self.curr_profile.social_tenure.parties
            tb_str_entities = self.parties + self.spatial_units

            for i, t in enumerate(tb_str_entities):
                QApplication.processEvents()
                entity_cfg = self._entity_config_from_profile(
                    str(t.name), t.short_name)

                if not entity_cfg is None:
                    entity_widget = self.add_entity_config(entity_cfg)

                    # entity_widget.setNodeFormatter(
                    #     EntityNodeFormatter(
                    #         entity_cfg, self.tvSTRResults, self
                    #     )
                    # )

        except DummyException as pe:
            self._notif_search_config.clear()
            self._notif_search_config.insertErrorNotification(str(pe))

    def _entity_config_from_profile(self, table_name, short_name):
        """
        Creates an EntityConfig object from the table name.
        :param table_name: Name of the database table.
        :type table_name: str
        :return: Entity configuration object.
        :rtype: EntityConfig
        """
        table_display_name = format_name(short_name)

        entity = self.curr_profile.entity_by_name(table_name)
        model = entity_model(entity)

        if model is not None:
            # Entity configuration
            entity_cfg = EntityConfiguration()
            entity_cfg.Title = table_display_name
            entity_cfg.STRModel = model
            entity_cfg.data_source_name = table_name
            for col, factory in self._get_widget_factory(entity):
                entity_cfg.LookupFormatters[col.name] = factory

            # Load filter and display columns
            # using only those which are of
            # numeric/varchar type
            searchable_columns = entity_searchable_columns(entity)
            display_columns = entity_display_columns(entity)
            for c in searchable_columns:
                if c != 'id':
                    entity_cfg.filterColumns[c] = format_name(c)
            for c in display_columns:
                if c != 'id':
                    entity_cfg.displayColumns[c] = format_name(c)
            return entity_cfg
        else:
            return None

    def _get_widget_factory(self, entity):
        """
        Get widget factory for specific column type
        :param entity: Current column entity object
        :type entity: Entity
        :return c: Column object corresponding to the widget factory
        :rtype c: BaseColumn
        :return col_factory: Widget factory corresponding to the column type
        :rtype col_factory: ColumnWidgetRegistry
        """
        for c in entity.columns.values():
            col_factory = ColumnWidgetRegistry.factory(c.TYPE_INFO)
            if col_factory is not None:
                yield c, col_factory(c)

    def add_entity_config(self, config):
        """
        Set an entity configuration option and
        add it to the 'Search Entity' tab.
        """
        entityWidg = STRViewEntityWidget(config)
        entityWidg.asyncStarted.connect(self._progressStart)
        entityWidg.asyncFinished.connect(self._progressFinish)

        tabIndex = self.tbSTREntity.addTab(entityWidg, config.Title)

        return entityWidg

    def entityTabIndexChanged(self, index):
        """
        Raised when the tab index of the entity search tab widget changes.
        """
        # Get the current widget in the tab container
        entityWidget = self.tbSTREntity.currentWidget()

        if isinstance(entityWidget, EntitySearchItem):
            entityWidget.loadAsync()

    def searchEntityRelations(self):
        """
        Slot that searches for matching items for
        the specified entity and corresponding STR entities.
        """

        entityWidget = self.tbSTREntity.currentWidget()

        entity_name = entityWidget.config.data_source_name

        self._reset_controls()

        if isinstance(entityWidget, EntitySearchItem):
            valid, msg = entityWidget.validate()

            if not valid:
                self._notif_search_config.clear()
                self._notif_search_config.insertErrorNotification(msg)

                return

            results, searchWord = entityWidget.executeSearch()

            # Show error message
            if len(results) == 0:
                noResultsMsg = QApplication.translate(
                    'ViewSTR', 'No results found for "{}"'.format(searchWord))
                self._notif_search_config.clear()
                self._notif_search_config.insertErrorNotification(noResultsMsg)

                return

            party_names = [
                e.name for e in self.curr_profile.social_tenure.parties
            ]
            entity = self.curr_profile.entity_by_name(entity_name)

            result_ids = [r.id for r in results]

            if entity_name in party_names:

                self.active_spu_id = self.details_tree_view.search_party(
                    entity, result_ids)
            else:
                self.details_tree_view.search_spatial_unit(entity, result_ids)

            # self.tbPropertyPreview._iface.activeLayer().selectByExpression("id={}".format(self.active_spu_id))
            # self.details_tree_view._selected_features = self.tbPropertyPreview._iface.activeLayer().selectedFeatures()
            # self._load_root_node(entity_name, formattedNode)

    def clearSearch(self):
        """
        Clear search input parameters (for current widget) and results.
        """
        entityWidget = self.tbSTREntity.currentWidget()
        if isinstance(entityWidget, EntitySearchItem):
            entityWidget.reset()
        self._reset_controls()

    def _reset_controls(self):
        # Clear tree view
        self._resetTreeView()

        # Clear document listings
        self._deleteSourceDocTabs()

        # Remove spatial unit memory layer
        self.tbPropertyPreview.remove_layer()

    def on_select_results(self):
        """
        Slot which is raised when the selection
        is changed in the tree view
        selection model.
        """
        if len(self.details_tree_view.view.selectedIndexes()) < 1:
            self.disable_buttons()
            return
        self.search_done = True

        index = self.details_tree_view.view.selectedIndexes()[0]
        item = self.details_tree_view.model.itemFromIndex(index)

        QApplication.processEvents()

        # STR node - edit social tenure relationship
        if item.text() == self.details_tree_view.str_text:
            entity = self.curr_profile.social_tenure
            str_model = self.details_tree_view.str_models[item.data()]

            self.details_tree_view.selected_model = str_model
            self.details_tree_view.selected_item = SelectedItem(item)

            documents = self.details_tree_view._supporting_doc_models(
                entity.name, str_model)

            self._load_source_documents(documents)
            # if there is supporting document,
            # expand supporting document tab
            if len(documents) > 0:
                self.toolBox.setCurrentIndex(1)
            self.disable_buttons(False)

        # party node - edit party
        elif item.data() in self.details_tree_view.spatial_unit_items.keys():
            self.toolBox.setCurrentIndex(0)
            entity = self.details_tree_view.spatial_unit_items[item.data()]

            model = self.details_tree_view.feature_model(entity, item.data())
            self.draw_spatial_unit(entity.name, model)
            self.disable_buttons()

            canvas = iface.mapCanvas()
            if canvas:
                canvas.zoomToFullExtent()

        else:
            self.disable_buttons()

    def disable_buttons(self, status=True):
        if self._can_edit:
            self.deleteSTR.setDisabled(status)
        if self._can_delete:
            self.editSTR.setDisabled(status)

    def str_party_column_obj(self, record):
        """
        Gets the current party column name in STR
        table by finding party column with value
        other than None.
        :param record: The STR record or result.
        :type record: Dictionary
        :return: The party column name with value.
        :rtype: String
        """
        for party in self.parties:
            party_name = party.short_name.lower()
            party_id = '{}_id'.format(party_name)
            if party_id not in record.__dict__:
                return None
            if record.__dict__[party_id] != None:
                party_id_obj = getattr(self.str_model, party_id)
                return party_id_obj

    def load_edit_str_editor(self):
        self.details_tree_view.edit_selected_node(self.details_tree_view)
        self.btnSearch.click()
        self.disable_buttons()

    def load_new_str_editor(self):
        try:
            # Check type of node and perform corresponding action
            add_str = STREditor()
            add_str.exec_()

        except DummyException as ex:
            QMessageBox.critical(
                self._plugin.iface.mainWindow(),
                QApplication.translate("STDMPlugin", "Loading Error"), str(ex))

    def delete_str(self):
        self.details_tree_view.delete_selected_item()
        self.btnSearch.click()
        self.disable_buttons()

    def onSourceDocumentRemoved(self, container_id, doc_uuid, removed_doc):
        """
        Slot raised when a source document is removed from the container.
        If there are no documents in the specified container then remove
        the tab.
        """
        curr_container = self.tbSupportingDocs.currentWidget()
        curr_doc_widget = curr_container.findChildren(DocumentWidget)

        for doc in curr_doc_widget:
            if doc.fileUUID == doc_uuid:
                doc.deleteLater()
        self.removed_docs = removed_doc

    def draw_spatial_unit(self, entity_name, model):
        """
        Render the geometry of the given spatial unit in the spatial view.
        :param row_id: Sqlalchemy object representing a feature.
        """
        entity = self.curr_profile.entity_by_name(entity_name)

        self.tbPropertyPreview.draw_spatial_unit(entity, model)

    def showEvent(self, event):
        """
        (Re)load map layers in the viewer and main canvas.
        :param event: Window event
        :type event: QShowEvent
        """
        self.setEnabled(True)
        if QTimer is not None:
            QTimer.singleShot(200, self.init_mirror_map)

        return QMainWindow.showEvent(self, event)

    def init_mirror_map(self):
        self._notify_no_base_layers()
        # Add spatial unit layer if it doesn't exist
        self.tbPropertyPreview.refresh_canvas_layers()
        self.tbPropertyPreview.load_web_map()

    def _notify_no_base_layers(self):
        """
        Checks if there are any base layers that will be used when
        visualizing the spatial units. If there are no base layers
        then insert warning message.
        """
        self._notif_search_config.clear()

        num_layers = len(QgsProject.instance().mapLayers())
        if num_layers == 0:
            msg = QApplication.translate(
                "ViewSTR", "No basemap layers are loaded in the "
                "current project. Basemap layers "
                "enhance the visualization of spatial units.")
            self._notif_search_config.insertWarningNotification(msg)

    def _deleteSourceDocTabs(self):
        """
        Removes all source document tabs and deletes their references.
        """
        tabCount = self.tbSupportingDocs.count()

        while tabCount != 0:
            srcDocWidget = self.tbSupportingDocs.widget(tabCount - 1)
            self.tbSupportingDocs.removeTab(tabCount - 1)
            del srcDocWidget
            tabCount -= 1

        self._strID = None
        self._source_doc_manager.reset()

    def _resetTreeView(self):
        """
        Clears the results tree view.
        """
        # Reset tree view
        strModel = self.details_tree_view.view.model()
        resultsSelModel = self.details_tree_view.view.selectionModel()

        if strModel:
            strModel.clear()

        if resultsSelModel:
            if self.search_done:
                resultsSelModel.selectionChanged.disconnect(
                    self.on_select_results)
            resultsSelModel.selectionChanged.connect(self.on_select_results)

    def _load_source_documents(self, source_docs):
        """
        Load source documents into document listing widget.
        """
        # Configure progress dialog
        progress_msg = QApplication.translate(
            "ViewSTR", "Loading supporting documents...")

        progress_dialog = QProgressDialog(self)
        if len(source_docs) > 0:
            progress_dialog.setWindowTitle(progress_msg)
            progress_dialog.setRange(0, len(source_docs))
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setFixedWidth(380)
            progress_dialog.show()
            progress_dialog.setValue(0)
        self._notif_search_config.clear()

        self.tbSupportingDocs.clear()
        self._source_doc_manager.reset()

        if len(source_docs) < 1:
            empty_msg = QApplication.translate(
                'ViewSTR', 'No supporting document is uploaded '
                'for this social tenure relationship.')
            self._notif_search_config.clear()
            self._notif_search_config.insertWarningNotification(empty_msg)

        for i, (doc_type_id, doc_obj) in enumerate(source_docs.items()):

            # add tabs, and container and widget for each tab
            tab_title = self._source_doc_manager.doc_type_mapping[doc_type_id]

            tab_widget = QWidget()
            tab_widget.setObjectName(tab_title)

            cont_layout = QVBoxLayout(tab_widget)
            cont_layout.setObjectName('widget_layout_' + tab_title)

            scrollArea = QScrollArea(tab_widget)
            scrollArea.setFrameShape(QFrame.NoFrame)

            scrollArea_contents = QWidget()
            scrollArea_contents.setObjectName('tab_scroll_area_' + tab_title)

            tab_layout = QVBoxLayout(scrollArea_contents)
            tab_layout.setObjectName('layout_' + tab_title)

            scrollArea.setWidgetResizable(True)

            scrollArea.setWidget(scrollArea_contents)
            cont_layout.addWidget(scrollArea)

            self._source_doc_manager.registerContainer(tab_layout, doc_type_id)

            for doc in doc_obj:

                try:
                    # add doc widgets
                    self._source_doc_manager.insertDocFromModel(
                        doc, doc_type_id)
                except DummyException as ex:
                    LOGGER.debug(str(ex))

            self.tbSupportingDocs.addTab(tab_widget, tab_title)
            progress_dialog.setValue(i + 1)

    # def _on_node_reference_changed(self, rootHash):
    #     """
    #     Method for resetting document listing and map preview
    #     if another root node and its children
    #     are selected then the documents are reset as
    #     well as the map preview control.
    #     """
    #     if rootHash != self._curr_rootnode_hash:
    #         self._deleteSourceDocTabs()
    #         self._curr_rootnode_hash = rootHash

    def _progressStart(self):
        """
        Load progress dialog window.
        For items whose durations is unknown,
        'isindefinite' = True by default.
        If 'isindefinite' is False, then
        'rangeitems' has to be specified.
        """
        pass

    def _progressFinish(self):
        """
        Hide progress dialog window.
        """
        pass

    def _edit_permissions(self):
        """
        Returns True/False whether the current logged in user
        has permissions to create new social tenure relationships.
        If true, then the system assumes that
        they can also edit STR records.
        """
        canEdit = False
        userName = globals.APP_DBCONN.User.UserName
        authorizer = Authorizer(userName)
        newSTRCode = "9576A88D-C434-40A6-A318-F830216CA15A"

        # Get the name of the content from the code
        cnt = Content()
        createSTRCnt = cnt.queryObject().filter(
            Content.code == newSTRCode).first()
        if createSTRCnt:
            name = createSTRCnt.name
            canEdit = authorizer.CheckAccess(name)

        return canEdit
Пример #6
0
    def __init__(self, entity, parent=None, state=MANAGE, load_records=True):
        EntityBrowser.__init__(self, entity, parent, state, load_records)
        self.record_id = 0

        self.highlight = None
        self.load_records = load_records
        self.selection_layer = None

        #Add action toolbar if the state contains Manage flag
        if (state & MANAGE) != 0:
            add = QApplication.translate("EntityBrowserWithEditor", "Add")
            edit = QApplication.translate("EntityBrowserWithEditor", "Edit")
            remove = QApplication.translate("EntityBrowserWithEditor",
                                            "Remove")

            self._newEntityAction = QAction(
                QIcon(":/plugins/stdm/images/icons/add.png"), add, self)

            self.connect(self._newEntityAction, SIGNAL("triggered()"),
                         self.onNewEntity)

            self._editEntityAction = QAction(
                QIcon(":/plugins/stdm/images/icons/edit.png"), edit, self)
            self._editEntityAction.setObjectName(
                QApplication.translate("EntityBrowserWithEditor", "edit_tool"))

            self.connect(self._editEntityAction, SIGNAL("triggered()"),
                         self.onEditEntity)

            self._removeEntityAction = QAction(
                QIcon(":/plugins/stdm/images/icons/remove.png"), remove, self)
            self._removeEntityAction.setObjectName(
                QApplication.translate("EntityBrowserWithEditor",
                                       "remove_tool"))
            self.connect(self._removeEntityAction, SIGNAL("triggered()"),
                         self.onRemoveEntity)

            #Manage position of the actions based on whether the entity
            # supports documents.
            if self.can_view_supporting_documents:
                manage_acts = [
                    self._newEntityAction, self._editEntityAction,
                    self._removeEntityAction
                ]
                self.tbActions.insertActions(self._view_docs_act, manage_acts)

                #Add action separator
                self._act_sep = QAction(self)
                self._act_sep.setSeparator(True)
                self.tbActions.insertAction(self._view_docs_act, self._act_sep)

            else:
                self.tbActions.addAction(self._newEntityAction)
                self.tbActions.addAction(self._editEntityAction)
                self.tbActions.addAction(self._removeEntityAction)

            if isinstance(parent, EntityEditorDialog):
                if self.can_view_supporting_documents:
                    self._view_docs_act.setVisible(False)
                self.parent_entity = parent._entity

            else:
                self.parent_entity = None

            # hide the add button and add layer preview for spatial entity
            if entity.has_geometry_column() and self.parent_entity is None:
                self.sp_unit_manager = SpatialUnitManagerDockWidget(iface)
                self.geom_cols = self.sp_unit_manager.geom_columns(
                    self._entity)

                self.add_spatial_unit_layer()
                self.tbEntity.clicked.connect(self.on_select_attribute)
                self.tbEntity.entered.connect(self.on_select_attribute)

                self.shift_spatial_entity_browser()
                # Hide the add button from spatial tables
                # self._newEntityAction.setVisible(False)

            self._editor_dlg = EntityEditorDialog
Пример #7
0
class EntityBrowserWithEditor(EntityBrowser):
    """
    Entity browser with added functionality for carrying out CRUD operations
    directly.
    """
    def __init__(self, entity, parent=None, state=MANAGE, load_records=True):
        EntityBrowser.__init__(self, entity, parent, state, load_records)
        self.record_id = 0

        self.highlight = None
        self.load_records = load_records
        self.selection_layer = None

        #Add action toolbar if the state contains Manage flag
        if (state & MANAGE) != 0:
            add = QApplication.translate("EntityBrowserWithEditor", "Add")
            edit = QApplication.translate("EntityBrowserWithEditor", "Edit")
            remove = QApplication.translate("EntityBrowserWithEditor",
                                            "Remove")

            self._newEntityAction = QAction(
                QIcon(":/plugins/stdm/images/icons/add.png"), add, self)

            self.connect(self._newEntityAction, SIGNAL("triggered()"),
                         self.onNewEntity)

            self._editEntityAction = QAction(
                QIcon(":/plugins/stdm/images/icons/edit.png"), edit, self)
            self._editEntityAction.setObjectName(
                QApplication.translate("EntityBrowserWithEditor", "edit_tool"))

            self.connect(self._editEntityAction, SIGNAL("triggered()"),
                         self.onEditEntity)

            self._removeEntityAction = QAction(
                QIcon(":/plugins/stdm/images/icons/remove.png"), remove, self)
            self._removeEntityAction.setObjectName(
                QApplication.translate("EntityBrowserWithEditor",
                                       "remove_tool"))
            self.connect(self._removeEntityAction, SIGNAL("triggered()"),
                         self.onRemoveEntity)

            #Manage position of the actions based on whether the entity
            # supports documents.
            if self.can_view_supporting_documents:
                manage_acts = [
                    self._newEntityAction, self._editEntityAction,
                    self._removeEntityAction
                ]
                self.tbActions.insertActions(self._view_docs_act, manage_acts)

                #Add action separator
                self._act_sep = QAction(self)
                self._act_sep.setSeparator(True)
                self.tbActions.insertAction(self._view_docs_act, self._act_sep)

            else:
                self.tbActions.addAction(self._newEntityAction)
                self.tbActions.addAction(self._editEntityAction)
                self.tbActions.addAction(self._removeEntityAction)

            if isinstance(parent, EntityEditorDialog):
                if self.can_view_supporting_documents:
                    self._view_docs_act.setVisible(False)
                self.parent_entity = parent._entity

            else:
                self.parent_entity = None

            # hide the add button and add layer preview for spatial entity
            if entity.has_geometry_column() and self.parent_entity is None:
                self.sp_unit_manager = SpatialUnitManagerDockWidget(iface)
                self.geom_cols = self.sp_unit_manager.geom_columns(
                    self._entity)

                self.add_spatial_unit_layer()
                self.tbEntity.clicked.connect(self.on_select_attribute)
                self.tbEntity.entered.connect(self.on_select_attribute)

                self.shift_spatial_entity_browser()
                # Hide the add button from spatial tables
                # self._newEntityAction.setVisible(False)

            self._editor_dlg = EntityEditorDialog

    def onNewEntity(self):
        '''
        Load editor dialog for adding a new record.
        '''
        self._notifBar.clear()

        if not self._can_add_edit():
            msg = QApplication.translate(
                'EntityBrowserWithEditor',
                'There are no user-defined columns for this entity.')
            self._notifBar.insertErrorNotification(msg)

            return
        if self._entity.has_geometry_column():
            self.sp_unit_manager.active_layer_source()

            gps_tool = GPSToolDialog(iface,
                                     self._entity,
                                     self._entity.name,
                                     self.sp_unit_manager.active_sp_col,
                                     reload=False,
                                     entity_browser=self)
            editor_trans = self.tr('Editor')
            title = u'{0} {1}'.format(format_name(self._entity.short_name),
                                      editor_trans)
            gps_tool.setWindowTitle(title)

            result = gps_tool.exec_()
            result = False  # a workaround to avoid duplicate model insert
            self.addEntityDlg = gps_tool.entity_editor
        else:
            self.addEntityDlg = self._editor_dlg(
                self._entity, parent=self, parent_entity=self.parent_entity)

            self.addEntityDlg.addedModel.connect(self.on_save_and_new)

            result = self.addEntityDlg.exec_()

        if result == QDialog.Accepted:
            model_obj = self.addEntityDlg.model()
            if self.addEntityDlg.is_valid:
                if self.parent_entity is None:
                    self.addModelToView(model_obj)
                    self.recomputeRecordCount()

    def on_save_and_new(self, model):
        """
        A slot raised when save and new button is clicked. It updates
        the entity browser with the new model added.
        :param model: The model saved.
        :type model: SQL Alchemy Model
        """
        if model is not None:
            insert_position = self.addModelToView(model)
            self.set_child_model(model, insert_position + 1)
            self.recomputeRecordCount()

    def _can_add_edit(self):
        """
        Check if there are columns specified (apart from id) for the given
        entity.
        :return: Returns True if there are other columns apart from id,
        otherwise False.
        """
        columns = self._entity.columns.values()

        if len(columns) < 2:
            return False

        return True

    def onEditEntity(self):
        '''
        Slot raised to load the editor for the selected row.
        '''
        self._notifBar.clear()

        if not self._can_add_edit():
            msg = QApplication.translate(
                'EntityBrowserWithEditor',
                'There are no user-defined columns for this entity.')
            self._notifBar.insertErrorNotification(msg)

            return
        if self.tbEntity.selectionModel() is None:
            return

        selRowIndices = self.tbEntity.selectionModel().selectedRows(0)

        if len(selRowIndices) == 0:
            msg = QApplication.translate(
                "EntityBrowserWithEditor",
                "Please select a record in the table"
                " below for editing.")
            self._notifBar.insertWarningNotification(msg)

            return

        #Exit if more than one record has been selected
        if len(selRowIndices) > 1:
            msg = QApplication.translate(
                "EntityBrowserWithEditor",
                "Multiple selection detected, please choose one record "
                "only for editing.")
            self._notifBar.insertWarningNotification(msg)

            return

        rowIndex = self._proxyModel.mapToSource(selRowIndices[0])
        recordid = rowIndex.data()

        self._load_editor_dialog(recordid, rowIndex.row())

    def set_child_model(self, model, row_position):
        """
        Sets the child model so that it can be used for editing and deleting if
        needed.
        :param model: The child model saved
        :type model: Object
        """
        self.child_model[row_position, self.entity] = model

    def onRemoveEntity(self):
        '''
        Load editor dialog for editing an existing record.
        '''
        self._notifBar.clear()

        sel_row_indices = self.tbEntity.selectionModel().selectedRows(0)

        if len(sel_row_indices) == 0:
            msg = QApplication.translate(
                "EntityBrowserWithEditor",
                "Please select one or more records in the "
                "table below to be deleted.")
            self._notifBar.insertWarningNotification(msg)

            return

        msg = QApplication.translate(
            "EntityBrowserWithEditor",
            "Are you sure you want to delete the selected record(s)?\n"
            "This action cannot be undone.")

        response = QMessageBox.warning(
            self,
            QApplication.translate("EntityBrowserWithEditor",
                                   "Delete Record(s)"), msg,
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        if response == QMessageBox.No:
            return

        while len(sel_row_indices) > 0:

            ri = sel_row_indices[0]
            source_row_index = self._proxyModel.mapToSource(ri)
            record_id = source_row_index.data()

            row_number = source_row_index.row()

            #Delete record
            result = self._delete_record(record_id, row_number)

            if not result:
                title = QApplication.translate('EntityBrowserWithEditor',
                                               'Delete Record(s)')

                msg = QApplication.translate(
                    'EntityBrowserWithEditor',
                    'An error occurred while attempting to delete a record, this '
                    'is most likely caused by a dependency issue.\nPlease check '
                    'if the record has dependencies such as social tenure '
                    'relationship or related entities. If it has then delete '
                    'these dependencies first.')
                QMessageBox.critical(self, title, msg)

                break

            #Refresh list of selected records
            sel_row_indices = self.tbEntity.selectionModel().selectedRows(0)

    def remove_rows(self):
        """
        Removes rows from the entity browser.
        """
        if self.tbEntity.model() is not None:
            row_count = self.tbEntity.model().rowCount()
            self.tbEntity.model().removeRows(0, row_count)

    def _load_editor_dialog(self, recid, rownumber):
        '''
        Load editor dialog based on the selected model instance with the given ID.
        '''
        model_obj = self._model_from_id(recid, rownumber)
        # show GPS editor if geometry
        if self._entity.has_geometry_column():
            self.sp_unit_manager.active_layer_source()

            gps_tool = GPSToolDialog(iface,
                                     self._entity,
                                     self._entity.name,
                                     self.sp_unit_manager.active_sp_col,
                                     model=model_obj,
                                     reload=False,
                                     row_number=rownumber,
                                     entity_browser=self)
            editor_trans = self.tr('Editor')
            title = u'{0} {1}'.format(format_name(self._entity.short_name),
                                      editor_trans)
            gps_tool.setWindowTitle(title)

            result = gps_tool.exec_()
        else:
            #Load editor dialog
            edit_entity_dlg = self._editor_dlg(
                self._entity,
                model=model_obj,
                parent=self,
                parent_entity=self.parent_entity)

            result = edit_entity_dlg.exec_()

        if result == QDialog.Accepted:
            if self._entity.has_geometry_column():
                edit_entity_dlg = gps_tool.entity_editor

            updated_model_obj = edit_entity_dlg.model()
            if not edit_entity_dlg.is_valid:
                return
            for i, attr in enumerate(self._entity_attrs):
                prop_idx = self._tableModel.index(rownumber, i)
                attr_val = getattr(updated_model_obj, attr)
                '''
                Check if there are display formatters and apply if
                one exists for the given attribute.
                '''
                if attr in self._cell_formatters:
                    formatter = self._cell_formatters[attr]
                    attr_val = formatter.format_column_value(attr_val)

                self._tableModel.setData(prop_idx, attr_val)

    def _delete_record(self, rec_id, row_number):
        """
        Delete the record with the given id and remove it from the table view.
        """
        del_result = True

        if self.parent_entity is not None:
            del self.child_model[row_number + 1, self.entity]
            del self._parent.child_models[row_number + 1, self.entity]

            self._tableModel.removeRows(row_number, 1)
            # Update number of records
            self.recomputeRecordCount()

            #Clear previous notifications
            self._notifBar.clear()

            return del_result
        #Remove record from the database
        dbHandler = self._dbmodel()
        entity = dbHandler.queryObject().filter(
            self._dbmodel.id == rec_id).first()

        if entity:
            result = entity.delete()

            if not result:
                return False

            self._tableModel.removeRows(row_number, 1)

            #Clear previous notifications
            self._notifBar.clear()

            #Notify user
            delMsg = QApplication.translate("EntityBrowserWithEditor",
                                            "Record successfully deleted!")

            self._notifBar.insertInformationNotification(delMsg)

            #Update number of records
            self.recomputeRecordCount()

        return del_result

    def onDoubleClickView(self, modelindex):
        '''
        Override for loading editor dialog.
        '''
        rowIndex = self._proxyModel.mapToSource(modelindex)
        rowNumber = rowIndex.row()
        recordIdIndex = self._tableModel.index(rowNumber, 0)

        recordId = recordIdIndex.data()
        self._load_editor_dialog(recordId, recordIdIndex.row())

    def shift_spatial_entity_browser(self):
        """
        Shift records manager to the bottom left corner
        :rtype: NoneType
        :return: None
        """
        parent_height = self.parent().geometry().height()
        parent_width = self.parent().geometry().width()
        parent_x = self.parent().geometry().x()
        parent_y = self.parent().geometry().y()
        dialog_width = self.width()
        dialog_height = self.height()
        self.setGeometry(parent_x,
                         parent_y + parent_height - dialog_height - 40,
                         dialog_width, dialog_height)

    def on_select_attribute(self):
        """
        Slot raised when selecting a spatial entity row.
        """
        sel_row_indices = self.tbEntity.\
            selectionModel().selectedRows(0)
        record_ids = []

        for sel_row_index in sel_row_indices:
            rowIndex = self._proxyModel.mapToSource(sel_row_index)
            record_id = rowIndex.data()
            record_ids.append(record_id)

        self.record_feature_highlighter(record_ids)

    def record_feature_highlighter(self, record_ids):
        """
        Highlights a feature of a record.
        :param record_id: The id of a row
        :type record_id: Integer
        :return: None
        :rtype: NoneType
        """
        if len(record_ids) < 1:
            return

        for geom in self.geom_cols:

            geom_wkb = entity_id_to_attr(self._entity, geom.name,
                                         record_ids[0])

            if geom_wkb is not None:
                sel_lyr_name = self.sp_unit_manager. \
                    geom_col_layer_name(
                    self._entity.name, geom
                )

                self.add_spatial_unit_layer(sel_lyr_name)
                layers = QgsMapLayerRegistry.instance().\
                    mapLayersByName(
                    sel_lyr_name
                )
                if len(layers) > 0:
                    layers[0].removeSelection()
                    layers[0].select(record_ids)
                    bounding_box = layers[0].boundingBoxOfSelected()
                    iface.mapCanvas().setExtent(bounding_box)
                    iface.mapCanvas().refresh()
                    self.selection_layer = layers[0]

    def add_spatial_unit_layer(self, layer_name=None):
        """
        Add the spatial unit layer into the map canvas for later use.
        :param layer_name: The name of the layer to be added to the map canvas.
        :type layer_name: String
        """
        if layer_name is not None:
            self.sp_unit_manager.add_layer_by_name(layer_name)
        else:
            # As this is used for startup of
            # entity browser, just add the first geom layer.
            if len(self.geom_cols) > 0:
                layer_name_item = \
                    self.sp_unit_manager.geom_col_layer_name(
                        self._entity.name,
                        self.geom_cols[0]
                )
                self.sp_unit_manager.\
                    add_layer_by_name(layer_name_item)

    def closeEvent(self, event):
        """
        The event handler that is triggered
        when the dialog is closed.
        :param event: the event
        :type QCloseEvent
        :return: None
        """
        if self._entity.has_geometry_column():
            try:
                if self.selection_layer is not None:
                    self.selection_layer.removeSelection()
                self.sp_unit_manager.zoom_to_layer()
            except RuntimeError:
                pass

    def hideEvent(self, hideEvent):
        """
        The event handler that is triggered
        when the dialog is hidden.
        :param hideEvent: the event
        :type QCloseEvent
        :return: None
        """
        if self._entity.has_geometry_column():
            if self.selection_layer is not None:
                self.selection_layer.removeSelection()
                self.sp_unit_manager.zoom_to_layer()
Пример #8
0
    def __init__(self, plugin):
        QMainWindow.__init__(self, plugin.iface.mainWindow())
        self.setupUi(self)

        self._plugin = plugin

        QTimer.singleShot(
            300, lambda: self.tbPropertyPreview.set_iface(self._plugin.iface))
        #self.tbPropertyPreview.set_iface(self._plugin.iface)
        self.curr_profile = current_profile()

        self.spatial_unit = self.curr_profile.social_tenure.spatial_unit
        #Center me
        self.move(QDesktopWidget().availableGeometry().center() -
                  self.frameGeometry().center())
        self.sp_unit_manager = SpatialUnitManagerDockWidget(
            self._plugin.iface, self._plugin)
        self.geom_cols = self.sp_unit_manager.geom_columns(self.spatial_unit)
        self.toolBox.setStyleSheet('''
            QToolBox::tab {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #EDEDED, stop: 0.4 #EDEDED,
                    stop: 0.5 #EDEDED, stop: 1.0 #D3D3D3
                );
                border-radius: 2px;
                border-style: outset;
                border-width: 2px;
                height: 100px;
                border-color: #C3C3C3;
            }

            QToolBox::tab:selected {
                font: italic;
            }
            ''')
        self.tvSTRResults.setStyleSheet('''
            QTreeView:!active {
                selection-background-color: #72a6d9;
            }
            ''')

        # Configure notification bar
        self._notif_search_config = NotificationBar(self.vl_notification)

        # set whether currently logged in user has
        # permissions to edit existing STR records
        self._can_edit = self._plugin.STRCntGroup.canUpdate()
        self._can_delete = self._plugin.STRCntGroup.canDelete()
        self._can_create = self._plugin.STRCntGroup.canCreate()
        # Variable used to store a reference to the
        # currently selected social tenure relationship
        # when displaying documents in the supporting documents tab window.
        # This ensures that there are no duplicates
        # when the same item is selected over and over again.

        self._strID = None
        self.removed_docs = None
        #Used to store the root hash of the currently selected node.
        self._curr_rootnode_hash = ""

        self.str_model, self.str_doc_model = entity_model(
            self.curr_profile.social_tenure, False, True)

        self._source_doc_manager = SourceDocumentManager(
            self.curr_profile.social_tenure.supporting_doc, self.str_doc_model,
            self)

        self._source_doc_manager.documentRemoved.connect(
            self.onSourceDocumentRemoved)

        self._source_doc_manager.setEditPermissions(False)

        self.initGui()
Пример #9
0
class ViewSTRWidget(QMainWindow, Ui_frmManageSTR):
    """
    Search and browse the social tenure relationship
    of all participating entities.
    """
    def __init__(self, plugin):
        QMainWindow.__init__(self, plugin.iface.mainWindow())
        self.setupUi(self)

        self._plugin = plugin

        QTimer.singleShot(
            300, lambda: self.tbPropertyPreview.set_iface(self._plugin.iface))
        #self.tbPropertyPreview.set_iface(self._plugin.iface)
        self.curr_profile = current_profile()

        self.spatial_unit = self.curr_profile.social_tenure.spatial_unit
        #Center me
        self.move(QDesktopWidget().availableGeometry().center() -
                  self.frameGeometry().center())
        self.sp_unit_manager = SpatialUnitManagerDockWidget(
            self._plugin.iface, self._plugin)
        self.geom_cols = self.sp_unit_manager.geom_columns(self.spatial_unit)
        self.toolBox.setStyleSheet('''
            QToolBox::tab {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #EDEDED, stop: 0.4 #EDEDED,
                    stop: 0.5 #EDEDED, stop: 1.0 #D3D3D3
                );
                border-radius: 2px;
                border-style: outset;
                border-width: 2px;
                height: 100px;
                border-color: #C3C3C3;
            }

            QToolBox::tab:selected {
                font: italic;
            }
            ''')
        self.tvSTRResults.setStyleSheet('''
            QTreeView:!active {
                selection-background-color: #72a6d9;
            }
            ''')

        # Configure notification bar
        self._notif_search_config = NotificationBar(self.vl_notification)

        # set whether currently logged in user has
        # permissions to edit existing STR records
        self._can_edit = self._plugin.STRCntGroup.canUpdate()
        self._can_delete = self._plugin.STRCntGroup.canDelete()
        self._can_create = self._plugin.STRCntGroup.canCreate()
        # Variable used to store a reference to the
        # currently selected social tenure relationship
        # when displaying documents in the supporting documents tab window.
        # This ensures that there are no duplicates
        # when the same item is selected over and over again.

        self._strID = None
        self.removed_docs = None
        #Used to store the root hash of the currently selected node.
        self._curr_rootnode_hash = ""

        self.str_model, self.str_doc_model = entity_model(
            self.curr_profile.social_tenure, False, True)

        self._source_doc_manager = SourceDocumentManager(
            self.curr_profile.social_tenure.supporting_doc, self.str_doc_model,
            self)

        self._source_doc_manager.documentRemoved.connect(
            self.onSourceDocumentRemoved)

        self._source_doc_manager.setEditPermissions(False)

        self.initGui()

    def add_tool_buttons(self):
        """
        Add toolbar buttons of add, edit and delete buttons.
        :return: None
        :rtype: NoneType
        """
        tool_buttons = QToolBar()
        tool_buttons.setObjectName('form_toolbar')
        tool_buttons.setIconSize(QSize(16, 16))

        self.addSTR = QAction(QIcon(':/plugins/stdm/images/icons/add.png'),
                              QApplication.translate('ViewSTRWidget', 'Add'),
                              self)

        self.editSTR = QAction(QIcon(':/plugins/stdm/images/icons/edit.png'),
                               QApplication.translate('ViewSTRWidget', 'Edit'),
                               self)

        self.deleteSTR = QAction(
            QIcon(':/plugins/stdm/images/icons/remove.png'),
            QApplication.translate('ViewSTRWidget', 'Remove'), self)

        tool_buttons.addAction(self.addSTR)
        tool_buttons.addAction(self.editSTR)
        tool_buttons.addAction(self.deleteSTR)

        self.toolbarVBox.addWidget(tool_buttons)

    def initGui(self):
        """
        Initialize widget
        """
        self.init_progress_dialog()

        self.tb_actions.setVisible(False)
        QTimer.singleShot(30, self._load_entity_configurations)

        self.add_tool_buttons()

        #Connect signals
        self.tbSTREntity.currentChanged.connect(self.entityTabIndexChanged)
        self.btnSearch.clicked.connect(self.searchEntityRelations)
        self.btnClearSearch.clicked.connect(self.clearSearch)
        self.tvSTRResults.expanded.connect(self.onTreeViewItemExpanded)

        #Set the results treeview to accept requests for context menus
        self.tvSTRResults.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tvSTRResults.customContextMenuRequested.connect(
            self.onResultsContextMenuRequested)

        if not self._can_create:
            self.addSTR.hide()

        if not self._can_edit:
            self.editSTR.hide()
        else:
            self.editSTR.setDisabled(True)
        if not self._can_delete:
            self.deleteSTR.hide()
        else:
            self.deleteSTR.setDisabled(True)

        self.addSTR.triggered.connect(self.load_new_str_editor)

        self.deleteSTR.triggered.connect(self.delete_str)

        self.editSTR.triggered.connect(self.load_edit_str_editor)

        #Load async for the current widget
        self.entityTabIndexChanged(0)

    def init_progress_dialog(self):
        """
        Initializes the progress dialog.
        """
        self.progress = QProgressBar(self)
        self.progress.resize(self.width(), 10)
        self.progress.setTextVisible(False)

    def add_spatial_unit_layer(self):
        """
        Add the spatial unit layer into the map canvas for later use.
        """
        # Used for startup of view STR, just add the first geom layer.
        if len(self.geom_cols) > 0:
            layer_name_item = \
                self.sp_unit_manager.geom_col_layer_name(
                    self.spatial_unit.name,
                    self.geom_cols[0]
            )
            self.sp_unit_manager.\
                add_layer_by_name(layer_name_item)

    def _check_permissions(self):
        """
        Enable/disable actions based on the
        permissions defined in the content
        group.
        """
        if self._can_edit:
            self.tb_actions.addAction(self._new_str_action)
        else:
            self.tb_actions.removeAction(self._new_str_action)

        if len(self.tb_actions.actions()) == 0:
            self.tb_actions.setVisible(False)

        else:
            self.tb_actions.setVisible(True)

    def _load_entity_configurations(self):
        """
        Specify the entity configurations.
        """
        try:

            spatial_unit = [self.curr_profile.social_tenure.spatial_unit]

            self.parties = self.curr_profile.social_tenure.parties
            tb_str_entities = self.parties + spatial_unit
            self.progress.setRange(0, len(tb_str_entities) - 1)

            for i, t in enumerate(tb_str_entities):
                QApplication.processEvents()
                entity_cfg = self._entity_config_from_profile(
                    str(t.name), t.short_name)

                if not entity_cfg is None:
                    entity_widget = self.add_entity_config(entity_cfg)

                    entity_widget.setNodeFormatter(
                        EntityNodeFormatter(entity_cfg, self.tvSTRResults,
                                            self))
                self.progress.setValue(i)
            self.progress.hide()

        except Exception as pe:
            self._notif_search_config.clear()
            self._notif_search_config.insertErrorNotification(unicode(pe))

    def _entity_config_from_profile(self, table_name, short_name):
        """
        Creates an EntityConfig object from the table name.
        :param table_name: Name of the database table.
        :type table_name: str
        :return: Entity configuration object.
        :rtype: EntityConfig
        """
        table_display_name = format_name(short_name)

        entity = self.curr_profile.entity_by_name(table_name)
        model = entity_model(entity)

        if model is not None:
            #Entity configuration
            entity_cfg = EntityConfiguration()
            entity_cfg.Title = table_display_name
            entity_cfg.STRModel = model
            entity_cfg.data_source_name = table_name

            # Load filter and display columns
            # using only those which are of
            # numeric/varchar type
            searchable_columns = entity_searchable_columns(entity)
            display_columns = entity_display_columns(entity)
            for c in searchable_columns:
                if c != 'id':
                    entity_cfg.filterColumns[c] = format_name(c)
            for c in display_columns:
                if c != 'id':
                    entity_cfg.displayColumns[c] = format_name(c)
            return entity_cfg
        else:
            return None

    def add_entity_config(self, config):
        """
        Set an entity configuration option and
        add it to the 'Search Entity' tab.
        """
        entityWidg = STRViewEntityWidget(config)
        entityWidg.asyncStarted.connect(self._progressStart)
        entityWidg.asyncFinished.connect(self._progressFinish)

        tabIndex = self.tbSTREntity.addTab(entityWidg, config.Title)

        return entityWidg

    def entityTabIndexChanged(self, index):
        """
        Raised when the tab index of the entity search tab widget changes.
        """
        #Get the current widget in the tab container
        entityWidget = self.tbSTREntity.currentWidget()

        if isinstance(entityWidget, EntitySearchItem):
            entityWidget.loadAsync()

    def searchEntityRelations(self):
        """
        Slot that searches for matching items for
        the specified entity and corresponding STR entities.
        """
        self._reset_controls()

        entityWidget = self.tbSTREntity.currentWidget()
        if isinstance(entityWidget, EntitySearchItem):
            valid, msg = entityWidget.validate()

            if not valid:
                self._notif_search_config.clear()
                self._notif_search_config.insertErrorNotification(msg)

                return

            formattedNode, results, searchWord = entityWidget.executeSearch()

            #Show error message
            if len(results) == 0:
                noResultsMsg = QApplication.translate(
                    'ViewSTR', 'No results found for "{}"'.format(searchWord))
                self._notif_search_config.clear()
                self._notif_search_config.insertErrorNotification(noResultsMsg)

                return

            if formattedNode is not None:
                self._load_root_node(formattedNode)

    def clearSearch(self):
        """
        Clear search input parameters (for current widget) and results.
        """
        entityWidget = self.tbSTREntity.currentWidget()
        if isinstance(entityWidget, EntitySearchItem):
            entityWidget.reset()

        self._reset_controls()

    def _reset_controls(self):
        #Clear tree view
        self._resetTreeView()

        #Clear document listings
        self._deleteSourceDocTabs()

        #Remove spatial unit memory layer
        self.tbPropertyPreview.remove_layer()

    def on_select_results(self):
        """
        Slot which is raised when the selection
        is changed in the tree view
        selection model.
        """
        index = self.tvSTRResults.currentIndex()

        #Check type of node and perform corresponding action
        #for mi in selIndexes:
        if index.isValid():
            node = index.internalPointer()
            self.editSTR.setDisabled(True)
            self.deleteSTR.setDisabled(True)
            if index.column() == 0:
                # Assert if node represents another
                # entity has been clicked
                self._on_node_reference_changed(node.rootHash())

                if isinstance(node, SupportsDocumentsNode):

                    src_docs = node.documents()

                    if isinstance(src_docs, dict):
                        self._load_source_documents(src_docs)
                        # if there is supporting document,
                        # expand supporting document tab
                        if len(src_docs) > 0:
                            self.toolBox.setCurrentIndex(1)
                        if self._can_edit:
                            self.deleteSTR.setDisabled(False)
                        if self._can_delete:
                            self.editSTR.setDisabled(False)

                if isinstance(node, SpatialUnitNode):
                    # Expand the Spatial Unit preview
                    self.toolBox.setCurrentIndex(0)
                    self.draw_spatial_unit(node.model())
                    self.editSTR.setDisabled(True)
                    self.deleteSTR.setDisabled(True)

    def str_party_column_obj(self, record):
        """
        Gets the current party column name in STR
        table by finding party column with value
        other than None.
        :param record: The STR record or result.
        :type record: Dictionary
        :return: The party column name with value.
        :rtype: String
        """

        for party in self.parties:
            party_name = party.short_name.lower()
            party_id = '{}_id'.format(party_name)
            if party_id not in record.__dict__:
                return None
            if record.__dict__[party_id] != None:
                party_id_obj = getattr(self.str_model, party_id)
                return party_id_obj

    def load_edit_str_editor(self):

        index = self.tvSTRResults.currentIndex()
        node = None
        if index.isValid():
            node = index.internalPointer()
        if index.column() == 0:
            if isinstance(node, SupportsDocumentsNode):

                self.edit_str = EditSTREditor(node)

                status = self.edit_str.exec_()

                if status == 1:
                    self.btnSearch.click()
                    # if node._parent.typeInfo() == 'ENTITY_NODE':
                    #     node_party_id = self.str_party_column_obj(
                    #         node._model
                    #     )
                    #     edit_str_party_id = self.str_party_column_obj(
                    #         self.edit_str.updated_str_obj
                    #     )
                    #     if node_party_id is None or \
                    #                     edit_str_party_id is None:
                    #         return
                    #     if node_party_id == \
                    #             edit_str_party_id:
                    #         self.btnSearch.click()
                    #         index = node.treeView().model().index(0, 0)
                    #         node._on_expand(index)
                    #
                    # if node._parent.typeInfo() == 'SPATIAL_UNIT_NODE':
                    #     if node._model.spatial_unit_id == \
                    #             self.edit_str.updated_str_obj.spatial_unit_id:
                    #         self.btnSearch.click()
                    #         index = node.treeView().model().index(0, 0)
                    #         node._on_expand(index)

    def load_new_str_editor(self):
        try:
            # Check type of node and perform corresponding action
            add_str = STREditor()
            add_str.exec_()

        except Exception as ex:
            QMessageBox.critical(
                self._plugin.iface.mainWindow(),
                QApplication.translate("STDMPlugin", "Loading Error"),
                unicode(ex))

    def delete_str(self):
        index = self.tvSTRResults.currentIndex()
        node = None
        if index.isValid():
            node = index.internalPointer()
        if isinstance(node, SupportsDocumentsNode):
            node.onDelete(index)
            self.btnSearch.click()
            index = node.treeView().model().index(0, 0)
            node._on_expand(index)

    def onSourceDocumentRemoved(self, container_id, doc_uuid, removed_doc):
        """
        Slot raised when a source document is removed from the container.
        If there are no documents in the specified container then remove
        the tab.
        """
        curr_container = self.tbSupportingDocs.currentWidget()
        curr_doc_widget = curr_container.findChildren(DocumentWidget)

        for doc in curr_doc_widget:
            if doc.fileUUID == doc_uuid:
                doc.deleteLater()
        self.removed_docs = removed_doc

    def draw_spatial_unit(self, model):
        """
        Render the geometry of the given spatial unit in the spatial view.
        :param row_id: Sqlalchemy object representing a feature.
        """
        self.tbPropertyPreview.draw_spatial_unit(model)

    def onTreeViewItemExpanded(self, modelindex):
        """
        Raised when a tree view item is expanded.
        Reset the document listing and map view if the hash
        of the parent node is different.
        """
        if modelindex.isValid():
            node = modelindex.internalPointer()
            #Assert if node representing another entity has been clicked
            self._on_node_reference_changed(node.rootHash())

    def onResultsContextMenuRequested(self, pnt):
        """
        Slot raised when the user right-clicks
        on a node item to request the
        corresponding context menu.
        """
        #Get the model index at the specified point
        mi = self.tvSTRResults.indexAt(pnt)
        if mi.isValid():
            node = mi.internalPointer()
            rMenu = QMenu(self)
            #Load node actions items into the context menu
            node.manageActions(mi, rMenu)
            rMenu.exec_(QCursor.pos())

    def showEvent(self, event):
        """
        (Re)load map layers in the viewer and main canvas.
        :param event: Window event
        :type event: QShowEvent
        """
        self.setEnabled(True)
        QTimer.singleShot(200, self.init_mirror_map)
        #self.init_mirror_map()

        return QMainWindow.showEvent(self, event)

    def init_mirror_map(self):
        self._notify_no_base_layers()
        # Add spatial unit layer if it doesn't exist
        self.add_spatial_unit_layer()
        self.tbPropertyPreview.refresh_canvas_layers()
        self.tbPropertyPreview.load_web_map()

    def _notify_no_base_layers(self):
        """
        Checks if there are any base layers that will be used when
        visualizing the spatial units. If there are no base layers
        then insert warning message.
        """
        self._notif_search_config.clear()

        num_layers = len(self._plugin.iface.legendInterface().layers())
        if num_layers == 0:
            msg = QApplication.translate(
                "ViewSTR", "No basemap layers are loaded in the "
                "current project. Basemap layers "
                "enhance the visualization of spatial units.")
            self._notif_search_config.insertWarningNotification(msg)

    def _deleteSourceDocTabs(self):
        """
        Removes all source document tabs and deletes their references.
        """
        tabCount = self.tbSupportingDocs.count()

        while tabCount != 0:
            srcDocWidget = self.tbSupportingDocs.widget(tabCount - 1)
            self.tbSupportingDocs.removeTab(tabCount - 1)
            del srcDocWidget
            tabCount -= 1

        self._strID = None
        self._source_doc_manager.reset()

    def _resetTreeView(self):
        """
        Clears the results tree view.
        """
        #Reset tree view
        strModel = self.tvSTRResults.model()
        resultsSelModel = self.tvSTRResults.selectionModel()

        if strModel:
            strModel.clear()

        if resultsSelModel:
            self.disconnect(
                resultsSelModel,
                SIGNAL(
                    "selectionChanged(const QItemSelection&,const QItemSelection&)"
                ), self.on_select_results)

    def _load_root_node(self, root):
        """
        Load the search results (formatted into
        an object of type 'stdm.navigaion.STR') into
        the tree view.
        """
        strTreeViewModel = STRTreeViewModel(root, view=self.tvSTRResults)
        self.tvSTRResults.setModel(strTreeViewModel)

        # Resize tree columns to fit contents
        self._resize_columns()

        #Capture selection changes signals when
        # results are returned in the tree view
        resultsSelModel = self.tvSTRResults.selectionModel()
        resultsSelModel.currentChanged.connect(self.on_select_results)

    def _resize_columns(self):
        """
        Adjusts the column sizes to fit its contents
        """
        qModel = self.tvSTRResults.model()
        columnCount = qModel.columnCount()

        for i in range(columnCount):
            self.tvSTRResults.resizeColumnToContents(i)

        #Once resized then slightly increase the width
        # of the first column so that text for 'No STR Defined' visible.
        currColWidth = self.tvSTRResults.columnWidth(0)
        newColWidth = currColWidth + 100
        self.tvSTRResults.setColumnWidth(0, newColWidth)

    def _load_source_documents(self, source_docs):
        """
        Load source documents into document listing widget.
        """
        # Configure progress dialog
        progress_msg = QApplication.translate(
            "ViewSTR", "Loading supporting documents...")

        progress_dialog = QProgressDialog(self)
        if len(source_docs) > 0:
            progress_dialog.setWindowTitle(progress_msg)
            progress_dialog.setRange(0, len(source_docs))
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setFixedWidth(380)
            progress_dialog.show()
            progress_dialog.setValue(0)
        self._notif_search_config.clear()

        self.tbSupportingDocs.clear()
        self._source_doc_manager.reset()

        if len(source_docs) < 1:
            empty_msg = QApplication.translate(
                'ViewSTR', 'No supporting document is uploaded '
                'for this social tenure relationship.')
            self._notif_search_config.clear()
            self._notif_search_config.insertWarningNotification(empty_msg)

        for i, (doc_type_id, doc_obj) in enumerate(source_docs.iteritems()):

            # add tabs, and container and widget for each tab
            tab_title = self._source_doc_manager.doc_type_mapping[doc_type_id]

            tab_widget = QWidget()
            tab_widget.setObjectName(tab_title)

            cont_layout = QVBoxLayout(tab_widget)
            cont_layout.setObjectName('widget_layout_' + tab_title)

            scrollArea = QScrollArea(tab_widget)
            scrollArea.setFrameShape(QFrame.NoFrame)

            scrollArea_contents = QWidget()
            scrollArea_contents.setObjectName('tab_scroll_area_' + tab_title)

            tab_layout = QVBoxLayout(scrollArea_contents)
            tab_layout.setObjectName('layout_' + tab_title)

            scrollArea.setWidgetResizable(True)

            scrollArea.setWidget(scrollArea_contents)
            cont_layout.addWidget(scrollArea)

            self._source_doc_manager.registerContainer(tab_layout, doc_type_id)

            for doc in doc_obj:

                try:
                    # add doc widgets
                    self._source_doc_manager.insertDocFromModel(
                        doc, doc_type_id)
                except Exception as ex:
                    LOGGER.debug(str(ex))

            self.tbSupportingDocs.addTab(tab_widget, tab_title)
            progress_dialog.setValue(i + 1)

    def _on_node_reference_changed(self, rootHash):
        """
        Method for resetting document listing and map preview
        if another root node and its children
        are selected then the documents are reset as
        well as the map preview control.
        """
        if rootHash != self._curr_rootnode_hash:
            self._deleteSourceDocTabs()
            self._curr_rootnode_hash = rootHash

    def _progressStart(self):
        """
        Load progress dialog window.
        For items whose durations is unknown,
        'isindefinite' = True by default.
        If 'isindefinite' is False, then
        'rangeitems' has to be specified.
        """
        pass

    def _progressFinish(self):
        """
        Hide progress dialog window.
        """
        pass

    def _edit_permissions(self):
        """
        Returns True/False whether the current logged in user
        has permissions to create new social tenure relationships.
        If true, then the system assumes that
        they can also edit STR records.
        """
        canEdit = False
        userName = stdm.data.app_dbconn.User.UserName
        authorizer = Authorizer(userName)
        newSTRCode = "9576A88D-C434-40A6-A318-F830216CA15A"

        #Get the name of the content from the code
        cnt = Content()
        createSTRCnt = cnt.queryObject().filter(
            Content.code == newSTRCode).first()
        if createSTRCnt:
            name = createSTRCnt.name
            canEdit = authorizer.CheckAccess(name)

        return canEdit
Пример #10
0
    def __init__(self, plugin):
        QMainWindow.__init__(self, plugin.iface.mainWindow())
        self.setupUi(self)

        self._plugin = plugin

        self.search_done = False
        # self.tbPropertyPreview.set_iface(self._plugin.iface)
        QTimer.singleShot(
            100, lambda: self.tbPropertyPreview.set_iface(self._plugin.iface))

        self.curr_profile = current_profile()

        self.spatial_units = self.curr_profile.social_tenure.spatial_units
        #Center me
        self.move(QDesktopWidget().availableGeometry().center() -
                  self.frameGeometry().center())
        self.sp_unit_manager = SpatialUnitManagerDockWidget(
            self._plugin.iface, self._plugin
        )
        self.geom_cols = []
        for spatial_unit in self.spatial_units:
            each_geom_col = self.sp_unit_manager.geom_columns(spatial_unit)
            self.geom_cols.extend(each_geom_col)

        # Configure notification bar
        self._notif_search_config = NotificationBar(
            self.vl_notification
        )

        # set whether currently logged in user has
        # permissions to edit existing STR records
        self._can_edit = self._plugin.STRCntGroup.canUpdate()
        self._can_delete = self._plugin.STRCntGroup.canDelete()
        self._can_create = self._plugin.STRCntGroup.canCreate()
        # Variable used to store a reference to the
        # currently selected social tenure relationship
        # when displaying documents in the supporting documents tab window.
        # This ensures that there are no duplicates
        # when the same item is selected over and over again.

        self._strID = None
        self.removed_docs = None
        #Used to store the root hash of the currently selected node.
        self._curr_rootnode_hash = ""


        self.str_model, self.str_doc_model = entity_model(
            self.curr_profile.social_tenure, False, True
        )

        self._source_doc_manager = SourceDocumentManager(
            self.curr_profile.social_tenure.supporting_doc,
            self.str_doc_model,
            self
        )

        self._source_doc_manager.documentRemoved.connect(
            self.onSourceDocumentRemoved
        )

        self._source_doc_manager.setEditPermissions(False)

        self.initGui()
        # if self._plugin.details_tree_view is None:
        # self._plugin.details_dock.init_dock()
        self.add_spatial_unit_layer()
        self.details_tree_view = DetailsTreeView(iface, self._plugin, self)
        # else:
        #     self.details_tree_view = self._plugin.details_tree_view
        self.details_tree_view.activate_feature_details(True)
        self.details_tree_view.add_tree_view()
        self.details_tree_view.model.clear()

        count = pg_table_count(self.curr_profile.social_tenure.name)
        self.setWindowTitle(
            self.tr(u'{}{}'.format(
                self.windowTitle(), '- ' + str(count) +' rows'
            ))
        )

        self.toolBox.setStyleSheet(
            '''
            QToolBox::tab {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #EDEDED, stop: 0.4 #EDEDED,
                    stop: 0.5 #EDEDED, stop: 1.0 #D3D3D3
                );
                border-radius: 2px;
                border-style: outset;
                border-width: 2px;
                height: 100px;
                border-color: #C3C3C3;
            }

            QToolBox::tab:selected {
                font: italic;
            }
            '''
        )

        self.details_tree_view.view.setStyleSheet(
            '''
            QTreeView:!active {
                selection-background-color: #72a6d9;
            }
            '''
        )
Пример #11
0
class ViewSTRWidget(QMainWindow, Ui_frmManageSTR):
    """
    Search and browse the social tenure relationship
    of all participating entities.
    """
    def __init__(self, plugin):
        QMainWindow.__init__(self, plugin.iface.mainWindow())
        self.setupUi(self)

        self._plugin = plugin

        self.search_done = False
        # self.tbPropertyPreview.set_iface(self._plugin.iface)
        QTimer.singleShot(
            100, lambda: self.tbPropertyPreview.set_iface(self._plugin.iface))

        self.curr_profile = current_profile()

        self.spatial_units = self.curr_profile.social_tenure.spatial_units
        #Center me
        self.move(QDesktopWidget().availableGeometry().center() -
                  self.frameGeometry().center())
        self.sp_unit_manager = SpatialUnitManagerDockWidget(
            self._plugin.iface, self._plugin
        )
        self.geom_cols = []
        for spatial_unit in self.spatial_units:
            each_geom_col = self.sp_unit_manager.geom_columns(spatial_unit)
            self.geom_cols.extend(each_geom_col)

        # Configure notification bar
        self._notif_search_config = NotificationBar(
            self.vl_notification
        )

        # set whether currently logged in user has
        # permissions to edit existing STR records
        self._can_edit = self._plugin.STRCntGroup.canUpdate()
        self._can_delete = self._plugin.STRCntGroup.canDelete()
        self._can_create = self._plugin.STRCntGroup.canCreate()
        # Variable used to store a reference to the
        # currently selected social tenure relationship
        # when displaying documents in the supporting documents tab window.
        # This ensures that there are no duplicates
        # when the same item is selected over and over again.

        self._strID = None
        self.removed_docs = None
        #Used to store the root hash of the currently selected node.
        self._curr_rootnode_hash = ""


        self.str_model, self.str_doc_model = entity_model(
            self.curr_profile.social_tenure, False, True
        )

        self._source_doc_manager = SourceDocumentManager(
            self.curr_profile.social_tenure.supporting_doc,
            self.str_doc_model,
            self
        )

        self._source_doc_manager.documentRemoved.connect(
            self.onSourceDocumentRemoved
        )

        self._source_doc_manager.setEditPermissions(False)

        self.initGui()
        # if self._plugin.details_tree_view is None:
        # self._plugin.details_dock.init_dock()
        self.add_spatial_unit_layer()
        self.details_tree_view = DetailsTreeView(iface, self._plugin, self)
        # else:
        #     self.details_tree_view = self._plugin.details_tree_view
        self.details_tree_view.activate_feature_details(True)
        self.details_tree_view.add_tree_view()
        self.details_tree_view.model.clear()

        count = pg_table_count(self.curr_profile.social_tenure.name)
        self.setWindowTitle(
            self.tr(u'{}{}'.format(
                self.windowTitle(), '- ' + str(count) +' rows'
            ))
        )

        self.toolBox.setStyleSheet(
            '''
            QToolBox::tab {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #EDEDED, stop: 0.4 #EDEDED,
                    stop: 0.5 #EDEDED, stop: 1.0 #D3D3D3
                );
                border-radius: 2px;
                border-style: outset;
                border-width: 2px;
                height: 100px;
                border-color: #C3C3C3;
            }

            QToolBox::tab:selected {
                font: italic;
            }
            '''
        )

        self.details_tree_view.view.setStyleSheet(
            '''
            QTreeView:!active {
                selection-background-color: #72a6d9;
            }
            '''
        )

    def add_tool_buttons(self):
        """
        Add toolbar buttons of add, edit and delete buttons.
        :return: None
        :rtype: NoneType
        """
        tool_buttons = QToolBar()
        tool_buttons.setObjectName('form_toolbar')
        tool_buttons.setIconSize(QSize(16, 16))

        self.addSTR = QAction(QIcon(
            ':/plugins/stdm/images/icons/add.png'),
            QApplication.translate('ViewSTRWidget', 'Add'),
            self
        )

        self.editSTR = QAction(
            QIcon(':/plugins/stdm/images/icons/edit.png'),
            QApplication.translate('ViewSTRWidget', 'Edit'),
            self
        )

        self.deleteSTR = QAction(
            QIcon(':/plugins/stdm/images/icons/remove.png'),
            QApplication.translate('ViewSTRWidget', 'Remove'),
            self
        )

        tool_buttons.addAction(self.addSTR)
        tool_buttons.addAction(self.editSTR)
        tool_buttons.addAction(self.deleteSTR)

        self.toolbarVBox.addWidget(tool_buttons)

    def initGui(self):
        """
        Initialize widget
        """
        self.tb_actions.setVisible(False)
        self._load_entity_configurations()

        self.add_tool_buttons()

        #Connect signals
        self.tbSTREntity.currentChanged.connect(self.entityTabIndexChanged)
        self.btnSearch.clicked.connect(self.searchEntityRelations)
        self.btnClearSearch.clicked.connect(self.clearSearch)
        # self.tvSTRResults.expanded.connect(self.onTreeViewItemExpanded)

        #Set the results treeview to accept requests for context menus
        # self.tvSTRResults.setContextMenuPolicy(Qt.CustomContextMenu)
        # self.tvSTRResults.customContextMenuRequested.connect(
        #     self.onResultsContextMenuRequested
        # )

        if not self._can_create:
            self.addSTR.hide()

        if not self._can_edit:
            self.editSTR.hide()
        else:
            self.editSTR.setDisabled(True)
        if not self._can_delete:
            self.deleteSTR.hide()
        else:
            self.deleteSTR.setDisabled(True)

        self.addSTR.triggered.connect(self.load_new_str_editor)

        self.deleteSTR.triggered.connect(self.delete_str)

        self.editSTR.triggered.connect(self.load_edit_str_editor)

        #Load async for the current widget
        self.entityTabIndexChanged(0)

    def init_progress_dialog(self):
        """
        Initializes the progress dialog.
        """
        self.progress = QProgressBar(self)
        self.progress.resize(self.width(), 10)
        self.progress.setTextVisible(False)

    def add_spatial_unit_layer(self):
        """
        Add the spatial unit layer into the map canvas for later use.
        """
        # Used for startup of view STR, just add the first geom layer.
        if len(self.geom_cols) > 0:
            for spatial_unit in self.spatial_units:
                layer_name_item = self.sp_unit_manager.geom_col_layer_name(
                    spatial_unit.name,
                    self.geom_cols[0]
                )
                self.sp_unit_manager.add_layer_by_name(layer_name_item)

    def _check_permissions(self):
        """
        Enable/disable actions based on the
        permissions defined in the content
        group.
        """
        if self._can_edit:
            self.tb_actions.addAction(self._new_str_action)
        else:
            self.tb_actions.removeAction(self._new_str_action)

        if len(self.tb_actions.actions()) == 0:
            self.tb_actions.setVisible(False)

        else:
            self.tb_actions.setVisible(True)

    def _load_entity_configurations(self):
        """
        Specify the entity configurations.
        """
        try:
            self.parties = self.curr_profile.social_tenure.parties
            tb_str_entities = self.parties + self.spatial_units

            for i, t in enumerate(tb_str_entities):
                QApplication.processEvents()
                entity_cfg = self._entity_config_from_profile(
                    str(t.name), t.short_name
                )

                if not entity_cfg is None:
                    entity_widget = self.add_entity_config(entity_cfg)

                    # entity_widget.setNodeFormatter(
                    #     EntityNodeFormatter(
                    #         entity_cfg, self.tvSTRResults, self
                    #     )
                    # )

        except Exception as pe:
            self._notif_search_config.clear()
            self._notif_search_config.insertErrorNotification(unicode(pe))

    def _entity_config_from_profile(self, table_name, short_name):
        """
        Creates an EntityConfig object from the table name.
        :param table_name: Name of the database table.
        :type table_name: str
        :return: Entity configuration object.
        :rtype: EntityConfig
        """
        table_display_name = format_name(short_name)

        entity = self.curr_profile.entity_by_name(table_name)
        model = entity_model(entity)

        if model is not None:
            #Entity configuration
            entity_cfg = EntityConfiguration()
            entity_cfg.Title = table_display_name
            entity_cfg.STRModel = model
            entity_cfg.data_source_name = table_name
            for col, factory in self._get_widget_factory(entity):
                entity_cfg.LookupFormatters[col.name] = factory

            # Load filter and display columns
            # using only those which are of
            # numeric/varchar type
            searchable_columns = entity_searchable_columns(entity)
            display_columns = entity_display_columns(entity)
            for c in searchable_columns:
                if c != 'id':
                    entity_cfg.filterColumns[c] = format_name(c)
            for c in display_columns:
                if c != 'id':
                    entity_cfg.displayColumns[c] = format_name(c)
            return entity_cfg
        else:
            return None

    def _get_widget_factory(self, entity):
        """
        Get widget factory for specific column type
        :param entity: Current column entity object
        :type entity: Entity
        :return c: Column object corresponding to the widget factory
        :rtype c: BaseColumn
        :return col_factory: Widget factory corresponding to the column type
        :rtype col_factory: ColumnWidgetRegistry
        """
        for c in entity.columns.values():
            col_factory = ColumnWidgetRegistry.factory(c.TYPE_INFO)
            if col_factory is not None:
                yield c, col_factory(c)

    def add_entity_config(self, config):
        """
        Set an entity configuration option and
        add it to the 'Search Entity' tab.
        """
        entityWidg = STRViewEntityWidget(config)
        entityWidg.asyncStarted.connect(self._progressStart)
        entityWidg.asyncFinished.connect(self._progressFinish)

        tabIndex = self.tbSTREntity.addTab(entityWidg, config.Title)

        return entityWidg

    def entityTabIndexChanged(self, index):
        """
        Raised when the tab index of the entity search tab widget changes.
        """
        #Get the current widget in the tab container
        entityWidget = self.tbSTREntity.currentWidget()

        if isinstance(entityWidget, EntitySearchItem):
            entityWidget.loadAsync()

    def searchEntityRelations(self):
        """
        Slot that searches for matching items for
        the specified entity and corresponding STR entities.
        """

        entityWidget = self.tbSTREntity.currentWidget()

        entity_name = entityWidget.config.data_source_name

        self._reset_controls()

        if isinstance(entityWidget,EntitySearchItem):
            valid, msg = entityWidget.validate()

            if not valid:
                self._notif_search_config.clear()
                self._notif_search_config.insertErrorNotification(msg)

                return

            results, searchWord = entityWidget.executeSearch()

            #Show error message
            if len(results) == 0:
                noResultsMsg = QApplication.translate(
                    'ViewSTR',
                    'No results found for "{}"'.format(searchWord)
                )
                self._notif_search_config.clear()
                self._notif_search_config.insertErrorNotification(
                    noResultsMsg
                )

                return

            party_names = [e.name for e in self.curr_profile.social_tenure.parties]
            entity = self.curr_profile.entity_by_name(entity_name)

            result_ids = [r.id for r in results]

            if entity_name in party_names:

                self.details_tree_view.search_party(
                    entity, result_ids
                )
            else:
                self.details_tree_view.search_spatial_unit(
                    entity, result_ids
                )
            #self._load_root_node(entity_name, formattedNode)

    def clearSearch(self):
        """
        Clear search input parameters (for current widget) and results.
        """
        entityWidget = self.tbSTREntity.currentWidget()
        if isinstance(entityWidget, EntitySearchItem):
            entityWidget.reset()
        self._reset_controls()

    def _reset_controls(self):
        #Clear tree view
        self._resetTreeView()

        #Clear document listings
        self._deleteSourceDocTabs()

        #Remove spatial unit memory layer
        self.tbPropertyPreview.remove_layer()

    def on_select_results(self):
        """
        Slot which is raised when the selection
        is changed in the tree view
        selection model.
        """

        if len(self.details_tree_view.view.selectedIndexes()) < 1:
            self.disable_buttons()
            return
        self.search_done = True
        index = self.details_tree_view.view.selectedIndexes()[0]
        item = self.details_tree_view.model.itemFromIndex(index)

        QApplication.processEvents()
        # STR steam - edit social tenure relationship
        if item.text() == self.details_tree_view.str_text:
            entity = self.curr_profile.social_tenure
            str_model = self.details_tree_view.str_models[item.data()]
            documents = self.details_tree_view._supporting_doc_models(
                entity.name, str_model
            )

            self._load_source_documents(documents)
            # if there is supporting document,
            # expand supporting document tab
            if len(documents) > 0:
                self.toolBox.setCurrentIndex(1)
            self.disable_buttons(False)

        # party steam - edit party
        elif item in self.details_tree_view.spatial_unit_items.keys():
            self.toolBox.setCurrentIndex(0)
            entity = self.details_tree_view.spatial_unit_items[item]

            model = self.details_tree_view.feature_model(entity, item.data())
            self.draw_spatial_unit(entity.name, model)
            self.disable_buttons()
        else:
            self.disable_buttons()

    def disable_buttons(self, status=True):
        if self._can_edit:
            self.deleteSTR.setDisabled(status)
        if self._can_delete:
            self.editSTR.setDisabled(status)

    def str_party_column_obj(self, record):
        """
        Gets the current party column name in STR
        table by finding party column with value
        other than None.
        :param record: The STR record or result.
        :type record: Dictionary
        :return: The party column name with value.
        :rtype: String
        """
        for party in self.parties:
            party_name = party.short_name.lower()
            party_id = '{}_id'.format(party_name)
            if party_id not in record.__dict__:
                return None
            if record.__dict__[party_id] != None:
                party_id_obj = getattr(self.str_model, party_id)
                return party_id_obj

    def load_edit_str_editor(self):
        self.details_tree_view.edit_selected_node()
        self.btnSearch.click()
        self.disable_buttons()

    def load_new_str_editor(self):
        try:
            # Check type of node and perform corresponding action
            add_str = STREditor()
            add_str.exec_()

        except Exception as ex:
            QMessageBox.critical(
                self._plugin.iface.mainWindow(),
                QApplication.translate(
                    "STDMPlugin",
                    "Loading Error"
                ),
                unicode(ex)
            )

    def delete_str(self):
        self.details_tree_view.delete_selected_item()
        self.btnSearch.click()
        self.disable_buttons()

    def onSourceDocumentRemoved(self, container_id, doc_uuid, removed_doc):
        """
        Slot raised when a source document is removed from the container.
        If there are no documents in the specified container then remove
        the tab.
        """
        curr_container = self.tbSupportingDocs.currentWidget()
        curr_doc_widget = curr_container.findChildren(DocumentWidget)

        for doc in curr_doc_widget:
            if doc.fileUUID == doc_uuid:
                doc.deleteLater()
        self.removed_docs = removed_doc

    def draw_spatial_unit(self, entity_name, model):
        """
        Render the geometry of the given spatial unit in the spatial view.
        :param row_id: Sqlalchemy object representing a feature.
        """
        entity = self.curr_profile.entity_by_name(entity_name)

        self.tbPropertyPreview.draw_spatial_unit(entity, model)

    def showEvent(self, event):
        """
        (Re)load map layers in the viewer and main canvas.
        :param event: Window event
        :type event: QShowEvent
        """
        self.setEnabled(True)
        if QTimer is not None:
            QTimer.singleShot(200, self.init_mirror_map)

        return QMainWindow.showEvent(self, event)

    def init_mirror_map(self):
        self._notify_no_base_layers()
        # Add spatial unit layer if it doesn't exist
        self.tbPropertyPreview.refresh_canvas_layers()
        self.tbPropertyPreview.load_web_map()

    def _notify_no_base_layers(self):
        """
        Checks if there are any base layers that will be used when
        visualizing the spatial units. If there are no base layers
        then insert warning message.
        """
        self._notif_search_config.clear()

        num_layers = len(self._plugin.iface.legendInterface().layers())
        if num_layers == 0:
            msg = QApplication.translate(
                "ViewSTR",
                "No basemap layers are loaded in the "
                "current project. Basemap layers "
                "enhance the visualization of spatial units."
            )
            self._notif_search_config.insertWarningNotification(msg)

    def _deleteSourceDocTabs(self):
        """
        Removes all source document tabs and deletes their references.
        """
        tabCount = self.tbSupportingDocs.count()

        while tabCount != 0:
            srcDocWidget = self.tbSupportingDocs.widget(tabCount-1)
            self.tbSupportingDocs.removeTab(tabCount-1)
            del srcDocWidget
            tabCount -= 1

        self._strID = None
        self._source_doc_manager.reset()

    def _resetTreeView(self):
        """
        Clears the results tree view.
        """
        #Reset tree view
        strModel = self.details_tree_view.view.model()
        resultsSelModel = self.details_tree_view.view.selectionModel()

        if strModel:
            strModel.clear()

        if resultsSelModel:
            if self.search_done:
                resultsSelModel.selectionChanged.disconnect(self.on_select_results)
            resultsSelModel.selectionChanged.connect(self.on_select_results)

    def _load_source_documents(self, source_docs):
        """
        Load source documents into document listing widget.
        """
        # Configure progress dialog
        progress_msg = QApplication.translate(
            "ViewSTR", "Loading supporting documents..."
        )

        progress_dialog = QProgressDialog(self)
        if len(source_docs) > 0:
            progress_dialog.setWindowTitle(progress_msg)
            progress_dialog.setRange(0, len(source_docs))
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setFixedWidth(380)
            progress_dialog.show()
            progress_dialog.setValue(0)
        self._notif_search_config.clear()

        self.tbSupportingDocs.clear()
        self._source_doc_manager.reset()

        if len(source_docs) < 1:
            empty_msg = QApplication.translate(
                'ViewSTR', 'No supporting document is uploaded '
                           'for this social tenure relationship.'
            )
            self._notif_search_config.clear()
            self._notif_search_config.insertWarningNotification(empty_msg)

        for i, (doc_type_id, doc_obj) in enumerate(source_docs.iteritems()):

            # add tabs, and container and widget for each tab
            tab_title = self._source_doc_manager.doc_type_mapping[doc_type_id]


            tab_widget = QWidget()
            tab_widget.setObjectName(tab_title)

            cont_layout = QVBoxLayout(tab_widget)
            cont_layout.setObjectName('widget_layout_' + tab_title)

            scrollArea = QScrollArea(tab_widget)
            scrollArea.setFrameShape(QFrame.NoFrame)

            scrollArea_contents = QWidget()
            scrollArea_contents.setObjectName('tab_scroll_area_' + tab_title)

            tab_layout = QVBoxLayout(scrollArea_contents)
            tab_layout.setObjectName('layout_' + tab_title)

            scrollArea.setWidgetResizable(True)

            scrollArea.setWidget(scrollArea_contents)
            cont_layout.addWidget(scrollArea)

            self._source_doc_manager.registerContainer(
                tab_layout, doc_type_id
            )

            for doc in doc_obj:

                try:
                    # add doc widgets
                    self._source_doc_manager.insertDocFromModel(
                        doc, doc_type_id
                    )
                except Exception as ex:
                    LOGGER.debug(str(ex))

            self.tbSupportingDocs.addTab(
                tab_widget, tab_title
            )
            progress_dialog.setValue(i + 1)

    # def _on_node_reference_changed(self, rootHash):
    #     """
    #     Method for resetting document listing and map preview
    #     if another root node and its children
    #     are selected then the documents are reset as
    #     well as the map preview control.
    #     """
    #     if rootHash != self._curr_rootnode_hash:
    #         self._deleteSourceDocTabs()
    #         self._curr_rootnode_hash = rootHash

    def _progressStart(self):
        """
        Load progress dialog window.
        For items whose durations is unknown,
        'isindefinite' = True by default.
        If 'isindefinite' is False, then
        'rangeitems' has to be specified.
        """
        pass

    def _progressFinish(self):
        """
        Hide progress dialog window.
        """
        pass

    def _edit_permissions(self):
        """
        Returns True/False whether the current logged in user
        has permissions to create new social tenure relationships.
        If true, then the system assumes that
        they can also edit STR records.
        """
        canEdit = False
        userName = stdm.data.app_dbconn.User.UserName
        authorizer = Authorizer(userName)
        newSTRCode = "9576A88D-C434-40A6-A318-F830216CA15A"

        #Get the name of the content from the code
        cnt = Content()
        createSTRCnt = cnt.queryObject().filter(
            Content.code == newSTRCode
        ).first()
        if createSTRCnt:
            name = createSTRCnt.name
            canEdit = authorizer.CheckAccess(name)

        return canEdit
Пример #12
0
    def __init__(self,entity, parent=None, state=MANAGE, load_records=True, plugin=None):
        EntityBrowser.__init__(self, entity, parent, state, load_records, plugin)
        self.record_id = 0

        self.highlight = None
        self.load_records = load_records
        self.selection_layer = None

        #Add action toolbar if the state contains Manage flag
        if (state & MANAGE) != 0:
            add = QApplication.translate("EntityBrowserWithEditor", "Add")
            edit = QApplication.translate("EntityBrowserWithEditor","Edit")
            remove = QApplication.translate("EntityBrowserWithEditor", "Remove")

            self._newEntityAction = QAction(QIcon(":/plugins/stdm/images/icons/add.png"),
                                            add, self)

            self.connect(self._newEntityAction,SIGNAL("triggered()"),self.onNewEntity)

            self._editEntityAction = QAction(QIcon(":/plugins/stdm/images/icons/edit.png"),
                                             edit,self)
            self._editEntityAction.setObjectName(
                QApplication.translate("EntityBrowserWithEditor", "edit_tool")
            )


            self.connect(self._editEntityAction,SIGNAL("triggered()"),self.onEditEntity)

            self._removeEntityAction = QAction(QIcon(":/plugins/stdm/images/icons/remove.png"),
                                  remove, self)
            self._removeEntityAction.setObjectName(
                QApplication.translate("EntityBrowserWithEditor", "remove_tool")
            )
            self.connect(self._removeEntityAction,SIGNAL("triggered()"),self.onRemoveEntity)

            #Manage position of the actions based on whether the entity
            # supports documents.
            if self.can_view_supporting_documents:
                manage_acts = [self._newEntityAction, self._editEntityAction,
                               self._removeEntityAction]
                self.tbActions.insertActions(self._view_docs_act, manage_acts)

                #Add action separator
                self._act_sep = QAction(self)
                self._act_sep.setSeparator(True)
                self.tbActions.insertAction(self._view_docs_act, self._act_sep)

            else:
                self.tbActions.addAction(self._newEntityAction)
                self.tbActions.addAction(self._editEntityAction)
                self.tbActions.addAction(self._removeEntityAction)

            if isinstance(parent, EntityEditorDialog):
                if self.can_view_supporting_documents:
                    self._view_docs_act.setVisible(False)
                self.parent_entity = parent._entity

            else:
                self.parent_entity = None

            # hide the add button and add layer preview for spatial entity
            if entity.has_geometry_column(): #and self.parent_entity is None:
                self.sp_unit_manager = SpatialUnitManagerDockWidget(
                        iface, self.plugin)
                #self.sp_unit_manager = self.plugin.spatialLayerManagerDockWidget

                self.geom_cols = self.sp_unit_manager.geom_columns(
                    self._entity
                )

                self.add_spatial_unit_layer()

                self.tbEntity.clicked.connect(
                    self.on_select_attribute
                )

                self.tbEntity.entered.connect(
                    self.on_select_attribute
                )

                self.shift_spatial_entity_browser()

                # Hide the add button from spatial tables
                # self._newEntityAction.setVisible(False)

            self._editor_dlg = EntityEditorDialog
Пример #13
0
class EntityBrowserWithEditor(EntityBrowser):
    """
    Entity browser with added functionality for carrying out CRUD operations
    directly.
    """
    def __init__(self,entity, parent=None, state=MANAGE, load_records=True, plugin=None):
        EntityBrowser.__init__(self, entity, parent, state, load_records, plugin)
        self.record_id = 0

        self.highlight = None
        self.load_records = load_records
        self.selection_layer = None

        #Add action toolbar if the state contains Manage flag
        if (state & MANAGE) != 0:
            add = QApplication.translate("EntityBrowserWithEditor", "Add")
            edit = QApplication.translate("EntityBrowserWithEditor","Edit")
            remove = QApplication.translate("EntityBrowserWithEditor", "Remove")

            self._newEntityAction = QAction(QIcon(":/plugins/stdm/images/icons/add.png"),
                                            add, self)

            self.connect(self._newEntityAction,SIGNAL("triggered()"),self.onNewEntity)

            self._editEntityAction = QAction(QIcon(":/plugins/stdm/images/icons/edit.png"),
                                             edit,self)
            self._editEntityAction.setObjectName(
                QApplication.translate("EntityBrowserWithEditor", "edit_tool")
            )


            self.connect(self._editEntityAction,SIGNAL("triggered()"),self.onEditEntity)

            self._removeEntityAction = QAction(QIcon(":/plugins/stdm/images/icons/remove.png"),
                                  remove, self)
            self._removeEntityAction.setObjectName(
                QApplication.translate("EntityBrowserWithEditor", "remove_tool")
            )
            self.connect(self._removeEntityAction,SIGNAL("triggered()"),self.onRemoveEntity)

            #Manage position of the actions based on whether the entity
            # supports documents.
            if self.can_view_supporting_documents:
                manage_acts = [self._newEntityAction, self._editEntityAction,
                               self._removeEntityAction]
                self.tbActions.insertActions(self._view_docs_act, manage_acts)

                #Add action separator
                self._act_sep = QAction(self)
                self._act_sep.setSeparator(True)
                self.tbActions.insertAction(self._view_docs_act, self._act_sep)

            else:
                self.tbActions.addAction(self._newEntityAction)
                self.tbActions.addAction(self._editEntityAction)
                self.tbActions.addAction(self._removeEntityAction)

            if isinstance(parent, EntityEditorDialog):
                if self.can_view_supporting_documents:
                    self._view_docs_act.setVisible(False)
                self.parent_entity = parent._entity

            else:
                self.parent_entity = None

            # hide the add button and add layer preview for spatial entity
            if entity.has_geometry_column(): #and self.parent_entity is None:
                self.sp_unit_manager = SpatialUnitManagerDockWidget(
                        iface, self.plugin)
                #self.sp_unit_manager = self.plugin.spatialLayerManagerDockWidget

                self.geom_cols = self.sp_unit_manager.geom_columns(
                    self._entity
                )

                self.add_spatial_unit_layer()

                self.tbEntity.clicked.connect(
                    self.on_select_attribute
                )

                self.tbEntity.entered.connect(
                    self.on_select_attribute
                )

                self.shift_spatial_entity_browser()

                # Hide the add button from spatial tables
                # self._newEntityAction.setVisible(False)

            self._editor_dlg = EntityEditorDialog

    def onNewEntity(self):
        '''
        Load editor dialog for adding a new record.
        '''
        self._notifBar.clear()

        if not self._can_add_edit():
            msg = QApplication.translate(
                'EntityBrowserWithEditor',
                'There are no user-defined columns for this entity.'
            )
            self._notifBar.insertErrorNotification(msg)

            return
        if self._entity.has_geometry_column():
            self.sp_unit_manager.active_layer_source()

            gps_tool = GPSToolDialog(
                iface,
                self._entity,
                self._entity.name,
                self.sp_unit_manager.active_sp_col,
                reload=False,
                entity_browser=self
            )

            result = gps_tool.exec_()
            result = False # a workaround to avoid duplicate model insert
            self.addEntityDlg = gps_tool.entity_editor
        else:
            self.addEntityDlg = self._editor_dlg(
                self._entity, parent=self, parent_entity=self.parent_entity, plugin=self.plugin
            )

            self.addEntityDlg.addedModel.connect(self.on_save_and_new)

            result = self.addEntityDlg.exec_()

        if result == QDialog.Accepted:
            model_obj = self.addEntityDlg.model()
            if self.addEntityDlg.is_valid:
                if self.parent_entity is None:
                    self.addModelToView(model_obj)
                    self.recomputeRecordCount()

    def on_save_and_new(self, model):
        """
        A slot raised when save and new button is clicked. It updates
        the entity browser with the new model added.
        :param model: The model saved.
        :type model: SQL Alchemy Model
        """
        if model is not None:
            insert_position = self.addModelToView(model)
            self.set_child_model(model, insert_position + 1)
            self.recomputeRecordCount()

    def _can_add_edit(self):
        """
        Check if there are columns specified (apart from id) for the given
        entity.
        :return: Returns True if there are other columns apart from id,
        otherwise False.
        """
        columns = self._entity.columns.values()

        if len(columns) < 2:
            return False

        return True

    def onEditEntity(self):
        '''
        Slot raised to load the editor for the selected row.
        '''
        self._notifBar.clear()


        if not self._can_add_edit():
            msg = QApplication.translate(
                'EntityBrowserWithEditor',
                'There are no user-defined columns for this entity.'
            )
            self._notifBar.insertErrorNotification(msg)

            return
        if self.tbEntity.selectionModel() is None:
            return

        selRowIndices = self.tbEntity.selectionModel().selectedRows(0)

        if len(selRowIndices) == 0:
            msg = QApplication.translate("EntityBrowserWithEditor",
                                         "Please select a record in the table"
                                         " below for editing.")
            self._notifBar.insertWarningNotification(msg)

            return

        #Exit if more than one record has been selected
        if len(selRowIndices) > 1:
            msg = QApplication.translate(
                "EntityBrowserWithEditor",
                "Multiple selection detected, please choose one record "
                "only for editing."
            )
            self._notifBar.insertWarningNotification(msg)

            return

        rowIndex = self._proxyModel.mapToSource(selRowIndices[0])
        recordid = rowIndex.data()

        self._load_editor_dialog(recordid, rowIndex.row())

    def set_child_model(self, model, row_position):
        """
        Sets the child model so that it can be used for editing and deleting if
        needed.
        :param model: The child model saved
        :type model: Object
        """
        self.child_model[row_position] = model


    def onRemoveEntity(self):
        '''
        Load editor dialog for editing an existing record.
        '''
        self._notifBar.clear()

        sel_row_indices = self.tbEntity.selectionModel().selectedRows(0)


        if len(sel_row_indices) == 0:
            msg = QApplication.translate(
                "EntityBrowserWithEditor",
                "Please select one or more records in the "
                "table below to be deleted."
            )
            self._notifBar.insertWarningNotification(msg)

            return

        msg = QApplication.translate(
            "EntityBrowserWithEditor",
            "Are you sure you want to delete the selected record(s)?\n"
            "This action cannot be undone."
        )

        response = QMessageBox.warning(
            self,
            QApplication.translate(
                "EntityBrowserWithEditor",
                "Delete Record(s)"),
            msg,
            QMessageBox.Yes|QMessageBox.No, QMessageBox.No
        )

        if response == QMessageBox.No:
            return

        while len(sel_row_indices) > 0:

            ri = sel_row_indices[0]
            source_row_index = self._proxyModel.mapToSource(ri)
            record_id = source_row_index.data()

            row_number = source_row_index.row()

            #Delete record
            result = self._delete_record(record_id, row_number)

            if not result:
                title = QApplication.translate(
                'EntityBrowserWithEditor',
                'Delete Record(s)'
                )

                msg =  QApplication.translate(
                'EntityBrowserWithEditor',
                'An error occurred while attempting to delete a record, this '
                'is most likely caused by a dependency issue.\nPlease check '
                'if the record has dependencies such as social tenure '
                'relationship or related entities. If it has then delete '
                'these dependencies first.'
                )
                QMessageBox.critical(self, title, msg)

                break

            #Refresh list of selected records
            sel_row_indices = self.tbEntity.selectionModel().selectedRows(0)


    def remove_rows(self):
        """
        Removes rows from the entity browser.
        """
        if self.tbEntity.model() is not None:
            row_count = self.tbEntity.model().rowCount()
            self.tbEntity.model().removeRows(0, row_count)

    def _load_editor_dialog(self, recid, rownumber):
        '''
        Load editor dialog based on the selected model instance with the given ID.
        '''
        model_obj = self._model_from_id(recid, rownumber)
        # show GPS editor if geometry
        if self._entity.has_geometry_column():
            self.sp_unit_manager.active_layer_source()

            gps_tool = GPSToolDialog(
                iface,
                self._entity,
                self._entity.name,
                self.sp_unit_manager.active_sp_col,
                model=model_obj,
                reload=False,
                row_number=rownumber,
                entity_browser=self
            )

            result = gps_tool.exec_()
        else:
            #Load editor dialog
            edit_entity_dlg = self._editor_dlg(self._entity, model=model_obj,
                                             parent=self, parent_entity=self.parent_entity, plugin=self.plugin)

            result = edit_entity_dlg.exec_()

        if result == QDialog.Accepted:

            if self._entity.has_geometry_column():
                edit_entity_dlg = gps_tool.entity_editor

            updated_model_obj = edit_entity_dlg.model()
            if not edit_entity_dlg.is_valid:
                return
            for i, attr in enumerate(self._entity_attrs):
                prop_idx = self._tableModel.index(rownumber, i)
                attr_val = getattr(updated_model_obj, attr)

                '''
                Check if there are display formatters and apply if
                one exists for the given attribute.
                '''
                if attr in self._cell_formatters:
                    formatter = self._cell_formatters[attr]
                    attr_val = formatter.format_column_value(attr_val)

                self._tableModel.setData(prop_idx, attr_val)

    def _delete_record(self, rec_id, row_number):
        """
        Delete the record with the given id and remove it from the table view.
        """
        del_result = True

        if self.parent_entity is not None:
            idx = row_number+1
            if idx in self.child_model:
                del self.child_model[idx]
                del self._parent.child_models[idx, self.entity]

            self._tableModel.removeRows(row_number, 1)
            # Update number of records
            self.recomputeRecordCount()

            #Clear previous notifications
            self._notifBar.clear()

        #Remove record from the database
        dbHandler = self._dbmodel()
        entity = dbHandler.queryObject().filter(
            self._dbmodel.id == rec_id
        ).first()

        if entity:
            result = entity.delete()

            if not result:
                return False

            self._tableModel.removeRows(row_number, 1)

            #Clear previous notifications
            self._notifBar.clear()

            #Notify user
            delMsg = QApplication.translate(
                "EntityBrowserWithEditor",
                "Record successfully deleted!"
            )

            self._notifBar.insertInformationNotification(delMsg)

            #Update number of records
            self.recomputeRecordCount()

        return del_result

    def onDoubleClickView(self, modelindex):
        '''
        Override for loading editor dialog.
        '''
        rowIndex = self._proxyModel.mapToSource(modelindex)
        rowNumber = rowIndex.row()
        recordIdIndex = self._tableModel.index(rowNumber, 0)
    
        recordId = recordIdIndex.data()
        self._load_editor_dialog(recordId,recordIdIndex.row())


    def shift_spatial_entity_browser(self):
        """
        Shift records manager to the bottom left corner
        :rtype:
        :return:
        """
        parent_height = self.parent().geometry().height()
        parent_width = self.parent().geometry().width()
        parent_x = self.parent().geometry().x()
        parent_y = self.parent().geometry().y()
        dialog_width = self.width()
        dialog_height = self.height()
        self.setGeometry(
            parent_x,
            parent_y+parent_height-dialog_height-40,
            dialog_width,
            dialog_height
        )

    def on_select_attribute(self):
        """
        Slot raised when selecting a spatial entity row.
        """
        sel_row_indices = self.tbEntity.\
            selectionModel().selectedRows(0)
        record_ids = []

        for sel_row_index in sel_row_indices:
            rowIndex = self._proxyModel.mapToSource(
                sel_row_index
            )
            record_id = rowIndex.data()
            record_ids.append(record_id)

        self.record_feature_highlighter(record_ids)

    def record_feature_highlighter(self, record_ids):
        """
        Highlights a feature of a record.
        :param record_id: The id of a row
        :type record_id: Integer
        :return: None
        :rtype: NoneType
        """
        if len(record_ids) < 1:
            return

        for geom in self.geom_cols:

            geom_wkb = entity_id_to_attr(
                self._entity,
                geom.name,
                record_ids[0]
            )

            if geom_wkb is not None:

                sel_lyr_name = self.sp_unit_manager. \
                    geom_col_layer_name(
                    self._entity.name, geom
                )

                self.add_spatial_unit_layer(sel_lyr_name)
                layers = QgsMapLayerRegistry.instance().mapLayersByName(
                    sel_lyr_name
                )
                if len(layers) > 0:
                    layers[0].removeSelection()

                    canvas = iface.mapCanvas()

                    layers[0].blockSignals(True)
                    # Get selection and extent while disabling signals of selection
                    # especially for geometry tools.
                    layers[0].select(record_ids)
                    bounding_box = layers[0].boundingBoxOfSelected()

                    layers[0].blockSignals(False)
                    canvas.setCrsTransformEnabled(True)

                    canvas.zoomToSelected(layers[0])
                    iface.mapCanvas().setExtent(bounding_box)
                    layers[0].select(record_ids)
                    canvas.refresh()
                    self.selection_layer = layers[0]

    def add_spatial_unit_layer(self, layer_name=None):
        """
        Add the spatial unit layer into the map canvas for later use.
        :param layer_name: The name of the layer to be added to the map canvas.
        :type layer_name: String
        """
        if layer_name is not None:
            self.sp_unit_manager.add_layer_by_name(layer_name)
        else:
            # As this is used for startup of
            # entity browser, just add the first geom layer.
            if len(self.geom_cols) > 0:
                layer_name_item = \
                    self.sp_unit_manager.geom_col_layer_name(
                        self._entity.name,
                        self.geom_cols[0]
                )
                self.sp_unit_manager.\
                    add_layer_by_name(layer_name_item)

    def closeEvent(self, event):
        """
        The event handler that is triggered
        when the dialog is closed.
        :param event: the event
        :type QCloseEvent
        :return: None
        """
        if self._entity.has_geometry_column():
            try:
                if self.selection_layer is not None:
                    self.selection_layer.removeSelection()

            except RuntimeError:
                pass

    def hideEvent(self, hideEvent):
        """
        The event handler that is triggered
        when the dialog is hidden.
        :param hideEvent: the event
        :type QCloseEvent
        :return: None
        """
        if self._entity.has_geometry_column():
            if self.selection_layer is not None:
                self.selection_layer.removeSelection()