Exemplo n.º 1
0
    def _build_describe_feature_type_xml(self, service_param: str,
                                         version_param: str,
                                         request_param: str):
        """ Returns the POST request XML for a DescribeFeatureType request
        
        Args:
            service_param (str): The service param 
            version_param (str): The version param 
            request_param (str): The request param 
        Returns:
             xml (str): The xml document
        """

        format_param = self._get_POST_val("format") or ""
        type_name_param = self._get_POST_val("typename")

        reduced_ns_map = self._get_version_specific_namespaces(
            version_param, service_param)

        root_attributes = {
            "service": service_param,
            "version": version_param,
            "outputFormat": format_param
        }
        root = etree.Element(_tag=request_param,
                             nsmap=reduced_ns_map,
                             attrib=root_attributes)
        for t_n_param in type_name_param.split(","):
            type_name_elem = etree.Element(_tag="TypeName")
            type_name_elem.text = t_n_param
            xml_helper.add_subelement(root, type_name_elem)

        xml = xml_helper.xml_to_string(root)

        return xml
Exemplo n.º 2
0
    def get_response(self):
        """ Creates the xml response

        Returns:
             xml_str (str): The response as string
        """
        # Check for a cached version, before we generate a new capabilities document
        cacher = DocumentCacher(title="CSW_CAPABILITIES", version="1.0")
        response = json.loads(cacher.get("csw") or "{}")
        csw_settings_hash = md5(json.dumps(CSW_CAPABILITIES_CONF).encode("UTF-8")).hexdigest()

        if response.get("hash", "") != csw_settings_hash or len(response.get("document", "")) == 0:
            # No cached version found or the used CSW_CAPABILITIES_CONF was different
            # So create a new document and overwrite!
            response = xml_helper.xml_to_string(self._create_csw_capabilities(), pretty_print=True)
            content = {
                "hash": csw_settings_hash,
                "document": response
            }
            cacher.set(key="csw", val=json.dumps(content))
        else:
            # There is a document, which can be returned
            response = response["document"]

        return response
Exemplo n.º 3
0
    def get_exception_report(self):
        """ Creates an OWSExceptionReport from a given Exception object

        Returns:
             report (str): The exception report as string
        """
        root = Element(
            "{}ExceptionReport".format(self.ows_ns),
            nsmap=self.namespace_map,
            attrib={
                "{}schemaLocation".format(self.xsi_ns):
                "http://schemas.opengis.net/ows/1.1.0/owsExceptionReport.xsd",
                "version": "1.2.0",
            })
        exception_elem = xml_helper.create_subelement(
            root,
            "{}Exception".format(self.ows_ns),
            attrib={
                "exceptionCode": self.exception.__class__.__name__,
                "locator": self.locator,
            })
        text_elem = xml_helper.create_subelement(
            exception_elem, "{}ExceptionText".format(self.ows_ns))
        text_elem.text = self.text

        return xml_helper.xml_to_string(root, pretty_print=True)
Exemplo n.º 4
0
    def _generate_request_POST_body(self,
                                    start_position: int,
                                    result_type: str = "results"):
        """ Creates a CSW POST body xml document for GetRecords

        Args:
            start_position (int): The start position for the request
        Returns:
             xml (str): The GetRecords xml document
        """
        namespaces = {
            "csw":
            "http://www.opengis.net/cat/csw/2.0.2",
            "apiso":
            "http://www.opengis.net/cat/csw/apiso/1.0",
            "ogc":
            "http://www.opengis.net/ogc",
            "gmd":
            "http://www.isotc211.org/2005/gmd",
            "ows":
            "http://www.opengis.net/ows",
            "xsd":
            "http://www.w3.org/2001/XMLSchema",
            "xsi":
            "http://www.w3.org/2001/XMLSchema",
            "dc":
            "http://purl.org/dc/elements/1.1/",
            "dct":
            "http://purl.org/dc/terms/",
            "schemaLocation":
            "http://www.opengis.net/cat/csw/{}".format(self.version),
            None:
            "http://www.opengis.net/cat/csw/{}".format(self.version),
        }
        csw_ns = "{" + namespaces["csw"] + "}"

        root_elem = Element(
            "{}{}".format(csw_ns, OGCOperationEnum.GET_RECORDS.value),
            attrib={
                "version": self.version,
                "service": "CSW",
                "resultType": result_type,
                "outputFormat": self.output_format,
                "startPosition": str(start_position),
                "maxRecords": str(self.max_records_per_request),
                "outputSchema": HARVEST_GET_REQUEST_OUTPUT_SCHEMA,
                #"{}schemaLocation".format(xsi_ns): "http://www.opengis.net/cat/csw/2.0.2",
            },
            nsmap=namespaces)
        xml_helper.create_subelement(root_elem, "{}Query".format(csw_ns), None,
                                     {"typeNames": "gmd:MD_Metadata"})
        post_content = xml_helper.xml_to_string(root_elem)
        return post_content
