示例#1
0
文件: timing.py 项目: sponce/fstimer
 def update_clock(self):
     '''Updates the clock'''
     # compute time
     t = time.time()-self.t0
     # update label
     self.clocklabel.set_markup(time_format(t))
     # keep updating
     return True
示例#2
0
文件: timing.py 项目: tomp/xctimer
 def update_clock(self):
     '''Updates the clock'''
     # compute time
     t = time.time() - self.t0
     # update label
     self.clocklabel.set_markup(time_format(t))
     # keep updating
     return True
示例#3
0
文件: timing.py 项目: sponce/fstimer
 def new_blank_time(self):
     '''Record a new time'''
     t = time_format(time.time()-self.t0)
     self.rawtimes['times'].insert(0, t) #we prepend to rawtimes, just as we prepend to timemodel
     if self.offset >= 0:
         # No IDs in the buffer, so just prepend it to the liststore.
         self.timemodel.prepend(['', t])
     elif self.offset < 0:
         # IDs in the buffer, so add the time to the oldest ID
         # put it in the last available ID slot
         self.timemodel.set_value(self.timemodel.get_iter(-self.offset-1), 1, t)
     self.offset += 1
     self.entrybox.set_text('')
示例#4
0
文件: timing.py 项目: tomp/xctimer
 def new_blank_time(self):
     '''Record a new time'''
     t = time_format(time.time() - self.t0)
     self.rawtimes['times'].insert(
         0, t)  #we prepend to rawtimes, just as we prepend to timemodel
     if self.offset >= 0:
         # No IDs in the buffer, so just prepend it to the liststore.
         self.timemodel.prepend(['', t])
     elif self.offset < 0:
         # IDs in the buffer, so add the time to the oldest ID
         # put it in the last available ID slot
         self.timemodel.set_value(self.timemodel.get_iter(-self.offset - 1),
                                  1, t)
     self.offset += 1
     self.entrybox.set_text('')
示例#5
0
def sort_results(result_rows, rank_indx, cols):
    # Try sorting as float, but if that doesn't work, use string.
    try:
        # Define a sorter that will handle the Nones
        def floatsort(x):
            if x[1][rank_indx] is None:
                return 1e20
            else:
                return x[1][rank_indx]

        result_rows = sorted(result_rows, key=floatsort)
    except TypeError:

        def stringsort(x):
            if x[1][rank_indx] is None:
                return ''
            else:
                return x[1][rank_indx]

        result_rows = sorted(result_rows, key=stringsort)
    # Remove duplicate entries: If a tag has multiple entries, keep only the
    # most highly ranked. Also replace total times and pace times with
    # formatted times, stringify everything but Lap Times, and replace Nones.
    taglist = set()
    result_rows_dedup = []
    for tag, row in result_rows:
        if tag in taglist:
            pass  # drop it
        else:
            taglist.add(tag)
            row_new = []
            for i, val in enumerate(row):
                if val is None:
                    if i == rank_indx:
                        val = '_'
                    else:
                        val = ''
                elif cols[i] in ['Time', 'Pace']:
                    val = time_format(val)
                elif cols[i] == 'Lap Times':
                    pass  # Leave it as is
                else:
                    val = str(val)
                row_new.append(val)
            result_rows_dedup.append((tag, row_new))
    return result_rows_dedup
