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 _create_parameter_elem(self, parameters: dict, upper_elem: Element): """ Creates <ows:Parameter> element and adds it to an upper element Returns: """ for param_key, param_val in parameters.items(): param_elem = xml_helper.create_subelement(upper_elem, "{}Parameter".format(self.ows_ns), attrib={ "name": param_key }) for val in param_val: xml_helper.create_subelement(param_elem, "{}Value".format(self.ows_ns)).text = val
def _create_constraint_elem(self, constraints: dict, upper_elem: Element): """ Creates <ows:Constraint> element and adds it to an upper element Returns: """ for cons_key, cons_val in constraints.items(): cons_elem = xml_helper.create_subelement(upper_elem, "{}Constraint".format(self.ows_ns), attrib={ "name": cons_key }) for val in cons_val: xml_helper.create_subelement(cons_elem, "{}Value".format(self.ows_ns)).text = val
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 _overwrite_capabilities_data(xml_obj: _Element, metadata: Metadata): """ Overwrites capabilities document data with changed data from editor based changes. Only capable of changing <Title>, <Abstract> and <AccessConstraints> Args: xml_obj (_Element): The document xml object metadata (Metadata): The metadata holding the data Returns: """ # Create licence appendix for AccessConstraints and Fees licence = metadata.licence licence_appendix = "" if licence is not None: licence_appendix = "\n {} ({}), \n {}, \n {}".format( licence.name, licence.identifier, licence.description, licence.description_url) elements = { "Title": metadata.title, "Abstract": metadata.abstract, "AccessConstraints": "{}{}".format(metadata.access_constraints, licence_appendix), "Fees": "{}{}".format(metadata.fees, licence_appendix), } for key, val in elements.items(): try: # Check if element exists to change it key_xml_obj = xml_helper.try_get_single_element_from_xml( "./" + GENERIC_NAMESPACE_TEMPLATE.format(key), xml_obj) if key_xml_obj is not None: # Element exists, we can change it easily xml_helper.write_text_to_element( xml_obj, "./" + GENERIC_NAMESPACE_TEMPLATE.format(key), val) else: # The element does not exist (happens in case of abstract sometimes) # First create, than change it xml_helper.create_subelement( xml_obj, key, ) xml_helper.write_text_to_element( xml_obj, "./" + GENERIC_NAMESPACE_TEMPLATE.format(key), val) except AttributeError as e: # for not is_root this will fail in AccessConstraints querying pass
def _create_csw_filter_capabilities(self, root: Element): """ Creates the <ogc:Filter_Capabilities> element Returns: """ elem = xml_helper.create_subelement( root, "{}Filter_Capabilities".format(self.ogc_ns) ) filter_capabilities = CSW_CAPABILITIES_CONF.get("filter_capabilities", {}) for cap_key, cap_val in filter_capabilities.items(): cap_elem = xml_helper.create_subelement(elem, "{}{}".format(self.ogc_ns, cap_key)) for operator_key, operator_val in cap_val.items(): op_elem = xml_helper.create_subelement(cap_elem, "{}{}".format(self.ogc_ns, operator_key)) singular_operator_key = operator_key[:-1] if operator_key[-1] == "s" else operator_key for val in operator_val: xml_helper.create_subelement(op_elem, "{}{}".format(self.ogc_ns, singular_operator_key)).text = val
def _create_dublin_core_brief_elem(self, md: Metadata): """ Creates the BriefRecord in Dublin core syntax Args: md (Metadata): The metadata object providing the data Returns: elem (_Element): The lxml element """ record_elem = Element( "{}BriefRecord".format(self.csw_ns), nsmap=self.dc_ns_map, ) # Perform xml creation for simple elements attribute_element_map = OrderedDict() attribute_element_map[IDENTIFIER_TEMPLATE.format( self.dc_ns)] = md.identifier attribute_element_map[TITLE_TEMPLATE.format(self.dc_ns)] = md.title attribute_element_map[TYPE_TEMPLATE.format( self.dc_ns )] = md.metadata_type if md.metadata_type == MetadataEnum.DATASET.value else MetadataEnum.SERVICE.value # Create xml elements from mapped information self._create_xml_from_map(record_elem, attribute_element_map) # Perform xml creation for complex elements geometry = md.bounding_geometry if md.bounding_geometry is not None and md.bounding_geometry.area > 0 else md.find_max_bounding_box( ) bbox_elem = xml_helper.create_subelement( record_elem, "{}BoundingBox".format(self.ows_ns), attrib={"crs": "EPSG:{}".format(geometry.srid)}) bbox = geometry.extent lower_corner_elem = xml_helper.create_subelement( bbox_elem, "{}LowerCorner".format(self.ows_ns)) lower_corner_elem.text = "{} {}".format(bbox[0], bbox[1]) upper_corner_elem = xml_helper.create_subelement( bbox_elem, "{}UpperCorner".format(self.ows_ns)) upper_corner_elem.text = "{} {}".format(bbox[2], bbox[3]) return record_elem
def _create_xml_from_map(self, parent_element: Element, map: OrderedDict): """ Creates xml elements from a given tag-attribute map. Only for simple elements, which only hold a tag and text data. Args: parent_element (Element): The upper xml element map (OrderedDict): The tag-attribute map Returns: """ for key, val in map.items(): if isinstance(val, list): for item in val: elem = xml_helper.create_subelement(parent_element, key) elem.text = item else: elem = xml_helper.create_subelement(parent_element, key) elem.text = val
def _create_dublin_core_full_elem(self, md: Metadata): """ Creates the default (full) record in Dublin core syntax Args: md (Metadata): The metadata object providing the data Returns: elem (_Element): The lxml element """ record_elem = Element( "{}Record".format(self.csw_ns), nsmap=self.dc_ns_map, ) # Perform xml creation for simple elements attribute_element_map = OrderedDict() attribute_element_map[IDENTIFIER_TEMPLATE.format( self.dc_ns)] = md.identifier attribute_element_map["{}date".format( self.dc_ns)] = md.created.strftime(DATE_STRF) attribute_element_map[TITLE_TEMPLATE.format(self.dc_ns)] = md.title attribute_element_map["{}abstract".format(self.dct_ns)] = md.abstract attribute_element_map["{}description".format(self.dc_ns)] = md.abstract attribute_element_map[TYPE_TEMPLATE.format( self.dc_ns )] = md.metadata_type if md.metadata_type == MetadataEnum.DATASET.value else MetadataEnum.SERVICE.value kws = md.keywords.all() formats = md.get_formats() attribute_element_map["{}subject".format( self.dc_ns)] = [kw.keyword for kw in kws] attribute_element_map["{}format".format( self.dc_ns)] = [format.mime_type for format in formats] attribute_element_map["{}modified".format( self.dct_ns)] = md.last_modified.strftime(DATE_STRF) attribute_element_map["{}rights".format(self.dc_ns)] = "ToDo" # ToDo # Create xml elements from mapped information self._create_xml_from_map(record_elem, attribute_element_map) # Perform xml creation for more complex elements # URI elem = xml_helper.create_subelement(record_elem, "{}URI".format(self.dc_ns), attrib={ "protocol": "", "name": md.identifier or "", "description": md.abstract or "", }) elem.text = md.capabilities_uri return record_elem
def _create_csw_operations_metadata(self, root: Element): """ Creates the <ows:OperationsMetadata> element Returns: """ elem = xml_helper.create_subelement( root, "{}OperationsMetadata".format(self.ows_ns) ) operations_metadata = CSW_CAPABILITIES_CONF.get("operations_metadata", {}) operations = operations_metadata.get("operations", {}) for operation_name, operation_val in operations.items(): operation_elem = xml_helper.create_subelement(elem, "{}Operation".format(self.ows_ns), attrib={ "name": operation_name }) # DCP | HTTP dcp_elem = xml_helper.create_subelement(operation_elem, "{}DCP".format(self.ows_ns)) http_elem = xml_helper.create_subelement(dcp_elem, "{}HTTP".format(self.ows_ns)) ## Get get_uri = operation_val.get("get_uri", None) if get_uri is not None: xml_helper.create_subelement(http_elem, "{}Get".format(self.ows_ns), attrib={ "{}href".format(self.xlink_ns): get_uri }) ## Post post_uri = operation_val.get("post_uri", None) if post_uri is not None: xml_helper.create_subelement(http_elem, "{}Post".format(self.ows_ns), attrib={ "{}href".format(self.xlink_ns): post_uri }) # Parameter parameters = operation_val.get("parameter", {}) self._create_parameter_elem(parameters, operation_elem) # Constraint constraints = operation_val.get("constraint", {}) self._create_constraint_elem(constraints, operation_elem) # General parameters and constraints parameters = operations_metadata.get("parameters", {}) self._create_parameter_elem(parameters, elem) constraints = operations_metadata.get("constraints", {}) self._create_constraint_elem(constraints, elem)
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 _create_csw_service_identification(self, root: Element): """ Creates the <ows:ServiceIdentification> element Returns: """ elem = xml_helper.create_subelement( root, "{}ServiceIdentification".format(self.ows_ns) ) service_identification = CSW_CAPABILITIES_CONF.get("service_identification") # Title xml_helper.create_subelement(elem, "{}Title".format(self.ows_ns)).text = service_identification.get("title", "") # Abstract xml_helper.create_subelement(elem, "{}Abstract".format(self.ows_ns)).text = service_identification.get("abstract", "") # Keywords kw_elem = xml_helper.create_subelement(elem, "{}Keywords".format(self.ows_ns)) keywords = service_identification.get("keywords", "").split(",") for kw in keywords: xml_helper.create_subelement(kw_elem, "{}Keyword".format(self.ows_ns)).text = kw # ServiceType xml_helper.create_subelement(elem, "{}ServiceType".format(self.ows_ns)).text = service_identification.get("service_type", "") # ServiceTypeVersion xml_helper.create_subelement(elem, "{}ServiceTypeVersion".format(self.ows_ns)).text = service_identification.get("service_type_version", "") # Fees xml_helper.create_subelement(elem, "{}Fees".format(self.ows_ns)).text = service_identification.get("fees", "") # AccessConstraints xml_helper.create_subelement(elem, "{}AccessConstraints".format(self.ows_ns)).text = service_identification.get("access_constraints", "")
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 _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
def _create_csw_service_provider(self, root: Element): """ Creates the <ows:ServiceProvider> element Returns: """ elem = xml_helper.create_subelement( root, "{}ServiceProvider".format(self.ows_ns) ) service_provider = CSW_CAPABILITIES_CONF.get("service_provider") # ProviderName xml_helper.create_subelement(elem, "{}ProviderName".format(self.ows_ns)).text = service_provider.get("name", "") # ProviderSite xml_helper.create_subelement(elem, "{}ProviderSite".format(self.ows_ns), attrib={"{}href".format(self.xlink_ns): service_provider.get("provider_site")}) # ServiceContact contact_elem = xml_helper.create_subelement(elem, "{}ServiceContact".format(self.ows_ns)) ## IndividualName xml_helper.create_subelement(contact_elem, "{}IndividualName".format(self.ows_ns)).text = service_provider.get("individual_name", "") ## PositionName xml_helper.create_subelement(contact_elem, "{}PositionName".format(self.ows_ns)).text = service_provider.get("position_name", "") ## ContactInfo contact_info_elem = xml_helper.create_subelement(contact_elem, "{}ContactInfo".format(self.ows_ns)) ### Phone phone_elem = xml_helper.create_subelement(contact_info_elem, "{}Phone".format(self.ows_ns)) #### Voice xml_helper.create_subelement(phone_elem, "{}Voice".format(self.ows_ns)).text = service_provider.get("contact_phone", "") #### Facsimile xml_helper.create_subelement(phone_elem, "{}Facsimile".format(self.ows_ns)).text = service_provider.get("contact_facsimile", "") ### Address address_elem = xml_helper.create_subelement(contact_info_elem, "{}Address".format(self.ows_ns)) #### DeliveryPoint xml_helper.create_subelement(address_elem, "{}DeliveryPoint".format(self.ows_ns)).text = service_provider.get("contact_address_delivery_point", "") #### City xml_helper.create_subelement(address_elem, "{}City".format(self.ows_ns)).text = service_provider.get("contact_address_city", "") #### AdministrativeArea xml_helper.create_subelement(address_elem, "{}AdministrativeArea".format(self.ows_ns)).text = service_provider.get("contact_address_administrative_area", "") #### PostalCode xml_helper.create_subelement(address_elem, "{}PostalCode".format(self.ows_ns)).text = service_provider.get("contact_address_postal_code", "") #### Country xml_helper.create_subelement(address_elem, "{}Country".format(self.ows_ns)).text = service_provider.get("contact_address_country", "") #### ElectronicMailAddress xml_helper.create_subelement(address_elem, "{}ElectronicMailAddress".format(self.ows_ns)).text = service_provider.get("contact_address_email", "")