Exemplo n.º 5
0
    def _build_lock_feature_xml(self, service_param: str, version_param: str,
                                request_param: str):
        """ Returns the POST request XML for a Lock request

        Args:
            service_param (str): The service param
            version_param (str): The version param
            request_param (str): The request param
        Returns:
             xml (str): The xml document
        """
        xml = ""

        lock_action_param = self._get_POST_val("lockAction") or ""
        type_name_param = self._get_POST_val("typename")
        filter_param = self._get_POST_val("filter")

        reduced_ns_map = self._get_version_specific_namespaces(
            version_param, service_param)

        root_attributes = {
            "service": service_param,
            "version": version_param,
            "lockAction": lock_action_param
        }
        root = etree.Element(_tag=request_param,
                             nsmap=reduced_ns_map,
                             attrib=root_attributes)

        # create the xml filter object from the filter string parameter
        filter_xml = xml_helper.parse_xml(filter_param)
        filter_xml_root = filter_xml.getroot()

        for t_n_param in type_name_param.split(","):
            query_attributes = {"typeName": t_n_param}
            query_elem = xml_helper.create_subelement(root,
                                                      "Query",
                                                      attrib=query_attributes)

            # add the filter xml object as subobject to the query to use e.g. the spatial restriction
            xml_helper.add_subelement(query_elem, filter_xml_root)

        xml = xml_helper.xml_to_string(root)

        return xml
Exemplo n.º 6
0
def _remove_iso_metadata(metadata: Metadata, md_links: list,
                         existing_iso_links: list):
    """ Remove iso metadata that is not found in the newer md_links list but still lives in the persisted existing_iso_links list

    Args:
        metadata (Metadata): The edited metadata
        md_links (list): The new iso metadata links
        existing_iso_links (list): The existing metadata links, related to the metadata object
    Returns:
         nothing
    """
    # remove iso metadata from capabilities document
    rel_md = metadata
    service_type = metadata.service_type
    if not metadata.is_root():
        if service_type == OGCServiceEnum.WMS:
            rel_md = metadata.service.parent_service.metadata
        elif service_type == OGCServiceEnum.WFS:
            rel_md = metadata.featuretype.parent_service.metadata
    cap_doc = Document.objects.get(
        metadata=rel_md,
        is_original=False,
        document_type=DocumentEnum.CAPABILITY.value,
    )
    cap_doc_txt = cap_doc.content
    xml_cap_obj = xml_helper.parse_xml(cap_doc_txt).getroot()

    # if there are links in existing_iso_links that do not show up in md_links -> remove them
    for link in existing_iso_links:
        if link not in md_links:
            missing_md = metadata.get_related_metadatas(
                filters={'to_metadatas__to_metadata__metadata_url': link})
            missing_md.delete()
            # remove from capabilities
            xml_iso_element = xml_helper.find_element_where_attr(
                xml_cap_obj, "xlink:href", link)
            for elem in xml_iso_element:
                xml_helper.remove_element(elem)
    cap_doc_txt = xml_helper.xml_to_string(xml_cap_obj)
    cap_doc.content = cap_doc_txt
    cap_doc.save()
Exemplo n.º 7
0
    def get_response(self):
        """ Creates the xml response

        Returns:
             xml_str (str): The response as string
        """
        metadata = self.get_metadata(
            filtered=True,
            sorted=True
        )
        i_from = self.param.start_position - 1
        i_to = i_from + self.param.max_records
        returned_metadata = metadata[i_from:i_to]
        if i_from > metadata.count():
            raise ValueError("Start position ({}) can't be greater than number of matching records ({})".format(self.param.start_position, metadata.count()), "startPosition")

        # Only return results content if this was requested
        md_converter = MetadataConverter(self.param, metadata, returned_metadata)
        response = md_converter.create_xml_response(with_content=self.param.result_type == "results")

        xml_str = xml_helper.xml_to_string(response, pretty_print=True)
        return xml_str