示例#6
0
def sort_results(result_rows, rank_indx, cols):
    # Try sorting as float, but if that doesn't work, use string.
    try:
        # Define a sorter that will handle the Nones
        def floatsort(x):
            if x[1][rank_indx] is None:
                return 1e20
            else:
                return x[1][rank_indx]
        result_rows = sorted(result_rows, key=floatsort)
    except TypeError:
        def stringsort(x):
            if x[1][rank_indx] is None:
                return ''
            else:
                return x[1][rank_indx]
        result_rows = sorted(result_rows, key=stringsort)
    # Remove duplicate entries: If a tag has multiple entries, keep only the
    # most highly ranked. Also replace total times and pace times with
    # formatted times, stringify everything but Lap Times, and replace Nones.
    taglist = set()
    result_rows_dedup = []
    for tag, row in result_rows:
        if tag in taglist:
            pass  # drop it
        else:
            taglist.add(tag)
            row_new = []
            for i, val in enumerate(row):
                if val is None:
                    if i == rank_indx:
                        val = '_'
                    else:
                        val = ''
                elif cols[i] in ['Time', 'Pace']:
                    val = time_format(val)
                elif cols[i] == 'Lap Times':
                    pass  # Leave it as is
                else:
                    val = str(val)
                row_new.append(val)
            result_rows_dedup.append((tag, row_new))
    return result_rows_dedup
