Skip to content

Commit

Permalink
Release v3.1.62-20240816123322
Browse files Browse the repository at this point in the history
  • Loading branch information
esitarski committed Aug 16, 2024
2 parents 5876b55 + 4e52e6d commit 290cbf9
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 90 deletions.
141 changes: 93 additions & 48 deletions LapsToGoCount.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,36 +129,57 @@ def __exit__(self, *args):
else:
getattr( self.dc, funcName )( vNew )

@Model.memoize
def LapsToGoCount( t=None ):
ltgc = {} # dict indexed by category with a list of (lapsToGo, count).
sc = {} # dict index by category with counts of each status.

race = Model.race
if not race or race.isUnstarted() or race.isFinished():
return ltgc
if not race or race.isUnstarted():
return ltgc, sc
isTimeTrial = race.isTimeTrial

if not t:
t = race.curRaceTime()
t = race.curRaceTime() if race.isRunning() else float('inf')

Finisher = Model.Rider.Finisher
NP = Model.Rider.NP

lapsToGoCountCategory = defaultdict( int )
for category in race.getCategories():
statusCategory = defaultdict( int )
categoryLaps = category.getNumLaps()

statusCategory = defaultdict( int )
for rr in GetResults(category):
statusCategory[rr.status] += 1
if rr.status != Finisher or not rr.raceTimes:

if rr.status != Finisher:
if isTimeTrial and rr.status == NP and categoryLaps is not None:
# Record TT riders who have started with the full laps.
rider = race.riders[rr.num]
# print( rider.num, rider.firstTime, t, rr.status, categoryLaps )
if rider.firstTime is not None and t >= rider.firstTime:
lapsToGoCountCategory[categoryLaps] += 1
# Reclassify NP to Finisher as we know the rider is on course.
statusCategory[NP] -= 1
statusCategory[Finisher] += 1
continue

if not rr.raceTimes:
if isTimeTrial and categoryLaps is not None:
lapsToGoCountCategory[categoryLaps] += 1
continue

try:
tSearch = race.riders[rr.num].raceTimeToRiderTime( t )
except KeyError:
continue

if rr.raceTimes[-1] <= tSearch or not (lap := bisect_right(rr.raceTimes, tSearch) ):
if not (lap := bisect_right(rr.raceTimes, tSearch) ):
continue
lap -= 1

lapsToGoCountCategory[len(rr.raceTimes) - lap] += 1
lapsToGoCountCategory[len(rr.raceTimes) - lap - 1] += 1

ltgc[category] = sorted( lapsToGoCountCategory.items(), reverse=True )
lapsToGoCountCategory.clear()
Expand All @@ -174,7 +195,7 @@ def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,

super().__init__(parent, id, pos, size, style, validator, name)

self.barBrushes = [wx.Brush(wx.Colour( int(c[:2],16), int(c[2:4],16), int(c[4:],16)), wx.SOLID) for c in ('D8E6AD', 'E6ADD8', 'ADD8E6')]
self.barBrushes = [wx.Brush(wx.Colour( int(c[:2],16), int(c[2:4],16), int(c[4:],16)), wx.SOLID) for c in ('A0A0A0', 'D3D3D3', 'E5E4E2')]
self.statusKeys = sorted( (k for k in Model.Rider.statusSortSeq.keys() if isinstance(k, int)), key=lambda k: Model.Rider.statusSortSeq[k] )

self.SetBackgroundColour(wx.WHITE)
Expand All @@ -199,7 +220,7 @@ def SetBackgroundColour(self, colour):
def ShouldInheritColours(self):
return True

def OnPaint(self, event ):
def OnPaint(self, event):
dc = wx.PaintDC(self)
self.Draw(dc)

Expand Down Expand Up @@ -231,94 +252,118 @@ def Draw( self, dc ):
race = Model.race
categories = race.getCategories()

