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

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

01.00.30beta

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._desc = {}
517    self.tt0 = {}
518    self.tt1 = {}
519    self.ttl2 = []
520    self.docs = {}
521    self.version = None
522    self.useLxml = lxml
523
524    if manifest != None:
525      assert os.path.isfile( manifest ), 'Manifest file not found: %s' % manifest
526      ii = open(manifest).readlines() 
527      docl = []
528      for l in ii[1:]:
529        bits = l.strip().split()
530        assert len( bits ) > 1, 'Failed to parse line in manifest %s: \n%s' % (manifest,l)
531        bb = []
532        for b in bits[:2]:
533          if not os.path.isfile( b ):
534             b = '%s/%s' % (PACKAGE_DIR,b)
535          assert os.path.isfile( b ), 'File %s not found (listed in %s)' % (b,manifest )
536          bb.append( b )
537        docl.append( tuple( bb ) )
538      for d,c in docl:
539        self.__read__(d, c)
540    else:
541      self.__read__(thisdoc, configdoc)
542
543  def getByUid(self,uid):
544    mmm = []
545    for fn,rr in self.docs.items():
546       root, doc = rr
547       mm = root.findall( ".//*[@uid='%s']" % uid )
548       mmm += mm
549       print ('%s: %s' % (fn, str(mm)) )
550    return mmm
551
552  def __read__(self, thisdoc, configdoc):
553    self.vdef = configdoc
554    self.vsamp = thisdoc
555    self.docl.append( (thisdoc,configdoc) )
556    fn = thisdoc.split( '/' )[-1]
557
558    if self.strings:
559      doc = xml.dom.minidom.parseString( self.vdef  )
560    else:
561      doc = xml.dom.minidom.parse( self.vdef  )
562##
563## elementTree parsing implemented for main document
564##
565    self.etree = False
566    self.etree = True
567    self.ns = None
568    if not self.configOnly:
569      if self.etree:
570        if self.useLxml:
571          import lxml
572          import lxml.etree as et
573        else:
574          import xml.etree.cElementTree as cel
575     
576        if not pythonPre27:
577          ## this for of namespace registration not available in 2.6
578          ## absence of registration means module cannot write data exactly as read.
579          ##
580          if self.useLxml:
581            ##et.register_namespace('', "urn:w3id.org:cmip6.dreq.dreq:a")
582            et.register_namespace('pav', "http://purl.org/pav/2.3")
583          else:
584            cel.register_namespace('', "urn:w3id.org:cmip6.dreq.dreq:a")
585            cel.register_namespace('pav', "http://purl.org/pav/2.3")
586
587        if not self.strings:
588          if self.useLxml:
589            ##parser = etree.XMLParser( remove_blank_text=True )
590            ##self.contentDoc = et.parse( self.vsamp, parser  )
591            self.contentDoc = et.parse( self.vsamp  )
592          elif python2:
593            parser = getParser()()
594            self.contentDoc = cel.parse( self.vsamp, parser=parser )
595          else:
596            self.contentDoc = cel.parse( self.vsamp )
597
598          root = self.contentDoc.getroot()
599        else:
600          root = cel.fromstring(self.vsamp)
601
602        self.docs[fn] = (root, self.contentDoc)
603        bs = root.tag.split( '}' )
604        if len( bs ) > 1:
605          self.ns = bs[0] + '}'
606        else:
607          self.ns = None
608        vl = root.findall( './/{http://purl.org/pav/2.3}version' )
609        if self.version != None:
610          if vl[0].text != self.version:
611            print ('WARNING: version difference between %s [%s] and %s [%s]' % (self.docl[0][0],self.version,thisdoc,vl[0].text) )
612        else:
613          self.version = vl[0].text
614        self.parent_map = dict((c, p) for p in root.getiterator() for c in p)
615      else:
616        if self.strings:
617          self.contentDoc = xml.dom.minidom.parseString( self.vsamp  )
618        else:
619          self.contentDoc = xml.dom.minidom.parse( self.vsamp )
620
621          vl = self.contentDoc.getElementsByTagName( 'prologue' )
622          v = vl[0].getElementsByTagName( 'pav:version' )
623          self.version = v[0].firstChild.data
624        self.ns = None
625
626    vl = doc.getElementsByTagName( 'table' ) + doc.getElementsByTagName( 'annextable' )
627    self.tables = {}
628    tables = {}
629    self.tableClasses = {}
630    self.tableItems = collections.defaultdict( list )
631##
632## this loads in some metadata, but not yet in a useful way.
633##
634    self._t0 = self.parsevcfg(None)
635    self._t2 = self.parsevcfg('__main__')
636    self._tableClass0 = self.lu.itemClassFact( self._t0, ns=self.ns )
637##
638## define a class for the section heading records.
639##
640    self._t1 = self.parsevcfg('__sect__')
641##
642## when used with manifest .. need to preserve entries in "__main__" from each document.
643##
644    self._sectClass0 = self.lu.itemClassFact( self._t1, ns=self.ns )
645
646    for k in self.bscc:
647      self.tt0[k] = self._tableClass0(idict=self.bscc[k])
648      if k in self._t0.attributes:
649        setattr( self._tableClass0, '%s' % k, self.tt0[k] )
650      if k in self._t1.attributes:
651        setattr( self._sectClass0, '%s' % k, self.tt0[k] )
652
653##
654## save header information, as for recordAttributeDefn below
655##
656    self._recAtDef = {'__core__':self._t0, '__sect__':self._t1}
657##
658## addition of __core__ to coll dictionary ..
659##
660    self.coll['__core__'] = self.ntf( self._t0.header, self._t0.attributes, [self.tt0[k] for k in self.tt0] )
661
662    ec = {}
663    for i in self.coll['__core__'].items:
664      ec[i.label] = i
665
666
667    for v in vl:
668      t = self.parsevcfg(v)
669      tables[t[0].label] = t
670      self.tableClasses[t[0].label] = self.lu.itemClassFact( t, ns=self.ns )
671      thisc = self.tableClasses[t[0].label]
672      self.tt1[t[0].label] = self._sectClass0( idict=t.header._asdict() )
673      self.tt1[t[0].label].maxOccurs = t.header.maxOccurs
674      self.tt1[t[0].label].labUnique = t.header.labUnique
675      self.tt1[t[0].label].level = t.header.level
676      self.tt1[t[0].label].uid = t.header.uid
677      self.tt1[t[0].label].itemLabelMode = t.header.itemLabelMode
678      self._desc[t[0].label] = t.header.description
679      self.ttl2 += [thisc.__dict__[a] for a in t.attributes]
680    mil = [t[1] for t in self._t2.attributes.items()]
681    self.coll['__main__'] = self.ntf( self._t2.header, self._t2.attributes, self.ttl2 )
682
683    self.coll['__sect__'] = self.ntf( self._t1.header, self._t1.attributes, [self.tt1[k] for k in self.tt1] )
684
685    for sct in ['__core__','__main__','__sect__']:
686      for k in self.coll[sct].attDefn.keys():
687        assert k in ec, 'Key %s [%s] not found' % (k,sct)
688        self.coll[sct].attDefn[k] = ec[k]
689
690    for k in tables:
691      self.recordAttributeDefn[k] = tables[k]
692
693    if self.configOnly:
694      return
695    for k in tables.keys():
696      if self.etree:
697        vl = root.findall( './/%s%s' % (self.ns,k) )
698        if len(vl) == 1:
699          v = vl[0]
700          t = v.get( 'title' )
701          i = v.get( 'id' )
702          uid = v.get( 'uid' )
703          useclass = v.get( 'useClass' )
704
705          self.tt1[k].label = k
706          self.tt1[k].title = t
707          self.tt1[k].id = i
708          self.tt1[k].uid = uid
709          self.tt1[k].useClass = useclass
710          self.tableClasses[k]._h = self.tt1[k]
711          il = v.findall( '%sitem' % self.ns )
712          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
713 
714          self.tables[k] = (i,t,len(il))
715       
716          for i in il:
717            try:
718              ii = self.tableClasses[k](xmlMiniDom=i, etree=True)
719            except:
720              print ('Exception raises instantiating item in section %s' % k)
721              print ('At item uid=%s' % str(i) )
722              raise
723            self.tableItems[k].append( ii )
724        elif len(vl) > 1:
725          assert False, 'not able to handle repeat sections with etree yet'
726      else:
727        vl = self.contentDoc.getElementsByTagName( k )
728        if len(vl) == 1:
729          v = vl[0]
730          t = v.getAttribute( 'title' )
731          i = v.getAttribute( 'id' )
732          il = v.getElementsByTagName( 'item' )
733          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
734 
735          self.tables[k] = (i,t,len(il))
736       
737          for i in il:
738            ii = self.tableClasses[k](xmlMiniDom=i)
739            self.tableItems[k].append( ii )
740        elif len(vl) > 1:
741          l1 = []
742          l2 = []
743          for v in vl:
744            t = v.getAttribute( 'title' )
745            i = v.getAttribute( 'id' )
746            il = v.getElementsByTagName( 'item' )
747            self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
748            l1.append( (i,t,len(il)) )
749         
750            l2i = []
751            for i in il:
752              ii = self.tableClasses[k](xmlMiniDom=i)
753              l2i.append( ii )
754            l2.append( l2i )
755          self.tables[k] = l1
756          self.tableItems[k] = l2
757      self.coll[k] = self.ntf( self.recordAttributeDefn[k].header, self.recordAttributeDefn[k].attributes, self.tableItems[k] )
758 
759  def info(self,ss):
760    """Switchable print function ... switch off by setting self.silent=True"""
761    if not self.silent:
762      print ( ss )
763
764  def parsevcfg(self,v):
765      """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."""
766      if v in [ None,'__main__']:
767        idict = {'description':'An extended description of the object', 'title':'Record Description', \
768           'techNote':'', 'useClass':'__core__', 'superclass':'rdf:property',\
769           'type':'xs:string', 'uid':'__core__:description', 'label':'label', 'required':'required' }
770        if v == None:
771          vtt = self.nts( '__core__', 'CoreAttributes', 'X.1 Core Attributes', '00000000', 'def', '0', '0', 'false', '__core__', 'The core attributes, used in defining sections and attributes' )
772        else:
773          vtt = self.nts( '__main__', 'DataRequestAttributes', 'X.2 Data Request Attributes', '00000001', 'def', '0', '0', 'false', '__main__' , 'The attributes used to define data request records')
774      elif v == '__sect__':
775        idict = {'title':'Record Description', \
776         'uid':'__core__:description', 'label':'label', 'useClass':'text', 'id':'id', 'maxOccurs':'', 'itemLabelMode':'', 'level':'', 'labUnique':'' }
777        vtt = self.nts( '__sect__', 'sectionAttributes', 'X.3 Section Attributes', '00000000', 'def', '0', '0', 'false', '__sect__', 'The attributes used to define data request sections' )
778##<var label="var" uid="SECTION:var" useClass="vocab" title="MIP Variable" id="cmip.drv.001">
779      else:
780        l = v.getAttribute( 'label' )
781        t = v.getAttribute( 'title' )
782        i = v.getAttribute( 'id' )
783        u = v.getAttribute( 'uid' )
784        ilm = v.getAttribute( 'itemLabelMode' )
785        lev = v.getAttribute( 'level' )
786        maxo = v.getAttribute( 'maxOccurs' )
787        labu = v.getAttribute( 'labUnique' )
788        des = v.getAttribute( 'description' )
789        il = v.getElementsByTagName( 'rowAttribute' )
790        ##vtt = self.nts( v.nodeName, l,t,i,ilm,lev, maxo, labu, 's__%s' % v.nodeName )
791        vtt = self.nts( v.nodeName, l,t,i,ilm,lev, maxo, labu, u, des )
792        idict = {}
793        for i in il:
794          tt = self.parseicfg(i)
795          idict[tt.label] = tt
796      deflt = self.nt__default( {}, '__unset__' )
797
798      ## return a named tuple: (header, attributes, defaults)
799      return self.ntt( vtt, idict, deflt )
800
801  def parseicfg(self,i):
802      """Parse a record attribute specification"""
803      defs = {'type':"xs:string"}
804      ll = []
805      ee = {}
806      for k in ['label','title','type','useClass','techNote','description','uid','required']:
807        if i.hasAttribute( k ):
808          ll.append( i.getAttribute( k ) )
809        else:
810          ll.append( defs.get( k, None ) )
811        ee[k] = ll[-1]
812      l, t, ty, cls, tn, desc, uid, rq = ll
813      self.lastTitle = t
814      if rq in ['0', 'false']:
815        rq = False
816      else:
817        rq = True
818      ee['required'] = rq
819
820      returnClass = True
821      if returnClass:
822        return self._tableClass0( idict=ee )
823      else:
824        return self.nti( i.nodeName, l,t,ty,cls,tn,rq )
825
826class container(object):
827  """Simple container class, to hold a set of dictionaries of lists."""
828  def __init__(self, atl ):
829    self.uid = {}
830    for a in atl:
831      self.__dict__[a] =  collections.defaultdict( list )
832
833class c1(object):
834  def __init__(self):
835    self.a = collections.defaultdict( list )
836
837class index(object):
838  """Create an index of the document. Cross-references are generated from attributes with class 'internalLink'.
839This version assumes that each record is identified by an "uid" attribute and that there is a "var" section.
840Invalid internal links are recorded in tme "missingIds" dictionary.
841For any record, with identifier u, iref_by_uid[u] gives a list of the section and identifier of records linking to that record.
842"""
843
844  def __init__(self, dreq,lock=True):
845    self.silent = True
846    self.uid = {}
847    self.uid2 = collections.defaultdict( list )
848    nativeAtts = ['uid','iref_by_uid','iref_by_sect','missingIds']
849    naok = map( lambda x: not x in dreq, nativeAtts )
850    assert all(naok), 'This version cannot index collections containing sections with names: %s' % str( nativeAtts )
851    self.var_uid = {}
852    self.var_by_name = collections.defaultdict( list )
853    self.var_by_sn = collections.defaultdict( list )
854    self.iref_by_uid = collections.defaultdict( list )
855    irefdict = collections.defaultdict( list )
856    for k in dreq.keys():
857      if 'sn' in dreq[k].attDefn:
858         self.__dict__[k] =  container( ['label','sn'] )
859      else:
860         self.__dict__[k] =  container( ['label'] )
861    ##
862    ## collected names of attributes which carry internal links
863    ##
864      for ka in dreq[k].attDefn.keys():
865        if hasattr( dreq[k].attDefn[ka], 'useClass') and dreq[k].attDefn[ka].useClass in  ['internalLink','internalLinkList']:
866           irefdict[k].append( ka )
867
868    for k in dreq.keys():
869        for i in dreq[k].items:
870          assert 'uid' in i.__dict__, 'uid not found::\n%s\n%s' % (str(i._h),str(i.__dict__) )
871          if 'uid' in self.uid:
872            print ( 'ERROR.100.0001: Duplicate uid: %s [%s]' % (i.uid,i._h.title) )
873            self.uid2[i.uid].append( (k,i) )
874          else:
875### create index bx uid.
876            self.uid[i.uid] = i
877
878    self.missingIds = collections.defaultdict( list )
879    self.iref_by_sect = collections.defaultdict( c1 )
880    for k in dreq.keys():
881        for k2 in irefdict.get( k, [] ):
882          n1 = 0
883          n2 = 0
884          for i in dreq[k].items:
885            if k2 in i.__dict__:
886              id2 = i.__dict__.get( k2 )
887              if id2 != '__unset__':
888                sect = i._h.label
889  ## append attribute name and target  -- item i.uid, attribute k2 reference item id2
890                if type(id2) not in [type( [] ),type(())]:
891                  id2 = [id2,]
892                for u in id2:
893                  self.iref_by_uid[ u ].append( (k2,i.uid) )
894                  self.iref_by_sect[ u ].a[sect].append( i.uid )
895                  if u in self.uid:
896                    n1 += 1
897                  else:
898                    n2 += 1
899                    self.missingIds[u].append( (k,k2,i.uid) )
900          self.info(  'INFO:: %s, %s%s (%s)' % (k,k2,n1,n2) )
901
902    for k in dreq.keys():
903      for i in dreq[k].items:
904        self.__dict__[k].uid[i.uid] = i
905        self.__dict__[k].label[i.label].append( i.uid )
906        if 'sn' in dreq[k].attDefn:
907          self.__dict__[k].sn[i.sn].append( i.uid )
908
909    if lock:
910      for k in self.iref_by_uid: 
911         self.iref_by_uid[k] = tuple( self.iref_by_uid[k] )
912      for k in self.iref_by_sect:
913        for s in self.iref_by_sect[ k ].a:
914          self.iref_by_sect[ k ].a[s] = tuple( self.iref_by_sect[ k ].a[s] )
915
916  def info(self,ss):
917    if not self.silent:
918      print ( ss )
919
920class ds(object):
921  """Comparison object to assist sorting of lists of dictionaries"""
922  def __init__(self,k):
923    self.k = k
924  def cmp(self,x,y):
925    return cmp( x.__dict__[self.k], y.__dict__[self.k] )
926  def key(self,x):
927    return x.__dict__[self.k]
928
929class kscl(object):
930  """Comparison object to assist sorting of dictionaries of class instances"""
931  def __init__(self,idict,k):
932    self.k = k
933    self.idict = idict
934  def cmp(self,x,y):
935    return cmp( self.idict[x].__dict__[self.k], self.idict[y].__dict__[self.k] )
936  def key(self,x):
937    return self.idict[x].__dict__[self.k]
938
939src1 = '../workbook/trial_20150831.xml'
940
941#DEFAULT LOCATION -- changed automatically when building distribution
942defaultDreq = 'dreq.xml'
943#DEFAULT CONFIG
944defaultConfig = 'dreq2Defn.xml'
945
946defaultDreqPath = '%s/%s' % (DOC_DIR, defaultDreq )
947defaultConfigPath = '%s/%s' % (DOC_DIR, defaultConfig )
948defaultManifestPath = '%s/dreqManifest.txt' % DOC_DIR
949
950class loadDreq(object):
951  """Load in a vocabulary document.
952  dreqXML: full path to the XML document
953  configdoc: full path to associated configuration document
954  useShelve: flag to specify whether to retrieve data from cache (not implemented)
955  htmlStyles: dictionary of styling directives which influence structure of html page generates by the "makeHtml" method
956  lxml [False]: if true, use python lxml package for elementree module instead of the default xml package.
957"""
958
959  def __init__(self, xmlVersion=None, dreqXML=None, configdoc=None, useShelve=False, htmlStyles=None, strings=False, manifest=defaultManifestPath , configOnly=False,lxml=False):
960    self._extensions_ = {}
961    if xmlVersion != None:
962      assert os.path.isdir( VERSION_DIR ),'Version diretory %s not found;\nCreate or change environment variable DRQ_VERSION_DIR' % VERSION_DIR
963      assert os.path.isdir( '%s/%s' % (VERSION_DIR,xmlVersion) ), 'Version %s not in %s .. download from ..' % (xmlVersion,VERSION_DIR)
964      dreqXML='%s/%s/dreq.xml' % (VERSION_DIR,xmlVersion)
965      configdoc='%s/%s/dreq2Defn.xml' % (VERSION_DIR,xmlVersion)
966      manifest = None
967    elif manifest == None:
968      if dreqXML == None:
969       dreqXML=defaultDreqPath
970      if configdoc==None:
971       configdoc=defaultConfigPath
972    self._VERSION_DIR_ = VERSION_DIR
973    self.c = config( thisdoc=dreqXML, configdoc=configdoc, useShelve=useShelve,strings=strings,manifest=manifest,configOnly=configOnly,lxml=lxml)
974    self.coll = self.c.coll
975    self.version = self.c.version
976    self.softwareVersion = version
977    self.indexSortBy = {}
978    if not configOnly:
979      self.inx = index(self.coll)
980      self.itemStyles = {}
981      self.defaultItemLineStyle = lambda i, frm='', ann='': '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
982##
983## add index to Item base class .. so that it can be accessed by item instances
984##
985      dreqItemBase._inx = self.inx
986      dreqItemBase._indexInitialised = True
987##
988## load in additional styling directives
989##
990      if htmlStyles != None:
991        for k in htmlStyles:
992          dreqItemBase._htmlStyle[k] = htmlStyles[k]
993
994##    dreqItemBase._htmlStyle['__general__'] = {'addRemarks':True}
995
996      self.pageTmpl = """<html><head><title>%%s</title>
997%%s
998<link rel="stylesheet" type="text/css" href="%%scss/dreq.css">
999%s
1000</head><body>
1001
1002<div id="top">
1003   <div id="corner"></div>
1004   CMIP6 Data Request
1005</div>
1006<div id="nav"><div><a href="%%s" title="Home">Home</a></div></div>
1007
1008<div id="section">
1009%%s
1010</div>
1011</body></html>"""  % dreqMonitoring
1012
1013  def getHtmlItemStyle(self, sect):
1014    """Get the styling method associated with a given section."""
1015    if sect in self.itemStyles:
1016      return self.itemStyles[sect]
1017    return self.defaultItemLineStyle
1018
1019  def updateByUid( self, uid, dd, delete=[] ):
1020    typePar={'xs:string':'x', 'xs:integer':0, 'xs:float':1., 'xs:boolean':True, 'xs:duration':'x', 'aa:st__sliceType':'simpleRange','aa:st__fortrantype':'real'}
1021    listTypePar={ "aa:st__integerList":1,"aa:st__integerListMonInc":1, "aa:st__stringList":'x', "aa:st__floatList":1. }
1022
1023    mmm = self.c.getByUid( uid )
1024    assert len(mmm) == 1, 'Expected single uid match, found: %s' % len(mmm)
1025    thisdoc = mmm[0]
1026    item = self.inx.uid[uid]
1027    d1 = [d for d in delete if d not in item._a]
1028    e1 = len(d1) > 0
1029    if e1:
1030      print ('ERROR.update.0001: request to delete non-existent keys: %s' % d1 )
1031    d1 = [d for d in delete if d in item._a and item._a[d].required ]
1032    e1b = len(d1) > 0
1033    if e1b:
1034      print ('ERROR.update.0002: request to delete required attributes: %s' % d1 )
1035    d2 = [d for d in dd if d not in item._a]
1036    e2 = len(d2) > 0
1037    if e2:
1038      print ('ERROR.update.0003: request to modify non-existent keys: %s' % d2 )
1039    e3 = []
1040    for k in [d for d in dd if d in item._a]:
1041      if item._a[k].type in typePar:
1042        e3.append( type( dd[k] ) != type( typePar[item._a[k].type] ) )
1043        if e3[-1]:
1044          print ('ERROR.update.0004: value has wrong type [%s] %s --- %s' % (item._a[k].type, dd[k], type( dd[k] ) ) )
1045      elif item._a[k].type in listTypePar:
1046        a = type( dd[k] ) not in [type( [] ), type( () )]
1047        if a:
1048          print ('ERROR.update.0005: value has wrong type [%s] %s --- should be list or tuple' % (item._a[k].type, dd[k] ) )
1049        else:
1050          a = not all( [type(x) == type(listTypePar[item._a[k].type]) for x in dd[k]] )
1051          if a:
1052            print ('ERROR.update.0005: value has wrong type [%s] %s --- %s' % (item._a[k].type, dd[k], [type(x)for x in dd[k]] ) )
1053
1054        if not a and item._a[k].type == 'aa:st__integerListMonInc':
1055          a = not all( [dd[k][i+1] > dd[k][i] for i in range( len(dd[k]) -1 )] )
1056          if a:
1057            print ('ERROR.update.0006: value should be a monotonic increasing integer list: %s' % str( dd[k] ) )
1058        e3.append(a)
1059      else:
1060        print ( 'Type not recognised: %s' % item._a[k].type )
1061        e3.append( False )
1062    eee = any([e1,e1b,e2,any(e3)])
1063    assert not eee, 'STOPPING: validation errors'
1064    self.thisdoc = thisdoc
1065    for k in dd:
1066      thisdoc.set( k, self.__string4xml__( dd[k], item._a[k].type ) )
1067      item.__dict__[k] = dd[k]
1068
1069  def saveXml( self, docfn=None, targ=None ):
1070    if docfn == None:
1071      docfn, tt = self.c.docs.items()[0]
1072    else:
1073      tt = self.c.docs[docfn]
1074
1075    if targ == None:
1076      targ = docfn
1077    tt[1].write( targ )
1078    print ('XML document saved to %s' % targ)
1079 
1080  def __string4xml__(self,v,typ):
1081    if typ in ['xs:string','xs:duration','aa:st__sliceType']:
1082       return v
1083    elif typ in ['xs:integer', 'xs:float', 'xs:boolean']:
1084       return str(v)
1085    elif typ == "aa:st__stringList":
1086       return ' '.join(v)
1087    elif typ in ["aa:st__integerList","aa:st__integerListMonInc", "aa:st__floatList"]:
1088       return ' '.join( [str(x) for x in v] )
1089    else:
1090       assert False, 'Data type not recognised'
1091     
1092  def _sectionSortHelper(self,title):
1093
1094    ab = title.split(  )[0].split('.')
1095    if len( ab ) == 2:
1096      a,b = ab
1097
1098      if self.c.rc.isIntStr(a):
1099        a = '%3.3i' % int(a)
1100      if self.c.rc.isIntStr(b):
1101        b = '%3.3i' % int(b)
1102      rv = (a,b)
1103    elif len(ab) == 1:
1104      rv = (ab[0],0)
1105    else:
1106      rv = ab
1107    return rv
1108
1109  def makeHtml(self,odir='./html', ttl0 = 'Data Request Index', annotations=None):
1110    """Generate a html view of the vocabularies, using the "__html__" method of the vocabulary item class to generate a
1111page for each item and also generating index pages.
1112    odir: directory for html files;
1113    ttl0: Title for main index (in odir/index.html)"""
1114    markup = utilities.markupHtml( '' )
1115
1116    ks = self.inx.uid.keys()
1117    ##ks.sort( kscl( self.inx.uid, 'title' ).cmp )
1118    ks = sorted( ks, key=kscl( self.inx.uid, 'title' ).key )
1119    for k in ks:
1120      i = self.inx.uid[k]
1121      ttl = 'Data Request Record: [%s]%s' % (i._h.label,i.label)
1122      bdy = '\n'.join( i.__html__( ghis=self.getHtmlItemStyle ) )
1123      oo = open( '%s/u/%s.html' % (odir,i.uid), 'w' )
1124      oo.write( self.pageTmpl % (ttl, jsh, '../', '../index.html', bdy ) )
1125      oo.close()
1126
1127    subttl1 = 'Overview tables and search'
1128    subttl2 = 'Sections of the data request'
1129    msg0 = ['<h1>%s</h1>' % ttl0, '<h2>%s</h2>' % subttl1, '<ul>',]
1130    msg0.append( '<li><a href="tab01_3_3.html">Overview: all variables and experiments</a></li>' )
1131    msg0.append( '<li><a href="tab01_1_1.html">Overview: priority 1 variables, tier 1 experiments</a></li>' )
1132    msg0.append( '<li><a href="tab01_1_1_dn.html">Overview: priority 1 variables, tier 1 experiments (grid default to native)</a></li>' )
1133    msg0.append( '<li><a href="mipVars.html">Search for variables</a></li>' )
1134    msg0.append( '<li><a href="experiments.html">Search for experiments</a></li>' )
1135    msg0 += ['</ul>', '<h2>%s</h2>' % subttl2, '<ul>',]
1136    ks = sorted( self.coll.keys() )
1137    ee = {}
1138    for k in ks:
1139      ee[self.coll[k].header.title] = k
1140    kks = sorted( ee.keys(),  key = self._sectionSortHelper )
1141    for kt in kks:
1142      k = ee[kt]
1143##
1144## sort on item label
1145##
1146      if annotations != None and k in annotations:
1147        ann = annotations[k]
1148      else:
1149        ann = {}
1150
1151      sortkey = self.indexSortBy.get( k,'title')
1152      ##self.coll[k].items.sort( ds(self.indexSortBy.get( k,'title') ).cmp )
1153      items = sorted( self.coll[k].items, key=ds(sortkey).key )
1154      ttl = 'Data Request Section: %s' % k
1155      msg0.append( '<li><a href="index/%s.html">%s [%s]</a></li>\n' % (k,self.coll[k].header.title,k) )
1156      msg = ['<h1>%s</h1>\n' % ttl, '<ul>',]
1157      msg.append( '<a href="../index.html">Home</a><br/>\n' )
1158      msg.append( '<p>%s</p>\n' % markup.parse( self.coll[k].header.description ) )
1159      lst = self.getHtmlItemStyle(k)
1160     
1161      msg1 = []
1162      for i in items:
1163        ##m = '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
1164       
1165        m = lst( i, ann=ann.get( i.label ) )
1166        msg1.append( m )
1167      if k in self.indexSortBy:
1168        msg += msg1
1169      else:
1170        msg += sorted( msg1 )
1171      msg.append( '</ul>' )
1172      bdy = '\n'.join( msg )
1173      oo = open( '%s/index/%s.html' % (odir,k), 'w' )
1174      oo.write( self.pageTmpl % (ttl, '', '../', '../index.html', bdy ) )
1175      oo.close()
1176    msg0.append( '</ul>' )
1177    bdy = '\n'.join( msg0 )
1178    oo = open( '%s/index.html' % odir, 'w' )
1179    oo.write( self.pageTmpl % (ttl0, '', '', 'index.html', bdy ) )
1180    oo.close()
1181   
1182if __name__ == '__main__':
1183  dreq = loadDreq(manifest='out/dreqManifest.txt' )
1184
Note: See TracBrowser for help on using the repository browser.