Ejemplo n.º 1
0
    def test_zip_unzip(self):
        tmpDir = QTemporaryDir()
        tmpFile = "{}/project.qgz".format(tmpDir.path())

        project = QgsProject()

        l0 = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "points.shp"), "points", "ogr")
        l1 = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "lines.shp"), "lines", "ogr")
        project.addMapLayers([l0, l1])

        self.assertTrue(project.write(tmpFile))

        project2 = QgsProject()
        self.assertFalse(project2.isZipped())
        self.assertTrue(project2.fileName() == "")
        self.assertTrue(project2.read(tmpFile))
        self.assertTrue(project2.isZipped())
        self.assertTrue(project2.fileName() == tmpFile)
        layers = project2.mapLayers()

        self.assertEqual(len(layers.keys()), 2)
        self.assertTrue(layers[l0.id()].isValid(), True)
        self.assertTrue(layers[l1.id()].isValid(), True)

        project2.clear()
        self.assertFalse(project2.isZipped())
Ejemplo n.º 2
0
    def test_zip_unzip(self):
        tmpDir = QTemporaryDir()
        tmpFile = "{}/project.qgz".format(tmpDir.path())

        project = QgsProject()

        l0 = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "points.shp"), "points", "ogr")
        l1 = QgsVectorLayer(os.path.join(TEST_DATA_DIR, "lines.shp"), "lines", "ogr")
        project.addMapLayers([l0, l1])

        self.assertTrue(project.write(tmpFile))

        project2 = QgsProject()
        self.assertFalse(project2.isZipped())
        self.assertTrue(project2.fileName() == "")
        self.assertTrue(project2.read(tmpFile))
        self.assertTrue(project2.isZipped())
        self.assertTrue(project2.fileName() == tmpFile)
        layers = project2.mapLayers()

        self.assertEqual(len(layers.keys()), 2)
        self.assertTrue(layers[l0.id()].isValid(), True)
        self.assertTrue(layers[l1.id()].isValid(), True)

        project2.clear()
        self.assertFalse(project2.isZipped())
Ejemplo n.º 3
0
def test_wmts_document_cache(client):
    """  Test getcapabilites response
    """
    plugin = client.getplugin('wmtsCacheServer')
    assert plugin is not None

    # Create a filter
    cachefilter = plugin.create_filter()

    # Delete document
    project = QgsProject()
    project.setFileName(client.getprojectpath("france_parts.qgs").strpath)

    # Get project document root path
    docroot = cachefilter._cache.get_documents_root(project.fileName())

    cachefilter.deleteCachedDocuments(project)

    assert not os.path.exists(docroot.as_posix())

    parameters = {
        'MAP': project.fileName(),
        'REQUEST': 'GetCapabilities',
        'SERVICE': 'WMTS'
    }

    # Get the cached path from the request parameters
    docpath = cachefilter._cache.get_document_cache(
        project.fileName(), parameters, '.xml'
    ).as_posix()

    assert not os.path.exists(docpath)

    # Make a request
    qs = "?" + "&".join("%s=%s" % item for item in parameters.items())
    rv = client.get(qs, project.fileName())
    assert rv.status_code == 200

    # Test that document cache has been created
    assert os.path.exists(docpath)

    original_content = rv.content

    # Make a second request and check for header
    rv = client.get(qs, project.fileName())
    assert rv.status_code == 200
    assert rv.headers.get('X-Qgis-Debug-Cache-Plugin') == 'wmtsCacheServer'
    assert rv.headers.get('X-Qgis-Debug-Cache-Path') == docpath

    cached_content = rv.content

    assert original_content == cached_content
Ejemplo n.º 4
0
 def _to_json(key: str, project: QgsProject):
     return dict(
         key=key,
         filename=project.fileName(),
         last_modified=project.lastModified().toString(Qt.ISODate),
         num_layers=project.count(),
     )