yTop = xLeft = int( min( height * 0.03, width * 0.03 ) )
catLabelFontHeight = min( 12, int(height * 0.1) )
catLabelHeight = int( catLabelFontHeight * 2.5 )
yTop = catLabelHeight
xLeft = 0

xRight = width - xLeft
yBottom = height - yTop
yBottom = height

catHeight = int( (yBottom-yTop) / len(categories) )
catLabelFontHeight = min( 12, int(catHeight * 0.1) )
catLabelHeight = int( catLabelFontHeight * 2.5 )

catFieldHeight = catHeight - catLabelHeight
barFieldHeight = catFieldHeight - catLabelHeight

catLabelFont = wx.Font( wx.FontInfo(catLabelFontHeight).FaceName('Helvetica') )
catLabelFontBold = wx.Font( wx.FontInfo(catLabelFontHeight).FaceName('Helvetica').Bold() )
catLabelMargin = int( (catLabelHeight - catLabelFontHeight) / 2 )

def statusCountStr( sc ):
Finisher = Model.Rider.Finisher
def statusCountStr( sc, lap0Count ):
statusNames = Model.Rider.statusNames
translate = _
t = []
onCourseCount = 0
for status in self.statusKeys:
if count := sc.get(status, 0):
if status == Finisher:
onCourseCount = count - lap0Count
sName = translate(statusNames[status].replace('Finisher','Competing'))
t.append( f'{count}={sName}' )
return ' | '.join( t )
return f"{onCourseCount}={_('OnCourse')}" + (" | " if t else '') + ' | '.join( t )

titleStyle = DCStyle( dc, Font=catLabelFontBold )
chartLineStyle = DCStyle( dc, Pen=greyPen, Brush=wx.TRANSPARENT_BRUSH )

lap0Total = finisherTotal = 0
yCur = yTop
for cat in categories:
# Draw the lines.
dc.SetPen( greyPen )
dc.DrawLine( xLeft, yCur + catFieldHeight, xLeft, yCur )
dc.DrawLine( xRight, yCur + catFieldHeight, xRight, yCur )
dc.DrawLine( xLeft, yCur + catFieldHeight, xRight, yCur + catFieldHeight )
dc.DrawLine( xLeft, yCur, xRight, yCur )
if barFieldHeight >= catLabelFontHeight:
# Draw the chart lines.
with chartLineStyle:
dc.DrawRectangle( xLeft, yCur, xRight-xLeft, catFieldHeight )

# Draw the lap bars.
ltg = lapsToGoCount[cat]
barCount = 1
if ltg:
barCount = ltg[0][0] - ltg[-1][0] + 1


finisherTotal += statusCount[cat].get( Finisher, 0 )

# Compute the barwidths so they take the entire horizontal line.
barWidth = (xRight - xLeft) / barCount
barX = [round( xLeft + i * barWidth ) for i in range(barCount)]
barX.append( xRight )
barTextWidth = barWidth - 2

# Draw the bars and labels.
countTotal = sum( count for lap, count in ltg )
dc.SetPen( greyPen )
dc.SetFont( catLabelFont )
lap0Count = 0
for lap, count in ltg:
barHeight = round( catFieldHeight * count / countTotal )
i = ltg[0][0] - lap
dc.SetBrush( self.barBrushes[lap%len(self.barBrushes)] )
dc.DrawRectangle( barX[i], yCur + catFieldHeight - barHeight, barX[i+1] - barX[i], barHeight )

if lap:
s = f'{count} @ {lap} {_("to go")}'
s = f'{lap} {_("to go")}'
tWidth = dc.GetTextExtent( s ).width
if tWidth >= barWidth - 2:
s = f'{count} @ {lap}'
if tWidth >= barTextWidth:
s = f'{lap}'
tWidth = dc.GetTextExtent( s ).width
if tWidth >= barWidth - 2:
s = f'{count}@{lap}'
tWidth = dc.GetTextExtent( s ).width
else:
s = f'{count} {_("Finished")}'
lap0Count = count
lap0Total += count
s = f'{_("Finished")}'
tWidth = dc.GetTextExtent( s ).width
if tWidth >= barWidth - 2:
s = f'{count} @ {_("Fin")}'
if tWidth >= barTextWidth:
s = f'{_("Fin")}'
tWidth = dc.GetTextExtent( s ).width
if tWidth >= barWidth - 2:
s = f'{count}@{_("Fin")}'
tWidth = dc.GetTextExtent( s ).width

