source: CMIP6dreqbuild/trunk/src/framework/dreqPy/table_utils.py @ 1060

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CMIP6dreqbuild/trunk/src/framework/dreqPy/table_utils.py@1060
Revision 1060, 20.4 KB checked in by mjuckes, 3 years ago (diff)

tracing makeTable TOTAL bug

Line 
1import collections, os, sys
2
3try:
4    import xlsxwriter
5except:
6    print ('No xlsxwrite: will not make tables ...')
7
8try:
9  import vrev
10  import misc_utils
11except:
12  import dreqPy.vrev as vrev
13  import dreqPy.misc_utils as misc_utils
14
15def realmFlt( ss ):
16  if ss == '':
17    return ss
18  if ss.find( ' ' ) == -1:
19    return ss
20  return ss.split( ' ' )[0]
21
22python2 = True
23if sys.version_info[0] == 3:
24  python2 = False
25  def cmp(x,y):
26    if x == y:
27      return 0
28    elif x > y:
29      return 1
30    else:
31      return -1
32
33if sys.version_info >= (2,7):
34  from functools import cmp_to_key
35  oldpython = False
36else:
37  oldpython = True
38
39class cmpdn(object):
40  def __init__(self,kl):
41    self.kl = kl
42  def cmp(self,x,y):
43    for k in self.kl:
44      if x.__dict__[k] != y.__dict__[k]:
45        return cmp( x.__dict__[k], y.__dict__[k] )
46
47    return cmp( 0,0 )
48
49def cmpAnnex( x, y ):
50  ax = len(x) > 2 and x[:2] == 'em'
51  ay = len(y) > 2 and y[:2] == 'em'
52  bx = len(x) > 5 and x[:5] in ['CMIP5','CORDE','SPECS']
53  by = len(y) > 5 and y[:5] in ['CMIP5','CORDE','SPECS']
54  if ax  == ay and bx == by:
55    return cmp(x,y)
56  elif ax:
57    if by:
58      return cmp(0,1)
59    else:
60      return cmp(1,0)
61  elif ay:
62    if bx:
63      return cmp(1,0)
64    else:
65      return cmp(0,1)
66  elif bx:
67      return cmp(1,0)
68  else:
69    return cmp(0,1)
70
71if not oldpython:
72  kAnnex = cmp_to_key( cmpAnnex )
73  kCmpdn = cmp_to_key( cmpdn(['sn','label']).cmp )
74  kCmpdnPrl = cmp_to_key( cmpdn(['prov','rowIndex','label']).cmp )
75
76
77class xlsx(object):
78  def __init__(self,fn,xls=True,txt=False,txtOpts=None):
79    self.xls=xls
80    self.txt=txt
81    self.txtOpts = txtOpts
82    self.mcfgNote = 'Reference Volume (1 deg. atmosphere, 0.5 deg. ocean)'
83    if xls:
84      self.wb = xlsxwriter.Workbook('%s.xlsx' % fn)
85      self.hdr_cell_format = self.wb.add_format({'text_wrap': True, 'font_size': 14, 'font_color':'#0000ff', 'bold':1, 'fg_color':'#aaaacc'})
86      self.hdr_cell_format.set_text_wrap()
87      self.sect_cell_format = self.wb.add_format({'text_wrap': True, 'font_size': 14, 'font_color':'#0000ff', 'bold':1, 'fg_color':'#ccccbb'})
88      self.sect_cell_format.set_text_wrap()
89      self.cell_format = self.wb.add_format({'text_wrap': True, 'font_size': 11})
90      self.cell_format.set_text_wrap()
91
92    if txt:
93      self.oo = open( '%s.csv' % fn, 'w' )
94
95  def header(self,tableNotes,collected):
96    if self.xls:
97      sht = self.newSheet( 'Notes' )
98      sht.write( 0,0, '', self.hdr_cell_format )
99      sht.write( 0,1, 'Notes on tables', self.hdr_cell_format )
100      ri = 0
101      sht.set_column(0,0,30)
102      sht.set_column(1,1,60)
103      self.sht = sht
104      for t in tableNotes:
105        ri += 1
106        for i in range(2):
107          sht.write( ri,i, t[i], self.cell_format )
108
109      if collected != None:
110        ri += 2
111        sht.write( ri, 0, 'Table', self.sect_cell_format )
112        sht.write( ri, 1, self.mcfgNote, self.sect_cell_format )
113        ttl = 0.
114        for k in sorted( collected.keys() ):
115          ri += 1
116          sht.write( ri, 0, k )
117          sht.write( ri, 1, misc_utils.vfmt( collected[k]*2. ) )
118          ttl += collected[k]
119
120        ri += 1
121        sht.write( ri, 0, 'TOTAL' )
122        sht.write( ri, 1, misc_utils.vfmt( ttl*2. ) )
123
124    if self.txt:
125      self.oo.write( '\t'.join( ['Notes','','Notes on tables']) + '\n' )
126      for t in tableNotes:
127        self.oo.write( '\t'.join( ['Notes',] + list(t)) + '\n' )
128
129      if collected != None:
130        self.oo.write( '\t'.join( ['Notes','Table','Reference Volume (1 deg. atmosphere, 0.5 deg. ocean)']) + '\n')
131        for k in sorted( collected.keys() ):
132          self.oo.write( '\t'.join( ['Notes',k,misc_utils.vfmt( collected[k]*2. )]) + '\n' )
133
134  def cmvtabrec(self,j,t,orec):
135     if self.xls:
136        for i in range(len(orec)):
137           if str( type(orec[i]) ) == "<class 'dreq.dreqItem_CoreAttributes'>":
138             self.sht.write( j,i, '', self.cell_format )
139           else:
140             ##print i, orec[i], type( orec[i] )
141             try:
142                self.sht.write( j,i, orec[i], self.cell_format )
143             except:
144               print ('FAILED TO WRITE RECORD: %s' % str(orec))
145               print ('FAILED TO WRITE RECORD: %s' % str(orec[i]))
146               raise
147
148     if self.txt:
149        self.oo.write( '\t'.join( [t,] + orec) + '\n' )
150
151  def varrec(self,j,orec):
152     if self.xls:
153        for i in range(len(orec)):
154           self.sht.write( j,i, orec[i], self.cell_format )
155
156     if self.txt:
157        self.oo.write( '\t'.join( orec, '\t') + '\n' )
158
159  def var(self):
160      if self.xls:
161        self.sht = self.newSheet( 'var' )
162      j = 0
163      hrec = ['Long name', 'units', 'description', 'Variable Name', 'CF Standard Name' ]
164      if self.xls:
165          self.sht.set_column(1,1,40)
166          self.sht.set_column(1,2,30)
167          self.sht.set_column(1,3,60)
168          self.sht.set_column(1,4,40)
169          self.sht.set_column(1,5,40)
170          self.sht.set_column(1,20,40)
171          self.sht.set_column(1,21,40)
172
173      if self.xls:
174        for i in range(len(hrec)):
175          self.sht.write( j,i, hrec[i], self.hdr_cell_format )
176
177      if self.txt:
178        for i in range(len(hrec)):
179          self.oo.write( hrec[i] + '\t' )
180        self.oo.write( '\n' )
181
182  def cmvtab(self,t,addMips,mode='c',tslice=False,byFreqRealm=False):
183      if self.xls:
184        self.sht = self.newSheet( t )
185      j = 0
186      ncga = 'NetCDF Global Attribute'
187      if mode == 'c':
188        hrec = ['Priority','Long name', 'units', 'description', 'comment', 'Variable Name', 'CF Standard Name', 'cell_methods', 'positive', 'type', 'dimensions', 'CMOR Name', 'modeling_realm', 'frequency', 'cell_measures', 'prov', 'provNote','rowIndex','UID','vid','stid','Structure Title','valid_min', 'valid_max', 'ok_min_mean_abs', 'ok_max_mean_abs']
189        hcmt = ['Default priority (generally overridden by settings in "requestVar" record)',ncga,'','','Name of variable in file','','','CMOR directive','','','CMOR name, unique within table','','','','','','','','','','CMOR variable identifier','MIP variable identifier','Structure identifier','','','','','']
190        if self.xls:
191          self.sht.set_column(1,1,40)
192          self.sht.set_column(1,3,50)
193          self.sht.set_column(1,4,30)
194          self.sht.set_column(1,5,50)
195          self.sht.set_column(1,6,30)
196          self.sht.set_column(1,9,40)
197          self.sht.set_column(1,18,40)
198          self.sht.set_column(1,19,40)
199          self.sht.set_column(1,20,40)
200          self.sht.set_column(1,21,40)
201          self.sht.set_column(1,26,40)
202          self.sht.set_column(1,27,40)
203          self.sht.set_column(1,30,40)
204      else:
205        hrec = ['','Long name', 'units', 'description', '', 'Variable Name', 'CF Standard Name', '','', 'cell_methods', 'valid_min', 'valid_max', 'ok_min_mean_abs', 'ok_max_mean_abs', 'positive', 'type', 'dimensions', 'CMOR name', 'modeling_realm', 'frequency', 'cell_measures', 'flag_values', 'flag_meanings', 'prov', 'provNote','rowIndex','UID']
206      if addMips:
207        hrec.append( 'MIPs (requesting)' )
208        hrec.append( 'MIPs (by experiment)' )
209
210      if byFreqRealm:
211        hrec = ['Table',] + hrec
212        hcmt = ['CMOR table',] + hcmt
213      if tslice:
214          hrec += ['Number of Years','Slice Type','Years','Grid']
215          hcmt += ['','','','']
216
217      if self.xls:
218        for i in range(len(hrec)):
219          self.sht.write( j,i, hrec[i], self.hdr_cell_format )
220          if hcmt[i] != '':
221            self.sht.write_comment( j,i,hcmt[i])
222
223      if self.txt:
224        self.oo.write( 'MIP table\t' )
225        for i in range(len(hrec)):
226          self.oo.write( hrec[i] + '\t' )
227        self.oo.write( '\n' )
228        self.oo.write( t + '\t' )
229        for i in range(len(hrec)):
230          if hcmt[i] != '':
231            self.oo.write( hcmt[i] + '\t')
232          else:
233            self.oo.write( '\t')
234        self.oo.write( '\n' )
235
236  def newSheet(self,name):
237    self.worksheet = self.wb.add_worksheet(name=name)
238    return self.worksheet
239
240  def close(self):
241    if self.xls:
242      self.wb.close()
243    if self.txt:
244      self.oo.close()
245
246###
247### need to have name of experiment here, for the aggregation over MIPs to work ... in the column of request by MIPs
248###
249class makeTab(object):
250  def __init__(self, dq, subset=None, mcfgNote=None, dest='tables/test', skipped=set(), collected=None,xls=True,txt=False,txtOpts=None,byFreqRealm=False, tslice=None):
251    """txtOpts: gives option to list MIP variables instead of CMOR variables"""
252    if subset != None:
253      cmv = [x for x in dq.coll['CMORvar'].items if x.uid in subset]
254    else:
255      cmv = dq.coll['CMORvar'].items
256    self.byFreqRealm=byFreqRealm
257
258    ixt = collections.defaultdict(list)
259    if not byFreqRealm:
260      for i in cmv:
261        ixt[i.mipTable].append( i.uid )
262    else:
263      for i in cmv:
264        ixt['%s.%s' % (i.frequency,realmFlt( i.modeling_realm) )].append( i.uid )
265
266    if oldpython:
267        tables = sorted( ixt.keys(), cmp=cmpAnnex )
268    else:
269        tables = sorted( ixt.keys(), key=kAnnex )
270
271    addMips = True
272    if addMips:
273      chkv = vrev.checkVar(dq)
274    mode = 'c'
275    tableNotes = [
276       ('Request Version',str(dq.version)),
277       ('MIPs (...)','The last two columns in each row list MIPs associated with each variable. The first column in this pair lists the MIPs which are requesting the variable in one or more experiments. The second column lists the MIPs proposing experiments in which this variable is requested. E.g. If a variable is requested in a DECK experiment by HighResMIP, then HighResMIP appears in the first column and DECK in the second')]
278
279    wb = xlsx( dest, xls=xls, txt=txt )
280    if mcfgNote != None:
281      wb.mcfgNote = mcfgNote
282    wb.header( tableNotes, collected)
283
284    if txtOpts != None and txtOpts.mode == 'var':
285      vl =  list( set( [v.vid for v in cmv] )  )
286      vli = [dq.inx.uid[i] for i in vl]
287      if oldpython:
288        thisvli =  sorted( vli, cmp=cmpdn(['sn','label']).cmp )
289      else:
290        thisvli = sorted( vli, key=kCmpdn )
291      wb.var()
292     
293      j = 0
294      for v in thisvli:
295      ###hrec = ['Long name', 'units', 'description', 'Variable Name', 'CF Standard Name' ]
296         orec = [v.title, v.units, v.description, v.label, v.sn]
297         j += 1
298         wb.varrec( j,orec )
299    else:
300      withoo = False
301      for t in tables:
302        if withoo:
303          oo = open( 'tables/test_%s.csv' % t, 'w' )
304        wb.cmvtab(t,addMips,mode='c',tslice=tslice != None,byFreqRealm=byFreqRealm)
305
306        j = 0
307        if oldpython:
308          thiscmv =  sorted( [dq.inx.uid[u] for u in ixt[t]], cmp=cmpdn(['prov','rowIndex','label']).cmp )
309        else:
310          thiscmv = sorted( [dq.inx.uid[u] for u in ixt[t]], key=kCmpdnPrl )
311
312        for v in thiscmv:
313          cv = dq.inx.uid[ v.vid ]
314          strc = dq.inx.uid[ v.stid ]
315          if strc._h.label == 'remarks':
316            print ( 'ERROR: structure not found for %s: %s .. %s (%s)' % (v.uid,v.label,v.title,v.mipTable) )
317            ok = False
318          else:
319            sshp = dq.inx.uid[ strc.spid ]
320            tshp = dq.inx.uid[ strc.tmid ]
321            ok = all( [i._h.label != 'remarks' for i in [cv,strc,sshp,tshp]] )
322          #[u'shuffle', u'ok_max_mean_abs', u'vid', '_contentInitialised', u'valid_min', u'frequency', u'uid', u'title', u'rowIndex', u'positive', u'stid', u'mipTable', u'label', u'type', u'description', u'deflate_level', u'deflate', u'provNote', u'ok_min_mean_abs', u'modeling_realm', u'prov', u'valid_max']
323
324          if not ok:
325            if (t,v.label) not in skipped:
326              ml = []
327              for i in range(4):
328                 ii = [cv,strc,sshp,tshp][i]
329                 if ii._h.label == 'remarks':
330                   ml.append( ['var','struct','time','spatial'][i] )
331              print ( 'makeTables: skipping %s %s: %s' % (t,v.label,','.join( ml)) )
332              skipped.add( (t,v.label) )
333          else:
334            dims = []
335            dims +=  sshp.dimensions.split( '|' )
336            if 'odims' in strc.__dict__:
337              dims +=  strc.odims.split( '|' )
338            dims +=  tshp.dimensions.split( '|' )
339            if 'coords' in strc.__dict__:
340              dims +=  strc.coords.split( '|' )
341            dims = ' '.join( dims )
342            if "qcranges" in dq.inx.iref_by_sect[v.uid].a:
343              u = dq.inx.iref_by_sect[v.uid].a['qcranges'][0]
344              qc = dq.inx.uid[u]
345              ll = []
346              for k in ['valid_min', 'valid_max', 'ok_min_mean_abs', 'ok_max_mean_abs']:
347                if qc.hasattr(k):
348                  ll.append( '%s %s' % (qc.__dict__[k],qc.__dict__['%s_status' % k][0]) )
349                else:
350                  ll.append( '' )
351              valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs = tuple( ll )
352            else:
353              valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs = ('','','','')
354               
355            if mode == 'c':
356              try:
357                orec = [str(v.defaultPriority),cv.title, cv.units, cv.description, v.description, cv.label, cv.sn, strc.cell_methods, v.positive, v.type, dims, v.label, v.modeling_realm, v.frequency, strc.cell_measures, v.prov,v.provNote,str(v.rowIndex),v.uid,v.vid,v.stid,strc.title, valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs]
358              except:
359                print ('FAILED TO CONSTRUCT RECORD: %s [%s], %s [%s]' % (v.uid,v.label,cv.uid,cv.label) )
360                raise
361            else:
362              orec = ['',cv.title, cv.units, v.description, '', cv.label, cv.sn, '','', strc.cell_methods, valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs, v.positive, v.type, dims, v.label, v.modeling_realm, v.frequency, strc.cell_measures, strc.flag_values, strc.flag_meanings,v.prov,v.provNote,str(v.rowIndex),cv.uid]
363
364            if byFreqRealm:
365              orec = [v.mipTable,] + orec
366
367##!
368# CHECK -- ERROR HERE FOR "TOTAL" ROW --spurious mips in thismips ---
369##!
370## change "c" to something searchable
371            if addMips:
372              thismips = chkv.chkCmv( v.uid )
373              thismips2 = chkv.chkCmv( v.uid, byExpt=True )
374              orec.append( ','.join( sorted( list( thismips) ) ) )
375              orec.append( ','.join( sorted( list( thismips2) ) ) )
376
377            if tslice != None:
378              msgLevel = 0
379              if v.uid in tslice and msgLevel > 1:
380                print ( 'INFO.table_utils.01001: slice 3: %s : %s' % ( str( tslice[v.uid] ), v.label ) )
381              if v.uid not in tslice:
382                orec += ['All', '','','']
383              elif type( tslice[v.uid] ) == type( 0 ):
384                print ( 'ERROR: unexpected tslice type: %s, %s' % (v.uid, tslice[v.uid] ) )
385              elif len(  tslice[v.uid] ) == 3:
386                x,priority,grid = tslice[v.uid]
387                orec[0] = priority
388                if x != None:
389                   tslab,tsmode,a,b = x
390                   orec += [tslab,tsmode,'',grid]
391                else:   
392                   print ( 'WARN.table_utils.01001: slice 3: %s : %s' % ( str( tslice[v.uid] ), v.label ) )
393                   orec += ['*unknown*','','',grid]
394              else:
395                tslab,tsmode,a,b,priority,grid = tslice[v.uid]
396                if type( priority ) != type(1):
397                  thisp = priority
398                  priority = thisp[1]
399                  ##print 'ERROR in priority type[2]: ',priority, tslice[v.uid]
400                orec[0] = priority
401                     
402                if tsmode[:4] in ['simp','bran']:
403                   nys = b + 1 - a
404                   ys = range(a,b+1)
405                   orec += [str(nys), '',str(ys)]
406                elif tsmode in ['rangeplus']:
407                   nys = b + 1 - a + 0.01
408                   ys = [1850,] + range(a,b+1)
409                   orec += [str(nys), 'Partial 1850 + %s to %s' % (a,b),'%6.2f' % ys]
410                elif tsmode[:4] in ['YEAR']:
411                   nys = a
412                   ys = b
413                   orec += [str(nys), '',str(ys)]
414                else:
415                   orec += ['slice', tslab,'']
416                orec.append( grid )
417               
418            if withoo:
419              oo.write( '\t'.join(orec ) + '\n' )
420            j+=1
421            wb.cmvtabrec( j,t,orec )
422
423        if withoo:
424          oo.close()
425    wb.close()
426
427class tables(object):
428  def __init__(self,sc, odir='xls',xls=True,txt=False,txtOpts=None):
429      self.sc = sc
430      self.dq = sc.dq
431      ##self.mips = mips
432      self.odir = odir
433      self.accReset()
434      self.doXls = xls
435      self.doTxt = txt
436      self.txtOpts = txtOpts
437
438  def accReset(self):
439    self.acc = [0.,collections.defaultdict(int),collections.defaultdict( float ) ]
440
441  def accAdd(self,x):
442    self.acc[0] += x[0]
443    for k in x[2]:
444       self.acc[2][k] += x[2][k]
445
446
447  def doTable(self,m,l1,m2,pmax,collector,acc=True, mlab=None,exptids=None,cc=None):
448      """*acc* allows accumulation of values to be switched off when called in single expt mode"""
449     
450      self.verbose = False
451      if mlab == None:
452        mlab = misc_utils.setMlab( m )
453
454      cc0 = misc_utils.getExptSum( self.dq, mlab, l1 )
455      ks = sorted( list( cc0.keys() ) )
456      if self.verbose:
457        print ('Experiment summary: %s %s' % (mlab,', '.join( ['%s: %s' % (k,len(cc0[k])) for k in ks] ) ) )
458
459      if m2 in [None, 'TOTAL']:
460        x = self.acc
461      else:
462        x = self.sc.volByExpt( l1, m2, pmax=pmax )
463
464##self.volByExpt( l1, e, pmax=pmax, cc=cc, retainRedundantRank=retainRedundantRank, intersection=intersection, adsCount=adsCount )
465        v0 = self.sc.volByMip( m, pmax=pmax,  exptid=m2 )
466####
467        if cc==None:
468          cc = collections.defaultdict( int )
469        for e in self.sc.volByE:
470          if self.verbose:
471             print ('INFO.mlab.... %s: %s: %s' % ( mlab, e, len( self.sc.volByE[e][2] ) ) )
472          for v in self.sc.volByE[e][2]:
473             cc[v] += self.sc.volByE[e][2][v]
474        xxx = 0
475        for v in cc:
476          xxx += cc[v]
477####
478        if acc:
479          for e in self.sc.volByE:
480            self.accAdd(self.sc.volByE[e])
481
482      if m2 not in [ None, 'TOTAL']:
483          im2 = self.dq.inx.uid[m2]
484          ismip = im2._h.label == 'mip'
485          mlab2 = im2.label
486
487          x0 = 0
488          for e in self.sc.volByE:
489            if exptids == None or e in exptids:
490              x = self.sc.volByE[e]
491              if x[0] > 0:
492                collector[mlab].a[mlab2] += x[0]
493                x0 += x[0]
494      else:
495          ismip = False
496          mlab2 = 'TOTAL'
497          x0 = x[0]
498
499      if mlab2 == 'TOTAL' and x0 == 0:
500        print ( 'no data detected for %s' % mlab )
501
502      if x0 > 0:
503#
504# create sum for each table
505#
506        xs = 0
507        kkc = '_%s_%s' % (mlab,mlab2)
508        kkct = '_%s_%s' % (mlab,'TOTAL')
509        if m2 in [None, 'TOTAL']:
510          x = self.acc
511          x2 = set(x[2].keys() )
512          for k in x[2].keys():
513           i = self.dq.inx.uid[k]
514           xxx =  x[2][k]
515           xs += xxx
516        else:
517          x2 = set()
518          for e in self.sc.volByE:
519            if exptids == None or e in exptids:
520              x = self.sc.volByE[e]
521              x2 = x2.union( set( x[2].keys() ) )
522              for k in x[2].keys():
523               i = self.dq.inx.uid[k]
524               xxx =  x[2][k]
525               xs += xxx
526               if xxx > 0:
527                collector[kkc].a[i.mipTable] += xxx
528                if ismip:
529                  collector[kkct].a[i.mipTable] += xxx
530
531##
532## One user was getting false error message here, with ('%s' % x0) == ('%s' % xs)
533##
534        if abs(x0 -xs) > 1.e-8*( abs(x0) + abs(xs) ):
535          print ( 'ERROR.0088: consistency problem %s  %s %s %s' % (m,m2,x0,xs) )
536        if x0 == 0:
537          print ( 'Zero size: %s, %s' % (m,m2) )
538          if len( x[2].keys() ) > 0:
539             print ( 'ERROR:zero: %s, %s: %s' % (m,m2,str(x[2].keys()) ) )
540
541        if acc and m2 not in [ None, 'TOTAL']:
542          collector[mlab].a['TOTAL'] += x0
543
544        dd = collections.defaultdict( list )
545        lll = set()
546        for v in x2:
547          vi = self.sc.dq.inx.uid[v]
548          if vi._h.label != 'remarks':
549            f,t,l,tt,d,u = (vi.frequency,vi.mipTable,vi.label,vi.title,vi.description,vi.uid)
550            lll.add(u)
551            dd[t].append( (f,t,l,tt,d,u) )
552
553        if len( dd.keys() ) > 0:
554          collector[mlab].dd[mlab2] = dd
555          if m2 not in [ None, 'TOTAL']:
556            if im2._h.label == 'experiment':
557              dothis = self.sc.tierMax >= min( im2.tier )
558###
559### BUT ... there is a treset in the request item .... it may be that some variables are excluded ...
560###         need the variable list itself .....
561###
562          makeTab( self.sc.dq, subset=lll, dest='%s/%s-%s_%s_%s' % (self.odir,mlab,mlab2,self.sc.tierMax,pmax), collected=collector[kkc].a,
563              mcfgNote=self.sc.mcfgNote,
564              txt=self.doTxt, xls=self.doXls, txtOpts=self.txtOpts )
Note: See TracBrowser for help on using the repository browser.