Coverage for gws-app/gws/plugin/ows_server/csw/templates/iso/record.py: 94%

68 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-05 13:35 +0200

1"""CSW record builder for the ISO profile (``gmd:MD_Metadata``).""" 

2 

3import gws 

4import gws.base.ows.server as server 

5import gws.base.ows.server.templatelib as tpl 

6from gws.lib.xmlx import tag 

7 

8ML_GMX_CODELISTS = 'http://standards.iso.org/iso/19139/resources/gmxCodelists.xml' 

9 

10 

11def record(ta: server.TemplateArgs, md: gws.Metadata): 

12 """Create a ``gmd:MD_Metadata`` element for a metadata object. 

13 

14 Args: 

15 ta: Template arguments. 

16 md: Metadata of the catalog record. 

17 

18 Returns: 

19 The XML element. 

20 """ 

21 def w_code(wrap, lst, value, text=None): 

22 return tag( 

23 f'GMD:{wrap}/GMD:{lst}', 

24 {'codeList': ML_GMX_CODELISTS + '#' + lst, 'codeListValue': value}, 

25 text or value, 

26 ) 

27 

28 def w_date(d, typ): 

29 return tag( 

30 'GMD:date/GMD:CI_Date', 

31 tag('GMD:date/GCO:Date', tpl.iso_date(d)), 

32 w_code( 

33 'dateType', 

34 'CI_DateTypeCode', 

35 typ, 

36 ), 

37 ) 

38 

39 def w_lang(): 

40 return tag( 

41 'GMD:language/GMD:LanguageCode', 

42 {'codeList': 'http://www.loc.gov/standards/iso639-2/', 'codeListValue': md.language3}, 

43 md.languageName, 

44 ) 

45 

46 def w_bbox(ext): 

47 return tag( 

48 'GMD:EX_GeographicBoundingBox', 

49 tag('GMD:westBoundLongitude/GCO:Decimal', tpl.coord_dms(ext[0])), 

50 tag('GMD:eastBoundLongitude/GCO:Decimal', tpl.coord_dms(ext[2])), 

51 tag('GMD:southBoundLatitude/GCO:Decimal', tpl.coord_dms(ext[1])), 

52 tag('GMD:northBoundLatitude/GCO:Decimal', tpl.coord_dms(ext[3])), 

53 ) 

54 

55 def contact(): 

56 yield tag( 

57 'GMD:CI_ResponsibleParty', 

58 tag('GMD:organisationName/GCO:CharacterString', md.contactOrganization), 

59 tag('GMD:positionName/GCO:CharacterString', md.contactPosition), 

60 tag( 

61 'GMD:contactInfo/GMD:CI_Contact', 

62 tag( 

63 'GMD:phone/GMD:CI_Telephone', 

64 tag('GMD:voice/GCO:CharacterString', md.contactPhone), 

65 tag('GMD:facsimile/GCO:CharacterString', md.contactFax), 

66 ), 

67 tag( 

68 'GMD:address/GMD:CI_Address', 

69 tag('GMD:deliveryPoint/GCO:CharacterString', md.contactAddress), 

70 tag('GMD:city/GCO:CharacterString', md.contactCity), 

71 tag('GMD:administrativeArea/GCO:CharacterString', md.contactArea), 

72 tag('GMD:postalCode/GCO:CharacterString', md.contactZip), 

73 tag('GMD:country/GCO:CharacterString', md.contactCountry), 

74 tag('GMD:electronicMailAddress/GCO:CharacterString', md.contactEmail), 

75 ), 

76 tag('GMD:onlineResource/GMD:CI_OnlineResource/GMD:linkage/GMD:URL', md.contactUrl), 

77 ), 

78 w_code('role', 'CI_RoleCode', md.contactRole), 

79 ) 

80 

81 def identification(): 

