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

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CMIP6dreq/trunk/dreqPy/dreq.py@1292
Revision 1292, 46.6 KB checked in by mjuckes, 10 months ago (diff)

01.00.29

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, collections
7import xml.dom
8import xml.dom.minidom
9import re, shelve, os, sys
10try:
11  from __init__ import DOC_DIR, version, PACKAGE_DIR, VERSION_DIR
12  import utilities
13except:
14  from dreqPy.__init__ import DOC_DIR, version, PACKAGE_DIR, VERSION_DIR
15  from dreqPy import utilities
16
17python2 = True
18if sys.version_info[0] == 3:
19  python2 = False
20  pythonPre27 = False
21elif sys.version_info[0] == 2:
22  pythonPre27 = sys.version_info[1] < 7
23
24charmeTempl = """<span title="Using the CHARMe annotation system">Comment on this page:<a href="%s/%s/%s.html" class="charme-metadata-document"></a></span>
25
26<span>
27<div id="charme-placeholder"></div>
28</span>
29<br/>
30<!-- the charme-placeholder-all-targets appears to be required, but can be hidden ... -->
31<span style="display: None;">
32<div id="charme-placeholder-all-targets"></div>
33</span>
34"""
35
36dreqMonitoring = '''<!-- Global site tag (gtag.js) - Google Analytics -->
37<script async src="https://www.googletagmanager.com/gtag/js?id=UA-126903002-1"></script>
38<script>
39  window.dataLayer = window.dataLayer || [];
40  function gtag(){dataLayer.push(arguments);}
41  gtag('js', new Date());
42
43  gtag('config', 'UA-126903002-1');
44</script>
45'''
46
47
48jsh='''<link type="text/css" href="/css/jquery-ui-1.8.16.custom.css" rel="Stylesheet" />
49 <script src="/js/2013/jquery.min.js" type="text/javascript"></script>
50 <script src="/js/2013/jquery-ui.min.js" type="text/javascript"></script>
51 <script src="/js/2013/jquery.cookie.js" type="text/javascript"></script>
52%s
53
54<link type="text/css" href="/css/dreq.css" rel="Stylesheet" />
55''' % dreqMonitoring
56
57def dref(i,x):
58  return i._inx.uid[i.__dict__[x]]
59
60blockSchemaFile = '%s/%s' % (DOC_DIR, 'BlockSchema.csv' )
61
62class lutilsC(object):
63
64  def __init__(self):
65    pass
66
67  def addAttributes( self, thisClass, attrDict ):
68    """Add a set of attributes, from a dictionary, to a class"""
69    for k in attrDict:
70      setattr( thisClass, '%s' % k , attrDict[k] )
71
72  def itemClassFact(self, sectionInfo,ns=None):
73     class dreqItem(dreqItemBase):
74       """Inherits all methods from dreqItemBase.
75
76USAGE
77-----
78The instanstiated object contains a single data record. The "_h" attribute links to information about the record and the section it belongs to.
79
80object._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.
81
82object._h: a python named tuple describing the section. E.g. object._h.title is the section title (E.g. "CMOR Variables")
83"""
84       _base=dreqItemBase
85       
86     dreqItem.__name__ = 'dreqItem_%s' % str( sectionInfo.header.label )
87     dreqItem._h = sectionInfo.header
88     dreqItem._a = sectionInfo.attributes
89     dreqItem._d = sectionInfo.defaults
90     if sectionInfo.attributes != None:
91        self.addAttributes(dreqItem, sectionInfo.attributes )
92     ##dreqItem.itemLabelMode = itemLabelMode
93     ##dreqItem.attributes = attributes
94     dreqItem._rc = rechecks()
95     dreqItem.ns = ns
96     return dreqItem
97
98##
99## from http://stackoverflow.com/questions/4474754/how-to-keep-comments-while-parsing-xml-using-python-elementtree
100##
101## does not work for python3 ... may not be needed.
102##
103## needed in python2.6 to preserve comments in output XML
104##
105def getParser():
106  import xml.etree.ElementTree as ET
107  class PCParser(ET.XMLTreeBuilder):
108
109     def __init__(self):
110       ET.XMLTreeBuilder.__init__(self)
111       # assumes ElementTree 1.2.X
112       self._parser.CommentHandler = self.handle_comment
113
114     def handle_comment(self, data):
115       self._target.start(ET.Comment, {})
116       self._target.data(data)
117       self._target.end(ET.Comment)
118
119  return PCParser
120
121def loadBS(bsfile):
122  """Read in the 'BlockSchema' definitions of the attributes defining attributes"""
123  ii = open( bsfile, 'r' ).readlines()
124  ll = []
125  for l in ii:
126    ll.append( [x for x in l.strip().split('\t') ] )
127  cc = collections.defaultdict( dict )
128 
129  for l in ll[3:]:
130    l[-1] = l[-1] == '1'
131    if len(l) < len(ll[2]):
132      l.append( '' )
133    try:
134      for i in range( len(ll[2]) ):
135        cc[l[0]][ll[2][i]] = l[i]
136    except:
137      print (l)
138      raise
139  return cc
140
141class rechecks(object):
142  """Checks to be applied to strings"""
143  def __init__(self):
144    self.__isInt = re.compile( '^-{0,1}[0-9]+$' )
145
146  def isIntStr( self, tv ):
147    """Check whether a string is a valid representation of an integer."""
148    if type( tv ) not in [type(''),type(u'')]:
149      self.reason = 'NOT STRING'
150      return False
151    ok = self.__isInt.match( tv ) != None
152    if not ok:
153      self.reason = 'Failed to match regular expression for integers'
154    else:
155      self.reason = ''
156    return ok
157
158class dreqItemBase(object):
159       __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."""
160       _indexInitialised = False
161       _inx = None
162       _urlBase = ''
163       _htmlStyle = {}
164       _linkAttrStyle = {}
165       __charmeEnable__ = {}
166       _extraHtml = {}
167       _strictRequired = False
168
169       def __init__(self,idict=None,xmlMiniDom=None,id='defaultId',etree=False):
170         self._strictRead = True
171         dictMode = idict != None
172         mdMode = xmlMiniDom != None
173         self._htmlTtl = None
174         assert not( dictMode and mdMode), 'Mode must be either dictionary of minidom: both assigned'
175         assert dictMode or mdMode, 'Mode must be either dictionary of minidom: neither assigned'
176         ##self._defaults = { }
177         ##self._globalDefault = '__unset__'
178         self._contentInitialised = False
179         self._greenIcon = '<img height="12pt" src="/images/154g.png" alt="[i]"/>'
180         if dictMode:
181           self.dictInit( idict )
182         elif mdMode:
183           try:
184             self.mdInit( xmlMiniDom, etree=etree )
185           except:
186             print ('Exception raise initialising data request item')
187             raise
188
189       def hasattr(self,tag):
190         """Return True id the argument passed is the name of an attribute of this instance."""
191         return tag in self.__dict__
192
193       def __repr__(self):
194         """Provide a one line summary of identifying the object."""
195         if self._contentInitialised:
196           return 'Item <%s>: [%s] %s' % (self._h.title,self.label,self.title)
197         else:
198           return 'Item <%s>: uninitialised' % self._h.title
199
200       def __info__(self,full=False):
201         """Print a summary of the data held in the object as a list of key/value pairs"""
202         if self._contentInitialised:
203           print ( 'Item <%s>: [%s] %s' % (self._h.title,self.label,self.title) )
204           for a in self.__dict__.keys():
205             if a[0] != '_' or full:
206               if hasattr( self._a[a], 'useClass') and self._a[a].useClass == 'internalLink' and self._base._indexInitialised:
207                 if self.__dict__[a] in self._base._inx.uid:
208                   targ = self._base._inx.uid[ self.__dict__[a] ]
209                   print ( '   %s: [%s]%s [%s]' % ( a, targ._h.label, targ.label, self.__dict__[a] ) )
210                 else:
211                   print ( '   %s: [ERROR: key not found] [%s]' % ( a, self.__dict__[a] ) )
212               else:
213                 print ( 'INFO:    %s: %s' % ( a, self.__dict__[a] ) )
214         else:
215           print ( 'Item <%s>: uninitialised' % self.sectionLabel )
216
217       def __href__(self,odir="",label=None,title=None):
218         """Generate html text for a link to this item."""
219         igns =  ['','__unset__']
220         if title == None:
221           if self._htmlTtl == None:
222             if 'description' in self.__dict__ and self.description != None and self.description.strip( ) not in igns:
223               ttl = self.description
224             elif 'title' in self.__dict__ and self.title != None and self.title.strip( ) not in igns:
225               ttl = self.title
226             else:
227               ttl = self.label
228             ttl = ttl.replace( '"', '&quot;' )
229             ttl = ttl.replace( '<', '&lt;' )
230             self._htmlTtl = ttl.replace( '>', '&gt;' )
231           title=self._htmlTtl
232         if label == None:
233             label = self.uid
234         if odir == '':
235           odir = './'
236             
237         return '<span title="%s"><a href="%s%s.html">%s</a></span>' % (title,odir,self.uid,label)
238
239       def htmlLinkAttrListStyle(self,a,targ,frm=None):
240           xx = '; '.join( ['%s [%s]' % (x.label, x.__href__()) for x in targ])
241           return '<li>%s: [%s] %s</li>' % ( a, targ[0]._h.label, xx )
242
243       def getHtmlLinkAttrStyle(self,a):
244           """Return a string containing a html fragment for a link to an attribute."""
245           if a in self.__class__._linkAttrStyle:
246             return self.__class__._linkAttrStyle[a]
247           else:
248             return lambda a,targ, frm='': '<li>%s: [%s] %s [%s]</li>' % ( a, targ._h.label, targ.label, targ.__href__() )
249
250       def __htmlLink__(self,a, sect,app):
251          """Create html line for a link or list of links"""
252          if self._a[a].useClass == 'internalLink':
253                   lst = self.getHtmlLinkAttrStyle(a)
254                   try:
255                     targ = self._base._inx.uid[ self.__dict__[a] ]
256                     m = lst( app, targ, frm=sect )
257                   except:
258                     print ( 'INFO.cex.00001:',a, self.__dict__[a], sect, self.label )
259                     m = '<li>%s: %s .... broken link</li>' % ( app, self.__dict__[a] )
260                     print ( '%s: %s .... broken link' % ( app, self.__dict__[a]  ) )
261                     raise
262          else:
263            assert self._a[a].useClass == 'internalLinkList', 'Unrecognised useClass in __htmlLink__: %s' % self._a[a].useClass
264            m = self.htmlLinkAttrListStyle( app, [self._base._inx.uid[u] for u in self.__dict__[a] ], frm=sect )
265            return m
266          return m
267                   ##m = '<li>%s, %s: [%s] %s [%s]</li>' % ( a, self.__class__.__dict__[a].__href__(label=self._greenIcon), targ._h.label, targ.label, targ.__href__() )
268       def __html__(self,ghis=None):
269         """Create html view"""
270         msg = []
271         if self._contentInitialised:
272           sect = self._h.label
273           msg.append( '<h1>%s: [%s] %s</h1>' % (self._h.title,self.label,self.title) )
274           if sect in self.__charmeEnable__:
275             msg.append( charmeTempl % (self.__charmeEnable__[sect].site, 'u', self.uid) )
276           msg.append( '<a href="../index.html">Home</a> &rarr; <a href="../index/%s.html">%s section index</a><br/>\n' % (sect, self._h.title) )
277           msg.append( '<ul>' )
278           for a in self.__dict__.keys():
279             if a[0] != '_':
280               app = '%s%s' % (a, self.__class__.__dict__[a].__href__(label=self._greenIcon) )
281               if hasattr( self._a[a], 'useClass') and self._a[a].useClass in ['internalLink','internalLinkList'] and self._base._indexInitialised:
282                 if self.__dict__[a] == '__unset__':
283                   m = '<li>%s: %s [missing link]</li>' % ( app, self.__dict__[a] )
284                 else:
285                   m = self.__htmlLink__(a, sect,app)
286               elif hasattr( self._a[a], 'useClass') and self._a[a].useClass == 'externalUrl':
287                 m = '<li>%s: <a href="%s" title="%s">%s</a></li>' % ( app, self.__dict__[a], self._a[a].description, self._a[a].title )
288               else:
289                 m = '<li>%s: %s</li>' % ( app, self.__dict__[a] )
290               msg.append( m )
291           msg.append( '</ul>' )
292           sect = self._h.label
293           if sect in self._extraHtml:
294             rc, href, hlab = self._extraHtml[sect](self)
295             if rc:
296                msg.append( '<p><a href="%s">%s</a></p>' % (href,hlab) )
297##
298## add list of inward references
299##
300           if self._base._indexInitialised:
301             msg += self.__irefHtml__(sect,ghis)
302
303           if sect in self.__charmeEnable__:
304             msg.append( '<script src="/js/dreq/charme/charme.js"></script>' )
305         else:
306           msg.append( '<b>Item %s: uninitialised</b>' % self.sectionLabel )
307         return msg
308
309
310       def __irefHtml__(self, sect,ghis):
311         """Returns html (as a list of text strings) for inward references"""
312         if self._htmlStyle.get( sect, {} ).get( 'getIrefs', None ) == None:
313           return []
314         
315         tl = self._htmlStyle[sect]['getIrefs']
316         doall = '__all__' in tl
317         if doall:
318           tl = self._inx.iref_by_sect[self.uid].a.keys()
319         tl1 = []
320         for t in tl:
321           if t in self._inx.iref_by_sect[self.uid].a and len( self._inx.iref_by_sect[self.uid].a[t] ) > 0:
322             tl1.append( t )
323         am = []
324         if len(tl1) > 0:
325               am.append( '''<div class="demo">\n<div id="tabs">\n<ul>''' )
326               for t in tl1:
327                   u0 = self._inx.iref_by_sect[self.uid].a[t][0]
328                   this1 = '<li><a href="#tabs-%s">%s</a></li>' % (t,self._inx.uid[u0]._h.title )
329                   am.append( this1 )
330               am.append( '</ul>' )
331               for t in tl1:
332                   u0 = self._inx.iref_by_sect[self.uid].a[t][0]
333                   am.append( '<div id="tabs-%s">' % t )
334                   am.append( '<h3>%s</h3>' % self._inx.uid[u0]._h.title )
335                   am.append( '<ul>' )
336                   items = [self._inx.uid[u] for  u in self._inx.iref_by_sect[self.uid].a[t] ]
337                   ##items.sort( ds('label').cmp )
338                   items = sorted( items, key=ds('label').key )
339                   for targ in items:
340                     if ghis == None:
341                       m = '<li>%s:%s [%s]</li>' % ( targ._h.label, targ.label, targ.__href__() )
342                     else:
343                       lst = ghis( targ._h.label )
344                       m = lst( targ, frm=sect )
345                     am.append( m )
346                   am.append( '</ul>' )
347                   am.append( '</div>' )
348               if len(am) > 0:
349                 am.append( '</div>' )
350                 am0 = [ '<h2>Links from other sections</h2>' ,]
351                 am0.append( ''' <script>
352        $(function() {
353                $( "#tabs" ).tabs({cookie: { expires: 1 } });
354        });
355 </script>
356<!-- 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-->''' )
357                 return am0 + am
358         else:
359           return []
360               
361       def dictInit( self, idict ):
362         __doc__ = """Initialise from a dictionary."""
363         for a in self._a.keys():
364           vv = False
365           if a in idict:
366             val = idict[a]
367             vv = True
368           else:
369             if type( self.__class__.__dict__[a] ) not in (type( ''), type( u'' )) and (not self.__class__.__dict__[a].required):
370               if hasattr( self, a):
371                 setattr( self, a, None )
372                 ##self.a = None
373                 delattr( self, a )
374                 ##if a in self.__dict__:
375                   ##self.__dict__.pop(a)
376               else:
377                 print ( 'ERROR: attempt to remove non-existent attribute: %s' % a )
378             else:
379               val = self._d.defaults.get( a, self._d.glob )
380               vv = True
381           if vv:
382             setattr( self, a, val )
383         self._contentInitialised = True
384
385       def mdInit( self, el, etree=False ):
386         __doc__ = """Initialisation from a mindom XML element. The list of attributes must be set by the class factory before the class is initialised"""
387         deferredHandling=False
388         nw1 = 0
389         tvtl = []
390         if etree:
391           ks = set( el.keys() )
392           for a in self._a.keys():
393             if a in ks:
394               aa = '%s%s' % (self.ns,a)
395               tvtl.append( (a,True, str( el.get( a ) ) ) )
396             elif self._a[a].__dict__.get( 'required', True ) in [False,'false',u'false']:
397               tvtl.append( (a,True,None) )
398             else:
399               tvtl.append( (a,False,None) )
400               if self._strictRequired:
401                 assert False, 'Required attribute absent: %s:: %s' % (a,str(el.__dict__))
402         else:
403           for a in self._a.keys():
404             if el.hasAttribute( a ):
405               tvtl.append( (a,True, str( el.getAttribute( a ) ) ) )
406##
407## attributes are treated as required unless they have a required attribute set to false
408##
409             elif self._a[a].__dict__.get( 'required', True ) not in [False,'false',u'false']:
410               tvtl.append( (a,False,None) )
411       
412         for a,tv,v in tvtl:
413           if a == 'uid':
414             uid = v
415
416         for a,tv,v in tvtl:
417           if tv:
418             erase = False
419             if v == None:
420               pass
421               erase = True
422             elif self._a[a].type == u'xs:float':
423               if v == '':
424                 v = None
425               else:
426                 try:
427                   v = float(v)
428                 except:
429                   print ( 'Failed to convert real number: %s,%s,%s' % (a,tv,v) )
430                   raise
431             elif self._a[a].type in [u'aa:st__floatList', u'aa:st__floatListMonInc']:
432                 v = [float(x) for x in v.split()]
433             elif self._a[a].type in [u'aa:st__integerList', u'aa:st__integerListMonInc']:
434               try:
435                 v = [int(x) for x in v.split()]
436               except:
437                 v = [int(float(x)) for x in v.split()]
438                 print ('WARN: non-compliant integer list [%s:%s]: %s' % (thissect,a,v))
439               if self._a[a].type in [u'aa:st__integerListMonInc'] and self._strictRead:
440                   for i in range(len(v)-1):
441                     assert v[i] <= v[i+1], 'Attribute %s of type %s with non-monotonic value: %s' % (a,self._a[a].type,str(v))
442             elif self._a[a].type == u'xs:integer':
443               if self._rc.isIntStr( v ):
444                 v = int(v)
445               else:
446                 v = v.strip()
447                 thissect = '%s [%s]' % (self._h.title,self._h.label)
448                 if v in [ '',u'',' ', u' ', [], '[]']:
449                   if nw1 < 20:
450                     print ( 'WARN.050.0001: input integer non-compliant: %s: %s: "%s" -- set to zero' % (thissect,a,v) )
451                     nw1 += 1
452                   v = 0
453                 else:
454                   try:
455                     v0 = v
456                     v = int(float(v))
457                     print ( 'WARN: input integer non-compliant: %s: %s: %s [%s] %s' % (thissect,a,v0,v,uid) )
458                   except:
459                     msg = 'ERROR: failed to convert integer: %s: %s: %s, %s' % (thissect,a,v,type(v))
460                     deferredHandling=True
461             elif self._a[a].type == u'xs:boolean':
462               v = v in ['true','1']
463             elif self._a[a].type == u'aa:st__stringList':
464               if v.find(' ') != -1:
465                 v = tuple( v.split() )
466               else:
467                 v = (v,)
468             elif self._a[a].type not in [u'xs:string', u'aa:st__uid', 'aa:st__fortranType', 'aa:st__configurationType','aa:st__sliceType']:
469               print ('ERROR: Type %s not recognised [%s:%s]' % (self._a[a].type,self._h.label,a) )
470
471             if erase:
472               ### need to overwrite attribute (which is inherited from parent class) before deleting it.
473               ### this may not be needed in python3
474               self.__dict__[a] = '__tmp__'
475               delattr( self, a )
476             else:
477               self.__dict__[a] = v
478           else:
479             if a in ['uid',]:
480               thissect = '%s' % (self._h.title)
481               print ( 'ERROR.020.0001: missing uid: %s: a,tv,v: %s, %s, %s\n %s' % (thissect,a,tv,v,str( self.__dict__ )) )
482               if etree:
483                 ##p = self.parent_map( el )
484                 print ( ks )
485                 raise
486                 import sys
487                 sys.exit(0)
488             self.__dict__[a] = self._d.defaults.get( a, self._d.glob )
489
490           if deferredHandling:
491             print ( msg )
492
493         self._contentInitialised = True
494
495   
496class config(object):
497  """Read in a vocabulary collection configuration document and a vocabulary document"""
498
499  def __init__(self, configdoc='out/dreqDefn.xml', thisdoc='../workbook/trial_20150724.xml', manifest=None, useShelve=False, strings=False,configOnly=False,lxml=False):
500    self.rc = rechecks()
501    self.lu = lutilsC()
502    self.silent = True
503    self.configOnly = configOnly
504    self.coll = {}
505    self.recordAttributeDefn = {}
506
507    self.nts = collections.namedtuple( 'sectdef', ['tag','label','title','id','itemLabelMode','level','maxOccurs','labUnique','uid','description'] )
508    self.nti = collections.namedtuple( 'itemdef', ['tag','label','title','type','useClass','techNote','required'] )
509    self.ntt = collections.namedtuple( 'sectinit', ['header','attributes','defaults'] )
510    self.nt__default = collections.namedtuple( 'deflt', ['defaults','glob'] )
511    self.ntf = collections.namedtuple( 'sect', ['header','attDefn','items'] )
512    self.bscc = loadBS(blockSchemaFile)
513    self.strings = strings
514    self.docl = []
515
516    self.tt0 = {}
517    self.tt1 = {}
518    self.ttl2 = []
519    self.docs = {}
520    self.version = None
521    self.useLxml = lxml
522
523    if manifest != None:
524      assert os.path.isfile( manifest ), 'Manifest file not found: %s' % manifest
525      ii = open(manifest).readlines() 
526      docl = []
527      for l in ii[1:]:
528        bits = l.strip().split()
529        assert len( bits ) > 1, 'Failed to parse line in manifest %s: \n%s' % (manifest,l)
530        bb = []
531        for b in bits[:2]:
532          if not os.path.isfile( b ):
533             b = '%s/%s' % (PACKAGE_DIR,b)
534          assert os.path.isfile( b ), 'File %s not found (listed in %s)' % (b,manifest )
535          bb.append( b )
536        docl.append( tuple( bb ) )
537      for d,c in docl:
538        self.__read__(d, c)
539    else:
540      self.__read__(thisdoc, configdoc)
541
542  def getByUid(self,uid):
543    mmm = []
544    for fn,rr in self.docs.items():
545       root, doc = rr
546       mm = root.findall( ".//*[@uid='%s']" % uid )
547       mmm += mm
548       print ('%s: %s' % (fn, str(mm)) )
549    return mmm
550
551  def __read__(self, thisdoc, configdoc):
552    self.vdef = configdoc
553    self.vsamp = thisdoc
554    self.docl.append( (thisdoc,configdoc) )
555    fn = thisdoc.split( '/' )[-1]
556
557    if self.strings:
558      doc = xml.dom.minidom.parseString( self.vdef  )
559    else:
560      doc = xml.dom.minidom.parse( self.vdef  )
561##
562## elementTree parsing implemented for main document
563##
564    self.etree = False
565    self.etree = True
566    self.ns = None
567    if not self.configOnly:
568      if self.etree:
569        if self.useLxml:
570          import lxml
571          import lxml.etree as et
572        else:
573          import xml.etree.cElementTree as cel
574     
575        if not pythonPre27:
576          ## this for of namespace registration not available in 2.6
577          ## absence of registration means module cannot write data exactly as read.
578          ##
579          if self.useLxml:
580            ##et.register_namespace('', "urn:w3id.org:cmip6.dreq.dreq:a")
581            et.register_namespace('pav', "http://purl.org/pav/2.3")
582          else:
583            cel.register_namespace('', "urn:w3id.org:cmip6.dreq.dreq:a")
584            cel.register_namespace('pav', "http://purl.org/pav/2.3")
585
586        if not self.strings:
587          if self.useLxml:
588            ##parser = etree.XMLParser( remove_blank_text=True )
589            ##self.contentDoc = et.parse( self.vsamp, parser  )
590            self.contentDoc = et.parse( self.vsamp  )
591          elif python2:
592            parser = getParser()()
593            self.contentDoc = cel.parse( self.vsamp, parser=parser )
594          else:
595            self.contentDoc = cel.parse( self.vsamp )
596
597          root = self.contentDoc.getroot()
598        else:
599          root = cel.fromstring(self.vsamp)
600
601        self.docs[fn] = (root, self.contentDoc)
602        bs = root.tag.split( '}' )
603        if len( bs ) > 1:
604          self.ns = bs[0] + '}'
605        else:
606          self.ns = None
607        vl = root.findall( './/{http://purl.org/pav/2.3}version' )
608        if self.version != None:
609          if vl[0].text != self.version:
610            print ('WARNING: version difference between %s [%s] and %s [%s]' % (self.docl[0][0],self.version,thisdoc,vl[0].text) )
611        else:
612          self.version = vl[0].text
613        self.parent_map = dict((c, p) for p in root.getiterator() for c in p)
614      else:
615        if self.strings:
616          self.contentDoc = xml.dom.minidom.parseString( self.vsamp  )
617        else:
618          self.contentDoc = xml.dom.minidom.parse( self.vsamp )
619
620          vl = self.contentDoc.getElementsByTagName( 'prologue' )
621          v = vl[0].getElementsByTagName( 'pav:version' )
622          self.version = v[0].firstChild.data
623        self.ns = None
624
625    vl = doc.getElementsByTagName( 'table' ) + doc.getElementsByTagName( 'annextable' )
626    self.tables = {}
627    tables = {}
628    self.tableClasses = {}
629    self.tableItems = collections.defaultdict( list )
630##
631## this loads in some metadata, but not yet in a useful way.
632##
633    self._t0 = self.parsevcfg(None)
634    self._t2 = self.parsevcfg('__main__')
635    self._tableClass0 = self.lu.itemClassFact( self._t0, ns=self.ns )
636##
637## define a class for the section heading records.
638##
639    self._t1 = self.parsevcfg('__sect__')
640##
641## when used with manifest .. need to preserve entries in "__main__" from each document.
642##
643    self._sectClass0 = self.lu.itemClassFact( self._t1, ns=self.ns )
644
645    for k in self.bscc:
646      self.tt0[k] = self._tableClass0(idict=self.bscc[k])
647      if k in self._t0.attributes:
648        setattr( self._tableClass0, '%s' % k, self.tt0[k] )
649      if k in self._t1.attributes:
650        setattr( self._sectClass0, '%s' % k, self.tt0[k] )
651
652##
653## save header information, as for recordAttributeDefn below
654##
655    self._recAtDef = {'__core__':self._t0, '__sect__':self._t1}
656##
657## addition of __core__ to coll dictionary ..
658##
659    self.coll['__core__'] = self.ntf( self._t0.header, self._t0.attributes, [self.tt0[k] for k in self.tt0] )
660
661    ec = {}
662    for i in self.coll['__core__'].items:
663      ec[i.label] = i
664
665    for v in vl:
666      t = self.parsevcfg(v)
667      tables[t[0].label] = t
668      self.tableClasses[t[0].label] = self.lu.itemClassFact( t, ns=self.ns )
669      thisc = self.tableClasses[t[0].label]
670      self.tt1[t[0].label] = self._sectClass0( idict=t.header._asdict() )
671      self.tt1[t[0].label].maxOccurs = t.header.maxOccurs
672      self.tt1[t[0].label].labUnique = t.header.labUnique
673      self.tt1[t[0].label].level = t.header.level
674      self.tt1[t[0].label].uid = t.header.uid
675      self.tt1[t[0].label].itemLabelMode = t.header.itemLabelMode
676      self.ttl2 += [thisc.__dict__[a] for a in t.attributes]
677    mil = [t[1] for t in self._t2.attributes.items()]
678    self.coll['__main__'] = self.ntf( self._t2.header, self._t2.attributes, self.ttl2 )
679
680    self.coll['__sect__'] = self.ntf( self._t1.header, self._t1.attributes, [self.tt1[k] for k in self.tt1] )
681
682    for sct in ['__core__','__main__','__sect__']:
683      for k in self.coll[sct].attDefn.keys():
684        assert k in ec, 'Key %s [%s] not found' % (k,sct)
685        self.coll[sct].attDefn[k] = ec[k]
686
687    for k in tables:
688      self.recordAttributeDefn[k] = tables[k]
689
690    if self.configOnly:
691      return
692    for k in tables.keys():
693      if self.etree:
694        vl = root.findall( './/%s%s' % (self.ns,k) )
695        if len(vl) == 1:
696          v = vl[0]
697          t = v.get( 'title' )
698          i = v.get( 'id' )
699          uid = v.get( 'uid' )
700          useclass = v.get( 'useClass' )
701
702          self.tt1[k].label = k
703          self.tt1[k].title = t
704          self.tt1[k].id = i
705          self.tt1[k].uid = uid
706          self.tt1[k].useClass = useclass
707          self.tableClasses[k]._h = self.tt1[k]
708          il = v.findall( '%sitem' % self.ns )
709          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
710 
711          self.tables[k] = (i,t,len(il))
712       
713          for i in il:
714            try:
715              ii = self.tableClasses[k](xmlMiniDom=i, etree=True)
716            except:
717              print ('Exception raises instantiating item in section %s' % k)
718              print ('At item uid=%s' % str(i) )
719              raise
720            self.tableItems[k].append( ii )
721        elif len(vl) > 1:
722          assert False, 'not able to handle repeat sections with etree yet'
723      else:
724        vl = self.contentDoc.getElementsByTagName( k )
725        if len(vl) == 1:
726          v = vl[0]
727          t = v.getAttribute( 'title' )
728          i = v.getAttribute( 'id' )
729          il = v.getElementsByTagName( 'item' )
730          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
731 
732          self.tables[k] = (i,t,len(il))
733       
734          for i in il:
735            ii = self.tableClasses[k](xmlMiniDom=i)
736            self.tableItems[k].append( ii )
737        elif len(vl) > 1:
738          l1 = []
739          l2 = []
740          for v in vl:
741            t = v.getAttribute( 'title' )
742            i = v.getAttribute( 'id' )
743            il = v.getElementsByTagName( 'item' )
744            self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
745            l1.append( (i,t,len(il)) )
746         
747            l2i = []
748            for i in il:
749              ii = self.tableClasses[k](xmlMiniDom=i)
750              l2i.append( ii )
751            l2.append( l2i )
752          self.tables[k] = l1
753          self.tableItems[k] = l2
754      self.coll[k] = self.ntf( self.recordAttributeDefn[k].header, self.recordAttributeDefn[k].attributes, self.tableItems[k] )
755 
756  def info(self,ss):
757    """Switchable print function ... switch off by setting self.silent=True"""
758    if not self.silent:
759      print ( ss )
760
761  def parsevcfg(self,v):
762      """Parse a section definition element, including all the record attributes. The results are returned as a named tuple of attributes for the section and a dictionary of record attribute specifications."""
763      if v in [ None,'__main__']:
764        idict = {'description':'An extended description of the object', 'title':'Record Description', \
765           'techNote':'', 'useClass':'__core__', 'superclass':'rdf:property',\
766           'type':'xs:string', 'uid':'__core__:description', 'label':'label', 'required':'required' }
767        if v == None:
768          vtt = self.nts( '__core__', 'CoreAttributes', 'X.1 Core Attributes', '00000000', 'def', '0', '0', 'false', '__core__', 'The core attributes, used in defining sections and attributes' )
769        else:
770          vtt = self.nts( '__main__', 'DataRequestAttributes', 'X.2 Data Request Attributes', '00000001', 'def', '0', '0', 'false', '__main__' , 'The attributes used to define data request records')
771      elif v == '__sect__':
772        idict = {'title':'Record Description', \
773         'uid':'__core__:description', 'label':'label', 'useClass':'text', 'id':'id', 'maxOccurs':'', 'itemLabelMode':'', 'level':'', 'labUnique':'' }
774        vtt = self.nts( '__sect__', 'sectionAttributes', 'X.3 Section Attributes', '00000000', 'def', '0', '0', 'false', '__sect__', 'The attributes used to define data request sections' )
775##<var label="var" uid="SECTION:var" useClass="vocab" title="MIP Variable" id="cmip.drv.001">
776      else:
777        l = v.getAttribute( 'label' )
778        t = v.getAttribute( 'title' )
779        i = v.getAttribute( 'id' )
780        u = v.getAttribute( 'uid' )
781        ilm = v.getAttribute( 'itemLabelMode' )
782        lev = v.getAttribute( 'level' )
783        maxo = v.getAttribute( 'maxOccurs' )
784        labu = v.getAttribute( 'labUnique' )
785        des = v.getAttribute( 'description' )
786        il = v.getElementsByTagName( 'rowAttribute' )
787        ##vtt = self.nts( v.nodeName, l,t,i,ilm,lev, maxo, labu, 's__%s' % v.nodeName )
788        vtt = self.nts( v.nodeName, l,t,i,ilm,lev, maxo, labu, u, des )
789        idict = {}
790        for i in il:
791          tt = self.parseicfg(i)
792          idict[tt.label] = tt
793      deflt = self.nt__default( {}, '__unset__' )
794
795      ## return a named tuple: (header, attributes, defaults)
796      return self.ntt( vtt, idict, deflt )
797
798  def parseicfg(self,i):
799      """Parse a record attribute specification"""
800      defs = {'type':"xs:string"}
801      ll = []
802      ee = {}
803      for k in ['label','title','type','useClass','techNote','description','uid','required']:
804        if i.hasAttribute( k ):
805          ll.append( i.getAttribute( k ) )
806        else:
807          ll.append( defs.get( k, None ) )
808        ee[k] = ll[-1]
809      l, t, ty, cls, tn, desc, uid, rq = ll
810      self.lastTitle = t
811      if rq in ['0', 'false']:
812        rq = False
813      else:
814        rq = True
815      ee['required'] = rq
816
817      returnClass = True
818      if returnClass:
819        return self._tableClass0( idict=ee )
820      else:
821        return self.nti( i.nodeName, l,t,ty,cls,tn,rq )
822
823class container(object):
824  """Simple container class, to hold a set of dictionaries of lists."""
825  def __init__(self, atl ):
826    self.uid = {}
827    for a in atl:
828      self.__dict__[a] =  collections.defaultdict( list )
829
830class c1(object):
831  def __init__(self):
832    self.a = collections.defaultdict( list )
833
834class index(object):
835  """Create an index of the document. Cross-references are generated from attributes with class 'internalLink'.
836This version assumes that each record is identified by an "uid" attribute and that there is a "var" section.
837Invalid internal links are recorded in tme "missingIds" dictionary.
838For any record, with identifier u, iref_by_uid[u] gives a list of the section and identifier of records linking to that record.
839"""
840
841  def __init__(self, dreq,lock=True):
842    self.silent = True
843    self.uid = {}
844    self.uid2 = collections.defaultdict( list )
845    nativeAtts = ['uid','iref_by_uid','iref_by_sect','missingIds']
846    naok = map( lambda x: not x in dreq, nativeAtts )
847    assert all(naok), 'This version cannot index collections containing sections with names: %s' % str( nativeAtts )
848    self.var_uid = {}
849    self.var_by_name = collections.defaultdict( list )
850    self.var_by_sn = collections.defaultdict( list )
851    self.iref_by_uid = collections.defaultdict( list )
852    irefdict = collections.defaultdict( list )
853    for k in dreq.keys():
854      if 'sn' in dreq[k].attDefn:
855         self.__dict__[k] =  container( ['label','sn'] )
856      else:
857         self.__dict__[k] =  container( ['label'] )
858    ##
859    ## collected names of attributes which carry internal links
860    ##
861      for ka in dreq[k].attDefn.keys():
862        if hasattr( dreq[k].attDefn[ka], 'useClass') and dreq[k].attDefn[ka].useClass in  ['internalLink','internalLinkList']:
863           irefdict[k].append( ka )
864
865    for k in dreq.keys():
866        for i in dreq[k].items:
867          assert 'uid' in i.__dict__, 'uid not found::\n%s\n%s' % (str(i._h),str(i.__dict__) )
868          if 'uid' in self.uid:
869            print ( 'ERROR.100.0001: Duplicate uid: %s [%s]' % (i.uid,i._h.title) )
870            self.uid2[i.uid].append( (k,i) )
871          else:
872### create index bx uid.
873            self.uid[i.uid] = i
874
875    self.missingIds = collections.defaultdict( list )
876    self.iref_by_sect = collections.defaultdict( c1 )
877    for k in dreq.keys():
878        for k2 in irefdict.get( k, [] ):
879          n1 = 0
880          n2 = 0
881          for i in dreq[k].items:
882            if k2 in i.__dict__:
883              id2 = i.__dict__.get( k2 )
884              if id2 != '__unset__':
885                sect = i._h.label
886  ## append attribute name and target  -- item i.uid, attribute k2 reference item id2
887                if type(id2) not in [type( [] ),type(())]:
888                  id2 = [id2,]
889                for u in id2:
890                  self.iref_by_uid[ u ].append( (k2,i.uid) )
891                  self.iref_by_sect[ u ].a[sect].append( i.uid )
892                  if u in self.uid:
893                    n1 += 1
894                  else:
895                    n2 += 1
896                    self.missingIds[u].append( (k,k2,i.uid) )
897          self.info(  'INFO:: %s, %s%s (%s)' % (k,k2,n1,n2) )
898
899    for k in dreq.keys():
900      for i in dreq[k].items:
901        self.__dict__[k].uid[i.uid] = i
902        self.__dict__[k].label[i.label].append( i.uid )
903        if 'sn' in dreq[k].attDefn:
904          self.__dict__[k].sn[i.sn].append( i.uid )
905
906    if lock:
907      for k in self.iref_by_uid: 
908         self.iref_by_uid[k] = tuple( self.iref_by_uid[k] )
909      for k in self.iref_by_sect:
910        for s in self.iref_by_sect[ k ].a:
911          self.iref_by_sect[ k ].a[s] = tuple( self.iref_by_sect[ k ].a[s] )
912
913  def info(self,ss):
914    if not self.silent:
915      print ( ss )
916
917class ds(object):
918  """Comparison object to assist sorting of lists of dictionaries"""
919  def __init__(self,k):
920    self.k = k
921  def cmp(self,x,y):
922    return cmp( x.__dict__[self.k], y.__dict__[self.k] )
923  def key(self,x):
924    return x.__dict__[self.k]
925
926class kscl(object):
927  """Comparison object to assist sorting of dictionaries of class instances"""
928  def __init__(self,idict,k):
929    self.k = k
930    self.idict = idict
931  def cmp(self,x,y):
932    return cmp( self.idict[x].__dict__[self.k], self.idict[y].__dict__[self.k] )
933  def key(self,x):
934    return self.idict[x].__dict__[self.k]
935
936src1 = '../workbook/trial_20150831.xml'
937
938#DEFAULT LOCATION -- changed automatically when building distribution
939defaultDreq = 'dreq.xml'
940#DEFAULT CONFIG
941defaultConfig = 'dreq2Defn.xml'
942
943defaultDreqPath = '%s/%s' % (DOC_DIR, defaultDreq )
944defaultConfigPath = '%s/%s' % (DOC_DIR, defaultConfig )
945defaultManifestPath = '%s/dreqManifest.txt' % DOC_DIR
946
947class loadDreq(object):
948  """Load in a vocabulary document.
949  dreqXML: full path to the XML document
950  configdoc: full path to associated configuration document
951  useShelve: flag to specify whether to retrieve data from cache (not implemented)
952  htmlStyles: dictionary of styling directives which influence structure of html page generates by the "makeHtml" method
953  lxml [False]: if true, use python lxml package for elementree module instead of the default xml package.
954"""
955
956  def __init__(self, xmlVersion=None, dreqXML=None, configdoc=None, useShelve=False, htmlStyles=None, strings=False, manifest=defaultManifestPath , configOnly=False,lxml=False):
957    self._extensions_ = {}
958    if xmlVersion != None:
959      assert os.path.isdir( VERSION_DIR ),'Version diretory %s not found;\nCreate or change environment variable DRQ_VERSION_DIR' % VERSION_DIR
960      assert os.path.isdir( '%s/%s' % (VERSION_DIR,xmlVersion) ), 'Version %s not in %s .. download from ..' % (xmlVersion,VERSION_DIR)
961      dreqXML='%s/%s/dreq.xml' % (VERSION_DIR,xmlVersion)
962      configdoc='%s/%s/dreq2Defn.xml' % (VERSION_DIR,xmlVersion)
963      manifest = None
964    elif manifest == None:
965      if dreqXML == None:
966       dreqXML=defaultDreqPath
967      if configdoc==None:
968       configdoc=defaultConfigPath
969    self._VERSION_DIR_ = VERSION_DIR
970    self.c = config( thisdoc=dreqXML, configdoc=configdoc, useShelve=useShelve,strings=strings,manifest=manifest,configOnly=configOnly,lxml=lxml)
971    self.coll = self.c.coll
972    self.version = self.c.version
973    self.softwareVersion = version
974    self.indexSortBy = {}
975    if not configOnly:
976      self.inx = index(self.coll)
977      self.itemStyles = {}
978      self.defaultItemLineStyle = lambda i, frm='', ann='': '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
979##
980## add index to Item base class .. so that it can be accessed by item instances
981##
982      dreqItemBase._inx = self.inx
983      dreqItemBase._indexInitialised = True
984##
985## load in additional styling directives
986##
987      if htmlStyles != None:
988        for k in htmlStyles:
989          dreqItemBase._htmlStyle[k] = htmlStyles[k]
990
991##    dreqItemBase._htmlStyle['__general__'] = {'addRemarks':True}
992
993      self.pageTmpl = """<html><head><title>%%s</title>
994%%s
995<link rel="stylesheet" type="text/css" href="%%scss/dreq.css">
996%s
997</head><body>
998
999<div id="top">
1000   <div id="corner"></div>
1001   CMIP6 Data Request
1002</div>
1003<div id="nav"><div><a href="%%s" title="Home">Home</a></div></div>
1004
1005<div id="section">
1006%%s
1007</div>
1008</body></html>"""  % dreqMonitoring
1009
1010  def getHtmlItemStyle(self, sect):
1011    """Get the styling method associated with a given section."""
1012    if sect in self.itemStyles:
1013      return self.itemStyles[sect]
1014    return self.defaultItemLineStyle
1015
1016  def updateByUid( self, uid, dd, delete=[] ):
1017    typePar={'xs:string':'x', 'xs:integer':0, 'xs:float':1., 'xs:boolean':True, 'xs:duration':'x', 'aa:st__sliceType':'simpleRange','aa:st__fortrantype':'real'}
1018    listTypePar={ "aa:st__integerList":1,"aa:st__integerListMonInc":1, "aa:st__stringList":'x', "aa:st__floatList":1. }
1019
1020    mmm = self.c.getByUid( uid )
1021    assert len(mmm) == 1, 'Expected single uid match, found: %s' % len(mmm)
1022    thisdoc = mmm[0]
1023    item = self.inx.uid[uid]
1024    d1 = [d for d in delete if d not in item._a]
1025    e1 = len(d1) > 0
1026    if e1:
1027      print ('ERROR.update.0001: request to delete non-existent keys: %s' % d1 )
1028    d1 = [d for d in delete if d in item._a and item._a[d].required ]
1029    e1b = len(d1) > 0
1030    if e1b:
1031      print ('ERROR.update.0002: request to delete required attributes: %s' % d1 )
1032    d2 = [d for d in dd if d not in item._a]
1033    e2 = len(d2) > 0
1034    if e2:
1035      print ('ERROR.update.0003: request to modify non-existent keys: %s' % d2 )
1036    e3 = []
1037    for k in [d for d in dd if d in item._a]:
1038      if item._a[k].type in typePar:
1039        e3.append( type( dd[k] ) != type( typePar[item._a[k].type] ) )
1040        if e3[-1]:
1041          print ('ERROR.update.0004: value has wrong type [%s] %s --- %s' % (item._a[k].type, dd[k], type( dd[k] ) ) )
1042      elif item._a[k].type in listTypePar:
1043        a = type( dd[k] ) not in [type( [] ), type( () )]
1044        if a:
1045          print ('ERROR.update.0005: value has wrong type [%s] %s --- should be list or tuple' % (item._a[k].type, dd[k] ) )
1046        else:
1047          a = not all( [type(x) == type(listTypePar[item._a[k].type]) for x in dd[k]] )
1048          if a:
1049            print ('ERROR.update.0005: value has wrong type [%s] %s --- %s' % (item._a[k].type, dd[k], [type(x)for x in dd[k]] ) )
1050
1051        if not a and item._a[k].type == 'aa:st__integerListMonInc':
1052          a = not all( [dd[k][i+1] > dd[k][i] for i in range( len(dd[k]) -1 )] )
1053          if a:
1054            print ('ERROR.update.0006: value should be a monotonic increasing integer list: %s' % str( dd[k] ) )
1055        e3.append(a)
1056      else:
1057        print ( 'Type not recognised: %s' % item._a[k].type )
1058        e3.append( False )
1059    eee = any([e1,e1b,e2,any(e3)])
1060    assert not eee, 'STOPPING: validation errors'
1061    self.thisdoc = thisdoc
1062    for k in dd:
1063      thisdoc.set( k, self.__string4xml__( dd[k], item._a[k].type ) )
1064      item.__dict__[k] = dd[k]
1065
1066  def saveXml( self, docfn=None, targ=None ):
1067    if docfn == None:
1068      docfn, tt = self.c.docs.items()[0]
1069    else:
1070      tt = self.c.docs[docfn]
1071
1072    if targ == None:
1073      targ = docfn
1074    tt[1].write( targ )
1075    print ('XML document saved to %s' % targ)
1076 
1077  def __string4xml__(self,v,typ):
1078    if typ in ['xs:string','xs:duration','aa:st__sliceType']:
1079       return v
1080    elif typ in ['xs:integer', 'xs:float', 'xs:boolean']:
1081       return str(v)
1082    elif typ == "aa:st__stringList":
1083       return ' '.join(v)
1084    elif typ in ["aa:st__integerList","aa:st__integerListMonInc", "aa:st__floatList"]:
1085       return ' '.join( [str(x) for x in v] )
1086    else:
1087       assert False, 'Data type not recognised'
1088     
1089  def _sectionSortHelper(self,title):
1090
1091    ab = title.split(  )[0].split('.')
1092    if len( ab ) == 2:
1093      a,b = ab
1094
1095      if self.c.rc.isIntStr(a):
1096        a = '%3.3i' % int(a)
1097      if self.c.rc.isIntStr(b):
1098        b = '%3.3i' % int(b)
1099      rv = (a,b)
1100    elif len(ab) == 1:
1101      rv = (ab[0],0)
1102    else:
1103      rv = ab
1104    return rv
1105
1106  def makeHtml(self,odir='./html', ttl0 = 'Data Request Index', annotations=None):
1107    """Generate a html view of the vocabularies, using the "__html__" method of the vocabulary item class to generate a
1108page for each item and also generating index pages.
1109    odir: directory for html files;
1110    ttl0: Title for main index (in odir/index.html)"""
1111    markup = utilities.markupHtml( '' )
1112
1113    ks = self.inx.uid.keys()
1114    ##ks.sort( kscl( self.inx.uid, 'title' ).cmp )
1115    ks = sorted( ks, key=kscl( self.inx.uid, 'title' ).key )
1116    for k in ks:
1117      i = self.inx.uid[k]
1118      ttl = 'Data Request Record: [%s]%s' % (i._h.label,i.label)
1119      bdy = '\n'.join( i.__html__( ghis=self.getHtmlItemStyle ) )
1120      oo = open( '%s/u/%s.html' % (odir,i.uid), 'w' )
1121      oo.write( self.pageTmpl % (ttl, jsh, '../', '../index.html', bdy ) )
1122      oo.close()
1123
1124    subttl1 = 'Overview tables and search'
1125    subttl2 = 'Sections of the data request'
1126    msg0 = ['<h1>%s</h1>' % ttl0, '<h2>%s</h2>' % subttl1, '<ul>',]
1127    msg0.append( '<li><a href="tab01_3_3.html">Overview: all variables and experiments</a></li>' )
1128    msg0.append( '<li><a href="tab01_1_1.html">Overview: priority 1 variables, tier 1 experiments</a></li>' )
1129    msg0.append( '<li><a href="tab01_1_1_dn.html">Overview: priority 1 variables, tier 1 experiments (grid default to native)</a></li>' )
1130    msg0.append( '<li><a href="mipVars.html">Search for variables</a></li>' )
1131    msg0.append( '<li><a href="experiments.html">Search for experiments</a></li>' )
1132    msg0 += ['</ul>', '<h2>%s</h2>' % subttl2, '<ul>',]
1133    ks = sorted( self.coll.keys() )
1134    ee = {}
1135    for k in ks:
1136      ee[self.coll[k].header.title] = k
1137    kks = sorted( ee.keys(),  key = self._sectionSortHelper )
1138    for kt in kks:
1139      k = ee[kt]
1140##
1141## sort on item label
1142##
1143      if annotations != None and k in annotations:
1144        ann = annotations[k]
1145      else:
1146        ann = {}
1147
1148      sortkey = self.indexSortBy.get( k,'title')
1149      ##self.coll[k].items.sort( ds(self.indexSortBy.get( k,'title') ).cmp )
1150      items = sorted( self.coll[k].items, key=ds(sortkey).key )
1151      ttl = 'Data Request Section: %s' % k
1152      msg0.append( '<li><a href="index/%s.html">%s [%s]</a></li>\n' % (k,self.coll[k].header.title,k) )
1153      msg = ['<h1>%s</h1>\n' % ttl, '<ul>',]
1154      msg.append( '<a href="../index.html">Home</a><br/>\n' )
1155      msg.append( '<p>%s</p>\n' % markup.parse( self.coll[k].header.description ) )
1156      lst = self.getHtmlItemStyle(k)
1157     
1158      msg1 = []
1159      for i in items:
1160        ##m = '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
1161       
1162        m = lst( i, ann=ann.get( i.label ) )
1163        msg1.append( m )
1164      if k in self.indexSortBy:
1165        msg += msg1
1166      else:
1167        msg += sorted( msg1 )
1168      msg.append( '</ul>' )
1169      bdy = '\n'.join( msg )
1170      oo = open( '%s/index/%s.html' % (odir,k), 'w' )
1171      oo.write( self.pageTmpl % (ttl, '', '../', '../index.html', bdy ) )
1172      oo.close()
1173    msg0.append( '</ul>' )
1174    bdy = '\n'.join( msg0 )
1175    oo = open( '%s/index.html' % odir, 'w' )
1176    oo.write( self.pageTmpl % (ttl0, '', '', 'index.html', bdy ) )
1177    oo.close()
1178   
1179if __name__ == '__main__':
1180  dreq = loadDreq(manifest='out/dreqManifest.txt' )
1181
Note: See TracBrowser for help on using the repository browser.