示例#7
0
文件: timing.py 项目: sponce/fstimer
 def __init__(self, pytimer, timebtn):
     '''Builds and display the compilation error window'''
     super(TimingWin, self).__init__(Gtk.WindowType.TOPLEVEL)
     self.path = pytimer.path
     self.projecttype = pytimer.projecttype
     self.fields = pytimer.fields
     self.fieldsdic = pytimer.fieldsdic
     self.write_timing_cb = pytimer.write_updated_timing
     self.timebtn = timebtn
     self.rawtimes = pytimer.rawtimes
     self.timing = pytimer.timing
     self.numlaps = pytimer.numlaps
     self.wineditblocktime = None
     self.winedittime = None
     self.t0win = None
     self.modify_bg(Gtk.StateType.NORMAL, fstimer.gui.bgcolor)
     self.set_transient_for(pytimer.rootwin)
     self.set_modal(True)
     self.set_title('fsTimer - ' + os.path.basename(self.path))
     self.set_position(Gtk.WindowPosition.CENTER)
     self.connect('delete_event', lambda b, jnk: self.done_timing(b))
     self.set_border_width(10)
     self.set_size_request(450, 450)
     # We will put the timing info in a liststore in a scrolledwindow
     self.timemodel = Gtk.ListStore(str, str)
     # We will put the liststore in a treeview
     self.timeview = Gtk.TreeView()
     column = Gtk.TreeViewColumn('ID', Gtk.CellRendererText(), text=0)
     self.timeview.append_column(column)
     column = Gtk.TreeViewColumn('Time', Gtk.CellRendererText(), text=1)
     self.timeview.append_column(column)
     #An extra column if it is a handicap race
     if self.projecttype == 'handicap':
         renderer = Gtk.CellRendererText()
         column = Gtk.TreeViewColumn('Corrected Time', renderer)
         column.set_cell_data_func(renderer, self.print_corrected_time)
         self.timeview.append_column(column)
     #Another extra column if it is a lap race
     if self.numlaps > 1:
         renderer = Gtk.CellRendererText()
         column = Gtk.TreeViewColumn('Completed laps', renderer)
         column.set_cell_data_func(renderer, self.print_completed_laps)
         self.timeview.append_column(column)
     self.timeview.set_model(self.timemodel)
     self.timeview.connect('size-allocate', self.scroll_times)
     treeselection = self.timeview.get_selection()
     # make it multiple selecting
     treeselection.set_mode(Gtk.SelectionMode.MULTIPLE)
     # And put it in a scrolled window, in an alignment
     self.timesw = Gtk.ScrolledWindow()
     self.timesw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
     self.timesw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
     self.timesw.add(self.timeview)
     timealgn = Gtk.Alignment.new(0, 0, 1, 1)
     timealgn.add(self.timesw)
     self.entrybox = Gtk.Entry()
     self.entrybox.set_max_length(40)
     self.offset = 0 #this is len(times) - len(ids)
     self.entrybox.connect('activate', self.record_time)
     self.entrybox.connect('changed', self.check_for_newtime)
     # And we will save our file
     self.timestr = re.sub(' +', '_', time.ctime()).replace(':', '')
     #we save with the current time in the filename so no chance of being overwritten accidentally
     # Now lets go on to boxes
     tophbox = Gtk.HBox()
     # our default t0, and the stuff on top for setting/edit t0
     self.t0 = 0.
     btn_t0 = Gtk.Button('Start!')
     btn_t0.connect('clicked', self.set_t0)
     # time display
     self.clocklabel = Gtk.Label()
     self.clocklabel.modify_font(Pango.FontDescription("sans 20"))
     self.clocklabel.set_markup(time_format(0))
     tophbox.pack_start(btn_t0, False, False, 10)
     tophbox.pack_start(self.clocklabel, False, False, 10)
     timevbox1 = Gtk.VBox(False, 8)
     timevbox1.pack_start(tophbox, False, False, 0)
     timevbox1.pack_start(timealgn, True, True, 0)
     timevbox1.pack_start(Gtk.Label('Select box below in order to mark times:'), False, False, 0)
     timevbox1.pack_start(self.entrybox, False, False, 0)
     # we will keep track of how many racers are still out.
     self.racers_reg = []
     for i_unused in range(self.numlaps):
         self.racers_reg.append(set([k for k in self.timing.keys()]))
     self.racers_total = len(self.racers_reg[0])
     self.racers_in = [0] * self.numlaps
     self.lapcounter = defaultdict(int)
     self.racerslabel = Gtk.Label()
     self.update_racers_label()
     timevbox1.pack_start(self.racerslabel, False, False, 0)
     vbox1align = Gtk.Alignment.new(0, 0, 1, 1)
     vbox1align.add(timevbox1)
     # buttons on the right side
     #First an options button that will actually be a menu
     options_menu = Gtk.Menu()
     menu_editreg = Gtk.MenuItem('Edit registration data')
     menu_editreg.connect_object("activate", self.edit_reg, None)
     menu_editreg.show()
     options_menu.append(menu_editreg)
     menu_resett0 = Gtk.MenuItem('Restart clock')
     menu_resett0.connect_object("activate", self.restart_t0, None)
     menu_resett0.show()
     options_menu.append(menu_resett0)
     menu_editt0 = Gtk.MenuItem('Edit starting time')
     menu_editt0.connect_object("activate", self.edit_t0, None)
     menu_editt0.show()
     options_menu.append(menu_editt0)
     menu_savecsv = Gtk.MenuItem('Save results to CSV')
     menu_savecsv.connect_object("activate", self.print_csv, pytimer)
     menu_savecsv.show()
     options_menu.append(menu_savecsv)
     menu_resume = Gtk.MenuItem('Load saved timing session')
     menu_resume.connect_object("activate", self.resume_times, None, False) #False is for not merging
     menu_resume.show()
     options_menu.append(menu_resume)
     menu_merge = Gtk.MenuItem('Merge in saved IDs or times')
     menu_merge.connect_object("activate", self.resume_times, None, True) #True is for merging
     menu_merge.show()
     options_menu.append(menu_merge)
     btnOPTIONS = Gtk.Button('Options')
     btnOPTIONS.connect_object("event", self.options_btn, options_menu)
     options_align = Gtk.Alignment.new(1, 0.1, 1, 0)
     options_align.add(btnOPTIONS)
     #Then the block of editing buttons
     btnDROPID = Gtk.Button('Drop ID')
     btnDROPID.connect('clicked', self.timing_rm_ID)
     btnDROPTIME = Gtk.Button('Drop time')
     btnDROPTIME.connect('clicked', self.timing_rm_time)
     btnEDIT = GtkStockButton('edit',"Edit")
     btnEDIT.connect('clicked', self.edit_time)
     edit_vbox = Gtk.VBox(True, 8)
     edit_vbox.pack_start(btnDROPID, False, False, 0)
     edit_vbox.pack_start(btnDROPTIME, False, False, 0)
     edit_vbox.pack_start(btnEDIT, False, False, 0)
     edit_align = Gtk.Alignment.new(1, 0, 1, 0)
     edit_align.add(edit_vbox)
     #Then the print and save buttons
     btnPRINT = Gtk.Button('Printouts')
     btnPRINT.connect('clicked', self.print_html, pytimer)
     btnSAVE = GtkStockButton('save',"Save")
     btnSAVE.connect('clicked', self.save_times)
     save_vbox = Gtk.VBox(True, 8)
     save_vbox.pack_start(btnPRINT, False, False, 0)
     save_vbox.pack_start(btnSAVE, False, False, 0)
     save_align = Gtk.Alignment.new(1, 1, 1, 0)
     save_align.add(save_vbox)
     #And finally the finish button
     btnOK = GtkStockButton('close',"Close")
     btnOK.connect('clicked', self.done_timing)
     done_align = Gtk.Alignment.new(1, 0.7, 1, 0)
     done_align.add(btnOK)
     vsubbox = Gtk.VBox(True, 0)
     vsubbox.pack_start(options_align, True, True, 0)
     vsubbox.pack_start(edit_align, True, True, 0)
     vsubbox.pack_start(save_align, True, True, 0)
     vsubbox.pack_start(done_align, True, True, 0)
     vspacer = Gtk.Alignment.new(1, 1, 0, 0)
     vspacer.add(vsubbox)
     timehbox = Gtk.HBox(False, 8)
     timehbox.pack_start(vbox1align, True, True, 0)
     timehbox.pack_start(vspacer, False, False, 0)
     self.add(timehbox)
     self.show_all()
