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

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

fixed python 3 compatibility issue regarding obsolete use of string module

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