source: ndgCommon/trunk/ndg/common/src/models/stubB.py @ 4793

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/ndgCommon/trunk/ndg/common/src/models/stubB.py@5136
Revision 4793, 12.0 KB checked in by cbyrom, 11 years ago (diff)

Checking in initial codebase for ndgUtils restructure.

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