Exemplo n.º 8
0
def overwrite_capabilities_document(metadata: Metadata):
    """ Overwrites the capabilities document which is related to the provided metadata.

    If a subelement of a service has been edited, the service root capabilities will be changed since this is the
    most requested document of the service.
    All subelements capabilities documents above the edited element will be reset to None and cached documents will be
    cleared. This forces an automatic creation of the correct capabilities on the next request for these elements,
    which will result in correct information about the edited subelement.

    Args:
        metadata (Metadata):
    Returns:
         nothing
    """
    is_root = metadata.is_root()
    if is_root:
        parent_metadata = metadata
    elif metadata.is_metadata_type(MetadataEnum.LAYER):
        parent_metadata = metadata.service.parent_service.metadata
    elif metadata.is_metadata_type(MetadataEnum.FEATURETYPE):
        parent_metadata = metadata.featuretype.parent_service.metadata

    # Make sure the Document record already exist by fetching the current capability xml
    # This is a little trick to auto-generate Document records which did not exist before!
    parent_metadata.get_current_capability_xml(
        parent_metadata.get_service_version().value)
    cap_doc = Document.objects.get(
        metadata=parent_metadata,
        document_type=DocumentEnum.CAPABILITY.value,
        is_original=False,
    )

    # overwrite all editable data
    xml_obj_root = xml_helper.parse_xml(cap_doc.content)

    # find matching xml element in xml doc
    _type = metadata.service_type.value
    _version = metadata.get_service_version()

    identifier = metadata.identifier
    if is_root:
        if metadata.is_service_type(OGCServiceEnum.WFS):
            if _version is OGCServiceVersionEnum.V_2_0_0 or _version is OGCServiceVersionEnum.V_2_0_2:
                XML_NAMESPACES["wfs"] = "http://www.opengis.net/wfs/2.0"
                XML_NAMESPACES["ows"] = "http://www.opengis.net/ows/1.1"
                XML_NAMESPACES["fes"] = "http://www.opengis.net/fes/2.0"
                XML_NAMESPACES["default"] = XML_NAMESPACES["wfs"]
            identifier = metadata.title

    xml_obj = xml_helper.find_element_where_text(xml_obj_root, txt=identifier)
    if len(xml_obj) > 0:
        xml_obj = xml_obj[0]

    # handle keywords
    _overwrite_capabilities_keywords(xml_obj, metadata, _type)

    # handle iso metadata links
    _overwrite_capabilities_iso_metadata_links(xml_obj, metadata)

    # overwrite data
    _overwrite_capabilities_data(xml_obj, metadata)

    # write xml back to Document record
    # Remove service_metadata_document as well, so it needs to be generated again!
    xml = xml_helper.xml_to_string(xml_obj_root)
    cap_doc.content = xml
    cap_doc.save()
    service_metadata_doc = Document.objects.filter(
        metadata=metadata,
        document_type=DocumentEnum.METADATA.value,
    )
    service_metadata_doc.delete()

    # Delete all cached documents, which holds old state!
    metadata.clear_cached_documents()

    # Delete all cached documents of root service, which holds old state!
    parent_metadata.clear_cached_documents()

    # Remove existing document contents from upper elements (children of root element), which holds old state!
    metadata.clear_upper_element_capabilities(clear_self_too=True)
