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

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

cleaned scope.py

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