示例#8
0
def get_sorted_results(projecttype, passid, numlaps, variablelaps, timing,
                       rawtimes, ranking_key, cols, col_fns):
    '''returns a sorted list of (id, result) items.
        The content of result depends on the race type'''
    # get raw times
    timeslist = zip(*get_sync_times_and_ids(rawtimes))
    #Handle blank times, and handicap correction
    # Handicap correction
    if projecttype == 'handicap':
        new_timeslist = []
        for tag, time in timeslist:
            if tag and time and tag != passid:
                try:
                    new_timeslist.append(
                        (tag, time_diff(time, timing[tag]['Handicap'])))
                except AttributeError:
                    # time or Handicap couldn't be converted to timedelta
                    new_timeslist.append((tag, '_'))
            # Else: drop entries with blank tag, blank time, or pass ID
        timeslist = list(new_timeslist)  #replace
    else:
        #Drop times that are blank or have the passid
        timeslist = [(tag, time) for tag, time in timeslist
                     if tag and time and tag != passid]
    # Compute lap times, if a lap race
    if numlaps > 1:
        # multi laps - groups times by tag
        # Each value of laptimesdic is a list, sorted in order from
        # earliest time (1st lap) to latest time (last lap).
        timeslist_sorted = []
        for (tag, time) in timeslist:
            try:
                timeslist_sorted.append((tag, time_parse(time)))
            except AttributeError:
                pass
        timeslist_sorted = sorted(timeslist_sorted, key=lambda x: x[1])
        laptimesdic = defaultdict(list)
        for (tag, time) in timeslist_sorted:
            laptimesdic[tag].append(time_format(time.total_seconds()))
        # compute the lap times.
        lap_times = {}
        total_times = {}
        for tag in laptimesdic:
            # First put the total race time
            if len(laptimesdic[tag]) == numlaps or variablelaps:
                total_times[tag] = laptimesdic[tag][-1]
            else:
                total_times[tag] = '_'
            # And the first lap
            lap_times[tag] = ['1 - ' + laptimesdic[tag][0]]
            # And now the subsequent laps
            for ii in range(len(laptimesdic[tag]) - 1):
                try:
                    lap_times[tag].append('{} - {}'.format(
                        ii + 2,
                        time_diff(laptimesdic[tag][ii + 1],
                                  laptimesdic[tag][ii])))
                except AttributeError:
                    lap_times[tag].append(str(ii + 2) + ' - _')
        # Now correct timeslist to have the new total times
        timeslist = list(total_times.items())
    else:
        lap_times = defaultdict(int)
    # Compute each results row
    result_rows = []
    for tag, time in timeslist:
        row = get_result_row(tag, time, lap_times, timing, col_fns)
        result_rows.append((tag, row))
    # sort by column of ranking_key.
    rank_indx = cols.index(ranking_key)
    return sort_results(result_rows, rank_indx, cols)
