def getCatCountImagesCategoryList(parent): il = wx.ImageList(16, 16) sm_rt = il.Add( wx.Bitmap(os.path.join(Utils.getImageFolder(), 'SmallRightArrow.png'), wx.BITMAP_TYPE_PNG)) sm_up = il.Add( wx.Bitmap(os.path.join(Utils.getImageFolder(), 'SmallUpArrow.png'), wx.BITMAP_TYPE_PNG)) sm_dn = il.Add( wx.Bitmap(os.path.join(Utils.getImageFolder(), 'SmallDownArrow.png'), wx.BITMAP_TYPE_PNG)) list = AutoWidthListCtrl(parent, style=wx.LC_REPORT | wx.BORDER_SUNKEN | wx.LC_HRULES) list.SetImageList(il, wx.IMAGE_LIST_SMALL) list.InsertColumn(0, _("Name")) list.InsertColumn(1, _("Gender")) list.InsertColumn(2, _("Type")) list.InsertColumn(3, _("Count"), wx.LIST_FORMAT_RIGHT) list.InsertColumn(4, '') catCount = {} race = Model.race if race: with UnstartedRaceWrapper(): SyncExcelLink(race) for c in race.getCategories(startWaveOnly=False, publishOnly=True): catCount[c] = race.catCount(c) if catCount[c] == 0: continue index = list.InsertStringItem(sys.maxint, c.name, sm_rt) list.SetStringItem(index, 1, getattr(c, 'gender', 'Open')) list.SetStringItem( index, 2, [_('Start Wave'), _('Component'), _('Custom')][c.catType]) list.SetStringItem(index, 3, u'{}'.format(catCount[c])) for col in xrange(4 + 1): list.SetColumnWidth(0, wx.LIST_AUTOSIZE) list.SetColumnWidth(1, 64) list.SetColumnWidth(3, 52) return catCount, il, list
def CrossResultsExport( fname ): race = Model.race if not race: return SyncExcelLink( race ) hasField = [False] * len(CrossResultsFields) hasField[0] = True hasField[1] = True # Check for what fields are actually filled in. publishCategories = race.getCategories( startWaveOnly = False, uploadOnly = True ) for cat in publishCategories: results = GetResults( cat ) if not results: continue for rr in results: for i, (field, cmgrField) in enumerate(CrossResultsFields): if getattr(rr, cmgrField, None): hasField[i] = True if not hasField[2]: return False, _('"LastName" must be linked to a column in the Excel sheetl') # Filter the fields by what exists in the data. crossResultsFields = [CrossResultsFields[i][0] for i in range(len(hasField)) if hasField[i]] year, month, day = race.date.split( '-' ) raceDate = datetime.date( year = int(year), month = int(month), day = int(day) ).strftime( '%m/%d/%Y' ) def toInt( n ): try: return int(n.split()[0]) except: return n maxLaps = 1 for cat in publishCategories: results = GetResults( cat ) if not results: continue maxLaps = max( maxLaps, max(rr.laps for rr in results) ) if maxLaps == 1 or maxLaps > 99: maxLaps = 0 lapHeaders = ['lap'] * maxLaps with io.open(fname, 'w', encoding='utf-8', newline='') as csvFile: csvWriter = csv.writer( csvFile, delimiter = ',', lineterminator = '\n' ) csvWriter.writerow( crossResultsFields + lapHeaders ) for cat in publishCategories: results = GetResults( cat ) if not results: continue csvWriter.writerow( [cat.fullname] ) for rr in results: try: finishTime = formatTimeGap(rr.lastTime - rr.raceTimes[0], forceHours=True) if rr.status == Model.Rider.Finisher else '' except Exception as e: finishTime = '' dataRow = [] for field in crossResultsFields: dataRow.append( { 'Place': lambda : 'DNP' if rr.pos in {'NP', 'OTL', 'PUL'} else toInt(rr.pos), 'Time': lambda : finishTime, 'Last Name': lambda : getattr(rr, 'LastName', ''), 'First Name': lambda : getattr(rr, 'FirstName', ''), 'Team': lambda : getattr(rr, 'Team', ''), 'License': lambda : getattr(rr, 'License', ''), }[field]() ) # Lap Times. for i in range(maxLaps): try: lapTime = formatTimeGap(rr.lapTimes[i]) except (AttributeError, IndexError) as e: lapTime = '' dataRow.append( lapTime ) csvWriter.writerow( [u'{}'.format(d) for d in dataRow] ) csvWriter.writerow( [] ) # Blank line separates each category. return True, _('Success')
def UCIExport(sheet, cat): race = Model.race if not race: return SyncExcelLink(race) sheetFit = FitSheetWrapper(sheet) titleStyle = xlwt.XFStyle() titleStyle.font.bold = True leftAlignStyle = xlwt.XFStyle() rightAlignStyle = xlwt.XFStyle() rightAlignStyle.alignment.horz = xlwt.Alignment.HORZ_RIGHT results = GetResults(cat) def toInt(n): try: return int(n.split()[0]) except: return n row = 0 for col, field in enumerate(UCIFields): sheetFit.write(row, col, field, titleStyle, bold=True) row += 1 for rr in results: try: finishTime = formatTimeGap( rr.lastTime - rr.raceTimes[0]) if rr.status == Model.Rider.Finisher else '' except Exception: finishTime = '' gap = getattr(rr, 'gap', '') if reHighPrecision.match(gap): gap = gap[:-4] + '"' for col, field in enumerate(UCIFields): { 'Pos': lambda: sheetFit.write(row, col, toInt(rr.pos), rightAlignStyle ), 'Nr.': lambda: sheetFit.write(row, col, rr.num, rightAlignStyle), 'Name': lambda: sheetFit.write(row, col, rr.full_name(), leftAlignStyle ), 'Team': lambda: sheetFit.write(row, col, getattr(rr, 'Team', ''), leftAlignStyle), 'UCI Code': lambda: sheetFit.write(row, col, getattr(rr, 'UCICode', ''), leftAlignStyle), 'Time': lambda: sheetFit.write(row, col, finishTime, rightAlignStyle), 'Gap': lambda: sheetFit.write(row, col, gap, rightAlignStyle), }[field]() row += 1
def WebScorerExport( fname ): race = Model.race if not race: return SyncExcelLink( race ) hasField = [False] * len(WebScorerFields) hasField[0] = True hasField[1] = True # Check for what fields are actually filled in. publishCategories = race.getCategories( startWaveOnly = False, uploadOnly = True ) for cat in publishCategories: results = GetResults( cat ) if not results: continue for rr in results: for i, (field, cmgrField) in enumerate(WebScorerFields): if getattr(rr, cmgrField, None): hasField[i] = True if not hasField[2]: return False, _('LastName must be linked to a column in the Excel sheet.') catDetails = dict( (cd['name'], cd) for cd in GetCategoryDetails() ) # Filter the fields by what exists in the data. webScorerFields = [WebScorerFields[i][0] for i in xrange(len(hasField)) if hasField[i]] webScorerToCrossMgr = dict( (ws, cm) for ws, cm in WebScorerFields ) hasDistance = False maxLaps = 0 for cat in publishCategories: results = GetResults( cat ) if not results: continue cd = catDetails[cat.fullname] if cd.get('raceDistance', None): hasDistance = True maxLaps = max( maxLaps, max(rr.laps for rr in results) ) if maxLaps == 1 or maxLaps > 99: maxLaps = 0 webScorerCategoryFields = [f for f in WebScorerCategoryFields if (hasDistance or f != 'Distance')] colNames = webScorerFields + webScorerCategoryFields + [u'{} {}'.format(_('Lap'), i+1) for i in xrange(maxLaps)] def toInt( n ): try: return int(n.split()[0]) except: return n with io.open(fname, 'w', encoding = 'utf-16') as txtFile: txtFile.write( u'{}\n'.format( u'\t'.join(unicode(c) for c in colNames) ) ) for cat in publishCategories: results = GetResults( cat ) if not results: continue cd = catDetails[cat.fullname] raceDistance = cd.get('raceDistance', '') raceDistanceType = cd.get('distanceUnit', '') if raceDistance: distance = u'{:.2f} {}'.format( raceDistance, raceDistanceType ) else: distance = u'' if cat.gender.startswith( 'M' ): gender = 'M' elif cat.gender.startswith( 'W' ): gender = 'F' else: gender = '' for rr in results: dataRow = [] try: finishTime = formatTimeGap(rr.lastTime - rr.raceTimes[0]) if rr.status == Model.Rider.Finisher else '' except Exception as e: finishTime = '' # Rider Fields. for field in webScorerFields: if field == 'Place': dataRow.append( 'DNP' if rr.pos in {'NP', 'OTL', 'PUL'} else toInt(rr.pos) ) elif field == 'Time': dataRow.append( finishTime ) else: dataRow.append( getattr(rr, webScorerToCrossMgr[field], '') ) # Category Fields. dataRow.append( cat.name ) if hasDistance: dataRow.append( distance ) dataRow.append( gender ) # Lap Times. for i in xrange(maxLaps): try: lapTime = formatTimeGap(rr.lapTimes[i]) except IndexError: lapTime = '' dataRow.append( lapTime ) txtFile.write( u'{}\n'.format( u'\t'.join(unicode(d).strip().replace(u'\t',' ') for d in dataRow) ) ) return True, _('Success')
six.print_( e ) Model.setRace( Model.Race() ) race = Model.getRace() race._populate() tStart = datetime.datetime.now() def timerUpdate(): tCur = datetime.datetime.now() situation.SetData( *GetSituationGaps(t=(tCur-tStart+tOffset).total_seconds()) ) wx.CallLater( 1001-tCur.microsecond/1000, timerUpdate ) wx.CallLater( 10, timerUpdate ) ''' try: fileName = os.path.join( 'Binghampton', '2014-04-27-Binghamton Circuit Race-r3-.cmn' ) with open(fileName, 'rb') as fp: race = pickle.load( fp ) Model.setRace( race ) ResetExcelLinkCache() race.resetAllCaches() SyncExcelLink( race ) except Exception as e: six.print_( e ) Model.setRace( Model.Race() ) race = Model.getRace() race._populate() mainWin.Show() situation.refresh() app.MainLoop()
def refresh(self): with Model.LockRace() as race: self.isEmpty = True if race is None: self.clearGrid() return SyncExcelLink(race) category = FixCategories( self.categoryChoice, getattr(race, 'recommendationsCategory', 0)) excelErrors = [] try: externalInfo = race.excelLink.read(True) excelErrors = race.excelLink.getErrors() except: externalInfo = {} excelErrors = [] def getName(num): info = externalInfo.get(num, {}) last = info.get('LastName', '') first = info.get('FirstName', '') if last and first: return u'{}, {}'.format(last, first) return last or first or ' ' colnames = [_('Num'), _('Name'), _('Issue'), _('Recommendation')] data = [[], [], [], []] def append(num=u'', name=u'', issue=u'', recommendation=u''): data[0].append(unicode(num)) data[1].append(unicode(name)) data[2].append(unicode(issue)) data[3].append(unicode(recommendation)) self.isEmpty = False # Check for Excel errors (very bad!). for num, errorStr in excelErrors: append(num, getName(num), 'Excel', u'{} {}'.format(_('Fix'), errorStr)) firstRiderInCategory = [] entries = race.interpolate() if entries: # Get riders who did extra laps. for catCur in race.getCategories(): if category and catCur != category: continue # Get the first recorded rider of the category. firstRiderTime = None for e in entries: if not race.inCategory(e.num, catCur): continue rider = race.riders[e.num] t = rider.getFirstKnownTime() if t is None: continue if firstRiderTime is None or t < firstRiderTime[0]: firstRiderTime = (t, rider, catCur) if firstRiderTime: firstRiderInCategory.append(firstRiderTime) results = GetResults(catCur) if not results: continue # Check for times recorded before the Category startOffset. for rr in results: rider = race.riders[rr.num] earlyTimeCount = rider.countEarlyTimes() if earlyTimeCount > 0: startOffsetStr = Utils.formatTime( race.getStartOffset(rider.num)) append( rider.num, getName(rider.num), _('EarlyTimes'), u'{} {} ({}={}, {}={} {}, {}="{}")'.format( _('Rider has entries before the Start Offset.' ), _('Early times are not shown in the results.' ), _('Count'), earlyTimeCount, _('StartOffset'), startOffsetStr, 'HH:MM:SS'[-len(startOffsetStr):], _('Category'), race.getCategory(rider.num).fullname, )) # Check for extra recorded laps. for rr in results: rider = race.riders[rr.num] if rider.status != rider.Finisher: break numRecordedTimes = len(rider.times) if numRecordedTimes > rr.laps: extra = numRecordedTimes - rr.laps append( rider.num, getName(rider.num), _('Laps'), u'{} ({}={})'.format( _("Rider has extra laps not shown in results (all riders finish on leader's last lap)" ), _('Count'), extra, ), ) # Report time penalties if getattr(race, 'isTimeTrial', False): for rr in results: rider = race.riders[rr.num] if rider.status == rider.Finisher and getattr( rr, 'ttPenalty', 0.0) > 0.0: reason = u' '.join([ u'{:.3f}'.format(getattr(rr, 'ttPenalty')), _('sec'), _("time penalty") ]) if getattr(rr, 'ttNote', u''): reason += u' - {}'.format( getattr(rr, 'ttNote')) append(rider.num, getName(rider.num), _('Penalty'), reason) # Trim out all entries not in this category and all non-finishers. if category: def match(num): return category.matches(num) else: def match(num): return True entries = [e for e in entries if match(e.num)] # Find the maximum recorded lap for each rider. riderMaxLapNonInterp, riderMaxLapInterp = {}, {} for e in entries: if e.interp: riderMaxLapInterp[e.num] = max( riderMaxLapInterp.get(e.num, 0), e.lap) else: riderMaxLapNonInterp[e.num] = max( riderMaxLapNonInterp.get(e.num, 0), e.lap) # Find the maximum recorded lap for each category. categoryMaxLapNonInterp, categoryMaxLapInterp = {}, {} for num, maxLap in riderMaxLapNonInterp.iteritems(): riderCat = race.getCategory(num) if riderCat: categoryMaxLapNonInterp[riderCat] = max( categoryMaxLapNonInterp.get(riderCat, 0), maxLap) for num, maxLap in riderMaxLapInterp.iteritems(): riderCat = race.getCategory(num) if riderCat: categoryMaxLapInterp[riderCat] = max( categoryMaxLapInterp.get(riderCat, 0), maxLap) # Check if all the riders in a particular category did not complete the maximum number of laps. raceLaps = race.getRaceLaps() for category, maxNonInterpLap in categoryMaxLapNonInterp.iteritems( ): maxCatLaps = (race.getNumLapsFromCategory(category) or raceLaps) try: if maxNonInterpLap < maxCatLaps and categoryMaxLapInterp[ category] > maxNonInterpLap: append( category.catStr, category.fullname, _('Laps'), u'{}: {} {}: {} {}'.format( _('Category'), category.fullname, _('Race Laps'), maxNonInterpLap, _('Verify that Category did Race Laps. Update Race Laps for Category if necessary.' ), )) except KeyError: pass # Collect all entries for every rider. riderEntries = {} for e in entries: riderEntries.setdefault(e.num, []).append(e) for num in sorted(r for r in race.getRiderNums() if match(r)): rider = race.riders[num] statusName = Model.Rider.statusNames[rider.status] if rider.status == Model.Rider.Finisher: # Check for unreported DNFs. try: riderEntriesCur = riderEntries[num] iLast = ( i for i in xrange(len(riderEntriesCur), 0, -1) if not riderEntriesCur[i - 1].interp).next() if iLast != len( riderEntriesCur) and race.isFinished(): append( num, getName(num), _('DNF'), u'{} {}.'.format( _('Check for DNF after rider lap'), iLast - 1)) except (KeyError, StopIteration): pass # Check for rider missing lap data relative to category. try: riderEntriesCur = riderEntries[num] leaderTimes = race.getCategoryTimesNums()[ race.getCategory(num)][0] appearedInLap = [False] * len(riderEntriesCur) appearedInLap[0] = True for e in riderEntriesCur: i = bisect.bisect_left(leaderTimes, e.t) if e.t < leaderTimes[i]: i -= 1 i = min( i, len(appearedInLap) - 1 ) # Handle if rider would have been lapped again on the last lap. appearedInLap[i] = True missingCount = sum(1 for b in appearedInLap if not b) if missingCount: append( num, getName(num), _('Lapped'), u'{} {}'.format( _("Confirm rider was lapped by Category Leader in leader's lap" ), (', '.join('{}'.format(i) for i, b in enumerate( appearedInLap) if not b)))) except (KeyError, IndexError, ValueError): pass elif rider.status == Model.Rider.DNS: # Check for DNS's with recorded times. if rider.times: append( num, getName(num), _('DNS'), u'{} {}: {}'.format( _('Rider has recorded times.'), _('Check status'), statusName)) elif rider.status in [Model.Rider.DNF, Model.Rider.Pulled]: if rider.tStatus == None: # Missing status times. append( num, getName(num), _('Time'), u'{} {}: {}'.format( _('Check if time is accurate.'), _('Status'), statusName)) else: # Recorded time exceeds status time. if rider.times and rider.times[-1] > rider.tStatus: append( num, getName(num), _('Time'), u'{}: {}. {}: {} > {}'.format( _('Check time for Status'), statusName, _('Found recorded time'), Utils.SecondsToStr(rider.times[-1]), Utils.SecondsToStr(rider.tStatus), )) # Check for bad numbers. category = race.getCategory(num) if not category: append( num, getName(num), _('Category'), _('Rider does not match any active category. Check if rider is in right race or data entry error.' )) # Show numbers with projected time. if race.isFinished(): projectedNums = [] for r in GetResults(category): pSum = sum(1 for i in r.interp if i) if pSum > 0: projectedNums.append((r.num, pSum)) projectedNums.sort() for num, count in projectedNums: append( num, getName(num), _('Projected'), u'{} ({})'.format( _('Check rider has projected times'), count)) # Show missing tag reads. missingTags = [ '{}'.format(m) for m in getattr(race, 'missingTags', set()) ] missingTags.sort() for m in missingTags: append( m, '', _('Tag'), _('Imported chip Tag missing from Excel sheet. Check if a stray read. If not, add Tag to Excel sheet and run Chip Import again' )) # Add information about the rider categories. firstRiderInCategory.sort(key=lambda f: (f[0], f[2].name)) for t, rider, catCur in firstRiderInCategory: append( rider.num, getName(rider.num), _('Start'), u'{}: {} {}: {}'.format( _('Category'), catCur.name, _('First recorded time'), Utils.formatTime(t, True), )) # end if entries self.grid.Set(data=data, colnames=colnames) self.grid.AutoSizeColumns(True) self.grid.Reset() self.updateColours() # Fix the grid's scrollbars. self.grid.SortByColumn(self.sortCol, self.sortDescending) self.grid.FitInside()
def JPResultsExport(workbook, sheet): race = Model.race if not race: return SyncExcelLink(race) sheetFit = FitSheetWrapperXLSX(sheet) titleStyle = workbook.add_format({'bold': True}) leftAlignStyle = workbook.add_format() rightAlignStyle = workbook.add_format({'align': 'right'}) timeStyle = workbook.add_format({ 'num_format': 'hh:mm:ss', 'align': 'right' }) Finisher = Model.Rider.Finisher row = 0 for cat in race.getCategories(startWaveOnly=False, uploadOnly=True): results = GetResults(cat) if not results: continue gender = None for rr in results: if row == 0: for col, field in enumerate(JPResultFields): sheetFit.write(row, col, field, titleStyle, bold=True) row += 1 try: finishTime = ( rr.lastTime - rr.raceTimes[0]) if rr.status == Finisher else None if race.roadRaceFinishTimes: finishTime = floor(finishTime)[ 0] # Truncate decimal seconds. finishTime /= (24.0 * 60.0 * 60.0 ) # Convert to fraction of a day. except Exception as e: finishTime = None valueStyle = { 'body_number': (rr.num, rightAlignStyle), 'racer_code': (getattr(rr, 'License', None), leftAlignStyle), 'family_name': (getattr(rr, 'LastName', None), leftAlignStyle), 'first_name': (getattr(rr, 'FirstName', None), leftAlignStyle), 'team': (getattr(rr, 'Team', None), leftAlignStyle), 'uci_id': (formatUciId(getattr(rr, 'UCIID', None)) if hasattr( rr, 'UCIID') else None, leftAlignStyle), 'gender': (gender, leftAlignStyle), 'birth_date': (getattr(rr, 'DateOfBirth', None), leftAlignStyle), 'entry_status': (getattr(rr, 'EntryStatus', None), leftAlignStyle), 'rank': (rr.pos if rr.status == Finisher else None, rightAlignStyle), 'result_status': (getStatusName(rr.status), leftAlignStyle), 'lap': (rr.laps if rr.status == Finisher else None, rightAlignStyle), 'goal_time': (finishTime if rr.status == Finisher else None, timeStyle), 'category_code': (cat.name, leftAlignStyle), } for col, field in enumerate(JPResultFields): value, style = valueStyle[field] if value is not None: sheetFit.write(row, col, value, style) row += 1
def VTTAExport( workbook, sheet ): race = Model.race if not race: return SyncExcelLink( race ) raceDiscipline = getattr( race, 'discipline', 'Cyclo-cross' ) if 'cyclo' in raceDiscipline.lower(): raceDiscipline = 'Cyclo-cross' elif 'road' in raceDiscipline.lower(): raceDiscipline = 'Road Race' if race.isTimeTrial: raceDiscipline = 'Time Trial' sheetFit = FitSheetWrapper( sheet ) titleStyle = workbook.add_format({'bold': True}) leftAlignStyle = workbook.add_format() rightAlignStyle = workbook.add_format({'align': 'right'}) catDetails = dict( (cd['name'], cd) for cd in GetCategoryDetails() ) hasDistance = None maxLaps = 0 publishCategories = race.getCategories( startWaveOnly = False, uploadOnly = True ) for cat in publishCategories: results = GetResults( cat ) if not results: continue cd = catDetails[cat.fullname] if cd.get('raceDistance', None): hasDistance = True maxLaps = max( maxLaps, max(rr.laps for rr in results) ) if maxLaps == 1 or maxLaps > 99: maxLaps = 0 lapTimeStartCol = (2 if hasDistance else 0) + lenVTTAFields year, month, day = race.date.split( '-' ) raceDate = datetime.date( year = int(year), month = int(month), day = int(day) ).strftime( '%m/%d/%Y' ) row = 0 for cat in publishCategories: results = GetResults( cat ) if not results: continue raceGender = getattr(cat, 'gender', 'Open') if raceGender == 'Open': raceGender = 'All' cd = catDetails[cat.fullname] raceDistance = cd.get('raceDistance', '') raceDistanceType = cd.get('distanceUnit', '') for rr in results: if row == 0: for col, field in enumerate(VTTAFields): sheetFit.write( row, col, field, titleStyle, bold=True ) if hasDistance: sheetFit.write( row, lenVTTAFields , 'Race Distance', titleStyle, bold=True ) sheetFit.write( row, lenVTTAFields+1, 'Race Distance Type', titleStyle, bold=True ) for i in xrange(maxLaps): sheetFit.write( row, lapTimeStartCol + i, 'Rider Lap {}'.format(i + 1), titleStyle, bold=True ) row += 1 try: finishTime = formatTimeGap(rr.lastTime - rr.raceTimes[0]) if rr.status == Model.Rider.Finisher else '' except Exception as e: finishTime = '' for col, field in enumerate(VTTAFields): { 'Race Date': lambda : sheet.write( row, col, raceDate, rightAlignStyle ), 'Race Gender': lambda : sheetFit.write( row, col, raceGender, leftAlignStyle ), 'Race Discipline': lambda : sheetFit.write( row, col, raceDiscipline, leftAlignStyle ), 'Race Category': lambda : sheetFit.write( row, col, cat.name, leftAlignStyle ), 'Rider Bib #': lambda : sheetFit.write( row, col, rr.num, rightAlignStyle ), 'Rider Last Name': lambda : sheetFit.write( row, col, getattr(rr, 'LastName', ''), leftAlignStyle ), 'Rider First Name': lambda : sheetFit.write( row, col, getattr(rr, 'FirstName', ''), leftAlignStyle ), 'Rider Age': lambda : sheetFit.write( row, col, getattr(rr, 'Age', ''), rightAlignStyle ), 'Rider City': lambda : sheetFit.write( row, col, getattr(rr, 'City', ''), leftAlignStyle ), 'Rider StateProv': lambda : sheetFit.write( row, col, getattr(rr, 'StateProv', '') or getattr(rr, 'Prov', '') or getattr(rr, 'State', ''), leftAlignStyle ), 'Rider Nat.': lambda : sheetFit.write( row, col, getattr(rr, 'Nat.', ''), leftAlignStyle ), 'Rider Team': lambda : sheetFit.write( row, col, getattr(rr, 'Team', ''), leftAlignStyle ), 'Rider License #': lambda : sheetFit.write( row, col, getattr(rr, 'License', ''), leftAlignStyle ), 'Rider UCICode': lambda : sheetFit.write( row, col, getattr(rr, 'UCICode', ''), leftAlignStyle ), 'Rider Place': lambda : sheetFit.write( row, col, 'DNP' if rr.pos in {'NP', 'OTL', 'PUL'} else toInt(rr.pos), rightAlignStyle ), 'Rider Time': lambda : sheetFit.write( row, col, finishTime, rightAlignStyle ), }[field]() if hasDistance: sheetFit.write( row, lenVTTAFields , raceDistance, rightAlignStyle ) sheetFit.write( row, lenVTTAFields+1, raceDistanceType, rightAlignStyle ) if maxLaps: for i, lapTime in enumerate(rr.lapTimes): sheetFit.write( row, lapTimeStartCol + i, formatTimeGap(lapTime), rightAlignStyle ) row += 1