Source code for svir.metadata.metadata_utilities

# coding=utf-8
"""
InaSAFE Disaster risk assessment tool developed by AusAid -
**Exception Classes.**

Custom exception classes for the IS application.

Contact : ole.moller.nielsen@gmail.com

.. note:: This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.

"""
from svir.utilities.defaults import get_defaults
from svir.utilities.utils import ReadMetadataError

__author__ = 'marco@opengis.ch'
__revision__ = '$Format:%H$'
__date__ = '12/10/2014'
__copyright__ = ('Copyright 2012, Australia Indonesia Facility for '
                 'Disaster Reduction')


import copy
import json
import os
import time

from pprint import pformat

from xml.etree import ElementTree

from svir.metadata.iso_19115_template import ISO_METADATA_XML_TEMPLATE

from svir.utilities.shared import DEBUG
from svir.utilities.utils import log_msg


# list of tags to get to the irmt project definition.
# this is stored in a list so it can be easily used in a for loop
ISO_METADATA_KEYWORD_NESTING = [
    '{http://www.isotc211.org/2005/gmd}identificationInfo',
    '{http://www.isotc211.org/2005/gmd}MD_DataIdentification',
    '{http://www.isotc211.org/2005/gmd}supplementalInformation',
    '{http://www.isotc211.org/2005/gco}CharacterString']

# flat xpath for the keyword container tag
ISO_METADATA_KEYWORD_TAG = '/'.join(ISO_METADATA_KEYWORD_NESTING)

ElementTree.register_namespace('gmi', 'http://www.isotc211.org/2005/gmi')
ElementTree.register_namespace('gco', 'http://www.isotc211.org/2005/gco')
ElementTree.register_namespace('gmd', 'http://www.isotc211.org/2005/gmd')
ElementTree.register_namespace('csw', 'http://www.opengis.net/cat/csw/2.0.2')
ElementTree.register_namespace('xsi',
                               'http://www.w3.org/2001/XMLSchema-instance')


# MONKEYPATCH CDATA support into Element tree
# inspired by http://stackoverflow.com/questions/174890/#answer-8915039
[docs]def CDATA(text=None): element = ElementTree.Element('![CDATA[') element.text = text return element
ElementTree._original_serialize_xml = ElementTree._serialize_xml def _serialize_xml(write, elem, encoding, qnames, namespaces): # log_msg("MONKEYPATCHED CDATA support into Element tree called") if elem.tag == '![CDATA[': write("\n<%s%s]]>\n" % (elem.tag, elem.text)) return return ElementTree._original_serialize_xml( write, elem, encoding, qnames, namespaces) ElementTree._serialize_xml = ElementTree._serialize['xml'] = _serialize_xml # END MONKEYPATCH CDATA
[docs]def generate_iso_metadata(supplemental_information=None): """Make a valid ISO 19115 XML using the values of get_defaults This method will create XML based on the iso_19115_template.py template The $placeholders there will be replaced by the values returned from defaults.get_defaults. Note that get_defaults takes care of using the values set in QGIS settings if available. :param supplemental_information: The supplemental information to write. :type supplemental_information: dict :return: str valid XML """ # get defaults from settings template_replacements = copy.copy(get_defaults()) # create runtime based replacement values template_replacements['ISO19115_TODAY_DATETIME'] = time.strftime( "%Y-%m-%dT%H:%M:%SZ") if supplemental_information is not None: if DEBUG: log_msg(pformat(supplemental_information, indent=4)) template_replacements['IRMT_SUPPLEMENTAL_INFORMATION'] = \ '<![CDATA[%s]]>' % json.dumps(supplemental_information, sort_keys=False, indent=2, separators=(',', ': ')) try: template_replacements['ISO19115_TITLE'] = \ supplemental_information['title'] except KeyError: pass try: template_replacements['ISO19115_ABSTRACT'] = \ supplemental_information['abstract'] except KeyError: pass try: template_replacements['ISO19115_LINEAGE'] = \ supplemental_information['source'] except KeyError: template_replacements['ISO19115_LINEAGE'] = '' try: template_replacements['ISO19115_ORGANIZATION'] = \ supplemental_information['organization'] except KeyError: template_replacements['ISO19115_ORGANIZATION'] = '' try: template_replacements['$ISO19115_EMAIL'] = \ supplemental_information['email'] except KeyError: template_replacements['ISO19115_EMAIL'] = '' try: template_replacements['ISO19115_LICENSE'] = \ 'licensed under ' + supplemental_information['license'] except KeyError: template_replacements['ISO19115_LICENSE'] = 'no license specified' try: template_replacements['$ISO19115_URL'] = \ supplemental_information['$ISO19115_URL'] except KeyError: template_replacements['ISO19115_URL'] = '' else: template_replacements['IRMT_SUPPLEMENTAL_INFORMATION'] = '' return ISO_METADATA_XML_TEMPLATE.safe_substitute(template_replacements)
[docs]def write_iso_metadata_file(xml_filename, supplemental_information=None): """ Make a valid ISO 19115 XML file using the values of defaults.get_defaults This method will create a file based on the iso_19115_template.py template The $placeholders there will be replaced by the values returned from defaults.get_defaults. Note that get_defaults takes care of using the values set in QGIS settings if available. :param xml_filename: full path to the file to be generated :return: """ xml = generate_iso_metadata(supplemental_information) with open(xml_filename, 'w') as f: f.write(xml) return xml
[docs]def valid_iso_xml(xml_filename): """add the necessary tags into an existing xml file or create a new one :param xml_filename: name of the xml file :return: tree the parsed ElementTree """ if os.path.isfile(xml_filename): #the file already has an xml file, we need to check it's structure tree = ElementTree.parse(xml_filename) root = tree.getroot() tag_str = '.' parent = root # Look for the correct nesting for tag in ISO_METADATA_KEYWORD_NESTING: tag_str += '/' + tag element = root.find(tag_str) if element is None: element = ElementTree.SubElement(parent, tag) parent = element else: # We create the XML from our template. # No more checks are needed since the template must be correct ;) write_iso_metadata_file(xml_filename) tree = ElementTree.parse(xml_filename) return tree
[docs]def get_supplemental_info(xml, prefix=None): # this raises a IOError if the file doesn't exist if os.path.isfile(xml): tree = ElementTree.parse(xml) root = tree.getroot() else: root = ElementTree.fromstring(xml) keyword_tag = prefix + ISO_METADATA_KEYWORD_TAG keyword_element = root.find(keyword_tag) # we have an xml file but it has no valid container if keyword_element is None: raise ReadMetadataError supplemental_info = json.loads(keyword_element.text) if DEBUG: log_msg("keyword_element.text: %s" % keyword_element.text) log_msg("Downloaded supplemental information:") log_msg(pformat(supplemental_info, indent=4)) return supplemental_info