source: TI05-delivery/ows_framework/trunk/ows_server/ows_server/models/stubB.py @ 4202

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/trunk/ows_server/ows_server/models/stubB.py@4202
Revision 4202, 13.2 KB checked in by cbyrom, 11 years ago (diff)

Fix handling of stubB config data + add logging info to stubB class
to make easier to follow.

Line 
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
10from Utilities import *
11from ndgUtils.BeautifulSoup import BeautifulSoup
12from AccessControl import AccessControl
13from geoUtilities import Bounding
14from People import *
15from ndgUtils import ndgObject
16from ows_server.models import Utilities
17import logging
18
19#from DeploymentHandling import *
20try: #python 2.5
21    from xml.etree import ElementTree as ET
22except ImportError:
23    try:
24        # if you've installed it yourself it comes this way
25        import ElementTree as ET
26    except ImportError:
27        # if you've egged it this is the way it comes
28        from elementtree import ElementTree as ET
29
30
31def collapse2(keywords,split='>'):
32    ''' Take the last element of a DIF parameter tree entry, and put them in a dictionary
33    under the DIF category '''
34    d={}
35    for item in keywords:
36        line=item.split(split)
37        category=line[0]
38        if category not in d: d[category]=[]
39        while line<>[]:
40            c=line.pop()
41            if c<>'': 
42                if c<>category:
43                    d[category].append(c)
44                line=[]
45    return d
46
47
48def idconvert(helper,elem,config=None,idelem='dgMetadataID'):
49    ''' Given an elementTree element for a dgMetadataID, create
50    a standard ndg URI, and produce an ndgObject '''
51    id=helper.find(elem,idelem)
52    entryID='%s__%s__%s'%(helper.getText(id,'repositoryIdentifier'),
53                            helper.getText(id,'schemeIdentifier'),
54                            helper.getText(id,'localIdentifier'))
55    uri=ndgObject(entryID,config)
56    return uri
57
58class ndgLink:
59    ''' This is a holder for an ndgObject which has a name (and possibly abbreviation) '''
60    def __init__(self,name,abbrev,ndgO,label='',image=None):
61        self.ndgObject=ndgO
62        self.name=name
63        self.abbrev=abbrev
64        self.image=image
65        self.label=label
66        self.URL=ndgO.BURL
67    def toHTML(self):
68        ### needs to be finished
69        return self.ndgObject.URL
70
71class dataGranule:
72        ''' Provides support for data granule structures '''
73        def __init__(self,helper,elem,config,name=''):
74            logging.info("Setting up data granule, %s" %name)
75            self.elem=elem
76            self.constraints=AccessControl(helper.find(elem,'accessControlPolicy'))
77            self.name=name
78            self.uri=idconvert(helper,elem,config,idelem='dataModelID')
79            self.entryID=self.uri.uri
80            self.bbox=Bounding(self.elem,helper,entity='moles')
81            self.timeCoverage=temporal(self.elem,helper)
82            self.parameters=helper.getText(elem,'dgGranuleSummary/dgParameterSummary/ParameterName',multiple=1)
83           
84class ObservationStation:
85    def __init__(self,h,e):
86        logging.info("Setting up stubB to represent observation station data")
87        self.e=
88        self.hdg='Observation Station'
89        if e is None: return
90       
91class DataProductionTool:
92    def __init__(self,h,e):
93        logging.info("Setting up stubB to represent data production tool data")
94        self.e=e
95        i=h.find(e,'dgInstrument')
96        if i is not None: 
97            self.hdg='Instrument'
98        else:
99            self.hdg='Model'
100        if e is None: return
101       
102class Activity:
103    def __init__(self,h,e):
104        logging.info("Setting up stubB to represent activity data")
105        self.e=e
106        self.hdg='Activity'
107        if e is None: return
108       
109def Deployments(elem,helper,config):
110    ''' This is the late June 2007 version '''
111    logging.info("Setting up deployments data")
112    if elem is None: return None
113    types={'dptList':'dataproductiontool','activityList':'activity','obsStnList':'observationstation','dataEntityList':'dataentity'}
114    names={'dptList':'Data Production Tools','activityList':'Activities','obsStnList':'Observation Stations','dataEntityList':'Data Entities'}
115    result={}
116    for alist in types:
117        logging.debug("Looking for --%s-" %alist)
118        blist=helper.find(elem,alist)
119        collection=[]
120        if blist is not None:
121            for object in blist:
122                logging.debug(object)
123                uri=idconvert(helper,object,config=config)
124                name=helper.getText(object,'name')
125                shortName=helper.getText(object,'abbreviation')
126                collection.append((uri,name,shortName))
127            result[names[alist]]=collection
128    logging.info("Deployments data set up")
129    return result
130           
131def temporal(elem,helper):
132    ''' Parse for time coverage attributes '''
133    timeElems=helper.findall(elem,'*//dgTemporalCoverage')
134    tc=[]
135    for e in timeElems:
136        t=e.find('DateRange')
137        if t is not None:
138            c=[helper.getText(t,'DateRangeStart'),]
139            c.append(helper.getText(t,'DateRangeEnd'))
140        else:
141            c=[helper.getText(e,'DateSingle'),'']
142        c.append(helper.getText(elem,'*/dgDatasetStatus/dgDatasetClosure'))
143        tc.append(c)
144    return tc
145           
146def parseParameters(plist,helper):
147    ''' Takes a list of ET Parameter Summary elements, and parses them into a
148    sensible pythonic structure. In particular, given:
149    <dgParameterSummary>
150        <IsOutput>true</IsOutput>
151        <dgStdParameterMeasured>
152            <dgValidTerm>BAROCLINIC V_VELOCITY (OCEAN) CM/S</dgValidTerm>
153            <dgValidTermID>
154                <ParentListID>COAPEC_500YrRun_wholerun_decadal_ocean</ParentListID>
155                <TermID>null</TermID>
156            </dgValidTermID>
157        </dgStdParameterMeasured>
158        <ParameterName>BAROCLINIC V_VELOCITY (OCEAN) CM/S</ParameterName>
159    </dgParameterSummary>
160    we take the parameter name and the parentlistID and use them to build up a
161    parameter dictionary '''
162    ptypes={}
163    dif={}
164    for p in plist:
165        ptype=helper.getText(p,'dgStdParameterMeasured/dgValidTermID/ParentListID')
166        if ptype not in ptypes: ptypes[ptype]=[]
167        ptypes[ptype].append(helper.getText(p,'ParameterName'))
168    gcmd='http://vocab.ndg.nerc.ac.uk/term/P111'
169    if gcmd in ptypes: 
170        dif=collapse2(ptypes[gcmd],split='/')
171        del ptypes[gcmd]
172    return dif,ptypes
173           
174class DataEntity:
175    def __init__(self,helper,element):
176        ''' Attempt to instantiate this stubB as a data entity'''
177        logging.info("Setting up stubB to represent data entity")
178        self.elem=element
179        self.helper=helper
180        #just one curator
181        self.curator=dgContact(self.elem.find('dgDataRoles/dgDataCurator'),ctype='organisation')
182        #possibly multiple creators
183        self.creators=[dgContact(i) for i in self.elem.findall('dgDataRoles/dgDataCreator')]
184        host,service = '','' # dummy for now
185        #possibly multiple granules
186        self.getGranules()
187        #bounding box, handled as a class because this is going to be difficult ...
188        self.bbox=Bounding(self.elem,helper,entity='moles')
189        self.timeCoverage=temporal(self.elem,self.helper)
190        #parameters
191        pelem=self.helper.findall(self.elem,'dgDataSummary/dgParameterSummary')
192        self.parameters,self.extraParameters=parseParameters(pelem,self.helper)
193        self.hdg='Data Entity'
194       
195    def getGranules(self):
196        ''' Load up the granule content within the entity '''
197        logging.info("Adding data granule data")
198        granList=self.elem.findall('dgDataGranule')
199        self.granules=[]
200        i=0
201        for item in granList:
202            i+=1
203            #following needs to be refactored when granule definition includes a proper name ...
204            name=wrapGetText(item,'dgGranuleSummary/dgGranuleName')
205            if name=='': name='Granule %s'%i
206            self.granules.append(dataGranule(self.helper,item,self.config,name=name))
207        logging.info("Granule data added")
208
209class dgMetadataDescription:
210   
211    def __init__(self,helper,elem,viewService):
212        ''' Initialise a Metadata Description '''
213        n=idconvert(helper,elem)
214        self.uri=n.uri
215        self.logo=None
216        self.abstract=helper.getText(elem,'abstract/abstractText')
217        self.texts=[]
218        self.onlineRefs=[]
219        for e in helper.findall(elem,'descriptionSection'):
220            textElem=helper.find(e,'dgDescriptionText')
221            if textElem is not None:
222                text=(textElem.text or '')
223                tail=''
224                for i in textElem: 
225                    text+=ET.tostring(i)
226                    tail=(i.tail or '')
227                text+=tail
228                ctype=helper.getText(e,'contentType')
229                if ctype in ['','text/html']:
230                    soup=BeautifulSoup(text)
231                    text=soup.prettify()
232                self.texts.append((text,ctype))
233        online=helper.findall(elem,'descriptionSection/descriptionOnlineReference/dgSimpleLink')
234        for o in online:
235            rtype=helper.getText(o,'name')
236            #url and uri supported in 1.4 moles, but no one has implemented that yet ...
237            uri,url=helper.getText(o,'URI'),helper.getText(o,'URL')
238            if rtype=='Logo':
239                self.logo=uri
240            else:
241                try:
242                    value=help
243                    self.onlineRefs.append((rtype,{'NumSim':'%s/%s'%(viewService,uri)}[rtype]))
244                except KeyError:
245                    self.onlineRefs.append((rtype,uri))
246
247
248class stubB(DataEntity,ObservationStation,DataProductionTool,Activity):
249       
250        ''' Holds the stub-b document and provides methods which get and manipulate it '''
251       
252        def __init__(self,elem,config):
253                logging.info("Setting up stubB object with moles data")
254                '''Instantiate with an element tree elem '''
255               
256                self.tree=elem
257                self.metadataType='NDG-B1'
258                self.services=[]
259                self.config=config
260                self.citation=''
261                self.personnel=[] # for DIF compatiability for the moment.
262                self.parameters={}
263                self.extraParameters={}
264                self.granules=[]
265                self.logos=[]
266               
267                try:
268                        helper=nsdumb(self.tree)
269                        self.name=helper.getText(self.tree,'name')
270                except Exception,e:
271                        raise ValueError('Error instantiating stubB [%s]'%e)
272
273                self.ndgObject=idconvert(helper,self.tree,self.config)
274                self.entryID=self.ndgObject.uri
275                self.viewService=self.ndgObject.viewService
276               
277                #Note that the root of the ElementTree instance is dgMetadataRecord
278                #so we don't need (or want) that in our xpath expressions.
279               
280                self.constraints=AccessControl(helper.find(self.tree,'dgMetadataSecurity'))
281                self.abbreviation=helper.getText(self.tree,'abbreviation')
282                if self.abbreviation=='': self.abbreviation=self.name[0:min(15,len(self.name))]
283                logos=helper.findall(self.tree,'dgMetadata/dgMetadatdataRecord/logos/logoURI')
284                for l in logos:
285                    self.logos.append((helper.find(l,'dgSimpleLink/name'),
286                                       helper.find(l,'dgSimpleLink/URI')))
287               
288                for i in ('dgDataEntity','dgActivity','dgDataProductionTool','dgObservationStation'):
289                    elem=helper.find(self.tree,i)
290                    if elem is not None:
291                        logging.info("- moles data represents: %s" %i) 
292                        break
293                if elem is None:
294                    raise ValueError('StubB record does not contain an Activity, DE, DPT, ObsStn')
295                elif elem.tag=='dgDataEntity':
296                        DataEntity.__init__(self,helper,elem)
297                elif elem.tag=='dgActivity':
298                        Activity.__init__(self,helper,elem)
299                elif elem.tag=='dgDataProductionTool':
300                        DataProductionTool.__init__(self,helper,elem)
301                elif elem.tag=='dgObservationStation':
302                        ObservationStation.__init__(self,helper,elem)
303
304                self.description=dgMetadataDescription(
305                    helper,helper.find(self.tree,'dgMetadataDescription'),self.viewService)
306                if self.description.logo is not None:
307                    self.logos.append((('Logo',self.description.logo)))
308                self.abstract=self.description.abstract
309                self.stubBtype=elem.tag
310               
311                # now go get all the related links
312                self.related=Deployments(helper.find(elem,'DeploymentSummary'),helper,self.config)
313
314
315if __name__=="__main__":
316    import unittest
317    import os.path
318    from ndgUtils import ndgRetrieve, xmlHandler2
319       
320   
321    class TestCase(unittest.TestCase):
322   
323            def testDE(self):
324                ''' Test rendering a DataEntity stubB '''
325                doc='badc.nerc.ac.uk__NDG-B1__dataent_COAPEC'
326                xml=self.getit(doc)     
327                self.doit(xml.tree)
328               
329            def getit(self,doc):
330                self.c=myConfig('../../ndgDiscovery.config')
331                uri=ndgObject(doc)
332                status,xml=ndgRetrieve(uri,self.c)
333                self.assertEqual(status,1)
334                xml=xmlHandler2.xmlHandler(xml,string=1)
335                return xml
336         
337            def doit(self,xml):
338                layoutdir=self.c.get('layout','layoutdir')
339                x=stubB(xml,self.c)
340               
341         
342    unittest.main()
343           
344           
Note: See TracBrowser for help on using the repository browser.