def doNumSelect(self, event): grid = event.GetEventObject() self.iLap = None if self.isEmpty: return row, col = event.GetRow(), event.GetCol() self.iRow, self.iCol = row, col if row >= self.labelGrid.GetNumberRows(): return if grid == self.lapGrid and self.lapGrid.GetCellValue(row, col): try: colName = self.lapGrid.GetColLabelValue(col) self.iLap = int(reLapMatch.match(colName).group(1)) except: pass value = self.labelGrid.GetCellValue(row, 1) numSelect = value if value else None if self.numSelect != numSelect: self.numSelect = numSelect self.showNumSelect() mainWin = Utils.getMainWin() if mainWin: historyCategoryChoice = mainWin.history.categoryChoice historyCat = FixCategories(historyCategoryChoice) if historyCat is not None: cat = FixCategories(self.categoryChoice) if historyCat != cat: Model.setCategoryChoice(self.categoryChoice.GetSelection(), 'resultsCategory') SetCategory(historyCategoryChoice, cat) mainWin.setNumSelect(numSelect)
def refresh( self ): with Model.LockRace() as race: enabled = (race is None) or not race.isRunning() for w in self.controls: w.Enable( enabled ) if race is None: self.setAnimationType( None ) self.animation.SetData( None, 0 ) self.animation.SetOptions() self.animation.StopAnimate() return self.showOval.SetValue( getattr(race, 'showOval', True) ) self.showGPX.SetValue( not getattr(race, 'showOval', True) ) self.animationGeo.SetGeoTrack( getattr(race, 'geoTrack', None ) ) self.setAnimationType( getattr(race, 'geoTrack', None) ) category = FixCategories( self.categoryChoice, getattr(race, 'raceAnimationCategory', 0) ) self.hbs.Layout() raceTime = race.lastRaceTime() if race.isRunning() else self.animation.t raceIsRunning = race.isRunning() self.finishTop.SetValue( getattr(race, 'finishTop', False) ) animationData = GetAnimationData( category, True ) self.animation.SetData( animationData, raceTime, dict( (cd['name'], cd) for cd in GetCategoryDetails() ) ) self.animation.SetOptions( getattr(race, 'reverseDirection', False), getattr(race, 'finishTop', False) ) if raceIsRunning: if not self.animation.IsAnimating(): self.animation.StartAnimateRealtime()
def refresh(self): race = Model.race if not race: self.histogram.SetData([], [], []) return self.category = FixCategories(self.categoryChoice, getattr(race, 'histogramCategory', 0)) if self.category is None: categories = race.getCategories(startWaveOnly=True) if not categories: return getCatName = GetCategoryName(categories) elif self.category.catType == Model.Category.CatWave: components = race.getComponentCategories(self.category) if components: getCatName = GetCategoryName(components) else: def getCatName(bib, name=self.category.fullname): return name else: def getCatName(bib, name=self.category.fullname): return name results = GetResults(self.category) Finisher = Model.Rider.Finisher data, label, category = [], [], [] maxLaps = None for rr in results: if rr.status != Finisher or not rr.raceTimes or len( rr.raceTimes) < 2: continue if maxLaps is None: if race.isRunning(): maxLaps = bisect.bisect_left(rr.raceTimes, race.curRaceTime(), 0, len(rr.raceTimes) - 1) else: maxLaps = len(rr.raceTimes) - 1 if self.lapOption.GetCount() != maxLaps + 1: self.lapOption.SetItems([_('Last')] + [ six.text_type(i) for i in six.moves.range(1, maxLaps + 1) ]) if self.lap >= self.lapOption.GetCount(): self.lap = 0 self.lapOption.SetSelection(self.lap) if self.lap: maxLaps = self.lap if len(rr.raceTimes) - 1 < maxLaps: continue data.append(rr.raceTimes[self.lap or -1]) label.append(u'{}: {}'.format(rr.num, rr.short_name())) category.append(getCatName(rr.num)) self.histogram.SetData(data, label, category, self.binOption.GetSelection()) self.fixBinWidth()
def refresh( self ): race = Model.race if not race: return category = FixCategories( self.topPanel.categoryChoice, getattr(race, 'situationCategory', 0) ) self.topPanel.situation.SetData( *GetSituationGaps(category=category, t=None) ) if race and race.isRunning(): if self.refreshTimer: self.refreshTimer.Stop() self.timerRefresh( False )
def getCategory(self): race = Model.race if not race: category = None else: category = race.getCategoryStartWave( FixCategories(self.categoryChoice, getattr(race, 'resultsCategory', 0))) categoryName = category.fullname if category else u'' if categoryName != self.showingCategory.GetLabel(): self.showingCategory.SetLabel(categoryName) self.hbs.Layout() return category
def timeScroll( self, event=None ): if self.timeAtMax(): self.timerUpdate() return race = Model.race if not race: return category = FixCategories( self.topPanel.categoryChoice, getattr(race, 'situationCategory', 0) ) tMax = GetRaceTMax(category) or 0.01 t = tMax * (float(self.topPanel.timeSlider.GetValue()) / float(self.topPanel.timeSlider.GetMax())) self.topPanel.situation.SetData( *GetSituationGaps(category=category, t=t) )
def timerUpdate( self ): if not self.timeAtMax(): return mainWin = Utils.getMainWin() if mainWin and not mainWin.isShowingPage(self): return race = Model.race if not race: return category = FixCategories( self.topPanel.categoryChoice, getattr(race, 'situationCategory', 0) ) self.topPanel.situation.SetData( *GetSituationGaps(category=category, t=None) ) if race and race.isRunning(): wx.CallLater( max(1,1001-datetime.datetime.now().microsecond//1000), self.timerUpdate )
def swapEntries(self, num, numAdjacent): if not num or not numAdjacent: return with Model.LockRace() as race: if (not race or num not in race or numAdjacent not in race.riders): return e1 = race.getRider(num).interpolate() e2 = race.getRider(numAdjacent).interpolate() category = FixCategories(self.categoryChoice, getattr(race, 'ganttCategory', 0)) riderResults = dict((r.num, r) for r in GetResults(category)) try: laps = riderResults[num].laps undo.pushState() with Model.LockRace() as race: EditEntry.SwapEntry(e1[laps], e2[laps]) wx.CallAfter(self.refresh) except KeyError: pass
def swapEntries(self, num, numAdjacent): if not num or not numAdjacent: return with Model.LockRace() as race: if (not race or num not in race.riders or numAdjacent not in race): return e1 = race.getRider(num).interpolate() e2 = race.getRider(numAdjacent).interpolate() category = FixCategories(self.categoryChoice, getattr(race, 'resultsCategory', 0)) riderResults = dict((r.num, r) for r in GetResults(category)) try: rr1, rr2 = riderResults[num], riderResults[numAdjacent] laps = rr1.laps undo.pushState() ee1 = next(e for e in e1 if e.t == rr1.raceTimes[laps]) ee2 = next(e for e in e2 if e.t == rr2.raceTimes[laps]) with Model.LockRace() as race: SwapEntry(ee1, ee2) wx.CallAfter(self.refresh) except (KeyError, StopIteration): pass
def updateGrid( self ): race = Model.race if not race: self.grid.ClearGrid() return category = FixCategories( self.categoryChoice, getattr(race, 'resultsCategory', 0) ) self.hbs.RecalcSizes() self.hbs.Layout() for si in self.hbs.GetChildren(): if si.IsWindow(): si.GetWindow().Refresh() self.category = category colnames = self.getColNames() results = GetTeamResults( self.category ) Utils.AdjustGridSize( self.grid, len(results), len(colnames) ) for col, colName in enumerate(colnames): self.grid.SetColLabelValue( col, colName ) attr = wx.grid.GridCellAttr() attr.SetReadOnly( True ) if col != 1: attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP ) self.grid.SetColAttr( col, attr ) for row, r in enumerate(results): self.grid.SetCellValue( row, 0, u'{}'.format(row+1) ) self.grid.SetCellValue( row, 1, r.team ) self.grid.SetCellValue( row, 2, r.criteria ) self.grid.SetCellValue( row, 3, r.gap ) if race.showNumTeamParticipants: self.grid.SetCellValue( row, 4, u'{}'.format(r.numParticipants) ) self.grid.AutoSizeColumns( False ) self.grid.AutoSizeRows( False )
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 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[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 = GetResultsCore( 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 = (category.getNumLaps() 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[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): 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 GetResultsCore( 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 refresh(self): race = Model.race if not race: self.ganttChart.SetData(None) self.updateStats(None) return category = FixCategories(self.categoryChoice, getattr(race, 'ganttCategory', 0)) Finisher = Model.Rider.Finisher statusNames = Model.Rider.statusNames translate = Utils.translate self.groupByStartWave.SetValue(race.groupByStartWave) self.groupByStartWave.Enable(not category) self.showFullNamesInChart.SetValue(race.showFullNamesInChart) if race and race.isRunning(): if self.refreshTimer: self.refreshTimer.Stop() self.timerRefresh(False) headerSet = set() if race.groupByStartWave and not category: results = [] for c in sorted(race.getCategories(), key=lambda x: x.getStartOffsetSecs()): catResults = GetResults(c) if not catResults: continue # Create a name for the category as a bogus rider. rr = RiderResult(num='', status=Finisher, lastTime=None, raceCat=c, lapTimes=[], raceTimes=[], interp=[]) rr.FirstName = c.fullname headerSet.add(rr.FirstName) results.append(rr) results.extend(list(catResults)) else: results = GetResults(category) if race.showFullNamesInChart: def getLabel(r): return u'{} {} {}'.format(getattr(r, 'FirstName', u''), getattr(r, 'LastName', u''), r.num or u'').strip() else: def getLabel(r): return u'{} {}'.format(r.short_name(12), r.num or u'').strip() resultBest = (0, sys.float_info.max) labels, status = [], [] for r in results: labels.append(getLabel(r)) try: riderStatus = race.riders[r.num].status status.append( translate(statusNames[riderStatus] ) if riderStatus != Finisher else '') if riderStatus == Finisher: resultBest = min(resultBest, (-r.laps, r.raceTimes[-1])) except (IndexError, KeyError) as e: status.append('') data = [r.raceTimes for r in results] interp = [r.interp for r in results] try: nowTime = min(resultBest[1], Model.race.lastRaceTime()) except: nowTime = None self.ganttChart.SetData( data, labels, nowTime, interp, set(i for i, r in enumerate(results) if r.status != Finisher), Model.race.numTimeInfo, getattr(Model.race, 'lapNote', None), headerSet=headerSet, status=status, ) self.updateStats(results)
def doRightClick(self, event): wx.CallAfter(self.search.SetFocus) self.doNumSelect(event) if self.numSelect is None: return allCases = 0 interpCase = 1 nonInterpCase = 2 if not hasattr(self, 'popupInfo'): self.popupInfo = [ (_('Passings'), _('Switch to Passings tab'), self.OnPopupHistory, allCases), (_('RiderDetail'), _('Show RiderDetail Dialog'), self.OnPopupRiderDetail, allCases), (None, None, None, None), (_('Show Photos'), _('Show Photos'), self.OnPopupShowPhotos, allCases), (None, None, None, None), (_('Correct...'), _('Change number or lap time...'), self.OnPopupCorrect, interpCase), (_('Shift...'), _('Move lap time earlier/later...'), self.OnPopupShift, interpCase), (_('Delete...'), _('Delete lap time...'), self.OnPopupDelete, nonInterpCase), (None, None, None, None), (_('Swap with Rider before'), _('Swap with Rider before'), self.OnPopupSwapBefore, allCases), (_('Swap with Rider after'), _('Swap with Rider after'), self.OnPopupSwapAfter, allCases), ] self.menuOptions = {} for numBefore in [False, True]: for numAfter in [False, True]: for caseCode in range(3): menu = wx.Menu() for name, text, callback, cCase in self.popupInfo: if not name: Utils.addMissingSeparator(menu) continue if caseCode < cCase: continue if (name.endswith(_('before')) and not numBefore) or (name.endswith( _('after')) and not numAfter): continue item = menu.Append(wx.ID_ANY, name, text) self.Bind(wx.EVT_MENU, callback, item) Utils.deleteTrailingSeparators(menu) self.menuOptions[(numBefore, numAfter, caseCode)] = menu num = int(self.numSelect) with Model.LockRace() as race: if not race or num not in race.riders: return category = FixCategories(self.categoryChoice, getattr(race, 'resultsCategory', 0)) riderResults = dict((r.num, r) for r in GetResults(category)) entries = race.riders[num].interpolate() try: laps = riderResults[num].laps self.entry = next(e for e in entries if e.t == riderResults[num].raceTimes[laps]) caseCode = 1 if self.entry.interp else 2 except (TypeError, IndexError, KeyError): caseCode = 0 except StopIteration: return self.numBefore, self.numAfter = None, None for iRow, attr in [(self.iRow - 1, 'numBefore'), (self.iRow + 1, 'numAfter')]: if not (0 <= iRow < self.lapGrid.GetNumberRows()): continue numAdjacent = int(self.labelGrid.GetCellValue(iRow, 1)) if RidersCanSwap(riderResults, num, numAdjacent): setattr(self, attr, numAdjacent) menu = self.menuOptions[(self.numBefore is not None, self.numAfter is not None, caseCode)] try: self.PopupMenu(menu) except Exception as e: Utils.writeLog('Results:doRightClick: {}'.format(e))
def setCategoryAll(self): FixCategories(self.categoryChoice, 0) Model.setCategoryChoice(0, 'historyCategory')
def refresh(self): self.category = None self.isEmpty = True self.iLastLap = 0 self.rcInterp = set( ) # Set of row/col coordinates of interpolated numbers. self.rcNumTime = set() self.search.SelectAll() CloseFinishTime = 0.07 self.closeFinishBibs = defaultdict(list) race = Model.race if not race: self.clearGrid() return category = FixCategories(self.categoryChoice, getattr(race, 'resultsCategory', 0)) self.hbs.RecalcSizes() self.hbs.Layout() for si in self.hbs.GetChildren(): if si.IsWindow(): si.GetWindow().Refresh() self.category = category sortLap = getattr(race, 'sortLap', None) sortLabel = getattr(race, 'sortLabel', None) if race.isTimeTrial: def getSortTime(rr): try: return rr.firstTime + rr._lastTimeOrig except: return 0 else: def getSortTime(rr): try: return rr._lastTimeOrig except: return 0 results = sorted((rr for rr in GetResults(category) if rr.status == Model.Rider.Finisher and rr.lapTimes and getSortTime(rr) > 0), key=getSortTime) for i in range(1, len(results)): if results[i]._lastTimeOrig - results[ i - 1]._lastTimeOrig <= CloseFinishTime: self.closeFinishBibs[results[i - 1].num].append(results[i].num) self.closeFinishBibs[results[i].num].append(results[i - 1].num) labelLastX, labelLastY = self.labelGrid.GetViewStart() lapLastX, lapLastY = self.lapGrid.GetViewStart() exportGrid = ExportGrid() exportGrid.setResultsOneList(category, self.showRiderData, showLapsFrequency=1) if not exportGrid.colnames: self.clearGrid() return # Fix the speed column. speedUnit = None iSpeedCol = None try: iSpeedCol = next(i for i, c in enumerate(exportGrid.colnames) if c == _('Speed')) except StopIteration: pass if iSpeedCol is not None: for r, d in enumerate(exportGrid.data[iSpeedCol]): d = d.strip() if not d: continue dSplit = d.split() if not speedUnit and len(dSplit) > 1: exportGrid.colnames[iSpeedCol] = speedUnit = dSplit[1] exportGrid.data[iSpeedCol][r] = dSplit[0] if exportGrid.data[iSpeedCol][r] == '"': exportGrid.data[iSpeedCol][r] += ' ' colnames = exportGrid.colnames data = exportGrid.data sortCol = None if sortLap: race.sortLabel = sortLabel = None for i, name in enumerate(colnames): if name.startswith(_('Lap')) and int( name.split()[1]) == sortLap: sortCol = i break elif sortLabel: race.sortLap = sortLap = None if sortLabel not in { _('Pos'), _('Gap'), _('Time'), _('mph'), _('km/h') }: for i, name in enumerate(colnames): if name == sortLabel: sortCol = i break if sortCol is None: race.sortLabel = race.sortLap = sortLabel = sortLap = None results = GetResults(category) hasSpeeds = False for result in results: if getattr(result, 'lapSpeeds', None) or getattr( result, 'raceSpeeds', None): hasSpeeds = True break if not hasSpeeds: self.showLapSpeedsRadio.Enable(False) self.showRaceSpeedsRadio.Enable(False) if self.selectDisplay > Results.DisplayRaceTimes: self.selectDisplay = Results.DisplayRaceTimes self.showRaceTimesRadio.SetValue(True) else: self.showLapSpeedsRadio.Enable(True) self.showRaceSpeedsRadio.Enable(True) for r in [ self.showLapTimesRadio, self.showRaceTimesRadio, self.showLapSpeedsRadio, self.showRaceSpeedsRadio ]: if r.GetValue(): r.SetFont(self.boldFont) else: r.SetFont(wx.NullFont) self.hbs.RecalcSizes() self.hbs.Layout() # Find the fastest lap time. self.fastestLapRC, fastestLapSpeed, fastestLapTime = None, 0.0, sys.float_info.max for r, result in enumerate(results): if getattr(result, 'lapSpeeds', None): # Use speeds if available. for c, s in enumerate(result.lapSpeeds): if s > fastestLapSpeed: fastestLapSpeed = s self.fastestLapRC = (r, c) elif result.lapTimes: # Else, use times. for c, t in enumerate(result.lapTimes): if t < fastestLapTime: fastestLapTime = t self.fastestLapRC = (r, c) highPrecision = Model.highPrecisionTimes() try: firstLapCol = next(i for i, name in enumerate(colnames) if name.startswith(_('Lap'))) except StopIteration: firstLapCol = len(colnames) # Convert to race times, lap speeds or race speeds as required. ''' DisplayLapTimes = 0 DisplayRaceTimes = 1 DisplayLapSpeeds = 2 DisplayRaceSpeeds = 3 ''' if self.selectDisplay == Results.DisplayRaceTimes: for r, result in enumerate(results): for i, t in enumerate(result.raceTimes[1:]): try: data[i + firstLapCol][r] = Utils.formatTimeCompressed( t, highPrecision) except IndexError: pass elif self.selectDisplay == Results.DisplayLapSpeeds: for r, result in enumerate(results): if getattr(result, 'lapSpeeds', None): for i, s in enumerate(result.lapSpeeds): try: data[i + firstLapCol][r] = u'{:.2f}'.format(s) except IndexError: pass elif self.selectDisplay == Results.DisplayRaceSpeeds: for r, result in enumerate(results): if getattr(result, 'raceSpeeds', None): for i, s in enumerate(result.raceSpeeds): try: data[i + firstLapCol][r] = u'{:.2f}'.format(s) except IndexError: pass # Sort by the given lap, if there is one. # Also, add a position for the lap itself. if sortCol is not None: maxVal = 1000.0 * 24.0 * 60.0 * 60.0 if sortLap: if self.selectDisplay in [ Results.DisplayLapTimes, Results.DisplayRaceTimes ]: getFunc = Utils.StrToSeconds else: getFunc = lambda x: -float(x) else: if colnames[sortCol] in [_('Start'), _('Finish'), _('Time')]: getFunc = Utils.StrToSeconds elif colnames[sortCol] in [_('mph'), _('km')]: getFunc = lambda x: -float(x) if x else 0.0 elif colnames[sortCol] == _('Factor'): getFunc = lambda x: float(x) if x else maxVal elif colnames[sortCol] in [_('Pos'), _('Bib')]: getFunc = lambda x: int(x) if x and six.text_type( x).isdigit() else maxVal else: getFunc = lambda x: u'{}'.format(x) maxVal = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' sortPairs = [] for r, result in enumerate(results): try: k = (getFunc(data[sortCol][r]), r) except Exception as e: k = (maxVal, r) sortPairs.append((k, r)) sortPairs.sort() for c in range(len(data)): col = data[c] data[c] = [ col[i] if i < len(col) else u'' for k, i in sortPairs ] if colnames[sortCol] != _('Bib'): for r in range(len(data[sortCol])): if data[sortCol][r]: data[sortCol][r] = u'{} [{}: {}]'.format( data[sortCol][r], r + 1, data[1][r]) # Highlight the sorted column. if sortLap: colnames = [] for name in exportGrid.colnames: try: if int(name.split()[1]) == sortLap: name = u'<{}>\n{}'.format(name, [ _('by Lap Time'), _('by Race Time'), _('by Lap Speed'), _('by Race Speed') ][self.selectDisplay]) except: pass colnames.append(name) elif sortLabel: colnames = [] for name in exportGrid.colnames: if name == sortLabel: name = u'<{}>'.format(name) colnames.append(name) else: colnames = exportGrid.colnames try: iLabelMax = next( i for i, name in enumerate(colnames) if name.startswith(_('Lap')) or name.startswith('<' + _('Lap'))) except StopIteration: iLabelMax = len(colnames) colnamesLabels = colnames[:iLabelMax] dataLabels = data[:iLabelMax] colnameLaps = colnames[iLabelMax:] dataLaps = data[iLabelMax:] self.labelGrid.Set(data=dataLabels, colnames=colnamesLabels) self.labelGrid.SetLeftAlignCols(exportGrid.leftJustifyCols) self.labelGrid.AutoSizeColumns(True) self.labelGrid.Reset() try: iUCICodeCol = colnamesLabels.index(_('UCICode')) self.labelGrid.SetColRenderer(iUCICodeCol, IOCCodeRenderer()) except ValueError: pass try: iNatCodeCol = colnamesLabels.index(_('NatCode')) self.labelGrid.SetColRenderer(iNatCodeCol, IOCCodeRenderer()) except ValueError: pass self.lapGrid.Set(data=dataLaps, colnames=colnameLaps) self.lapGrid.Reset() self.lapGrid.AutoSizeColumns(self.lapGrid.GetNumberCols() < 100) self.isEmpty = False # Find interpolated entries. with Model.LockRace() as race: numTimeInfo = race.numTimeInfo riders = race.riders for r in range(self.lapGrid.GetNumberRows()): try: rider = riders[int(self.labelGrid.GetCellValue(r, 1))] except: continue try: entries = rider.interpolate() except (ValueError, IndexError): continue if not entries: continue for c in range(self.lapGrid.GetNumberCols()): if not self.lapGrid.GetCellValue(r, c): break try: if entries[c + 1].interp: self.rcInterp.add((r, c)) elif numTimeInfo.getInfo(entries[c + 1].num, entries[c + 1].t) is not None: self.rcNumTime.add((r, c)) elif c > self.iLastLap: self.iLastLap = c except IndexError: pass self.labelGrid.Scroll(labelLastX, labelLastY) self.lapGrid.Scroll(lapLastX, lapLastY) self.showNumSelect() if self.firstDraw: self.firstDraw = False self.splitter.SetSashPosition(400) # Fix the grids' scrollbars. self.labelGrid.FitInside() self.lapGrid.FitInside()
def setCategoryAll(self): FixCategories(self.categoryChoice, 0) Model.setCategoryChoice(0, 'resultsCategory')
def refresh( self ): self.category = None self.clearGrid() potentialDNS = {} with Model.LockRace() as race: if not race: return self.category = FixCategories( self.categoryChoice, getattr(race, 'DNSManagerCategory', 0) ) self.hbs.RecalcSizes() self.hbs.Layout() for si in self.hbs.GetChildren(): if si.IsWindow(): si.GetWindow().Refresh() try: externalFields = race.excelLink.getFields() externalInfo = race.excelLink.read() except: self.clearGrid() return for num, info in externalInfo.iteritems(): if num <= 0 or (self.category and not race.inCategory(num, self.category)): continue rider = race.riders.get( num, None ) if not rider: potentialDNS[num] = info else: # Also add riders marked as Finishers that have no times. if rider.status == rider.Finisher and not rider.times: potentialDNS[num] = info if not potentialDNS: return # Add the headers. GetTranslation = _ for c, f in enumerate(externalFields): self.list.InsertColumn( c+1, GetTranslation(f), wx.LIST_FORMAT_RIGHT if f.startswith('Bib') else wx.LIST_FORMAT_LEFT ) # Create the data. Sort by Bib# data = [tuple( num if i == 0 else info.get(f, '') for i, f in enumerate(externalFields)) for num, info in potentialDNS.iteritems()] data.sort() # Populate the list. for row, d in enumerate(data): index = self.list.InsertItem(sys.maxint, u'{}'.format(d[0]), self.sm_rt) for i, v in enumerate(itertools.islice(d, 1, len(d))): self.list.SetItem( index, i+1, unicode(v) ) self.list.SetItemData( row, d[0] ) # This key links to the sort fields used by ColumnSorterMixin # Set the sort fields and configure the sorter mixin. self.itemDataMap = dict( (d[0], d) for d in data ) listmix.ColumnSorterMixin.__init__(self, len(externalFields)) # Make all column widths autosize. for i, f in enumerate(externalFields): self.list.SetColumnWidth( i, wx.LIST_AUTOSIZE ) # Fixup the Bib number, as autosize gets confused with the graphic. self.list.SetColumnWidth( 0, 64 ) wx.CallAfter( self.list.SetFocus )
def onRightClick(self, xPos, yPos, num, iRider, iLap): with Model.LockRace() as race: if not race or num not in race.riders: return category = FixCategories(self.categoryChoice, getattr(race, 'ganttCategory', 0)) entries = race.getRider(num).interpolate() try: self.entry = entries[iLap] self.entryStart = entries[iLap - 1] self.entryEnd = self.entry except (IndexError, KeyError): return self.setNumSelect(num) self.numSelect = num if Utils.isMainWin(): wx.CallAfter(Utils.getMainWin().setNumSelect, self.ganttChart.numSelect) self.iLap = iLap self.iRow = iRider allCases = 0 interpCase = 1 nonInterpCase = 2 if not hasattr(self, 'popupInfo'): self.popupInfo = [ (_('Add Missing Last Lap'), _('Add Missing Last Lap'), self.OnPopupAddMissingLastLap, allCases), (None, None, None, None), (_('Pull after Lap End') + u'...', _('Pull after Lap End'), self.OnPopupPull, allCases), (_('DNF after Lap End') + u'...', _('DNF after Lap End'), self.OnPopupDNF, allCases), (None, None, None, None), (_('Correct Lap End Time') + u'...', _('Change number or lap end time'), self.OnPopupCorrect, interpCase), (_('Shift Lap End Time') + u'...', _('Move lap end time earlier/later'), self.OnPopupShift, interpCase), (_('Delete Lap End Time') + u'...', _('Delete Lap End Time'), self.OnPopupDelete, nonInterpCase), (None, None, None, None), (_('Mass Pull after Lap End Time') + u'...', _('Pull Rider and all Riders after at Lap End'), self.OnPopupMassPull, allCases), (None, None, None, None), (_('Note') + u'...', _('Add/Edit lap Note'), self.OnPopupLapNote, allCases), (None, None, None, None), (_('Turn off Autocorrect') + u'...', _('Turn off Autocorrect'), self.OnPopupAutocorrect, allCases), (None, None, None, None), (_('Swap with Rider before'), _('Swap with Rider before'), self.OnPopupSwapBefore, nonInterpCase), (_('Swap with Rider after'), _('Swap with Rider after'), self.OnPopupSwapAfter, nonInterpCase), (None, None, None, None), (_('Show Lap Details') + u'...', _('Show Lap Details'), self.OnPopupLapDetail, allCases), (None, None, None, None), (_('RiderDetail'), _('Show RiderDetail Dialog'), self.OnPopupRiderDetail, allCases), (_('Results'), _('Switch to Results tab'), self.OnPopupResults, allCases), ] self.splitMenuInfo = [ (u'{} {}'.format( split - 1, _('Split') if split - 1 == 1 else _('Splits')), lambda evt, s=self, splits=split: s.doSplitLap(splits)) for split in range(2, 8) ] + [(_('Custom') + u'...', lambda evt, s=self: s.doCustomSplitLap())] self.menuOptions = {} for numBefore in [False, True]: for numAfter in [False, True]: for caseCode in range(3): menu = wx.Menu() for name, text, callback, cCase in self.popupInfo: if not name: Utils.addMissingSeparator(menu) continue if caseCode < cCase: continue if (name.endswith(_('before')) and not numBefore) or (name.endswith( _('after')) and not numAfter): continue item = menu.Append(wx.ID_ANY, name, text) self.Bind(wx.EVT_MENU, callback, item) if caseCode == 2: submenu = wx.Menu() for name, callback in self.splitMenuInfo: item = submenu.Append(wx.ID_ANY, name) self.Bind(wx.EVT_MENU, callback, item) Utils.addMissingSeparator(menu) menu.PrependSeparator() menu.Prepend(wx.ID_ANY, _('Add Missing Split'), submenu) Utils.deleteTrailingSeparators(menu) self.menuOptions[(numBefore, numAfter, caseCode)] = menu caseCode = 1 if entries[iLap].interp else 2 riderResults = dict((r.num, r) for r in GetResults(category)) self.numBefore, self.numAfter = None, None for iRow, attr in [(self.iRow - 1, 'numBefore'), (self.iRow + 1, 'numAfter')]: if not (0 <= iRow < len(self.ganttChart.labels)): continue numAdjacent = GanttChartPanel.numFromLabel( self.ganttChart.labels[iRow]) if RidersCanSwap(riderResults, num, numAdjacent): setattr(self, attr, numAdjacent) menu = self.menuOptions[(bool(self.numBefore), bool(self.numAfter), caseCode)] try: self.PopupMenu(menu) except Exception as e: Utils.writeLog('Gantt:onRightClick: {}'.format(e))
def refresh(self): self.isEmpty = True self.history = None self.category = None self.rcInterp = set() self.rcNumTime = set() self.search.SelectAll() wx.CallAfter(self.Refresh) race = Model.race if race is None: self.clearGrid() return lastGridX, lastGridY = self.grid.GetViewStart() highPrecision = Model.highPrecisionTimes() if highPrecision or race.isTimeTrial: formatTime = lambda t: Utils.formatTime(t, True) formatTimeDiff = lambda a, b: Utils.formatTimeGap( TimeDifference(a, b, True), True) else: formatTime = Utils.formatTime formatTimeDiff = lambda a, b: Utils.formatTimeGap( TimeDifference(a, b, False), False) category = FixCategories(self.categoryChoice, getattr(race, 'historyCategory', 0), doSyncCategories=self.sync.GetValue()) self.hbs.Layout() Finisher = Model.Rider.Finisher results = GetResults(category) lapsDown = { rr.num: rr.gap for rr in results if unicode(rr.gap).startswith('-') } position = { rr.num: pos for pos, rr in enumerate(results, 1) if rr.status == Finisher } winnerLaps = None if results and results[0].status == Finisher: winnerLaps = results[0].laps maxLaps = 0 doLapsToGo = True if race.winAndOut: entries = GetEntries(category) if entries: try: maxLaps = max(e.lap for e in entries if not e.interp) except ValueError: pass if race.isRunning(): maxLaps += 2 else: try: maxLaps = race.getNumLapsFromCategory(category) except Exception as e: maxLaps = race.numLaps doLapsToGo = True if not maxLaps: maxLaps = race.getMaxLap() if race.isRunning(): maxLaps += 2 doLapsToGo = False entries = GetEntries(category) if maxLaps: entries = [e for e in entries if e.lap <= maxLaps] entries = [ e for e in entries if e.lap <= race.getCategoryNumLaps(e.num) ] if race.isTimeTrial: entries = [ Model.Entry(e.num, e.lap, (race.riders[e.num].firstTime or 0.0) + e.t, e.interp) for e in entries ] entries.sort(key=operator.attrgetter('t', 'num')) # Collect the number and times for all entries so we can compute lap times. numTimes = {(e.num, e.lap): e.t for e in entries} self.category = category # Trim out the lap 0 starts. entries = [e for e in entries if e.lap > 0] if not entries: self.clearGrid() return # Organize all the entries into a grid as we would like to see them. self.history = [[]] numSeen = set() lapCur = 0 leaderTimes = [entries[0].t] for e in entries: if e.num in numSeen: numSeen.clear() lapCur += 1 self.history.append([]) leaderTimes.append(e.t) self.history[lapCur].append(e) numSeen.add(e.num) if not any(h for h in self.history): self.clearGrid() return # Show the values. self.isEmpty = False numTimeInfo = race.numTimeInfo colnames = [] raceTime = 0 for c, h in enumerate(self.history): try: lapTime = h[0].t - raceTime raceTime = h[0].t except IndexError as e: lapTime = 0 colnames.append('{}\n{}\n{}\n{}'.format( c + 1, (maxLaps - c - 1) if doLapsToGo else ' ', formatTime(lapTime), formatTime(raceTime))) formatStr = ['$num'] if self.showTimes: formatStr.append(', $raceTime') if self.showLapTimes: formatStr.append(', $lapTime') if self.showTimeDown: formatStr.append(', $downTime') if self.showRiderName: formatStr.append(', $riderName') template = Template(u''.join(formatStr)) try: info = race.excelLink.read() except: info = {} def getName(num): d = info.get(num, {}) return u', '.join( v for v in [d.get('LastName', None), d.get('FirstName', None)] if v) templateSave = template data = [] for col, h in enumerate(self.history): template = templateSave if col + 1 == winnerLaps: if self.showPosition: formatStr.insert(0, '$pos$lapsDown: ') template = Template(u''.join(formatStr)) data.append([ template.safe_substitute({ 'num': e.num, 'pos': Utils.ordinal(position.get(e.num, '')), 'lapsDown': u' ({})'.format(lapsDown[e.num]) if e.num in lapsDown else u'', 'raceTime': formatTime(e.t) if self.showTimes else '', 'lapTime': formatTime(e.t - numTimes[(e.num, e.lap - 1)]) if self.showLapTimes and (e.num, e.lap - 1) in numTimes else '', 'downTime': formatTimeDiff(e.t, leaderTimes[col]) if self.showTimeDown and col < len(leaderTimes) else '', 'riderName': getName(e.num) if self.showRiderName else '', }) for e in h ]) self.rcInterp.update( (row, col) for row, e in enumerate(h) if e.interp) self.rcNumTime.update( (row, col) for row, e in enumerate(h) if numTimeInfo.getInfo(e.num, e.t) is not None) self.grid.Set(data=data, colnames=colnames) self.grid.AutoSizeColumns(True) self.grid.Reset() self.updateColours() self.grid.Set(textColour=self.textColour, backgroundColour=self.backgroundColour) # Fix the grid's scrollbars. self.grid.FitInside() self.grid.Scroll(lastGridX, lastGridY)
def doRightClick(self, event): wx.CallAfter(self.search.SetFocus) self.doNumSelect(event) if self.numSelect is None: return allCases = 0 interpCase = 1 nonInterpCase = 2 if not hasattr(self, 'popupInfo'): self.popupInfo = [ (wx.NewId(), _('Passings'), _('Switch to Passings tab'), self.OnPopupHistory, allCases), (wx.NewId(), _('RiderDetail'), _('Show RiderDetail Dialog'), self.OnPopupRiderDetail, allCases), (None, None, None, None, None), (wx.NewId(), _('Show Photos'), _('Show Photos'), self.OnPopupShowPhotos, allCases), (None, None, None, None, None), (wx.NewId(), _('Correct...'), _('Change number or lap time...'), self.OnPopupCorrect, interpCase), (wx.NewId(), _('Shift...'), _('Move lap time earlier/later...'), self.OnPopupShift, interpCase), (wx.NewId(), _('Delete...'), _('Delete lap time...'), self.OnPopupDelete, nonInterpCase), (None, None, None, None, None), (wx.NewId(), _('Swap with Rider before'), _('Swap with Rider before'), self.OnPopupSwapBefore, allCases), (wx.NewId(), _('Swap with Rider after'), _('Swap with Rider after'), self.OnPopupSwapAfter, allCases), ] for p in self.popupInfo: if p[0]: self.Bind(wx.EVT_MENU, p[3], id=p[0]) num = int(self.numSelect) with Model.LockRace() as race: if not race or num not in race.riders: return entries = race.getRider(num).interpolate() category = FixCategories(self.categoryChoice, getattr(race, 'resultsCategory', 0)) riderResults = dict((r.num, r) for r in GetResults(category)) try: self.entry = entries[self.iLap] caseCode = 1 if self.entry.interp else 2 except (TypeError, IndexError, KeyError): caseCode = 0 self.numBefore, self.numAfter = None, None for iRow, attr in [(self.iRow - 1, 'numBefore'), (self.iRow + 1, 'numAfter')]: if not (0 <= iRow < self.lapGrid.GetNumberRows()): continue numAdjacent = int(self.labelGrid.GetCellValue(iRow, 1)) if RidersCanSwap(riderResults, num, numAdjacent): setattr(self, attr, numAdjacent) menu = wx.Menu() for id, name, text, callback, cCase in self.popupInfo: if not id: Utils.addMissingSeparator(menu) continue if caseCode >= cCase: if (name.endswith('before') and not self.numBefore) or (name.endswith('after') and not self.numAfter): continue menu.Append(id, name, text) Utils.deleteTrailingSeparators(menu) try: self.PopupMenu(menu) menu.Destroy() except Exception as e: Utils.writeLog('Results:doRightClick: {}'.format(e))