示例#9
0
文件: timing.py 项目: tomp/xctimer
 def __init__(self, pytimer, timebtn):
     '''Builds and display the compilation error window'''
     super(TimingWin, self).__init__(Gtk.WindowType.TOPLEVEL)
     self.path = pytimer.path
     self.projecttype = pytimer.projecttype
     self.fields = pytimer.fields
     self.fieldsdic = pytimer.fieldsdic
     self.write_timing_cb = pytimer.write_updated_timing
     self.timebtn = timebtn
     self.rawtimes = pytimer.rawtimes
     self.timing = pytimer.timing
     self.numlaps = pytimer.numlaps
     self.wineditblocktime = None
     self.winedittime = None
     self.t0win = None
     self.modify_bg(Gtk.StateType.NORMAL, fstimer.gui.bgcolor)
     self.set_transient_for(pytimer.rootwin)
     self.set_modal(True)
     self.set_title('fsTimer - ' + os.path.basename(self.path))
     self.set_position(Gtk.WindowPosition.CENTER)
     self.connect('delete_event', lambda b, jnk: self.done_timing(b))
     self.set_border_width(10)
     self.set_size_request(450, 450)
     # We will put the timing info in a liststore in a scrolledwindow
     self.timemodel = Gtk.ListStore(str, str)
     # We will put the liststore in a treeview
     self.timeview = Gtk.TreeView()
     column = Gtk.TreeViewColumn('ID', Gtk.CellRendererText(), text=0)
     self.timeview.append_column(column)
     column = Gtk.TreeViewColumn('Time', Gtk.CellRendererText(), text=1)
     self.timeview.append_column(column)
     #An extra column if it is a handicap race
     if self.projecttype == 'handicap':
         renderer = Gtk.CellRendererText()
         column = Gtk.TreeViewColumn('Corrected Time', renderer)
         column.set_cell_data_func(renderer, self.print_corrected_time)
         self.timeview.append_column(column)
     #Another extra column if it is a lap race
     if self.numlaps > 1:
         renderer = Gtk.CellRendererText()
         column = Gtk.TreeViewColumn('Completed laps', renderer)
         column.set_cell_data_func(renderer, self.print_completed_laps)
         self.timeview.append_column(column)
     self.timeview.set_model(self.timemodel)
     self.timeview.connect('size-allocate', self.scroll_times)
     treeselection = self.timeview.get_selection()
     # make it multiple selecting
     treeselection.set_mode(Gtk.SelectionMode.MULTIPLE)
     # And put it in a scrolled window, in an alignment
     self.timesw = Gtk.ScrolledWindow()
     self.timesw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
     self.timesw.set_policy(Gtk.PolicyType.AUTOMATIC,
                            Gtk.PolicyType.AUTOMATIC)
     self.timesw.add(self.timeview)
     timealgn = Gtk.Alignment.new(0, 0, 1, 1)
     timealgn.add(self.timesw)
     self.entrybox = Gtk.Entry()
     self.entrybox.set_max_length(40)
     self.offset = 0  #this is len(times) - len(ids)
     self.entrybox.connect('activate', self.record_time)
     self.entrybox.connect('changed', self.check_for_newtime)
     # And we will save our file
     self.timestr = re.sub(' +', '_', time.ctime()).replace(':', '')
     #we save with the current time in the filename so no chance of being overwritten accidentally
     # Now lets go on to boxes
     tophbox = Gtk.HBox()
     # our default t0, and the stuff on top for setting/edit t0
     self.t0 = 0.
     btn_t0 = Gtk.Button('Start!')
     btn_t0.connect('clicked', self.set_t0)
     # time display
     self.clocklabel = Gtk.Label()
     self.clocklabel.modify_font(Pango.FontDescription("sans 20"))
     self.clocklabel.set_markup(time_format(0))
     tophbox.pack_start(btn_t0, False, False, 10)
     tophbox.pack_start(self.clocklabel, False, False, 10)
     timevbox1 = Gtk.VBox(False, 8)
     timevbox1.pack_start(tophbox, False, False, 0)
     timevbox1.pack_start(timealgn, True, True, 0)
     timevbox1.pack_start(
         Gtk.Label('Select box below in order to mark times:'), False,
         False, 0)
     timevbox1.pack_start(self.entrybox, False, False, 0)
     # we will keep track of how many racers are still out.
     self.racers_reg = []
     for i_unused in range(self.numlaps):
         self.racers_reg.append(set([k for k in self.timing.keys()]))
     self.racers_total = len(self.racers_reg[0])
     self.racers_in = [0] * self.numlaps
     self.lapcounter = defaultdict(int)
     self.racerslabel = Gtk.Label()
     self.update_racers_label()
     timevbox1.pack_start(self.racerslabel, False, False, 0)
     vbox1align = Gtk.Alignment.new(0, 0, 1, 1)
     vbox1align.add(timevbox1)
     # buttons on the right side
     #First an options button that will actually be a menu
     options_menu = Gtk.Menu()
     menu_editreg = Gtk.MenuItem('Edit registration data')
     menu_editreg.connect_object("activate", self.edit_reg, None)
     menu_editreg.show()
     options_menu.append(menu_editreg)
     menu_resett0 = Gtk.MenuItem('Restart clock')
     menu_resett0.connect_object("activate", self.restart_t0, None)
     menu_resett0.show()
     options_menu.append(menu_resett0)
     menu_editt0 = Gtk.MenuItem('Edit starting time')
     menu_editt0.connect_object("activate", self.edit_t0, None)
     menu_editt0.show()
     options_menu.append(menu_editt0)
     menu_savecsv = Gtk.MenuItem('Save results to CSV')
     menu_savecsv.connect_object("activate", self.print_csv, pytimer)
     menu_savecsv.show()
     options_menu.append(menu_savecsv)
     menu_resume = Gtk.MenuItem('Load saved timing session')
     menu_resume.connect_object("activate", self.resume_times, None,
                                False)  #False is for not merging
     menu_resume.show()
     options_menu.append(menu_resume)
     menu_merge = Gtk.MenuItem('Merge in saved IDs or times')
     menu_merge.connect_object("activate", self.resume_times, None,
                               True)  #True is for merging
     menu_merge.show()
     options_menu.append(menu_merge)
     btnOPTIONS = Gtk.Button('Options')
     btnOPTIONS.connect_object("event", self.options_btn, options_menu)
     options_align = Gtk.Alignment.new(1, 0.1, 1, 0)
     options_align.add(btnOPTIONS)
     #Then the block of editing buttons
     btnDROPID = Gtk.Button('Drop ID')
     btnDROPID.connect('clicked', self.timing_rm_ID)
     btnDROPTIME = Gtk.Button('Drop time')
     btnDROPTIME.connect('clicked', self.timing_rm_time)
     btnEDIT = GtkStockButton('edit', "Edit")
     btnEDIT.connect('clicked', self.edit_time)
     edit_vbox = Gtk.VBox(True, 8)
     edit_vbox.pack_start(btnDROPID, False, False, 0)
     edit_vbox.pack_start(btnDROPTIME, False, False, 0)
     edit_vbox.pack_start(btnEDIT, False, False, 0)
     edit_align = Gtk.Alignment.new(1, 0, 1, 0)
     edit_align.add(edit_vbox)
     #Then the print and save buttons
     btnPRINT = Gtk.Button('Printouts')
     btnPRINT.connect('clicked', self.print_html, pytimer)
     btnSAVE = GtkStockButton('save', "Save")
     btnSAVE.connect('clicked', self.save_times)
     save_vbox = Gtk.VBox(True, 8)
     save_vbox.pack_start(btnPRINT, False, False, 0)
     save_vbox.pack_start(btnSAVE, False, False, 0)
     save_align = Gtk.Alignment.new(1, 1, 1, 0)
     save_align.add(save_vbox)
     #And finally the finish button
     btnOK = GtkStockButton('close', "Close")
     btnOK.connect('clicked', self.done_timing)
     done_align = Gtk.Alignment.new(1, 0.7, 1, 0)
     done_align.add(btnOK)
     vsubbox = Gtk.VBox(True, 0)
     vsubbox.pack_start(options_align, True, True, 0)
     vsubbox.pack_start(edit_align, True, True, 0)
     vsubbox.pack_start(save_align, True, True, 0)
     vsubbox.pack_start(done_align, True, True, 0)
     vspacer = Gtk.Alignment.new(1, 1, 0, 0)
     vspacer.add(vsubbox)
     timehbox = Gtk.HBox(False, 8)
     timehbox.pack_start(vbox1align, True, True, 0)
     timehbox.pack_start(vspacer, False, False, 0)
     self.add(timehbox)
     self.show_all()
