Skip to content

Commit

Permalink
[Fixes #261] Add remote WMS importer handler
Browse files Browse the repository at this point in the history
  • Loading branch information
mattiagiupponi committed Jul 26, 2024
1 parent 205bc83 commit 35c7c93
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 23 deletions.
5 changes: 3 additions & 2 deletions importer/handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,12 @@ def get_ogr2ogr_task_group(
"""
return NotImplementedError

def delete_resource(self, instance):
@staticmethod
def delete_resource(instance):
"""
Base function to delete the resource with all the dependencies (example: dynamic model)
"""
return NotImplementedError
return

def _get_execution_request_object(self, execution_id: str):
return ExecutionRequest.objects.filter(exec_id=execution_id).first()
Expand Down
68 changes: 47 additions & 21 deletions importer/handlers/common/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,22 @@ def import_resource(self, files: dict, execution_id: str, **kwargs) -> str:

should_be_overwritten = _exec.input_params.get("overwrite_existing_layer")

payload_alternate = params.get("remote_resource_id", None)

user_datasets = ResourceBase.objects.filter(
owner=_exec.user, alternate=layer_name
owner=_exec.user, alternate=payload_alternate or layer_name
)

dataset_exists = user_datasets.exists()

if dataset_exists and should_be_overwritten:
layer_name, alternate = (
layer_name,
user_datasets.first().alternate.split(":")[-1],
)
elif not dataset_exists:
alternate = layer_name
else:
alternate = create_alternate(layer_name, execution_id)
layer_name, alternate = self.generate_alternate(
layer_name,
execution_id,
should_be_overwritten,
payload_alternate,
user_datasets,
dataset_exists,
)

import_orchestrator.apply_async(
(
Expand All @@ -150,12 +151,32 @@ def import_resource(self, files: dict, execution_id: str, **kwargs) -> str:
logger.error(e)
raise e

def generate_alternate(
self,
layer_name,
execution_id,
should_be_overwritten,
payload_alternate,
user_datasets,
dataset_exists,
):
if dataset_exists and should_be_overwritten:
layer_name, alternate = (
payload_alternate or layer_name,
user_datasets.first().alternate.split(":")[-1],
)
elif not dataset_exists:
alternate = payload_alternate or layer_name
else:
alternate = create_alternate(payload_alternate or layer_name, execution_id)
return layer_name, alternate

def create_geonode_resource(
self,
layer_name: str,
alternate: str,
execution_id: str,
resource_type: Dataset = ...,
resource_type: ResourceBase = ResourceBase,
asset=None,
):
"""
Expand All @@ -166,19 +187,12 @@ def create_geonode_resource(
"""
_exec = orchestrator.get_execution_object(execution_id)
params = _exec.input_params.copy()
subtype = params.get("type")

resource = resource_manager.create(
None,
resource_type=ResourceBase,
defaults=dict(
resource_type="dataset",
subtype=subtype,
sourcetype=SOURCE_TYPE_REMOTE,
alternate=alternate,
dirty_state=True,
title=params.get("title", layer_name),
owner=_exec.user,
resource_type=resource_type,
defaults=self.generate_resource_payload(
layer_name, alternate, asset, _exec, None, **params
),
)
resource_manager.set_thumbnail(None, instance=resource)
Expand Down Expand Up @@ -217,3 +231,15 @@ def create_resourcehandlerinfo(
execution_request=execution_id,
kwargs=kwargs.get("kwargs", {}) or kwargs,
)

def generate_resource_payload(
self, layer_name, alternate, asset, _exec, workspace, **kwargs
):
return dict(
subtype=kwargs.get("type"),
sourcetype=SOURCE_TYPE_REMOTE,
alternate=alternate,
dirty_state=True,
title=kwargs.get("title", layer_name),
owner=_exec.user,
)
Empty file.
17 changes: 17 additions & 0 deletions importer/handlers/remote/serializers/wms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from rest_framework import serializers
from importer.handlers.common.serializer import RemoteResourceSerializer


class RemoteWMSSerializer(RemoteResourceSerializer):
class Meta:
model = RemoteResourceSerializer.Meta.model
ref_name = "RemoteWMSSerializer"
fields = RemoteResourceSerializer.Meta.fields + (
"lookup",
"bbox",
"parse_remote_metadata",
)

lookup = serializers.CharField(required=True)
bbox = serializers.ListField(required=False)
parse_remote_metadata = serializers.BooleanField(required=False, default=False)
21 changes: 21 additions & 0 deletions importer/handlers/remote/tiles3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@
import requests
from geonode.layers.models import Dataset
from importer.handlers.common.remote import BaseRemoteResourceHandler
from importer.handlers.common.serializer import RemoteResourceSerializer
from importer.handlers.tiles3d.handler import Tiles3DFileHandler
from importer.orchestrator import orchestrator
from importer.handlers.tiles3d.exceptions import Invalid3DTilesException
from geonode.base.enumerations import SOURCE_TYPE_REMOTE

logger = logging.getLogger(__name__)


class RemoteTiles3DResourceHandler(BaseRemoteResourceHandler, Tiles3DFileHandler):

@staticmethod
def has_serializer(data) -> bool:
if "url" in data and "3dtiles" in data.get("type", "").upper():
return RemoteResourceSerializer
return False

@staticmethod
def can_handle(_data) -> bool:
"""
Expand Down Expand Up @@ -75,3 +83,16 @@ def create_geonode_resource(
resource = self.set_bbox_from_boundingVolume(js_file, resource=resource)

return resource

def generate_resource_payload(
self, layer_name, alternate, asset, _exec, workspace, **kwargs
):
return dict(
resource_type="dataset",
subtype=kwargs.get("type"),
sourcetype=SOURCE_TYPE_REMOTE,
alternate=alternate,
dirty_state=True,
title=kwargs.get("title", layer_name),
owner=_exec.user,
)
134 changes: 134 additions & 0 deletions importer/handlers/remote/wms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import logging
from django.conf import settings
from geonode.layers.models import Dataset
from importer.handlers.common.remote import BaseRemoteResourceHandler
from geonode.services import enumerations
from geonode.base.enumerations import SOURCE_TYPE_REMOTE
from geonode.base.models import Link
from geonode.resource.enumerator import ExecutionRequestAction as exa
from importer.handlers.remote.serializers.wms import RemoteWMSSerializer
from importer.orchestrator import orchestrator
from geonode.harvesting.harvesters.wms import WebMapService
from geonode.services.serviceprocessors.wms import WmsServiceHandler
from geonode.resource.manager import resource_manager

logger = logging.getLogger(__name__)


class RemoteWMSResourceHandler(BaseRemoteResourceHandler):

@staticmethod
def has_serializer(data) -> bool:
if "url" in data and enumerations.WMS in data.get("type", "").upper():
return RemoteWMSSerializer
return False

@staticmethod
def can_handle(_data) -> bool:
"""
This endpoint will return True or False if with the info provided
the handler is able to handle the file or not
"""
if "url" in _data and enumerations.WMS.lower() in _data.get("type", "").lower():
return True
return False

@staticmethod
def extract_params_from_data(_data, action=None):
"""
Remove from the _data the params that needs to save into the executionRequest object
all the other are returned
"""
payload, original_data = BaseRemoteResourceHandler.extract_params_from_data(
_data, action=action
)
if action != exa.COPY.value:
payload["lookup"] = original_data.pop("lookup", None)
payload["bbox"] = original_data.pop("bbox", None)
payload["parse_remote_metadata"] = original_data.pop(
"parse_remote_metadata", None
)

return payload, original_data

def prepare_import(self, files, execution_id, **kwargs):
_exec = orchestrator.get_execution_object(exec_id=execution_id)
cleaned_url, _, _, _ = WmsServiceHandler.get_cleaned_url_params(
_exec.input_params.get("url")
)
parsed_url = f"{cleaned_url.scheme}://{cleaned_url.netloc}{cleaned_url.path}"
ows_url = f"{parsed_url}?{cleaned_url.query}"
to_update = {"ows_url": ows_url, "parsed_url": parsed_url}
if _exec.input_params.get("parse_remote_metadata", False):
_, wms = WebMapService(_exec.input_params.get("url"))
wms_resource = wms[_exec.input_params.get("lookup")]
to_update.update(
{
"title": wms_resource.title,
"bbox": wms_resource.boundingBoxWGS84,
"remote_resource_id": _exec.input_params.get("lookup", None),
}
)
_exec.input_params.update(to_update)
_exec.save()

def generate_alternate(
self,
layer_name,
execution_id,
should_be_overwritten,
payload_alternate,
user_datasets,
dataset_exists,
):
return layer_name, payload_alternate

def create_geonode_resource(
self,
layer_name: str,
alternate: str,
execution_id: str,
resource_type: Dataset = ...,
asset=None,
):
resource = super().create_geonode_resource(
layer_name, alternate, execution_id, Dataset, asset
)
_exec = orchestrator.get_execution_object(execution_id)
bbox = _exec.input_params.get("bbox")
resource.set_bbox_polygon(bbox, "EPSG:4326")
resource_manager.set_thumbnail(None, instance=resource)
return resource

def generate_resource_payload(
self, layer_name, alternate, asset, _exec, workspace, **kwargs
):
return dict(
resource_type="dataset",
subtype="remote",
sourcetype=SOURCE_TYPE_REMOTE,
alternate=alternate,
dirty_state=True,
title=kwargs.get("title", layer_name),
name=alternate,
owner=_exec.user,
workspace=getattr(settings, "DEFAULT_WORKSPACE", "geonode"),
store=_exec.input_params.get("parsed_url")
.encode("utf-8", "ignore")
.decode("utf-8")
.replace(".", "")
.replace("/", ""),
ptype="gxp_wmscsource",
ows_url=_exec.input_params.get("ows_url"),
)

def create_link(self, resource, params: dict, name):
link = Link(
resource=resource,
extension="text/html",
url=params.get("ows_url"),
link_type="OGC:WMS",
name="OGC WMS: remoteWorkspace Service",
)
link.save()
return resource

0 comments on commit 35c7c93

Please sign in to comment.