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
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
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)
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
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
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()
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
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)
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
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