Ejemplo n.º 5
0
def make_temp_qgis_file(project: QgsProject) -> str:
    project_backup_dir = tempfile.mkdtemp()
    original_filename = project.fileName()
    backup_filename = os.path.join(project_backup_dir,
                                   f"{project.baseName()}.qgs")
    project.write(backup_filename)
    project.setFileName(original_filename)

    return backup_filename
Ejemplo n.º 6
0
 def get(self, query: str, project: str=None) -> _Response:
     """ Return server response from query
     """
     request  = QgsBufferServerRequest(query, QgsServerRequest.GetMethod, {}, None)
     response = QgsBufferServerResponse()
     if project is not None and not os.path.isabs(project):
         projectpath = self.datapath.join(project)
         qgsproject  = QgsProject()
         if not qgsproject.read(projectpath.strpath):
             raise ValueError("Error reading project '%s':" % projectpath.strpath)
     else:
         qgsproject = None
     # See https://github.com/qgis/QGIS/pull/9773
     self.server.serverInterface().setConfigFilePath(qgsproject.fileName())
     self.server.handleRequest(request, response, project=qgsproject)
     return _Response(response,request)
Ejemplo n.º 7
0
def transactional_project(iface,
                          src_url=None,
                          dest_url=None,
                          dont_resolve_layers=True):
    """Context manager returning a ``QgsProject`` instance and saves it on exit
    if no error occured.

    If ``src_url`` is ``None``, the returned project is the current one (the
    one loaded int QGIS interface). Else, the project found at ``src_url`` is
    returned.

    The project is saved to its original location if ``dest_url`` is ``None``,
    else it is saved to ``dest_url``.

    Implementation detail: after saving the project with ``proj.write()`` (thus
    updating the project file on disk), when the user clicks on the Save icon
    in QGIS UI, a warning is shown indicating that the file has been modified
    after it has been opened by QGIS. Workaround: the project is reloaded with
    ``proj.clear()`` and ``proj.read()``.
    """
    try:
        if src_url:
            proj = QgsProject()
            if dont_resolve_layers:
                proj.read(src_url, QgsProject.FlagDontResolveLayers)
            else:
                proj.read(src_url)
        else:
            proj = QgsProject.instance()
        yield proj
    except Exception as exc:
        log_exception(exc, level='Critical', feedback=True, iface=iface)
    finally:
        if not dest_url:
            project_saved = proj.write()
            dest_url = proj.fileName()
        else:
            project_saved = proj.write(dest_url)
        if not project_saved:
            log_message('Project has not been saved after transaction.',
                        level='Warning',
                        feedback=True,
                        iface=iface)
        # XXX: better way to avoid warning if the user click save ?
        proj.clear()
        proj.read(dest_url)
Ejemplo n.º 8
0
    def test_configpath(self):
        """ Test plugin can read confif path
        """
        try:
            from qgis.server import QgsServerFilter
            from qgis.core import QgsProject
        except ImportError:
            print("QGIS Server plugins are not compiled. Skipping test")
            return

        d = unitTestDataPath('qgis_server_accesscontrol') + '/'
        self.projectPath = os.path.join(d, "project.qgs")
        self.server = QgsServer()

        # global to be modified inside plugin filters
        globals()['configFilePath2'] = None

        class Filter0(QgsServerFilter):
            """Body setter, clear body, keep headers"""

            def requestReady(self):
                global configFilePath2
                configFilePath2 = self.serverInterface().configFilePath()

        serverIface = self.server.serverInterface()
        serverIface.registerFilter(Filter0(serverIface), 100)

        # Test using MAP
        self._execute_request('?service=simple&MAP=%s' % self.projectPath)

        # Check config file path
        self.assertEqual(configFilePath2, self.projectPath)

        # Reset result
        globals()['configFilePath2'] = None

        # Test with prqject as argument
        project = QgsProject()
        project.read(self.projectPath)

        self._execute_request_project('?service=simple', project=project)

        # Check config file path
        self.assertEqual(configFilePath2, project.fileName())
