source: CMIP6dreq/trunk/dreqPy/dreq.py @ 683

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CMIP6dreq/trunk/dreqPy/dreq.py@684
Revision 683, 36.7 KB checked in by mjuckes, 4 years ago (diff)

01.beta.30

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