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

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

Updated setup for tag 01.beta.19

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