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

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

updates

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