Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
	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()
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
	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 )
Ejemplo n.º 5
0
 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
Ejemplo n.º 6
0
	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) )
Ejemplo n.º 7
0
	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 )
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
	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 )
Ejemplo n.º 11
0
    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()
Ejemplo n.º 12
0
	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()
Ejemplo n.º 13
0
    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)
Ejemplo n.º 14
0
    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))
Ejemplo n.º 15
0
 def setCategoryAll(self):
     FixCategories(self.categoryChoice, 0)
     Model.setCategoryChoice(0, 'historyCategory')
Ejemplo n.º 16
0
    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()
Ejemplo n.º 17
0
 def setCategoryAll(self):
     FixCategories(self.categoryChoice, 0)
     Model.setCategoryChoice(0, 'resultsCategory')
Ejemplo n.º 18
0
	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 )
Ejemplo n.º 19
0
    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))
Ejemplo n.º 20
0
    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)
Ejemplo n.º 21
0
    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))