Exemplo n.º 9
0
    def _build_get_map_1_3_0_xml(self, service_param: str, version_param: str,
                                 request_param: str):
        """ Returns the POST request XML for a GetMap request

        Examples can be found here:

        http://schemas.opengis.net/sld/1.1/example_getmap.xml
        https://www.how2map.com/2013/10/geoserver-getmap-with-http-post.html

        Args:
            service_param (str): The service param
            version_param (str): The version param
            request_param (str): The request param
        Returns:
             xml (str): The xml document
        """
        xml = ""

        format_param = self._get_POST_val("format") or ""
        width_param = self._get_POST_val("width")
        height_param = self._get_POST_val("height")
        layers_param = self._get_POST_val("layer")
        bbox_param = self._get_POST_val("bbox")
        srs_param = self._get_POST_val("srs")

        reduced_ns_map = self._get_version_specific_namespaces(
            version_param, service_param)

        root_attributes = {"version": version_param}
        root = etree.Element(_tag=request_param,
                             nsmap=reduced_ns_map,
                             attrib=root_attributes)

        # Add <StyledLayerDescriptor> element with layer elements
        styled_layer_descriptor = etree.Element("StyledLayerDescriptor",
                                                {"version": version_param},
                                                reduced_ns_map)
        for layer in layers_param.split(","):
            named_layer_elem = etree.Element("NamedLayer")

            name_elem = etree.Element("{" + reduced_ns_map["se"] + "}Name")
            name_elem.text = layer

            xml_helper.add_subelement(named_layer_elem, name_elem)
            xml_helper.add_subelement(styled_layer_descriptor,
                                      named_layer_elem)
        xml_helper.add_subelement(root, styled_layer_descriptor)

        # Add <CRS> element
        crs_elem = etree.Element("CRS")
        crs_elem.text = srs_param
        xml_helper.add_subelement(root, crs_elem)

        # Add <BoundingBox> element
        if bbox_param is not None:
            bbox_elem = etree.Element("BoundingBox")
            lower_corner_elem = etree.Element("ows:LowerCorner")
            upper_corner_elem = etree.Element("ows:UpperCorner")

            bbox_params_list = bbox_param.split(",")
            lower_corner_elem.text = "{} {}".format(bbox_params_list[0],
                                                    bbox_params_list[1])
            upper_corner_elem.text = "{} {}".format(bbox_params_list[2],
                                                    bbox_params_list[3])

            xml_helper.add_subelement(bbox_elem, lower_corner_elem)
            xml_helper.add_subelement(bbox_elem, upper_corner_elem)
            xml_helper.add_subelement(root, bbox_elem)

        # Add <Output> element
        output_elem = etree.Element("Output")
        size_elem = etree.Element("Size")
        width_elem = etree.Element("Width")
        width_elem.text = width_param
        height_elem = etree.Element("Height")
        height_elem.text = height_param
        xml_helper.add_subelement(size_elem, width_elem)
        xml_helper.add_subelement(size_elem, height_elem)
        format_elem = etree.Element("wms:Format")
        format_elem.text = format_param

        xml_helper.add_subelement(output_elem, size_elem)
        xml_helper.add_subelement(output_elem, format_elem)
        xml_helper.add_subelement(root, output_elem)

        xml = xml_helper.xml_to_string(root)

        return xml
Exemplo n.º 10
0
    def _build_get_feature_xml(self, service_param: str, version_param: str,
                               request_param: str):
        """ Returns the POST request XML for a GetFeature request

        Args:
            service_param (str): The service param
            version_param (str): The version param
            request_param (str): The request param
        Returns:
             xml (str): The xml document
        """
        xml = ""

        format_param = self._get_POST_val("format")
        type_name_param = self._get_POST_val("typename") or self._get_POST_val(
            "typenames")
        filter_param = self._get_POST_val("filter")
        count_param = self._get_POST_val("count") or self._get_POST_val(
            "maxFeatures")
        resulttype_param = self._get_POST_val("count") or self._get_POST_val(
            "resultType")

        # check if the newer 'typeNames' instead of 'typeName' should be used
        type_name_identifier = "typeName"
        if version_param == OGCServiceVersionEnum.V_2_0_0.value or version_param == OGCServiceVersionEnum.V_2_0_2.value:
            type_name_identifier = "typeNames"

        reduced_ns_map = self._get_version_specific_namespaces(
            version_param, service_param)
        wfs_ns = reduced_ns_map["wfs"]

        root_attributes = {
            "service": service_param,
            "version": version_param,
        }

        if resulttype_param is not None:
            root_attributes["resultType"] = resulttype_param
        if format_param is not None:
            root_attributes["outputFormat"] = format_param
        if count_param is not None:
            param_tag = "maxFeatures"
            if version_param == OGCServiceVersionEnum.V_2_0_0.value or version_param == OGCServiceVersionEnum.V_2_0_2.value:
                param_tag = "count"
            root_attributes[param_tag] = count_param

        root = etree.Element(_tag="{" + wfs_ns + "}" + request_param,
                             nsmap=reduced_ns_map,
                             attrib=root_attributes)

        # create the xml filter object from the filter string parameter
        filter_xml = xml_helper.parse_xml(filter_param)
        if filter_xml is not None:
            filter_xml_root = filter_xml.getroot()

            for t_n_param in type_name_param.split(","):
                query_attributes = {type_name_identifier: t_n_param}
                query_elem = xml_helper.create_subelement(
                    root,
                    "{" + wfs_ns + "}" + "Query",
                    attrib=query_attributes)

                # add the filter xml object as subobject to the query to use e.g. the spatial restriction
                xml_helper.add_subelement(query_elem, filter_xml_root)

            xml = xml_helper.xml_to_string(root)

        return xml