def DelFile(root, interface): poolname = _GetPoolName(root, interface) if not poolname: return while 1: # get Protocol proto = clients.SelectProtocol( root, text='Select protocol class to remove file', canx='Done') if not proto: return # get list of files lst = glob.glob(poolname + '\\' + proto + '\\*.raw') lst.sort() # show files and allow a deletion IFF at least 5 files are left clist = [] for f in lst: try: fd = open(f, 'rb') except: ##print 'no open ',f continue cname = rawlist.rawheaderfield(fd, 'CLIENTNAME') fd.close() if not cname: org = "unk" else: spl = cname.split('\\') org = spl[-1] if len(spl) > 1: org = spl[-2] + '/' + org if len(spl) > 2: org = spl[-3] + '/' + org base = os.path.basename(f) clist.append(proto + '/' + base + ' from ' + org) if len(clist) < 6: tkMessageBox.showerror('Pool Files', 'There must be 5 or more defined') ##continue md = FileDlg(root, 'Selecting files to delete', clist, buttons=['Delete File', 'Cancel']) md.Activate() if md.val == 'Delete File': fext = md.ans[0].split() pname = poolname + '\\' + fext[0] try: os.unlink(pname) except: tkMessageBox.showerror('Pool Files', 'Failed to delete ' + pname) del md
def update_eyes(fobj): try: fdraw = open(fobj.fname[:-3] + 'raw', 'rb') except: return {} frq = rawlist.rawheaderfield(fdraw, 'CLOCKRATE') cbxx = rawlist.rawcb('', rawfd=fdraw) frq = rawlist.rawheaderfield(fdraw, 'CLOCKRATE') cbxx = rawlist.rawcb('', rawfd=fdraw) #must fix error in saving summary data status of eyes eoec = {} for cb in cbxx[0]: g, a, s = rawlist.unpack_action(cb[1]) if g == 14 and a == 0: vv = struct.unpack('ff', cb[3]) if vv[0] == 5.0: cls = int(vv[1]) if vv[1] == 2.0: cls = 2 sec = cb[2] / frq eoec[sec] = cls return eoec
def get_sum_file_fd(fd, details=1, prt=0, filename='', baseposition=0, layout=''): "External entry point to 'load' a summary file using file descriptor" global allsitelist, headerversion, formatstring, BLK # builds a 'sumfile' object posit = fd.tell() fd.seek(0, 2) fsize = fd.tell() fd.seek(baseposition, 0) # in case reading from inside raw file try: hdr = fd.read(struct.calcsize(HDR)) except: if not baseposition: fd.close() if prt: print '*****Error reading file header', filename, posit return None try: header = struct.unpack(HDR, hdr) except: if not baseposition: fd.close() if prt: print '*****Error unpacking header', filename, posit return None ftd, ffmt, kind, streams, lowpass, datecode, timecode = header[:7] fmtstring, gameid, lcode, spare, cid, cfull = header[-6:] if ftd != 'SUMD': if not baseposition: fd.close() if prt: print '*****Not a SUM file', filename return None headerversion = int(ffmt) if headerversion not in validversions: if not baseposition: fd.close() if prt: print '*****incorrect format', filename return None if prt: print 'version ', headerversion if headerversion >= 114: # for different header formats BLK = NEWBLK else: BLK = OLDBLK if headerversion >= 118: try: xxhdr = fd.read(struct.calcsize(HDR118)) except: if not baseposition: fd.close() if prt: print '*****Error reading xtra file header', filename, posit return None try: hdr118 = struct.unpack(HDR118, xxhdr) except: if not baseposition: fd.close() if prt: print '*****Error unpacking xtra header', filename, posit return None fileminutes = fsize / (streams * 120 * 3) # approx file minutes mxdt = mx.DateTime.DateTimeFromAbsDateTime(datecode, float(timecode)) fobj = SumFile() fobj.datestring = mx.DateTime.ISO.str(mxdt)[0:10] fobj.timestring = mx.DateTime.ISO.str(mxdt)[11:16] fobj.datecode = datecode fobj.timecode = timecode fobj.version = headerversion sd = mx.DateTime.ARPA.str(mxdt) try: k = sd.index(',') k = k + 2 except: k = 0 fobj.simpledate = sd[k:k + 7] + sd[k + 9:k + 11] fobj.formatstring = nonull(fmtstring) formatstring = fobj.formatstring if layout == '': try: fraw = open(filename[:-4] + '.raw', 'rb') val = rawlist.rawheaderfield(fraw, 'FILLER') pos = val.find('|') if pos > 0: layout = val[pos + 1:] fraw.close() except: pass fobj.layout = layout fobj.uname = nonull(cfull) fobj.ucode = nonull(cid) fobj.fname = filename fobj.lcode = lcode fobj.gameid = nonull(gameid) hdr_size = struct.calcsize(BLK) fobj.number_streams = streams fobj.number_lowpass = lowpass if headerversion >= 118: fobj.number_periph = hdr118[0] fobj.multiplier = hdr118[1:] else: fobj.number_periph = 1 allsitelist = {} if headerversion >= 112: fobj.has_total_in_percent = 1 if prt: print fobj.__dict__ dtm = [] pct = [] avg = [] periph = [] event = [] prev_mode = 0 prev_site = '' prev_freq = [] newcl = 0 prev_score = 0 prev_time = 0 total_per = 0 fp = None # for the data if prt: print 'fileminues,headerversion', fileminutes, headerversion while 1: posit = fd.tell() if prt > 2: print 'at ', posit try: blk = fd.read(hdr_size) except: if prt: print '*****Error reading a sub block header', posit if not baseposition: fd.close() return None if blk == '': if prt > 2: print 'blk length = 0' break try: bsize, fmt, tstamp = struct.unpack(BLK, blk) except: if not baseposition: fd.close() if prt: print '*****Partial block header', filename, posit return None ####print filename ####print 'fmt,num,tstamp',fmt,num,tstamp if prt: print 'bsize,fmt,offset', bsize, fmt, '%x' % posit if bsize == 0: if prt > 2: print 'block length = 0' break #end of all if headerversion < 114: if (fileminutes > 45 and headerversion < 108) or (fileminutes > 95 and fmt == 3): # here to try to fix up the values # before 108, there was no clamp on sessions so the block size # was only modulo 65536 (16 bit saved value!!!) # after 108, I allowed 100 minutes but the overall pct value still # overflowed the 16 bits. I now clamp at 90 minutes # but need to handle the bad files out there... # fix up bad headers if bsize < 0: bsize = 65536 + bsize if fmt <= 3 and fmt != 1: while 1: mins = (bsize - 6) / 600 if fileminutes < mins: if headerversion < 108: bsize -= 65536 break bsize += 65536 rest = bsize - hdr_size posit = fd.tell() try: part = fd.read(rest) except: if prt: print '*****Block read error', filename, '%x' % posit if not baseposition: fd.close() return None if len(part) != rest: if prt: print '*****Incomplete block read', filename, '%x' % posit if not baseposition: fd.close() return None try: if prt: try: ftype = NAMES[fmt].strip() if ftype == 'DATA': ftype = 'DATA+++++++++++' except: ftype = '????' print 'Block->', fmt, '(%s)' % ftype, streams, len( part), '%x' % posit if fmt == SUM_DATA: sg = data_part(streams, lowpass, part, tstamp, prt) dtm.append(sg) elif fmt == SUM_THRESH: # thresh thr = thresh_part(streams, lowpass, part, tstamp, prt) event.append(thr) elif fmt == SUM_AVERAGE: # long term avg if details: sg = data_part(streams, lowpass, part, tstamp, prt) sg.type = SUM_AVERAGE fobj.avg.append(sg) elif fmt == SUM_PERCENT: # percent if headerversion >= 112: n = streams + 1 # account for total % else: n = streams sg = data_part(n, lowpass, part, tstamp, prt) sg.type = SUM_PERCENT fobj.pct.append(sg) elif fmt == SUM_FREQ: # freq fp = freq_part(streams, lowpass, part, tstamp, prt) event.append(fp) elif fmt == SUM_SCALE: # scale sg = scale_part(streams, lowpass, part, tstamp, prt) event.append(sg) elif fmt == SUM_SITE: # channel/site s, m = site_part(streams, lowpass, part, tstamp, prt) if s: event.append(s) event.append(m) #LATER we worry about changes elif fmt == SUM_PERIOD: # period end pe = period_part(streams, lowpass, part, tstamp, prt) if pe != None and total_per < 128: if total_per: prevstart = fobj.per[-1].start if pe.start == prevstart: # aha must be the bogus one continue total_per += 1 pe.period_number = total_per fobj.per.append(pe) if pe.end > 5398: if prt: print 'Readjust pe.end ', pe.period_number, pe.end pe.end = 5398 if pe.start > 5396: if prt: print 'Readjust pe.start ', pe.period_number, pe.start pe.start = 5396 ev = EventSeg( SUM_PERIOD) # add two events also, begin/end period ev.start = pe.start ev.typename = 'BEGIN' ev.period = pe event.append(ev) ev = EventSeg(SUM_PERIOD) ev.start = pe.end ev.typename = 'END' ev.period = pe ev.overall_reward = pe.overall_reward event.append(ev) elif fmt == SUM_MODE: ev = mode_part(streams, lowpass, part, tstamp, prt) event.append(ev) elif fmt == SUM_MARK: ev = mark_part(streams, lowpass, part, tstamp, prt) event.append(ev) elif fmt == SUM_PERIPH: if fobj.number_periph: sg = data_part(fobj.number_periph, lowpass, part, tstamp, prt) # ?? streams stream in periph data sg.type = SUM_PERIPH fobj.periph.append(sg) elif fmt == SUM_ZSCORE: sg, zsckind = zscore_part(part, tstamp, prt) if sg: ##print 'zsc',len(sg.data) sg.type = SUM_ZSCORE fobj.zscore.append(sg) fobj.zscore_kind = zsckind elif fmt == (SUM_MARK | 0x100): # here for special recordings that have rawsub formatted data sg = misc_part(part, tstamp, prt) #action code,frame,data fobj.misc.append(sg) else: if prt: print '*****Invalid format code', filename, posit, fmt ## fd.close() ## return None continue except: if prt: print '******* error processing segment', filename, posit, fmt if prt > 2: raise if not baseposition: fd.close() ##raise return None if not baseposition: fd.close() # now 'merge' the data lists if len(dtm) == 0: if prt: print 'data length = 0' return None fobj.seg = combine_possible_segments(dtm[0].start, dtm) if len(fobj.seg) == 0: return None if len(fobj.seg[0].data) < 3: return None # sorry, I need 3 seconds of data if fobj.pct: fobj.pct = combine_possible_segments(fobj.pct[0].start, fobj.pct) if fobj.avg: fobj.avg = combine_possible_segments(fobj.avg[0].start, fobj.avg) if fobj.periph: fobj.periph = combine_possible_segments(fobj.periph[0].start, fobj.periph) # filter the combined event list # things looked for: # time a "change" begins for each kind of thing # when last change occurs for each kind of thing # value of last change!! # initial value # filtering freq changes, thresh changes, site changes, reward dir changes, fback mode changes holds = map(lambda x: None, range(16)) lasts = map(lambda x: None, range(16)) fev = [] for ev in event: t = ev.type if ev.type == SUM_MARK: fev.append(ev) ## elif ev.type == SUM_MODE: ## if holds[t] == None: ## lasts[t]=holds[t]=ev ## continue ## if ev.start - fr_hold.start > HOLD_TIME: ## fr_last.start=fr_hold.start ## fev.append(fr_last) ## fr_hold=ev elif ev.type == SUM_PERIOD: fev.append(ev) else: if holds[t] == None: holds[t] = lasts[t] = ev continue if ev.start - holds[t].start > HOLD_TIME: lasts[t].start = holds[t].start fev.append(lasts[t]) lasts[t] = holds[t] = ev lasts[t] = ev # now add in the guys left for k in range(len(holds)): if holds[k]: if holds[k] == lasts[k]: # if same, must be just a FIRST instance fev.append(holds[k]) else: # different, use time of first lasts[k].start = holds[k].start fev.append(lasts[k]) # after all this, NOW we have to sort back into time order fev.sort(evtime_sort) # aggregate the site list ## sitelist=[] ## for ev in fev: ## if ev.type == SUM_SITE: ## for s in ev.sites: ## try: ## sitelist.index?????????????? ##============================== fobj.events = fev fobj.number_periods = total_per fobj.number_seconds = len(fobj.seg[0].data) # ending seconds keys = allsitelist.keys() ##keys.sort() fobj.sitelist = keys if len(keys) == 0: fobj.sitelist = [ 'Unknown', ] return fobj
def raw2edf(rawfile, destination_dir, debug=0): ''' Convert RAW file to EDF ''' global SAMPLING_RATE global MAX_CHAN_LENGTH rawFD = open(rawfile, 'rb') # open RAW file for reading rawfile_info = rawlist.rawinfo(rawfile) rawfile_subset = rawlist.rawheaderfield(rawFD, 'SUBSET') rawfile_formatstring = rawlist.rawheaderfield(rawFD, 'FORMATSTRING') # get base name for use with GetClientFile basename = "\\".join(rawfile.split("\\")[:-2]) client = clients.GetClientFile(basename) # get patient identification, date and time from raw file datetime = rawlist.rawdate(rawfile) if debug > 1: print 'debugging' try: pid = client['client.xguid'] except: # for old version 4.0.3h and before pid = client['client.fullname'] patient = client['client.clientid'] rec_date = datetime[8:10] + '-' + MONTHS[int(datetime[5:7]) - 1] + '-' + datetime[0:4] startdate = datetime[8:10] + '.' + datetime[5:7] + '.' + datetime[2:4] starttime = datetime[11:13] + '.' + datetime[14:16] + '.00' # Combine directory for edf file and name of files that's going to be created rsplit = os.path.split(rawfile) rdir = os.path.split(rsplit[0]) actname = rsplit[1].split('.') output_file = destination_dir + '\\' + actname[0] + rdir[1][ 0] # add E,B,A for category c_scales = rawlist.rawparams(rawFD)[1:] # list of channel scales c_channels = [] # list of channel data peripheral = [] # peripheral data # there will be up to 3 scales (2 for channel a and b, 1 for peripheral) c_channels.append(rawlist.rawread(rawFD, CHAN_A)[0]) c_channels.append(rawlist.rawread(rawFD, CHAN_B)[0]) if rawfile_formatstring[2] == 'C': # must be 4 chan if debug > 1: print 'aha- 4 channels' c_channels.append(rawlist.rawread(rawFD, CHAN_C)[0]) c_channels.append(rawlist.rawread(rawFD, CHAN_D)[0]) ####!!!! # check for peripheral data (NOT INCORPORATED INTO EDF FILE YET) #if len(c_scales) > 2: # try: # peripheral.append( rawlist.rawread(rawFD, PERIPH)[0] ) # except: # pass # set 1) Sampling Rate, 2) Max channel length SAMPLING_RATE = rawfile_info[1] MAX_CHAN_LENGTH = ((len(max(c_channels)) / 2) / 256) if debug > 1: print SAMPLING_RATE, MAX_CHAN_LENGTH # get annotations annotations = [] initial_site = [] initial_site.append(rawfile_info[len(rawfile_info) - 1][1:]) numchannels = 0 if rawfile_subset >= 2: #sumfilepath = rawfile.split(".")[0] + ".SUM" #if os.path.exists(sumfilepath): # summary = sumfile.get_sum_file(sumfilepath, 0, 0) # if summary: # annotations = get_annotation_list(summary) # numchannels=summary.number_streams #if numchannels == 0: # # do it the hard way # while numchannels < 16: # dta=rawlist.readrawamplvalues(fd,numchannels,countonly=1) # if len(dta): # numchannels += 1 # else: # break for astr in range(len(rawfile_formatstring)): dta = rawlist.rawreadampl(rawFD, numchannels, prt=0) if len(dta): c_channels.append(dta[0][:]) if debug > 1: print 'achan ', len(c_channels[-1]), numchannels c_scales.append(0.01) numchannels += 1 startinfo = (pid, patient, rec_date, startdate, starttime, initial_site, numchannels, rawfile_formatstring) rawevents, ans = rawlist.rawcb(rawfile) if ans == 0: # now merge in the desired raw event data annotations = merge_rawsum(rawevents, annotations) #if numchannels: # for ch in range(numchannels): # c_scales.append(0.0) if debug > 1: print 'write2file', len(c_channels) for i in range(len(c_channels)): print i, len(c_channels[i]) write2file(startinfo, c_channels, c_scales, annotations, output_file, debug) rawFD.close() return 1
def compute_data(fobj,prt=0): try: fdraw=open(fobj.fname[:-3]+'raw','rb') except: return [] if prt: print fobj.fname rslt=csv_dump_out.csv_dump_out(None,fobj) seg=[] ampoffset=[0,0,0,0] throffset=[0,0,0,0] siteoffset=[0,0,0,0] frq=rawlist.rawheaderfield(fdraw,'CLOCKRATE') #cbxx=rawlist.rawcb('',rawfd=fdraw) #eoec=_update_eyes(cbxx[0],frq) if prt: print len(rslt) per=0 # figure out which columns have data I want period,eyes,ampl-0,ampl-1,thresh-0,thresh-1 eyes='' for num,ln in enumerate(rslt): fld=ln.split(',') if not num: numraw=fobj.number_lowpass perslot=fld.index('"Period"') eyeslot=fld.index('"Eyes"') for i in range(numraw): try: x=fld.index('"Ampl-%d"'%i) ampoffset[i]=x except: pass try: x=fld.index('"Thresh-%d"'%i) throffset[i]=x except: pass try: x=fld.index('"Chan-%c"' % chr(ord('A')+i)) siteoffset[i]=x except: pass continue #find beg of per #if eoec.has_key(int(fld[0])): # eyes=eoec[int(fld[0])] if int(fld[perslot]) == 0: if per == 0: ##if prt: ## print 'not found first period ',num continue # not found end of first period if prt: print 'found end of period ',per,num #here to process previous period logic p=fobj.per[per-1] ssites=p.sitelist for i in range(numraw): for s in checksites: look=s+'-a' if fld[siteoffset[i]][1:-1].lower()[:4] == look: # aha- this a a valid site lnx=rslt[num-1].split(',') seg.append((p.start,p.end,perstart,num-1,i,per,fld[eyeslot],fld[siteoffset[i]][1:-1])) #if prt: # print 'match ',fld[siteoffset[i]][1:-1],look,i,numraw,len(ssites) ##if prt: ## print 'no match ',ssites[j],look,i else: #if prt: # print 'no match ',fld[siteoffset[i]][1:-1],fld[siteoffset[i]][1:-1].lower()[:4],look,i pass per=0 continue if per== 0: perstart=num per=int(fld[perslot]) if prt: print 'start of period ',per,num # get here with possibles in seg if prt: print 'found possible ',len(seg) ans=[] for start,end,pstart,pend,ch,per,eyes,site in seg: if prt: print "s,e,ps,pe,ch,per",start,end,pstart,pend,ch,per if end-start < 30: if prt: print ' less than 30 seconds' continue # too short for i in range(numraw): if i != ch: #if prt: # print "not this chan" continue ans.append([start,end,pstart,pend,ch+100,per,eyes,site]) thrv=[] amplv=[] for k in range(pstart,pend): fld=rslt[k].split(',') thrv.append(fld[throffset[i]]) amplv.append(fld[ampoffset[i]]) beg=pstart org=None lstart=lend=0 poss=[] while beg < pend: if thrv[beg-pstart] > amplv[beg-pstart]: if org: if beg-org >= 30: poss.append((beg-org,org,beg-1)) org=None else: if org == None: org=beg beg += 1 if org: if pend-org >= 30: poss.append((pend-org,org,pend)) else: if len(poss) == 0: poss.append((pend-pstart,pstart,pend)) #now find longest segment if prt: print poss beg=0 for l,s,e in poss: if l > beg: lstart=s lend=e beg=l # now have start end if beg: ans.append([start,end,lstart,lend,ch,per,eyes,site]) if prt: print 'longest ',ch,per,start,end,lstart,lend # if not found, good sample # now do the fft stuff if prt: print 'found valid ',len(ans),' chans=',numraw final=[] dtaa=[] for i in range(numraw): dta=rawlist.rawreadvalues(fdraw,i) dtaa.append(dta) fdraw.close() extfft.initfft(frq) prevch=None prevper=0 grp=[] for start,end,valid,pend,ch,per,eyes,site in ans: if prt: print ch,valid,pend,per,site if (ch%100) != prevch or prevper != per: if prevch == None: prevch = ch%100 prevper=per else: final.append((prevch%100,site,prevper,eyes[1:-1],grp)) grp=[] prevch = ch%100 prevper=per dta=dtaa[ch% 100] #get the raw file span=pend-valid if ch < 100: if span > 60: span=60 b=pend-span-10*frq if b < start: b=start sumr=[0 for x in range(640)] cntr=0 for d in range((b+1)*frq,pend*frq,frq): x=extfft.compute_fft_array(dta[d-frq*10:d],frq*10,640) # take last 60 or less seconds of data and # compute FFT in 0.1 Hz bins and average the bins # for i in range(640): sumr[i] += x[i] cntr += 1 biggie=0 if cntr: for i in range(640): sumr[i] = sumr[i]/cntr if sumr[i] > biggie: biggie=sumr[i] else: if prt: print 'no fft samples!',b+1,pend sumr.append(biggie) grp.append(sumr) if grp != []: final.append((ch%100,site,per,eyes[1:-1],grp)) extfft.clean() # i s i, s 65*f if prt: for item in final: print item[0],item[1],item[2],len(item[4]),item[4][0][-1], if len(item[4]) > 1: print item[4][1][-1], else: print 'None', print return final # array of (chan,site, per,eyes,64 bins of average+max)
def smdumpfile(fname, fmtcheck=0): # get # channels of raw svals = [] try: fd = open(fname, 'rb') except: return None, None # make sure new format hasextradata = 0 subset = rawlist.rawheaderfield(fd, 'SUBSET') if subset > 1: hasextradata = subset if fmtcheck: if hasextradata == 0: fd.close() return None, None delta = 0 if hasextradata >= 3: delta = 1 fstr = rawlist.rawheaderfield(fd, 'FORMATSTRING') maxchan = 0 for c in fstr: if c == 'C': maxchan += 1 # get data list for each channel datalist = [] for ch in range(maxchan): dta = rawlist.rawreadvalues(fd, ch) if dta[0] == None: break datalist.append(dta) # get filtered data filtlist = [] if hasextradata: for ch in range(14 + delta): dta = rawlist.rawreadamplvalues(fd, ch) if dta == []: break filtlist.append(dta) # get reward list rwddict = _rawrewards(fd) datecode = rawlist.rawheaderfield(fd, 'DATECODE') timecode = rawlist.rawheaderfield(fd, 'TIMECODE') dt = mx.DateTime.DateTimeFromAbsDateTime(int(datecode), int(timecode)) datetime = (dt.year, dt.month, dt.day, dt.hour, dt.minute, int(dt.second)) threshdict = None if hasextradata: pos = rawlist.rawheaderfield(fd, 'SUMMARYOFFSET') if pos: threshdict = _getthresh(fd, pos) fd.close() if len(datalist) == 0: return None, None fmftext = None for frm in range(len(datalist[0])): data = [] for dta in datalist: try: data.append(dta[frm]) except: data.append(0.0) filt = [] if hasextradata: for flst in filtlist: try: filt.append(flst[frm]) except: filt.append(0.0) events = rwddict.get(frm, ()) other = '' rwd = 0 if len(events): for t, reason, x in events: if reason.find('REWARD') >= 0: rwd = 1 #for testing if len(other): other = other + ',' other = other + reason else: if len(other): other = other + ',' other = other + reason if not fmftext: fmftext = 'Time' fmf = '%.4f' for i in range(len(data)): fmftext = fmftext + ',Chan%s' % 'ABCD'[i] fmf = fmf + ",%.3f" fmftext = fmftext + ',Reward' fmf = fmf + ',%d' if hasextradata: delta = 0 if hasextradata >= 3: delta = 1 for i in range(len(filt) - delta): fmftext = fmftext + ',%d' % i fmf = fmf + ',%.2f' if delta: fmftext = fmftext + ',OT' fmf = fmf + ',%x' filt[-1] = 100 * filt[-1] fmftext = fmftext + ',other' fmf = fmf + ',"%s"' if threshdict: tx = threshdict.keys() tx.sort() thr = threshdict[tx[0]] for i in range(len(thr)): fmf = fmf + ',%.3f' fmftext = fmftext + ',Thrsh' + '%d' % i currthresh = thr svals.append(fmftext) if 0: line = '%.4f' % (frm / 256.0) for i in range(len(data)): line = line + ',%.3f' % data[i] line = line + ',%d' % rwd for i in range(len(filt)): line = line + ',%.2f' % filt[i] line = line + ',"%s"' % other else: dx = [ frm / 256.0, ] dx.extend(data) dx.append(rwd) if len(filt): dx.extend(filt) dx.append(other) if threshdict: currthresh = threshdict.get(frm, currthresh) dx.extend(currthresh) try: line = fmf % tuple(dx) except: print fmf print dx print fmftext raise svals.append(line) return '\n'.join(svals), datetime