source: TI02-CSML/trunk/csml/csml2Moles/molesReadWrite.py @ 4034

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI02-CSML/trunk/csml/csml2Moles/molesReadWrite.py@4034
Revision 4034, 14.2 KB checked in by cbyrom, 13 years ago (diff)

Fix PrettyPrint? to cast elem data as string, before attempting to
call strip() on it (to avoid problems when handling, e.g. ints).
Add ndgmetadata1.3.xsd to csml package and extend molesReadWrite to
use this version, to avoid the need to having to copy this file
locally on each use.

Line 
1#Dominic Lowe, BADC  18 October 2006
2#Updated Sue Latham, various, see SVN log
3
4try: #python 2.5
5    from xml.etree import cElementTree as ET
6except ImportError:
7    try:
8        # if you've installed it yourself it comes this way
9        import cElementTree as ET
10    except ImportError:
11        # if you've egged it this is the way it comes
12        from ndgUtils.elementtree import cElementTree as ET
13import sys, os, pkg_resources
14
15#this is the NEW xml schema class
16
17class xmlSchema(object):
18    ''' this class creates a mapping of the xml schema sequences so that it can be referred to when
19    writing out a new moles document - this enables elements to be written in the order specified by the schema
20    If a particular sequence mapping can't be found then the elements will be written in whichever order python sees fit.
21    It tries various searches of the schema to pick out the sequences (which can be nested)
22    '''
23    def __init__(self, schema):
24        self.schema=schema
25        self.types={}
26        self.sequences={}
27        self.names=[]
28        self.setup()
29
30    def __isParent(self,elem):
31        #if this element is a parent of other elements (excluding annotation and documentation)
32        #then return True else return false
33        if elem.getchildren()==[]:
34            return False
35        for child in elem.getchildren():
36            if child.tag == '{http://www.w3.org/2001/XMLSchema}element':
37                return True
38            elif child.tag == '{http://www.w3.org/2001/XMLSchema}complexType':
39                return True
40            elif child.tag == '{http://www.w3.org/2001/XMLSchema}simpleType':
41                return True
42            elif child.tag == '{http://www.w3.org/2001/XMLSchema}annotation':
43                pass
44            elif child.tag == '{http://www.w3.org/2001/XMLSchema}sequence':
45                return True
46            elif child.tag == '{http://www.w3.org/2001/XMLSchema}choice':
47                return True
48            elif child.tag == '{http://www.w3.org/2001/XMLSchema}complexContent':
49                return True
50            elif child.tag == '{http://www.w3.org/2001/XMLSchema}documentation':
51                pass
52            elif child.tag == '{http://www.w3.org/2001/XMLSchema}restriction':
53                return True
54            elif child.tag == '{http://www.w3.org/2001/XMLSchema}extension':
55                pass
56            elif child.tag == '{http://www.w3.org/2001/XMLSchema}attribute':
57                pass #?
58            elif child.tag == '{http://www.w3.org/2001/XMLSchema}enumeration':
59                pass #?
60       
61             #pattern
62             #attributeGroup
63             #minLength
64             #minInclusive
65             #maxInclusive
66
67        return False
68
69       
70    def __checkChildren(self, parentclassname, elem):     
71        #if parentclassname == 'dgMetadataProvenance':
72            #pdb.set_trace()
73        for child in elem.getchildren():
74            childname = None
75            if child.attrib.has_key('name'):       
76                if child.attrib['name'][-4:]=='Type':
77                    if child.attrib['name'] in ['dgDataSetType','dgFeatureType']:  #add other rogue 'names' ending in Type to this list
78                        childname=child.attrib['name']
79                    else:
80                        childname=child.attrib['name'][:-4]
81                else:
82                    childname=child.attrib['name']
83                if child.attrib.has_key('type'):
84                    if child.attrib['type'][:6]=='moles:':
85                        typename=child.attrib['type'][6:-4]
86                        self.types[childname]=typename             
87            elif child.attrib.has_key('ref'):
88                if child.attrib['ref'][:6]=='moles:':
89                    childname=child.attrib['ref'][6:]
90            if childname is not None:
91                if self.sequences[parentclassname] is None:
92                    self.sequences[parentclassname]=[childname]
93                else:
94                    tmp= self.sequences[parentclassname]                   
95                    tmp.append(childname)
96                    self.sequences[parentclassname]=tmp
97                if self.__isParent(child):
98                    if not self.sequences.has_key(childname):
99                        self.sequences[childname]=[]
100                    self.__checkChildren(childname,child)
101            else:
102                if self.__isParent(child):
103                    self.__checkChildren(parentclassname,child)
104
105    def __resolveTypes(self):
106        for ty in self.types:
107            try:
108                self.sequences[ty]=self.sequences[self.types[ty]]
109            except:
110                pass
111           
112    def setup(self):
113        tree = ET.parse(self.schema)
114        root = tree.getroot()
115        self.sequences['dummy']=[]
116        self.__checkChildren('dummy',root)
117        self.__resolveTypes()
118        self.sequences['dummy']=None
119   
120    def lookupOrder(self, dict,classname):
121        '''takes the attributes in a dictionary and orders them according to the schema sequence'''
122        try:       
123            order=self.sequences[classname]
124        except KeyError:
125            order = []
126            for key in dict:
127                #print key error
128                print 'KEY ERROR %s'%classname
129                if key is not 'schema':
130                    if key is not 'ns':
131                         order.append(key) # if it can't be found an unordered list is returned from the original dictionary items
132            #print 'returning %s'%order
133        return order
134
135class molesElement(object):
136    ''' molesElement class - base class of all elements '''
137    def __init__(self, namespace=None, **kwargs):
138        if namespace !=None:
139            self.ns=namespace
140        else:
141            self.ns = '{http://ndg.nerc.ac.uk/moles}'
142        self.__dict__.update(kwargs)
143   
144    def __combineattributes(self,attname, newChild):
145        att = getattr(self,attname)
146        if isinstance(att,molesElement):
147            setattr(self,attname,[att, newChild])
148        else:
149            att.append(newChild)
150            setattr(self, attname,att)
151           
152    def _stripNS(self, tagtostrip):
153        try:
154            elemname=tagtostrip.split('}')[1]
155            ns=tagtostrip.split('}')[0]+'}'
156        except IndexError:
157            elemname=tagtostrip
158            ns='{https://ndg.nerc.ac.uk/moles}'
159        return elemname, ns
160           
161        ns=tagtostrip.split('}')[1]
162     
163   
164    def addChildElem(self, childname, childobj):
165        #sometimes you want to add a child element but don't know if there is one already. In which case you want to create a list of child objects.
166        if hasattr(self, childname):
167            currentattribute=getattr(self,childname)
168            if type(getattr(self,childname)) is list:
169                currentattribute.append(childobj)
170            else:
171                newlist=[currentattribute]
172                newlist.append(childobj)
173                setattr(self,childname, newlist)
174        else:
175            setattr(self,childname, childobj)
176   
177    def toXML(self,molesFrag, schema=None):
178        if schema != None:
179            self.schema=schema
180        else:
181            self.schema=None
182        #print molesFrag.tag
183        orderedAttribs=schema.lookupOrder(self.__dict__,molesFrag.tag)
184        for item in orderedAttribs:
185           #print "item in orderedAttribs is %s" %item
186           if type(item) is xmlSchema:
187                        continue
188           if item == '{http://ndg.nerc.ac.uk/moles}':
189                continue
190                   
191           # avoid abstractText being output twice
192           # NB, this is a hack to fix the problem of this class not handling
193           # namespaces correctly - ideally this is what should be fixed here
194           if str(molesFrag.tag) == "abstract":
195                if len(molesFrag) > 0:
196                    continue
197           
198           if hasattr(self, item):
199                if isinstance(self.__dict__[item], molesElement):
200                    frag=ET.Element(item)
201                    self.__dict__[item].toXML(frag,schema=self.schema)
202                    molesFrag.append(frag)
203                elif isinstance(self.__dict__[item], list):
204                    for it in self.__dict__[item]:
205                        if isinstance(it, molesElement):
206                            frag=ET.Element(item)
207                            it.toXML(frag, schema=self.schema)
208                            molesFrag.append(frag)
209                        else:
210                            frag=ET.Element(item)
211                            frag.text=it                           
212                            molesFrag.append(frag)
213                else:
214                    frag=ET.Element(item)
215                    frag.text=self.__dict__[item]
216                    molesFrag.append(frag)
217        return molesFrag
218           
219    def fromXML(self,molesFrag):
220        children = molesFrag.getchildren()
221       
222        if children ==[]:
223            elementWithoutNS, ns=self._stripNS(molesElement.tag)
224            setattr(self,elementWithoutNS, molesElement.text)
225        if children!=[]:
226            for child in children:
227                if child.getchildren()!=[]:
228                    childWithoutNS, ns=self._stripNS(child.tag)
229                    newClass=type(childWithoutNS, (molesElement,),{})
230                    newChild=newClass(ns)
231                    newChild.fromXML(child)
232                    kw=child.tag
233                    if hasattr(self, childWithoutNS):
234                        self.__combineattributes(childWithoutNS, newChild)
235                    else:
236                        setattr(self,childWithoutNS, newChild)
237                else:
238                    childWithoutNS, ns=self._stripNS(child.tag)
239                    setattr(self,childWithoutNS, child.text)
240       
241                   
242class dgMetadata(molesElement):   
243    def __init__(self, **kwargs):
244        molesElement.__init__(self, **kwargs)
245       
246    def toXML(self):
247        # ensure that the schema is avaiable locally - if not extract from egg
248        self.molesSchema = 'ndgmetadata1.3.xsd'
249        self.extractLocalSchema()
250        self.schema = xmlSchema(self.molesSchema)
251        molesFrag=ET.Element('dgMetadata')
252        molesFrag.attrib['xmlns']='http://ndg.nerc.ac.uk/moles'
253        molesElement.toXML(self,molesFrag,schema=self.schema)
254        return molesFrag
255   
256    def extractLocalSchema(self):
257        schema = 'XMLSchemas/moles/' + self.molesSchema
258        if not os.path.isfile(self.molesSchema):
259            content = pkg_resources.resource_string('csml',schema)
260            try:
261                f = open(self.molesSchema,'w')
262                f.write(content)
263                f.close()
264            except:
265                raise SystemError, "ERROR: Problem encountered when creating file, %s" %schema
266
267class MolesDoc(object):
268    def __init__(self):
269        self._createClasses()
270   
271    def _create_a_class(self,name, base_class):
272        aNewClass=type(name, (base_class,),{})
273        return aNewClass
274
275    def _createClasses(self):
276        #if you want more classes just add their names to this list
277        #could probably examine the schema here....
278        classList= \
279        ['dataModelID', \
280        'dgDataGranule', \
281        'dgGranuleSummary',\
282        'dgGranuleName',\
283        'simpleCondition',\
284        'dgSecurityCondition',\
285        'accessControlPolicy',\
286        'dgDataEntity', \
287        'dgMetadataRecord', \
288        'dgMetadataID',
289        'dgCoverage', \
290        'dgDataCoverage',\
291        'dgSpatioTemporalRange',\
292        'dgSpatioTemporalCoverage', \
293        'dgSpatialCoverage', \
294        'dgTemporalCoverage', \
295        'dgBoundingBox', \
296        'dgArea',\
297        'DateRange', \
298        'dgDataSummary', \
299        'dgParameterSummary', \
300        'dgParameterValue', \
301        'dgValueDataParameter', \
302        'dgStandardUnit', \
303        'dgOriginalUnit', \
304        'dgRangeDataParameter', \
305        'dgEnumerationParameter', \
306        'dgParameterGroup', \
307        'dgComponentParameter', \
308        'dgStdParameterMeasured', \
309        'dgStandardUnit', \
310        'dgValidTermID', \
311        'dgValidTermParentID', \
312        'dgValidSubterm', \
313        'ListLevel',\
314        'metadataDescriptionID', \
315        'dgMetadataDescription', \
316        'dgStructuredKeyword', \
317        'abstract',\
318        'descriptionSection',\
319        'dgReferenceClass',\
320        'descriptionOnlineReference',\
321        'dgSimpleLink',\
322        'logos',\
323        'logoURI',\
324        'dgDataSetType',\
325        'dgSimulation',\
326        'dgAnalysis',\
327        'dgMeasurement',\
328        'dgFeatureType',\
329        'dgDataRoles',\
330        'dgDataCreator',\
331        'dgDataCurator',\
332        'dgRoleHolder',\
333        'dgOrganisationID',\
334        'dgPersonID',\
335        'dgRoleID',\
336        'contactDetails',\
337        'address',\
338        'dgActivity',\
339        'relatedActivity',\
340        'dgActivityDataCollection',\
341        'dgActivityDataProject',\
342        'dgActivityDataCampaign',\
343        'dgActivityDataInvestigation',\
344        'dgActivityRole',\
345        'dgActivityDeployment',\
346        'dgActivityCoverage',\
347        'dgActivityDuration',\
348        'ActivityDeployment',\
349        'DateStart',\
350        'DateEnd',\
351        'ActivityID',\
352        'DataProductionToolID',\
353        'ObservationStationID',\
354        'ObsStationDeployment',\
355        'dgPrincipalInvestigator',\
356        'dgInvestigator',\
357        'dgFlight',\
358        'dgCruise',\
359        'RelatedDeployment',\
360        'dgModel',\
361        'DPTDeployment',\
362        'dgInstrument',\
363        'dgDataProductionTool',\
364        'dgDPTRoles',\
365        'dgObservationStation',\
366        'dgStationaryPlatform',\
367        'dgMovingPlatform',\
368        'dgLandStation',\
369        'dgMooring',\
370        'position',\
371        'dgStationGroup',\
372        'dgShip',\
373        'vesselType',\
374        'RecordCreation',\
375        'RecordUpdate',\
376        'dgMetadataProvenance',\
377        ]
378
379        for className in classList:
380            newClass=self._create_a_class(className, molesElement)
381            setattr(self,className,newClass)
382
383
384def main():
385    schema = xmlSchema('ndgmetadata1.3.xsd') 
386       
387    #print schema.sequences
388    for key in schema.sequences:
389        print '------------------------------------------------------------------'
390        print key
391        print schema.sequences[key]
392 
393
394if __name__=='__main__':
395    main()
Note: See TracBrowser for help on using the repository browser.