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

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

01.beta.10

Line 
1"""This module provides a basic python API to the Data Request.
2After ingesting the XML documents (configuration and request) the module generates two python objects:
31. A collection of records
42. Index
5"""
6import xml, string, collections
7import xml.dom
8import xml.dom.minidom
9import re, shelve
10from __init__ import DOC_DIR
11
12
13blockSchemaFile = '%s/%s' % (DOC_DIR, 'BlockSchema.csv' )
14
15def loadBS(bsfile):
16  ii = open( bsfile, 'r' ).readlines()
17  ll = []
18  for l in ii:
19    ll.append( [x[1:-1] for x in string.strip(l).split('\t') ] )
20  cc = collections.defaultdict( dict )
21  for l in ll[3:]:
22    for i in range( len(ll[2]) -1 ):
23      cc[l[0]][ll[2][i+1]] = l[i+1]
24  return cc
25
26class rechecks(object):
27  """Checks to be applied to strings"""
28  def __init__(self):
29    self.__isInt = re.compile( '-{0,1}[0-9]+' )
30
31  def isIntStr( self, tv ):
32    """Check whether a string is a valid representation of an integer."""
33    if type( tv ) not in {type(''),type(u'')}:
34      self.reason = 'NOT STRING'
35      return False
36    ok = self.__isInt.match( tv ) != None
37    if not ok:
38      self.reason = 'Failed to match regular expression for integers'
39    else:
40      self.reason = ''
41    return ok
42
43class dreqItemBase(object):
44       __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."""
45       _indexInitialised = False
46       _inx = None
47       _urlBase = ''
48       _htmlStyle = {}
49       _linkAttrStyle = {}
50
51       def __init__(self,dict=None,xmlMiniDom=None,id='defaultId',etree=False):
52         dictMode = dict != None
53         mdMode = xmlMiniDom != None
54         assert not( dictMode and mdMode), 'Mode must be either dictionary of minidom: both assigned'
55         assert dictMode or mdMode, 'Mode must be either dictionary of minidom: neither assigned'
56         ##self._defaults = { }
57         ##self._globalDefault = '__unset__'
58         self._contentInitialised = False
59         if dictMode:
60           self.dictInit( dict )
61         elif mdMode:
62           self.mdInit( xmlMiniDom, etree=etree )
63
64       def __repr__(self):
65         """Provide a one line summary of identifying the object."""
66         if self._contentInitialised:
67           return 'Item <%s>: [%s] %s' % (self._h.title,self.label,self.title)
68         else:
69           return 'Item <%s>: uninitialised' % self._h.title
70
71       def __info__(self,full=False):
72         """Print a summary of the data held in the object as a list of key/value pairs"""
73         if self._contentInitialised:
74           print ( 'Item <%s>: [%s] %s' % (self._h.title,self.label,self.title) )
75           for a in self.__dict__.keys():
76             if a[0] != '_' or full:
77               if hasattr( self._a[a], 'useClass') and self._a[a].useClass == 'internalLink' and self._base._indexInitialised:
78                 if self.__dict__[a] in self._base._inx.uid:
79                   targ = self._base._inx.uid[ self.__dict__[a] ]
80                   print ( '   %s: [%s]%s [%s]' % ( a, targ._h.label, targ.label, self.__dict__[a] ) )
81                 else:
82                   print ( '   %s: [ERROR: key not found] [%s]' % ( a, self.__dict__[a] ) )
83               else:
84                 print ( '    %s: %s' % ( a, self.__dict__[a] ) )
85         else:
86           print ( 'Item <%s>: uninitialised' % self.sectionLabel )
87
88       def __href__(self,odir="",label=None):
89         igns =  {'','__unset__'}
90         if 'description' in self.__dict__ and string.strip( self.description ) not in igns:
91           ttl = self.description
92         elif 'title' in self.__dict__ and string.strip( self.title ) not in igns:
93           ttl = self.title
94         else:
95           ttl = self.label
96         if label == None:
97           label = self.uid
98         return '<span title="%s"><a href="%s%s.html">%s</a></span>' % (ttl,odir,self.uid,label)
99
100       def getHtmlLinkAttrStyle(self,a):
101         """Return a string containing a html fragment for a link to an attribute."""
102         if a in self.__class__._linkAttrStyle:
103           return self.__class__._linkAttrStyle[a]
104         else:
105           return lambda a,targ, frm='': '<li>%s: [%s] %s [%s]</li>' % ( a, targ._h.label, targ.label, targ.__href__() )
106
107       def __html__(self,ghis=None):
108         """Create html view"""
109         msg = []
110         if self._contentInitialised:
111           sect = self._h.label
112           msg.append( '<h1>%s: [%s] %s</h1>' % (self._h.title,self.label,self.title) )
113           msg.append( '<a href="../index.html">Home</a> &rarr; <a href="../index/%s.html">%s section index</a><br/>\n' % (sect, self._h.title) )
114           msg.append( '<ul>' )
115           for a in self.__dict__.keys():
116             if a[0] != '_':
117               if hasattr( self._a[a], 'useClass') and self._a[a].useClass == 'internalLink' and self._base._indexInitialised:
118                 if self.__dict__[a] == '__unset__':
119                   m = '<li>%s: %s [missing link]</li>' % ( a, self.__dict__[a] )
120                 else:
121                   try:
122                     targ = self._base._inx.uid[ self.__dict__[a] ]
123                   except:
124                     print ( a, self.__dict__[a], sect )
125                     raise
126                   lst = self.getHtmlLinkAttrStyle(a)
127                   m = lst( a, targ, frm=sect )
128                   ##m = '<li>%s: [%s] %s [%s]</li>' % ( a, targ._h.label, targ.label, targ.__href__() )
129               else:
130                 m = '<li>%s: %s</li>' % ( a, self.__dict__[a] )
131               msg.append( m )
132           msg.append( '</ul>' )
133##
134## add list of inward references
135##
136           if self._base._indexInitialised:
137             f1 = self._htmlStyle.get( sect, {} ).get( 'getIrefs', None ) != None
138             if f1:
139               tl = []
140               if f1:
141                 tl = self._htmlStyle[sect]['getIrefs']
142               doall = '__all__' in tl
143               if doall:
144                 tl = self._inx.iref_by_sect[self.uid].a.keys()
145               am = []
146               for t in tl:
147                 if t in self._inx.iref_by_sect[self.uid].a:
148                   am.append( '<h3>%s</h3>' % t )
149                   am.append( '<ul>' )
150                   items = [self._inx.uid[u] for  u in self._inx.iref_by_sect[self.uid].a[t] ]
151                   items.sort( ds('label').cmp )
152                   for targ in items:
153                     if ghis == None:
154                       m = '<li>%s:%s [%s]</li>' % ( targ._h.label, targ.label, targ.__href__() )
155                     else:
156                       lst = ghis( targ._h.label )
157                       m = lst( targ, frm=sect )
158                     am.append( m )
159                   am.append( '</ul>' )
160               if len(am) > 0:
161                  msg.append( '<h2>Links from other sections</h2>' )
162                  for m in am:
163                    msg.append(m)
164               
165         else:
166           msg.append( '<b>Item %s: uninitialised</b>' % self.sectionLabel )
167         return msg
168
169
170       def dictInit( self, dict ):
171         __doc__ = """Initialise from a dictionary."""
172         for a in self._a.keys():
173           if a in dict:
174             self.__dict__[a] = dict[a]
175           else:
176             self.__dict__[a] = self._d.defaults.get( a, self._d.glob )
177         self._contentInitialised = True
178
179       def mdInit( self, el, etree=False ):
180         __doc__ = """Initialisation from a mindom XML element. The list of attributes must be set by the class factory before the class is initialised"""
181         deferredHandling=False
182         nw1 = 0
183         tvtl = []
184         if etree:
185           ks = set( el.keys() )
186           for a in self._a.keys():
187             if a in ks:
188               aa = '%s%s' % (self.ns,a)
189               tvtl.append( (a,True, str( el.get( a ) ) ) )
190             else:
191               tvtl.append( (a,False,None) )
192         else:
193           for a in self._a.keys():
194             if el.hasAttribute( a ):
195               tvtl.append( (a,True, str( el.getAttribute( a ) ) ) )
196             else:
197               tvtl.append( (a,False,None) )
198       
199         for a,tv,v in tvtl:
200           if tv:
201             if self._a[a].type == u'xs:float':
202               try:
203                 v = float(v)
204               except:
205                 print ( 'Failed to convert real number: %s' % v )
206                 raise
207             elif self._a[a].type == u'xs:integer':
208               if self._rc.isIntStr( v ):
209                 v = int(v)
210               else:
211                 v = string.strip(v)
212                 thissect = '%s [%s]' % (self._h.title,self._h.tag)
213                 if v in { '',u'',' ', u' '}:
214                   if nw1 < 20:
215                     print ( 'WARN.050.0001: input integer non-compliant: %s: %s: "%s" -- set to zero' % (thissect,a,v) )
216                     nw1 += 1
217                   v = 0
218                 else:
219                   try:
220                     v = int(float(v))
221                     print ( 'WARN: input integer non-compliant: %s: %s: %s' % (thissect,a,v) )
222                   except:
223                     msg = 'ERROR: failed to convert integer: %s: %s: %s' % (thissect,a,v)
224                     deferredHandling=True
225             elif self._a[a].type == u'xs:boolean':
226               v = v in {'true','1'}
227             self.__dict__[a] = v
228           else:
229             if a in {'uid'}:
230               thissect = '%s [%s]' % (self._h.title,self._h.tag)
231               print ( 'ERROR.020.0001: missing uid: %s' % thissect )
232               if etree:
233                 print ( ks )
234                 import sys
235                 sys.exit(0)
236             self.__dict__[a] = self._d.defaults.get( a, self._d.glob )
237
238           ##if type( self.__dict__.get( 'rowIndex', 0 ) ) != type(0):
239             ##print 'Bad row index ', el.hasAttribute( 'rowIndex' )
240             ##raise
241           if deferredHandling:
242             print ( msg )
243
244         self._contentInitialised = True
245
246   
247class config(object):
248  """Read in a vocabulary collection configuration document and a vocabulary document"""
249
250  def __init__(self, configdoc='out/dreqDefn.xml', thisdoc='../workbook/trial_20150724.xml', useShelve=False):
251    self.rc = rechecks()
252    self.silent = True
253    self.vdef = configdoc
254    self.vsamp = thisdoc
255    self.nts = collections.namedtuple( 'sectdef', ['tag','label','title','id','itemLabelMode','level'] )
256    self.nti = collections.namedtuple( 'itemdef', ['tag','label','title','type','useClass','techNote'] )
257    self.ntt = collections.namedtuple( 'sectinit', ['header','attributes','defaults'] )
258    self.nt__default = collections.namedtuple( 'deflt', ['defaults','glob'] )
259    self.ntf = collections.namedtuple( 'sect', ['header','attDefn','items'] )
260    self.bscc = loadBS(blockSchemaFile)
261
262    self.coll = {}
263    doc = xml.dom.minidom.parse( self.vdef  )
264##
265## elementTree parsing implemented for main document
266##
267    self.etree = False
268    self.etree = True
269    if self.etree:
270      import xml.etree.cElementTree as cel
271
272      self.contentDoc = cel.parse( self.vsamp )
273      root = self.contentDoc.getroot()
274      ##bs = string.split( root.tag, '}' )
275      bs = root.tag.split( '}' )
276      if len( bs ) > 1:
277        self.ns = bs[0] + '}'
278      else:
279        self.ns = None
280    else:
281      self.contentDoc = xml.dom.minidom.parse( self.vsamp )
282      self.ns = None
283
284    vl = doc.getElementsByTagName( 'table' )
285    self.tables = {}
286    tables = {}
287    self.tableClasses = {}
288    self.tableItems = collections.defaultdict( list )
289##
290## this loads in some metadata, but not yet in a useful way.
291##
292    self._t0 = self.parsevcfg(None)
293    self.tt0 = {}
294    self._tableClass0 = self.itemClassFact( self._t0, ns=self.ns )
295    for k in self.bscc:
296      self.tt0[k] = self._tableClass0(dict=self.bscc[k])
297      setattr( self._tableClass0, '_%s' % k, self.tt0[k] )
298
299    for v in vl:
300      t = self.parsevcfg(v)
301      tables[t[0].label] = t
302      self.tableClasses[t[0].label] = self.itemClassFact( t, ns=self.ns )
303
304    self.recordAttributeDefn = tables
305    for k in tables.keys():
306      if self.etree:
307        vl = root.findall( './/%s%s' % (self.ns,k) )
308        if len(vl) == 1:
309          v = vl[0]
310          t = v.get( 'title' )
311          i = v.get( 'id' )
312          il = v.findall( '%sitem' % self.ns )
313          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
314 
315          self.tables[k] = (i,t,len(il))
316       
317          for i in il:
318            ii = self.tableClasses[k](xmlMiniDom=i, etree=True)
319            self.tableItems[k].append( ii )
320        elif len(vl) > 1:
321          assert False, 'not able to handle repeat sections with etree yet'
322      else:
323        vl = self.contentDoc.getElementsByTagName( k )
324        if len(vl) == 1:
325          v = vl[0]
326          t = v.getAttribute( 'title' )
327          i = v.getAttribute( 'id' )
328          il = v.getElementsByTagName( 'item' )
329          self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
330 
331          self.tables[k] = (i,t,len(il))
332       
333          for i in il:
334            ii = self.tableClasses[k](xmlMiniDom=i)
335            self.tableItems[k].append( ii )
336        elif len(vl) > 1:
337          l1 = []
338          l2 = []
339          for v in vl:
340            t = v.getAttribute( 'title' )
341            i = v.getAttribute( 'id' )
342            il = v.getElementsByTagName( 'item' )
343            self.info( '%s, %s, %s, %s' % ( k, t, i, len(il) ) )
344            l1.append( (i,t,len(il)) )
345         
346            l2i = []
347            for i in il:
348              ii = self.tableClasses[k](xmlMiniDom=i)
349              l2i.append( ii )
350            l2.append( l2i )
351          self.tables[k] = l1
352          self.tableItems[k] = l2
353      self.coll[k] = self.ntf( self.recordAttributeDefn[k].header, self.recordAttributeDefn[k].attributes, self.tableItems[k] )
354 
355  def info(self,ss):
356    if not self.silent:
357      print ( ss )
358
359  def get(self):
360    return self.coll
361
362  def itemClassFact(self, sectionInfo,ns=None):
363     class dreqItem(dreqItemBase):
364       """Inherits all methods from dreqItemBase.
365
366USAGE
367-----
368The instanstiated object contains a single data record. The "_h" attribute links to information about the record and the section it belongs to.
369
370object._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.
371
372object._h: a python named tuple describing the section. E.g. object._h.title is the section title (E.g. "CMOR Variables")
373"""
374       _base=dreqItemBase
375       
376     dreqItem.__name__ = 'dreqItem_%s' % str( sectionInfo.header.label )
377     dreqItem._h = sectionInfo.header
378     dreqItem._a = sectionInfo.attributes
379     dreqItem._d = sectionInfo.defaults
380     if sectionInfo.attributes != None:
381        self.addAttributes(dreqItem, sectionInfo.attributes )
382     ##dreqItem.itemLabelMode = itemLabelMode
383     ##dreqItem.attributes = attributes
384     dreqItem._rc = self.rc
385     dreqItem.ns = ns
386     return dreqItem
387
388  def addAttributes( self, thisClass, attrDict ):
389    for k in attrDict:
390      setattr( thisClass, '_%s' % k , attrDict[k] )
391         
392  def parsevcfg(self,v):
393      """Parse a section definition element, including all the record attributes. The results are returned as a namedtuple of attributes for the section and a dictionary of record attribute specifications."""
394      if v == None:
395        vtt = self.nts( '__core__', 'CoreAttributes', 'Core Attributes', '00000000', 'def', '0' )
396        idict = {'label':None, 'title':None, 'id':None, 'itemLabelMode':None, 'level':None }
397      else:
398        l = v.getAttribute( 'label' )
399        t = v.getAttribute( 'title' )
400        i = v.getAttribute( 'id' )
401        ilm = v.getAttribute( 'itemLabelMode' )
402        lev = v.getAttribute( 'level' )
403        il = v.getElementsByTagName( 'rowAttribute' )
404        vtt = self.nts( v.nodeName, l,t,i,ilm,lev )
405        idict = {}
406        for i in il:
407          tt = self.parseicfg(i)
408          idict[tt.label] = tt
409      deflt = self.nt__default( {}, '__unset__' )
410      return self.ntt( vtt, idict, deflt )
411
412  def parseicfg(self,i):
413      """Parse a record attribute specification"""
414      defs = {'type':"xs:string"}
415      ll = []
416      for k in ['label','title','type','useClass','techNote']:
417        if i.hasAttribute( k ):
418          ll.append( i.getAttribute( k ) )
419        else:
420          ll.append( defs.get( k, None ) )
421      l, t, ty, cls, tn = ll
422      self.lastTitle = t
423      return self.nti( i.nodeName, l,t,ty,cls,tn )
424
425class container(object):
426  """Simple container class, to hold a set of dictionaries of lists."""
427  def __init__(self, atl ):
428    self.uid = {}
429    for a in atl:
430      self.__dict__[a] =  collections.defaultdict( list )
431
432class c1(object):
433  def __init__(self):
434    self.a = collections.defaultdict( list )
435
436class index(object):
437  """Create an index of the document. Cross-references are generated from attributes with class 'internalLink'.
438This version assumes that each record is identified by an "uid" attribute and that there is a "var" section.
439Invalid internal links are recorded in tme "missingIds" dictionary.
440For any record, with identifier u, iref_by_uid[u] gives a list of the section and identifier of records linking to that record.
441"""
442
443  def __init__(self, dreq):
444    self.silent = True
445    self.uid = {}
446    self.uid2 = collections.defaultdict( list )
447    nativeAtts = ['uid','iref_by_uid','iref_by_sect','missingIds']
448    naok = map( lambda x: not x in dreq, nativeAtts )
449    assert all(naok), 'This version cannot index collections containing sections with names: %s' % str( nativeAtts )
450    self.var_uid = {}
451    self.var_by_name = collections.defaultdict( list )
452    self.var_by_sn = collections.defaultdict( list )
453    self.iref_by_uid = collections.defaultdict( list )
454    irefdict = collections.defaultdict( list )
455    for k in dreq.keys():
456      if 'sn' in dreq[k].attDefn:
457         self.__dict__[k] =  container( ['label','sn'] )
458      else:
459         self.__dict__[k] =  container( ['label'] )
460    ##
461    ## collected names of attributes which carry internal links
462    ##
463      for ka in dreq[k].attDefn.keys():
464        if hasattr( dreq[k].attDefn[ka], 'useClass') and dreq[k].attDefn[ka].useClass == 'internalLink':
465           irefdict[k].append( ka )
466
467    for k in dreq.keys():
468        for i in dreq[k].items:
469          assert 'uid' in i.__dict__, 'uid not found::\n%s\n%s' % (str(i._h),str(i.__dict__) )
470          if 'uid' in self.uid:
471            print ( 'ERROR.100.0001: Duplicate uid: %s [%s]' % (i.uid,i._h.title) )
472            self.uid2[i.uid].append( (k,i) )
473          else:
474### create index bx uid.
475            self.uid[i.uid] = i
476
477    self.missingIds = collections.defaultdict( list )
478    self.iref_by_sect = collections.defaultdict( c1 )
479    for k in dreq.keys():
480        for k2 in irefdict.get( k, [] ):
481          n1 = 0
482          n2 = 0
483          for i in dreq[k].items:
484            id2 = i.__dict__.get( k2 )
485            if id2 != '__unset__':
486              sect = i._h.label
487## append attribute name and target  -- item i.uid, attribute k2 reference item id2
488              self.iref_by_uid[ id2 ].append( (k2,i.uid) )
489              self.iref_by_sect[ id2 ].a[sect].append( i.uid )
490              if id2 in self.uid:
491                n1 += 1
492              else:
493                n2 += 1
494                self.missingIds[id2].append( (k,k2,i.uid) )
495          self.info(  'INFO:: %s, %s%s (%s)' % (k,k2,n1,n2) )
496
497    for k in dreq.keys():
498      for i in dreq[k].items:
499        self.__dict__[k].uid[i.uid] = i
500        self.__dict__[k].label[i.label].append( i.uid )
501        if 'sn' in dreq[k].attDefn:
502          self.__dict__[k].sn[i.sn].append( i.uid )
503
504  def info(self,ss):
505    if not self.silent:
506      print ( ss )
507
508class ds(object):
509  def __init__(self,k):
510    self.k = k
511  def cmp(self,x,y):
512    return cmp( x.__dict__[self.k], y.__dict__[self.k] )
513
514src1 = '../workbook/trial_20150831.xml'
515
516#DEFAULT LOCATION -- changed automatically when building distribution
517defaultDreq = 'dreq.xml'
518#DEFAULT CONFIG
519defaultConfig = 'dreq2Defn.xml'
520
521defaultDreqPath = '%s/%s' % (DOC_DIR, defaultDreq )
522defaultConfigPath = '%s/%s' % (DOC_DIR, defaultConfig )
523
524class loadDreq(object):
525  """Load in a vocabulary document.
526  dreqXML: full path to the XML document
527  configdoc: full path to associated configuration document
528  useShelve: flag to specify whether to retrieve data from cache (not implemented)
529  htmlStyles: dictionary of styling directives which influence structure of html page generates by the "makeHtml" method
530"""
531
532  def __init__(self,dreqXML=defaultDreqPath, configdoc=defaultConfigPath, useShelve=False, htmlStyles=None ):
533    self.c = config( thisdoc=dreqXML, configdoc=configdoc, useShelve=useShelve)
534    self.coll = self.c.get()
535    self.inx = index(self.coll)
536    self.defaultItemLineStyle = lambda i, frm='': '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
537    self.itemStyles = {}
538##
539## add index to Item base class .. so that it can be accessed by item instances
540##
541    dreqItemBase._inx = self.inx
542    dreqItemBase._indexInitialised = True
543##
544## load in additional styling directives
545##
546    if htmlStyles != None:
547      for k in htmlStyles:
548        dreqItemBase._htmlStyle[k] = htmlStyles[k]
549
550##    dreqItemBase._htmlStyle['__general__'] = {'addRemarks':True}
551
552    self.pageTmpl = """<html><head><title>%s</title>
553<link rel="stylesheet" type="text/css" href="%scss/dreq.css">
554</head><body>
555<div id="top">CMIP6 Data Request</div>
556%s</body></html>"""
557
558  def getHtmlItemStyle(self, sect):
559    if sect in self.itemStyles:
560      return self.itemStyles[sect]
561    return self.defaultItemLineStyle
562
563  def makeHtml(self,odir='./html', ttl0 = 'Data Request Index'):
564    """Generate a html view of the vocabularies, using the "__html__" method of the vocabulary item class to generate a
565page for each item and also generating index pages.
566    odir: directory for html files;
567    ttl0: Title for main index (in odir/index.html)"""
568
569    for k in self.inx.uid.keys():
570      i = self.inx.uid[k]
571      ttl = 'Data Request Record: [%s]%s' % (i._h.label,i.label)
572      bdy = string.join( i.__html__( ghis=self.getHtmlItemStyle ), '\n' )
573      oo = open( '%s/u/%s.html' % (odir,i.uid), 'w' )
574      oo.write( self.pageTmpl % (ttl, '../', bdy ) )
575      oo.close()
576
577    msg0 = ['<h1>%s</h1>' % ttl0, '<ul>',]
578    ks = sorted( self.coll.keys() )
579    for k in ks:
580##
581## sort on item label
582##
583      self.coll[k].items.sort( ds('label').cmp )
584      ttl = 'Data Request Section: %s' % k
585      msg0.append( '<li><a href="index/%s.html">%s [%s]</a></li>\n' % (k,self.coll[k].header.title,k) )
586      msg = ['<h1>%s</h1>\n' % ttl, '<ul>',]
587      msg.append( '<a href="../index.html">Home</a><br/>\n' )
588      lst = self.getHtmlItemStyle(k)
589      for i in self.coll[k].items:
590        ##m = '<li>%s: %s</li>' % ( i.label, i.__href__(odir='../u/') )
591        m = lst( i )
592        msg.append( m )
593      msg.append( '</ul>' )
594      bdy = string.join( msg, '\n' )
595      oo = open( '%s/index/%s.html' % (odir,k), 'w' )
596      oo.write( self.pageTmpl % (ttl, '../', bdy ) )
597      oo.close()
598    msg0.append( '</ul>' )
599    bdy = string.join( msg0, '\n' )
600    oo = open( '%s/index.html' % odir, 'w' )
601    oo.write( self.pageTmpl % (ttl0, '', bdy ) )
602    oo.close()
603   
604if __name__ == '__main__':
605  dreq = loadDreq( )
606
Note: See TracBrowser for help on using the repository browser.