82 yield tag( 

83 'GMD:citation/GMD:CI_Citation', 

84 tag('GMD:title/GCO:CharacterString', md.title), 

85 w_date(md.dateCreated, 'publication'), 

86 w_date(md.dateUpdated, 'revision'), 

87 tag('GMD:identifier/GMD:MD_Identifier/GMD:code/GCO:CharacterString', md.catalogCitationUid), 

88 ) 

89 

90 yield tag('GMD:abstract/GCO:CharacterString', md.abstract) 

91 

92 yield tag('GMD:pointOfContact', contact()) 

93 

94 if md.inspireSpatialScope: 

95 lst = 'http://inspire.ec.europa.eu/metadata-codelist/SpatialScope/' 

96 yield tag( 

97 'GMD:descriptiveKeywords/GMD:MD_Keywords', 

98 tag('GMD:keyword/GMX:Anchor', {'XLINK:href': lst + md.inspireSpatialScope}, md.inspireSpatialScopeName), 

99 tag( 

100 'GMD:thesaurusName/GMD:CI_Citation', 

101 tag('GMD:title/GMX:Anchor', {'XLINK:href': lst + 'SpatialScope'}, 'Spatial scope'), 

102 w_date('2019-05-22', 'publication'), 

103 ), 

104 ) 

105 

106 if md.inspireTheme: 

107 yield tag( 

108 'GMD:descriptiveKeywords/GMD:MD_Keywords', 

109 tag('GMD:keyword/GCO:CharacterString', md.inspireThemeNameEn), 

110 w_code('type', 'MD_KeywordTypeCode', 'theme'), 

111 tag( 

112 'GMD:thesaurusName/GMD:CI_Citation', 

113 tag('GMD:title/GCO:CharacterString', 'GEMET - INSPIRE themes, version 1.0'), 

114 w_date('2008-06-01', 'publication'), 

115 ), 

116 ) 

117 

118 if md.keywords: 

119 yield tag( 

120 'GMD:descriptiveKeywords/GMD:MD_Keywords', 

121 [tag('GMD:keyword/GCO:CharacterString', kw) for kw in md.keywords], 

122 ) 

123 

124 yield tag( 

125 'GMD:resourceConstraints/GMD:MD_LegalConstraints', 

126 w_code('useConstraints', 'MD_RestrictionCode', 'otherRestrictions'), 

127 tag('GMD:otherConstraints/GCO:CharacterString', md.accessConstraints), 

128 tag('GMD:otherConstraints/GCO:CharacterString', md.license), 

129 ) 

130 

131 yield w_code( 

132 'spatialRepresentationType', 

133 'MD_SpatialRepresentationTypeCode', 

134 md.isoSpatialRepresentationType, 

135 ) 

136 

137 if md.isoSpatialResolution: 

138 yield tag( 

139 'GMD:spatialResolution/GMD:MD_Resolution/GMD:equivalentScale/GMD:MD_RepresentativeFraction/GMD:denominator/GCO:Integer', 

140 md.isoSpatialResolution, 

141 ) 

142 

143 yield w_lang() 

144 yield w_code('characterSet', 'MD_CharacterSetCode', 'utf8') 

145 

146 if md.isoTopicCategories: 

147 for cat in md.isoTopicCategories: 

148 yield tag('GMD:topicCategory/GMD:MD_TopicCategoryCode', cat) 

149 

150 if md.wgsExtent: 

151 yield tag('GMD:extent/GMD:EX_Extent/GMD:geographicElement', w_bbox(md.wgsExtent)) 

152 

153 # @TODO 

154 # if md.bounding_polygon_element: 

155 # yield ( 

156 # 'GMD:extent/GMD:EX_Extent/GMD:geographicElement/GMD:EX_BoundingPolygon/GMD:polygon', 

157 # md.bounding_polygon_element 

158 # ) 

159 

160 if md.temporalBegin: 

161 yield tag( 

162 'GMD:extent/GMD:EX_Extent/GMD:temporalElement/GMD:EX_TemporalExtent/GMD:extent/GML:TimePeriod', 

163 tag('GML:beginPosition', md.temporalBegin), 

164 tag('GML:endPosition', md.temporalEnd), 

165 ) 