y = yCur + catHeight - catLabelMargin - catLabelFontHeight
x = barX[i] + (barX[i+1] - barX[i] - tWidth) // 2
dc.DrawText( s, x, y )
if barFieldHeight < catLabelFontHeight:
continue

barHeight = round( barFieldHeight * count / countTotal )

i = ltg[0][0] - lap
dc.SetBrush( self.barBrushes[lap%len(self.barBrushes)] )
dc.DrawRectangle( barX[i], yCur + catFieldHeight - barHeight, barX[i+1] - barX[i], barHeight )

if tWidth < barTextWidth:
y = yCur + catHeight - catLabelMargin - catLabelFontHeight
x = barX[i] + (barX[i+1] - barX[i] - tWidth) // 2
dc.DrawText( s, x, y )

s = f'{count}'
tWidth = dc.GetTextExtent( s ).width
if tWidth < barTextWidth:
y = min( yCur + catFieldHeight - catLabelFontHeight- catLabelFontHeight//4, yCur + catFieldHeight - barHeight + catLabelFontHeight//2 )
x = barX[i] + (barX[i+1] - barX[i] - tWidth) // 2
dc.DrawText( s, x, y )

# Draw the category label with the on course total.
#onCourse = countTotal - (ltg[-1][1] if ltg and ltg[-1][0] == 0 else 0)
#finished = countTotal - onCourse
# Draw the category label with the status totals.
with titleStyle:
dc.DrawText( f'{cat.fullname}', xLeft + catLabelMargin, yCur + catLabelMargin )
dc.DrawText( f'{statusCountStr(statusCount[cat])}', xLeft + catLabelMargin*4, int(yCur + catLabelMargin + catLabelFontHeight*1.75) )
catText = f'{cat.fullname}'
titleTextWidth = dc.GetTextExtent(catText).width
dc.DrawText( catText, xLeft + catLabelMargin, yCur + catLabelMargin )
dc.DrawText( f'{statusCountStr(statusCount[cat], lap0Count)}', xLeft + titleTextWidth + catLabelMargin*2, int(yCur + catLabelMargin) )

yCur += catHeight

yCur += catHeight
with titleStyle:
catText = f'{_("All")}'
titleTextWidth = dc.GetTextExtent(catText).width
dc.DrawText( catText, xLeft + catLabelMargin, catLabelFontHeight//2 )
dc.DrawText( f'{finisherTotal-lap0Total}={_("OnCourse")}', xLeft + titleTextWidth + catLabelMargin*2, catLabelFontHeight//2 )

def OnEraseBackground(self, event):
# This is intentionally empty.
Expand Down
5 changes: 4 additions & 1 deletion MainWin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2686,7 +2686,7 @@ def onCloseWindow( self, event ):
self.doCleanup()
wx.Exit()

@logCall
#@logCall
def writeRace( self, doCommit = True ):
if doCommit:
self.commit()
Expand Down Expand Up @@ -4065,6 +4065,9 @@ def refresh( self ):
self.updateRaceClock()

def refreshTTStart( self ):
if Model.race:
# If a rider started the TT, force the results to be re-computed if necessary.
Model.race.setChanged()
if self.notebook.GetSelection() in (self.iHistoryPage, self.iRecordPage):
self.refreshCurrentPage()

Expand Down
43 changes: 4 additions & 39 deletions NumKeypad.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from NonBusyCall import NonBusyCall
from SetLaps import SetLaps
from InputUtils import enterCodes, validKeyCodes, clearCodes, actionCodes, getRiderNumsFromText, MakeKeypadButton
from LapsToGoCount import LapsToGoCountGraph

SplitterMinPos = 390
SplitterMaxPos = 530
Expand Down Expand Up @@ -419,15 +420,8 @@ def __init__( self, parent, id = wx.ID_ANY ):
#------------------------------------------------------------------------------
# Rider Lap Count.
rcVertical = wx.BoxSizer( wx.VERTICAL )
rcVertical.AddSpacer( 32 )

self.categoryStatsList = wx.ListCtrl( panel, wx.ID_ANY, style = wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_HRULES|wx.BORDER_NONE )
self.categoryStatsList.SetFont( wx.Font(int(fontSize*0.9), wx.DEFAULT, wx.NORMAL, wx.NORMAL) )
self.categoryStatsList.AppendColumn( _('Category'), wx.LIST_FORMAT_LEFT, 140 )
self.categoryStatsList.AppendColumn( _('Composition'), wx.LIST_FORMAT_LEFT, 130 )
self.categoryStatsList.SetColumnWidth( 0, wx.LIST_AUTOSIZE_USEHEADER )
self.categoryStatsList.SetColumnWidth( 1, wx.LIST_AUTOSIZE_USEHEADER )
rcVertical.Add( self.categoryStatsList, 1, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border = 4 )
self.lapsToGoCountGraph = LapsToGoCountGraph( panel )
rcVertical.Add( self.lapsToGoCountGraph, 1, flag=wx.EXPAND|wx.TOP|wx.RIGHT, border = 4 )

horizontalMainSizer.Add( rcVertical, 1, flag=wx.EXPAND|wx.LEFT, border = 4 )
self.horizontalMainSizer = horizontalMainSizer
Expand Down Expand Up @@ -663,36 +657,7 @@ def refreshLaps( self ):
wx.CallAfter( self.refreshRaceHUD )

def refreshRiderCategoryStatsList( self ):
self.categoryStatsList.DeleteAllItems()
race = Model.race
if not race:
return

def appendListRow( row = tuple(), colour = None, bold = None ):
r = self.categoryStatsList.InsertItem( self.categoryStatsList.GetItemCount(), '{}'.format(row[0]) if row else '' )
for c in range(1, len(row)):
self.categoryStatsList.SetItem( r, c, '{}'.format(row[c]) )
if colour is not None:
item = self.categoryStatsList.GetItem( r )
item.SetTextColour( colour )
self.categoryStatsList.SetItem( item )
if bold is not None:
item = self.categoryStatsList.GetItem( r )
font = self.categoryStatsList.GetFont()
font.SetWeight( wx.FONTWEIGHT_BOLD )
item.SetFont( font )
self.categoryStatsList.SetItem( item )
return r

for catStat in getCategoryStats():
if catStat[0] == _('All'):
colour, bold = wx.BLUE, None
else:
colour = bold = None
appendListRow( catStat, colour, bold )

self.categoryStatsList.SetColumnWidth( 0, wx.LIST_AUTOSIZE_USEHEADER )
self.categoryStatsList.SetColumnWidth( 1, wx.LIST_AUTOSIZE_USEHEADER )
self.lapsToGoCountGraph.Refresh()

def refreshLastRiderOnCourse( self ):
race = Model.race
Expand Down
4 changes: 3 additions & 1 deletion Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,8 +629,10 @@ def refresh():
mainWin.refresh()

def refreshForecastHistory():
if mainWin is not None:
try:
mainWin.forecastHistory.refresh()
except AttributeError:
pass

def updateUndoStatus():
if mainWin is not None:
Expand Down
2 changes: 1 addition & 1 deletion Version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
AppVerName="CrossMgr 3.1.61-private"
AppVerName="CrossMgr 3.1.62-private"

0 comments on commit 290cbf9

Please sign in to comment.