def onFeaturesCommitted(self, layerid, features): """ Update the related entities with the FK key from the primary spatial unit PK. """ if layerid in self._pendingFKEntities: pendingLayerEntity = self._pendingFKEntities[layerid] # Get map layer using layerid refLayer = QgsProject.instance().mapLayer(layerid) if refLayer is not None: fidx = refLayer.fields().indexFromName( pendingLayerEntity.featureAttributeName()) # Show progress dialog for updating the features. progressLabel = QApplication.translate( "StdmMapToolCreateFeature", "Updating related entities...") progressDlg = QProgressDialog(progressLabel, "", 0, len(features), self.iface.mainWindow()) progressDlg.setWindowModality(Qt.WindowModal) for i, feat in enumerate(features): progressDlg.setValue(i) uniqueAttrValue = feat.attributes()[fidx] pendingLayerEntity.setPrimaryKey(uniqueAttrValue, int(feat.id())) progressDlg.setValue(len(features)) progressDlg.deleteLater() del progressDlg
class OsmDownloaderDialog(QDialog, FORM_CLASS): """Downloader for OSM data.""" def __init__(self, parent=None, iface=None): """Constructor for import dialog. :param parent: Optional widget to use as parent. :type parent: QWidget :param iface: An instance of QgisInterface. :type iface: QgisInterface """ QDialog.__init__(self, parent) self.parent = parent self.setupUi(self) icon = resources_path('img', 'icons', 'show-osm-download.svg') self.setWindowIcon(QtGui.QIcon(icon)) title = self.tr('InaSAFE OpenStreetMap Downloader') self.setWindowTitle(title) self.iface = iface self.progress_dialog = None # Set up things for context help self.help_button = self.button_box.button( QtWidgets.QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) self.main_stacked_widget.setCurrentIndex(1) # Output directory self.directory_button.clicked.connect(self.directory_button_clicked) self.output_directory.setPlaceholderText( self.tr('[Create a temporary layer]')) # Disable boundaries group box until boundary checkbox is ticked self.boundary_group.setEnabled(False) # set up the validator for the file name prefix expression = QRegExp('^[A-Za-z0-9-_]*$') validator = QtGui.QRegExpValidator(expression, self.filename_prefix) self.filename_prefix.setValidator(validator) # Advanced panel self.line_edit_custom.setPlaceholderText(STAGING_SERVER) developer_mode = setting('developer_mode', expected_type=bool) self.group_box_advanced.setVisible(developer_mode) self.restore_state() # Setup the rectangle map tool self.canvas = iface.mapCanvas() self.rectangle_map_tool = \ RectangleMapTool(self.canvas) self.rectangle_map_tool.rectangle_created.connect( self.update_extent_from_rectangle) self.capture_button.clicked.connect(self.drag_rectangle_on_map_canvas) # Setup pan tool self.pan_tool = QgsMapToolPan(self.canvas) self.canvas.setMapTool(self.pan_tool) # Setup helper for admin_level json_file_path = resources_path('osm', 'admin_level_per_country.json') if os.path.isfile(json_file_path): with open(json_file_path, encoding='utf-8') as f: self.countries = json.load(f) self.bbox_countries = None self.populate_countries() # connect self.country_comboBox.currentIndexChanged.connect( self.update_helper_political_level) self.admin_level_comboBox.currentIndexChanged.connect( self.update_helper_political_level) self.update_extent_from_map_canvas() def update_helper_political_level(self): """To update the helper about the country and the admin_level.""" current_country = self.country_comboBox.currentText() index = self.admin_level_comboBox.currentIndex() current_level = self.admin_level_comboBox.itemData(index) content = None try: content = \ self.countries[current_country]['levels'][str(current_level)] if content == 'N/A' or content == 'fixme' or content == '': raise KeyError except KeyError: content = self.tr('undefined') finally: text = self.tr('which represents %s in') % content self.boundary_helper.setText(text) def populate_countries(self): """Populate the combobox about countries and levels.""" for i in range(1, 12): self.admin_level_comboBox.addItem(self.tr('Level %s') % i, i) # Set current index to admin_level 8, the most common one self.admin_level_comboBox.setCurrentIndex(7) list_countries = sorted( [self.tr(country) for country in list(self.countries.keys())]) for country in list_countries: self.country_comboBox.addItem(country) self.bbox_countries = {} for country in list_countries: multipolygons = self.countries[country]['bbox'] self.bbox_countries[country] = [] for coords in multipolygons: bbox = QgsRectangle(coords[0], coords[3], coords[2], coords[1]) self.bbox_countries[country].append(bbox) self.update_helper_political_level() def help_toggled(self, flag): """Show or hide the help tab in the stacked widget. .. versionadded: 3.2 :param flag: Flag indicating whether help should be shown or hidden. :type flag: bool """ if flag: self.help_button.setText(self.tr('Hide Help')) self.show_help() else: self.help_button.setText(self.tr('Show Help')) self.hide_help() def hide_help(self): """Hide the usage info from the user. .. versionadded:: 3.2 """ self.main_stacked_widget.setCurrentIndex(1) def show_help(self): """Show usage info to the user.""" # Read the header and footer html snippets self.main_stacked_widget.setCurrentIndex(0) header = html_header() footer = html_footer() string = header message = osm_downloader_help() string += message.to_html() string += footer self.help_web_view.setHtml(string) def restore_state(self): """Read last state of GUI from configuration file.""" last_path = setting('directory', '', expected_type=str) self.output_directory.setText(last_path) def save_state(self): """Store current state of GUI to configuration file.""" set_setting('directory', self.output_directory.text()) def update_extent(self, extent): """Update extent value in GUI based from an extent. :param extent: A list in the form [xmin, ymin, xmax, ymax] where all coordinates provided are in Geographic / EPSG:4326. :type extent: list """ self.x_minimum.setValue(extent[0]) self.y_minimum.setValue(extent[1]) self.x_maximum.setValue(extent[2]) self.y_maximum.setValue(extent[3]) # Updating the country if possible. rectangle = QgsRectangle(extent[0], extent[1], extent[2], extent[3]) center = rectangle.center() for country in self.bbox_countries: for polygon in self.bbox_countries[country]: if polygon.contains(center): index = self.country_comboBox.findText(country) self.country_comboBox.setCurrentIndex(index) break else: # Continue if the inner loop wasn't broken. continue # Inner loop was broken, break the outer. break else: self.country_comboBox.setCurrentIndex(0) def update_extent_from_map_canvas(self): """Update extent value in GUI based from value in map. .. note:: Delegates to update_extent() """ self.bounding_box_group.setTitle( self.tr('Bounding box from the map canvas')) # Get the extent as [xmin, ymin, xmax, ymax] extent = viewport_geo_array(self.iface.mapCanvas()) self.update_extent(extent) def update_extent_from_rectangle(self): """Update extent value in GUI based from the QgsMapTool rectangle. .. note:: Delegates to update_extent() """ self.show() self.canvas.unsetMapTool(self.rectangle_map_tool) self.canvas.setMapTool(self.pan_tool) rectangle = self.rectangle_map_tool.rectangle() if rectangle: self.bounding_box_group.setTitle( self.tr('Bounding box from rectangle')) extent = rectangle_geo_array(rectangle, self.iface.mapCanvas()) self.update_extent(extent) def directory_button_clicked(self): """Show a dialog to choose directory.""" # noinspection PyCallByClass,PyTypeChecker self.output_directory.setText( QFileDialog.getExistingDirectory( self, self.tr('Select download directory'))) def drag_rectangle_on_map_canvas(self): """Hide the dialog and allow the user to draw a rectangle.""" self.hide() self.rectangle_map_tool.reset() self.canvas.unsetMapTool(self.pan_tool) self.canvas.setMapTool(self.rectangle_map_tool) def get_checked_features(self): """Create a tab with all checked features. :return A list with all features which are checked in the UI. :rtype list """ feature_types = [] if self.roads_flag.isChecked(): feature_types.append('roads') if self.buildings_flag.isChecked(): feature_types.append('buildings') if self.building_points_flag.isChecked(): feature_types.append('building-points') if self.flood_prone_flag.isChecked(): feature_types.append('flood-prone') if self.evacuation_centers_flag.isChecked(): feature_types.append('evacuation-centers') if self.boundary_flag.isChecked(): level = self.admin_level_comboBox.currentIndex() + 1 feature_types.append('boundary-%s' % level) return feature_types def accept(self): """Do osm download and display it in QGIS.""" error_dialog_title = self.tr('InaSAFE OpenStreetMap Downloader Error') # Lock the bounding_box_group self.bounding_box_group.setDisabled(True) # Get the extent y_minimum = self.y_minimum.value() y_maximum = self.y_maximum.value() x_minimum = self.x_minimum.value() x_maximum = self.x_maximum.value() extent = [x_minimum, y_minimum, x_maximum, y_maximum] # Validate extent valid_flag = validate_geo_array(extent) if not valid_flag: message = self.tr( 'The bounding box is not valid. Please make sure it is ' 'valid or check your projection!') # noinspection PyCallByClass,PyTypeChecker,PyArgumentList display_warning_message_box(self, error_dialog_title, message) # Unlock the bounding_box_group self.bounding_box_group.setEnabled(True) return # Validate features feature_types = self.get_checked_features() if len(feature_types) < 1: message = self.tr('No feature selected. ' 'Please make sure you have checked one feature.') # noinspection PyCallByClass,PyTypeChecker,PyArgumentList display_warning_message_box(self, error_dialog_title, message) # Unlock the bounding_box_group self.bounding_box_group.setEnabled(True) return if self.radio_custom.isChecked(): server_url = self.line_edit_custom.text() if not server_url: # It's the place holder. server_url = STAGING_SERVER else: server_url = PRODUCTION_SERVER try: self.save_state() self.require_directory() # creating progress dialog for download self.progress_dialog = QProgressDialog(self) self.progress_dialog.setAutoClose(False) self.progress_dialog.setWindowTitle(self.windowTitle()) for feature_type in feature_types: output_directory = self.output_directory.text() if output_directory == '': output_directory = temp_dir(sub_dir='work') output_prefix = self.filename_prefix.text() overwrite = self.overwrite_flag.isChecked() output_base_file_path = self.get_output_base_path( output_directory, output_prefix, feature_type, overwrite) # noinspection PyTypeChecker download(feature_type, output_base_file_path, extent, self.progress_dialog, server_url) try: self.load_shapefile(feature_type, output_base_file_path) except FileMissingError as exception: display_warning_message_box(self, error_dialog_title, str(exception)) self.done(QDialog.Accepted) self.rectangle_map_tool.reset() except CanceledImportDialogError: # don't show anything because this exception raised # when user canceling the import process directly pass except Exception as exception: # pylint: disable=broad-except # noinspection PyCallByClass,PyTypeChecker,PyArgumentList display_warning_message_box(self, error_dialog_title, str(exception)) self.progress_dialog.cancel() self.progress_dialog.deleteLater() finally: # Unlock the bounding_box_group self.bounding_box_group.setEnabled(True) def get_output_base_path(self, output_directory, output_prefix, feature_type, overwrite): """Get a full base name path to save the shapefile. :param output_directory: The directory where to put results. :type output_directory: str :param output_prefix: The prefix to add for the shapefile. :type output_prefix: str :param feature_type: What kind of features should be downloaded. Currently 'buildings', 'building-points' or 'roads' are supported. :type feature_type: str :param overwrite: Boolean to know if we can overwrite existing files. :type overwrite: bool :return: The base path. :rtype: str """ path = os.path.join(output_directory, '%s%s' % (output_prefix, feature_type)) if overwrite: # If a shapefile exists, we must remove it (only the .shp) shp = '%s.shp' % path if os.path.isfile(shp): os.remove(shp) else: separator = '-' suffix = self.get_unique_file_path_suffix('%s.shp' % path, separator) if suffix: path = os.path.join( output_directory, '%s%s%s%s' % (output_prefix, feature_type, separator, suffix)) return path @staticmethod def get_unique_file_path_suffix(file_path, separator='-', i=0): """Return the minimum number to suffix the file to not overwrite one. Example : /tmp/a.txt exists. - With file_path='/tmp/b.txt' will return 0. - With file_path='/tmp/a.txt' will return 1 (/tmp/a-1.txt) :param file_path: The file to check. :type file_path: str :param separator: The separator to add before the prefix. :type separator: str :param i: The minimum prefix to check. :type i: int :return: The minimum prefix you should add to not overwrite a file. :rtype: int """ basename = os.path.splitext(file_path) if i != 0: file_path_test = os.path.join( '%s%s%s%s' % (basename[0], separator, i, basename[1])) else: file_path_test = file_path if os.path.isfile(file_path_test): return OsmDownloaderDialog.get_unique_file_path_suffix( file_path, separator, i + 1) else: return i def require_directory(self): """Ensure directory path entered in dialog exist. When the path does not exist, this function will ask the user if he want to create it or not. :raises: CanceledImportDialogError - when user choose 'No' in the question dialog for creating directory. """ path = self.output_directory.text() if path == '': # If let empty, we create an temporary directory return if os.path.exists(path): return title = self.tr('Directory %s not exist') % path question = self.tr( 'Directory %s not exist. Do you want to create it?') % path # noinspection PyCallByClass,PyTypeChecker answer = QMessageBox.question(self, title, question, QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.Yes: if len(path) != 0: os.makedirs(path) else: # noinspection PyCallByClass,PyTypeChecker,PyArgumentList display_warning_message_box( self, self.tr('InaSAFE error'), self.tr('Output directory can not be empty.')) raise CanceledImportDialogError() else: raise CanceledImportDialogError() def load_shapefile(self, feature_type, base_path): """Load downloaded shape file to QGIS Main Window. :param feature_type: What kind of features should be downloaded. Currently 'buildings', 'building-points' or 'roads' are supported. :type feature_type: str :param base_path: The base path of the shape file (without extension). :type base_path: str :raises: FileMissingError - when buildings.shp not exist """ path = '%s.shp' % base_path if not os.path.exists(path): message = self.tr( '%s does not exist. The server does not have any data for ' 'this extent.' % path) raise FileMissingError(message) layer = self.iface.addVectorLayer(path, feature_type, 'ogr') # Check if it's a building layer about the 2.5D if feature_type == 'buildings': layer_scope = QgsExpressionContextUtils.layerScope(layer) if not layer_scope.variable('qgis_25d_height'): QgsExpressionContextUtils.setLayerVariable( layer, 'qgis_25d_height', 0.0002) if not layer_scope.variable('qgis_25d_angle'): QgsExpressionContextUtils.setLayerVariable( layer, 'qgis_25d_angle', 70) def reject(self): """Redefinition of the method to remove the rectangle selection tool. It will call the super method. """ self.canvas.unsetMapTool(self.rectangle_map_tool) self.rectangle_map_tool.reset() super(OsmDownloaderDialog, self).reject()
def open_style(input_file): # pylint: disable=too-many-locals,too-many-branches,too-many-statements """ Opens a .style file """ if not Extractor.is_mdb_tools_binary_available(): bar = iface.messageBar() widget = bar.createMessage('SLYR', "MDB Tools utility not found") settings_button = QPushButton("Configure…", pressed=partial(open_settings, widget)) widget.layout().addWidget(settings_button) bar.pushWidget(widget, Qgis.Critical) return True style = QgsStyle() style.createMemoryDatabase() symbol_names = set() def make_name_unique(name): """ Ensures that the symbol name is unique (in a case insensitive way) """ counter = 0 candidate = name while candidate.lower() in symbol_names: # make name unique if counter == 0: candidate += '_1' else: candidate = candidate[:candidate.rfind('_') + 1] + str(counter) counter += 1 symbol_names.add(candidate.lower()) return candidate feedback = QgsFeedback() progress_dialog = QProgressDialog("Loading style database…", "Abort", 0, 100, None) progress_dialog.setWindowTitle("Loading Style") def progress_changed(progress): """ Handles feedback to progress dialog bridge """ progress_dialog.setValue(progress) iters = 0 while QCoreApplication.hasPendingEvents() and iters < 100: QCoreApplication.processEvents() iters += 1 feedback.progressChanged.connect(progress_changed) def cancel(): """ Slot to cancel the import """ feedback.cancel() progress_dialog.canceled.connect(cancel) unreadable = [] warnings = set() errors = set() types_to_extract = [Extractor.FILL_SYMBOLS, Extractor.LINE_SYMBOLS, Extractor.MARKER_SYMBOLS, Extractor.COLOR_RAMPS, Extractor.TEXT_SYMBOLS, Extractor.LABELS, Extractor.MAPLEX_LABELS, Extractor.AREA_PATCHES, Extractor.LINE_PATCHES] type_percent = 100 / len(types_to_extract) for type_index, symbol_type in enumerate(types_to_extract): try: raw_symbols = Extractor.extract_styles(input_file, symbol_type) except MissingBinaryException: show_warning('MDB Tools utility not found', 'Convert style', 'The MDB tools "mdb-export" utility is required to convert .style databases. Please setup a path to the MDB tools utility in the SLYR options panel.', level=Qgis.Critical) progress_dialog.deleteLater() return True if feedback.isCanceled(): break for index, raw_symbol in enumerate(raw_symbols): feedback.setProgress(index / len(raw_symbols) * type_percent + type_percent * type_index) if feedback.isCanceled(): break name = raw_symbol[Extractor.NAME] tags = raw_symbol[Extractor.TAGS].split(';') if symbol_type in ( Extractor.AREA_PATCHES, Extractor.LINE_PATCHES, Extractor.TEXT_SYMBOLS, Extractor.MAPLEX_LABELS, Extractor.LABELS): if symbol_type == Extractor.AREA_PATCHES: type_string = 'area patches' elif symbol_type == Extractor.LINE_PATCHES: type_string = 'line patches' elif symbol_type == Extractor.TEXT_SYMBOLS: type_string = 'text symbols' elif symbol_type == Extractor.MAPLEX_LABELS: type_string = 'maplex labels' elif symbol_type == Extractor.LABELS: type_string = 'labels' else: type_string = '' unreadable.append('<b>{}</b>: {} conversion requires a licensed version of the SLYR plugin'.format( html.escape(name), type_string)) continue unique_name = make_name_unique(name) handle = BytesIO(raw_symbol[Extractor.BLOB]) stream = Stream(handle) stream.allow_shortcuts = False try: symbol = stream.read_object() except UnreadableSymbolException as e: e = 'Unreadable object: {}'.format(e) unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e)))) continue except NotImplementedException as e: unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e)))) continue except UnsupportedVersionException as e: e = 'Unsupported version: {}'.format(e) unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e)))) continue except UnknownClsidException as e: unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e)))) continue except UnreadablePictureException as e: unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e)))) continue context = Context() context.symbol_name = unique_name def unsupported_object_callback(msg, level=Context.WARNING): if level == Context.WARNING: warnings.add('<b>{}</b>: {}'.format(html.escape(unique_name), html.escape(msg))) elif level == Context.CRITICAL: errors.add('<b>{}</b>: {}'.format(html.escape(unique_name), html.escape(msg))) context.unsupported_object_callback = unsupported_object_callback # context.style_folder, _ = os.path.split(output_file) try: qgis_symbol = SymbolConverter.Symbol_to_QgsSymbol(symbol, context) except NotImplementedException as e: unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e)))) continue except UnreadablePictureException as e: unreadable.append('<b>{}</b>: {}'.format(html.escape(name), html.escape(str(e)))) continue if isinstance(qgis_symbol, QgsSymbol): # self.check_for_missing_fonts(qgis_symbol, feedback) style.addSymbol(unique_name, qgis_symbol, True) elif isinstance(qgis_symbol, QgsColorRamp): style.addColorRamp(unique_name, qgis_symbol, True) if tags: if isinstance(qgis_symbol, QgsSymbol): assert style.tagSymbol(QgsStyle.SymbolEntity, unique_name, tags) elif isinstance(qgis_symbol, QgsColorRamp): assert style.tagSymbol(QgsStyle.ColorrampEntity, unique_name, tags) progress_dialog.deleteLater() if feedback.isCanceled(): return True if errors or unreadable or warnings: message = '' if unreadable: message = '<p>The following symbols could not be converted:</p>' message += '<ul>' for w in unreadable: message += '<li>{}</li>'.format(w.replace('\n', '<br>')) message += '</ul>' if errors: message += '<p>The following errors were generated while converting symbols:</p>' message += '<ul>' for w in errors: message += '<li>{}</li>'.format(w.replace('\n', '<br>')) message += '</ul>' if warnings: message += '<p>The following warnings were generated while converting symbols:</p>' message += '<ul>' for w in warnings: message += '<li>{}</li>'.format(w.replace('\n', '<br>')) message += '</ul>' show_warning('style could not be completely converted', 'Convert style', message, level=Qgis.Critical if (unreadable or errors) else Qgis.Warning) if Qgis.QGIS_VERSION_INT >= 30800: dlg = QgsStyleManagerDialog(style, readOnly=True) dlg.setFavoritesGroupVisible(False) dlg.setSmartGroupsVisible(False) fi = QFileInfo(input_file) dlg.setBaseStyleName(fi.baseName()) else: dlg = QgsStyleManagerDialog(style) dlg.exec_() return True
def db2Feat(self, parent, table, results, columns, geom=""): # Execute the export process # Create driver drv = ogr.GetDriverByName(self.getDriverName()) if drv is None: raise Exception("{0} driver not available.".format( self.getDriverName())) # Create data source self._ds = drv.CreateDataSource(self._targetFile) if self._ds is None: raise Exception("Creation of output file failed.") dest_crs = None # Create layer if geom != "": pgGeomType, srid = geometryType(table, geom) geomType = wkbTypes[pgGeomType] try: dest_crs = ogr.osr.SpatialReference() except AttributeError: dest_crs = osr.SpatialReference() dest_crs.ImportFromEPSG(srid) else: geomType = ogr.wkbNone layer_name = self.getLayerName() lyr = self._ds.CreateLayer(layer_name, dest_crs, geomType) if lyr is None: raise Exception("Layer creation failed") # Create fields for c in columns: field_defn = self.createField(table, c) if lyr.CreateField(field_defn) != 0: raise Exception("Creating %s field failed" % (c)) # Add Geometry column to list for referencing in the result set if geom != "": columns.append(geom) featGeom = None # Configure progress dialog initVal = 0 numFeat = results.rowcount progress = QProgressDialog("", "&Cancel", initVal, numFeat, parent) progress.setWindowModality(Qt.WindowModal) lblMsgTemp = QApplication.translate('OGRWriter', 'Writing {0} of {1} to file...') entity = current_profile().entity_by_name(table) # Iterate the result set for r in results: # Progress dialog progress.setValue(initVal) progressMsg = lblMsgTemp.format(str(initVal + 1), str(numFeat)) progress.setLabelText(progressMsg) if progress.wasCanceled(): break # Create OGR Feature feat = ogr.Feature(lyr.GetLayerDefn()) for i in range(len(columns)): colName = columns[i] # Check if its the geometry column in the iteration if colName == geom: if r[i] is not None: featGeom = ogr.CreateGeometryFromWkt(r[i]) else: featGeom = ogr.CreateGeometryFromWkt("") feat.SetGeometry(featGeom) else: field_value = r[i] feat.SetField(i, str(field_value)) if lyr.CreateFeature(feat) != 0: progress.close() progress.deleteLater() del progress raise Exception("Failed to create feature in %s" % (self._targetFile)) if featGeom is not None: featGeom.Destroy() feat.Destroy() initVal += 1 progress.setValue(numFeat) progress.deleteLater() del progress
def executeSearch(self): """ Base class override. Search for matching items for the specified entity and column. """ model_root_node = None prog_dialog = QProgressDialog(self) prog_dialog.setFixedWidth(380) prog_dialog.setWindowTitle( QApplication.translate("STRViewEntityWidget", "Searching for STR...")) prog_dialog.show() prog_dialog.setRange(0, 10) search_term = self._searchTerm() prog_dialog.setValue(2) # Try to get the corresponding search term value from the completer model if self._completer_model is not None: reg_exp = QRegExp("^%s$" % search_term, Qt.CaseInsensitive, QRegExp.RegExp2) self._proxy_completer_model.setFilterRegExp(reg_exp) if self._proxy_completer_model.rowCount() > 0: # Get corresponding actual value from the first matching item value_model_idx = self._proxy_completer_model.index(0, 1) source_model_idx = self._proxy_completer_model.mapToSource( value_model_idx) prog_dialog.setValue(4) search_term = self._completer_model.data( source_model_idx, Qt.DisplayRole) modelInstance = self.config.STRModel() modelQueryObj = modelInstance.queryObject() queryObjProperty = getattr(self.config.STRModel, self.currentFieldName()) entity_name = modelQueryObj._primary_entity._label_name entity = self.curr_profile.entity_by_name(entity_name) prog_dialog.setValue(6) # Get property type so that the filter can # be applied according to the appropriate type propType = queryObjProperty.property.columns[0].type results = [] try: if not isinstance(propType, String): col_name = self.currentFieldName() col = entity.columns[self.currentFieldName()] if col.TYPE_INFO == 'LOOKUP': lookup_entity = lookup_parent_entity( self.curr_profile, col_name) lkp_model = entity_model(lookup_entity) lkp_obj = lkp_model() value_obj = getattr(lkp_model, 'value') result = lkp_obj.queryObject().filter( func.lower(value_obj) == func.lower( search_term)).first() if result is None: result = lkp_obj.queryObject().filter( func.lower(value_obj).like(search_term + '%')).first() if result is not None: results = modelQueryObj.filter( queryObjProperty == result.id).all() else: results = [] else: results = modelQueryObj.filter( func.lower(queryObjProperty) == func.lower( search_term)).all() if self.validity.isEnabled(): valid_str_ids = self.str_validity_period_filter(results) else: valid_str_ids = None prog_dialog.setValue(7) except exc.StatementError: prog_dialog.deleteLater() del prog_dialog return model_root_node, [], search_term # if self.formatter is not None: # self.formatter.setData(results) # model_root_node = self.formatter.root(valid_str_ids) prog_dialog.setValue(10) prog_dialog.hide() prog_dialog.deleteLater() del prog_dialog return results, search_term
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) progress_dialog.deleteLater() del progress_dialog
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 self._source_doc_manager is not 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)) # Insert the record self._insertRow(targettable, column_value_mapping) init_val += 1 try: self._dbSession.commit() except (DataError, IntegrityError) as e: self._dbSession.rollback() progress.close() progress.deleteLater() del progress raise ImportFeatureException(str(e)) progress.setValue(numFeat) progress.deleteLater() del progress
def onGenerate(self): """ Slot raised to initiate the certificate generation process. """ self._notif_bar.clear() success_status = True config = self.current_config() self.last_data_source = config.data_source() if config is None: self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \ "The entity configuration could not be extracted.")) return # Get selected records and validate records = self.tabWidget.currentWidget().entities() if self.chk_template_datasource.isChecked(): records = self._dummy_template_records() if len(records) == 0: self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \ "Please load at least one entity record")) return if not self._docTemplatePath: self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \ "Please select a document template to use")) return documentNamingAttrs = self.lstDocNaming.selectedMappings() if self.chkUseOutputFolder.checkState() == Qt.Checked and len( documentNamingAttrs) == 0: self._notif_bar.insertErrorNotification(QApplication.translate("DocumentGeneratorDialog", \ "Please select at least one field for naming the output document")) return # Set output file properties if self.rbExpImage.isChecked(): outputMode = DocumentGenerator.Image fileExtension = self.cboImageType.currentText() saveAsText = "Image File" else: outputMode = DocumentGenerator.PDF fileExtension = "pdf" saveAsText = "PDF File" # Show save file dialog if not using output folder if self.chkUseOutputFolder.checkState() == Qt.Unchecked: docDir = source_document_location() if self._outputFilePath: fileInfo = QFileInfo(self._outputFilePath) docDir = fileInfo.dir().path() self._outputFilePath, _ = QFileDialog.getSaveFileName( self, QApplication.translate("DocumentGeneratorDialog", "Save Document"), docDir, "{0} (*.{1})".format( QApplication.translate("DocumentGeneratorDialog", saveAsText), fileExtension)) if not self._outputFilePath: self._notif_bar.insertErrorNotification( QApplication.translate( "DocumentGeneratorDialog", "Process aborted. No output file was specified.")) return # Include extension in file name self._outputFilePath = self._outputFilePath # + "." + fileExtension # else: # Multiple files to be generated. # pass self._doc_generator.set_link_field(config.link_field()) self._doc_generator.clear_attr_value_formatters() if not self.chk_template_datasource.isChecked(): # Apply cell formatters for naming output files self._doc_generator.set_attr_value_formatters(config.formatters()) entity_field_name = "id" # Iterate through the selected records progressDlg = QProgressDialog(self) progressDlg.setMaximum(len(records)) try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) for i, record in enumerate(records): progressDlg.setValue(i) if progressDlg.wasCanceled(): success_status = False break # User-defined location if self.chkUseOutputFolder.checkState() == Qt.Unchecked: status, msg = self._doc_generator.run( self._docTemplatePath, entity_field_name, record.id, outputMode, data_source=self.ds_entity.name, filePath=self._outputFilePath) self._doc_generator.clear_temporary_layers() # Output folder location using custom naming else: status, msg = self._doc_generator.run( self._docTemplatePath, entity_field_name, record.id, outputMode, dataFields=documentNamingAttrs, fileExtension=fileExtension, data_source=self.ds_entity.name) self._doc_generator.clear_temporary_layers() if not status: result = QMessageBox.warning( self, QApplication.translate("DocumentGeneratorDialog", "Document Generate Error"), msg, QMessageBox.Ignore | QMessageBox.Abort) if result == QMessageBox.Abort: progressDlg.close() progressDlg.deleteLater() del progressDlg success_status = False # Restore cursor QApplication.restoreOverrideCursor() return # If its the last record and user has selected to ignore if i + 1 == len(records): progressDlg.close() progressDlg.deleteLater() del progressDlg success_status = False # Restore cursor QApplication.restoreOverrideCursor() return else: progressDlg.setValue(len(records)) QApplication.restoreOverrideCursor() QMessageBox.information( self, QApplication.translate("DocumentGeneratorDialog", "Document Generation Complete"), QApplication.translate( "DocumentGeneratorDialog", "Document generation has successfully completed.")) except DummyException as ex: LOGGER.debug(str(ex)) err_msg = sys.exc_info()[1] QApplication.restoreOverrideCursor() QMessageBox.critical( self, "STDM", QApplication.translate( "DocumentGeneratorDialog", "Error Generating documents - %s" % (err_msg))) success_status = False progressDlg.deleteLater() del progressDlg # Reset UI self.reset(success_status)