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

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

removed rogue line in makeTables.py -> data3.js

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