def test_unicodeInQuery(self): os.environ['PGDATABASE'] = self.testdb obj = QObject() # needs to be kept alive database = PGDatabase(obj, QgsDataSourceUri()) self.assertIsInstance(database, PGDatabase) # SQL as string literal res = database.sqlResultModel("SELECT 'é'::text", obj) self.assertIsInstance(res, PGSqlResultModel) dat = res.getData(0, 0) self.assertEqual(dat, u"é") # SQL as unicode literal res = database.sqlResultModel(u"SELECT 'é'::text", obj) self.assertIsInstance(res, PGSqlResultModel) dat = res.getData(0, 0) self.assertEqual(dat, u"é")
def getConnectorFromUri(connectionParams: Dict[str, str]) -> 'DBConnector': """ Set connector property for the given database type and parameters """ connector = None uri = QgsDataSourceUri() if connectionParams['dbType'] == 'postgis': if connectionParams['host']: uri.setConnection(connectionParams['host'], connectionParams['port'], connectionParams['dbname'], connectionParams['user'], connectionParams['password']) if connectionParams['service']: uri.setConnection(connectionParams['service'], connectionParams['dbname'], connectionParams['user'], connectionParams['password']) if Qgis.QGIS_VERSION_INT >= 31200: # we need a fake DBPlugin object # with connectionName and providerName methods obj = QObject() obj.connectionName = lambda: 'fake' obj.providerName = lambda: 'postgres' connector = PostGisDBConnector(uri, obj) else: connector = PostGisDBConnector(uri) if connectionParams['dbType'] == 'spatialite': uri.setConnection('', '', connectionParams['dbname'], '', '') if hasSpatialiteSupport(): from db_manager.db_plugins.spatialite.connector import ( SpatiaLiteDBConnector, ) # Il y a bug évident ici si il n'y pas le support spatialite, quid de SpatiaLiteDBConnector ? try: connector = SpatiaLiteDBConnector(uri) except ConnectionError as e: QgsMessageLog.logMessage( "Erreur lors de la récupération du fichier SQLite : {}".format( str(e)), 'cadastre', Qgis.Critical) return connector
def getConnectorFromUri(connectionParams: Dict[str, str]) -> 'DBConnector': """ Set connector property for the given database type and parameters """ connector = None uri = QgsDataSourceUri() if connectionParams['dbType'] == 'postgis': if connectionParams['host']: uri.setConnection( connectionParams['host'], connectionParams['port'], connectionParams['dbname'], connectionParams['user'], connectionParams['password'] ) if connectionParams['service']: uri.setConnection( connectionParams['service'], connectionParams['dbname'], connectionParams['user'], connectionParams['password'] ) if Qgis.QGIS_VERSION_INT >= 31200: # we need a fake DBPlugin object # with connectionName and providerName methods obj = QObject() obj.connectionName = lambda: 'fake' obj.providerName = lambda: 'postgres' connector = PostGisDBConnector(uri, obj) else: connector = PostGisDBConnector(uri) if connectionParams['dbType'] == 'spatialite': uri.setConnection('', '', connectionParams['dbname'], '', '') if hasSpatialiteSupport(): from db_manager.db_plugins.spatialite.connector import SpatiaLiteDBConnector connector = SpatiaLiteDBConnector(uri) return connector
def test_dbnameLessURI(self): obj = QObject() # needs to be kept alive obj.connectionName = lambda: 'fake' obj.providerName = lambda: 'postgres' c = PostGisDBConnector(QgsDataSourceUri(), obj) self.assertIsInstance(c, PostGisDBConnector) uri = c.uri() # No username was passed, so we expect it to be taken # from PGUSER or USER environment variables expected_user = os.environ.get('PGUSER') or os.environ.get('USER') actual_user = self._getUser(c) self.assertEqual(actual_user, expected_user) # No database was passed, so we expect it to be taken # from PGDATABASE or expected user expected_db = os.environ.get('PGDATABASE') or expected_user actual_db = self._getDatabase(c) self.assertEqual(actual_db, expected_db)
# You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os from datetime import datetime import ftplib from qgis.core import QgsProject from qgis.PyQt.QtCore import QObject from qgis.PyQt.QtWidgets import QFileDialog, QInputDialog, QDialog, QLineEdit from .utils import tempFolder from .feedbackDialog import Feedback from .ui_ftp_configuration import Ui_FtpConfiguration translator = QObject() class Exporter(QObject): """ Generic base class for web map exporters """ def __init__(self): super(QObject, self).__init__() @classmethod def type(cls): """ :return: Unique string for exporter type
def applyClicked(self): self.applyProperties(QObject().sender())
class ProjectChecker: tr = QObject().tr class CheckConfig(TypedDict): type: Feedback.Level fn: Callable scope: Optional[ExportType] def __init__(self, project: QgsProject) -> None: self.project = project self.project_checks: List[ProjectChecker.CheckConfig] = [ { "type": Feedback.Level.ERROR, "fn": self.check_no_absolute_filepaths, "scope": None, }, { "type": Feedback.Level.ERROR, "fn": self.check_no_homepath, "scope": None, }, { "type": Feedback.Level.ERROR, "fn": self.check_files_have_unsupported_characters, "scope": None, }, { "type": Feedback.Level.WARNING, "fn": self.check_project_is_dirty, "scope": None, }, ] self.layer_checks: List[ProjectChecker.CheckConfig] = [ { "type": Feedback.Level.WARNING, "fn": self.check_layer_has_utf8_datasources, "scope": None, }, { "type": Feedback.Level.WARNING, "fn": self.check_layer_has_ascii_filename, "scope": None, }, { "type": Feedback.Level.ERROR, "fn": self.check_layer_primary_key, "scope": ExportType.Cloud, }, { "type": Feedback.Level.WARNING, "fn": self.check_layer_memory, "scope": None, }, { "type": Feedback.Level.WARNING, "fn": self.check_layer_configured, "scope": None, }, ] def check(self, scope: ExportType = None) -> ProjectCheckerFeedback: checked_feedback = ProjectCheckerFeedback() for check in self.project_checks: if check["scope"] != scope: continue feedback_result = check["fn"]() if feedback_result: checked_feedback.add(Feedback(check["type"], feedback_result)) for layer in self.project.mapLayers().values(): layer_source = LayerSource(layer) if (layer_source.action == SyncAction.REMOVE and layer_source.cloud_action == SyncAction.REMOVE): continue for check in self.layer_checks: if (check["scope"] == ExportType.Cable and layer_source.action == SyncAction.REMOVE ) or (check["scope"] == ExportType.Cloud and layer_source.cloud_action == SyncAction.REMOVE): break if check["scope"] != scope: continue feedback_result = check["fn"](layer_source) if feedback_result: checked_feedback.add( Feedback(check["type"], feedback_result, layer)) return checked_feedback def check_no_absolute_filepaths(self) -> Optional[FeedbackResult]: if Qgis.QGIS_VERSION_INT >= 32200: is_absolute = self.project.filePathStorage( ) == Qgis.FilePathType.Absolute else: is_absolute = (QgsSettings().value( "/qgis/defaultProjectPathsRelative") == "false") if is_absolute: return FeedbackResult( self. tr("QField does not support projects configured to use absolute paths. " 'Please change this configuration in "File -> Project settings" first.' )) def check_no_homepath(self) -> Optional[FeedbackResult]: if self.project.presetHomePath(): return FeedbackResult( self. tr("QField does not support projects with configured home path. " 'Please change this configuration in "File -> Project settings" first.' )) def check_basemap_configuration(self) -> Optional[FeedbackResult]: project_configuration = ProjectConfiguration(self.project) if not project_configuration.create_base_map: return base_map_type = project_configuration.base_map_type if base_map_type == ProjectProperties.BaseMapType.SINGLE_LAYER: basemap_layer = self.project.mapLayer( project_configuration.base_map_layer) if not project_configuration.base_map_layer.strip(): return FeedbackResult( self. tr("No basemap layer selected. " 'Please change this configuration in "File -> Project settings -> QField" first.' )) if not basemap_layer: return FeedbackResult( self. tr('Cannot find the configured base layer with id "{}". ' 'Please change this configuration in "File -> Project settings -> QField" first.' ).format(project_configuration.base_map_layer), ) elif base_map_type == ProjectProperties.BaseMapType.MAP_THEME: if not self.project.mapThemeCollection().hasMapTheme( project_configuration.base_map_theme): return FeedbackResult( self. tr('Cannot find the configured base theme with name "{}".' 'Please change this configuration in "File -> Project settings -> QField" first.' ).format(project_configuration.base_map_theme), ) def check_files_have_unsupported_characters( self) -> Optional[FeedbackResult]: problematic_paths = [] regexp = re.compile(r'[<>:"\\|?*]') home_path = Path(self.project.fileName()).parent for path in home_path.rglob("*"): relative_path = path.relative_to(home_path) if str(relative_path).startswith(".qfieldsync"): continue if regexp.search(str(relative_path.as_posix())) is not None: problematic_paths.append(relative_path) if problematic_paths: return FeedbackResult( self. tr('Forbidden characters in filesystem path(s) "{}". ' 'Please make sure there are no files and directories with "<", ">", ":", "/", "\\", "|", "?", "*" or double quotes (") characters in their path.' ).format(", ".join( [f'"{path}"' for path in problematic_paths]))) def check_project_is_dirty(self) -> Optional[FeedbackResult]: if self.project.isDirty(): return FeedbackResult( self.tr( "QGIS project has unsaved changes. " "Unsaved changes will not be uploaded to QFieldCloud.")) def check_layer_has_utf8_datasources( self, layer_source: LayerSource) -> Optional[FeedbackResult]: layer = layer_source.layer if (layer.type() == QgsMapLayer.VectorLayer and layer.dataProvider() and layer.dataProvider().encoding() != "UTF-8" # some providers return empty string as encoding, just ignore them and layer.dataProvider().encoding() != ""): return FeedbackResult( self. tr('Layer does not use UTF-8, but "{}" encoding.' "Working with layers that do not use UTF-8 encoding might cause problems." "It is highly recommended to convert them to UTF-8 encoded layers." ).format(layer.dataProvider().encoding()), ) def check_layer_has_ascii_filename( self, layer_source: LayerSource) -> Optional[FeedbackResult]: if layer_source.is_file and not isascii(layer_source.filename): return FeedbackResult( self. tr("Non ASCII character detected in the layer filename." "Working with file paths that are not in ASCII might cause problems." "It is highly recommended to rename them to ASCII encoded paths." ).format(layer_source.layer.dataProvider().encoding()), ) def check_layer_primary_key( self, layer_source: LayerSource) -> Optional[FeedbackResult]: layer = layer_source.layer if layer.type() != QgsMapLayer.VectorLayer: return layer_source = LayerSource(layer) # when the layer is configured as "no_action" and it is an "online" layer, then QFieldCloud is not responsible for the PKs, # therefore we should accept them if (layer_source.cloud_action == SyncAction.NO_ACTION and not layer_source.is_file): return pkeys_count = len(layer.primaryKeyAttributes()) if pkeys_count == 0: suggestion = ( 'Please change the layer action to "Remove" in "Layer Properties -> QField".' if layer_source.is_file else 'Please change the layer action to either "Remove" or "Directly access data source" in "Layer Properties -> QField".' ) return FeedbackResult( self. tr("Missing primary key. " "QFieldCloud supports only layers with a defined single-column primary key. " "{}").format(suggestion), ) elif pkeys_count > 1: return FeedbackResult( self.tr("Composite primary keys are not supported.")) def check_layer_memory( self, layer_source: LayerSource) -> Optional[FeedbackResult]: layer = layer_source.layer if layer.isValid() and layer.dataProvider().name() == "memory": return FeedbackResult( self. tr("Memory layer features are only available during this QGIS session. " "The layer will be empty on QField."), ) def check_layer_configured( self, layer_source: LayerSource) -> Optional[FeedbackResult]: if not layer_source.is_configured and not layer_source.is_cloud_configured: return FeedbackResult( self. tr("The layer is not configured with neither cable, nor cloud action yet. " "Default action will be selected only for this time. " 'Please select and save appropriate layer action in "Layer Properties -> QField". ' ), )