166 

167 def distributionInfo(): 

168 for link in md.metaLinks: 

169 if link.format: 

170 yield tag( 

171 'GMD:distributionFormat/GMD:MD_Format', 

172 tag('GMD:name/GCO:CharacterString', link.format), 

173 tag('GMD:version/GCO:CharacterString', link.formatVersion), 

174 ) 

175 

176 for link in md.metaLinks: 

177 yield tag( 

178 'GMD:transferOptions/GMD:MD_DigitalTransferOptions', 

179 tag( 

180 'GMD:onLine/GMD:CI_OnlineResource', 

181 tag('GMD:linkage/GMD:URL', ta.url_for(link.url)), 

182 w_code('function', 'CI_OnLineFunctionCode', link.function), 

183 ), 

184 ) 

185 

186 def dataQualityInfo(): 

187 yield tag('GMD:scope/GMD:DQ_Scope', w_code('level', 'MD_ScopeCode', md.isoScope)) 

188 

189 if md.isoQualityConformanceQualityPass: 

190 yield tag( 

191 'GMD:report/GMD:DQ_DomainConsistency/GMD:result/GMD:DQ_ConformanceResult', 

192 tag( 

193 'GMD:specification/GMD:CI_Citation', 

194 tag('GMD:title/GCO:CharacterString', md.isoQualityConformanceSpecificationTitle), 

195 w_date(md.isoQualityConformanceSpecificationDate, 'publication'), 

196 ), 

197 tag('GMD:explanation/GCO:CharacterString', md.isoQualityConformanceExplanation), 

198 tag('GMD:pass/GCO:Boolean', md.isoQualityConformanceQualityPass), 

199 ) 

200 

201 if md.isoQualityLineageStatement: 

202 yield tag( 

203 'GMD:lineage/GMD:LI_Lineage', 

204 tag('GMD:statement/GCO:CharacterString', md.isoQualityLineageStatement), 

205 tag( 

206 'GMD:source/GMD:LI_Source', 

207 tag('GMD:description/GCO:CharacterString', md.isoQualityLineageSource), 

208 tag('GMD:scaleDenominator/GMD:MD_RepresentativeFraction/GMD:denominator/GCO:Integer', md.isoQualityLineageSourceScale), 

209 ), 

210 ) 

211 

212 def content(): 

213 yield tag('GMD:fileIdentifier/GCO:CharacterString', md.catalogUid) 

214 

215 w_lang() 

216 yield w_code('characterSet', 'MD_CharacterSetCode', 'utf8') 

217 

218 yield w_code('hierarchyLevel', 'MD_ScopeCode', md.isoScope) 

219 yield tag('GMD:hierarchyLevelName/GCO:CharacterString', md.isoScopeName) 

220 

221 yield tag('GMD:contact', contact()) 

222 

223 yield tag('GMD:dateStamp/GCO:Date', tpl.iso_date(md.dateUpdated)) 

224 

225 yield tag('GMD:metadataStandardName/GCO:CharacterString', 'ISO19115') 

226 yield tag('GMD:metadataStandardVersion/GCO:CharacterString', '2003/Cor.1:2006') 

227 

228 if md.crs: 

229 yield tag( 

230 'GMD:referenceSystemInfo/GMD:MD_ReferenceSystem/GMD:referenceSystemIdentifier/GMD:RS_Identifier/GMD:code/GCO:CharacterString', 

231 md.crs.uri, 

232 ) 

233 

234 yield tag('GMD:identificationInfo/GMD:MD_DataIdentification', identification()) 

235 yield tag('GMD:distributionInfo/GMD:MD_Distribution', distributionInfo()) 

236 yield tag('GMD:dataQualityInfo/GMD:DQ_DataQuality', dataQualityInfo()) 

237 

238 ## 

239 

240 return tag('GMD:MD_Metadata', content())