def _create_additional_records(self, service: Service, metadata: Metadata, group: MrMapGroup): """ Creates additional records like linked service metadata, keywords or MimeTypes/Formats Args: service (Service): The service record metadata (Metadata): THe metadata record Returns: """ # Keywords for kw in self.service_identification_keywords: if kw is None: continue keyword = Keyword.objects.get_or_create(keyword=kw)[0] metadata.keywords.add(keyword) # MimeTypes / Formats for operation, formats in self.operation_format_map.items(): for format in formats: mime_type = MimeType.objects.get_or_create(operation=operation, mime_type=format, created_by=group)[0] metadata.formats.add(mime_type) # Check for linked service metadata that might be found during parsing if self.linked_service_metadata is not None: service.linked_service_metadata = self.linked_service_metadata.to_db_model( MetadataEnum.SERVICE.value, created_by=metadata.created_by) metadata.add_metadata_relation( to_metadata=service.linked_service_metadata, relation_type=MetadataRelationEnum.VISUALIZES.value, origin=ResourceOriginEnum.CAPABILITIES.value)
def _add_iso_metadata(metadata: Metadata, md_links: list, existing_iso_links: list): """ Adds iso metadata that is found in the newer md_links list but not 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 """ # iterate over all links from the form and check if we need to persist them for link in md_links: if len(link) == 0: continue # check if this is already an existing uri and skip if so if link in existing_iso_links: continue # ... otherwise create a new iso metadata object iso_md = ISOMetadata(link, ResourceOriginEnum.EDITOR.value) iso_md = iso_md.to_db_model(created_by=metadata.created_by) iso_md.save() metadata.add_metadata_relation( to_metadata=iso_md, origin=iso_md.origin, relation_type=MetadataRelationEnum.DESCRIBES.value)
def done(self, form_list, **kwargs): """ Iterates over all forms and fills the Metadata/Dataset records accordingly Args: form_list (FormList): An iterable list of forms kwargs: Returns: """ # Create instances self.metadata = Metadata() self.metadata.metadata_type = MetadataEnum.DATASET.value self.metadata.is_active = True self.dataset = Dataset() self.dataset.is_active = True self.dataset.md_identifier_code = self.metadata.identifier self.dataset.metadata_standard_name = "ISO 19115 Geographic information - Metadata" self.dataset.metadata_standard_version = "ISO 19115:2003(E)" # Pre-save objects to be able to add M2M relations self.metadata.save() self.metadata.identifier = self.metadata.id self.dataset.metadata = self.metadata self.dataset.save() self.metadata.metadata_url = reverse("resource:get-dataset-metadata", args=(self.dataset.id, )) return super().done(form_list=form_list, **kwargs)
def _fill_metadata_dataset_identification_form(data: dict, metadata: Metadata, dataset: Dataset, user: MrMapUser): """ Fills form data into Metadata/Dataset records Args: data (dict): Cleaned form data metadata (dict): The metadata record dataset (dict): The dataset record user: The performing user Returns: """ metadata.title = data.get("title", None) metadata.abstract = data.get("abstract", None) metadata.created = data.get("date_stamp", None) metadata.created_by = data.get("created_by", None) dataset.language_code = data.get("language_code", None) dataset.character_set_code = data.get("character_set_code", None) dataset.date_stamp = data.get("date_stamp", None) ref_systems = data.get("reference_system", []) metadata.reference_system.clear() for ref_system in ref_systems: metadata.reference_system.add(ref_system) additional_related_objects = data.get("additional_related_objects", []) MetadataRelation.objects.filter(to_metadata=metadata, origin=ResourceOriginEnum.EDITOR.value).delete() for additional_object in additional_related_objects: additional_object.add_metadata_relation(to_metadata=metadata, relation_type=MetadataRelationEnum.DESCRIBES.value, internal=True, origin=ResourceOriginEnum.EDITOR.value)
def _fill_metadata_dataset_licence_form(data: dict, metadata: Metadata, dataset: Dataset, user: MrMapUser): """ Fills form data into Metadata/Dataset records Args: data (dict): Cleaned form data metadata (dict): The metadata record dataset (dict): The dataset record user: The performing user Returns: """ metadata.licence = data.get("licence", None) metadata.access_constraints = data.get("access_constraints", None)
def to_db_model(self, type=MetadataEnum.DATASET.value, created_by: MrMapGroup = None): """ Get corresponding metadata object from database or create it if not found! Returns: metadata (Metadata): A db model Metadata object """ update = False new = False # try to find the object by uuid and uri. If not existing yet, create a new record try: metadata = Metadata.objects.get(identifier=self.file_identifier, metadata_url=self.uri) # check if the parsed metadata might be newer # make sure both date time objects will be comparable persisted_change = metadata.last_remote_change.replace(tzinfo=utc) new_change = self.last_change_date.replace(tzinfo=utc) if persisted_change > new_change: # Nothing to do here return metadata else: update = True except ObjectDoesNotExist: # object does not seem to exist -> create it! metadata = Metadata() md_type = type metadata.metadata_type = md_type if metadata.is_dataset_metadata: metadata.dataset = Dataset() metadata.dataset.created_by = created_by metadata.created_by = created_by new = True if update or new: # In case of a dataset, we need to fill the information into the dataset object if metadata.is_dataset_metadata: metadata.dataset = self._fill_dataset_db_model( metadata.dataset) metadata = self._fill_metadata_db_model(metadata) metadata.save() metadata.dataset.save() orig_document = Document.objects.get_or_create( metadata=metadata, document_type=DocumentEnum.METADATA.value, is_original=True, )[0] orig_document.content = self.raw_metadata orig_document.save() if update: metadata.keywords.clear() for kw in self.keywords: keyword = Keyword.objects.get_or_create(keyword=kw)[0] metadata.keywords.add(keyword) return metadata
def _create_dublin_core_summary_elem(self, md: Metadata): """ Creates the SummaryRecord in Dublin core syntax Args: md (Metadata): The metadata object providing the data Returns: elem (_Element): The lxml element """ record_elem = Element( "{}SummaryRecord".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 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["{}abstract".format(self.dct_ns)] = md.abstract # Create xml elements from mapped information self._create_xml_from_map(record_elem, attribute_element_map) return record_elem
def get_table(self, **kwargs): # set some custom attributes for template rendering table = super(WmsIndexView, self).get_table(**kwargs) # whether whole services or single layers should be displayed, we have to exclude some columns filter_by_show_layers = self.filterset.form_prefix + '-' + 'service__is_root' if filter_by_show_layers in self.filterset.data and self.filterset.data.get( filter_by_show_layers) == 'on': table.exclude = ( 'layers', 'featuretypes', 'harvest_results', 'collected_harvest_records', 'harvest_duration', ) else: table.exclude = ( 'parent_service', 'featuretypes', 'harvest_results', 'collected_harvest_records', 'harvest_duration', ) render_helper = RenderHelper(user_permissions=list( filter(None, self.request.user.get_all_permissions()))) table.actions = [ render_helper.render_item(item=Metadata.get_add_resource_action()) ] return table
def create_metadata_elem(self, returned_md: Metadata): """ Returns existing service/dataset metadata as xml elements Args: returned_md (Metadata): The processing metadata Returns: xml (Element): The xml element """ if returned_md.is_dataset_metadata: doc = Document.objects.get( metadata=returned_md, document_type=DocumentEnum.METADATA.value, ) xml = doc.content else: xml = returned_md.get_service_metadata_xml() xml = xml_helper.parse_xml(xml) xml = xml_helper.try_get_single_element_from_xml( xml_elem=xml, elem="//" + GENERIC_NAMESPACE_TEMPLATE.format("MD_Metadata")) # Reduce the amount of information returned based on the requested elementSetName parameter xml = self.reduce_information(xml) return xml
def _fill_metadata_dataset_responsible_party_form(data: dict, metadata: Metadata, dataset: Dataset, user: MrMapUser): """ Fills form data into Metadata/Dataset records Args: data (dict): Cleaned form data metadata (dict): The metadata record dataset (dict): The dataset record user: The performing user Returns: """ # Check on an existing organization org = data.get("organization") if org is None: # A new org has to be created with minimal contact details org = Organization.objects.get_or_create( organization_name=data.get("organization_name"), is_auto_generated=True, person_name=data.get("person_name"), phone=data.get("phone"), email=data.get("mail"), facsimile=data.get("facsimile"), created_by=user, )[0] metadata.contact = org
def _fill_metadata_dataset_spatial_extent_form(data: dict, metadata: Metadata, dataset: Dataset, user: MrMapUser): """ Fills form data into Metadata/Dataset records Args: data (dict): Cleaned form data metadata (dict): The metadata record dataset (dict): The dataset record user: The performing user Returns: """ try: bounding_geometry = json.loads(data.get("bounding_geometry", "{}")) except JSONDecodeError: bounding_geometry = {} if bounding_geometry.get("features", None) is not None: # A list of features geoms = [GEOSGeometry(str(feature["geometry"]), srid=DEFAULT_SRS) for feature in bounding_geometry.get("features")] geom = GeometryCollection(geoms, srid=DEFAULT_SRS).unary_union elif bounding_geometry.get("feature", None) is not None: geom = GEOSGeometry(str(bounding_geometry.get("feature")["geometry"]), srid=DEFAULT_SRS) else: try: geom = GEOSGeometry(str(bounding_geometry), srid=DEFAULT_SRS) except Exception: # No features provided return metadata.bounding_geometry = geom
def _overwrite_capabilities_iso_metadata_links(xml_obj: _Element, metadata: Metadata): """ Overwrites links in capabilities document Args: xml_obj (_Element): The xml_object of the document metadata (Metadata): The metadata object, holding the data Returns: """ # get list of all iso md links that really exist (from the metadata object) iso_md_links = metadata.get_related_metadata_uris() # get list of all MetadataURL elements from the capabilities element xml_links = xml_helper.try_get_element_from_xml("./MetadataURL", xml_obj) for xml_link in xml_links: xml_online_resource_elem = xml_helper.try_get_element_from_xml( "./OnlineResource", xml_link) xml_link_attr = xml_helper.try_get_attribute_from_xml_element( xml_online_resource_elem, "xlink:href") if xml_link_attr in iso_md_links: # we still use this, so we are good # Remove this link from iso_md_links to get an overview of which links are left over in the end # These links must be new then! iso_md_links.remove(xml_link_attr) continue else: # this does not seem to exist anymore -> remove it from the xml xml_helper.remove_element(xml_link) # what is left over in iso_md_links are new links that must be added to the capabilities doc for new_link in iso_md_links: xml_helper.add_iso_md_element(xml_obj, new_link)
def _fill_form_list(form_list, metadata: Metadata, dataset: Dataset, user: MrMapUser): """ Iterates over all forms and applies the metadata changes on the objects Args: form_list: The list of forms metadata: The metadata record dataset: The dataset record user: The performing user Returns: """ function_map = { "DatasetIdentificationForm": DatasetWizard._fill_metadata_dataset_identification_form, "DatasetResponsiblePartyForm": DatasetWizard._fill_metadata_dataset_responsible_party_form, "DatasetClassificationForm": DatasetWizard._fill_metadata_dataset_classification_form, "DatasetSpatialExtentForm": DatasetWizard._fill_metadata_dataset_spatial_extent_form, "DatasetLicenseConstraintsForm": DatasetWizard._fill_metadata_dataset_licence_form, "DatasetQualityForm": DatasetWizard._fill_metadata_dataset_quality_form, } for form in form_list: form_class = type(form).__name__ function_map[form_class](form.cleaned_data, metadata, dataset, user) dataset.save() metadata.is_custom = True metadata.save() try: doc = Document.objects.get( metadata__id=metadata.id, document_type=DocumentEnum.METADATA.value, is_original=False, ) doc.is_active = metadata.is_active DatasetWizard._overwrite_dataset_document(metadata, doc) except ObjectDoesNotExist: DatasetWizard._create_dataset_document(metadata)
class NewDatasetWizard(PermissionRequiredMixin, DatasetWizard): permission_required = PermissionEnum.CAN_ADD_DATASET_METADATA.value raise_exception = True permission_denied_message = NO_PERMISSION def __init__(self, *args, **kwargs): super().__init__(action_url=reverse( 'editor:dataset-metadata-wizard-new', ), title=_(format_html('<b>Add New Dataset</b>')), *args, **kwargs) def get_form_kwargs(self, step=None): return {'request': self.request} def done(self, form_list, **kwargs): """ Iterates over all forms and fills the Metadata/Dataset records accordingly Args: form_list (FormList): An iterable list of forms kwargs: Returns: """ # Create instances self.metadata = Metadata() self.metadata.metadata_type = MetadataEnum.DATASET.value self.metadata.is_active = True self.dataset = Dataset() self.dataset.is_active = True self.dataset.md_identifier_code = self.metadata.identifier self.dataset.metadata_standard_name = "ISO 19115 Geographic information - Metadata" self.dataset.metadata_standard_version = "ISO 19115:2003(E)" # Pre-save objects to be able to add M2M relations self.metadata.save() self.metadata.identifier = self.metadata.id self.dataset.metadata = self.metadata self.dataset.save() self.metadata.metadata_url = reverse("resource:get-dataset-metadata", args=(self.dataset.id, )) return super().done(form_list=form_list, **kwargs)
def _create_additional_records(self, metadata: Metadata, layer: Layer, group: MrMapGroup, epsg_api: EpsgApi): """ Creates additional records such as Keywords, ReferenceSystems, Dimensions, ... Args: metadata (Metadata): The layer's metadata object layer (Layer): The Layer record object group (MrMapGroup): The owner/creator group epsg_api (EpsgApi): A epsg_api object Returns: """ # Keywords for kw in self.capability_keywords: keyword = Keyword.objects.get_or_create(keyword=kw)[0] metadata.keywords.add(keyword) # handle reference systems for sys in self.capability_projection_system: parts = epsg_api.get_subelements(sys) # check if this srs is allowed for us. If not, skip it! if parts.get("code") not in ALLOWED_SRS: continue ref_sys = ReferenceSystem.objects.get_or_create( code=parts.get("code"), prefix=parts.get("prefix"))[0] metadata.reference_system.add(ref_sys) for iso_md in self.iso_metadata: iso_md = iso_md.to_db_model(created_by=group) metadata.add_metadata_relation( to_metadata=iso_md, relation_type=MetadataRelationEnum.DESCRIBES.value, origin=iso_md.origin) # Dimensions for dimension in self.dimension_list: dim = Dimension.objects.get_or_create( type=dimension.get("type"), units=dimension.get("units"), extent=dimension.get("extent"), )[0] layer.metadata.dimensions.add(dim)
def get_table(self, **kwargs): # set some custom attributes for template rendering table = super(DatasetIndexView, self).get_table(**kwargs) render_helper = RenderHelper(user_permissions=list( filter(None, self.request.user.get_all_permissions())), update_url_qs=get_current_view_args( self.request)) table.actions = [ render_helper.render_item(item=Metadata.get_add_dataset_action()) ] return table
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 _create_service_record(self, group: MrMapGroup, orga_published_for: Organization, md: Metadata, is_update_candidate_for: Service): """ Creates a Service object from the OGCWebFeatureService object Args: group (MrMapGroup): The owner/creator group orga_published_for (Organization): The organization for which the service is published orga_publisher (Organization): THe organization that publishes md (Metadata): The describing metadata Returns: service (Service): The persisted service object """ service = Service() service_type = ServiceType.objects.get_or_create( name=self.service_type.value.lower(), version=self.service_version.value)[0] service.service_type = service_type service.created_by = group service.published_for = orga_published_for service.availability = 0.0 service.is_available = False service.is_root = True md.service = service service.is_update_candidate_for = is_update_candidate_for # Save record to enable M2M relations service.save() operation_urls = [] for operation, parsed_operation_url, method in self.operation_urls: # todo: optimize as bulk create try: operation_urls.append( ServiceUrl.objects.get_or_create(operation=operation, url=getattr( self, parsed_operation_url), method=method)[0]) except IntegrityError: # empty/None url values will be ignored pass service.operation_urls.add(*operation_urls) # Persist capabilities document service.persist_original_capabilities_doc( self.service_capabilities_xml) return service
def get_table(self, **kwargs): # set some custom attributes for template rendering table = super(CswIndexView, self).get_table(**kwargs) table.exclude = ('parent_service', 'layers', 'featuretypes', 'service__published_for') render_helper = RenderHelper(user_permissions=list( filter(None, self.request.user.get_all_permissions())), update_url_qs=get_current_view_args( self.request)) table.actions = [ render_helper.render_item(item=Metadata.get_add_resource_action()) ] return table
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 get_table(self, **kwargs): # set some custom attributes for template rendering table = super(WfsIndexView, self).get_table(**kwargs) table.exclude = ('parent_service', 'layers', 'harvest_results', 'collected_harvest_records', 'harvest_duration') render_helper = RenderHelper(user_permissions=list( filter(None, self.request.user.get_all_permissions())), update_url_qs=get_current_view_args( self.request)) table.actions = [ render_helper.render_item(item=Metadata.get_add_resource_action()) ] return table
def _fill_metadata_dataset_spatial_extent_form(data: dict, metadata: Metadata, dataset: Dataset, user: MrMapUser): """ Fills form data into Metadata/Dataset records Args: data (dict): Cleaned form data metadata (dict): The metadata record dataset (dict): The dataset record user: The performing user Returns: """ metadata.bounding_geometry = data.get("bounding_geometry", None)
def collect_metadata_related_objects(md: Metadata, request: HttpRequest,): params = {} # get all related Metadata objects from type dataset related_metadatas = md.get_related_dataset_metadatas() if related_metadatas: # build django tables2 table related_metadatas_table = CoupledMetadataTable( queryset=related_metadatas, order_by='title', show_header=True, request=request, param_lead='rm-t') params['related_metadatas'] = related_metadatas return params
def resolve_iso_metadata_links(metadata: Metadata, editor_form): """ Iterate over all provided iso metadata links and create metadata from it which will be related to the metadata Args: metadata (Metadata): The edited metadata editor_form: The editor form Returns: nothing """ # iterate over iso metadata links and create IsoMetadata objects md_links = editor_form.data.get("iso_metadata_url", "").split(",") # create list of all persisted and related iso md links existing_iso_links = metadata.get_related_metadata_uris() try: _remove_iso_metadata(metadata, md_links, existing_iso_links) _add_iso_metadata(metadata, md_links, existing_iso_links) except MissingSchema as e: editor_logger.error(msg=EDITOR_INVALID_ISO_LINK.format(e.link)) editor_logger.exception(e, exc_info=True, stack_info=True) except Exception as e: editor_logger.exception(e, exc_info=True, stack_info=True)
def overwrite_metadata(original_md: Metadata, custom_md: Metadata, editor_form): """ Overwrites the original data with the custom date Args: original_md (Metadata): The original Metadata object custom_md (Metadata): The custom Metadata object editor_form: The editor form which holds additional data Returns: nothing """ original_md.title = custom_md.title original_md.abstract = custom_md.abstract original_md.access_constraints = custom_md.access_constraints # we need the metadata_url to reset dataset metadatas # original_md.metadata_url = custom_md.metadata_url original_md.licence = custom_md.licence # get db objects from values # Keyword updating keywords = editor_form.cleaned_data["keywords"] original_md.keywords.clear() for kw in keywords: keyword = Keyword.objects.get_or_create(keyword=kw)[0] original_md.keywords.add(keyword) # Language updating original_md.language_code = editor_form.cleaned_data["language_code"] # Categories updating # Categories are provided as id's to prevent language related conflicts try: categories = editor_form.cleaned_data["categories"] original_md.categories.clear() for category in categories: original_md.categories.add(category) except KeyError: pass # Categories are inherited by subelements subelements = original_md.get_described_element().get_subelements( ).select_related('metadata') for subelement in subelements: subelement.metadata.categories.clear() for category in categories: subelement.metadata.categories.add(category) # change capabilities document so that all sensitive elements (links) are proxied if original_md.use_proxy_uri != custom_md.use_proxy_uri: if custom_md.use_proxy_uri == 'on': original_md.set_proxy(True) else: original_md.set_proxy(False) # save metadata original_md.is_custom = True original_md.save() if original_md.is_dataset_metadata: overwrite_dataset_metadata_document(original_md) else: overwrite_capabilities_document(original_md)
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 _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 _create_metadata_record(self, parent_service: Service, group: MrMapGroup): """ Creates a Metadata record from the OGCLayer object Args: self (OGCLayer): The OGCLayer object (result of parsing) parent_service (Service): The parent Service object group (MrMapGroup): The creator/owner group Returns: metadata (Metadata): The persisted metadata object """ metadata = Metadata() md_type = MetadataEnum.LAYER.value metadata.metadata_type = md_type metadata.title = self.title metadata.abstract = self.abstract metadata.online_resource = parent_service.metadata.online_resource metadata.capabilities_original_uri = parent_service.metadata.capabilities_original_uri metadata.identifier = self.identifier metadata.contact = parent_service.metadata.contact metadata.access_constraints = parent_service.metadata.access_constraints metadata.is_active = False metadata.created_by = group # Save metadata to use id afterwards metadata.save() # create bounding box polygon bounding_points = ((float(self.capability_bbox_lat_lon["minx"]), float(self.capability_bbox_lat_lon["miny"])), (float(self.capability_bbox_lat_lon["minx"]), float(self.capability_bbox_lat_lon["maxy"])), (float(self.capability_bbox_lat_lon["maxx"]), float(self.capability_bbox_lat_lon["maxy"])), (float(self.capability_bbox_lat_lon["maxx"]), float(self.capability_bbox_lat_lon["miny"])), (float(self.capability_bbox_lat_lon["minx"]), float(self.capability_bbox_lat_lon["miny"]))) metadata.bounding_geometry = Polygon(bounding_points) metadata.save() return metadata
def _fill_metadata_db_model(self, metadata: Metadata): """ Fills a Metadata db record from the ISOMetadata data Args: metadata (Metadata): The old/empty object Returns: metadata (Metadata): The metadata object """ metadata.identifier = self.file_identifier metadata.abstract = self.abstract metadata.access_constraints = self.access_constraints # Take the polygon with the largest area as bounding geometry if len(self.polygonal_extent_exterior) > 0: max_area_poly = None for poly in self.polygonal_extent_exterior: if max_area_poly is None: max_area_poly = poly if max_area_poly.area < poly.area: max_area_poly = poly metadata.bounding_geometry = max_area_poly try: metadata.contact = Organization.objects.get_or_create( organization_name=self.responsible_party, email=self.contact_email, )[0] except MultipleObjectsReturned: # okay, we need to create a unique organization # "unique" since it will only be identified using organization_name and email metadata.contact = Organization.objects.get_or_create( organization_name="{}#1".format(self.responsible_party), email=self.contact_email, )[0] metadata.is_inspire_conform = self.inspire_interoperability metadata.metadata_url = self.uri metadata.last_remote_change = self.last_change_date metadata.spatial_res_type = self.spatial_res_type metadata.spatial_res_value = self.spatial_res_val if self.title is None: self.title = "BROKEN" metadata.title = self.title metadata.origin = self.origin metadata.is_broken = self.is_broken metadata.save() # save legal dates and reports for report in self.legal_reports: report.date.save() report.save() metadata.legal_reports.add(report) for date in self.legal_dates: date.save() metadata.legal_dates.add(date) metadata.save() return metadata
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