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

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

cleaning CMOR tables

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