source: CMIP6dreqbuild/trunk/src/framework/dreqPy/dreq.py @ 727

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CMIP6dreqbuild/trunk/src/framework/dreqPy/dreq.py@727
Revision 727, 43.5 KB checked in by mjuckes, 4 years ago (diff)

correct path issues with manifest

Line 
1"""This module provides a basic python API to the Data Request.
2After ingesting the XML documents (configuration and request) the module generates two python objects:
31. A collection of records
42. Index
5"""
6import xml, string, collections
7import xml.dom
8import xml.dom.minidom
9import re, shelve, os, sys
10try:
11  from __init__ import DOC_DIR, version, PACKAGE_DIR
12except:
13  from dreqPy.__init__ import DOC_DIR, version, PACKAGE_DIR
14
15python2 = True
16if sys.version_info[0] == 3:
17  python2 = False
18  pythonPre27 = False
19elif sys.version_info[0] == 2:
20  pythonPre27 = sys.version_info[1] < 7
21
22charmeTempl = """<span title="Using the CHARMe annotation system">Comment on this page:<a href="%s/%s/%s.html" class="charme-metadata-document"></a></span>
23
24<span>
25<div id="charme-placeholder"></div>
26</span>
27<br/>
28<!-- the charme-placeholder-all-targets appears to be required, but can be hidden ... -->
29<span style="display: None;">
30<div id="charme-placeholder-all-targets"></div>
31</span>
32"""
33
34jsh='''<link type="text/css" href="/css/jquery-ui-1.8.16.custom.css" rel="Stylesheet" />
35 <script src="/js/2013/jquery.min.js" type="text/javascript"></script>
36 <script src="/js/2013/jquery-ui.min.js" type="text/javascript"></script>
37 <script src="/js/2013/jquery.cookie.js" type="text/javascript"></script>
38
39<link type="text/css" href="/css/dreq.css" rel="Stylesheet" />
40'''
41
42def dref(i,x):
43  return i._inx.uid[i.__dict__[x]]
44
45blockSchemaFile = '%s/%s' % (DOC_DIR, 'BlockSchema.csv' )
46
47class lutilsC(object):
48
49  def __init__(self):
50    pass
51
52  def addAttributes( self, thisClass, attrDict ):
53    """Add a set of attributes, from a dictionary, to a class"""
54    for k in attrDict:
55      setattr( thisClass, '%s' % k , attrDict[k] )
56
57  def itemClassFact(self, sectionInfo,ns=None):
58     class dreqItem(dreqItemBase):
59       """Inherits all methods from dreqItemBase.
60
61USAGE
62-----
63The instanstiated object contains a single data record. The "_h" attribute links to information about the record and the section it belongs to.
64
65object._a: a python dictionary defining the attributes in each record. The keys in the dictionary correspond to the attribute names and the values are python "named tuples" (from the "collections" module). E.g. object._a['priority'].type contains the type of the 'priority' attribute. Type is expressed using XSD schema language, so "xs:integer" implies integer.  The "useClass" attribute carries information about usage. If object._a['xxx'].useClass = u'internalLink' then the record attribute provides a link to another element and object.xxx is the unique identifier of that element.
66
67object._h: a python named tuple describing the section. E.g. object._h.title is the section title (E.g. "CMOR Variables")
68"""
69       _base=dreqItemBase
70       
71     dreqItem.__name__ = 'dreqItem_%s' % str( sectionInfo.header.label )
72     dreqItem._h = sectionInfo.header
73     dreqItem._a = sectionInfo.attributes
74     dreqItem._d = sectionInfo.defaults
75     if sectionInfo.attributes != None:
76        self.addAttributes(dreqItem, sectionInfo.attributes )
77     ##dreqItem.itemLabelMode = itemLabelMode
78     ##dreqItem.attributes = attributes
79     dreqItem._rc = rechecks()
80     dreqItem.ns = ns
81     return dreqItem
82
83         
84
85##
86## from http://stackoverflow.com/questions/4474754/how-to-keep-comments-while-parsing-xml-using-python-elementtree
87##
88## does not work for python3 ... may not be needed.
89##
90## needed in python2.6 to preserve comments in output XML
91##
92def getParser():
93  import xml.etree.ElementTree as ET
94  class PCParser(ET.XMLTreeBuilder):
95
96     def __init__(self):
97       ET.XMLTreeBuilder.__init__(self)
98       # assumes ElementTree 1.2.X
99       self._parser.CommentHandler = self.handle_comment
100
101     def handle_comment(self, data):
102       self._target.start(ET.Comment, {})
103       self._target.data(data)
104       self._target.end(ET.Comment)
105
106  return PCParser
107
108def loadBS(bsfile):
109  """Read in the 'BlockSchema' definitions of the attributes defining attributes"""
110  ii = open( bsfile, 'r' ).readlines()
111  ll = []
112  for l in ii:
113    ll.append( [x for x in l.strip().split('\t') ] )
114  cc = collections.defaultdict( dict )
115 
116  for l in ll[3:]:
117    l[-1] = l[-1] == '1'
118    if len(l) < len(ll[2]):
119      l.append( '' )
120    try:
121      for i in range( len(ll[2]) ):
122        cc[l[0]][ll[2][i]] = l[i]
123    except:
124      print (l)
125      raise
126  return cc
127
128class rechecks(object):
129  """Checks to be applied to strings"""
130  def __init__(self):
131    self.__isInt = re.compile( '-{0,1}[0-9]+' )
132
133  def isIntStr( self, tv ):
134    """Check whether a string is a valid representation of an integer."""
135    if type( tv ) not in [type(''),type(u'')]:
136      self.reason = 'NOT STRING'
137      return False
138    ok = self.__isInt.match( tv ) != None
139    if not ok:
140      self.reason = 'Failed to match regular expression for integers'
141    else:
142      self.reason = ''
143    return ok
144
145class dreqItemBase(object):
146       __doc__ = """A base class used in the definition of records. Designed to be used via a class factory which sets "itemLabelMode" and "attributes" before the class is instantiated: attempting to instantiate the class before setting these will trigger an exception."""
147       _indexInitialised = False
148       _inx = None
149       _urlBase = ''
150       _htmlStyle = {}
151       _linkAttrStyle = {}
152       __charmeEnable__ = {}
153
154       def __init__(self,idict=None,xmlMiniDom=None,id='defaultId',etree=False):
155         self._strictRead = True
156         dictMode = idict != None
157         mdMode = xmlMiniDom != None
158         self._htmlTtl = None
159         assert not( dictMode and mdMode), 'Mode must be either dictionary of minidom: both assigned'
160         assert dictMode or mdMode, 'Mode must be either dictionary of minidom: neither assigned'
161         ##self._defaults = { }
162         ##self._globalDefault = '__unset__'
163         self._contentInitialised = False
164         self._greenIcon = '<img height="12pt" src="/images/154g.png" alt="[i]"/>'
165         if dictMode:
166           self.dictInit( idict )
167         elif mdMode:
168           self.mdInit( xmlMiniDom, etree=etree )
169
170       def __repr__(self):
171         """Provide a one line summary of identifying the object."""
172         if self._contentInitialised:
173           return 'Item <%s>: [%s] %s' % (self._h.title,self.label,self.title)
174         else:
175           return 'Item <%s>: uninitialised' % self._h.title
176
177       def __info__(self,full=False):
178         """Print a summary of the data held in the object as a list of key/value pairs"""
179         if self._contentInitialised:
180           print ( 'Item <%s>: [%s] %s' % (self._h.title,self.label,self.title) )
181           for a in self.__dict__.keys():
182             if a[0] != '_' or full:
183               if hasattr( self._a[a], 'useClass') and self._a[a].useClass == 'internalLink' and self._base._indexInitialised:
184                 if self.__dict__[a] in self._base._inx.uid:
185                   targ = self._base._inx.uid[ self.__dict__[a] ]
186                   print ( '   %s: [%s]%s [%s]' % ( a, targ._h.label, targ.label, self.__dict__[a] ) )
187                 else:
188                   print ( '   %s: [ERROR: key not found] [%s]' % ( a, self.__dict__[a] ) )
189               else:
190                 print ( 'INFO:    %s: %s' % ( a, self.__dict__[a] ) )
191         else:
192           print ( 'Item <%s>: uninitialised' % self.sectionLabel )
193
194       def __href__(self,odir="",label=None,title=None):
195         """Generate html text for a link to this item."""
196         igns =  ['','__unset__']
197         if title == None:
198           if self._htmlTtl == None:
199             if 'description' in self.__dict__ and self.description != None and self.description.strip( ) not in igns:
200               ttl = self.description
201             elif 'title' in self.__dict__ and self.title != None and self.title.strip( ) not in igns:
202               ttl = self.title
203             else:
204               ttl = self.label
205             ttl = string.replace( ttl,'"', '&quot;' )
206             ttl = string.replace( ttl,'<', '&lt;' )
207             self._htmlTtl = string.replace( ttl,'>', '&gt;' )
208           title=self._htmlTtl
209         if label == None:
210             label = self.uid
211         if odir == '':
212           odir = './'
213             
214         return '<span title="%s"><a href="%s%s.html">%s</a></span>' % (title,odir,self.uid,label)
215
216       def htmlLinkAttrListStyle(self,a,targ,frm=None):
217           xx = string.join( ['%s [%s]' % (x.label, x.__href__()) for x in targ], '; ')
218           return '<li>%s: [%s] %s</li>' % ( a, targ[0]._h.label, xx )
219
220       def getHtmlLinkAttrStyle(self,a):
221           """Return a string containing a html fragment for a link to an attribute."""
222           if a in self.__class__._linkAttrStyle:
223             return self.__class__._linkAttrStyle[a]
224           else:
225             return lambda a,targ, frm='': '<li>%s: [%s] %s [%s]</li>' % ( a, targ._h.label, targ.label, targ.__href__() )
226
227       def __htmlLink__(self,a, sect,app):
228          """Create html line for a link or list of links"""
229          if self._a[a].useClass == 'internalLink':
230                   lst = self.getHtmlLinkAttrStyle(a)
231                   try:
232                     targ = self._base._inx.uid[ self.__dict__[a] ]
233                     m = lst( app, targ, frm=sect )
234                   except:
235                     print ( 'INFO.cex.00001:',a, self.__dict__[a], sect, self.label )
236                     m = '<li>%s: %s .... broken link</li>' % ( app, self.__dict__[a] )
237                     raise
238          else:
239            assert self._a[a].useClass == 'internalLinkList', 'Unrecognised useClass in __htmlLink__: %s' % self._a[a].useClass
240            m = self.htmlLinkAttrListStyle( app, [self._base._inx.uid[u] for u in self.__dict__[a] ], frm=sect )
241            return m
242          return m
243                   ##m = '<li>%s, %s: [%s] %s [%s]</li>' % ( a, self.__class__.__dict__[a].__href__(label=self._greenIcon), targ._h.label, targ.label, targ.__href__() )
244       def __html__(self,ghis=None):
245         """Create html view"""
246         msg = []
247         if self._contentInitialised:
248           sect = self._h.label
249           msg.append( '<h1>%s: [%s] %s</h1>' % (self._h.title,self.label,self.title) )
250           if sect in self.__charmeEnable__:
251             msg.append( charmeTempl % (self.__charmeEnable__[sect].site, 'u', self.uid) )
252           msg.append( '<a href="../index.html">Home</a> &rarr; <a href="../index/%s.html">%s section index</a><br/>\n' % (sect, self._h.title) )
253           msg.append( '<ul>' )
254           for a in self.__dict__.keys():
255             if a[0] != '_':
256               app = '%s%s' % (a, self.__class__.__dict__[a].__href__(label=self._greenIcon) )
257               if hasattr( self._a[a], 'useClass') and self._a[a].useClass in ['internalLink','internalLinkList'] and self._base._indexInitialised:
258                 if self.__dict__[a] == '__unset__':
259                   m = '<li>%s: %s [missing link]</li>' % ( app, self.__dict__[a] )
260                 else:
261                   m = self.__htmlLink__(a, sect,app)
262               elif hasattr( self._a[a], 'useClass') and self._a[a].useClass == 'externalUrl':
263                 m = '<li>%s: <a href="%s" title="%s">%s</a></li>' % ( app, self.__dict__[a], self._a[a].description, self._a[a].title )
264               else:
265                 m = '<li>%s: %s</li>' % ( app, self.__dict__[a] )
266               msg.append( m )
267           msg.append( '</ul>' )
268##
269## add list of inward references
270##
271           oldCode = False
272           if self._base._indexInitialised and oldCode:
273             f1 = self._htmlStyle.get( sect, {} ).get( 'getIrefs', None ) != None
274             if f1:
275               tl = []
276               if f1:
277                 tl = self._htmlStyle[sect]['getIrefs']
278               doall = '__all__' in tl
279               if doall:
280                 tl = self._inx.iref_by_sect[self.uid].a.keys()
281               tl1 = []
282               for t in tl:
283                 if t in self._inx.iref_by_sect[self.uid].a and len( self._inx.iref_by_sect[self.uid].a[t] ) > 0:
284                   tl1.append( t )
285               am = []
286               if len(tl1) > 0:
287                 am.append( '''<div class="demo">\n<div id="tabs">\n<ul>''' )
288                 for t in tl1:
289                   u0 = self._inx.iref_by_sect[self.uid].a[t][0]
290                   this1 = '<li><a href="#tabs-%s">%s</a></li>' % (t,self._inx.uid[u0]._h.title )
291                   am.append( this1 )
292                 am.append( '</ul>' )
293               for t in tl1:
294                   u0 = self._inx.iref_by_sect[self.uid].a[t][0]
295                   am.append( '<div id="tabs-%s">' % t )
296                   am.append( '<h3>%s</h3>' % self._inx.uid[u0]._h.title )
297                   am.append( '<ul>' )
298                   items = [self._inx.uid[u] for  u in self._inx.iref_by_sect[self.uid].a[t] ]
299                   items.sort( ds('label').cmp )
300                   for targ in items:
301                     if ghis == None:
302                       m = '<li>%s:%s [%s]</li>' % ( targ._h.label, targ.label, targ.__href__() )
303                     else:
304                       lst = ghis( targ._h.label )
305                       m = lst( targ, frm=sect )
306                     am.append( m )
307                   am.append( '</ul>' )
308                   am.append( '</div>' )
309               if len(am) > 0:
310                 am.append( '</div>' )
311                 msg.append( '<h2>Links from other sections</h2>' )
312                 msg.append( ''' <script>
313        $(function() {
314                $( "#tabs" ).tabs({cookie: { expires: 1 } });
315        });
316 </script>
317<!-- how to make tab selection stick: http://stackoverflow.com/questions/5066581/jquery-ui-tabs-wont-save-selected-tab-index-upon-page-reload  expiry time in days-->''' )
318                 for m in am:
319                    msg.append(m)
320               
321           elif self._base._indexInitialised:
322             msg += self.__irefHtml__(sect,ghis)
323           if sect in self.__charmeEnable__:
324             msg.append( '<script src="/js/dreq/charme/charme.js"></script>' )
325         else:
326           msg.append( '<b>Item %s: uninitialised</b>' % self.sectionLabel )
327         return msg
328
329
330       def __irefHtml__(self, sect,ghis):
331         """Returns html (as a list of text strings) for inward references"""
332         if self._htmlStyle.get( sect, {} ).get( 'getIrefs', None ) == None:
333           return []
334         
335         tl = self._htmlStyle[sect]['getIrefs']
336         doall = '__all__' in tl
337         if doall:
338           tl = self._inx.iref_by_sect[self.uid].a.keys()
339         tl1 = []
340         for t in tl:
341           if t in self._inx.iref_by_sect[self.uid].a and len( self._inx.iref_by_sect[self.uid].a[t] ) > 0:
342             tl1.append( t )
343         am = []
344         if len(tl1) > 0:
345               am.append( '''<div class="demo">\n<div id="tabs">\n<ul>''' )
346               for t in tl1:
347                   u0 = self._inx.iref_by_sect[self.uid].a[t][0]
348                   this1 = '<li><a href="#tabs-%s">%s</a></li>' % (t,self._inx.uid[u0]._h.title )
349                   am.append( this1 )
350               am.append( '</ul>' )
351               for t in tl1:
352                   u0 = self._inx.iref_by_sect[self.uid].a[t][0]
353                   am.append( '<div id="tabs-%s">' % t )
354                   am.append( '<h3>%s</h3>' % self._inx.uid[u0]._h.title )
355                   am.append( '<ul>' )
356                   items = [self._inx.uid[u] for  u in self._inx.iref_by_sect[self.uid].a[t] ]
357                   items.sort( ds('label').cmp )
358                   for targ in items:
359                     if ghis == None:
360                       m = '<li>%s:%s [%s]</li>' % ( targ._h.label, targ.label, targ.__href__() )
361                     else:
362                       lst = ghis( targ._h.label )
363                       m = lst( targ, frm=sect )
364                     am.append( m )
365                   am.append( '</ul>' )
366                   am.append( '</div>' )
367               if len(am) > 0:
368                 am.append( '</div>' )
369                 am0 = [ '<h2>Links from other sections</h2>' ,]
370                 am0.append( ''' <script>
371        $(function() {
372                $( "#tabs" ).tabs({cookie: { expires: 1 } });
373        });
374 </script>
375<!-- how to make tab selection stick: http://stackoverflow.com/questions/5066581/jquery-ui-tabs-wont-save-selected-tab-index-upon-page-reload  expiry time in days-->''' )
376                 return am0 + am
377         else:
378           return []
379               
380       def dictInit( self, idict ):
381         __doc__ = """Initialise from a dictionary."""
382         for a in self._a.keys():
383           vv = False
384           if a in idict:
385             val = idict[a]
386             vv = True
387           else:
388             if type( self.__class__.__dict__[a] ) not in (type( ''), type( u'' )) and (not self.__class__.__dict__[a].required):
389               if hasattr( self, a):
390                 setattr( self, a, None )
391                 ##self.a = None
392                 delattr( self, a )
393                 ##if a in self.__dict__:
394                   ##self.__dict__.pop(a)
395               else:
396                 print ( 'ERROR: attempt to remove non-existent attribute: %s' % a )
397             else:
398               val = self._d.defaults.get( a, self._d.glob )
399               vv = True
400           if vv:
401             setattr( self, a, val )
402         self._contentInitialised = True
403
404       def mdInit( self, el, etree=False ):
405         __doc__ = """Initialisation from a mindom XML element. The list of attributes must be set by the class factory before the class is initialised"""
406         deferredHandling=False
407         nw1 = 0
408         tvtl = []
409         if etree:
410           ks = set( el.keys() )
411           for a in self._a.keys():
412             if a in ks:
413               aa = '%s%s' % (self.ns,a)
414               tvtl.append( (a,True, str( el.get( a ) ) ) )
415             elif self._a[a].__dict__.get( 'required', True ) in [False,'false',u'false']:
416               tvtl.append( (a,True,None) )
417             else:
418               tvtl.append( (a,False,None) )
419         else:
420           for a in self._a.keys():
421             if el.hasAttribute( a ):
422               tvtl.append( (a,True, str( el.getAttribute( a ) ) ) )
423##
424## attributes are treated as required unless they have a required attribute set to false
425##
426             elif self._a[a].__dict__.get( 'required', True ) not in [False,'false',u'false']:
427               tvtl.append( (a,False,None) )
428       
429         for a,tv,v in tvtl:
430           if tv:
431             erase = False
432             if v == None:
433               pass
434               erase = True
435             elif self._a[a].type == u'xs:float':
436               if v == '':
437                 v = None
438               else:
439                 try:
440                   v = float(v)
441                 except:
442                   print ( 'Failed to convert real number: %s,%s,%s' % (a,tv,v) )
443                   raise
444             elif self._a[a].type in [u'aa:st__floatList', u'aa:st__floatListMonInc']:
445                 v = [float(x) for x in v.split()]
446             elif self._a[a].type in [u'aa:st__integerList', u'aa:st__integerListMonInc']:
447                 v = [int(x) for x in v.split()]
448                 if self._a[a].type in [u'aa:st__integerListMonInc'] and self._strictRead:
449                   for i in range(len(v)-1):
450                     assert v[i] < v[i+1], 'Attribute %s of type %s with non-monotonic value: %s' % (a,self._a[a].type,str(v))
451             elif self._a[a].type == u'xs:integer':
452               if self._rc.isIntStr( v ):
453                 v = int(v)
454               else:
455                 v = v.strip()
456                 thissect = '%s [%s]' % (self._h.title,self._h.label)
457                 if v in [ '',u'',' ', u' ', [], '[]']:
458                   if nw1 < 20:
459                     print ( 'WARN.050.0001: input integer non-compliant: %s: %s: "%s" -- set to zero' % (thissect,a,v) )
460                     nw1 += 1
461                   v = 0
462                 else:
463                   try:
464                     v = int(float(v))
465                     print ( 'WARN: input integer non-compliant: %s: %s: %s' % (thissect,a,v) )
466                   except:
467                     msg = 'ERROR: failed to convert integer: %s: %s: %s, %s' % (thissect,a,v,type(v))
468                     deferredHandling=True
469             elif self._a[a].type == u'xs:boolean':
470               v = v in ['true','1']
471             elif self._a[a].type == u'aa:st__stringList':
472               if v.find(' ') != -1:
473                 v = v.split()
474               else:
475                 v = [v,]
476             elif self._a[a].type not in [u'xs:string']:
477               print ('ERROR: Type %s not recognised [%s:%s]' % (self._a[a].type,self._h.label,a) )
478
479             if erase:
480               ### need to overwrite attribute (which is inherited from parent class) before deleting it.
481               ### this may not be needed in python3
482               self.__dict__[a] = '__tmp__'
483               delattr( self, a )
484             else:
485               self.__dict__[a] = v
486           else:
487             if a in ['uid',]:
488               thissect = '%s [%s]' % (self._h.title,self._h.tag)
489               print ( 'ERROR.020.0001: missing uid: %s' % thissect )
490               if etree:
491                 print ( ks )
492                 import sys
493                 sys.exit(0)
494             self.__dict__[a] = self._d.defaults.get( a, self._d.glob )
495
496           if deferredHandling:
497             print ( msg )
498
499         self._contentInitialised = True
500
501   
502class config(object):
503  """Read in a vocabulary collection configuration document and a vocabulary document"""
504
505  def __init__(self, configdoc='out/dreqDefn.xml', thisdoc='../workbook/trial_20150724.xml', manifest=None, useShelve=False, strings=False):
506    self.rc = rechecks()
507    self.lu = lutilsC()
508    self.silent = True
509    self.coll = {}
510
511    self.nts = collections.namedtuple( 'sectdef', ['tag','label','title','id','itemLabelMode','level','maxOccurs','labUnique','uid'] )
512    self.nti = collections.namedtuple( 'itemdef', ['tag','label','title','type','useClass','techNote','required'] )
513    self.ntt = collections.namedtuple( 'sectinit', ['header','attributes','defaults'] )
514    self.nt__default = collections.namedtuple( 'deflt', ['defaults','glob'] )
515    self.ntf = collections.namedtuple( 'sect', ['header','attDefn','items'] )
516    self.bscc = loadBS(blockSchemaFile)
517    self.strings = strings
518
519    self.tt0 = {}
520    self.tt1 = {}
521    self.ttl2 = []
522    self.docs = {}
523
524    if manifest != None:
525      assert os.path.isfile( manifest ), 'Manifest file not found: %s' % manifest
526      ii = open(manifest).readlines() 
527      docl = []
528      for l in ii[1:]:
529        bits = l.strip().split()
530        assert len( bits ) > 1, 'Failed to parse line in manifest %s: \n%s' % (manifest,l)
531        bb = []
532        for b in bits[:2]:
533          if not os.path.isfile( b ):
534             b = '%s/%s' % (PACKAGE_DIR,b)
535          assert os.path.isfile( b ), 'File %s not found (listed in %s)' % (b,manifest )
536          bb.append( b )
537        docl.append( tuple( bb ) )
538      for d,c in docl:
539        self.__read__(d, c)
540    else:
541      self.__read__(thisdoc, configdoc)
542
543  def getByUid(self,uid):
544    mmm = []
545    for fn,rr in self.docs.items():
546       root, doc = rr
547       mm = root.findall( ".//*[@uid='%s']" % uid )
548       mmm += mm
549       print ('%s: %s' % (fn, str(mm)) )
550    return mmm
551
552  def __read__(self, thisdoc, configdoc):
553    self.vdef = configdoc
554    self.vsamp = thisdoc
555    fn = thisdoc.split( '/' )[-1]
556
557    if self.strings:
558      doc = xml.dom.minidom.parseString( self.vdef  )
559    else:
560      doc = xml.dom.minidom.parse( self.vdef  )
561##
562## elementTree parsing implemented for main document
563##
564    self.etree = False
565    self.etree = True
566    if self.etree:
567      import xml.etree.cElementTree as cel
568     
569      if not pythonPre27:
570        ## this for of namespace registration not available in 2.6
571        ## absence of registration means module cannot write data exactly as read.
572        ##
573        cel.register_namespace('', "urn:w3id.org:cmip6.dreq.dreq:a")
574        cel.register_namespace('pav', "http://purl.org/pav/2.3")
575
576      if not self.strings:
577        if python2:
578          parser = getParser()()
579          self.contentDoc = cel.parse( self.vsamp, parser=parser )
580        else:
581          self.contentDoc = cel.parse( self.vsamp )
582
583        root = self.contentDoc.getroot()
584      else:
585        root = cel.fromstring(self.vsamp)
586      ##bs = string.split( root.tag, '}' )
587      self.docs[fn] = (root, self.contentDoc)
588      bs = root.tag.split( '}' )
589      if len( bs ) > 1:
590        self.ns = bs[0] + '}'
591      else:
592        self.ns = None
593      vl = root.findall( './/{http://purl.org/pav/2.3}version' )
594      self.version = vl[0].text
595    else:
596      if self.strings:
597        self.contentDoc = xml.dom.minidom.parseString( self.vsamp  )
598      else:
599        self.contentDoc = xml.dom.minidom.parse( self.vsamp )
600
601        vl = self.contentDoc.getElementsByTagName( 'prologue' )
602        v = vl[0].getElementsByTagName( 'pav:version' )
603        self.version = v[0].firstChild.data
604      self.ns = None
605
606    vl = doc.getElementsByTagName( 'table' ) + doc.getElementsByTagName( 'annextable' )
607    self.tables = {}
608    tables = {}
609    self.tableClasses = {}
610    self.tableItems = collections.defaultdict( list )
611##
612## this loads in some metadata, but not yet in a useful way.
613##
614    self._t0 = self.parsevcfg(None)
615    self._t2 = self.parsevcfg('__main__')
616    self._tableClass0 = self.lu.itemClassFact( self._t0, ns=self.ns )
617##
618## define a class for the section heading records.
619##
620    self._t1 = self.parsevcfg('__sect__')
621##
622## when used with manifest .. need to preserve entries in "__main__" from each document.
623##
624    self._sectClass0 = self.lu.itemClassFact( self._t1, ns=self.ns )
625
626    for k in self.bscc:
627      self.tt0[k] = self._tableClass0(idict=self.bscc[k])
628      if k in self._t0.attributes:
629        setattr( self._tableClass0, '%s' % k, self.tt0[k] )
630      if k in self._t1.attributes:
631        setattr( self._sectClass0, '%s' % k, self.tt0[k] )
632
633##
634## save header information, as for recordAttributeDefn below
635##
636    self._recAtDef = {'__core__':self._t0, '__sect__':self._t1}
637##
638## addition of __core__ to coll dictionary ..
639##
640    self.coll['__core__'] = self.ntf( self._t0.header, self._t0.attributes, [self.tt0[k] for k in self.tt0] )
641
642    ec = {}
643    for i in self.coll['__core__'].items:
644      ec[i.label] = i
645
646    for v in vl:
647      t = self.parsevcfg(v)
648      tables[t[0].label] = t
649      self.tableClasses[t[0].label] = self.lu.itemClassFact( t, ns=self.ns )
650      thisc = self.tableClasses[t[0].label]
651      self.tt1[t[0].label] = self._sectClass0( idict=t.header._asdict() )
652      self.tt1[t[0].label].maxOccurs = t.header.maxOccurs
653      self.tt1[t[0].label].labUnique = t.header.labUnique
654      self.tt1[t[0].label].level = t.header.level
655      self.tt1[t[0].label].itemLabelMode = t.header.itemLabelMode
656      self.ttl2 += [thisc.__dict__[a] for a in t.attributes]
657    mil = [t[1] for t in self._t2.attributes.items()]
658    self.coll['__main__'] = self.ntf( self._t2.header, self._t2.attributes, self.ttl2 )
659
660    self.coll['__sect__'] = self.ntf( self._t1.header, self._t1.attributes, [self.tt1[k] for k in self.tt1] )
661
662    for sct in ['__core__','__main__','__sect__']:
663      for k in self.coll[sct].attDefn.keys():
664        assert k in ec, 'Key %s [%s] not found' % (k,sct)
665        self.coll[sct].attDefn[k] = ec[k]
666
667    self.recordAttributeDefn = tables
668    for k in tables.keys():
669      if self.etree:
670        vl = root.findall( './/%s%s' % (self.ns,k) )
671        if len(vl) == 1:
672          v = vl[0]
673          t = v.get( 'title' )
674          i = v.get( 'id' )
675          uid = v.get( 'uid' )
676          useclass = v.get( 'useClass' )
677
678          self.tt1[k].label = k
679          self.tt1[k].title = t
680          self.tt1[k].id = i
681          self.tt1[k].uid = uid
682          self.tt1[k].useClass = useclass
683          self.tableClasses[k]._h = self.tt1[k]
684          il = v.findall( '%sitem' % self.ns )
685          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
686 
687          self.tables[k] = (i,t,len(il))
688       
689          for i in il:
690            ii = self.tableClasses[k](xmlMiniDom=i, etree=True)
691            self.tableItems[k].append( ii )
692        elif len(vl) > 1:
693          assert False, 'not able to handle repeat sections with etree yet'
694      else:
695        vl = self.contentDoc.getElementsByTagName( k )
696        if len(vl) == 1:
697          v = vl[0]
698          t = v.getAttribute( 'title' )
699          i = v.getAttribute( 'id' )
700          il = v.getElementsByTagName( 'item' )
701          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
702 
703          self.tables[k] = (i,t,len(il))
704       
705          for i in il:
706            ii = self.tableClasses[k](xmlMiniDom=i)
707            self.tableItems[k].append( ii )
708        elif len(vl) > 1:
709          l1 = []
710          l2 = []
711          for v in vl:
712            t = v.getAttribute( 'title' )
713            i = v.getAttribute( 'id' )
714            il = v.getElementsByTagName( 'item' )
715            self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
716            l1.append( (i,t,len(il)) )
717         
718            l2i = []
719            for i in il:
720              ii = self.tableClasses[k](xmlMiniDom=i)
721              l2i.append( ii )
722            l2.append( l2i )
723          self.tables[k] = l1
724          self.tableItems[k] = l2
725      self.coll[k] = self.ntf( self.recordAttributeDefn[k].header, self.recordAttributeDefn[k].attributes, self.tableItems[k] )
726 
727  def info(self,ss):
728    """Switchable print function ... switch off by setting self.silent=True"""
729    if not self.silent:
730      print ( ss )
731
732  def parsevcfg(self,v):
733      """Parse a section definition element, including all the record attributes. The results are returned as a named tuple of attributes for the section and a dictionary of record attribute specifications."""
734      if v in [ None,'__main__']:
735        idict = {'description':'An extended description of the object', 'title':'Record Description', \
736           'techNote':'', 'useClass':'__core__', 'superclass':'rdf:property',\
737           'type':'xs:string', 'uid':'__core__:description', 'label':'label', 'required':'required' }
738        if v == None:
739          vtt = self.nts( '__core__', 'CoreAttributes', 'X.1 Core Attributes', '00000000', 'def', '0', '0', 'false', '__core__' )
740        else:
741          vtt = self.nts( '__main__', 'DataRequestAttributes', 'X.2 Data Request Attributes', '00000001', 'def', '0', '0', 'false', '__main__' )
742      elif v == '__sect__':
743        idict = {'title':'Record Description', \
744         'uid':'__core__:description', 'label':'label', 'useClass':'text', 'id':'id', 'maxOccurs':'', 'itemLabelMode':'', 'level':'', 'labUnique':'' }
745        vtt = self.nts( '__sect__', 'sectionAttributes', 'X.3 Section Attributes', '00000000', 'def', '0', '0', 'false', '__sect__' )
746##<var label="var" uid="SECTION:var" useClass="vocab" title="MIP Variable" id="cmip.drv.001">
747      else:
748        l = v.getAttribute( 'label' )
749        t = v.getAttribute( 'title' )
750        i = v.getAttribute( 'id' )
751        ilm = v.getAttribute( 'itemLabelMode' )
752        lev = v.getAttribute( 'level' )
753        maxo = v.getAttribute( 'maxOccurs' )
754        labu = v.getAttribute( 'labUnique' )
755        il = v.getElementsByTagName( 'rowAttribute' )
756        vtt = self.nts( v.nodeName, l,t,i,ilm,lev, maxo, labu, 's__%s' % v.nodeName )
757        idict = {}
758        for i in il:
759          tt = self.parseicfg(i)
760          idict[tt.label] = tt
761      deflt = self.nt__default( {}, '__unset__' )
762
763      ## return a named tuple: (header, attributes, defaults)
764      return self.ntt( vtt, idict, deflt )
765
766  def parseicfg(self,i):
767      """Parse a record attribute specification"""
768      defs = {'type':"xs:string"}
769      ll = []
770      ee = {}
771      for k in ['label','title','type','useClass','techNote','description','uid','required']:
772        if i.hasAttribute( k ):
773          ll.append( i.getAttribute( k ) )
774        else:
775          ll.append( defs.get( k, None ) )
776        ee[k] = ll[-1]
777      l, t, ty, cls, tn, desc, uid, rq = ll
778      self.lastTitle = t
779      if rq in ['0', 'false']:
780        rq = False
781      else:
782        rq = True
783      ee['required'] = rq
784
785      returnClass = True
786      if returnClass:
787        return self._tableClass0( idict=ee )
788      else:
789        return self.nti( i.nodeName, l,t,ty,cls,tn,rq )
790
791class container(object):
792  """Simple container class, to hold a set of dictionaries of lists."""
793  def __init__(self, atl ):
794    self.uid = {}
795    for a in atl:
796      self.__dict__[a] =  collections.defaultdict( list )
797
798class c1(object):
799  def __init__(self):
800    self.a = collections.defaultdict( list )
801
802class index(object):
803  """Create an index of the document. Cross-references are generated from attributes with class 'internalLink'.
804This version assumes that each record is identified by an "uid" attribute and that there is a "var" section.
805Invalid internal links are recorded in tme "missingIds" dictionary.
806For any record, with identifier u, iref_by_uid[u] gives a list of the section and identifier of records linking to that record.
807"""
808
809  def __init__(self, dreq,lock=True):
810    self.silent = True
811    self.uid = {}
812    self.uid2 = collections.defaultdict( list )
813    nativeAtts = ['uid','iref_by_uid','iref_by_sect','missingIds']
814    naok = map( lambda x: not x in dreq, nativeAtts )
815    assert all(naok), 'This version cannot index collections containing sections with names: %s' % str( nativeAtts )
816    self.var_uid = {}
817    self.var_by_name = collections.defaultdict( list )
818    self.var_by_sn = collections.defaultdict( list )
819    self.iref_by_uid = collections.defaultdict( list )
820    irefdict = collections.defaultdict( list )
821    for k in dreq.keys():
822      if 'sn' in dreq[k].attDefn:
823         self.__dict__[k] =  container( ['label','sn'] )
824      else:
825         self.__dict__[k] =  container( ['label'] )
826    ##
827    ## collected names of attributes which carry internal links
828    ##
829      for ka in dreq[k].attDefn.keys():
830        if hasattr( dreq[k].attDefn[ka], 'useClass') and dreq[k].attDefn[ka].useClass in  ['internalLink','internalLinkList']:
831           irefdict[k].append( ka )
832
833    for k in dreq.keys():
834        for i in dreq[k].items:
835          assert 'uid' in i.__dict__, 'uid not found::\n%s\n%s' % (str(i._h),str(i.__dict__) )
836          if 'uid' in self.uid:
837            print ( 'ERROR.100.0001: Duplicate uid: %s [%s]' % (i.uid,i._h.title) )
838            self.uid2[i.uid].append( (k,i) )
839          else:
840### create index bx uid.
841            self.uid[i.uid] = i
842
843    self.missingIds = collections.defaultdict( list )
844    self.iref_by_sect = collections.defaultdict( c1 )
845    for k in dreq.keys():
846        for k2 in irefdict.get( k, [] ):
847          n1 = 0
848          n2 = 0
849          for i in dreq[k].items:
850            if k2 in i.__dict__:
851              id2 = i.__dict__.get( k2 )
852              if id2 != '__unset__':
853                sect = i._h.label
854  ## append attribute name and target  -- item i.uid, attribute k2 reference item id2
855                if type(id2) != type( [] ):
856                  id2 = [id2,]
857                for u in id2:
858                  self.iref_by_uid[ u ].append( (k2,i.uid) )
859                  self.iref_by_sect[ u ].a[sect].append( i.uid )
860                  if u in self.uid:
861                    n1 += 1
862                  else:
863                    n2 += 1
864                    self.missingIds[u].append( (k,k2,i.uid) )
865          self.info(  'INFO:: %s, %s%s (%s)' % (k,k2,n1,n2) )
866
867    for k in dreq.keys():
868      for i in dreq[k].items:
869        self.__dict__[k].uid[i.uid] = i
870        self.__dict__[k].label[i.label].append( i.uid )
871        if 'sn' in dreq[k].attDefn:
872          self.__dict__[k].sn[i.sn].append( i.uid )
873
874    if lock:
875      for k in self.iref_by_uid: 
876         self.iref_by_uid[k] = tuple( self.iref_by_uid[k] )
877      for k in self.iref_by_sect:
878        for s in self.iref_by_sect[ k ].a:
879          self.iref_by_sect[ k ].a[s] = tuple( self.iref_by_sect[ k ].a[s] )
880
881  def info(self,ss):
882    if not self.silent:
883      print ( ss )
884
885class ds(object):
886  """Comparison object to assist sorting of lists of dictionaries"""
887  def __init__(self,k):
888    self.k = k
889  def cmp(self,x,y):
890    return cmp( x.__dict__[self.k], y.__dict__[self.k] )
891
892class kscl(object):
893  """Comparison object to assist sorting of dictionaries of class instances"""
894  def __init__(self,idict,k):
895    self.k = k
896    self.idict = idict
897  def cmp(self,x,y):
898    return cmp( self.idict[x].__dict__[self.k], self.idict[y].__dict__[self.k] )
899
900src1 = '../workbook/trial_20150831.xml'
901
902#DEFAULT LOCATION -- changed automatically when building distribution
903defaultDreq = 'annotated_20150731.xml'
904#DEFAULT CONFIG
905defaultConfig = 'dreq2Defn.xml'
906
907defaultDreqPath = '%s/%s' % (DOC_DIR, defaultDreq )
908defaultConfigPath = '%s/%s' % (DOC_DIR, defaultConfig )
909
910class loadDreq(object):
911  """Load in a vocabulary document.
912  dreqXML: full path to the XML document
913  configdoc: full path to associated configuration document
914  useShelve: flag to specify whether to retrieve data from cache (not implemented)
915  htmlStyles: dictionary of styling directives which influence structure of html page generates by the "makeHtml" method
916"""
917
918  def __init__(self,dreqXML=defaultDreqPath, configdoc=defaultConfigPath, useShelve=False, htmlStyles=None, strings=False, manifest=None ):
919    self.c = config( thisdoc=dreqXML, configdoc=configdoc, useShelve=useShelve,strings=strings,manifest=manifest)
920    self.coll = self.c.coll
921    self.version = self.c.version
922    self.inx = index(self.coll)
923    self.itemStyles = {}
924    self.defaultItemLineStyle = lambda i, frm='', ann='': '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
925    self.softwareVersion = version
926##
927## add index to Item base class .. so that it can be accessed by item instances
928##
929    dreqItemBase._inx = self.inx
930    dreqItemBase._indexInitialised = True
931##
932## load in additional styling directives
933##
934    if htmlStyles != None:
935      for k in htmlStyles:
936        dreqItemBase._htmlStyle[k] = htmlStyles[k]
937
938##    dreqItemBase._htmlStyle['__general__'] = {'addRemarks':True}
939
940    self.pageTmpl = """<html><head><title>%s</title>
941%s
942<link rel="stylesheet" type="text/css" href="%scss/dreq.css">
943</head><body>
944
945<div id="top">
946   <div id="corner"></div>
947   CMIP6 Data Request
948</div>
949<div id="nav"><div><a href="%s" title="Home">Home</a></div></div>
950
951<div id="section">
952%s
953</div>
954</body></html>"""
955
956  def getHtmlItemStyle(self, sect):
957    """Get the styling method associated with a given section."""
958    if sect in self.itemStyles:
959      return self.itemStyles[sect]
960    return self.defaultItemLineStyle
961
962  def updateByUid( self, uid, dd, delete=[] ):
963    typePar={'xs:string':'x', 'xs:integer':0, 'xs:float':1., 'xs:boolean':True, 'xs:duration':'x'}
964    listTypePar={ "aa:st__integerList":1,"aa:st__integerListMonInc":1, "aa:st__stringList":'x', "aa:st__floatList":1. }
965
966    mmm = self.c.getByUid( uid )
967    assert len(mmm) == 1, 'Expected single uid match, found: %s' % len(mmm)
968    thisdoc = mmm[0]
969    item = self.inx.uid[uid]
970    d1 = [d for d in delete if d not in item._a]
971    e1 = len(d1) > 0
972    if e1:
973      print ('ERROR.update.0001: request to delete non-existent keys: %s' % d1 )
974    d1 = [d for d in delete if d in item._a and item._a[d].required ]
975    e1b = len(d1) > 0
976    if e1b:
977      print ('ERROR.update.0002: request to delete required attributes: %s' % d1 )
978    d2 = [d for d in dd if d not in item._a]
979    e2 = len(d2) > 0
980    if e2:
981      print ('ERROR.update.0003: request to modify non-existent keys: %s' % d2 )
982    e3 = []
983    for k in [d for d in dd if d in item._a]:
984      if item._a[k].type in typePar:
985        e3.append( type( dd[k] ) != type( typePar[item._a[k].type] ) )
986        if e3[-1]:
987          print ('ERROR.update.0004: value has wrong type [%s] %s --- %s' % (item._a[k].type, dd[k], type( dd[k] ) ) )
988      elif item._a[k].type in listTypePar:
989        a = type( dd[k] ) not in [type( [] ), type( () )]
990        if a:
991          print ('ERROR.update.0005: value has wrong type [%s] %s --- should be list or tuple' % (item._a[k].type, dd[k] ) )
992        else:
993          a = not all( [type(x) == type(listTypePar[item._a[k].type]) for x in dd[k]] )
994          if a:
995            print ('ERROR.update.0005: value has wrong type [%s] %s --- %s' % (item._a[k].type, dd[k], [type(x)for x in dd[k]] ) )
996
997        if not a and item._a[k].type == 'aa:st__integerListMonInc':
998          a = not all( [dd[k][i+1] > dd[k][i] for i in range( len(dd[k]) -1 )] )
999          if a:
1000            print ('ERROR.update.0006: value should be a monotonic increasing integer list: %s' % str( dd[k] ) )
1001        e3.append(a)
1002      else:
1003        print ( 'Type not recognised: %s' % item._a[k].type )
1004        e3.append( False )
1005    eee = any([e1,e1b,e2,any(e3)])
1006    assert not eee, 'STOPPING: validation errors'
1007    self.thisdoc = thisdoc
1008    for k in dd:
1009      thisdoc.set( k, self.__string4xml__( dd[k], item._a[k].type ) )
1010      item.__dict__[k] = dd[k]
1011
1012  def saveXml( self, docfn=None, targ=None ):
1013    if docfn == None:
1014      docfn, tt = self.c.docs.items()[0]
1015    else:
1016      tt = self.c.docs[docfn]
1017
1018    if targ == None:
1019      targ = docfn
1020    tt[1].write( targ )
1021    print ('XML document saved to %s' % targ)
1022 
1023  def __string4xml__(self,v,typ):
1024    if typ in ['xs:string','xs:duration']:
1025       return v
1026    elif typ in ['xs:integer', 'xs:float', 'xs:boolean']:
1027       return str(v)
1028    elif typ == "aa:st__stringList":
1029       return string.join(v)
1030    elif typ in ["aa:st__integerList","aa:st__integerListMonInc", "aa:st__floatList"]:
1031       return string.join( [str(x) for x in v] )
1032    else:
1033       assert False, 'Data type not recognised'
1034     
1035  def _sectionSortHelper(self,title):
1036
1037    ab = title.split(  )[0].split('.')
1038    if len( ab ) == 2:
1039      a,b = ab
1040
1041      if self.c.rc.isIntStr(a):
1042        a = int(a)
1043      if self.c.rc.isIntStr(b):
1044        b = int(b)
1045      rv = (a,b)
1046    elif len(ab) == 1:
1047      rv = (ab[0],0)
1048    else:
1049      rv = ab
1050    return rv
1051
1052  def makeHtml(self,odir='./html', ttl0 = 'Data Request Index', annotations=None):
1053    """Generate a html view of the vocabularies, using the "__html__" method of the vocabulary item class to generate a
1054page for each item and also generating index pages.
1055    odir: directory for html files;
1056    ttl0: Title for main index (in odir/index.html)"""
1057
1058    ks = self.inx.uid.keys()
1059    ks.sort( kscl( self.inx.uid, 'title' ).cmp )
1060    for k in ks:
1061      i = self.inx.uid[k]
1062      ttl = 'Data Request Record: [%s]%s' % (i._h.label,i.label)
1063      bdy = string.join( i.__html__( ghis=self.getHtmlItemStyle ), '\n' )
1064      oo = open( '%s/u/%s.html' % (odir,i.uid), 'w' )
1065      oo.write( self.pageTmpl % (ttl, jsh, '../', '../index.html', bdy ) )
1066      oo.close()
1067
1068    msg0 = ['<h1>%s</h1>' % ttl0, '<ul>',]
1069    msg0.append( '<li><a href="tab01_1_1.html">Overview: priority 1 variables, tier 1 experiments</a></li>' )
1070    msg0.append( '<li><a href="tab01_3_3.html">Overview: all variables and experiments</a></li>' )
1071    ks = sorted( self.coll.keys() )
1072    ee = {}
1073    for k in ks:
1074      ee[self.coll[k].header.title] = k
1075    kks = sorted( ee.keys(),  key = self._sectionSortHelper )
1076    for kt in kks:
1077      k = ee[kt]
1078##
1079## sort on item label
1080##
1081      if annotations != None and k in annotations:
1082        ann = annotations[k]
1083      else:
1084        ann = {}
1085
1086      self.coll[k].items.sort( ds('label').cmp )
1087      ttl = 'Data Request Section: %s' % k
1088      msg0.append( '<li><a href="index/%s.html">%s [%s]</a></li>\n' % (k,self.coll[k].header.title,k) )
1089      msg = ['<h1>%s</h1>\n' % ttl, '<ul>',]
1090      msg.append( '<a href="../index.html">Home</a><br/>\n' )
1091      lst = self.getHtmlItemStyle(k)
1092     
1093      for i in self.coll[k].items:
1094        ##m = '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
1095       
1096        m = lst( i, ann=ann.get( i.label ) )
1097        msg.append( m )
1098      msg.append( '</ul>' )
1099      bdy = string.join( msg, '\n' )
1100      oo = open( '%s/index/%s.html' % (odir,k), 'w' )
1101      oo.write( self.pageTmpl % (ttl, '', '../', '../index.html', bdy ) )
1102      oo.close()
1103    msg0.append( '</ul>' )
1104    bdy = string.join( msg0, '\n' )
1105    oo = open( '%s/index.html' % odir, 'w' )
1106    oo.write( self.pageTmpl % (ttl0, '', '', 'index.html', bdy ) )
1107    oo.close()
1108   
1109if __name__ == '__main__':
1110  dreq = loadDreq(manifest='out/dreqManifest.txt' )
1111
Note: See TracBrowser for help on using the repository browser.