1 | #!/usr/bin/env python |
---|
2 | # |
---|
3 | # This python code (will) handle all forms of stub-B and provide HTML |
---|
4 | # methods to the extent required by the NDG browse functionality. |
---|
5 | # NB, It would be cleaner to use Dom's way of doing things but we'd |
---|
6 | # need a schema for that ... |
---|
7 | # BNL April 2006 |
---|
8 | # |
---|
9 | |
---|
10 | from Utilities import * |
---|
11 | from AccessControl import AccessControl |
---|
12 | from geoUtilities import Bounding |
---|
13 | from People import * |
---|
14 | from ndgObject import ndgObject |
---|
15 | #from DeploymentHandling import * |
---|
16 | try: #python 2.5 |
---|
17 | from xml.etree import ElementTree as ET |
---|
18 | except ImportError: |
---|
19 | try: |
---|
20 | # if you've installed it yourself it comes this way |
---|
21 | import ElementTree as ET |
---|
22 | except ImportError: |
---|
23 | # if you've egged it this is the way it comes |
---|
24 | from elementtree import ElementTree as ET |
---|
25 | |
---|
26 | |
---|
27 | def collapse2(keywords,split='>'): |
---|
28 | ''' Take the last element of a DIF parameter tree entry, and put them in a dictionary |
---|
29 | under the DIF category ''' |
---|
30 | d={} |
---|
31 | for item in keywords: |
---|
32 | line=item.split(split) |
---|
33 | category=line[0] |
---|
34 | if category not in d: d[category]=[] |
---|
35 | while line<>[]: |
---|
36 | c=line.pop() |
---|
37 | if c<>'': |
---|
38 | if c<>category: |
---|
39 | d[category].append(c) |
---|
40 | line=[] |
---|
41 | return d |
---|
42 | |
---|
43 | |
---|
44 | def idconvert(helper,elem,config=None,idelem='dgMetadataID'): |
---|
45 | ''' Given an elementTree element for a dgMetadataID, create |
---|
46 | a standard ndg URI, and produce an ndgObject ''' |
---|
47 | id=helper.find(elem,idelem) |
---|
48 | entryID='%s__%s__%s'%(helper.getText(id,'repositoryIdentifier'), |
---|
49 | helper.getText(id,'schemeIdentifier'), |
---|
50 | helper.getText(id,'localIdentifier')) |
---|
51 | uri=ndgObject(entryID,config) |
---|
52 | return uri |
---|
53 | |
---|
54 | class ndgLink: |
---|
55 | ''' This is a holder for an ndgObject which has a name (and possibly abbreviation) ''' |
---|
56 | def __init__(self,name,abbrev,ndgO,label='',image=None): |
---|
57 | self.ndgObject=ndgO |
---|
58 | self.name=name |
---|
59 | self.abbrev=abbrev |
---|
60 | self.image=image |
---|
61 | self.label=label |
---|
62 | self.URL=ndgO.BURL |
---|
63 | def toHTML(self): |
---|
64 | ### needs to be finished |
---|
65 | return self.ndgObject.URL |
---|
66 | |
---|
67 | class dataGranule: |
---|
68 | ''' Provides support for data granule structures ''' |
---|
69 | def __init__(self,helper,elem,config,name=''): |
---|
70 | self.elem=elem |
---|
71 | self.constraints=AccessControl(helper.find(elem,'accessControlPolicy')) |
---|
72 | self.name=name |
---|
73 | self.uri=idconvert(helper,elem,config,idelem='dataModelID') |
---|
74 | self.entryID=self.uri.uri |
---|
75 | |
---|
76 | class ObservationStation: |
---|
77 | def __init__(self,h,e): |
---|
78 | self.e=e |
---|
79 | if e is None: return |
---|
80 | class DataProductionTool: |
---|
81 | def __init__(self,h,e): |
---|
82 | self.e=e |
---|
83 | if e is None: return |
---|
84 | class Activity: |
---|
85 | def __init__(self,h,e): |
---|
86 | self.e=e |
---|
87 | if e is None: return |
---|
88 | |
---|
89 | def Deployments(elem,helper,config): |
---|
90 | ''' This is the late June 2007 version ''' |
---|
91 | if elem is None: return None |
---|
92 | types={'dptList':'dataproductiontool','activityList':'activity','obsStnList':'observationstation','deList':''} |
---|
93 | names={'dptList':'Data Production Tools','activityList':'Activities','obsStnList':'Observation Stations','deList':'Data Entities'} |
---|
94 | result={} |
---|
95 | for alist in types: |
---|
96 | blist=helper.find(elem,alist) |
---|
97 | collection=[] |
---|
98 | if blist is not None: |
---|
99 | for object in blist: |
---|
100 | uri=idconvert(helper,object,config=config) |
---|
101 | name=helper.getText(object,'name') |
---|
102 | shortName=helper.getText(object,'abbreviation') |
---|
103 | collection.append((uri,name,shortName)) |
---|
104 | result[names[alist]]=collection |
---|
105 | return result |
---|
106 | |
---|
107 | class DataEntity: |
---|
108 | def __init__(self,helper,element): |
---|
109 | ''' Attempt to instantiate this stubB as a data entity''' |
---|
110 | self.elem=element |
---|
111 | self.helper=helper |
---|
112 | #just one curator |
---|
113 | self.curator=dgContact(self.elem.find('dgDataRoles/dgDataCurator'),ctype='organisation') |
---|
114 | #possibly multiple creators |
---|
115 | self.creators=[dgContact(i) for i in self.elem.findall('dgDataRoles/dgDataCreator')] |
---|
116 | host,service = '','' # dummy for now |
---|
117 | #possibly multiple granules |
---|
118 | self.getGranules() |
---|
119 | #bounding box, handled as a class because this is going to be difficult ... |
---|
120 | self.bbox=Bounding(self.elem,entity='stubB') |
---|
121 | self.temporal() |
---|
122 | #parameters |
---|
123 | plist=wrapGetText(self.elem,'dgDataSummary/dgParameterSummary/ParameterName',multiple=1) |
---|
124 | self.parameters=collapse2(plist,split='/') |
---|
125 | |
---|
126 | |
---|
127 | def getGranules(self): |
---|
128 | ''' Load up the granule content within the entity ''' |
---|
129 | granList=self.elem.findall('dgDataGranule') |
---|
130 | self.granules=[] |
---|
131 | i=0 |
---|
132 | for item in granList: |
---|
133 | i+=1 |
---|
134 | #following needs to be refactored when granule definition includes a proper name ... |
---|
135 | name=wrapGetText(item,'dgGranuleSummary/dgGranuleName') |
---|
136 | if name=='': name='Granule %s'%i |
---|
137 | self.granules.append(dataGranule(self.helper,item,self.config,name=name)) |
---|
138 | |
---|
139 | def temporal(self): |
---|
140 | '''Instantiate the timeCoverage attribute by parsing for temporal coverage ''' |
---|
141 | t=self.elem.find('dgDataSummary/dgDataCoverage/dgTemporalCoverage/DateRange') |
---|
142 | if t is not None: |
---|
143 | try: |
---|
144 | self.timeCoverage=(float(i.text) for i in t) |
---|
145 | except: |
---|
146 | self.timeCoverage=('',) |
---|
147 | |
---|
148 | else: |
---|
149 | t=self.elem.find('dgDataSummary/dgDataCoverage/dgTemporalCoverage/DateSingle') |
---|
150 | try: |
---|
151 | self.timeCoverage=(t.text,) |
---|
152 | except: |
---|
153 | self.timeCoverage=('',) |
---|
154 | |
---|
155 | class dgMetadataDescription: |
---|
156 | |
---|
157 | def __init__(self,helper,elem): |
---|
158 | ''' Initialise a Metadata Description ''' |
---|
159 | n=idconvert(helper,elem) |
---|
160 | self.uri=n.uri |
---|
161 | self.abstract=helper.getText(elem,'abstract/abstractText') |
---|
162 | self.texts=[] |
---|
163 | self.onlineRefs=[] |
---|
164 | for e in helper.findall(elem,'descriptionSection'): |
---|
165 | self.texts.append((helper.getText(e,'dgDescriptionText'),helper.getText(e,'contentType'))) |
---|
166 | online=helper.findall(elem,'descriptionSection/descriptionOnlineReference/dgSimpleLink') |
---|
167 | for o in online: |
---|
168 | self.onlineRefs.append((helper.getText(o,'name'),helper.getText(o,'URI'))) |
---|
169 | |
---|
170 | |
---|
171 | class stubB(DataEntity,ObservationStation,DataProductionTool,Activity): |
---|
172 | |
---|
173 | ''' Holds the stub-b document and provides methods which get and manipulate it ''' |
---|
174 | |
---|
175 | def __init__(self,elem,config): |
---|
176 | |
---|
177 | '''Instantiate with an element tree elem ''' |
---|
178 | |
---|
179 | self.tree=elem |
---|
180 | self.metadataType='NDG-B1' |
---|
181 | self.services=[] |
---|
182 | self.config=config |
---|
183 | self.citation='' |
---|
184 | self.personnel=[] # for DIF compatiability for the moment. |
---|
185 | self.parameters=None |
---|
186 | self.granules=[] |
---|
187 | self.logos=[] |
---|
188 | |
---|
189 | |
---|
190 | try: |
---|
191 | helper=nsdumb(self.tree) |
---|
192 | self.name=helper.getText(self.tree,'name') |
---|
193 | except Exception,e: |
---|
194 | raise ValueError('Error instantiating stubB [%s]'%e) |
---|
195 | |
---|
196 | self.ndgObject=idconvert(helper,self.tree,self.config) |
---|
197 | self.entryID=self.ndgObject.uri |
---|
198 | |
---|
199 | #Note that the root of the ElementTree instance is dgMetadataRecord |
---|
200 | #so we don't need (or want) that in our xpath expressions. |
---|
201 | |
---|
202 | self.constraints=AccessControl(helper.find(self.tree,'dgMetadataSecurity')) |
---|
203 | self.abbreviation=helper.getText(self.tree,'abbreviation') |
---|
204 | if self.abbreviation=='': self.abbreviation=self.name[0:min(15,len(self.name))] |
---|
205 | logos=helper.findall(self.tree,'dgMetadata/dgMetadatdataRecord/logos/logoURI') |
---|
206 | for l in logos: |
---|
207 | self.logos.append((helper.find(l,'dgSimpleLink/name'), |
---|
208 | helper.find(l,'dgSimpleLink/URI'))) |
---|
209 | |
---|
210 | for i in ('dgDataEntity','dgActivity','dgDataProductionTool','dgObservationStation'): |
---|
211 | elem=helper.find(self.tree,i) |
---|
212 | if elem is not None: break |
---|
213 | if elem is None: |
---|
214 | raise ValueError('StubB record does not contain an Activity, DE, DPT, ObsStn') |
---|
215 | elif elem.tag=='dgDataEntity': |
---|
216 | DataEntity.__init__(self,helper,elem) |
---|
217 | elif elem.tag=='dgActivity': |
---|
218 | Activity.__init__(self,helper,elem) |
---|
219 | elif elem.tag=='dgDataProductionTool': |
---|
220 | DataProductionTool.__init__(self,helper,elem) |
---|
221 | elif elem.tag=='dgObservationStation': |
---|
222 | ObservationStation.__init__(self,helper,elem) |
---|
223 | |
---|
224 | self.description=dgMetadataDescription(helper,helper.find(self.tree,'dgMetadataDescription')) |
---|
225 | self.abstract=self.description.abstract |
---|
226 | self.stubBtype=elem.tag |
---|
227 | |
---|
228 | # now go get all the related links |
---|
229 | |
---|
230 | self.related=Deployments(helper.find(elem,'DeploymentSummary'),helper,self.config) |
---|
231 | |
---|
232 | |
---|
233 | if __name__=="__main__": |
---|
234 | import unittest |
---|
235 | import os.path |
---|
236 | |
---|
237 | de='examples/badc.nerc.ac.uk__NDG-B1__dataent_chablis.xml' |
---|
238 | dpt='examples/badc.nerc.ac.uk__NDG-B1__dpt_11634276941110630.xml' |
---|
239 | |
---|
240 | class TestCase(unittest.TestCase): |
---|
241 | |
---|
242 | def testDE(self): |
---|
243 | ''' Test rendering a DataEntity stubB ''' |
---|
244 | fname=de |
---|
245 | self.doit(fname) |
---|
246 | |
---|
247 | def testDPT(self): |
---|
248 | ''' Test rendering a Data Production Tool stubB ''' |
---|
249 | fname=dpt |
---|
250 | self.doit(fname) |
---|
251 | |
---|
252 | |
---|
253 | def doit(self,fname): |
---|
254 | xml=open(fname,'r').read() |
---|
255 | config=myConfig('ndgDiscovery.config') |
---|
256 | layoutdir=config.get('layout','layoutdir') |
---|
257 | x=stubB(xml,config) |
---|
258 | y='''<?xml version="1.0" encoding="UTF-8"?> |
---|
259 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
---|
260 | <html xmlsns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
---|
261 | <head> |
---|
262 | <META http-equiv="Content-Type" content="text/xhtml; charset=iso-8859-1"/> |
---|
263 | <title>stubB</title> |
---|
264 | <LINK media="all, screen" href="%s/layout/ndg.css" type="text/css" rel="stylesheet"/> |
---|
265 | </head> '''%layoutdir+x.toHTML(config) |
---|
266 | ff=fname+'-output.html' |
---|
267 | f=file(ff,'w') |
---|
268 | f.write(y) |
---|
269 | |
---|
270 | |
---|
271 | unittest.main() |
---|
272 | |
---|
273 | |
---|