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 _overwrite_capabilities_keywords(xml_obj: _Element, metadata: Metadata, _type: str): """ Overwrites existing capabilities keywords with metadata editor input Args: xml_obj (_Element): The parent xml object which holds the KeywordList element metadata (Metadata): The metadata object which holds the edited keyword data _type (str): Defines if this is a wms or wfs Returns: nothing """ ns_prefix = "" keyword_container_tag = "KeywordList" keyword_prefix = "" keyword_ns_map = {} if _type == 'wfs': ns_keyword_prefix_s = "ows" ns_prefix = "wfs:" if metadata.is_root(): # for the <ows:ServiceIdentification> element we need the prefix "ows:" ns_prefix = "ows:" keyword_container_tag = "Keywords" keyword_prefix = "{" + XML_NAMESPACES[ns_keyword_prefix_s] + "}" keyword_ns_map[ns_keyword_prefix_s] = XML_NAMESPACES[ ns_keyword_prefix_s] xml_keywords_list_obj = xml_helper.try_get_single_element_from_xml( "./" + GENERIC_NAMESPACE_TEMPLATE.format(keyword_container_tag), xml_obj) if xml_keywords_list_obj is None: # there are no keywords in this capabilities for this element yet # we need to add an element first! try: xml_keywords_list_obj = xml_helper.create_subelement( xml_obj, "{}{}".format(keyword_prefix, keyword_container_tag), after="{}Abstract".format(ns_prefix), nsmap=keyword_ns_map) except (TypeError, ValueError) as e: # there seems to be no <Abstract> element. We add simply after <Title> and also create a new Abstract element xml_keywords_list_obj = xml_helper.create_subelement( xml_obj, "{}{}".format(keyword_prefix, keyword_container_tag), after="{}Title".format(ns_prefix)) xml_helper.create_subelement(xml_obj, "{}".format("Abstract"), after="{}Title".format(ns_prefix)) xml_keywords_objs = xml_helper.try_get_element_from_xml( "./" + GENERIC_NAMESPACE_TEMPLATE.format("Keyword"), xml_keywords_list_obj) or [] # first remove all persisted keywords for kw in xml_keywords_objs: xml_keywords_list_obj.remove(kw) # then add all edited for kw in metadata.keywords.all(): xml_keyword = xml_helper.create_subelement( xml_keywords_list_obj, "{}Keyword".format(keyword_prefix), nsmap=keyword_ns_map) xml_helper.write_text_to_element(xml_keyword, txt=kw.keyword)
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 get_resource_capabilities(request: HttpRequest, md: Metadata): """ Logic for retrieving a capabilities document. If no capabilities document can be provided by the given parameter, a fallback document will be returned. Args: request: md: Returns: """ from service.tasks import async_increase_hits stored_version = md.get_service_version().value # move increasing hits to background process to speed up response time! # todo: after refactoring of md.increase_hits() maybe we don't need to start async tasks... test it!!! async_increase_hits.delay(md.id) if not md.is_active: return HttpResponse(content=SERVICE_DISABLED, status=423) # check that we have the requested version in our database version_param = None version_tag = None request_param = None request_tag = None use_fallback = None for k, v in request.GET.dict().items(): if k.upper() == "VERSION": version_param = v version_tag = k elif k.upper() == "REQUEST": request_param = v request_tag = k elif k.upper() == "FALLBACK": use_fallback = resolve_boolean_attribute_val(v) # No version parameter has been provided by the request - we simply use the one we have. if version_param is None or len(version_param) == 0: version_param = stored_version if version_param not in [data.value for data in OGCServiceVersionEnum]: # version number not valid return HttpResponse(content=PARAMETER_ERROR.format(version_tag), status=404) elif request_param is not None and request_param != OGCOperationEnum.GET_CAPABILITIES.value: # request not valid return HttpResponse(content=PARAMETER_ERROR.format(request_tag), status=404) else: pass if md.is_catalogue_metadata: doc = md.get_remote_original_capabilities_document(version_param) elif stored_version == version_param or use_fallback is True or not md.is_root(): # This is the case if # 1) a version is requested, which we have in our database # 2) the fallback parameter is set explicitly # 3) a subelement is requested, which normally do not have capability documents # We can check the cache for this document or we need to generate it! doc = md.get_current_capability_xml(version_param) else: # we have to fetch the remote document # fetch the requested capabilities document from remote - we do not provide this as our default (registered) one xml = md.get_remote_original_capabilities_document(version_param) tmp = xml_helper.parse_xml(xml) if tmp is None: raise ValueError("No xml document was retrieved. Content was :'{}'".format(xml)) # we fake the persisted service version, so the document setters will change the correct elements in the xml # md.service.service_type.version = version_param doc = Document( content=xml, metadata=md, document_type=DocumentEnum.CAPABILITY.value, is_original=True ) doc.set_capabilities_secured(auto_save=False) if md.use_proxy_uri: doc.set_proxy(True, auto_save=False, force_version=version_param) doc = doc.content return doc