From d7af1a85bd84d4f13f96fec36125b715cfde9590 Mon Sep 17 00:00:00 2001 From: Vincent Fazio Date: Fri, 26 Apr 2024 20:43:23 +1000 Subject: [PATCH] Funder should be in 'citedResponsibleParty' (#924) - Changed parsing of funder from 'pointOfContact' to 'citedResponsibleParty' - Added parsing of new roles - Added tests for new roles --- owslib/iso3.py | 67 ++++- .../iso3_examples/auscope-3d-model.xml | 247 +++++++++++++----- tests/test_iso3_parsing.py | 25 ++ 3 files changed, 261 insertions(+), 78 deletions(-) diff --git a/owslib/iso3.py b/owslib/iso3.py index 4e3689d8..730fd5f0 100644 --- a/owslib/iso3.py +++ b/owslib/iso3.py @@ -407,16 +407,24 @@ def __init__(self, namespaces, md=None): self.onlineresource = None self.role = None else: - val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:individual/cit:CI_Individual/cit:name/gco:CharacterString', self.namespaces)) + # Individual name + val = md.find(util.nspath_eval('cit:party/cit:CI_Individual/cit:name/gco:CharacterString', self.namespaces)) self.name = util.testXMLValue(val) + # Individual within organisation name + if self.name is None: + val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:individual/cit:CI_Individual/cit:name/gco:CharacterString', self.namespaces)) + self.name = util.testXMLValue(val) + + # Organisation name val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:name/gco:CharacterString', self.namespaces)) self.organization = util.testXMLValue(val) + # Individual within organisation position val = md.find(util.nspath_eval('cit:party/cit:CI_Organisation/cit:individual/cit:CI_Individual/cit:positionName/gco:CharacterString', self.namespaces)) self.position = util.testXMLValue(val) - # Telephone + # Organisation telephone val_list = md.xpath('cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:phone/cit:CI_Telephone[cit:numberType/cit:CI_TelephoneTypeCode/@codeListValue="voice"]/cit:number/gco:CharacterString', namespaces=self.namespaces) if len(val_list) > 0: self.phone = util.testXMLValue(val_list[0]) @@ -426,6 +434,7 @@ def __init__(self, namespaces, md=None): if len(val_list) > 0: self.fax = util.testXMLValue(val_list[0]) + # Organisation address val = md.find(util.nspath_eval( 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:deliveryPoint/gco:CharacterString', self.namespaces)) @@ -450,11 +459,13 @@ def __init__(self, namespaces, md=None): self.namespaces)) self.country = util.testXMLValue(val) + # Organisation email val = md.find(util.nspath_eval( 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:address/cit:CI_Address/cit:electronicMailAddress/gco:CharacterString', self.namespaces)) self.email = util.testXMLValue(val) + # Organisation online resources val = md.find(util.nspath_eval( 'cit:party/cit:CI_Organisation/cit:contactInfo/cit:CI_Contact/cit:onlineResource/cit:CI_OnlineResource', self.namespaces)) if val is not None: @@ -586,6 +597,7 @@ def __init__(self, namespaces, md=None, identtype=None): self.supplementalinformation = None self.spatialrepresentationtype = [] else: + # Title self.identtype = identtype val = md.find(util.nspath_eval( 'mri:citation/cit:CI_Citation/cit:title/gco:CharacterString', self.namespaces)) @@ -595,6 +607,7 @@ def __init__(self, namespaces, md=None, identtype=None): 'mri:citation/cit:CI_Citation/cit:alternateTitle/gco:CharacterString', self.namespaces)) self.alternatetitle = util.testXMLValue(val) + # Identifier self.uricode = [] for end_tag in ['gco:CharacterString', 'gcx:Anchor']: _values = md.findall(util.nspath_eval( @@ -613,12 +626,14 @@ def __init__(self, namespaces, md=None, identtype=None): if val is not None: self.uricodespace.append(val) + # Date self.date = [] self.datetype = [] for i in md.findall(util.nspath_eval('mri:citation/cit:CI_Citation/cit:date/cit:CI_Date', self.namespaces)): self.date.append(CI_Date(self.namespaces, i)) + # Use Limitation self.uselimitation = [] self.uselimitation_url = [] uselimit_values = md.findall(util.nspath_eval( @@ -628,6 +643,7 @@ def __init__(self, namespaces, md=None, identtype=None): if val is not None: self.uselimitation.append(val) + # Access constraints self.accessconstraints = [] for i in md.findall(util.nspath_eval( 'mri:resourceConstraints/mco:MD_LegalConstraints/mco:accessConstraints/mco:MD_RestrictionCode', @@ -636,8 +652,10 @@ def __init__(self, namespaces, md=None, identtype=None): if val is not None: self.accessconstraints.append(val) + # Classification self.classification = [] # Left empty - no legal classification equivalent + # Other constraints self.otherconstraints = [] for end_tag in ['gco:CharacterString', 'gcx:Anchor']: for i in md.findall(util.nspath_eval( @@ -647,6 +665,7 @@ def __init__(self, namespaces, md=None, identtype=None): if val is not None: self.otherconstraints.append(val) + # Security constraints self.securityconstraints = [] for i in md.findall(util.nspath_eval( 'mri:resourceConstraints/mco:MD_SecurityConstraints/mco:classification/mco:MD_ClassificationCode', @@ -655,6 +674,7 @@ def __init__(self, namespaces, md=None, identtype=None): if val is not None: self.securityconstraints.append(val) + # Use constraints self.useconstraints = [] for i in md.findall(util.nspath_eval( 'mri:resourceConstraints/mco:MD_LegalConstraints/mco:useConstraints/mco:MD_RestrictionCode', @@ -663,6 +683,7 @@ def __init__(self, namespaces, md=None, identtype=None): if val is not None: self.useconstraints.append(val) + # Spatial resolution denominators self.denominators = [] for i in md.findall(util.nspath_eval( 'mri:spatialResolution/mri:MD_Resolution/mri:equivalentScale/mri:MD_RepresentativeFraction/mri:denominator/gco:Integer', @@ -671,6 +692,7 @@ def __init__(self, namespaces, md=None, identtype=None): if val is not None: self.denominators.append(val) + # Spatial resolution distance and units of measure self.distance = [] self.uom = [] for i in md.findall(util.nspath_eval( @@ -680,12 +702,14 @@ def __init__(self, namespaces, md=None, identtype=None): self.distance.append(val) self.uom.append(i.get("uom")) + # Language code self.resourcelanguagecode = [] for i in md.findall(util.nspath_eval('mri:defaultLocale/lan:PT_Locale/lan:language/lan:LanguageCode', self.namespaces)): val = _testCodeListValue(i) if val is not None: self.resourcelanguagecode.append(val) + # Language self.resourcelanguage = [] for i in md.findall(util.nspath_eval('mri:defaultLocale/lan:PT_Locale/lan:language/gco:CharacterString', self.namespaces)): val = util.testXMLValue(i) @@ -696,23 +720,46 @@ def __init__(self, namespaces, md=None, identtype=None): self.publisher = [] self.contributor = [] self.funder = [] + # Extract roles from point of contact for resource for val in md.findall(util.nspath_eval('mri:pointOfContact/cit:CI_Responsibility', self.namespaces)): role = val.find(util.nspath_eval('cit:role/cit:CI_RoleCode', self.namespaces)) if role is not None: clv = _testCodeListValue(role) rp = CI_Responsibility(self.namespaces, val) - if clv == 'originator': + # Creator + if clv in ['originator', 'principalInvestigator', 'author']: self.creator.append(rp) + # Publisher elif clv == 'publisher': self.publisher.append(rp) - elif clv == 'author': + # Contributor + elif clv in ['collaborator', 'coAuthor', 'contributor', 'editor']: self.contributor.append(rp) - elif clv == 'funder': - self.funder.append(rp) + # Edition val = md.find(util.nspath_eval('cit:CI_Citation/cit:edition/gco:CharacterString', self.namespaces)) self.edition = util.testXMLValue(val) + # Extract roles from responsible party for resource + for val in md.findall(util.nspath_eval('mri:citation/cit:CI_Citation/cit:citedResponsibleParty/cit:CI_Responsibility', self.namespaces)): + role = val.find(util.nspath_eval('cit:role/cit:CI_RoleCode', self.namespaces)) + if role is not None: + clv = _testCodeListValue(role) + rp = CI_Responsibility(self.namespaces, val) + # Creator + if clv in ['originator', 'principalInvestigator', 'author']: + self.creator.append(rp) + # Publisher + elif clv == 'publisher': + self.publisher.append(rp) + # Contributor + elif clv in ['collaborator', 'coAuthor', 'contributor', 'processor', 'editor']: + self.contributor.append(rp) + # Funder + elif clv == 'funder': + self.funder.append(rp) + + # Abstract val = md.find(util.nspath_eval('mri:abstract/gco:CharacterString', self.namespaces)) self.abstract = util.testXMLValue(val) @@ -723,11 +770,14 @@ def __init__(self, namespaces, md=None, identtype=None): self.abstract = util.testXMLValue(val) self.abstract_url = val.attrib.get(util.nspath_eval('xlink:href', self.namespaces)) + # Purpose val = md.find(util.nspath_eval('mri:purpose/gco:CharacterString', self.namespaces)) self.purpose = util.testXMLValue(val) + # Status self.status = _testCodeListValue(md.find(util.nspath_eval('mri:status/mri:MD_ProgressCode', self.namespaces))) + # Graphic overview self.graphicoverview = [] for val in md.findall(util.nspath_eval( 'mri:graphicOverview/mcc:MD_BrowseGraphic/mcc:fileName/gco:CharacterString', self.namespaces)): @@ -736,11 +786,13 @@ def __init__(self, namespaces, md=None, identtype=None): if val2 is not None: self.graphicoverview.append(val2) + # Point of Contact self.contact = [] for i in md.findall(util.nspath_eval('mri:pointOfContact/cit:CI_Responsibility', self.namespaces)): o = CI_Responsibility(self.namespaces, i) self.contact.append(o) + # Spatial repreentation type self.spatialrepresentationtype = [] for val in md.findall(util.nspath_eval( 'mri:spatialRepresentationType/mcc:MD_SpatialRepresentationTypeCode', self.namespaces)): @@ -748,16 +800,19 @@ def __init__(self, namespaces, md=None, identtype=None): if val: self.spatialrepresentationtype.append(val) + # Keywords self.keywords = [] for mdkw in md.findall(util.nspath_eval('mri:descriptiveKeywords/mri:MD_Keywords', self.namespaces)): self.keywords.append(MD_Keywords(self.namespaces, mdkw)) + # Topic category self.topiccategory = [] for i in md.findall(util.nspath_eval('mri:topicCategory/mri:MD_TopicCategoryCode', self.namespaces)): val = util.testXMLValue(i) if val is not None: self.topiccategory.append(val) + # Supplamental information val = md.find(util.nspath_eval('mri:supplementalInformation/gco:CharacterString', self.namespaces)) self.supplementalinformation = util.testXMLValue(val) diff --git a/tests/resources/iso3_examples/auscope-3d-model.xml b/tests/resources/iso3_examples/auscope-3d-model.xml index 59ab1738..9f3dfa98 100644 --- a/tests/resources/iso3_examples/auscope-3d-model.xml +++ b/tests/resources/iso3_examples/auscope-3d-model.xml @@ -1,5 +1,5 @@ - + @@ -93,7 +93,7 @@ - 2022-11-03T06:17:02 + 2024-04-25T02:45:01 @@ -137,12 +137,171 @@ https://geology.data.vic.gov.au/searchAssistant/document.php?q=parent_id:107513 - - Reference - + Reference + + + + + + + + + AuScope + + + + + + + Level 2, 700 Swanston Street + + + Carlton + + + Victoria + + + 3053 + + + Australia + + + info@auscope.org.au + + + + + + + + + + https://ror.org/04s1m4564 + + + ROR + + + Research Organization Registry (ROR) Entry + + + + + + + + + + + + + + + + Earth Resources Victoria + + + + + + + 1300 366 356 + + + + + + + + + + GPO Box 2392 + + + Melbourne + + + Victoria + + + 3001 + + + Australia + + + customer.service@ecodev.vic.gov.au + + + + + + + + + + + + + + + + + + P.B. SKLADZIEN + + + + + + + + + + + + + + + C. Jorand + + + + + + + + + + + + + + A. Krassay + + + + + + + + + + + + + + L. Hall + + + + + @@ -168,16 +327,16 @@ The construction and integration of the basin model has involved both the interp - - - - -400 - - - 300 - - - + + + + -400 + + + 300 + + + @@ -226,62 +385,6 @@ The construction and integration of the basin model has involved both the interp - - - - - - - - - - AuScope - - - - - - - - - Level 2, 700 Swanston Street - - - - Carlton - - - Victoria - - - 3053 - - - Australia - - - info@auscope.org.au - - - - - - - - - https://ror.org/04s1m4564 - - - - Research Organization Registry (ROR) Entry - - - - - - - - @@ -338,4 +441,4 @@ The construction and integration of the basin model has involved both the interp - \ No newline at end of file + diff --git a/tests/test_iso3_parsing.py b/tests/test_iso3_parsing.py index eba56cdf..a1525281 100644 --- a/tests/test_iso3_parsing.py +++ b/tests/test_iso3_parsing.py @@ -475,13 +475,38 @@ def test_aus(amd): """ assert amd is not None ident = amd.identification[0] + # Test 3D - vertical extents assert ident.extent.vertExtMax == '300' assert ident.extent.vertExtMin == '-400' + # Test constraints & limitations assert ident.securityconstraints[0] == 'unclassified' assert ident.uselimitation[0] == 'https://creativecommons.org/licenses/by/4.0/' assert ident.accessconstraints[0] == 'license' assert ident.useconstraints[0] == 'license' + # Test funder assert ident.funder[0].organization == 'AuScope' + assert ident.funder[0].address == 'Level 2, 700 Swanston Street' + assert ident.funder[0].city == 'Carlton' + assert ident.funder[0].region == 'Victoria' + assert ident.funder[0].country == 'Australia' + assert ident.funder[0].postcode == '3053' + assert ident.funder[0].email == 'info@auscope.org.au' + # Test publisher + assert ident.publisher[0].organization == 'Earth Resources Victoria' + assert ident.publisher[0].phone == '1300 366 356' + assert ident.publisher[0].address == 'GPO Box 2392' + assert ident.publisher[0].city == 'Melbourne' + assert ident.publisher[0].region == 'Victoria' + assert ident.publisher[0].country == 'Australia' + assert ident.publisher[0].postcode == '3001' + assert ident.publisher[0].email == 'customer.service@ecodev.vic.gov.au' + # Test creator + assert ident.creator[0].name == 'P.B. SKLADZIEN' + # Test contributor + assert ident.contributor[0].name == 'C. Jorand' + assert ident.contributor[1].name == 'A. Krassay' + assert ident.contributor[2].name == 'L. Hall' + # Test uricode assert ident.uricode[0] == 'https://geology.data.vic.gov.au/searchAssistant/document.php?q=parent_id:107513'