Ejemplo n.º 9
0
    def test_configpath(self):
        """ Test plugin can read confif path
        """
        try:
            from qgis.server import QgsServerFilter
            from qgis.core import QgsProject
        except ImportError:
            print("QGIS Server plugins are not compiled. Skipping test")
            return

        d = unitTestDataPath('qgis_server_accesscontrol') + '/'
        self.projectPath = os.path.join(d, "project.qgs")
        self.server = QgsServer()

        # global to be modified inside plugin filters
        globals()['configFilePath2'] = None

        class Filter0(QgsServerFilter):
            """Body setter, clear body, keep headers"""

            def requestReady(self):
                global configFilePath2
                configFilePath2 = self.serverInterface().configFilePath()

        serverIface = self.server.serverInterface()
        serverIface.registerFilter(Filter0(serverIface), 100)

        # Test using MAP
        self._execute_request('?service=simple&MAP=%s' % self.projectPath)

        # Check config file path
        self.assertEqual(configFilePath2, self.projectPath)

        # Reset result
        globals()['configFilePath2'] = None

        # Test with prqject as argument
        project = QgsProject()
        project.read(self.projectPath)

        self._execute_request_project('?service=simple', project=project)

        # Check config file path
        self.assertEqual(configFilePath2, project.fileName())
Ejemplo n.º 10
0
def get_project_summary(key: str, project: QgsProject):
    """ Return json summary for cached project
    """
    def layer_summary(layer_id: str, layer: QgsMapLayer):
        return dict(
            id=layer_id,
            name=layer.name(),
            source=layer.publicSource(),
            crs=layer.crs().userFriendlyIdentifier(),
            valid=layer.isValid(),
            spatial=layer.isSpatial(),
        )

    layers = [
        layer_summary(idstr, l) for (idstr, l) in project.mapLayers().items()
    ]

    return dict(cache_key=key,
                filename=project.fileName(),
                bad_layers_count=sum(1 for ls in layers if not ls['valid']),
                layers=layers,
                crs=project.crs().userFriendlyIdentifier(),
                last_modified=project.lastModified().toString(Qt.ISODate))
def test_wmts_cachemngrapi_delete_docs(client):
    """ Test the API with to remove docs cache
        /wmtscache/collections
        /wmtscache/collection/(?<collectionId>[^/]+?)/docs
    """
    plugin = client.getplugin('wmtsCacheServer')
    assert plugin is not None

    # Create a filter
    cachefilter = plugin.create_filter()

    # Delete document
    project = QgsProject()
    project.setFileName(client.getprojectpath("france_parts.qgs").strpath)

    # Get project document root path
    docroot = cachefilter._cache.get_documents_root(project.fileName())

    cachefilter.deleteCachedDocuments(project)

    assert not os.path.exists(docroot.as_posix())

    parameters = {
        'MAP': project.fileName(),
        'REQUEST': 'GetCapabilities',
        'SERVICE': 'WMTS'
    }

    # Get the cached path from the request parameters
    docpath = cachefilter._cache.get_document_cache(project.fileName(),
                                                    parameters,
                                                    '.xml').as_posix()

    assert not os.path.exists(docpath)

    # Make a request
    qs = "?" + "&".join("%s=%s" % item for item in parameters.items())
    rv = client.get(qs, project.fileName())
    assert rv.status_code == 200

    # Test that document cache has been created
    assert os.path.exists(docpath)

    # Cache manager API requests
    qs = "/wmtscache/collections"
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'collections' in json_content
    assert len(json_content['collections']) == 1

    collection = json_content['collections'][0]
    assert 'id' in collection
    assert 'links' in collection
    assert 'project' in collection
    assert collection['project'] == client.getprojectpath(
        "france_parts.qgs").strpath

    # Get docs info
    qs = "/wmtscache/collections/{}/docs".format(collection['id'])
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'id' in json_content
    assert json_content['id'] == collection['id']

    assert 'project' in json_content
    assert json_content['project'] == client.getprojectpath(
        "france_parts.qgs").strpath

    assert 'documents' in json_content
    assert json_content['documents'] == 1

    # Delete docs
    qs = "/wmtscache/collections/{}/docs".format(collection['id'])
    rv = client.delete(qs)
    assert rv.status_code == 200

    # Get docs info
    qs = "/wmtscache/collections/{}/docs".format(collection['id'])
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'id' in json_content
    assert json_content['id'] == collection['id']

    assert 'project' in json_content
    assert json_content['project'] == client.getprojectpath(
        "france_parts.qgs").strpath

    assert 'documents' in json_content
    assert json_content['documents'] == 0

    # Test that document cache has been deleted
    assert not os.path.exists(docpath)
