def init_documents(self): """ Initializes the document type combobox by populating data. """ self.supporting_doc_manager = SourceDocumentManager( self.social_tenure.supporting_doc, self.str_doc_model, self._parent) self.create_doc_tab_populate_combobox() self.doc_type_cbo.currentIndexChanged.connect( self.match_doc_combo_to_tab) self.docs_tab.currentChanged.connect(self.match_doc_tab_to_combo)
def __init__(self, entity_supporting_document, supporting_doc_model_cls, parent=None): """ Class constructor. :param entity_supporting_document: Object containing information pertaining to document types, parent entity etc. :type entity_supporting_document: EntitySupportingDocument :param supporting_doc_model_cls: Class representing the data model corresponding to the entity supporting document object. :type supporting_doc_model_cls: object :param parent: Parent container widget. :type parent: QWidget """ QWidget.__init__(self, parent) self._init_gui() self._entity_supporting_doc = entity_supporting_document #Container for document type widgets based on lookup id self._doc_type_widgets = {} #Init document manager self.source_document_manager = SourceDocumentManager( self._entity_supporting_doc, supporting_doc_model_cls, self ) self._load_document_types() #Connect signals self._btn_add_document.clicked.connect( self._on_add_supporting_document ) self._cbo_doc_type.currentIndexChanged.connect( self.on_doc_type_changed ) self._doc_tab_container.currentChanged.connect( self.on_tab_doc_type_changed )
def dbmodel_from_entity(self): """ Format model attributes from passed entity attributes :return: """ if self.entity_has_supporting_docs(): entity_object, self.doc_model = entity_model(self.entity, with_supporting_document=True) entity_object_model = entity_object() if hasattr(entity_object_model, 'documents'): if self.entity.TYPE_INFO == 'SOCIAL_TENURE': obj_doc_col = current_profile().social_tenure.supporting_doc else: obj_doc_col = self.entity.supporting_doc self._doc_manager = SourceDocumentManager( obj_doc_col, self.doc_model ) else: entity_object = entity_model(self.entity) entity_object_model = entity_object() return entity_object_model
def onAddSourceDocument(self,doctype): ''' Slot raised when the user has selected to add a new source document. ''' #Create a proxy source document manager for inserting documents. srcDocManager = SourceDocumentManager(self.parentWidget()) vBoxProxy = QVBoxLayout(self._parentWidget) self._parentWidget.connect(srcDocManager,SIGNAL("fileUploaded(PyQt_PyObject)"), self._onSourceDocUploaded) if doctype == TITLE_DEED: dialogTitle = QApplication.translate("STRNode", "Specify Title Deed File Location") elif doctype == STATUTORY_REF_PAPER: dialogTitle = QApplication.translate("STRNode", "Specify Statutory Reference Paper File Location") elif doctype == SURVEYOR_REF: dialogTitle = QApplication.translate("STRNode", "Specify Surveyor Reference File Location") elif doctype == NOTARY_REF: dialogTitle = QApplication.translate("STRNode", "Specify Notary Reference File Location") else: return docPath = self._selectSourceDocumentDialog(dialogTitle) if not docPath.isNull(): #Register container then upload document srcDocManager.registerContainer(vBoxProxy, doctype) srcDocManager.insertDocumentFromFile(docPath,doctype)
def init_documents(self): """ Initializes the document type combobox by populating data. """ self.supporting_doc_manager = SourceDocumentManager( self.social_tenure.supporting_doc, self.str_doc_model, self._parent ) self.create_doc_tab_populate_combobox() self.doc_type_cbo.currentIndexChanged.connect( self.match_doc_combo_to_tab ) self.docs_tab.currentChanged.connect( self.match_doc_tab_to_combo )
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; } ''')
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
class Save2DB: """ Class to insert entity data into db """ def __init__(self, entity, attributes, ids=None): """ Initialize class and class variable """ self.attributes = attributes self.form_entity = entity self.doc_model = None self._doc_manager = None self.entity = self.object_from_entity_name(self.form_entity) self.model = self.dbmodel_from_entity() self.key = 0 self.parents_ids = ids self.geom = 4326 self.entity_mapping = {} def object_from_entity_name(self, entity): """ :return: """ if entity == 'social_tenure': return current_profile().social_tenure else: user_entity = current_profile().entity_by_name(entity) return user_entity def entity_has_supporting_docs(self): """ Check if the entity has supporting document before importing :return: Bool """ if self.entity.supports_documents: return self.entity.supports_documents else: return None def entity_supported_document_types(self): """ Get the supported document types before importing so that they are captured during import process :return: List """ return self.entity.document_types_non_hex() def dbmodel_from_entity(self): """ Format model attributes from passed entity attributes :return: """ if self.entity_has_supporting_docs(): entity_object, self.doc_model = entity_model( self.entity, with_supporting_document=True) entity_object_model = entity_object() if hasattr(entity_object_model, 'documents'): if self.entity.TYPE_INFO == 'SOCIAL_TENURE': obj_doc_col = current_profile( ).social_tenure.supporting_doc else: obj_doc_col = self.entity.supporting_doc self._doc_manager = SourceDocumentManager( obj_doc_col, self.doc_model) else: entity_object = entity_model(self.entity) entity_object_model = entity_object() return entity_object_model def objects_from_supporting_doc(self, instance_file=None): """ Create supporting doc path instances based on the collected documents :return:paths :rtype: document object instance """ if instance_file: f_dir, file_name = os.path.split(instance_file) for document, val in self.attributes.iteritems(): if str(document).endswith('supporting_document'): if val != '': doc = self.format_document_name_from_attribute( document) doc_path = os.path.normpath(f_dir + '/' + val) abs_path = doc_path.replace('\\', '/').strip() if QFile.exists(abs_path): self.supporting_document_model(abs_path, doc) def supporting_document_model(self, doc_path, doc): """ :param doc_path: absolute document path :param doc: document name :type: str Construct supporting document model instance to add into the db :return: """ # Create document container doc_container = QVBoxLayout() supporting_doc_entity = self.entity.supporting_doc.document_type_entity document_type_id = entity_attr_to_id(supporting_doc_entity, 'value', doc, lower=False) # Register container self._doc_manager.registerContainer(doc_container, document_type_id) #Copy the document to STDM working directory self._doc_manager.insertDocumentFromFile(doc_path, document_type_id, self.entity) def format_document_name_from_attribute(self, doc): """ Get the type of document from attribute name So that supporting document class instance can save it in the right format :return: """ formatted_doc_list = self.entity_supported_document_types() default = 'General' doc_type = str(doc).split('_', 1) if doc_type[0].startswith( 'supporting') and formatted_doc_list[0] == default: return default elif doc_type[0].startswith( 'supporting') and formatted_doc_list[0] != default: return formatted_doc_list[0] elif not doc_type[0].startswith('supporting'): actual_doc_name = doc_type[0].replace('-', ' ') for doc_name in formatted_doc_list: if actual_doc_name in doc_name or doc_name.startswith( actual_doc_name): return doc_name else: return formatted_doc_list[0] else: return formatted_doc_list[0] def extract_social_tenure_entities(self): ''' We want to extract social tenure enities so that we know if it has multiple or single entities in the list :return: ''' party_ref_column = '' spatial_ref_column = '' if self.parents_ids is not None: print self.parents_ids if self.attributes.has_key('party'): full_party_ref_column = self.attributes.get('party') party_ref_column = full_party_ref_column + '_id' print 'party{}.'.format(party_ref_column) setattr(self.model, party_ref_column, self.parents_ids.get(full_party_ref_column)[0]) if self.attributes.has_key('spatial_unit'): full_spatial_ref_column = self.attributes.get('spatial_unit') spatial_ref_column = full_spatial_ref_column + '_id' print 'sp.{}.'.format(spatial_ref_column) setattr(self.model, spatial_ref_column, self.parents_ids.get(full_spatial_ref_column)[0]) return party_ref_column, spatial_ref_column def save_to_db(self): """ Format object attribute data from entity and save them into database :return: """ self.column_info() attributes = self.attributes try: if self.entity.short_name == 'social_tenure_relationship': #try: prefix = current_profile().prefix + '_' if self.attributes.has_key('party'): full_party_ref_column = self.attributes.get('party') party_ref_column = full_party_ref_column + '_id' self.attributes.pop('party') else: full_party_ref_column = current_profile( ).social_tenure.parties[0].name party_ref_column = full_party_ref_column.replace( prefix, '') + '_id' setattr(self.model, party_ref_column, self.parents_ids.get(full_party_ref_column)[0]) if self.attributes.has_key('spatial_unit'): full_spatial_ref_column = self.attributes.get( 'spatial_unit') spatial_ref_column = full_spatial_ref_column + '_id' self.attributes.pop('spatial_unit') else: full_spatial_ref_column = current_profile( ).social_tenure.spatial_units[0].name spatial_ref_column = full_spatial_ref_column.replace( prefix, '') + '_id' setattr(self.model, spatial_ref_column, self.parents_ids.get(full_spatial_ref_column)[0]) attributes = self.attributes['social_tenure'] except: pass for k, v in attributes.iteritems(): if hasattr(self.model, k): col_type = self.entity_mapping.get(k) col_prop = self.entity.columns[k] var = self.attribute_formatter(col_type, col_prop, v) setattr(self.model, k, var) if self.entity_has_supporting_docs(): self.model.documents = self._doc_manager.model_objects() self.model.save() return self.model.id def save_parent_to_db(self): """ Format object attribute data from entity and save them into database attribute :return: """ self.column_info() for k, v in self.attributes.iteritems(): if hasattr(self.model, k): col_type = self.entity_mapping.get(k) col_prop = self.entity.columns[k] #print "property{0}.... and type.{1}".format(col_prop, col_type) var = self.attribute_formatter(col_type, col_prop, v) setattr(self.model, k, var) if self.entity_has_supporting_docs(): self.model.documents = self._doc_manager.model_objects() self.model.save() self.key = self.model.id return self.key def save_foreign_key_table(self): """ Get the table with foreign keys only :return: """ for col, type_info in self.column_info().iteritems(): col_prop = self.entity.columns[col] var = self.attribute_formatter(type_info, col_prop, None) setattr(self.model, col, var) self.model.save() self.cleanup() def column_info(self): """ :return: """ self.entity_mapping = {} cols = self.entity.columns.values() for c in cols: self.entity_mapping[c.name] = c.TYPE_INFO def get_srid(self, srid): """ Let the user specify the coordinate system during data import :param srid: :return: """ self.geom = srid return self.geom def id_from_model_object(self, obj): """ We need to obtian id from object instance :param obj: :return: """ return obj.id def attribute_formatter(self, col_type, col_prop, var=None): """ Format geoodk attributes collected in the field to conform to STDM database contrains :return: """ if col_type == 'BOOL': if len(var) < 1: return None if len(var) > 1: if var == '' or var is None: return None if var == 'Yes' or var == True: return True if var == 'No' or var == False: return False else: return None if col_type == 'LOOKUP': if len(var) < 1 or var is None: return None if len(var) < 4: if var == 'Yes' or var == 'No': return entity_attr_to_model(col_prop.parent, 'value', var).id if var != 'Yes' and var != 'No': lk_code = entity_attr_to_id(col_prop.parent, "code", var) if not str(lk_code).isdigit(): return None else: return lk_code if len(var) > 3: if not str(entity_attr_to_id(col_prop.parent, 'code', var)).isdigit(): id_value = entity_attr_to_model(col_prop.parent, 'value', var) if id_value is not None: return id_value.id #return entity_attr_to_model(col_prop.parent, 'value', var).id else: lk_code = entity_attr_to_id(col_prop.parent, "code", var) if not str(lk_code).isdigit(): return None else: return lk_code else: return None elif col_type == 'ADMIN_SPATIAL_UNIT': var_code = None try: if len(var) < 1 or var is None: return None elif not len(var) > 3: var_code = entity_attr_to_id(col_prop.parent, "code", var) if var_code and var_code == var: return None else: return var_code elif len(var) > 3 and entity_attr_to_id( col_prop.parent, "name", var) is not None: var_code = entity_attr_to_id(col_prop.parent, "name", var) if var_code and var_code == var: return None else: return var_code else: if entity_attr_to_id(col_prop.parent, "name", var) is None: var_code = entity_attr_to_id(col_prop.parent, "code", var) if not var_code or var_code == var: return None except: pass elif col_type == 'MULTIPLE_SELECT': print 'multiple select {}'.format(var) if var == '' or var is None: return None else: col_parent = col_prop.association.first_parent lk_val_list = col_parent.values.values() choices_list = [] for code in lk_val_list: choices_list.append( entity_attr_to_id(col_parent.association.first_parent, 'value', code.value)) if len(choices_list) > 1: return choices_list else: return None elif col_type == 'GEOMETRY': defualt_srid = 0 if var: geom_provider = STDMGeometry(var) if isinstance(col_prop, GeometryColumn): defualt_srid = col_prop.srid if defualt_srid != 0: geom_provider.set_user_srid(defualt_srid) else: geom_provider.set_user_srid(GEOMPARAM) if col_prop.geometry_type() == 'POINT': return geom_provider.point_to_Wkt() if col_prop.geometry_type() == 'POLYGON': return geom_provider.polygon_to_Wkt() else: return None elif col_type == 'FOREIGN_KEY': ret_val = None for code, val in self.parents_ids.iteritems(): if col_prop.parent.name == code: ret_val = val[0] break return ret_val elif col_type == 'INT' or col_type == 'DOUBLE' or col_type == 'PERCENT': ret_val = None if var <> '': ret_val = var return ret_val elif col_type == 'DATETIME' or col_type == 'DATE': ret_val = None if var <> '': ret_val = var return ret_val else: return var def cleanup(self): """ Reset all the model and entity data before we process the next entity data :return: None z""" self.model = None self.entity = None self.attributes = None self._doc_manager = None
class SupportingDocuments(ComponentUtility): onUploadDocument = pyqtSignal(list) def __init__(self, box, combobox, add_documents_btn, notification_bar, parent=None): """ Handles the supporting documents component loading. :param box: The layout holding the container widget. :type box: QVBoxLayout :param combobox: The combobox loading supporting document types. :type combobox: QComboBox :param add_documents_btn: The add supporting document button :type add_documents_btn: QPushButton :param notification_bar: The NotificationBar object that displays notification. :type notification_bar: Object :param parent: The container of the widget :type parent: QDialog or None """ ComponentUtility.__init__(self) self._parent = parent self.container_box = box self.doc_type_cbo = combobox self.notification_bar = notification_bar self.str_number = 1 self.add_documents_btn = add_documents_btn self.str_numbers = [1] self.current_party_count = None self.init_documents() def init_documents(self): """ Initializes the document type combobox by populating data. """ self.supporting_doc_manager = SourceDocumentManager( self.social_tenure.supporting_doc, self.str_doc_model, self._parent) self.create_doc_tab_populate_combobox() self.doc_type_cbo.currentIndexChanged.connect( self.match_doc_combo_to_tab) self.docs_tab.currentChanged.connect(self.match_doc_tab_to_combo) def party_count(self, count): """ A setter for current_party_count that is used to determined the number of copies for each supporting document. :param count: The number of currently added party records. :type count: Integer """ self.current_party_count = count def create_doc_tab_populate_combobox(self): """ Creates the supporting document component widget. """ self.doc_tab_data() self.docs_tab = QTabWidget() self.docs_tab_index = OrderedDict() for i, (id, doc) in enumerate(self.doc_types.iteritems()): self.docs_tab_index[doc] = i # the tab widget containing the document widget layout # and the child of the tab. tab_widget = QWidget() tab_widget.setObjectName(doc) # The layout of the tab widget cont_layout = QVBoxLayout(tab_widget) cont_layout.setObjectName(u'widget_layout_{}'.format(doc)) # the scroll area widget inside the tab widget. scroll_area = QScrollArea(tab_widget) scroll_area.setFrameShape(QFrame.NoFrame) scroll_area.setObjectName(u'tab_scroll_area_{}'.format(doc)) layout_widget = QWidget() # the widget the is under the scroll area content and # the widget containing the document widget layout # This widget is hidden and shown based on the STR number layout_widget.setObjectName(u'widget_{}'.format(doc)) doc_widget_layout = QVBoxLayout(layout_widget) doc_widget_layout.setObjectName( u'doc_widget_layout_{}'.format(doc)) doc_widget = QWidget() doc_widget.setObjectName(u'doc_widget_{}_{}'.format( doc, self.str_number)) doc_widget_layout.addWidget(doc_widget) # the layout containing document widget. ### This is the layout that is registered to add uploaded # supporting documents widgets into. tab_layout = QVBoxLayout(doc_widget) tab_layout.setObjectName(u'layout_{}_{}'.format( doc, self.str_number)) scroll_area.setWidgetResizable(True) scroll_area.setWidget(layout_widget) cont_layout.addWidget(scroll_area) # Add the tab widget with the document # type name to create a tab. self.docs_tab.addTab(tab_widget, doc) self.container_box.addWidget(self.docs_tab, 1) if len(self.str_numbers) == 1: self.doc_type_cbo.addItem(doc, id) def doc_tab_data(self): """ Sets the document types in the social tenure entity. """ doc_entity = self.social_tenure. \ supporting_doc.document_type_entity doc_type_model = entity_model(doc_entity) docs = doc_type_model() doc_type_list = docs.queryObject().all() self.doc_types = [(doc.id, doc.value) for doc in doc_type_list] self.doc_types = OrderedDict(self.doc_types) def match_doc_combo_to_tab(self): """ Changes the active tab based on the selected value of document type combobox. """ combo_text = self.doc_type_cbo.currentText() if combo_text is not None and len(combo_text) > 0: index = self.docs_tab_index[combo_text] self.docs_tab.setCurrentIndex(index) def match_doc_tab_to_combo(self): """ Changes the document type combobox value based on the selected tab. """ doc_tab_index = self.docs_tab.currentIndex() self.doc_type_cbo.setCurrentIndex(doc_tab_index) @staticmethod def hide_doc_widgets(widget, visibility): """ Hides or shows the visibility of the supporting document container widgets. :param widget: The widget to which the visibility is set. :type widget: QWidget :param visibility: A boolean to show or hide visibility. True hides widget and False shows it. :type visibility: Boolean """ widget.setHidden(visibility) def update_container(self, str_number): """ Update the current supporting document widget container to be used. :param str_number: The STR node number :type str_number: Integer """ doc_text = self.doc_type_cbo.currentText() cbo_index = self.doc_type_cbo.currentIndex() doc_id = self.doc_type_cbo.itemData(cbo_index) scroll_area = self.docs_tab.findChild( QScrollArea, u'tab_scroll_area_{}'.format(doc_text, str_number)) doc_widget = scroll_area.findChild( QWidget, u'doc_widget_{}_{}'.format(doc_text, str_number)) # If the doc widget doesn't exist create it for new STR instance if doc_widget is None: # find the doc_widget layout that contains # all STR doc widget layouts. Single # doc_widget_layout is created for each document type. # But all doc_widgets for each STR instance and # document types will be added here. doc_widget_layout = scroll_area.findChild( QVBoxLayout, u'doc_widget_layout_{}'.format(doc_text)) doc_widget = QWidget() doc_widget.setObjectName(u'doc_widget_{}_{}'.format( doc_text, str_number)) self.hide_all_other_widget(doc_text, str_number) doc_widget_layout.addWidget(doc_widget) # Create the layout so that layouts are registered in # which uploaded document widgets are added. layout = QVBoxLayout(doc_widget) layout.setObjectName(u'layout_{}_{}'.format(doc_text, str_number)) # If the doc widget exists, get the lowest # layout so that it is registered. else: # hide all other widgets self.hide_all_other_widget(doc_text, str_number) # show the current doc widget to display # the document widgets for the current tab. self.hide_doc_widgets(doc_widget, False) layout = doc_widget.findChild( QVBoxLayout, u'layout_{}_{}'.format(doc_text, str_number)) # register layout self.supporting_doc_manager.registerContainer(layout, doc_id) def hide_all_other_widget(self, doc_text, str_number): """ Hides all other supporting document widget except the current STR node widget. :param doc_text: The current document type selected. :type doc_text: String :param str_number: The STR node number :type str_number: Integer """ expression = QRegExp(u'doc_widget*') # hide all existing widgets in all layouts for widget in self.docs_tab.findChildren(QWidget, expression): if widget.objectName() != u'doc_widget_{}_{}'.format( doc_text, str_number): self.hide_doc_widgets(widget, True) def on_upload_document(self): ''' Slot raised when the user clicks to upload a supporting document. ''' document_str = QApplication.translate( "SupportingDocuments", "Specify the Document File Location") documents = self.select_file_dialog(document_str) cbo_index = self.doc_type_cbo.currentIndex() doc_id = self.doc_type_cbo.itemData(cbo_index) for doc in documents: self.supporting_doc_manager.insertDocumentFromFile( doc, doc_id, self.social_tenure, self.current_party_count) # Set last path if len(documents) > 0: doc = documents[0] fi = QFileInfo(doc) dir_path = fi.absolutePath() set_last_document_path(dir_path) model_objs = self.supporting_doc_manager.model_objects() self.onUploadDocument.emit(model_objs) def select_file_dialog(self, title): """ Displays a file dialog for a user to specify a source document :param title: The title of the file dialog :type title: String """ # Get last path for supporting documents last_path = last_document_path() if last_path is None: last_path = '/home' files = QFileDialog.getOpenFileNames( iface.mainWindow(), title, last_path, "Source Documents (*.jpg *.jpeg *.png *.bmp *.tiff *.svg)") return files
class Save2DB: """ Class to insert entity data into db """ def __init__(self, entity, attributes, ids=None): """ Initialize class and class variable """ self.attributes = attributes self.form_entity = entity self.doc_model = None self._doc_manager = None self.entity = self.object_from_entity_name(self.form_entity) self.model = self.dbmodel_from_entity() self.key = 0 self.parents_ids = ids self.geom = 4326 def object_from_entity_name(self, entity): """ :return: """ if entity == 'social_tenure': return current_profile().social_tenure else: user_entity = current_profile().entity_by_name(entity) return user_entity def entity_has_supporting_docs(self): """ Check if the entity has supporting document before importing :return: Bool """ return self.entity.supports_documents def entity_supported_document_types(self): """ Get the supported document types before importing so that they are captured during import process :return: List """ return self.entity.document_types_non_hex() def dbmodel_from_entity(self): """ Format model attributes from passed entity attributes :return: """ if self.entity_has_supporting_docs(): entity_object, self.doc_model = entity_model( self.entity, with_supporting_document=True) entity_object_model = entity_object() if hasattr(entity_object_model, 'documents'): if self.entity.TYPE_INFO == 'SOCIAL_TENURE': obj_doc_col = current_profile( ).social_tenure.supporting_doc else: obj_doc_col = self.entity.supporting_doc self._doc_manager = SourceDocumentManager( obj_doc_col, self.doc_model) else: entity_object = entity_model(self.entity) entity_object_model = entity_object() return entity_object_model def objects_from_supporting_doc(self, instance_file=None): """ Create supporting doc path instances based on the collected documents :return:paths :rtype: document object instance """ if instance_file: f_dir, file_name = os.path.split(instance_file) for document, val in self.attributes.iteritems(): if str(document).endswith('supporting_document'): if val != '': doc = self.format_document_name_from_attribute( document) doc_path = os.path.normpath(f_dir + '/' + val) abs_path = doc_path.replace('\\', '/').strip() if QFile.exists(abs_path): self.supporting_document_model(abs_path, doc) def supporting_document_model(self, doc_path, doc): """ Construct supporting document model instance to add into the db :return: """ # Create document container doc_container = QVBoxLayout() supporting_doc_entity = self.entity.supporting_doc.document_type_entity document_type_id = entity_attr_to_id(supporting_doc_entity, 'value', doc, lower=False) # Register container self._doc_manager.registerContainer(doc_container, document_type_id) #Copy the document to STDM working directory self._doc_manager.insertDocumentFromFile(doc_path, document_type_id, self.entity) def format_document_name_from_attribute(self, key_name): """ Get the type of document from attribute name So that supporting document class instance can save it in the right format :return: """ doc_list = self.entity_supported_document_types() default = 'General' doc_type = str(key_name).split('_') if len(doc_type) > 2: if doc_type[0] in doc_list: return doc_type[0] else: for doc in doc_list: if doc.startswith(doc_type[0]): return doc elif len(doc_type) < 2 and key_name != default: return key_name else: return default def save_to_db(self): """ Format object attribute data from entity and save them into database :return: """ try: if self.parents_ids is not None and self.entity.short_name == 'social_tenure_relationship': str_tables = current_profile().social_tenure setattr(self.model, str_tables.parties[0].short_name.lower() + '_id', self.parents_ids.get(str_tables.parties[0].name)[0]) setattr( self.model, str_tables.spatial_units[0].short_name.lower() + '_id', self.parents_ids.get(str_tables.spatial_units[0].name)[0]) except: pass for k, v in self.attributes.iteritems(): if hasattr(self.model, k): col_type = self.column_info().get(k) col_prop = self.entity.columns[k] var = self.attribute_formatter(col_type, col_prop, v) setattr(self.model, k, var) if self.entity_has_supporting_docs(): self.model.documents = self._doc_manager.model_objects() self.model.save() return self.model.id #self.cleanup() def save_parent_to_db(self): """ Format object attribute data from entity and save them into database attribute :return: """ for k, v in self.attributes.iteritems(): if hasattr(self.model, k): col_type = self.column_info().get(k) col_prop = self.entity.columns[k] var = self.attribute_formatter(col_type, col_prop, v) setattr(self.model, k, var) if self.entity_has_supporting_docs(): self.model.documents = self._doc_manager.model_objects() self.model.save() self.key = self.model.id return self.key #def model_object_formatter(self): def column_info(self): """ :return: """ type_mapping = {} cols = self.entity.columns.values() for c in cols: type_mapping[c.name] = c.TYPE_INFO return type_mapping def get_srid(self, srid): """ Let the user specify the coordinate system during data import :param srid: :return: """ self.geom = srid return self.geom def id_from_model_object(self, obj): """ We need to obtian id from object instance :param obj: :return: """ return obj.id def attribute_formatter(self, col_type, col_prop, var): """ :return: """ if col_type == 'LOOKUP': if var == '' or var is None: return None if var == 'Yes' or var == 'No': return entity_attr_to_model(col_prop.parent, 'value', var).id if not len(var) > 3 and var != 'Yes' and var != 'No': lk_code = entity_attr_to_id(col_prop.parent, "code", var) if not str(lk_code).isdigit(): return None else: return lk_code if len(var) > 3: if not str(entity_attr_to_id(col_prop.parent, 'code', var)).isdigit(): return entity_attr_to_model(col_prop.parent, 'value', var).id else: lk_code = entity_attr_to_id(col_prop.parent, "code", var) if not str(lk_code).isdigit(): return None else: return lk_code else: return None elif col_type == 'ADMIN_SPATIAL_UNIT': if not len(var) > 3: return entity_attr_to_id(col_prop.parent, "code", var) else: return entity_attr_to_id(col_prop.parent, "name", var) elif col_type == 'MULTIPLE_SELECT': if var == '' or var is None: return None if not len(var) > 3: return entity_attr_to_id(col_prop.association.first_parent, "code", var) elif len(var) > 3: if not str( entity_attr_to_id(col_prop.association.first_parent, "code", var)).isdigit(): return entity_attr_to_model( col_prop.association.first_parent, 'value', var).id else: return entity_attr_to_id(col_prop.association.first_parent, "code", var) elif col_type == 'GEOMETRY': defualt_srid = 0 geom_provider = STDMGeometry(var) if isinstance(col_prop, GeometryColumn): defualt_srid = col_prop.srid if defualt_srid != 0: geom_provider.set_user_srid(defualt_srid) else: geom_provider.set_user_srid(GEOMPARAM) if col_prop.geometry_type() == 'POINT': return geom_provider.point_to_Wkt() if col_prop.geometry_type() == 'POLYGON': return geom_provider.polygon_to_Wkt() elif col_type == 'FOREIGN_KEY': if self.parents_ids is None or len(self.parents_ids) < 0: return else: for code, val in self.parents_ids.iteritems(): if code is not None: if val[1] == GROUPCODE and col_prop.parent.name == code: return val[0] else: if col_prop.parent.name == code: return val[0] elif col_type == 'INT' or col_type == 'DOUBLE': if var == '': return 0 else: return var else: return var def cleanup(self): """ Reset all the model and entity data before we process the next entity data :return: None z""" self.model = None self.entity = None self.attributes = None self._doc_manager = None
class SupportingDocumentsWidget(QWidget): """ Widget for managing an entity's supporting documents. It enables listing of documents grouped by tabs depending on type. """ def __init__(self, entity_supporting_document, supporting_doc_model_cls, parent=None): """ Class constructor. :param entity_supporting_document: Object containing information pertaining to document types, parent entity etc. :type entity_supporting_document: EntitySupportingDocument :param supporting_doc_model_cls: Class representing the data model corresponding to the entity supporting document object. :type supporting_doc_model_cls: object :param parent: Parent container widget. :type parent: QWidget """ QWidget.__init__(self, parent) self._init_gui() self._entity_supporting_doc = entity_supporting_document #Container for document type widgets based on lookup id self._doc_type_widgets = {} #Init document manager self.source_document_manager = SourceDocumentManager( self._entity_supporting_doc, supporting_doc_model_cls, self ) self._load_document_types() #Connect signals self._btn_add_document.clicked.connect( self._on_add_supporting_document ) self._cbo_doc_type.currentIndexChanged.connect( self.on_doc_type_changed ) self._doc_tab_container.currentChanged.connect( self.on_tab_doc_type_changed ) def _init_gui(self): self._gl = QGridLayout(self) self._label = QLabel(self) self._label.setText(self.tr('Select document type')) self._gl.addWidget(self._label, 0, 0, 1, 1) self._cbo_doc_type = QComboBox(self) self._gl.addWidget(self._cbo_doc_type, 0, 1, 1, 1) self._btn_add_document = QPushButton(self) doc_ico = QIcon(':/plugins/stdm/images/icons/document.png') self._btn_add_document.setIcon(doc_ico) self._btn_add_document.setText(self.tr('Add document...')) self._btn_add_document.setMaximumWidth(200) self._gl.addWidget(self._btn_add_document, 0, 2, 1, 1) self._doc_tab_container = QTabWidget(self) self._gl.addWidget(self._doc_tab_container, 1, 0, 1, 3) self.setMinimumHeight(140) def on_doc_type_changed(self, idx): """ Slot raised when the document types changes. The corresponding widget in the tab container is also selected. :param idx: Item index in the combobox. :type idx: int """ if idx == -1: return self._doc_tab_container.setCurrentIndex(idx) def on_tab_doc_type_changed(self, idx): """ Slot raised when the document types changes. The corresponding widget in the tab container is also selected. :param idx: Item index in the tab widget. :type idx: int """ if idx == -1: return self._cbo_doc_type.setCurrentIndex(idx) def current_document_type(self): """ :return: Returns the currently selected document type in the combobox. :rtype: str """ return self._cbo_doc_type.currentText() def current_document_type_id(self): """ :return: Returns the primary key/id of the currently selected document type in the combobox, else -1 if there is not current item in the combobox :rtype: int """ if not self.current_document_type(): return -1 curr_idx = self._cbo_doc_type.currentIndex() return self._cbo_doc_type.itemData(curr_idx) def count(self): """ :return: Returns the number of document types supported by this widget. :rtype: int """ return self._cbo_doc_type.count() def document_type_containers(self): """ :return: Returns a list of document container widgets for all registered document types. :rtype: list """ return self.source_document_manager.containers.values() def document_type_widget(self, name): """ Searches for the document type widget that corresponds to the given name. :param name: Name of the document type. :type name: str :return: Returns the document widget corresponding to the given type name. :rtype: QWidget """ idx = self._cbo_doc_type.findText(name) if idx == -1: return None rec_id = self._cbo_doc_type.itemData(idx) return self._doc_type_widgets.get(rec_id, None) def _load_document_types(self): #Load document types in the combobox and tab widget vl_cls = entity_model( self._entity_supporting_doc.document_type_entity, entity_only=True ) vl_obj = vl_cls() res = vl_obj.queryObject().all() for r in res: #Add to combo self._cbo_doc_type.addItem(r.value, r.id) #Add to tab widget doc_type_widget = _DocumentTypeContainer(self) self._doc_tab_container.addTab(doc_type_widget, r.value) self._doc_type_widgets[r.id] = doc_type_widget #Register container self.source_document_manager.registerContainer( doc_type_widget.container, r.id ) def _on_add_supporting_document(self): #Slot raised when the user select to add a supporting document if self.count == 0: return select = self.tr('Select') supporting_docs_str = 'Supporting Documents' title = u'{0} {1} {2}'.format( select, self.current_document_type(), supporting_docs_str ) filter_str = u'{0} (*.jpg *.jpeg *.png *.bmp *.tiff *.svg)'.format( supporting_docs_str ) #Get last path for supporting documents last_path = last_document_path() if last_path is None: last_path = '/home' else: dir = QDir(last_path) if not dir.exists(): last_path = '/home' source_docs = QFileDialog.getOpenFileNames( self, title, last_path, filter_str ) doc_type_id = self._cbo_doc_type.itemData(self._cbo_doc_type.currentIndex()) parent_entity = self._entity_supporting_doc.parent_entity for doc in source_docs: self.source_document_manager.insertDocumentFromFile( doc, doc_type_id, parent_entity ) #Set last path if len(source_docs) > 0: doc = source_docs[0] fi = QFileInfo(doc) dir_path = fi.absolutePath() set_last_document_path(dir_path)
class SupportingDocuments(ComponentUtility): onUploadDocument = pyqtSignal(list) def __init__(self, box, combobox, add_documents_btn, notification_bar, parent=None): """ Handles the supporting documents component loading. :param box: The layout holding the container widget. :type box: QVBoxLayout :param combobox: The combobox loading supporting document types. :type combobox: QComboBox :param add_documents_btn: The add supporting document button :type add_documents_btn: QPushButton :param notification_bar: The NotificationBar object that displays notification. :type notification_bar: Object :param parent: The container of the widget :type parent: QDialog or None """ ComponentUtility.__init__(self) self._parent = parent self.container_box = box self.doc_type_cbo = combobox self.notification_bar = notification_bar self.str_number = 1 self.add_documents_btn = add_documents_btn self.str_numbers = [1] self.current_party_count = None self.init_documents() def init_documents(self): """ Initializes the document type combobox by populating data. """ self.supporting_doc_manager = SourceDocumentManager( self.social_tenure.supporting_doc, self.str_doc_model, self._parent ) self.create_doc_tab_populate_combobox() self.doc_type_cbo.currentIndexChanged.connect( self.match_doc_combo_to_tab ) self.docs_tab.currentChanged.connect( self.match_doc_tab_to_combo ) def party_count(self, count): """ A setter for current_party_count that is used to determined the number of copies for each supporting document. :param count: The number of currently added party records. :type count: Integer """ self.current_party_count = count def create_doc_tab_populate_combobox(self): """ Creates the supporting document component widget. """ self.doc_tab_data() self.docs_tab = QTabWidget() self.docs_tab_index = OrderedDict() for i, (id, doc) in enumerate(self.doc_types.iteritems()): self.docs_tab_index[doc] = i # the tab widget containing the document widget layout # and the child of the tab. tab_widget = QWidget() tab_widget.setObjectName(doc) # The layout of the tab widget cont_layout = QVBoxLayout(tab_widget) cont_layout.setObjectName( u'widget_layout_{}'.format(doc) ) # the scroll area widget inside the tab widget. scroll_area = QScrollArea(tab_widget) scroll_area.setFrameShape(QFrame.NoFrame) scroll_area.setObjectName( u'tab_scroll_area_{}'.format(doc) ) layout_widget = QWidget() # the widget the is under the scroll area content and # the widget containing the document widget layout # This widget is hidden and shown based on the STR number layout_widget.setObjectName( u'widget_{}'.format(doc) ) doc_widget_layout = QVBoxLayout(layout_widget) doc_widget_layout.setObjectName( u'doc_widget_layout_{}'.format( doc ) ) doc_widget = QWidget() doc_widget.setObjectName( u'doc_widget_{}_{}'.format(doc, self.str_number) ) doc_widget_layout.addWidget(doc_widget) # the layout containing document widget. ### This is the layout that is registered to add uploaded # supporting documents widgets into. tab_layout = QVBoxLayout(doc_widget) tab_layout.setObjectName( u'layout_{}_{}'.format(doc, self.str_number) ) scroll_area.setWidgetResizable(True) scroll_area.setWidget(layout_widget) cont_layout.addWidget(scroll_area) # Add the tab widget with the document # type name to create a tab. self.docs_tab.addTab(tab_widget, doc) self.container_box.addWidget(self.docs_tab, 1) if len(self.str_numbers) == 1: self.doc_type_cbo.addItem(doc, id) def doc_tab_data(self): """ Sets the document types in the social tenure entity. """ doc_entity = self.social_tenure. \ supporting_doc.document_type_entity doc_type_model = entity_model(doc_entity) docs = doc_type_model() doc_type_list = docs.queryObject().all() self.doc_types = [(doc.id, doc.value) for doc in doc_type_list ] self.doc_types = OrderedDict(self.doc_types) def match_doc_combo_to_tab(self): """ Changes the active tab based on the selected value of document type combobox. """ combo_text = self.doc_type_cbo.currentText() if combo_text is not None and len(combo_text) > 0: index = self.docs_tab_index[combo_text] self.docs_tab.setCurrentIndex(index) def match_doc_tab_to_combo(self): """ Changes the document type combobox value based on the selected tab. """ doc_tab_index = self.docs_tab.currentIndex() self.doc_type_cbo.setCurrentIndex(doc_tab_index) @staticmethod def hide_doc_widgets(widget, visibility): """ Hides or shows the visibility of the supporting document container widgets. :param widget: The widget to which the visibility is set. :type widget: QWidget :param visibility: A boolean to show or hide visibility. True hides widget and False shows it. :type visibility: Boolean """ widget.setHidden(visibility) def update_container(self, str_number): """ Update the current supporting document widget container to be used. :param str_number: The STR node number :type str_number: Integer """ doc_text = self.doc_type_cbo.currentText() cbo_index = self.doc_type_cbo.currentIndex() doc_id = self.doc_type_cbo.itemData(cbo_index) scroll_area = self.docs_tab.findChild( QScrollArea, u'tab_scroll_area_{}'.format( doc_text, str_number ) ) doc_widget = scroll_area.findChild( QWidget, u'doc_widget_{}_{}'.format( doc_text, str_number ) ) # If the doc widget doesn't exist create it for new STR instance if doc_widget is None: # find the doc_widget layout that contains # all STR doc widget layouts. Single # doc_widget_layout is created for each document type. # But all doc_widgets for each STR instance and # document types will be added here. doc_widget_layout = scroll_area.findChild( QVBoxLayout, u'doc_widget_layout_{}'.format(doc_text) ) doc_widget = QWidget() doc_widget.setObjectName( u'doc_widget_{}_{}'.format(doc_text, str_number) ) self.hide_all_other_widget(doc_text, str_number) doc_widget_layout.addWidget(doc_widget) # Create the layout so that layouts are registered in # which uploaded document widgets are added. layout = QVBoxLayout(doc_widget) layout.setObjectName(u'layout_{}_{}'.format( doc_text, str_number ) ) # If the doc widget exists, get the lowest # layout so that it is registered. else: # hide all other widgets self.hide_all_other_widget(doc_text, str_number) # show the current doc widget to display # the document widgets for the current tab. self.hide_doc_widgets(doc_widget, False) layout = doc_widget.findChild( QVBoxLayout, u'layout_{}_{}'.format( doc_text, str_number ) ) # register layout self.supporting_doc_manager.registerContainer( layout, doc_id ) def hide_all_other_widget(self, doc_text, str_number): """ Hides all other supporting document widget except the current STR node widget. :param doc_text: The current document type selected. :type doc_text: String :param str_number: The STR node number :type str_number: Integer """ expression = QRegExp(u'doc_widget*') # hide all existing widgets in all layouts for widget in self.docs_tab.findChildren(QWidget, expression): if widget.objectName() != u'doc_widget_{}_{}'.format( doc_text, str_number): self.hide_doc_widgets(widget, True) def on_upload_document(self): ''' Slot raised when the user clicks to upload a supporting document. ''' document_str = QApplication.translate( "SupportingDocuments", "Specify the Document File Location" ) documents = self.select_file_dialog(document_str) cbo_index = self.doc_type_cbo.currentIndex() doc_id = self.doc_type_cbo.itemData(cbo_index) for doc in documents: self.supporting_doc_manager.insertDocumentFromFile( doc, doc_id, self.social_tenure, self.current_party_count ) # Set last path if len(documents) > 0: doc = documents[0] fi = QFileInfo(doc) dir_path = fi.absolutePath() set_last_document_path(dir_path) model_objs = self.supporting_doc_manager.model_objects() self.onUploadDocument.emit(model_objs) def select_file_dialog(self, title): """ Displays a file dialog for a user to specify a source document :param title: The title of the file dialog :type title: String """ # Get last path for supporting documents last_path = last_document_path() if last_path is None: last_path = '/home' files = QFileDialog.getOpenFileNames( iface.mainWindow(), title, last_path, "Source Documents (*.jpg *.jpeg *.png *.bmp *.tiff *.svg)" ) return files
class OGRReader(object): def __init__(self, source_file): self._ds = ogr.Open(source_file) self._targetGeomColSRID = -1 self._geomType = '' self._dbSession = STDMDb.instance().session self._mapped_cls = None self._mapped_doc_cls = None self._current_profile = current_profile() self._source_doc_manager = None def getLayer(self): #Return the first layer in the data source if self.isValid(): numLayers = self._ds.GetLayerCount() if numLayers > 0: return self._ds.GetLayer(0) else: return None def getSpatialRefCode(self): #Get the EPSG code (More work required) if self.getLayer() != None: spRef = self.getLayer().GetSpatialRef() refCode = spRef.GetAttrValue("PRIMEM|AUTHORITY", 1) else: #Fallback to WGS84 refCode = 4326 return refCode def isValid(self): #Whether the open process succeeded or failed if self._ds is None: return False else: return True def reset(self): #Destroy self._ds = None self._geomType = "" self._targetGeomColSRID = -1 def getFields(self): #Return the data source's fields in a list fields = [] lyr = self.getLayer() lyr.ResetReading() feat_defn = lyr.GetLayerDefn() for l in range(feat_defn.GetFieldCount()): field_defn = feat_defn.GetFieldDefn(l) fields.append(str(field_defn.GetNameRef())) return fields def _data_source_entity(self, table_name): entity = self._current_profile.entity_by_name(table_name) return entity def entity_virtual_columns(self, table_name): """ :param table_name: Name of the target table. :type table_name: str :return: Returns a list of derived columns for the specified target table. :rtype: list """ entity = self._data_source_entity(table_name) if entity is None: return [] return entity.virtual_columns() def _get_mapped_class(self, table_name): #Get entity from the corresponding table name entity = self._data_source_entity(table_name) if entity is None: return None ent_model, doc_model = entity_model(entity, with_supporting_document=True) return ent_model, doc_model def _insertRow(self, columnValueMapping): """ Insert a new row using the mapped class instance then mapping column names to the corresponding column values. """ model_instance = self._mapped_cls() for col, value in columnValueMapping.iteritems(): if hasattr(model_instance, col): ''' #Check if column type is enumeration and transform accordingly col_is_enum, enum_symbol = self._enumeration_column_type(col, value) if col_is_enum: value = enum_symbol ''' if not isinstance(value, IgnoreType): setattr(model_instance, col, value) try: self._dbSession.add(model_instance) self._dbSession.commit() except: self._dbSession.rollback() raise def featToDb(self, targettable, columnmatch, append, parentdialog, geomColumn=None, geomCode=-1, translator_manager=None): """ Performs the data import from the source layer to the STDM database. :param targettable: Destination table name :param columnmatch: Dictionary containing source columns as keys and target columns as the values. :param append: True to append, false to overwrite by deleting previous records :param parentdialog: A reference to the calling dialog. :param translator_manager: Instance of 'stdm.data.importexport.ValueTranslatorManager' containing value translators defined for the destination table columns. :type translator_manager: ValueTranslatorManager """ #Check current profile if self._current_profile is None: msg = QApplication.translate( 'OGRReader', 'The current profile could not be determined.\nPlease set it ' 'in the Options dialog or Configuration Wizard.') raise ConfigurationException(msg) if translator_manager is None: translator_manager = ValueTranslatorManager() #Delete existing rows in the target table if user has chosen to overwrite if not append: delete_table_data(targettable) #Container for mapping column names to their corresponding values column_value_mapping = {} lyr = self.getLayer() lyr.ResetReading() feat_defn = lyr.GetLayerDefn() numFeat = lyr.GetFeatureCount() #Configure progress dialog init_val = 0 progress = QProgressDialog("", "&Cancel", init_val, numFeat, parentdialog) progress.setWindowModality(Qt.WindowModal) lblMsgTemp = "Importing {0} of {1} to STDM..." #Set entity for use in translators destination_entity = self._data_source_entity(targettable) for feat in lyr: column_count = 0 progress.setValue(init_val) progressMsg = lblMsgTemp.format((init_val + 1), numFeat) progress.setLabelText(progressMsg) if progress.wasCanceled(): break #Reset source document manager for new records if destination_entity.supports_documents: if not self._source_doc_manager is None: self._source_doc_manager.reset() for f in range(feat_defn.GetFieldCount()): field_defn = feat_defn.GetFieldDefn(f) field_name = field_defn.GetNameRef() #Append value only if it has been defined by the user if field_name in columnmatch: dest_column = columnmatch[field_name] field_value = feat.GetField(f) #Create mapped class only once if self._mapped_cls is None: mapped_cls, mapped_doc_cls = self._get_mapped_class( targettable) if mapped_cls is None: msg = QApplication.translate( "OGRReader", "Something happened that caused the " "database table not to be mapped to the " "corresponding model class. Please contact" " your system administrator.") raise RuntimeError(msg) self._mapped_cls = mapped_cls self._mapped_doc_cls = mapped_doc_cls #Create source document manager if the entity supports them if destination_entity.supports_documents: self._source_doc_manager = SourceDocumentManager( destination_entity.supporting_doc, self._mapped_doc_cls) if geomColumn is not None: #Use geometry column SRID in the target table self._geomType,self._targetGeomColSRID = \ geometryType(targettable,geomColumn) ''' Check if there is a value translator defined for the specified destination column. ''' value_translator = translator_manager.translator( dest_column) if not value_translator is None: #Set destination table entity value_translator.entity = destination_entity source_col_names = value_translator.source_column_names( ) field_value_mappings = self._map_column_values( feat, feat_defn, source_col_names) #Set source document manager if required if value_translator.requires_source_document_manager: value_translator.source_document_manager = self._source_doc_manager field_value = value_translator.referencing_column_value( field_value_mappings) if not isinstance(field_value, IgnoreType): column_value_mapping[dest_column] = field_value #Set supporting documents if destination_entity.supports_documents: column_value_mapping['documents'] = \ self._source_doc_manager.model_objects() column_count += 1 #Only insert geometry if it has been defined by the user if geomColumn is not None: geom = feat.GetGeometryRef() if geom is not None: #Get geometry in WKT/WKB format geomWkb = geom.ExportToWkt() column_value_mapping[geomColumn] = "SRID={0!s};{1}".format( self._targetGeomColSRID, geomWkb) #Check if the geometry types match layerGeomType = geom.GetGeometryName() if layerGeomType.lower() != self._geomType.lower(): raise TypeError("The geometries of the source and destination columns do not match.\n" \ "Source Geometry Type: {0}, Destination Geometry Type: {1}".format(layerGeomType, self._geomType)) return try: #Insert the record self._insertRow(column_value_mapping) except: progress.close() raise init_val += 1 progress.setValue(numFeat) def _enumeration_column_type(self, column_name, value): """ Checks if the given column is of DeclEnumType. :param column_name: Name of the enumeration column. :type column_name: str :return: True if column is of DeclType and return Enum symbol; Else, False and None. :rtype: tuple """ try: #Get column type of the enumeration enum_col_type = self._mapped_cls.__mapper__.columns[ column_name].type except KeyError: return False, None if not hasattr(enum_col_type, "enum"): return False, None else: enum_obj = enum_col_type.enum try: if not isinstance(value, str) or not isinstance( value, unicode): value = unicode(value) enum_symbol = enum_obj.from_string(value.strip()) except ValueError: enum_symbol = IgnoreType() return True, enum_symbol def _map_column_values(self, feature, feature_defn, source_cols): """ Retrieves values for specific columns from the specified feature. :param feature: Input feature. :type feature: ogr.Feature :param feature_defn: Feature definition for the layer. :type feature_defn: ogr.FeatureDefn :param source_cols: List of columns whose respective values will be retrieved. :type source_cols: list :return: Collection containing pairs of column names and corresponding values. :rtype: dict """ col_values = {} if len(source_cols) == 0: return col_values for f in range(feature_defn.GetFieldCount()): field_defn = feature_defn.GetFieldDefn(f) field_name = field_defn.GetNameRef() match_idx = getIndex(source_cols, field_name) if match_idx != -1: field_value = feature.GetField(f) col_values[field_name] = field_value return col_values
def featToDb(self, targettable, columnmatch, append, parentdialog, geomColumn=None, geomCode=-1, translator_manager=None): """ Performs the data import from the source layer to the STDM database. :param targettable: Destination table name :param columnmatch: Dictionary containing source columns as keys and target columns as the values. :param append: True to append, false to overwrite by deleting previous records :param parentdialog: A reference to the calling dialog. :param translator_manager: Instance of 'stdm.data.importexport.ValueTranslatorManager' containing value translators defined for the destination table columns. :type translator_manager: ValueTranslatorManager """ # Check current profile if self._current_profile is None: msg = QApplication.translate( 'OGRReader', 'The current profile could not be determined.\nPlease set it ' 'in the Options dialog or Configuration Wizard.') raise ConfigurationException(msg) if translator_manager is None: translator_manager = ValueTranslatorManager() # Delete existing rows in the target table if user has chosen to overwrite if not append: delete_table_data(targettable) # Container for mapping column names to their corresponding values lyr = self.getLayer() lyr.ResetReading() feat_defn = lyr.GetLayerDefn() numFeat = lyr.GetFeatureCount() # Configure progress dialog init_val = 0 progress = QProgressDialog("", "&Cancel", init_val, numFeat, parentdialog) progress.setWindowModality(Qt.WindowModal) lblMsgTemp = "Importing {0} of {1} to STDM..." # Set entity for use in translators destination_entity = self._data_source_entity(targettable) for feat in lyr: column_value_mapping = {} column_count = 0 progress.setValue(init_val) progressMsg = lblMsgTemp.format((init_val + 1), numFeat) progress.setLabelText(progressMsg) if progress.wasCanceled(): break # Reset source document manager for new records if destination_entity.supports_documents: if not self._source_doc_manager is None: self._source_doc_manager.reset() for f in range(feat_defn.GetFieldCount()): field_defn = feat_defn.GetFieldDefn(f) field_name = field_defn.GetNameRef() # Append value only if it has been defined by the user if field_name in columnmatch: dest_column = columnmatch[field_name] field_value = feat.GetField(f) # Create mapped class only once if self._mapped_cls is None: mapped_cls, mapped_doc_cls = self._get_mapped_class( targettable) if mapped_cls is None: msg = QApplication.translate( "OGRReader", "Something happened that caused the " "database table not to be mapped to the " "corresponding model class. Please contact" " your system administrator.") raise RuntimeError(msg) self._mapped_cls = mapped_cls self._mapped_doc_cls = mapped_doc_cls # Create source document manager if the entity supports them if destination_entity.supports_documents: self._source_doc_manager = SourceDocumentManager( destination_entity.supporting_doc, self._mapped_doc_cls) if geomColumn is not None: # Use geometry column SRID in the target table self._geomType, self._targetGeomColSRID = \ geometryType(targettable, geomColumn) ''' Check if there is a value translator defined for the specified destination column. ''' value_translator = translator_manager.translator( dest_column) if value_translator is not None: # Set destination table entity value_translator.entity = destination_entity source_col_names = value_translator.source_column_names( ) field_value_mappings = self._map_column_values( feat, feat_defn, source_col_names) # Set source document manager if required if value_translator.requires_source_document_manager: value_translator.source_document_manager = self._source_doc_manager field_value = value_translator.referencing_column_value( field_value_mappings) if not isinstance(field_value, IgnoreType): # Check column type and rename if multiple select for # SQLAlchemy compatibility col_obj = destination_entity.column(dest_column) if col_obj.TYPE_INFO == 'MULTIPLE_SELECT': lk_name = col_obj.value_list.name dest_column = '{0}_collection'.format(lk_name) column_value_mapping[dest_column] = field_value # Set supporting documents if destination_entity.supports_documents: column_value_mapping['documents'] = \ self._source_doc_manager.model_objects() column_count += 1 # Only insert geometry if it has been defined by the user if geomColumn is not None: geom = feat.GetGeometryRef() if geom is not None: # Check if the geometry types match layerGeomType = geom.GetGeometryName() # Convert polygon to multipolygon if the destination table is multi-polygon. geom_wkb, geom_type = self.auto_fix_geom_type( geom, layerGeomType, self._geomType) column_value_mapping[geomColumn] = "SRID={0!s};{1}".format( self._targetGeomColSRID, geom_wkb) if geom_type.lower() != self._geomType.lower(): raise TypeError( "The geometries of the source and destination columns do not match.\n" \ "Source Geometry Type: {0}, Destination Geometry Type: {1}".format( geom_type, self._geomType)) try: # Insert the record self._insertRow(targettable, column_value_mapping) except: progress.close() raise init_val += 1 progress.setValue(numFeat)
class SupportingDocumentsWidget(QWidget): """ Widget for managing an entity's supporting documents. It enables listing of documents grouped by tabs depending on type. """ def __init__(self, entity_supporting_document, supporting_doc_model_cls, parent=None): """ Class constructor. :param entity_supporting_document: Object containing information pertaining to document types, parent entity etc. :type entity_supporting_document: EntitySupportingDocument :param supporting_doc_model_cls: Class representing the data model corresponding to the entity supporting document object. :type supporting_doc_model_cls: object :param parent: Parent container widget. :type parent: QWidget """ QWidget.__init__(self, parent) self._init_gui() self._entity_supporting_doc = entity_supporting_document #Container for document type widgets based on lookup id self._doc_type_widgets = {} #Init document manager self.source_document_manager = SourceDocumentManager( self._entity_supporting_doc, supporting_doc_model_cls, self) self._load_document_types() #Connect signals self._btn_add_document.clicked.connect( self._on_add_supporting_document) self._cbo_doc_type.currentIndexChanged.connect( self.on_doc_type_changed) self._doc_tab_container.currentChanged.connect( self.on_tab_doc_type_changed) def _init_gui(self): self._gl = QGridLayout(self) self._label = QLabel(self) self._label.setText(self.tr('Select document type')) self._gl.addWidget(self._label, 0, 0, 1, 1) self._cbo_doc_type = QComboBox(self) self._gl.addWidget(self._cbo_doc_type, 0, 1, 1, 1) self._btn_add_document = QPushButton(self) doc_ico = QIcon(':/plugins/stdm/images/icons/document.png') self._btn_add_document.setIcon(doc_ico) self._btn_add_document.setText(self.tr('Add document...')) self._btn_add_document.setMaximumWidth(200) self._gl.addWidget(self._btn_add_document, 0, 2, 1, 1) self._doc_tab_container = QTabWidget(self) self._gl.addWidget(self._doc_tab_container, 1, 0, 1, 3) self.setMinimumHeight(140) def on_doc_type_changed(self, idx): """ Slot raised when the document types changes. The corresponding widget in the tab container is also selected. :param idx: Item index in the combobox. :type idx: int """ if idx == -1: return self._doc_tab_container.setCurrentIndex(idx) def on_tab_doc_type_changed(self, idx): """ Slot raised when the document types changes. The corresponding widget in the tab container is also selected. :param idx: Item index in the tab widget. :type idx: int """ if idx == -1: return self._cbo_doc_type.setCurrentIndex(idx) def current_document_type(self): """ :return: Returns the currently selected document type in the combobox. :rtype: str """ return self._cbo_doc_type.currentText() def current_document_type_id(self): """ :return: Returns the primary key/id of the currently selected document type in the combobox, else -1 if there is not current item in the combobox :rtype: int """ if not self.current_document_type(): return -1 curr_idx = self._cbo_doc_type.currentIndex() return self._cbo_doc_type.itemData(curr_idx) def count(self): """ :return: Returns the number of document types supported by this widget. :rtype: int """ return self._cbo_doc_type.count() def document_type_containers(self): """ :return: Returns a list of document container widgets for all registered document types. :rtype: list """ return self.source_document_manager.containers.values() def document_type_widget(self, name): """ Searches for the document type widget that corresponds to the given name. :param name: Name of the document type. :type name: str :return: Returns the document widget corresponding to the given type name. :rtype: QWidget """ idx = self._cbo_doc_type.findText(name) if idx == -1: return None rec_id = self._cbo_doc_type.itemData(idx) return self._doc_type_widgets.get(rec_id, None) def _load_document_types(self): #Load document types in the combobox and tab widget vl_cls = entity_model(self._entity_supporting_doc.document_type_entity, entity_only=True) vl_obj = vl_cls() res = vl_obj.queryObject().all() for r in res: #Add to combo self._cbo_doc_type.addItem(r.value, r.id) #Add to tab widget doc_type_widget = _DocumentTypeContainer(self) self._doc_tab_container.addTab(doc_type_widget, r.value) self._doc_type_widgets[r.id] = doc_type_widget #Register container self.source_document_manager.registerContainer( doc_type_widget.container, r.id) def _on_add_supporting_document(self): #Slot raised when the user select to add a supporting document if self.count == 0: return select = self.tr('Select') supporting_docs_str = 'Supporting Documents' title = u'{0} {1} {2}'.format(select, self.current_document_type(), supporting_docs_str) filter_str = u'{0} (*.jpg *.jpeg *.png *.bmp *.tiff *.svg *.pdf)'.format( supporting_docs_str) #Get last path for supporting documents last_path = last_document_path() if last_path is None: last_path = '/home' else: dir = QDir(last_path) if not dir.exists(): last_path = '/home' source_docs = QFileDialog.getOpenFileNames(self, title, last_path, filter_str) doc_type_id = self._cbo_doc_type.itemData( self._cbo_doc_type.currentIndex()) parent_entity = self._entity_supporting_doc.parent_entity for doc in source_docs: self.source_document_manager.insertDocumentFromFile( doc, doc_type_id, parent_entity) #Set last path if len(source_docs) > 0: doc = source_docs[0] fi = QFileInfo(doc) dir_path = fi.absolutePath() set_last_document_path(dir_path)
class OGRReader: def __init__(self, source_file): self._ds = ogr.Open(source_file) self._targetGeomColSRID = -1 self._geomType = '' self._dbSession = STDMDb.instance().session self._mapped_cls = None self._mapped_doc_cls = None self._current_profile = current_profile() self._source_doc_manager = None def getLayer(self): # Return the first layer in the data source if self.isValid(): numLayers = self._ds.GetLayerCount() if numLayers > 0: return self._ds.GetLayer(0) else: return None def getSpatialRefCode(self): # Get the EPSG code (More work required) if self.getLayer() != None: spRef = self.getLayer().GetSpatialRef() refCode = spRef.GetAttrValue("PRIMEM|AUTHORITY", 1) else: # Fallback to WGS84 refCode = 4326 return refCode def isValid(self): # Whether the open process succeeded or failed if self._ds is None: return False else: return True def reset(self): # Destroy self._ds = None self._geomType = "" self._targetGeomColSRID = -1 def getFields(self): # Return the data source's fields in a list fields = [] lyr = self.getLayer() lyr.ResetReading() feat_defn = lyr.GetLayerDefn() for l in range(feat_defn.GetFieldCount()): field_defn = feat_defn.GetFieldDefn(l) fields.append(str(field_defn.GetNameRef())) return fields def _data_source_entity(self, table_name): entity = self._current_profile.entity_by_name(table_name) return entity def entity_virtual_columns(self, table_name): """ :param table_name: Name of the target table. :type table_name: str :return: Returns a list of derived columns for the specified target table. :rtype: list """ entity = self._data_source_entity(table_name) if entity is None: return [] return entity.virtual_columns() def _get_mapped_class(self, table_name): # Get entity from the corresponding table name entity = self._data_source_entity(table_name) if entity is None: return None ent_model, doc_model = entity_model(entity, with_supporting_document=True) return ent_model, doc_model def auto_fix_percent(self, target_table, col_name, value): """ Fixes percent columns if empty and with a wrong format. :param target_table: The destination table name :type target_table: String :param col_name: The destination column name :type col_name: String :param value: Value to be saved to the DB :type value: Any :return: Converted value :rtype: Any """ entity = self._data_source_entity(target_table) if entity.columns[col_name].TYPE_INFO == 'PERCENT': if isinstance(value, str): if not bool(value.strip()) or value.strip().lower() == 'null': value = None if '%' in value: value = value.replace('%', '') try: if value is not None: value = float(value) except ValueError: value = None return value def auto_fix_float_integer(self, target_table, col_name, value): """ Fixes float and integer columns if empty and with a wrong format. :param target_table: The destination table name :type target_table: String :param col_name: The destination column name :type col_name: String :param value: Value to be saved to the DB :type value: Any :return: Converted value :rtype: Any """ entity = self._data_source_entity(target_table) integer_types = [ 'INT', 'LOOKUP', 'ADMIN_SPATIAL_UNIT', 'FOREIGN_KEY', 'DOUBLE' ] float_type = ['DOUBLE'] int_type = ['INT', 'LOOKUP', 'ADMIN_SPATIAL_UNIT', 'FOREIGN_KEY'] if col_name in entity.columns.keys(): if entity.columns[col_name].TYPE_INFO in integer_types: if isinstance(value, str): if not bool( value.strip()) or value.strip().lower() == 'null': value = None if entity.columns[col_name].TYPE_INFO in float_type: try: if value is not None: value = float(value) except ValueError: value = None elif entity.columns[col_name].TYPE_INFO in int_type: try: if value is not None: value = int(value) if isinstance(value, int): if value == 0: if entity.columns[col_name].TYPE_INFO in \ ['LOOKUP', 'ADMIN_SPATIAL_UNIT', 'FOREIGN_KEY']: value = None except ValueError: # TODO show warning to the user that # some values cannot be converted to integer. value = None return value def auto_fix_date(self, target_table, col_name, value): """ Fixes date and datetime columns if empty and with a wrong format. :param target_table: The destination table name :type target_table: String :param col_name: The destination column name :type col_name: String :param value: Value to be saved to the DB :type value: Any :return: Converted value :rtype: Any """ entity = self._data_source_entity(target_table) date_types = ['DATE', 'DATETIME'] if entity.columns[col_name].TYPE_INFO in date_types: if not bool(value) or value.lower() == 'null': value = None return value def auto_fix_yes_no(self, target_table, col_name, value): """ Fixes Yes_NO columns if empty and with a wrong format. :param target_table: The destination table name :type target_table: String :param col_name: The destination column name :type col_name: String :param value: Value to be saved to the DB :type value: Any :return: Converted value :rtype: Any """ entity = self._data_source_entity(target_table) yes_no_types = ['BOOL'] if entity.columns[col_name].TYPE_INFO in yes_no_types: if not bool(value.strip()) or value.strip().lower() == 'null': value = None elif value.strip().lower() == 'yes': value = True elif value.strip().lower() == 'no': value = False elif value.strip().lower() == 'true': value = True elif value.strip().lower() == 'false': value = False return value def _insertRow(self, target_table, columnValueMapping): """ Insert a new row using the mapped class instance then mapping column names to the corresponding column values. """ model_instance = self._mapped_cls() for col, value in columnValueMapping.items(): if hasattr(model_instance, col): # 'documents' is not a column so exclude it. if col != 'documents' and not 'collection' in col: value = self.auto_fix_float_integer( target_table, col, value) value = self.auto_fix_percent(target_table, col, value) value = self.auto_fix_date(target_table, col, value) value = self.auto_fix_yes_no(target_table, col, value) if not isinstance(value, IgnoreType): setattr(model_instance, col, value) try: self._dbSession.add(model_instance) self._dbSession.commit() except: self._dbSession.rollback() raise def auto_fix_geom_type(self, geom, source_geom_type, destination_geom_type): """ Converts single geometry type to multi type if the destination is multi type. :param geom: The OGR geometry :type geom: OGRGeometry :param source_geom_type: Source geometry type :type source_geom_type: String :param destination_geom_type: Destination geometry type :type destination_geom_type: String :return: WkB geometry and the output layer geometry type. :rtype: Tuple """ # Convert polygon to multipolygon if the destination table is multi-polygon. if source_geom_type.lower() == 'polygon' and \ destination_geom_type.lower() == 'multipolygon': geom_wkb, geom_type = self.to_ogr_multi_type( geom, ogr.wkbMultiPolygon) elif source_geom_type.lower() == 'linestring' and \ destination_geom_type.lower() == 'multilinestring': geom_wkb, geom_type = self.to_ogr_multi_type( geom, ogr.wkbMultiLineString) elif source_geom_type.lower() == 'point' and \ destination_geom_type.lower() == 'multipoint': geom_wkb, geom_type = self.to_ogr_multi_type( geom, ogr.wkbMultiPoint) else: geom_wkb = geom.ExportToWkt() geom_type = geom.GetGeometryName() return geom_wkb, geom_type def to_ogr_multi_type(self, geom, ogr_type): """ Convert ogr geometry to multi-type when supplied the ogr.type. :param geom: The ogr geometry :type geom: OGRGeometry :param ogr_type: The ogr geometry type :type ogr_type: String :return: WkB geometry and the output layer geometry type. :rtype: Tuple """ multi_geom = ogr.Geometry(ogr_type) multi_geom.AddGeometry(geom) geom_wkb = multi_geom.ExportToWkt() geom_type = multi_geom.GetGeometryName() return geom_wkb, geom_type def featToDb(self, targettable, columnmatch, append, parentdialog, geomColumn=None, geomCode=-1, translator_manager=None): """ Performs the data import from the source layer to the STDM database. :param targettable: Destination table name :param columnmatch: Dictionary containing source columns as keys and target columns as the values. :param append: True to append, false to overwrite by deleting previous records :param parentdialog: A reference to the calling dialog. :param translator_manager: Instance of 'stdm.data.importexport.ValueTranslatorManager' containing value translators defined for the destination table columns. :type translator_manager: ValueTranslatorManager """ # Check current profile if self._current_profile is None: msg = QApplication.translate( 'OGRReader', 'The current profile could not be determined.\nPlease set it ' 'in the Options dialog or Configuration Wizard.') raise ConfigurationException(msg) if translator_manager is None: translator_manager = ValueTranslatorManager() # Delete existing rows in the target table if user has chosen to overwrite if not append: delete_table_data(targettable) # Container for mapping column names to their corresponding values lyr = self.getLayer() lyr.ResetReading() feat_defn = lyr.GetLayerDefn() numFeat = lyr.GetFeatureCount() # Configure progress dialog init_val = 0 progress = QProgressDialog("", "&Cancel", init_val, numFeat, parentdialog) progress.setWindowModality(Qt.WindowModal) lblMsgTemp = "Importing {0} of {1} to STDM..." # Set entity for use in translators destination_entity = self._data_source_entity(targettable) for feat in lyr: column_value_mapping = {} column_count = 0 progress.setValue(init_val) progressMsg = lblMsgTemp.format((init_val + 1), numFeat) progress.setLabelText(progressMsg) if progress.wasCanceled(): break # Reset source document manager for new records if destination_entity.supports_documents: if not self._source_doc_manager is None: self._source_doc_manager.reset() for f in range(feat_defn.GetFieldCount()): field_defn = feat_defn.GetFieldDefn(f) field_name = field_defn.GetNameRef() # Append value only if it has been defined by the user if field_name in columnmatch: dest_column = columnmatch[field_name] field_value = feat.GetField(f) # Create mapped class only once if self._mapped_cls is None: mapped_cls, mapped_doc_cls = self._get_mapped_class( targettable) if mapped_cls is None: msg = QApplication.translate( "OGRReader", "Something happened that caused the " "database table not to be mapped to the " "corresponding model class. Please contact" " your system administrator.") raise RuntimeError(msg) self._mapped_cls = mapped_cls self._mapped_doc_cls = mapped_doc_cls # Create source document manager if the entity supports them if destination_entity.supports_documents: self._source_doc_manager = SourceDocumentManager( destination_entity.supporting_doc, self._mapped_doc_cls) if geomColumn is not None: # Use geometry column SRID in the target table self._geomType, self._targetGeomColSRID = \ geometryType(targettable, geomColumn) ''' Check if there is a value translator defined for the specified destination column. ''' value_translator = translator_manager.translator( dest_column) if value_translator is not None: # Set destination table entity value_translator.entity = destination_entity source_col_names = value_translator.source_column_names( ) field_value_mappings = self._map_column_values( feat, feat_defn, source_col_names) # Set source document manager if required if value_translator.requires_source_document_manager: value_translator.source_document_manager = self._source_doc_manager field_value = value_translator.referencing_column_value( field_value_mappings) if not isinstance(field_value, IgnoreType): # Check column type and rename if multiple select for # SQLAlchemy compatibility col_obj = destination_entity.column(dest_column) if col_obj.TYPE_INFO == 'MULTIPLE_SELECT': lk_name = col_obj.value_list.name dest_column = '{0}_collection'.format(lk_name) column_value_mapping[dest_column] = field_value # Set supporting documents if destination_entity.supports_documents: column_value_mapping['documents'] = \ self._source_doc_manager.model_objects() column_count += 1 # Only insert geometry if it has been defined by the user if geomColumn is not None: geom = feat.GetGeometryRef() if geom is not None: # Check if the geometry types match layerGeomType = geom.GetGeometryName() # Convert polygon to multipolygon if the destination table is multi-polygon. geom_wkb, geom_type = self.auto_fix_geom_type( geom, layerGeomType, self._geomType) column_value_mapping[geomColumn] = "SRID={0!s};{1}".format( self._targetGeomColSRID, geom_wkb) if geom_type.lower() != self._geomType.lower(): raise TypeError( "The geometries of the source and destination columns do not match.\n" \ "Source Geometry Type: {0}, Destination Geometry Type: {1}".format( geom_type, self._geomType)) try: # Insert the record self._insertRow(targettable, column_value_mapping) except: progress.close() raise init_val += 1 progress.setValue(numFeat) def _enumeration_column_type(self, column_name, value): """ Checks if the given column is of DeclEnumType. :param column_name: Name of the enumeration column. :type column_name: str :return: True if column is of DeclType and return Enum symbol; Else, False and None. :rtype: tuple """ try: # Get column type of the enumeration enum_col_type = self._mapped_cls.__mapper__.columns[ column_name].type except KeyError: return False, None if not hasattr(enum_col_type, "enum"): return False, None else: enum_obj = enum_col_type.enum try: if not isinstance(value, str) or not isinstance(value, str): value = str(value) enum_symbol = enum_obj.from_string(value.strip()) except ValueError: enum_symbol = IgnoreType() return True, enum_symbol def _map_column_values(self, feature, feature_defn, source_cols): """ Retrieves values for specific columns from the specified feature. :param feature: Input feature. :type feature: ogr.Feature :param feature_defn: Feature definition for the layer. :type feature_defn: ogr.FeatureDefn :param source_cols: List of columns whose respective values will be retrieved. :type source_cols: list :return: Collection containing pairs of column names and corresponding values. :rtype: dict """ col_values = {} if len(source_cols) == 0: return col_values for f in range(feature_defn.GetFieldCount()): field_defn = feature_defn.GetFieldDefn(f) field_name = field_defn.GetNameRef() match_idx = getIndex(source_cols, field_name) if match_idx != -1: field_value = feature.GetField(f) col_values[field_name] = field_value return col_values
class Save2DB: """ Class to insert entity data into db """ def __init__(self, entity, attributes, ids=None): """ Initialize class and class variable """ self.attributes = attributes self.form_entity = entity self.doc_model = None self._doc_manager =None self.entity = self.object_from_entity_name(self.form_entity) self.model = self.dbmodel_from_entity() self.key = 0 self.parents_ids = ids self.geom = 4326 self.entity_mapping = {} def object_from_entity_name(self, entity): """ :return: """ if entity == 'social_tenure': return current_profile().social_tenure else: user_entity = current_profile().entity_by_name(entity) return user_entity def entity_has_supporting_docs(self): """ Check if the entity has supporting document before importing :return: Bool """ if self.entity.supports_documents: return self.entity.supports_documents else: return None def entity_supported_document_types(self): """ Get the supported document types before importing so that they are captured during import process :return: List """ return self.entity.document_types_non_hex() def dbmodel_from_entity(self): """ Format model attributes from passed entity attributes :return: """ if self.entity_has_supporting_docs(): entity_object, self.doc_model = entity_model(self.entity, with_supporting_document=True) entity_object_model = entity_object() if hasattr(entity_object_model, 'documents'): if self.entity.TYPE_INFO == 'SOCIAL_TENURE': obj_doc_col = current_profile().social_tenure.supporting_doc else: obj_doc_col = self.entity.supporting_doc self._doc_manager = SourceDocumentManager( obj_doc_col, self.doc_model ) else: entity_object = entity_model(self.entity) entity_object_model = entity_object() return entity_object_model def objects_from_supporting_doc(self, instance_file = None): """ Create supporting doc path instances based on the collected documents :return:paths :rtype: document object instance """ if instance_file: f_dir, file_name = os.path.split(instance_file) for document, val in self.attributes.iteritems(): if str(document).endswith('supporting_document'): if val != '': doc = self.format_document_name_from_attribute(document) doc_path = os.path.normpath(f_dir+'/'+val) abs_path = doc_path.replace('\\','/').strip() if QFile.exists(abs_path): self.supporting_document_model(abs_path, doc) def supporting_document_model(self,doc_path,doc): """ :param doc_path: absolute document path :param doc: document name :type: str Construct supporting document model instance to add into the db :return: """ # Create document container doc_container = QVBoxLayout() supporting_doc_entity = self.entity.supporting_doc.document_type_entity document_type_id = entity_attr_to_id(supporting_doc_entity, 'value', doc, lower=False) # Register container self._doc_manager.registerContainer( doc_container, document_type_id ) #Copy the document to STDM working directory self._doc_manager.insertDocumentFromFile( doc_path, document_type_id, self.entity ) def format_document_name_from_attribute(self, doc): """ Get the type of document from attribute name So that supporting document class instance can save it in the right format :return: """ formatted_doc_list = self.entity_supported_document_types() default = 'General' doc_type = str(doc).split('_',1) if doc_type[0].startswith('supporting') and formatted_doc_list[0] == default: return default elif doc_type[0].startswith('supporting') and formatted_doc_list[0] != default: return formatted_doc_list[0] elif not doc_type[0].startswith('supporting'): actual_doc_name = doc_type[0].replace('-', ' ') for doc_name in formatted_doc_list: if actual_doc_name in doc_name or doc_name.startswith(actual_doc_name): return doc_name else: return formatted_doc_list[0] else: return formatted_doc_list[0] def extract_social_tenure_entities(self): ''' We want to extract social tenure enities so that we know if it has multiple or single entities in the list :return: ''' party_ref_column = '' spatial_ref_column = '' if self.parents_ids is not None: print self.parents_ids if self.attributes.has_key('party'): full_party_ref_column = self.attributes.get('party') party_ref_column = full_party_ref_column + '_id' print 'party{}.'.format(party_ref_column) setattr(self.model, party_ref_column, self.parents_ids.get(full_party_ref_column)[0]) if self.attributes.has_key('spatial_unit'): full_spatial_ref_column = self.attributes.get('spatial_unit') spatial_ref_column = full_spatial_ref_column + '_id' print 'sp.{}.'.format(spatial_ref_column) setattr(self.model, spatial_ref_column, self.parents_ids.get(full_spatial_ref_column)[0]) return party_ref_column, spatial_ref_column def save_to_db(self): """ Format object attribute data from entity and save them into database :return: """ self.column_info() try: if self.entity.short_name == 'social_tenure_relationship': #try: prefix = current_profile().prefix + '_' if self.attributes.has_key('party'): full_party_ref_column = self.attributes.get('party') party_ref_column = full_party_ref_column + '_id' self.attributes.pop('party') else: full_party_ref_column = current_profile().social_tenure.parties[0].name party_ref_column = full_party_ref_column + '_id' setattr(self.model, party_ref_column, self.parents_ids.get(prefix + full_party_ref_column)[0]) if self.attributes.has_key('spatial_unit'): full_spatial_ref_column = self.attributes.get('spatial_unit') spatial_ref_column = full_spatial_ref_column + '_id' self.attributes.pop('spatial_unit') else: full_spatial_ref_column = current_profile().social_tenure.spatial_units[0].name spatial_ref_column = full_spatial_ref_column + '_id' setattr(self.model, spatial_ref_column, self.parents_ids.get(prefix + full_spatial_ref_column)[0]) except: pass for k, v in self.attributes.iteritems(): if hasattr(self.model, k): col_type = self.entity_mapping.get(k) col_prop = self.entity.columns[k] var = self.attribute_formatter(col_type, col_prop, v) setattr(self.model, k, var) if self.entity_has_supporting_docs(): self.model.documents = self._doc_manager.model_objects() self.model.save() return self.model.id def save_parent_to_db(self): """ Format object attribute data from entity and save them into database attribute :return: """ self.column_info() for k, v in self.attributes.iteritems(): if hasattr(self.model, k): col_type = self.entity_mapping.get(k) col_prop = self.entity.columns[k] #print "property{0}.... and type.{1}".format(col_prop, col_type) var = self.attribute_formatter(col_type, col_prop, v) setattr(self.model, k, var) if self.entity_has_supporting_docs(): self.model.documents = self._doc_manager.model_objects() self.model.save() self.key = self.model.id return self.key def save_foreign_key_table(self): """ Get the table with foreign keys only :return: """ for col, type_info in self.column_info().iteritems(): col_prop = self.entity.columns[col] var = self.attribute_formatter(type_info, col_prop, None) setattr(self.model, col, var) self.model.save() self.cleanup() def column_info(self): """ :return: """ self.entity_mapping = {} cols = self.entity.columns.values() for c in cols: self.entity_mapping[c.name] = c.TYPE_INFO def get_srid(self, srid): """ Let the user specify the coordinate system during data import :param srid: :return: """ self.geom = srid return self.geom def id_from_model_object(self, obj): """ We need to obtian id from object instance :param obj: :return: """ return obj.id def attribute_formatter(self, col_type, col_prop, var=None): """ Format geoodk attributes collected in the field to conform to STDM database contrains :return: """ if col_type == 'BOOL': if len(var) < 1: return None if len(var)>1: if var == '' or var is None: return None if var == 'Yes' or var == True: return True if var == 'No' or var == False: return False else: return None if col_type == 'LOOKUP': if len(var) < 1 or var is None: return None if len(var) <4: if var == 'Yes' or var == 'No': return entity_attr_to_model(col_prop.parent, 'value', var).id if var != 'Yes' and var != 'No': lk_code = entity_attr_to_id(col_prop.parent, "code", var) if not str(lk_code).isdigit(): return None else: return lk_code if len(var) > 3: if not str(entity_attr_to_id(col_prop.parent, 'code', var)).isdigit(): id_value = entity_attr_to_model(col_prop.parent, 'value', var) if id_value is not None: return id_value.id #return entity_attr_to_model(col_prop.parent, 'value', var).id else: lk_code = entity_attr_to_id(col_prop.parent, "code", var) if not str(lk_code).isdigit(): return None else: return lk_code else: return None elif col_type == 'ADMIN_SPATIAL_UNIT': var_code = None try: if len(var) < 1 or var is None: return None elif not len(var) > 3: var_code = entity_attr_to_id(col_prop.parent, "code", var) if var_code and var_code == var: return None else: return var_code elif len(var) > 3 and entity_attr_to_id(col_prop.parent, "name", var) is not None: var_code = entity_attr_to_id(col_prop.parent, "name", var) if var_code and var_code == var: return None else: return var_code else: if entity_attr_to_id(col_prop.parent, "name", var) is None: var_code = entity_attr_to_id(col_prop.parent, "code", var) if not var_code or var_code == var: return None except: pass elif col_type == 'MULTIPLE_SELECT': print 'multiple select {}'.format(var) if var == '' or var is None: return None else: print var col_parent = col_prop.association.first_parent lk_val_list = col_parent.values.values() choices_list = [] for code in lk_val_list: choices_list.append(entity_attr_to_id( col_parent.association.first_parent, 'value', code.value)) print choices_list if len(choices_list) > 1: return choices_list else: return None elif col_type == 'GEOMETRY': defualt_srid = 0 if var: geom_provider = STDMGeometry(var) if isinstance(col_prop, GeometryColumn): defualt_srid = col_prop.srid if defualt_srid != 0: geom_provider.set_user_srid(defualt_srid) else: geom_provider.set_user_srid(GEOMPARAM) if col_prop.geometry_type() == 'POINT': return geom_provider.point_to_Wkt() if col_prop.geometry_type() == 'POLYGON': return geom_provider.polygon_to_Wkt() else: return None elif col_type == 'FOREIGN_KEY': if self.parents_ids is None or len(self.parents_ids) < 0: return None else: for code, val in self.parents_ids.iteritems(): if col_prop.parent.name == code: return val[0] else: return None elif col_type == 'INT' or col_type == 'DOUBLE' or col_type == 'PERCENT': if var == '': return None else: return var elif col_type == 'DATETIME' or col_type == 'DATE': if var is None: return None if var == '': return None else: return var else: return var def cleanup(self): """ Reset all the model and entity data before we process the next entity data :return: None z""" self.model = None self.entity = None self.attributes = None self._doc_manager = None