示例#10
0
def get_sorted_results(projecttype, passid, numlaps, variablelaps, timing,
                       rawtimes, ranking_key, cols, col_fns):
    '''returns a sorted list of (id, result) items.
        The content of result depends on the race type'''
    # get raw times
    timeslist = zip(*get_sync_times_and_ids(rawtimes))
    #Handle blank times, and handicap correction
    # Handicap correction
    if projecttype == 'handicap':
        new_timeslist = []
        for tag, time in timeslist:
            if tag and time and tag != passid:
                try:
                    new_timeslist.append(
                        (tag, time_diff(time, timing[tag]['Handicap'])))
                except AttributeError:
                    # time or Handicap couldn't be converted to timedelta
                    new_timeslist.append((tag, '_'))
            # Else: drop entries with blank tag, blank time, or pass ID
        timeslist = list(new_timeslist) #replace
    else:
        #Drop times that are blank or have the passid
        timeslist = [(tag, time) for tag, time in timeslist
                     if tag and time and tag != passid]
    # Compute lap times, if a lap race
    if numlaps > 1:
        # multi laps - groups times by tag
        # Each value of laptimesdic is a list, sorted in order from
        # earliest time (1st lap) to latest time (last lap).
        timeslist_sorted = []
        for (tag, time) in timeslist:
            try:
                timeslist_sorted.append((tag, time_parse(time)))
            except AttributeError:
                pass
        timeslist_sorted = sorted(timeslist_sorted, key=lambda x: x[1])
        laptimesdic = defaultdict(list)
        for (tag, time) in timeslist_sorted:
            laptimesdic[tag].append(time_format(time.total_seconds()))
        # compute the lap times.
        lap_times = {}
        total_times = {}
        for tag in laptimesdic:
            # First put the total race time
            if len(laptimesdic[tag]) == numlaps or variablelaps:
                total_times[tag] = laptimesdic[tag][-1]
            else:
                total_times[tag] = '_'
            # And the first lap
            lap_times[tag] = ['1 - ' + laptimesdic[tag][0]]
            # And now the subsequent laps
            for ii in range(len(laptimesdic[tag])-1):
                try:
                    lap_times[tag].append(
                        '{} - {}'.format(
                            ii + 2,
                            time_diff(laptimesdic[tag][ii+1],
                                      laptimesdic[tag][ii])))
                except AttributeError:
                    lap_times[tag].append(str(ii+2) + ' - _')
        # Now correct timeslist to have the new total times
        timeslist = list(total_times.items())
    else:
        lap_times = defaultdict(int)
    # Compute each results row
    result_rows = []
    for tag, time in timeslist:
        row = get_result_row(tag, time, lap_times, timing, col_fns)
        result_rows.append((tag, row))
    # sort by column of ranking_key.
    rank_indx = cols.index(ranking_key)
    return sort_results(result_rows, rank_indx, cols)