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

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

Updated setup for tag 01.beta.20

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
424    ec = {}
425    for i in self.coll['__core__'].items:
426      ec[i.label] = i
427
428      ##self.coll[k] = self.ntf( self.recordAttributeDefn[k].header, self.recordAttributeDefn[k].attributes, self.tableItems[k] )
429
430    self.tt1 = {}
431    self.ttl2 = []
432    for v in vl:
433      t = self.parsevcfg(v)
434      tables[t[0].label] = t
435      self.tableClasses[t[0].label] = self.itemClassFact( t, ns=self.ns )
436      thisc = self.tableClasses[t[0].label]
437      self.tt1[t[0].label] = self._sectClass0( idict=t.header._asdict() )
438      self.tt1[t[0].label].maxOccurs = t.header.maxOccurs
439      self.tt1[t[0].label].labUnique = t.header.labUnique
440      self.tt1[t[0].label].level = t.header.level
441      self.tt1[t[0].label].itemLabelMode = t.header.itemLabelMode
442      self.ttl2 += [thisc.__dict__[a] for a in t.attributes]
443    self.coll['__main__'] = self.ntf( self._t2.header, self._t2.attributes, self.ttl2 )
444
445    self.coll['__sect__'] = self.ntf( self._t1.header, self._t1.attributes, [self.tt1[k] for k in self.tt1] )
446
447    for sct in ['__core__','__main__','__sect__']:
448      for k in self.coll[sct].attDefn.keys():
449        assert k in ec, 'Key %s [%s] not found' % (k,sct)
450        self.coll[sct].attDefn[k] = ec[k]
451
452    self.recordAttributeDefn = tables
453    for k in tables.keys():
454      if self.etree:
455        vl = root.findall( './/%s%s' % (self.ns,k) )
456        if len(vl) == 1:
457          v = vl[0]
458          t = v.get( 'title' )
459          i = v.get( 'id' )
460          uid = v.get( 'uid' )
461          useclass = v.get( 'useClass' )
462
463          self.tt1[k].label = k
464          self.tt1[k].title = t
465          self.tt1[k].id = i
466          self.tt1[k].uid = uid
467          self.tt1[k].useClass = useclass
468          self.tableClasses[k]._h = self.tt1[k]
469          il = v.findall( '%sitem' % self.ns )
470          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
471 
472          self.tables[k] = (i,t,len(il))
473       
474          for i in il:
475            ii = self.tableClasses[k](xmlMiniDom=i, etree=True)
476            self.tableItems[k].append( ii )
477        elif len(vl) > 1:
478          assert False, 'not able to handle repeat sections with etree yet'
479      else:
480        vl = self.contentDoc.getElementsByTagName( k )
481        if len(vl) == 1:
482          v = vl[0]
483          t = v.getAttribute( 'title' )
484          i = v.getAttribute( 'id' )
485          il = v.getElementsByTagName( 'item' )
486          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
487 
488          self.tables[k] = (i,t,len(il))
489       
490          for i in il:
491            ii = self.tableClasses[k](xmlMiniDom=i)
492            self.tableItems[k].append( ii )
493        elif len(vl) > 1:
494          l1 = []
495          l2 = []
496          for v in vl:
497            t = v.getAttribute( 'title' )
498            i = v.getAttribute( 'id' )
499            il = v.getElementsByTagName( 'item' )
500            self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
501            l1.append( (i,t,len(il)) )
502         
503            l2i = []
504            for i in il:
505              ii = self.tableClasses[k](xmlMiniDom=i)
506              l2i.append( ii )
507            l2.append( l2i )
508          self.tables[k] = l1
509          self.tableItems[k] = l2
510      self.coll[k] = self.ntf( self.recordAttributeDefn[k].header, self.recordAttributeDefn[k].attributes, self.tableItems[k] )
511 
512  def info(self,ss):
513    """Switchable print function ... switch off by setting self.silent=True"""
514    if not self.silent:
515      print ( ss )
516
517  ###def get(self):
518    ###return self.coll
519
520  def itemClassFact(self, sectionInfo,ns=None):
521     class dreqItem(dreqItemBase):
522       """Inherits all methods from dreqItemBase.
523
524USAGE
525-----
526The instanstiated object contains a single data record. The "_h" attribute links to information about the record and the section it belongs to.
527
528object._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.
529
530object._h: a python named tuple describing the section. E.g. object._h.title is the section title (E.g. "CMOR Variables")
531"""
532       _base=dreqItemBase
533       
534     dreqItem.__name__ = 'dreqItem_%s' % str( sectionInfo.header.label )
535     dreqItem._h = sectionInfo.header
536     dreqItem._a = sectionInfo.attributes
537     dreqItem._d = sectionInfo.defaults
538     if sectionInfo.attributes != None:
539        self.addAttributes(dreqItem, sectionInfo.attributes )
540     ##dreqItem.itemLabelMode = itemLabelMode
541     ##dreqItem.attributes = attributes
542     dreqItem._rc = self.rc
543     dreqItem.ns = ns
544     return dreqItem
545
546  def addAttributes( self, thisClass, attrDict ):
547    """Add a set of attributes, from a dictionary, to a class"""
548    for k in attrDict:
549      setattr( thisClass, '%s' % k , attrDict[k] )
550         
551  def parsevcfg(self,v):
552      """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."""
553      if v in [ None,'__main__']:
554        idict = {'description':'An extended description of the object', 'title':'Record Description', \
555         'techNote':'', 'useClass':'__core__', 'superclass':'rdf:property',\
556         'type':'xs:string', 'uid':'__core__:description', 'label':'label', 'required':'required' }
557        if v == None:
558          vtt = self.nts( '__core__', 'CoreAttributes', 'X.1 Core Attributes', '00000000', 'def', '0', '0', 'false', '__core__' )
559        else:
560          vtt = self.nts( '__main__', 'DataRequestAttributes', 'X.2 Data Request Attributes', '00000001', 'def', '0', '0', 'false', '__main__' )
561      elif v == '__sect__':
562        idict = {'title':'Record Description', \
563         'uid':'__core__:description', 'label':'label', 'useClass':'text', 'id':'id', 'maxOccurs':'', 'itemLabelMode':'', 'level':'', 'labUnique':'', 'required':'' }
564        vtt = self.nts( '__sect__', 'sectionAttributes', 'X.3 Section Attributes', '00000000', 'def', '0', '0', 'false', '__sect__' )
565##<var label="var" uid="SECTION:var" useClass="vocab" title="MIP Variable" id="cmip.drv.001">
566      else:
567        l = v.getAttribute( 'label' )
568        t = v.getAttribute( 'title' )
569        i = v.getAttribute( 'id' )
570        ilm = v.getAttribute( 'itemLabelMode' )
571        lev = v.getAttribute( 'level' )
572        maxo = v.getAttribute( 'maxOccurs' )
573        labu = v.getAttribute( 'labUnique' )
574        il = v.getElementsByTagName( 'rowAttribute' )
575        vtt = self.nts( v.nodeName, l,t,i,ilm,lev, maxo, labu, 's__%s' % v.nodeName )
576        idict = {}
577        for i in il:
578          tt = self.parseicfg(i)
579          idict[tt.label] = tt
580      deflt = self.nt__default( {}, '__unset__' )
581      return self.ntt( vtt, idict, deflt )
582
583  def parseicfg(self,i):
584      """Parse a record attribute specification"""
585      defs = {'type':"xs:string"}
586      ll = []
587      ee = {}
588      for k in ['label','title','type','useClass','techNote','description','uid','required']:
589        if i.hasAttribute( k ):
590          ll.append( i.getAttribute( k ) )
591        else:
592          ll.append( defs.get( k, None ) )
593        ee[k] = ll[-1]
594      l, t, ty, cls, tn, desc, uid, rq = ll
595      self.lastTitle = t
596      if rq in ['0', 'false']:
597        rq = False
598      else:
599        rq = True
600      ee['required'] = rq
601
602      returnClass = True
603      if returnClass:
604        return self._tableClass0( idict=ee )
605      else:
606        return self.nti( i.nodeName, l,t,ty,cls,tn,rq )
607
608class container(object):
609  """Simple container class, to hold a set of dictionaries of lists."""
610  def __init__(self, atl ):
611    self.uid = {}
612    for a in atl:
613      self.__dict__[a] =  collections.defaultdict( list )
614
615class c1(object):
616  def __init__(self):
617    self.a = collections.defaultdict( list )
618
619class index(object):
620  """Create an index of the document. Cross-references are generated from attributes with class 'internalLink'.
621This version assumes that each record is identified by an "uid" attribute and that there is a "var" section.
622Invalid internal links are recorded in tme "missingIds" dictionary.
623For any record, with identifier u, iref_by_uid[u] gives a list of the section and identifier of records linking to that record.
624"""
625
626  def __init__(self, dreq,lock=True):
627    self.silent = True
628    self.uid = {}
629    self.uid2 = collections.defaultdict( list )
630    nativeAtts = ['uid','iref_by_uid','iref_by_sect','missingIds']
631    naok = map( lambda x: not x in dreq, nativeAtts )
632    assert all(naok), 'This version cannot index collections containing sections with names: %s' % str( nativeAtts )
633    self.var_uid = {}
634    self.var_by_name = collections.defaultdict( list )
635    self.var_by_sn = collections.defaultdict( list )
636    self.iref_by_uid = collections.defaultdict( list )
637    irefdict = collections.defaultdict( list )
638    for k in dreq.keys():
639      if 'sn' in dreq[k].attDefn:
640         self.__dict__[k] =  container( ['label','sn'] )
641      else:
642         self.__dict__[k] =  container( ['label'] )
643    ##
644    ## collected names of attributes which carry internal links
645    ##
646      for ka in dreq[k].attDefn.keys():
647        if hasattr( dreq[k].attDefn[ka], 'useClass') and dreq[k].attDefn[ka].useClass == 'internalLink':
648           irefdict[k].append( ka )
649
650    for k in dreq.keys():
651        for i in dreq[k].items:
652          assert 'uid' in i.__dict__, 'uid not found::\n%s\n%s' % (str(i._h),str(i.__dict__) )
653          if 'uid' in self.uid:
654            print ( 'ERROR.100.0001: Duplicate uid: %s [%s]' % (i.uid,i._h.title) )
655            self.uid2[i.uid].append( (k,i) )
656          else:
657### create index bx uid.
658            self.uid[i.uid] = i
659
660    self.missingIds = collections.defaultdict( list )
661    self.iref_by_sect = collections.defaultdict( c1 )
662    for k in dreq.keys():
663        for k2 in irefdict.get( k, [] ):
664          n1 = 0
665          n2 = 0
666          for i in dreq[k].items:
667            id2 = i.__dict__.get( k2 )
668            if id2 != '__unset__':
669              sect = i._h.label
670## append attribute name and target  -- item i.uid, attribute k2 reference item id2
671              self.iref_by_uid[ id2 ].append( (k2,i.uid) )
672              self.iref_by_sect[ id2 ].a[sect].append( i.uid )
673              if id2 in self.uid:
674                n1 += 1
675              else:
676                n2 += 1
677                self.missingIds[id2].append( (k,k2,i.uid) )
678          self.info(  'INFO:: %s, %s%s (%s)' % (k,k2,n1,n2) )
679
680    for k in dreq.keys():
681      for i in dreq[k].items:
682        self.__dict__[k].uid[i.uid] = i
683        self.__dict__[k].label[i.label].append( i.uid )
684        if 'sn' in dreq[k].attDefn:
685          self.__dict__[k].sn[i.sn].append( i.uid )
686
687    if lock:
688      for k in self.iref_by_uid: 
689         self.iref_by_uid[k] = tuple( self.iref_by_uid[k] )
690      for k in self.iref_by_sect:
691        for s in self.iref_by_sect[ k ].a:
692          self.iref_by_sect[ k ].a[s] = tuple( self.iref_by_sect[ k ].a[s] )
693
694  def info(self,ss):
695    if not self.silent:
696      print ( ss )
697
698class ds(object):
699  """Comparison object to assist sorting of lists of dictionaries"""
700  def __init__(self,k):
701    self.k = k
702  def cmp(self,x,y):
703    return cmp( x.__dict__[self.k], y.__dict__[self.k] )
704
705class kscl(object):
706  """Comparison object to assist sorting of dictionaries of class instances"""
707  def __init__(self,idict,k):
708    self.k = k
709    self.idict = idict
710  def cmp(self,x,y):
711    return cmp( self.idict[x].__dict__[self.k], self.idict[y].__dict__[self.k] )
712
713src1 = '../workbook/trial_20150831.xml'
714
715#DEFAULT LOCATION -- changed automatically when building distribution
716defaultDreq = 'dreq.xml'
717#DEFAULT CONFIG
718defaultConfig = 'dreq2Defn.xml'
719
720defaultDreqPath = '%s/%s' % (DOC_DIR, defaultDreq )
721defaultConfigPath = '%s/%s' % (DOC_DIR, defaultConfig )
722
723class loadDreq(object):
724  """Load in a vocabulary document.
725  dreqXML: full path to the XML document
726  configdoc: full path to associated configuration document
727  useShelve: flag to specify whether to retrieve data from cache (not implemented)
728  htmlStyles: dictionary of styling directives which influence structure of html page generates by the "makeHtml" method
729"""
730
731  def __init__(self,dreqXML=defaultDreqPath, configdoc=defaultConfigPath, useShelve=False, htmlStyles=None, strings=False ):
732    self.c = config( thisdoc=dreqXML, configdoc=configdoc, useShelve=useShelve,strings=strings)
733    self.coll = self.c.coll
734    self.inx = index(self.coll)
735    self.itemStyles = {}
736    self.defaultItemLineStyle = lambda i, frm='', ann='': '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
737##
738## add index to Item base class .. so that it can be accessed by item instances
739##
740    dreqItemBase._inx = self.inx
741    dreqItemBase._indexInitialised = True
742##
743## load in additional styling directives
744##
745    if htmlStyles != None:
746      for k in htmlStyles:
747        dreqItemBase._htmlStyle[k] = htmlStyles[k]
748
749##    dreqItemBase._htmlStyle['__general__'] = {'addRemarks':True}
750
751    self.pageTmpl = """<html><head><title>%s</title>
752%s
753<link rel="stylesheet" type="text/css" href="%scss/dreq.css">
754</head><body>
755
756<div id="top">
757   <div id="corner"></div>
758   CMIP6 Data Request
759</div>
760<div id="nav"><div><a href="%s" title="Home">Home</a></div></div>
761
762<div id="section">
763%s
764</div>
765</body></html>"""
766
767  def getHtmlItemStyle(self, sect):
768    """Get the styling method associated with a given section."""
769    if sect in self.itemStyles:
770      return self.itemStyles[sect]
771    return self.defaultItemLineStyle
772
773
774  def _sectionSortHelper(self,title):
775    ##ab = string.split( string.split(title)[0], '.' )
776    ab = title.split(  )[0].split('.')
777    if len( ab ) == 2:
778      a,b = ab
779    ##sorter =  lambda x: [int(y) for y in string.split( string.split(x,':')[0], '.' )]
780      if self.c.rc.isIntStr(a):
781        a = int(a)
782      if self.c.rc.isIntStr(b):
783        b = int(b)
784      rv = (a,b)
785    elif len(ab) == 1:
786      rv = (ab[0],0)
787    else:
788      rv = ab
789    return rv
790
791  def makeHtml(self,odir='./html', ttl0 = 'Data Request Index', annotations=None):
792    """Generate a html view of the vocabularies, using the "__html__" method of the vocabulary item class to generate a
793page for each item and also generating index pages.
794    odir: directory for html files;
795    ttl0: Title for main index (in odir/index.html)"""
796
797    ks = self.inx.uid.keys()
798    ks.sort( kscl( self.inx.uid, 'title' ).cmp )
799    for k in ks:
800      i = self.inx.uid[k]
801      ttl = 'Data Request Record: [%s]%s' % (i._h.label,i.label)
802      bdy = string.join( i.__html__( ghis=self.getHtmlItemStyle ), '\n' )
803      oo = open( '%s/u/%s.html' % (odir,i.uid), 'w' )
804      oo.write( self.pageTmpl % (ttl, jsh, '../', '../index.html', bdy ) )
805      oo.close()
806
807    msg0 = ['<h1>%s</h1>' % ttl0, '<ul>',]
808    msg0.append( '<li><a href="tab01_1_1.html">Overview: priority 1 variables, tier 1 experiments</a></li>' )
809    msg0.append( '<li><a href="tab01_3_3.html">Overview: all variables and experiments</a></li>' )
810    ks = sorted( self.coll.keys() )
811    ee = {}
812    for k in ks:
813      ee[self.coll[k].header.title] = k
814    kks = sorted( ee.keys(),  key = self._sectionSortHelper )
815    for kt in kks:
816      k = ee[kt]
817##
818## sort on item label
819##
820      if annotations != None and k in annotations:
821        ann = annotations[k]
822      else:
823        ann = {}
824
825      self.coll[k].items.sort( ds('label').cmp )
826      ttl = 'Data Request Section: %s' % k
827      msg0.append( '<li><a href="index/%s.html">%s [%s]</a></li>\n' % (k,self.coll[k].header.title,k) )
828      msg = ['<h1>%s</h1>\n' % ttl, '<ul>',]
829      msg.append( '<a href="../index.html">Home</a><br/>\n' )
830      lst = self.getHtmlItemStyle(k)
831     
832      for i in self.coll[k].items:
833        ##m = '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
834       
835        m = lst( i, ann=ann.get( i.label ) )
836        msg.append( m )
837      msg.append( '</ul>' )
838      bdy = string.join( msg, '\n' )
839      oo = open( '%s/index/%s.html' % (odir,k), 'w' )
840      oo.write( self.pageTmpl % (ttl, '', '../', '../index.html', bdy ) )
841      oo.close()
842    msg0.append( '</ul>' )
843    bdy = string.join( msg0, '\n' )
844    oo = open( '%s/index.html' % odir, 'w' )
845    oo.write( self.pageTmpl % (ttl0, '', '', 'index.html', bdy ) )
846    oo.close()
847   
848if __name__ == '__main__':
849  dreq = loadDreq( )
850
Note: See TracBrowser for help on using the repository browser.