Esempio n. 1
0
 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"é")
Esempio n. 2
0
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
Esempio n. 4
0
    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)
Esempio n. 5
0
# 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())
Esempio n. 7
0
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". '
                   ), )