Skip to content

Commit

Permalink
Minor improvements to ArcGIS and test coverage, and increment version.
Browse files Browse the repository at this point in the history
  • Loading branch information
dharvey-consbio committed Oct 20, 2016
1 parent 8db53bb commit 7c2a2ce
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 26 deletions.
32 changes: 26 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# gis_metadata_parser
Parser for GIS metadata standards including FGDC and ISO-19115.
Parser for GIS metadata standards including ArcGIS, FGDC and ISO-19115.

[![Build Status](https://travis-ci.org/consbio/gis-metadata-parser.png?branch=master)](https://travis-ci.org/consbio/gis-metadata-parser) [![Coverage Status](https://coveralls.io/repos/github/consbio/gis-metadata-parser/badge.svg?branch=master)](https://coveralls.io/github/consbio/gis-metadata-parser?branch=master)

Expand All @@ -10,13 +10,17 @@ Install with `pip install gis-metadata-parser`.

Parsers can be instantiated from files, XML strings or URLs. They can be converted from one standard to another as well.
```python
from gis_metadata.arcgis_metadata_parser import ArcGISParser
from gis_metadata.fgdc_metadata_parser import FgdcParser
from gis_metadata.iso_metadata_parser import IsoParser
from gis_metadata.metadata_parser import get_metadata_parser

# From file objects
fgdc_from_file = FgdcParser(file(r'/path/to/metadata.xml'))
iso_from_file = IsoParser(file(r'/path/to/metadata.xml'))
with open(r'/path/to/metadata.xml') as metadata:
fgdc_from_file = FgdcParser(metadata)

with open(r'/path/to/metadata.xml') as metadata:
iso_from_file = IsoParser(metadata)

# Detect standard based on root element, metadata
fgdc_from_string = get_metadata_parser(
Expand All @@ -29,7 +33,19 @@ fgdc_from_string = get_metadata_parser(
"""
)

# Detect standard based on root element, MD_Metadata or MI_Metadata
# Detect ArcGIS standard based on root element and its nodes
iso_from_string = get_metadata_parser(
"""
<?xml version='1.0' encoding='UTF-8'?>
<metadata>
<dataIdInfo/></dataIdInfo>
<distInfo/></distInfo>
<dqInfo/></dqInfo>
</metadata>
"""
)

# Detect ISO standard based on root element, MD_Metadata or MI_Metadata
iso_from_string = get_metadata_parser(
"""
<?xml version='1.0' encoding='UTF-8'?>
Expand All @@ -43,18 +59,22 @@ iso_from_string = get_metadata_parser(
# Convert from one standard to another
fgdc_converted = iso_from_file.convert_to(FgdcParser)
iso_converted = fgdc_from_file.convert_to(IsoParser)
arcgis_converted = iso_converted.convert_to(ArcGISParser)
```

Finally, the properties of the parser can be updated, validated, applied and output:
```python
fgdc_from_file = FgdcParser(file(r'/path/to/metadata.xml'))
with open(r'/path/to/metadata.xml') as metadata:
fgdc_from_file = FgdcParser(metadata)

# Example simple properties
fgdc_from_file.title
fgdc_from_file.abstract
fgdc_from_file.place_keywords
fgdc_from_file.thematic_keywords

# :see: gis_metadata.utils.get_supported_props for list of all supported properties

# Complex properties
fgdc_from_file.attributes
fgdc_from_file.bounding_box
Expand All @@ -63,7 +83,7 @@ fgdc_from_file.digital_forms
fgdc_from_file.larger_works
fgdc_from_file.process_steps

# :see: gis_metadata.utils.get_supported_props for list of all supported properties
# :see: gis_metadata.utils.get_complex_definitions for structure of all complex properties

# Update properties
fgdc_from_file.title = 'New Title'
Expand Down
9 changes: 4 additions & 5 deletions gis_metadata/arcgis_metadata_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@


ARCGIS_ROOTS = ('metadata', 'Metadata')
ARCGIS_NODES = ('dataIdInfo', 'distInfo', 'dqInfo')
ARCGIS_NODES = ('dataIdInfo', 'distInfo', 'dqInfo', 'Esri')

_agis_definitions = get_complex_definitions()

Expand Down Expand Up @@ -261,12 +261,12 @@ def _parse_digital_forms(self, prop=DIGITAL_FORMS):
def _parse_report_item(self, prop):
""" :return: the text for each element at the configured path if type attribute matches"""

item_type = None

if prop == 'attribute_accuracy':
item_type = 'DQQuanAttAcc'
elif prop == 'dataset_completeness':
item_type = 'DQCompOm'
else:
return u''

xroot = self._get_xroot_for(prop)

Expand Down Expand Up @@ -351,13 +351,12 @@ def _update_report_item(self, **update_props):
xroot = self._get_xroot_for(prop)

attr_key = 'type'
attr_val = u''

if prop == 'attribute_accuracy':
attr_val = 'DQQuanAttAcc'
elif prop == 'dataset_completeness':
attr_val = 'DQCompOm'
else:
return []

# Clear (make empty) all elements of the appropriate type
for elem in get_elements(tree_to_update, xroot):
Expand Down
10 changes: 6 additions & 4 deletions gis_metadata/metadata_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,19 @@ def get_metadata_parser(metadata_container, **metadata_defaults):

# The get_parsed_content method ensures only these roots will be returned

parser = None

if xml_root in ISO_ROOTS:
return IsoParser(xml_tree, **metadata_defaults)
parser = IsoParser(xml_tree, **metadata_defaults)
else:
has_arcgis_data = any(element_exists(xml_tree, e) for e in ARCGIS_NODES)

if xml_root == FGDC_ROOT and not has_arcgis_data:
return FgdcParser(xml_tree, **metadata_defaults)
parser = FgdcParser(xml_tree, **metadata_defaults)
elif xml_root in ARCGIS_ROOTS:
return ArcGISParser(xml_tree, **metadata_defaults)
parser = ArcGISParser(xml_tree, **metadata_defaults)

return None
return parser


def get_parsed_content(metadata_content):
Expand Down
2 changes: 0 additions & 2 deletions gis_metadata/tests/data/test_arcgis.xml

This file was deleted.

50 changes: 42 additions & 8 deletions gis_metadata/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

from parserutils.collections import wrap_value
from parserutils.elements import element_exists, element_to_dict, element_to_string
from parserutils.elements import get_element_text, get_elements, get_remote_element
from parserutils.elements import clear_element, remove_element, remove_element_attributes, set_element_attributes
from parserutils.elements import clear_element, get_element_text, get_elements, get_remote_element
from parserutils.elements import insert_element, remove_element, remove_element_attributes, set_element_attributes

from gis_metadata.arcgis_metadata_parser import ArcGISParser, ARCGIS_NODES, ARCGIS_ROOTS
from gis_metadata.fgdc_metadata_parser import FgdcParser, FGDC_ROOT
Expand Down Expand Up @@ -288,15 +288,12 @@ def assert_parser_after_write(self, parser_type, in_file, out_file_path, use_tem
with open(out_file_path) as out_file:
self.assert_parsers_are_equal(parser, parser_type(out_file))

def assert_valid_parser(self, parser, root=None):
def assert_valid_parser(self, parser):

parser_type = type(parser.validate()).__name__

self.assertIsNotNone(parser._xml_root, '{0} root not set'.format(parser_type))

if root is not None:
self.assertEqual(parser._xml_root, root)

self.assertIsNotNone(parser._xml_tree)
self.assertEqual(parser._xml_tree.getroot().tag, parser._xml_root)

Expand Down Expand Up @@ -517,12 +514,30 @@ def test_generic_parser(self):
""" Covers code that enforces certain behaviors for custom parsers """

parser = MetadataParser()
prop_get = '{0}'.format
prop_set = '{xpaths}'.format

with self.assertRaises(ParserError):
# Un-callable property parser (no xpath)
ParserProperty(None, None)

with self.assertRaises(ParserError):
ParserProperty(None, None) # Un-callable property parser
# Un-callable property parser (no xpath)
ParserProperty(None, prop_set)

with self.assertRaises(ParserError):
ParserProperty(list, None) # Un-callable property updater
# Un-callable property updater
ParserProperty(prop_get, None)

parser_prop = ParserProperty(None, prop_set, 'path')
with self.assertRaises(ParserError):
# Un-callable property parser with xpath
parser_prop.get_prop('prop')

parser_prop = ParserProperty(prop_get, prop_set, 'path')
self.assertEqual(parser_prop.get_prop('prop'), 'prop')
self.assertEqual(parser_prop.set_prop(), 'path')
self.assertEqual(parser_prop.set_prop(xpaths='diff'), 'path')

data_map_1 = parser._data_map
parser._init_data_map()
Expand Down Expand Up @@ -565,6 +580,25 @@ def test_arcgis_parser(self):
arcgis_parser = ArcGISParser(element_to_string(arcgis_element))
self.assertEqual(arcgis_parser.dates, {'type': 'range', 'values': ['Date Range Start', 'Date Range End']})

# Remove one of the date range values and assert that only the end date is read in as a single
remove_element(arcgis_element, 'dataIdInfo/dataExt/tempEle/TempExtent/exTemp/TM_Period/tmBegin', True)
arcgis_parser = ArcGISParser(element_to_string(arcgis_element))
self.assertEqual(arcgis_parser.dates, {'type': 'single', 'values': ['Date Range End']})

# Remove the last of the date range values and assert that no dates are read in
remove_element(arcgis_element, 'dataIdInfo/dataExt/tempEle/TempExtent/exTemp/TM_Period', True)
arcgis_parser = ArcGISParser(element_to_string(arcgis_element))
self.assertEqual(arcgis_parser.dates, {})

# Insert a single date value and assert that only it is read in

single_path = 'dataIdInfo/dataExt/tempEle/TempExtent/exTemp/TM_Instant/tmPosition'
single_text = 'Single Date'
insert_element(arcgis_element, 0, single_path, single_text)

arcgis_parser = ArcGISParser(element_to_string(arcgis_element))
self.assertEqual(arcgis_parser.dates, {'type': 'single', 'values': [single_text]})

def test_fgdc_parser(self):
""" Tests behavior unique to the FGDC parser """

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def run(self):
name='gis_metadata_parser',
description='Parser for GIS metadata standards including FGDC and ISO-19115',
keywords='fgdc,iso,ISO-19115,ISO-19139,metadata,xml,parser',
version='0.8.0',
version='0.9.0',
packages=[
'gis_metadata', 'gis_metadata.tests'
],
Expand Down

0 comments on commit 7c2a2ce

Please sign in to comment.