Ejemplo n.º 12
0
    def _convert(self, project: QgsProject) -> None:
        project.baseName()
        xml_elements_to_preserve = {}
        on_original_project_read = self._on_original_project_read_wrapper(
            xml_elements_to_preserve)
        project.readProject.connect(on_original_project_read)

        if self.export_type == ExportType.Cable:
            # the `backup_filename` is copied right after packaging is requested. It has all the unsaved
            # project settings, which means they will be available in the packaged project too.
            project.read(self.backup_filename)
        elif self.export_type == ExportType.Cloud:
            # if you save the project without QGIS GUI, the project no longer has `theMapCanvas` canvas
            # so we should use the original project file that already has `theMapCanvas`. There is no
            # gain using the `backup_filename`, since there is no user to modify the project.
            project.read(project.fileName())
        else:
            raise NotImplementedError(
                f"Unknown package type: {self.export_type}")

        project.readProject.disconnect(on_original_project_read)

        self.export_folder.mkdir(parents=True, exist_ok=True)
        self.total_progress_updated.emit(0, 100,
                                         self.trUtf8("Converting project…"))
        self.__layers = list(project.mapLayers().values())

        # We store the pks of the original vector layers
        for layer in self.__layers:
            pk_names = None
            if layer.type() == QgsMapLayer.VectorLayer:
                pk_names = []
                for idx in layer.primaryKeyAttributes():
                    pk_name = layer.fields()[idx].name()
                    # and we check that the primary key fields names don't have a comma in the name
                    if "," in pk_name:
                        raise ValueError("Comma in field names not allowed")
                    pk_names.append(pk_name)
                    layer.setCustomProperty("QFieldSync/sourceDataPrimaryKeys",
                                            ",".join(pk_names))

            layer_data: LayerData = {
                "id": layer.id(),
                "name": layer.name(),
                "type": layer.type(),
                "source": layer.source(),
                "fields": layer.fields() if hasattr(layer, "fields") else None,
                "pk_names": pk_names,
            }

            self.__layer_data_by_id[layer.id()] = layer_data
            self.__layer_data_by_name[layer.name()] = layer_data

        if self.create_basemap and self.project_configuration.create_base_map:
            self._export_basemap()

        # Loop through all layers and copy/remove/offline them
        copied_files = list()
        non_ascii_filename_layers: Dict[str, str] = {}
        non_utf8_encoding_layers: Dict[str, str] = {}
        for layer_idx, layer in enumerate(self.__layers):
            self.total_progress_updated.emit(
                layer_idx - len(self.__offline_layers),
                len(self.__layers),
                self.trUtf8("Copying layers…"),
            )

            layer_source = LayerSource(layer)
            layer_action = (layer_source.action if self.export_type
                            == ExportType.Cable else layer_source.cloud_action)

            if not layer.isValid():
                project.removeMapLayer(layer)
                continue

            if not layer_source.is_supported:
                project.removeMapLayer(layer)
                continue

            if layer_source.is_file and not isascii(layer_source.filename):
                non_ascii_filename_layers[layer.name()] = layer_source.filename

            if layer_source.is_localized_path:
                continue

            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() != ""):
                non_utf8_encoding_layers[
                    layer.name()] = layer.dataProvider().encoding()

            if layer_action == SyncAction.OFFLINE:
                if self.project_configuration.offline_copy_only_aoi:
                    extent = QgsCoordinateTransform(
                        QgsCoordinateReferenceSystem(
                            self.area_of_interest_crs),
                        layer.crs(),
                        QgsProject.instance(),
                    ).transformBoundingBox(self.area_of_interest.boundingBox())
                    layer.selectByRect(extent)

                if not layer.selectedFeatureCount():
                    layer.selectByIds([FID_NULL])

                self.__offline_layers.append(layer)
                self.__offline_layer_names.append(layer.name())
            elif (layer_action == SyncAction.COPY
                  or layer_action == SyncAction.NO_ACTION):
                copied_files = layer_source.copy(self.export_folder,
                                                 copied_files)
            elif layer_action == SyncAction.KEEP_EXISTENT:
                layer_source.copy(self.export_folder, copied_files, True)
            elif layer_action == SyncAction.REMOVE:
                project.removeMapLayer(layer)

        if non_ascii_filename_layers:
            layers = ", ".join([
                f'"{name}" at "{path}"'
                for name, path in non_ascii_filename_layers.items()
            ])
            message = self.tr(
                "Some layers are stored at file paths that are not ASCII encoded: {}. Working with paths that are not in ASCII might cause problems. It is highly recommended to rename them to ASCII encoded paths."
            ).format(layers)
            self.warning.emit(self.tr("QFieldSync"), message)

        if non_utf8_encoding_layers:
            layers = ", ".join([
                f"{name} ({encoding})"
                for name, encoding in non_utf8_encoding_layers.items()
            ])
            message = self.tr(
                "Some layers do not use UTF-8 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(layers)
            self.warning.emit(self.tr("QFieldSync"), message)

        export_project_filename = self.export_folder.joinpath(
            f"{self.original_filename.stem}_qfield.qgs")

        # save the original project path
        self.project_configuration.original_project_path = str(
            self.original_filename)

        # save the offline project twice so that the offline plugin can "know" that it's a relative path
        QgsProject.instance().write(str(export_project_filename))

        # export the DCIM folder
        copy_images(
            str(self.original_filename.parent.joinpath("DCIM")),
            str(export_project_filename.parent.joinpath("DCIM")),
        )
        try:
            # Run the offline plugin for gpkg
            gpkg_filename = "data.gpkg"
            if self.__offline_layers:
                offline_layer_ids = [o_l.id() for o_l in self.__offline_layers]

                if not self.offline_editing.convertToOfflineProject(
                        str(self.export_folder),
                        gpkg_filename,
                        offline_layer_ids,
                        self.project_configuration.offline_copy_only_aoi,
                        self.offline_editing.GPKG,
                        None,
                ):
                    raise Exception(
                        self.
                        tr("QGIS Offline editing error: failed to convert layers to offline layers"
                           ))
        except AttributeError:
            # Run the offline plugin for spatialite
            spatialite_filename = "data.sqlite"
            if self.__offline_layers:
                offline_layer_ids = [o_l.id() for o_l in self.__offline_layers]

                if not self.offline_editing.convertToOfflineProject(
                        str(self.export_folder),
                        spatialite_filename,
                        offline_layer_ids,
                        self.project_configuration.offline_copy_only_aoi,
                        self.offline_editing.SpatiaLite,
                        None,
                ):
                    raise Exception(
                        self.
                        tr("QGIS Offline editing error: failed to convert layers to offline layers"
                           ))

        # Disable project options that could create problems on a portable
        # project with offline layers
        self.post_process_layers()

        # Now we have a project state which can be saved as offline project
        on_original_project_write = self._on_original_project_write_wrapper(
            xml_elements_to_preserve)
        project.writeProject.connect(on_original_project_write)
        QgsProject.instance().write(str(export_project_filename))
        project.writeProject.disconnect(on_original_project_write)
def test_wmts_cachemngrapi_layerid_error(client):
    """ Test the API with empty cache
        /wmtscache/collection/(?<collectionId>[^/]+?)
        /wmtscache/collection/(?<collectionId>[^/]+?)/layers
        /wmtscache/collection/(?<collectionId>[^/]+?)/layers/(?<layerId>[^/]+?)
    """

    plugin = client.getplugin('wmtsCacheServer')
    assert plugin is not None

    # Create a filter
    cachefilter = plugin.create_filter()

    # Delete tiles
    project = QgsProject()
    project.setFileName(client.getprojectpath("france_parts.qgs").strpath)

    # Get project tiles root path
    tileroot = cachefilter._cache.get_tiles_root(project.fileName())

    cachefilter.deleteCachedImages(project)

    assert not os.path.exists(tileroot.as_posix())

    parameters = {
        "MAP": project.fileName(),
        "SERVICE": "WMTS",
        "VERSION": "1.0.0",
        "REQUEST": "GetTile",
        "LAYER": "france_parts",
        "STYLE": "",
        "TILEMATRIXSET": "EPSG:4326",
        "TILEMATRIX": "0",
        "TILEROW": "0",
        "TILECOL": "0",
        "FORMAT": "image/png"
    }

    # Get the cached path from the request parameters
    tilepath = cachefilter._cache.get_tile_cache(project.fileName(),
                                                 parameters).as_posix()

    assert not os.path.exists(tilepath)

    # Make a request
    qs = "?" + "&".join("%s=%s" % item for item in parameters.items())
    rv = client.get(qs, project.fileName())

    # original_content = rv.content

    if rv.status_code != 200:
        LOGGER.error(lxml.etree.tostring(rv.xml, pretty_print=True))

    assert rv.status_code == 200

    # Test that tile cache has been created
    assert os.path.exists(tilepath)

    # Cache manager API requests
    qs = "/wmtscache/collections"
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'collections' in json_content
    assert len(json_content['collections']) == 1

    collection = json_content['collections'][0]
    assert 'id' in collection
    assert 'links' in collection
    assert 'project' in collection
    assert collection['project'] == client.getprojectpath(
        "france_parts.qgs").strpath

    # Get layers info
    qs = "/wmtscache/collections/{}/layers".format(collection['id'])
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'id' in json_content
    assert json_content['id'] == collection['id']

    assert 'layers' in json_content
    assert len(json_content['layers']) == 1

    qs = "/wmtscache/collections/{}/layers/{}".format(collection['id'],
                                                      'foobar')
    rv = client.delete(qs)
    assert rv.status_code == 404

    json_content = json.loads(rv.content)
    assert 'error' in json_content
    assert json_content['error'].get('message') == "Layer 'foobar' not found"
def test_wmts_cachemngrapi_cache_info(client):
    """ Test the API with cache
        /wmtscache
        /wmtscache/collections
        /wmtscache/collection/(?<collectionId>[^/]+?)
        /wmtscache/collection/(?<collectionId>[^/]+?)/docs
        /wmtscache/collection/(?<collectionId>[^/]+?)/layers
        /wmtscache/collection/(?<collectionId>[^/]+?)/layers/(?<layerId>[^/]+?)
    """
    plugin = client.getplugin('wmtsCacheServer')
    assert plugin is not None

    # Create a filter
    cachefilter = plugin.create_filter()

    # Delete document
    project = QgsProject()
    project.setFileName(client.getprojectpath("france_parts.qgs").strpath)

    # Get project document root path
    docroot = cachefilter._cache.get_documents_root(project.fileName())

    cachefilter.deleteCachedDocuments(project)

    assert not os.path.exists(docroot.as_posix())

    parameters = {
        'MAP': project.fileName(),
        'REQUEST': 'GetCapabilities',
        'SERVICE': 'WMTS'
    }

    # Get the cached path from the request parameters
    docpath = cachefilter._cache.get_document_cache(project.fileName(),
                                                    parameters,
                                                    '.xml').as_posix()

    assert not os.path.exists(docpath)

    # Make a request
    qs = "?" + "&".join("%s=%s" % item for item in parameters.items())
    rv = client.get(qs, project.fileName())
    assert rv.status_code == 200

    # Test that document cache has been created
    assert os.path.exists(docpath)

    # Get project tiles root path
    tileroot = cachefilter._cache.get_tiles_root(project.fileName())

    cachefilter.deleteCachedImages(project)

    assert not os.path.exists(tileroot.as_posix())

    parameters = {
        "MAP": project.fileName(),
        "SERVICE": "WMTS",
        "VERSION": "1.0.0",
        "REQUEST": "GetTile",
        "LAYER": "france_parts",
        "STYLE": "",
        "TILEMATRIXSET": "EPSG:4326",
        "TILEMATRIX": "0",
        "TILEROW": "0",
        "TILECOL": "0",
        "FORMAT": "image/png"
    }

    # Get the cached path from the request parameters
    tilepath = cachefilter._cache.get_tile_cache(project.fileName(),
                                                 parameters).as_posix()

    assert not os.path.exists(tilepath)

    # Make a request
    qs = "?" + "&".join("%s=%s" % item for item in parameters.items())
    rv = client.get(qs, project.fileName())

    # original_content = rv.content

    if rv.status_code != 200:
        LOGGER.error(lxml.etree.tostring(rv.xml, pretty_print=True))

    assert rv.status_code == 200

    # Test that document cache has been created
    assert os.path.exists(tilepath)

    # Cache manager API requests
    qs = "/wmtscache/collections"
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'cache_layout' in json_content
    assert json_content['cache_layout'] == 'tc'

    assert 'collections' in json_content
    assert len(json_content['collections']) == 1

    assert 'links' in json_content
    assert len(json_content['links']) == 0

    collection = json_content['collections'][0]
    assert 'id' in collection
    assert 'links' in collection
    assert 'project' in collection
    assert collection['project'] == client.getprojectpath(
        "france_parts.qgs").strpath

    qs = "/wmtscache/collections/{}".format(collection['id'])
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'id' in json_content
    assert json_content['id'] == collection['id']

    assert 'project' in json_content
    assert json_content['project'] == client.getprojectpath(
        "france_parts.qgs").strpath

    assert 'links' in json_content
    assert len(json_content['links']) == 2

    qs = "/wmtscache/collections/{}/docs".format(collection['id'])
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'id' in json_content
    assert json_content['id'] == collection['id']

    assert 'project' in json_content
    assert json_content['project'] == client.getprojectpath(
        "france_parts.qgs").strpath

    assert 'documents' in json_content
    assert json_content['documents'] == 1

    assert 'links' in json_content
    assert len(json_content['links']) == 0

    qs = "/wmtscache/collections/{}/layers".format(collection['id'])
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'id' in json_content
    assert json_content['id'] == collection['id']

    assert 'project' in json_content
    assert json_content['project'] == client.getprojectpath(
        "france_parts.qgs").strpath

    assert 'layers' in json_content
    assert len(json_content['layers']) == 1

    assert 'links' in json_content
    assert len(json_content['links']) == 0

    layer = json_content['layers'][0]
    assert 'id' in layer
    assert 'links' in layer

    qs = "/wmtscache/collections/{}/layers/{}".format(collection['id'],
                                                      layer['id'])
    rv = client.get(qs)
    assert rv.status_code == 200
    assert rv.headers.get('Content-Type', "").startswith('application/json')

    json_content = json.loads(rv.content)
    assert 'id' in json_content
    assert json_content['id'] == layer['id']

    assert 'links' in json_content
    assert len(json_content['links']) == 0
Ejemplo n.º 15
0
def get_project_title(project: QgsProject) -> str:
    """ Gets project title, or if non available, the basename of the filename"""
    if project.title():
        return project.title()
    else:
        return Path(project.fileName()).stem
Ejemplo n.º 16
0
def test_wmts_document_cache_time(client):
    """  Test getcapabilites response time
    """
    plugin = client.getplugin('wmtsCacheServer')
    assert plugin is not None

    # Create a filter
    cachefilter = plugin.create_filter()

    # Copy project
    shutil.copy(
        client.getprojectpath("france_parts.qgs"),
        client.getprojectpath("france_parts_copy.qgs")
    )

    # Delete document
    project = QgsProject()
    project.setFileName(client.getprojectpath("france_parts_copy.qgs").strpath)

    # Get project document root path
    docroot = cachefilter._cache.get_documents_root(project.fileName())

    cachefilter.deleteCachedDocuments(project)

    assert not os.path.exists(docroot.as_posix())

    parameters = {
        'MAP': project.fileName(),
        'REQUEST': 'GetCapabilities',
        'SERVICE': 'WMTS'
    }

    # Get the cached path from the request parameters
    docpath = cachefilter._cache.get_document_cache(
        project.fileName(), parameters, '.xml'
    ).as_posix()

    assert not os.path.exists(docpath)

    # Make a request
    qs = "?" + "&".join("%s=%s" % item for item in parameters.items())
    rv = client.get(qs, project.fileName())
    assert rv.status_code == 200

    # Test that document cache has been created
    assert os.path.exists(docpath)

    # Get time of document cache creation
    docmtime = os.stat(docpath).st_mtime
    projmtime = project.lastModified().toMSecsSinceEpoch() / 1000.0

    assert projmtime < docmtime

    project.write()
    projmtime = project.lastModified().toMSecsSinceEpoch() / 1000.0

    assert projmtime > docmtime

    # Make a second request
    rv = client.get(qs, project.fileName())
    assert rv.status_code == 200

    ndocmtime = os.stat(docpath).st_mtime
    projmtime = project.lastModified().toMSecsSinceEpoch() / 1000.0

    assert projmtime < ndocmtime
    assert docmtime < ndocmtime

    # Clean files after testing
    Path(client.getprojectpath("france_parts_copy.qgs")).unlink()
Ejemplo n.º 17
0
def test_wmts_document_tile(client):
    """  Test WMTS tile response
    """
    plugin = client.getplugin('wmtsCacheServer')
    assert plugin is not None

    # Create a filter
    cachefilter = plugin.create_filter()

    # Delete document
    project = QgsProject()
    project.setFileName(client.getprojectpath("france_parts.qgs").strpath)

    # Get project document root path
    tileroot = cachefilter._cache.get_tiles_root(project.fileName())

    cachefilter.deleteCachedImages(project)

    assert not os.path.exists(tileroot.as_posix())

    parameters = {
        "MAP": project.fileName(),
        "SERVICE": "WMTS",
        "VERSION": "1.0.0",
        "REQUEST": "GetTile",
        "LAYER": "france_parts",
        "STYLE": "",
        "TILEMATRIXSET": "EPSG:4326",
        "TILEMATRIX": "0",
        "TILEROW": "0",
        "TILECOL": "0",
        "FORMAT": "image/png"
    }

    # Get the cached path from the request parameters
    tilepath = cachefilter._cache.get_tile_cache(
        project.fileName(), parameters
    ).as_posix()

    assert not os.path.exists(tilepath)

    # Make a request
    qs = "?" + "&".join("%s=%s" % item for item in parameters.items())
    rv = client.get(qs, project.fileName())

    original_content = rv.content

    if rv.status_code != 200:
        LOGGER.error(lxml.etree.tostring(rv.xml, pretty_print=True))

    assert rv.status_code == 200

    # Test that document cache has been created
    assert os.path.exists(tilepath)

    # Make a second request and check for header
    rv = client.get(qs, project.fileName())
    assert rv.status_code == 200
    assert rv.headers.get('X-Qgis-Debug-Cache-Plugin') == 'wmtsCacheServer'
    assert rv.headers.get('X-Qgis-Debug-Cache-Path') == tilepath

    cached_content = rv.content

    assert original_content == cached_content