Exemple #1
0
    def getGrid(self):
        headerNames = [u'', u'']

        grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        grid.DisableDragRowSize()
        grid.SetRowLabelSize(0)
        grid.EnableReorderRows(False)
        grid.CreateGrid(len(self.modelFields) + 1, len(headerNames))
        for col, h in enumerate(headerNames):
            grid.SetColLabelValue(col, h)
        grid.Show(False)

        for row, fd in enumerate(self.modelFields):
            grid.SetCellValue(row, 0, fd.name)
            grid.SetCellAlignment(row, 0, wx.ALIGN_RIGHT, wx.ALIGN_BOTTOM)
            grid.SetCellValue(row, 1, fd.getText())

        row = len(self.modelFields)
        grid.SetCellValue(row, 0, _('Competition Format'))
        grid.SetCellAlignment(row, 0, wx.ALIGN_RIGHT, wx.ALIGN_BOTTOM)
        grid.SetCellValue(
            row, 1,
            self.competitionFormatCtrl.GetStringSelection().split(
                u'.', 1)[1].strip())

        return grid
	def writeKOMGC():
		if not model.kom_gc:
			return
		
		riderFields = set( model.registration.getFieldsInUse() )
		headers = (
			['place', 'bib', 'last_name', 'first_name', 'team'] +
			(['uci_id'] if 'uci_id' in riderFields else []) +
			(['license'] if 'license' in riderFields else []) +
			['KOM Total', 'HC Wins', 'C1 Wins', 'C2 Wins', 'C3 Wins', 'C4 Wins', 'GC']
		)
		
		grid = ReorderableGrid( notebook )
		grid.CreateGrid( len(model.kom_gc), len(headers) )
		grid.EnableReorderRows( False )
		
		for col, h in enumerate(headers):
			attr = gridlib.GridCellAttr()
			attr.SetReadOnly()
			if h in Model.Result.NumericFields or h in {'KOM Total', 'HC Wins', 'C1 Wins', 'C2 Wins', 'C3 Wins', 'C4 Wins', 'GC'}:
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_CENTRE )
			grid.SetColAttr( col, attr )
			grid.SetColLabelValue( col, Utils.fieldToHeader(h, True) )
		
		rowNum = 0
		for place, r in enumerate(model.kom_gc, 1):
			try:
				rider = model.registration.bibToRider[r[-1]]
			except KeyError:
				continue
		
			col = 0
			grid.SetCellValue( rowNum, col, unicode(place) ); col += 1			
			grid.SetCellValue( rowNum, col, unicode(rider.bib) ); col += 1
			grid.SetCellValue( rowNum, col, unicode(rider.last_name).upper()); col += 1
			grid.SetCellValue( rowNum, col, unicode(rider.first_name) ); col += 1
			grid.SetCellValue( rowNum, col, unicode(rider.team) ); col += 1
			
			if 'uci_id' in riderFields:
				grid.SetCellValue( rowNum, col, unicode(rider.uci_id) ); col += 1
			if 'license' in riderFields:
				grid.SetCellValue( rowNum, col, unicode(rider.license) ); col += 1

			for v in r[:-1]:
				grid.SetCellValue( rowNum, col, unicode(v) if v else u'' ); col += 1
				
			rowNum +=1
			
		grid.AutoSize()
		return grid
	def writeTeamClass( stage ):
		
		headers = ['Place', 'Team', 'Gap', 'Combined\nTimes', 'Combined\nPlaces', 'Best\nRider GC']
		
		grid = ReorderableGrid( notebook )
		grid.CreateGrid( len(stage.team_classification), len(headers) )
		grid.EnableReorderRows( False )
		
		for col, h in enumerate(headers):
			attr = gridlib.GridCellAttr()
			attr.SetReadOnly()
			if h != 'Team':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_CENTRE )
			grid.SetColAttr( col, attr )
			grid.SetColLabelValue( col, h )
		
		rowNum = 0
		gapLast = None
		timeLast = None
		for place, tc in enumerate(stage.team_classification, 1):
			col = 0
			grid.SetCellValue( rowNum, col, unicode(place) ); col += 1
			grid.SetCellValue( rowNum, col, tc.team ); col += 1
			
			grid.SetCellValue( rowNum, col, Utils.formatTime(tc.gap, twoDigitHours=True) if tc.gap != gapLast else sameGap )
			gapLast = tc.gap
			col += 1
			
			timeCur = tc.sum_best_top_times.value
			grid.SetCellValue( rowNum, col, Utils.formatTime(timeCur, forceHours=True) if timeCur != timeLast else sameTime )
			timeLast = timeCur
			setComment( rowNum, col, formatContext(tc.sum_best_top_times.context), {'width':256} )
			col += 1
			
			grid.SetCellValue( rowNum, col, unicode(tc.sum_best_top_places.value) )
			setComment( rowNum, col, formatContext(tc.sum_best_top_places.context), {'width':256} )
			col += 1
			
			grid.SetCellValue( rowNum, col, unicode(tc.best_place.value) )
			setComment( rowNum, col, formatContext(tc.best_place.context), {'width':256} )
			col += 1
			rowNum +=1
			
		grid.GetGridWindow().Bind(wx.EVT_MOTION, getCommentCallback(grid))
		grid.AutoSize()
		return grid
Exemple #4
0
class Errors(wx.Panel):
    #----------------------------------------------------------------------
    ErrorCol = 0
    RaceCol = 1

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        self.headerNames = ['Error', 'Race']

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(64)
        self.grid.CreateGrid(0, len(self.headerNames))
        for col in xrange(self.grid.GetNumberCols()):
            self.grid.SetColLabelValue(col, self.headerNames[col])
            attr = gridlib.GridCellAttr()
            attr.SetReadOnly(True)
            self.grid.SetColAttr(col, attr)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=6)
        self.SetSizer(sizer)

    def getGrid(self):
        return self.grid

    def gridAutoSize(self):
        self.grid.AutoSize()
        self.grid.EnableDragGridSize(False)
        self.grid.EnableDragColSize(False)
        self.Layout()
        self.Refresh()

    def refresh(self):
        model = SeriesModel.model
        Utils.AdjustGridSize(self.grid, len(model.errors))
        for row, (r, e) in enumerate(model.errors):
            self.grid.SetCellValue(row, self.RaceCol, r.fileName)
            self.grid.SetCellValue(row, self.ErrorCol, '{}'.format(e))
        wx.CallAfter(self.gridAutoSize)

    def commit(self):
        pass
Exemple #5
0
class Results(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)

        self.font = wx.Font((0, FontSize), wx.FONTFAMILY_SWISS,
                            wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)

        self.showResultsLabel = wx.StaticText(self, wx.ID_ANY, u'Show:')
        self.showResultsLabel.SetFont(self.font)
        self.showResults = wx.Choice(self, wx.ID_ANY, choices=['Qualifiers'])
        self.showResults.SetFont(self.font)
        self.showResults.SetSelection(0)

        self.communiqueLabel = wx.StaticText(self, wx.ID_ANY,
                                             u'Communiqu\u00E9:')
        self.communiqueLabel.SetFont(self.font)
        self.communiqueNumber = wx.TextCtrl(self, wx.ID_ANY, '', size=(80, -1))
        self.communiqueNumber.SetFont(self.font)

        self.showResults.Bind(wx.EVT_LEFT_DOWN, self.onClickResults)
        self.showResults.Bind(wx.EVT_CHOICE, self.onShowResults)
        self.showNames = wx.ToggleButton(self, wx.ID_ANY, u'Show Names')
        self.showNames.SetFont(self.font)
        self.showNames.Bind(wx.EVT_TOGGLEBUTTON, self.onToggleShow)
        self.showTeams = wx.ToggleButton(self, wx.ID_ANY, u'Show Teams')
        self.showTeams.SetFont(self.font)
        self.showTeams.Bind(wx.EVT_TOGGLEBUTTON, self.onToggleShow)
        self.competitionTime = wx.StaticText(self)

        self.headerNames = ['uPos', u'Bib', u'Rider', u'Team', u'License']

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(64)
        self.grid.CreateGrid(0, len(self.headerNames))
        self.grid.SetRowLabelSize(0)
        self.grid.EnableReorderRows(False)
        self.setColNames()

        sizer = wx.BoxSizer(wx.VERTICAL)

        hs = wx.BoxSizer(wx.HORIZONTAL)
        hs.Add(self.showResultsLabel,
               0,
               flag=wx.ALIGN_CENTRE_VERTICAL,
               border=4)
        hs.Add(self.showResults,
               0,
               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
               border=4)
        hs.Add(self.communiqueLabel,
               0,
               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
               border=4)
        hs.Add(self.communiqueNumber,
               0,
               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND,
               border=4)
        hs.Add(self.showNames,
               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
               border=4)
        hs.Add(self.showTeams,
               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
               border=4)
        hs.Add(self.competitionTime,
               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
               border=4)

        sizer.Add(hs,
                  0,
                  flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
                  border=6)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=6)
        self.SetSizer(sizer)

    def onToggleShow(self, e):
        model = Model.model
        model.resultsShowNames = self.showNames.GetValue()
        model.resultsShowTeams = self.showTeams.GetValue()
        self.refresh()

    def setColNames(self):
        self.grid.SetLabelFont(self.font)
        for col, headerName in enumerate(self.headerNames):
            self.grid.SetColLabelValue(col, headerName)

            attr = gridlib.GridCellAttr()
            attr.SetFont(self.font)
            if self.headerNames[col] in {u'Bib', u'Event'}:
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
            elif u'Time' in self.headerNames[col]:
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_TOP)
            elif self.headerNames[col] == u'Pos':
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_TOP)
            elif Arrow in self.headerNames[col]:
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_VERTICAL_CENTRE)
            elif self.headerNames[col].startswith(u'H'):
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
            attr.SetReadOnly(True)
            self.grid.SetColAttr(col, attr)

    def getResultChoices(self):
        model = Model.model
        competition = model.competition
        choices = [u'Qualifiers']
        for tournament in competition.tournaments:
            for system in tournament.systems:
                name = ('%s: ' % tournament.name
                        if tournament.name else '') + system.name
                choices.append(name)
        choices.append(u'Final Classification')
        return choices

    def fixShowResults(self):
        model = Model.model
        competition = model.competition

        choices = self.getResultChoices()
        self.showResults.SetItems(choices)

        if model.showResults >= len(choices):
            model.showResults = 0
        self.showResults.SetSelection(model.showResults)

    def getHideCols(self, headerNames):
        model = Model.model
        toHide = set()
        for col, h in enumerate(headerNames):
            if h == u'Name' and not getattr(model, 'resultsShowNames', True):
                toHide.add(col)
            elif h == u'Team' and not getattr(model, 'resultsShowTeams', True):
                toHide.add(col)
        return toHide

    def getGrid(self):
        return self.grid

    def getPhase(self, num=None):
        if num is None:
            num = self.showResults.GetSelection()
        choices = self.getResultChoices()
        return choices[num]

    def getTitle(self):
        phase = self.getPhase()
        title = u'Communiqu\u00E9: {}\n{} {} '.format(
            self.communiqueNumber.GetValue(), phase,
            '' if phase.startswith(u'Final') or phase.startswith('Time') else
            u'Draw Sheet/Intermediate Results')
        return title

    def onClickResults(self, event):
        self.commit()
        event.Skip()

    def onShowResults(self, event):
        Model.model.showResults = self.showResults.GetSelection()
        self.refresh()

    def refresh(self):
        self.fixShowResults()
        self.grid.ClearGrid()

        model = Model.model
        competition = model.competition

        self.showNames.SetValue(getattr(model, 'resultsShowNames', True))
        self.showTeams.SetValue(getattr(model, 'resultsShowTeams', True))

        self.communiqueNumber.SetValue(
            model.communique_number.get(self.getPhase(), ''))

        resultName = self.showResults.GetStringSelection()

        if 'Qualifiers' in resultName:
            starters = competition.starters

            self.headerNames = [u'Pos', u'Bib', u'Name', u'Team', u'Time']
            hideCols = self.getHideCols(self.headerNames)
            self.headerNames = [
                h for c, h in enumerate(self.headerNames) if c not in hideCols
            ]

            riders = sorted(model.riders, key=lambda r: r.keyQualifying())
            for row, r in enumerate(riders):
                if row >= starters or r.status == 'DNQ':
                    riders[row:] = sorted(riders[row:],
                                          key=lambda r: r.keyQualifying()[1:])
                    break
            Utils.AdjustGridSize(self.grid,
                                 rowsRequired=len(riders),
                                 colsRequired=len(self.headerNames))
            Utils.SetGridCellBackgroundColour(self.grid, wx.WHITE)
            self.setColNames()
            for row, r in enumerate(riders):
                if row < starters and r.status != 'DNQ':
                    pos = u'{}'.format(row + 1)
                    for col in six.moves.range(self.grid.GetNumberCols()):
                        self.grid.SetCellBackgroundColour(row, col, wx.WHITE)
                else:
                    pos = 'DNQ'
                    for col in six.moves.range(self.grid.GetNumberCols()):
                        self.grid.SetCellBackgroundColour(
                            row, col, wx.Colour(200, 200, 200))

                writeCell = WriteCell(self.grid, row)
                for col, value in enumerate([
                        pos, u' {}'.format(r.bib), r.full_name, r.team,
                        r.qualifyingTimeText
                ]):
                    if col not in hideCols:
                        writeCell(value)

            competitionTime = model.qualifyingCompetitionTime
            self.competitionTime.SetLabel(u'{}: {}'.format(
                _('Est. Competition Time'), Utils.formatTime(competitionTime)
            ) if competitionTime else u'')

        elif 'Final Classification' in resultName:
            self.headerNames = [u'Pos', u'Bib', u'Name', u'Team', u'License']
            hideCols = self.getHideCols(self.headerNames)
            self.headerNames = [
                h for c, h in enumerate(self.headerNames) if c not in hideCols
            ]

            results, dnfs, dqs = competition.getResults()
            Utils.AdjustGridSize(self.grid,
                                 rowsRequired=len(model.riders),
                                 colsRequired=len(self.headerNames))
            Utils.SetGridCellBackgroundColour(self.grid, wx.WHITE)

            self.setColNames()
            for row, (classification, r) in enumerate(results):
                writeCell = WriteCell(self.grid, row)
                if not r:
                    for col in six.moves.range(self.grid.GetNumberCols()):
                        writeCell('')
                else:
                    for col, value in enumerate([
                            classification, r.bib if r.bib else '',
                            r.full_name, r.team, r.license
                    ]):
                        if col not in hideCols:
                            writeCell(u' {}'.format(value))
            self.competitionTime.SetLabel(u'')
        else:
            # Find the Tournament and System selected.
            keepGoing = True
            for tournament in competition.tournaments:
                for system in tournament.systems:
                    name = (u'%s: ' % tournament.name
                            if tournament.name else '') + system.name
                    if name == resultName:
                        keepGoing = False
                        break
                if not keepGoing:
                    break

            heatsMax = max(event.heatsMax for event in system.events)
            if heatsMax == 1:
                self.headerNames = [
                    u'Event', u'Bib', u'Name', u'Note', u'Team', u'    ',
                    u'Pos', u'Bib', u'Name', u'Note', u'Team', u'Time'
                ]
            else:
                self.headerNames = [
                    u'Event', u'Bib', u'Name', u'Note', u'Team', u'H1', u'H2',
                    u'H3', u'    ', u'Pos', u'Bib', u'Name', u'Note', u'Team',
                    u'Time'
                ]
            hideCols = self.getHideCols(self.headerNames)
            self.headerNames = [
                h for c, h in enumerate(self.headerNames) if c not in hideCols
            ]

            Utils.AdjustGridSize(self.grid,
                                 rowsRequired=len(system.events),
                                 colsRequired=len(self.headerNames))
            Utils.SetGridCellBackgroundColour(self.grid, wx.WHITE)

            self.setColNames()
            state = competition.state

            for row, event in enumerate(system.events):
                writeCell = WriteCell(self.grid, row)

                writeCell(u'{}'.format(row + 1))

                riders = [state.labels.get(c, None) for c in event.composition]
                writeCell(u'\n'.join([
                    u'{}'.format(rider.bib) if rider and rider.bib else ''
                    for rider in riders
                ]))
                if getattr(model, 'resultsShowNames', True):
                    writeCell(u'\n'.join([
                        rider.full_name if rider else u'' for rider in riders
                    ]))
                writeCell(u'\n'.join([
                    competition.getRelegationsWarningsStr(
                        rider.bib, event, True) if rider else u''
                    for rider in riders
                ]))
                if getattr(model, 'resultsShowTeams', True):
                    writeCell(u'\n'.join(
                        [rider.team if rider else '' for rider in riders]))

                if heatsMax != 1:
                    for heat in six.moves.range(heatsMax):
                        if event.heatsMax != 1:
                            writeCell(u'\n'.join(event.getHeatPlaces(heat +
                                                                     1)))
                        else:
                            writeCell(u'')

                #writeCell( u' ===> ', vert=wx.ALIGN_CENTRE )
                writeCell(' '.join(['', Arrow, '']), vert=wx.ALIGN_CENTRE)

                out = [event.winner] + event.others
                riders = [state.labels.get(c, None) for c in out]
                writeCell(u'\n'.join(u'{}'.format(i + 1)
                                     for i in six.moves.range(len(riders))))
                writeCell(u'\n'.join([
                    u'{}'.format(rider.bib if rider.bib else '')
                    if rider else '' for rider in riders
                ]))
                if getattr(model, 'resultsShowNames', True):
                    writeCell(u'\n'.join([
                        rider.full_name if rider else '' for rider in riders
                    ]))
                writeCell(u'\n'.join([
                    competition.getRelegationsWarningsStr(
                        rider.bib, event, False) if rider else u''
                    for rider in riders
                ]))
                if getattr(model, 'resultsShowTeams', True):
                    writeCell(u'\n'.join(
                        [rider.team if rider else '' for rider in riders]))
                if event.winner in state.labels:
                    try:
                        value = u'%.3f' % event.starts[-1].times[1]
                    except (KeyError, IndexError, ValueError):
                        value = ''
                    writeCell(value)

            competitionTime = system.competitionTime
            self.competitionTime.SetLabel(u'{}: {}'.format(
                _('Est. Competition Time'), Utils.formatTime(competitionTime)
            ) if competitionTime else u'')

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

    def commit(self):
        model = Model.model
        phase = self.getPhase()
        cn = self.communiqueNumber.GetValue()
        if cn != model.communique_number.get(phase, u''):
            model.communique_number[phase] = cn
            model.setChanged()
Exemple #6
0
class Points(wx.Panel):
    #----------------------------------------------------------------------
    NameCol = 0
    OldNameCol = 1
    DepthCol = 2
    PointsCol = 3
    ParticipationCol = 4
    DNFCol = 5

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        #--------------------------------------------------------------------------
        box = wx.StaticBox(self, -1, 'Score Competitors by')
        bsizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        self.scoreByPoints = wx.RadioButton(self,
                                            label='Points',
                                            style=wx.RB_GROUP)
        self.scoreByPoints.Bind(wx.EVT_RADIOBUTTON, self.fixEnable)

        self.scoreByTime = wx.RadioButton(self, label='Time')
        self.scoreByTime.Bind(wx.EVT_RADIOBUTTON, self.fixEnable)

        self.scoreByPercent = wx.RadioButton(
            self, label='Percent Time (100 * WinnersTime / FinishTime)')
        self.scoreByPercent.Bind(wx.EVT_RADIOBUTTON, self.fixEnable)

        self.scoreByTrueSkill = wx.RadioButton(self, label='TrueSkill')
        self.scoreByTrueSkill.Bind(wx.EVT_RADIOBUTTON, self.fixEnable)

        self.scoreByPoints.SetValue(True)

        hb = wx.BoxSizer(wx.HORIZONTAL)
        hb.Add(self.scoreByPoints)
        hb.Add(self.scoreByTime, flag=wx.LEFT, border=16)
        hb.Add(self.scoreByPercent, flag=wx.LEFT, border=16)
        hb.Add(self.scoreByTrueSkill, flag=wx.LEFT, border=16)
        bsizer.Add(hb, flag=wx.ALL, border=2)

        #--------------------------------------------------------------------------
        bsizer.Add(wx.StaticLine(self), 1, flag=wx.EXPAND | wx.ALL, border=4)
        self.considerPrimePointsOrTimeBonus = wx.CheckBox(
            self, label='Consider Points or Time Bonuses from CrossMgr Primes')
        self.considerPrimePointsOrTimeBonus.SetValue(True)
        bsizer.Add(self.considerPrimePointsOrTimeBonus,
                   0,
                   flag=wx.ALL,
                   border=4)

        #--------------------------------------------------------------------------
        bsizer.Add(wx.StaticLine(self), 1, flag=wx.EXPAND | wx.ALL, border=4)

        maxOptions = 30
        self.considerLabel = wx.StaticText(self,
                                           label='{}:'.format('Consider'))
        self.bestResultsToConsider = wx.Choice(
            self,
            choices=['All Results', 'Best Result Only'] + [
                '{} {} {}'.format('Best', i, 'Results Only')
                for i in xrange(2, maxOptions + 1)
            ])

        self.participationLabel = wx.StaticText(
            self, label='{}:'.format('Must have completed'))
        self.mustHaveCompleted = wx.Choice(
            self,
            choices=[
                '{} {}'.format(i, 'or more Events')
                for i in xrange(0, maxOptions + 1)
            ])

        hb = wx.BoxSizer(wx.HORIZONTAL)
        hb.Add(self.considerLabel, flag=wx.ALIGN_CENTRE_VERTICAL)
        hb.Add(self.bestResultsToConsider)
        hb.Add(self.participationLabel,
               flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
               border=16)
        hb.Add(self.mustHaveCompleted)
        bsizer.Add(hb, flag=wx.ALL, border=2)

        #--------------------------------------------------------------------------
        bsizer.Add(wx.StaticLine(self), 1, flag=wx.EXPAND | wx.ALL, border=4)
        self.ifRidersTiedOnPoints = wx.StaticText(
            self, label='If Riders are Tied on Points:')
        bsizer.Add(self.ifRidersTiedOnPoints, flag=wx.ALL, border=2)
        self.mostEventsCompleted = wx.CheckBox(
            self, label='Consider Number of Events Completed')
        self.mostEventsCompleted.SetValue(False)
        bsizer.Add(self.mostEventsCompleted, 0, flag=wx.ALL, border=4)

        bsizer.Add(wx.StaticLine(self), 1, flag=wx.EXPAND | wx.ALL, border=4)
        self.numPlacesTieBreaker = wx.RadioBox(
            self,
            majorDimension=3,
            style=wx.RA_SPECIFY_ROWS,
            choices=[
                'Do not consider Place Finishes.',
                'Number of 1st Place Finishes',
                'Number of 1st then 2nd Place Finishes',
                'Number of 1st, 2nd then 3rd Place Finishes',
                'Number of 1st, 2nd, 3rd then 4th Place Finishes',
                'Number of 1st, 2nd, 3rd, 4th then 5th Place Finishes',
            ])
        self.numPlacesTieBreaker.SetLabel(
            u'If Riders are still Tied on Points:')
        bsizer.Add(self.numPlacesTieBreaker,
                   0,
                   flag=wx.ALL | wx.EXPAND,
                   border=2)
        self.ifRidersAreStillTiedOnPoints = wx.StaticText(
            self,
            label=u'If Riders are still Tied on Points, use most Recent Results'
        )
        bsizer.Add(self.ifRidersAreStillTiedOnPoints, flag=wx.ALL, border=4)

        self.headerNames = [
            'Name', 'OldName', 'Depth', 'Points for Position', 'Participation',
            'DNF'
        ]

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(64)
        self.grid.CreateGrid(50, len(self.headerNames))
        for col in xrange(self.grid.GetNumberCols()):
            self.grid.SetColLabelValue(
                col, self.headerNames[col] +
                (u'       ' if self.headerNames[col] == 'DNF' else ''))

        attr = gridlib.GridCellAttr()
        attr.SetReadOnly(True)
        attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
        self.grid.SetColAttr(self.DepthCol, attr)

        attr = gridlib.GridCellAttr()
        attr.SetEditor(PointsEditor())
        self.grid.SetColAttr(self.PointsCol, attr)

        for c in (self.ParticipationCol, self.DNFCol):
            attr = gridlib.GridCellAttr()
            attr.SetRenderer(wx.grid.GridCellNumberRenderer())
            attr.SetEditor(wx.grid.GridCellNumberEditor(0, 10000))
            self.grid.SetColAttr(c, attr)

        self.gridAutoSize()

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(bsizer, 0, flag=wx.EXPAND | wx.ALL, border=4)
        self.pointsStructures = wx.StaticText(self, wx.ID_ANY,
                                              'Points Structures:')
        sizer.Add(self.pointsStructures, 0, flag=wx.ALL, border=4)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=6)
        self.SetSizer(sizer)

        self.scoreByPointsControls = [
            self.ifRidersTiedOnPoints,
            self.mostEventsCompleted,
            self.numPlacesTieBreaker,
            self.ifRidersAreStillTiedOnPoints,
            self.pointsStructures,
            self.grid,
        ]

    def getGrid(self):
        return self.grid

    def gridAutoSize(self):
        self.grid.AutoSize()
        self.grid.SetColMinimalAcceptableWidth(0)
        self.grid.SetColMinimalWidth(self.OldNameCol, 0)
        self.grid.SetColSize(self.OldNameCol, 0)
        self.grid.EnableDragGridSize(False)
        self.grid.EnableDragColSize(False)
        self.Layout()
        self.Refresh()

    def updateDepth(self, row):
        v = self.grid.GetCellValue(row, self.PointsCol).strip()
        depth = unicode(len(v.split())) if v else u''
        self.grid.SetCellValue(row, self.DepthCol, depth)

    def onGridChange(self, event):
        row = event.GetRow()
        if row >= 0:
            self.updateDepth(row)
        wx.CallAfter(self.gridAutoSize)

    def fixEnable(self, event=None):
        enable = self.scoreByPoints.GetValue()
        for c in self.scoreByPointsControls:
            c.Enable(enable)

    def refresh(self):
        model = SeriesModel.model
        for row in xrange(self.grid.GetNumberRows()):
            for col in xrange(self.grid.GetNumberCols()):
                self.grid.SetCellValue(row, col, '')

        for row, ps in enumerate(model.pointStructures):
            self.grid.SetCellValue(row, self.NameCol, ps.name)
            self.grid.SetCellValue(row, self.OldNameCol, ps.name)
            self.grid.SetCellValue(row, self.PointsCol, ps.getStr())
            self.grid.SetCellValue(row, self.ParticipationCol,
                                   unicode(ps.participationPoints))
            self.grid.SetCellValue(row, self.DNFCol, unicode(ps.dnfPoints))
            self.updateDepth(row)

        wx.CallAfter(self.gridAutoSize)

        self.considerPrimePointsOrTimeBonus.SetValue(
            model.considerPrimePointsOrTimeBonus)
        self.bestResultsToConsider.SetSelection(model.bestResultsToConsider)
        self.mustHaveCompleted.SetSelection(model.mustHaveCompleted)

        self.mostEventsCompleted.SetValue(model.useMostEventsCompleted)
        self.numPlacesTieBreaker.SetSelection(model.numPlacesTieBreaker)

        if model.scoreByTime:
            self.scoreByTime.SetValue(True)
        elif model.scoreByPercent:
            self.scoreByPercent.SetValue(True)
        elif model.scoreByTrueSkill:
            self.scoreByTrueSkill.SetValue(True)
        else:
            self.scoreByPoints.SetValue(True)
        self.fixEnable()

    def commit(self):
        self.grid.SaveEditControlValue()
        self.grid.DisableCellEditControl(
        )  # Make sure the current edit is committed.
        pointsList = []
        for row in xrange(self.grid.GetNumberRows()):
            if (self.grid.GetCellValue(row, self.NameCol).strip()):
                pointsList.append((
                    self.grid.GetCellValue(row, self.NameCol),
                    self.grid.GetCellValue(row, self.OldNameCol),
                    self.grid.GetCellValue(row, self.PointsCol),
                    self.grid.GetCellValue(row, self.ParticipationCol),
                    self.grid.GetCellValue(row, self.DNFCol),
                ))

        model = SeriesModel.model
        model.setPoints(pointsList)

        modelUpdate = {
            'considerPrimePointsOrTimeBonus':
            self.considerPrimePointsOrTimeBonus.GetValue(),
            'bestResultsToConsider':
            self.bestResultsToConsider.GetSelection(),
            'mustHaveCompleted':
            self.mustHaveCompleted.GetSelection(),
            'useMostEventsCompleted':
            self.mostEventsCompleted.GetValue(),
            'numPlacesTieBreaker':
            self.numPlacesTieBreaker.GetSelection(),
            'scoreByTime':
            self.scoreByTime.GetValue(),
            'scoreByPercent':
            self.scoreByPercent.GetValue(),
            'scoreByTrueSkill':
            self.scoreByTrueSkill.GetValue(),
        }

        for attr, value in modelUpdate.iteritems():
            if getattr(model, attr) != value:
                setattr(model, attr, value)
                model.changed = True
Exemple #7
0
class RestartDialog(wx.Dialog):
    def __init__(self, parent, id=wx.ID_ANY):
        wx.Dialog.__init__(self,
                           parent,
                           id,
                           "Restart",
                           style=wx.DEFAULT_DIALOG_STYLE | wx.TAB_TRAVERSAL)

        font = GetFont()

        sizer = wx.BoxSizer(wx.VERTICAL)

        self.SetBackgroundColour(wx.WHITE)

        bitmap = wx.Bitmap(os.path.join(Utils.getImageFolder(), 'refresh.png'),
                           wx.BITMAP_TYPE_PNG)
        restartBitmap = wx.StaticBitmap(self, wx.ID_ANY, bitmap)

        title = wx.StaticText(self, label='Restart Status Changes')
        title.SetFont(font)
        self.titleText = title

        hsTitle = wx.BoxSizer(wx.HORIZONTAL)
        hsTitle.Add(restartBitmap, 0, flag=wx.EXPAND | wx.ALL, border=4)
        hsTitle.Add(title, 0, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=4)

        sizer.Add(hsTitle, flag=wx.EXPAND)

        self.headerNames = ['Bib', 'Name', 'Team', 'Status    ']
        self.iColStatus = len(self.headerNames) - 1

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.EnableReorderRows(False)
        self.grid.SetRowLabelSize(0)
        self.grid.CreateGrid(0, len(self.headerNames))

        sizer.Add(self.grid, 1, flag=wx.ALL | wx.EXPAND, border=4)

        hs = wx.BoxSizer(wx.HORIZONTAL)
        self.okButton = MakeRoundButton(self, 'OK')
        self.cancelButton = MakeRoundButton(self, 'Cancel', isCancel)
        hs.Add(self.cancelButton, flag=wx.ALL, border=4)
        hs.AddStretchSpacer()
        hs.Add(self.okButton, flag=wx.ALL, border=4)

        self.okButton.Bind(wx.EVT_BUTTON, self.onOK)
        self.cancelButton.Bind(wx.EVT_BUTTON, self.onCancel)

        sizer.Add(hs, 0, flag=wx.EXPAND)

        self.SetSizer(sizer)

    def refresh(self, event):
        self.event = event

        start = event.starts[-1]
        state = event.competition.state

        font = GetFont()

        startPositions = start.startPositions
        Utils.AdjustGridSize(self.grid, rowsRequired=len(startPositions))

        self.grid.SetLabelFont(font)
        for col in six.moves.range(self.grid.GetNumberCols()):
            self.grid.SetColLabelValue(col, self.headerNames[col])
            attr = gridlib.GridCellAttr()
            attr.SetFont(font)
            if self.headerNames[col] == 'Bib':
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
                attr.SetReadOnly(True)
            elif col == 1 or col == 2:
                attr.SetReadOnly(True)
            elif self.headerNames[col].startswith('Status'):
                if len(start.getRemainingComposition()) > 2:
                    choices = ['DQ', 'DNF', '']
                    self.titleText.SetLabel(u'Restart Status Change')
                else:
                    choices = ['Inside', '']
                    self.titleText.SetLabel(u'Restart Position Change')
                attr.SetEditor(gridlib.GridCellChoiceEditor(choices=choices))
            self.grid.SetColAttr(col, attr)

        for row, p in enumerate(startPositions):
            rider = state.labels[p]
            for col, v in enumerate(
                [rider.bib, rider.full_name, rider.team, '']):
                self.grid.SetCellValue(row, col, u' {}'.format(v))

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

        self.GetSizer().Layout()
        self.GetSizer().Fit(self)

        self.CentreOnParent(wx.BOTH)
        self.SetFocus()

    def commit(self):
        places = []
        for row in six.moves.range(self.grid.GetNumberRows()):
            bib = self.grid.GetCellValue(row, 0)
            status = self.grid.GetCellValue(row, self.iColStatus)
            warning = self.grid.GetCellValue(row, self.iColWarning)
            relegation = self.grid.GetCellValue(row, self.iColRelegation)
            places.append((bib, status, warning, relegation))

        start = self.event.starts[-1]
        start.setPlaces(places)
        start.restartRequired = True
        self.event.propagate()
        Model.model.competition.propagate()
        Model.model.setChanged(True)
        Utils.setTitle()

    def onOK(self, event):
        self.commit()
        self.EndModal(wx.ID_OK)

    def onCancel(self, event):
        self.EndModal(wx.ID_CANCEL)
Exemple #8
0
class EventFinishOrderConfirmDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self, parent)

        self.SetBackgroundColour(wx.WHITE)

        vs = wx.BoxSizer(wx.VERTICAL)

        font = GetFont()

        bitmap = wx.Bitmap(
            os.path.join(Utils.getImageFolder(), 'checkered_flag_wavy.png'),
            wx.BITMAP_TYPE_PNG)
        flagBitmap = wx.StaticBitmap(self, wx.ID_ANY, bitmap)

        title = wx.StaticText(self, label='Confirm Event Result')
        title.SetFont(
            wx.Font((0, int(FontSize * 1.5)), wx.FONTFAMILY_SWISS,
                    wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))

        hsTitle = wx.BoxSizer(wx.HORIZONTAL)
        hsTitle.Add(flagBitmap, 0, flag=wx.EXPAND | wx.ALL, border=4)
        hsTitle.Add(title, 0, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=4)

        vs.Add(hsTitle, flag=wx.EXPAND)

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.EnableReorderRows(False)
        self.grid.CreateGrid(2, 6)
        self.grid.SetRowLabelSize(0)

        self.grid.SetLabelFont(font)

        vs.Add(self.grid, 1, flag=wx.ALL | wx.EXPAND, border=4)

        self.okButton = MakeRoundButton(self, 'OK')
        self.okButton.Bind(wx.EVT_BUTTON, self.onOK)

        self.cancelButton = MakeRoundButton(self, 'Cancel', isCancel)
        self.cancelButton.Bind(wx.EVT_BUTTON, self.onCancel)

        hs = wx.BoxSizer(wx.HORIZONTAL)
        hs.Add(self.okButton, 0, flag=wx.ALL | wx.EXPAND, border=4)
        hs.AddStretchSpacer()
        hs.Add(self.cancelButton, 0, flag=wx.ALL | wx.EXPAND, border=4)

        vs.Add(hs, 0, flag=wx.ALL | wx.EXPAND, border=4)

        self.SetSizer(vs)

    def refresh(self, grid):
        font = GetFont()

        Utils.AdjustGridSize(self.grid,
                             rowsRequired=grid.GetNumberRows(),
                             colsRequired=grid.GetNumberCols() + 1)
        self.grid.SetColLabelValue(0, 'Pos')
        attr = gridlib.GridCellAttr()
        attr.SetFont(font)
        attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
        attr.SetReadOnly(True)
        self.grid.SetColAttr(0, attr)

        iColStatus = None
        for col in six.moves.range(grid.GetNumberCols()):
            headerName = grid.GetColLabelValue(col)
            self.grid.SetColLabelValue(col + 1, headerName)
            attr = gridlib.GridCellAttr()
            attr.SetFont(font)
            if headerName == 'Bib':
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
            elif headerName.startswith('Time'):
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
            elif headerName.startswith('Status'):
                iColStatus = col
            elif headerName.startswith('Warn'):
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                attr.SetRenderer(gridlib.GridCellBoolRenderer())
            elif headerName.startswith('Rel'):
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                attr.SetRenderer(gridlib.GridCellBoolRenderer())
            attr.SetReadOnly(True)
            self.grid.SetColAttr(col + 1, attr)

        results = [[
            grid.GetCellValue(row, col)
            for col in six.moves.range(grid.GetNumberCols())
        ] for row in six.moves.range(grid.GetNumberRows())]
        results.sort(key=lambda x: x[iColStatus])

        for row in six.moves.range(grid.GetNumberRows()):
            self.grid.SetCellValue(row, 0, u'{}'.format(row + 1))
            for col in six.moves.range(grid.GetNumberCols()):
                v = results[row][col]
                self.grid.SetCellValue(row, col + 1, v if v != '0.000' else '')

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

        self.GetSizer().Layout()
        self.GetSizer().Fit(self)

        self.CentreOnParent(wx.BOTH)
        self.SetFocus()

    def onOK(self, event):
        self.EndModal(wx.ID_OK)

    def onCancel(self, event):
        self.EndModal(wx.ID_CANCEL)
Exemple #9
0
class EventSelect(EnablePanel):
    def __init__(self, parent):
        EnablePanel.__init__(self, parent)
        self.box = wx.StaticBox(self, label=u'Available Events')
        boxSizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)

        self.SetBackgroundColour(wx.WHITE)

        self.events = []
        self.event = None

        self.activeBar = EnableBar(self)
        self.activeBar.SetToolTip(
            wx.ToolTip(u'\n'.join([
                u'Click on an available Event in the table.',
                u'Then press Select.',
            ])))

        self.selectButton = MakeRoundButton(self, 'Select', isSelect)

        self.headerNames = ['Event', 'Bib', 'Name', 'Team', 'In', 'Out']
        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.CreateGrid(0, len(self.headerNames))
        self.grid.EnableReorderRows(False)
        self.grid.SetRowLabelSize(40)
        self.grid.SetSelectionMode(gridlib.Grid.SelectRows)

        font = GetFont()
        self.grid.SetLabelFont(font)
        for col in six.moves.range(self.grid.GetNumberCols()):
            self.grid.SetColLabelValue(col, self.headerNames[col])
            attr = gridlib.GridCellAttr()
            attr.SetFont(font)
            attr.SetRenderer(GridCellMultiLineStringRenderer())
            attr.SetReadOnly(True)
            if self.headerNames[col] == 'Bib':
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
            self.grid.SetColAttr(col, attr)

        self.clock = Clock(self, size=(128, 128))
        self.clock.SetBackgroundColour(wx.WHITE)

        boxSizer.Add(self.activeBar, 0, flag=wx.ALL | wx.EXPAND, border=4)
        vs = wx.BoxSizer(wx.VERTICAL)
        vs.AddSpacer(
            int(self.grid.GetColLabelSize() + FontSize * 1.15 -
                RoundButtonSize / 2))
        vs.Add(self.selectButton,
               flag=wx.LEFT | wx.RIGHT | wx.BOTTOM,
               border=4)
        boxSizer.Add(vs, 0, flag=wx.ALL, border=4)
        boxSizer.Add(self.grid, 1, flag=wx.ALL | wx.EXPAND, border=4)
        boxSizer.Add(self.clock, 0, flag=wx.ALL, border=4)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(boxSizer, 1, flag=wx.EXPAND)
        self.SetSizer(sizer)

    def SetEnable(self, enable):
        EnablePanel.SetEnable(self, enable)
        for b, t in [(self.selectButton, isSelect)]:
            EnableRoundButton(b, enable, t)
        self.activeBar.SetBackgroundColour(
            wx.Colour(0, 128, 0) if enable else wx.WHITE)
        self.Refresh()

    def refresh(self):
        self.grid.ClearGrid()
        model = Model.model
        if not model:
            return

        self.events = [e for t, s, e in model.competition.getCanStart()]
        self.events.sort(
            key=lambda e: (0 if e == self.event else 1, e.tournament.i, e.
                           system.i, e.getHeat(), e.i))

        Utils.AdjustGridSize(self.grid, rowsRequired=len(self.events))
        for row, e in enumerate(self.events):
            for col, v in enumerate([
                    e.multi_line_name, e.multi_line_bibs,
                    e.multi_line_rider_names, e.multi_line_rider_teams,
                    e.multi_line_inlabels, e.multi_line_outlabels
            ]):
                self.grid.SetCellValue(row, col, u' {}'.format(v))
            if e.system != self.events[0].system:
                for col in six.moves.range(self.grid.GetNumberCols()):
                    self.grid.SetCellBackgroundColour(
                        row, col, InactiveBackgroundColour)
            else:
                for col in six.moves.range(self.grid.GetNumberCols()):
                    self.grid.SetCellBackgroundColour(row, col, wx.WHITE)

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

        if self.events:
            self.grid.SelectRow(0)
Exemple #10
0
class Primes( wx.Panel ):
	def __init__( self, parent, id=wx.ID_ANY, size=wx.DefaultSize ):
		super(Primes, self).__init__( parent, id, size=size )
		
		self.state = RaceInputState()
		
		vsOverall = wx.BoxSizer( wx.VERTICAL )
		
		#---------------------------------------------------------------
		self.colNameFields = (
			(_('Prime For'),			'effortType',	's'),
			(_('or Custom'),			'effortCustom',	's'),
			(_('Position'),				'position',		'i'),
			(_('Laps\nTo Go'),			'lapsToGo',		'i'),
			(_('Sponsor'),				'sponsor', 		's'),
			(_('Cash'),					'cash', 		'f'),
			(_('Merchandise'),			'merchandise', 	's'),
			(_('Points'),				'points', 		'i'),
			(_('Time\nBonus'),			'timeBonus', 	't'),
			(_('Winner\nBib'),			'winnerBib',	'i'),
			(u'',						'winnerInfo',	's'),
		)
		self.colnames = [colName for colName, fieldName, dataType in self.colNameFields]
		self.iCol = dict( (fieldName, i) for i, (colName, fieldName, dataType) in enumerate(self.colNameFields) if fieldName )
		self.grid = ReorderableGrid( self )
		self.grid.CreateGrid( 0, len(self.colNameFields) )
		GetTranslation = _
		for col, (colName, fieldName, dataType) in enumerate(self.colNameFields):
			self.grid.SetColLabelValue( col, colName )
			attr = wx.grid.GridCellAttr()
			if fieldName == 'effortType':
				attr.SetEditor( wx.grid.GridCellChoiceEditor(choices=[GetTranslation(name) for code, name in EffortChoices]) )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'position':
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'winnerInfo':
				attr.SetReadOnly( True )
			elif dataType == 'i':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=0) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=0) )
			elif dataType == 'f':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=2) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=2) )
			elif dataType == 't':
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				attr.SetEditor( TimeEditor() )
			
			self.grid.SetColAttr( col, attr )
			if fieldName == 'lapsToGo':
				self.lapsToGoCol = col
		
		self.grid.Bind( wx.grid.EVT_GRID_CELL_CHANGE, self.onCellChange )
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )

		#---------------------------------------------------------------
		self.photosButton = wx.Button( self, label=u'{}...'.format(_('Photos')) )
		self.photosButton.Bind( wx.EVT_BUTTON, self.onPhotos )
		self.finishStrip = wx.Button( self, label=u'{}...'.format(_('Finish Strip')) )
		self.finishStrip.Bind( wx.EVT_BUTTON, self.onFinishStrip )
		self.history = wx.Button( self, label=u'{}...'.format(_('Passings')) )
		self.history.Bind( wx.EVT_BUTTON, self.onHistory )
		
		self.newButton = wx.Button( self, id=wx.ID_NEW )
		self.newButton.SetToolTip( wx.ToolTip(_('Create a new Prime')) )
		self.newButton.Bind( wx.EVT_BUTTON, self.onNew )
		self.nextPositionButton = wx.Button( self, label=('Next Position') )
		self.nextPositionButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime for the Next Position')) )
		self.nextPositionButton.Bind( wx.EVT_BUTTON, self.onNextPosition )
		self.nextPrimeButton = wx.Button( self, label=('Next Prime') )
		self.nextPrimeButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime')) )
		self.nextPrimeButton.Bind( wx.EVT_BUTTON, self.onNextPrime )
		self.deleteButton = wx.Button( self, id=wx.ID_DELETE )
		self.deleteButton.SetToolTip( wx.ToolTip(_('Delete a Prime')) )
		self.deleteButton.Bind( wx.EVT_BUTTON, self.onDelete )
		hsButtons = wx.BoxSizer( wx.HORIZONTAL )
		hsButtons.Add( self.photosButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.finishStrip, flag=wx.ALL, border=4 )
		hsButtons.Add( self.history, flag=wx.ALL, border=4 )
		hsButtons.AddStretchSpacer()
		hsButtons.Add( self.newButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPositionButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPrimeButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.deleteButton, flag=wx.ALL, border=4 )
		
		#---------------------------------------------------------------
		
		vsOverall.Add( self.grid, 1, flag=wx.EXPAND|wx.ALL, border=4 )
		vsOverall.Add( hsButtons, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=4 )
		self.SetSizer( vsOverall )
	
	def onCellChange( self, event ):
		row, col = event.GetRow(), event.GetCol()
		colName = self.colNameFields[col][1]
		GetTranslation = _
		if colName == 'effortCustom':
			if self.grid.GetCellValue(row, col).strip():
				self.grid.SetCellValue( row, col-1, GetTranslation('Custom') )
		elif colName == 'effortType':
			if self.grid.GetCellValue(row, col) != 'Custom':
				self.grid.SetCellValue( row, col+1, u'' )
		elif colName == 'winnerBib':
			bib = int( u''.join(c for c in self.grid.GetCellValue(row, col) if c.isdigit()) )
			self.grid.SetCellValue( row, col+1, getWinnerInfo(bib) )
		
		wx.CallAfter( self.grid.AutoSizeColumns, False )
	
	def getT( self ):
		race = Model.race
		if not race:
			return 0
		row = self.grid.GetGridCursorRow()
		if row is None or row < 0:
			return
		lapsToGo = int( u''.join(c for c in self.grid.GetCellValue(row, self.lapsToGoCol) if c.isdigit()) )
		tMax = 0.0
		for rr in GetResults(None):
			try:
				tMax = min( tMax, rr.raceTimes[-1-lapsToGo] )
			except IndexError:
				pass
		return tMax
		
	def onPhotos( self, event ):
		mainWin = Utils.getMainWin()
		if not mainWin:
			return
		mainWin.photoDialog.SetT( self.getT() )
		mainWin.photoDialog.Show()
		
	def onFinishStrip( self, event ):
		ShowFinishStrip( self, self.getT() )
	
	def onHistory( self, event ):
		mainWin = Utils.getMainWin()
		if not mainWin:
			return
		mainWin.openMenuWindow( 'Passings' )
	
	def selectGridRow( self, row ):
		self.grid.SelectRow( row )
		self.grid.SetGridCursor( row, 0 )
		self.grid.ShowCellEditControl()
	
	def onNew( self, event ):
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a Prime.'), _('Missing Race') )
			return
		self.commit()
		race.primes = getattr(race, 'primes', [])
		rowNew = len( race.primes )
		race.primes.append( {} )
		self.updateGrid()
		self.selectGridRow( rowNew )
	
	def onNextPosition( self, event ):
		rowNext = self.grid.GetGridCursorRow()
		if rowNext is None or rowNext < 0:
			return
		self.commit()
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a next Prime.'), _('Missing Race') )
			return
		
		nextPrime = race.primes[rowNext].copy()
		
		nextPoints = {
			(1, 5):	3,
			(2, 3): 2,
			(3, 2): 1,
		}.get( (nextPrime['position'], nextPrime['points']), None )
		
		if nextPoints is not None:
			nextPrime['points'] = nextPoints
		
		try:
			nextPrime['position'] += 1
		except:
			pass
		nextPrime['winnerBib'] = None
		race.primes = race.primes[:rowNext+1] + [nextPrime] + race.primes[rowNext+1:]
		self.updateGrid()
		self.selectGridRow( rowNext + 1 )
	
	def onNextPrime( self, event ):
		rowNext = self.grid.GetGridCursorRow()
		if rowNext is None or rowNext < 0:
			return
		self.commit()
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a next Prime.'), _('Missing Race') )
			return
		nextPrime = race.primes[rowNext].copy()
		nextPrime['position'] = 1
		if nextPrime['points']:
			nextPrime['points'] = 5
		if nextPrime['lapsToGo'] > 0:
			nextPrime['lapsToGo'] -= 1
		nextPrime['winnerBib'] = None
		race.primes = race.primes[:rowNext+1] + [nextPrime] + race.primes[rowNext+1:]
		self.updateGrid()
		self.selectGridRow( rowNext + 1 )
		
	def onDelete( self, event ):
		rowDelete = self.grid.GetGridCursorRow()
		if rowDelete is None or rowDelete < 0:
			return
		self.commit()
		race = Model.race
		if race and Utils.MessageOKCancel( self, u'{}: {} ?'.format(_('Delete Prime'), rowDelete+1), _('Confirm Delete Primes') ):
			race.primes = getattr(race, 'primes', [])
			try:
				del race.primes[rowDelete]
			except Exception as e:
				return
			self.updateGrid()
			if race.primes:
				self.grid.SetGridCursor( rowDelete, 0 )
		
	def getSponsors( self ):
		race = Model.race
		if not race:
			return []
		sponsors = [prime.get('sponsor', u'') for prime in getattr(race, 'primes', [])] + [race.organizer]
		sponsors = [s for s in sponsors if s]
		return sponsors
		
	def getMerchandise( self ):
		race = Model.race
		if not race:
			return []
		merchandise = [prime.get('merchandise', u'') for prime in getattr(race, 'primes', [])]
		merchandise = [m for m in merchandise if m]
		return merchandise
	
	def setRow( self, prime, row, updateGrid=True ):
		GetTranslation = _
		
		data = []
		for col, (name, attr, dataType) in enumerate(self.colNameFields):
			if attr == 'effortType':
				effortType = prime.get('effortType', 'Pack')
				v = GetTranslation(effortType)
			elif attr == 'position':
				position = prime.get('position', 1)
				v = u'' if position == 0 else unicode(position)
			elif attr == 'points':
				points = prime.get('points', 0)
				v = u'' if points == 0 else unicode(points)
			elif attr == 'winnerBib':
				winnerBib = prime.get('winnerBib', None)
				v = u'' if not winnerBib else unicode(winnerBib)
			elif attr == 'winnerInfo':
				v = getWinnerInfo(winnerBib)
			elif dataType == 'f':
				f = prime.get(attr, 0.0)
				v = u'{:.2f}'.format(f) if f else u''
			elif dataType == 't':
				t = prime.get(attr, 0.0)
				v = Utils.formatTime(t, forceHours=True, twoDigitHours=True) if t != 0 else u''
			else:
				v = unicode(prime.get(attr, u''))
			if updateGrid:
				self.grid.SetCellValue( row, col, v )
			data.append( v )
		
		return data
	
	def getRow( self, row ):
		values = {}
		for col, (name, attr, dataType) in enumerate(self.colNameFields):
			v = self.grid.GetCellValue( row, col ).strip()
			if dataType == 'i':
				v = u''.join( c for c in v if c.isdigit() )
				v = int( v or 0 )
			elif dataType == 'f':
				v = u''.join( c for c in v if c.isdigit() or c == '.')
				v = float( v or 0.0 )
			elif dataType == 't':
				v = Utils.StrToSeconds( v )
				
			if attr == 'position' and not v:
				v = 1
			
			values[attr] = v
		
		GetTranslation = _
		for code, name in EffortChoices:
			if values['effortType'] == GetTranslation(name):
				values['effortType'] = name
				break
		
		if values['effortCustom']:
			values['effortType'] = 'Custom'
		return values
	
	def updateGrid( self ):
		race = Model.race
		if not race or not getattr(race, 'primes', None):
			self.grid.ClearGrid()
			return
		
		Utils.AdjustGridSize( self.grid, len(race.primes) )
		for row, prime in enumerate(race.primes):
			self.setRow( prime, row )
		
		self.grid.AutoSizeColumns( False )								# Resize to fit the column name.
		self.grid.AutoSizeRows( False )
	
	def refresh( self ):
		if self.state.changed():
			self.updateGrid()
		
	def commit( self ):
		self.grid.SaveEditControlValue()	# Make sure the current edit is committed.
		self.grid.DisableCellEditControl()
		race = Model.race
		if not race:
			return
		race.primes = [self.getRow(row) for row in xrange(self.grid.GetNumberRows())]
	def writeTeamGC():
		headers = (
			['Place', 'Team', 'Gap', 'Combined\nTime'] +
			['{}s'.format(Utils.ordinal(i+1)) for i in xrange(len(model.all_teams))] +
			['Best\nRider GC']
		)
		
		grid = ReorderableGrid( notebook )
		grid.CreateGrid( len(model.team_gc) + len(model.unranked_teams), len(headers) )
		grid.EnableReorderRows( False )
		
		for col, h in enumerate(headers):
			attr = gridlib.GridCellAttr()
			attr.SetReadOnly()
			if h != 'Team':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_CENTRE )
			grid.SetColAttr( col, attr )
			grid.SetColLabelValue( col, h )
		
		rowNum = 0
		leaderTime = None
		gapLast = None
		timeLast = None
		for place, tgc in enumerate(model.team_gc, 1):
			col = 0
			grid.SetCellValue( rowNum, col, unicode(place) ); col += 1
			grid.SetCellValue( rowNum, col, unicode(tgc[-1]) ); col += 1
			
			combinedTime = tgc[0].value
			if leaderTime is None:
				leaderTime = combinedTime
			gap = combinedTime - leaderTime
			grid.SetCellValue( rowNum, col, Utils.formatTime(gap, twoDigitHours=True) if gap != gapLast else sameGap ); col += 1
			gapLast = gap
			
			grid.SetCellValue( rowNum, col, Utils.formatTime(combinedTime, forceHours=True) if combinedTime != timeLast else sameTime)
			timeLast = combinedTime
			setComment( rowNum, col, formatContextList(tgc[0].context), {'width':512} )
			col += 1
			
			for i in xrange(1, len(tgc)-2):
				if tgc[i].value:
					grid.SetCellValue( rowNum, col, unicode(tgc[i].value) )
					setComment( rowNum, col, u'\n'.join(tgc[i].context), {'width':128} )
				col += 1
			
			grid.SetCellValue( rowNum, col, unicode(tgc[-2].value) )
			setComment( rowNum, col, formatContext(tgc[-2].context), {'width':256} )
			col += 1
			
			rowNum +=1
		
		for team in model.unranked_teams:
			col = 0
			grid.SetCellValue( rowNum, col, 'DNF' ); col += 1
			grid.SetCellValue( rowNum, col, team ); col += 1
			rowNum +=1
	
		grid.GetGridWindow().Bind(wx.EVT_MOTION, getCommentCallback(grid))
		grid.AutoSize()
		return grid
Exemple #12
0
class MainWin(wx.Frame):
    def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)):
        wx.Frame.__init__(self, parent, id, title, size=size)

        self.SetBackgroundColour(wx.Colour(240, 240, 240))

        self.fname = None
        self.updated = False
        self.firstTime = True
        self.lastUpdateTime = None
        self.sources = []
        self.errors = []

        self.filehistory = wx.FileHistory(16)

        dataDir = Utils.getHomeDir()
        configFileName = os.path.join(dataDir, 'CallupSeedingMgr.cfg')
        self.config = wx.Config(appName="CallupSeedingMgr",
                                vendorName="SmartCyclingSolutions",
                                localFilename=configFileName)

        self.filehistory.Load(self.config)

        ID_MENU_UPDATE = wx.NewIdRef()
        ID_MENU_HELP = wx.NewIdRef()
        self.menuBar = wx.MenuBar(wx.MB_DOCKABLE)
        if 'WXMAC' in wx.Platform:
            self.appleMenu = self.menuBar.OSXGetAppleMenu()
            self.appleMenu.SetTitle("CallupSeedingMgr")

            self.appleMenu.Insert(0, wx.ID_ABOUT, "&About")

            self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT)

            self.editMenu = wx.Menu()
            self.editMenu.Append(
                wx.MenuItem(self.editMenu, ID_MENU_UPDATE, "&Update"))

            self.Bind(wx.EVT_MENU, self.doUpdate, id=ID_MENU_UPDATE)
            self.menuBar.Append(self.editMenu, "&Edit")

            self.helpMenu = wx.Menu()
            self.helpMenu.Append(
                wx.MenuItem(self.helpMenu, ID_MENU_HELP, "&Help"))

            self.menuBar.Append(self.helpMenu, "&Help")
            self.Bind(wx.EVT_MENU, self.onTutorial, id=ID_MENU_HELP)

        else:
            self.fileMenu = wx.Menu()
            self.fileMenu.Append(
                wx.MenuItem(self.fileMenu, ID_MENU_UPDATE, "&Update"))
            self.fileMenu.Append(wx.ID_EXIT)
            self.Bind(wx.EVT_MENU, self.doUpdate, id=ID_MENU_UPDATE)
            self.Bind(wx.EVT_MENU, self.onClose, id=wx.ID_EXIT)
            self.menuBar.Append(self.fileMenu, "&File")
            self.helpMenu = wx.Menu()
            self.helpMenu.Insert(0, wx.ID_ABOUT, "&About")
            self.helpMenu.Insert(1, ID_MENU_HELP, "&Help")
            self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT)
            self.Bind(wx.EVT_MENU, self.onTutorial, id=ID_MENU_HELP)
            self.menuBar.Append(self.helpMenu, "&Help")

        self.SetMenuBar(self.menuBar)

        inputBox = wx.StaticBox(self, label=_('Input'))
        inputBoxSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
        self.fileBrowse = filebrowse.FileBrowseButtonWithHistory(
            self,
            labelText=_('Excel File'),
            buttonText=('Browse...'),
            startDirectory=os.path.expanduser('~'),
            fileMask=
            'Excel Spreadsheet (*.xlsx; *.xlsm; *.xls)|*.xlsx; *.xlsml; *.xls',
            size=(400, -1),
            history=lambda: [
                self.filehistory.GetHistoryFile(i)
                for i in range(self.filehistory.GetCount())
            ],
            changeCallback=self.doChangeCallback,
        )
        inputBoxSizer.Add(self.fileBrowse,
                          0,
                          flag=wx.EXPAND | wx.ALL,
                          border=4)

        horizontalControlSizer = wx.BoxSizer(wx.HORIZONTAL)

        #-------------------------------------------------------------------------------------------
        verticalControlSizer = wx.BoxSizer(wx.VERTICAL)

        self.useUciIdCB = wx.CheckBox(self,
                                      label=_("Use UCI ID (assume no errors)"))
        self.useUciIdCB.SetValue(True)
        verticalControlSizer.Add(self.useUciIdCB, flag=wx.ALL, border=4)

        self.useLicenseCB = wx.CheckBox(
            self, label=_("Use License (assume no errors)"))
        self.useLicenseCB.SetValue(True)
        verticalControlSizer.Add(self.useLicenseCB, flag=wx.ALL, border=4)

        self.soundalikeCB = wx.CheckBox(
            self, label=_("Match misspelled names with Sound-Alike"))
        self.soundalikeCB.SetValue(True)
        verticalControlSizer.Add(self.soundalikeCB, flag=wx.ALL, border=4)

        self.callupSeedingRB = wx.RadioBox(
            self,
            style=wx.RA_SPECIFY_COLS,
            majorDimension=1,
            label=_("Sequence"),
            choices=[
                _("Callups: Highest ranked FIRST (Cyclo-cross, MTB)"),
                _("Seeding: Highest ranked LAST (Time Trials)"),
            ],
        )
        verticalControlSizer.Add(self.callupSeedingRB,
                                 flag=wx.EXPAND | wx.ALL,
                                 border=4)
        verticalControlSizer.Add(wx.StaticText(
            self,
            label=_('Riders with no criteria will be sequenced randomly.')),
                                 flag=wx.ALL,
                                 border=4)

        horizontalControlSizer.Add(verticalControlSizer, flag=wx.EXPAND)

        self.updateButton = RoundButton(self, size=(96, 96))
        self.updateButton.SetLabel(_('Update'))
        self.updateButton.SetFontToFitLabel()
        self.updateButton.SetForegroundColour(wx.Colour(0, 100, 0))
        self.updateButton.Bind(wx.EVT_BUTTON, self.doUpdate)
        horizontalControlSizer.Add(self.updateButton, flag=wx.ALL, border=4)

        horizontalControlSizer.AddSpacer(48)

        vs = wx.BoxSizer(wx.VERTICAL)
        self.tutorialButton = wx.Button(self, label=_('Help/Tutorial...'))
        self.tutorialButton.Bind(wx.EVT_BUTTON, self.onTutorial)
        vs.Add(self.tutorialButton, flag=wx.ALL, border=4)
        branding = wx.adv.HyperlinkCtrl(
            self,
            id=wx.ID_ANY,
            label=u"Powered by CrossMgr",
            url=u"http://www.sites.google.com/site/crossmgrsoftware/")
        vs.Add(branding, flag=wx.ALL, border=4)
        horizontalControlSizer.Add(vs)

        inputBoxSizer.Add(horizontalControlSizer, flag=wx.EXPAND)

        self.sourceList = wx.ListCtrl(self, style=wx.LC_REPORT, size=(-1, 100))
        inputBoxSizer.Add(self.sourceList, flag=wx.ALL | wx.EXPAND, border=4)
        self.sourceList.InsertColumn(0, "Sheet")
        self.sourceList.InsertColumn(1, "Data Columns and Derived Information")
        self.sourceList.InsertColumn(2, "Key Fields")
        self.sourceList.InsertColumn(3, "Rows", wx.LIST_FORMAT_RIGHT)
        self.sourceList.InsertColumn(4, "Errors/Warnings",
                                     wx.LIST_FORMAT_RIGHT)
        self.sourceList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected)

        instructions = [
            _('Drag-and-Drop the row numbers on the Left to change the sequence.'
              ),
            _('Click on Points or Position cells for details.'),
            _('Orange Cells: Multiple Matches.  Click on the cell to see what you need to fix in the spreadsheet.'
              ),
            _('Yellow Cells: Soundalike Matches.  Click on the cell to validate if the names are matched correctly.'
              ),
        ]

        self.grid = ReorderableGrid(self)
        self.grid.CreateGrid(0, 1)
        self.grid.SetColLabelValue(0, u'')
        self.grid.EnableDragRowSize(False)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.onGridCellClick)
        #self.grid.Bind( wx.EVT_MOTION, self.onMouseOver )

        outputBox = wx.StaticBox(self, label=_('Output'))
        outputBoxSizer = wx.StaticBoxSizer(outputBox, wx.VERTICAL)

        hs = wx.BoxSizer(wx.HORIZONTAL)
        self.excludeUnrankedCB = wx.CheckBox(
            self, label=_("Exclude riders with no ranking info"))
        hs.Add(self.excludeUnrankedCB,
               flag=wx.ALL | wx.ALIGN_CENTRE_VERTICAL,
               border=4)
        hs.AddSpacer(24)
        hs.Add(wx.StaticText(self, label=_("Output:")),
               flag=wx.ALL | wx.ALIGN_CENTRE_VERTICAL,
               border=4)
        self.topRiders = wx.Choice(self,
                                   choices=[
                                       _('All Riders'),
                                       _('Top 5'),
                                       _('Top 10'),
                                       _('Top 15'),
                                       _('Top 20'),
                                       _('Top 25')
                                   ])
        self.topRiders.SetSelection(0)
        hs.Add(self.topRiders, flag=wx.ALIGN_CENTRE_VERTICAL)

        self.saveAsExcel = wx.Button(self, label=_('Save as Excel...'))
        self.saveAsExcel.Bind(wx.EVT_BUTTON, self.doSaveAsExcel)
        hs.AddSpacer(48)
        hs.Add(self.saveAsExcel, flag=wx.ALL, border=4)

        outputBoxSizer.Add(hs)

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(inputBoxSizer, flag=wx.EXPAND | wx.ALL, border=4)
        for i, instruction in enumerate(instructions):
            flag = wx.LEFT | wx.RIGHT
            if i == len(instructions) - 1:
                flag |= wx.BOTTOM
            mainSizer.Add(wx.StaticText(self, label=instruction),
                          flag=flag,
                          border=8)
        mainSizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=4)
        mainSizer.Add(outputBoxSizer, flag=wx.EXPAND | wx.ALL, border=4)

        self.SetSizer(mainSizer)

    def onClose(self, event):
        wx.Exit()

    def OnAboutBox(self, e):
        description = """CallupSeedingMgr is an Seeding Manager for CrossMgr
	"""

        licence = """CallupSeedingMgr free software; you can redistribute 
	it and/or modify it under the terms of the GNU General Public License as 
	published by the Free Software Foundation; either version 2 of the License, 
	or (at your option) any later version.

	CallupSeedingMgr is distributed in the hope that it will be useful, 
	but WITHOUT ANY WARRANTY; without even the implied warranty of 
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
	See the GNU General Public License for more details. You should have 
	received a copy of the GNU General Public License along with File Hunter; 
	if not, write to the Free Software Foundation, Inc., 59 Temple Place, 
	Suite 330, Boston, MA  02111-1307  USA"""

        info = wx.adv.AboutDialogInfo()

        crossMgrPng = Utils.getImageFolder() + '/CallupSeedingMgr.png'
        info.SetIcon(wx.Icon(crossMgrPng, wx.BITMAP_TYPE_PNG))
        info.SetName('CallupSeedingMgr')
        info.SetVersion(AppVerName.split(' ')[1])
        info.SetDescription(description)
        info.SetCopyright('(C) 2020 Edward Sitarski')
        info.SetWebSite('http://www.sites.google.com/site/crossmgrsoftware/')
        info.SetLicence(licence)

        wx.adv.AboutBox(info, self)

    def onTutorial(self, event):
        if not Utils.MessageOKCancel(
                self, u"\n".join([
                    _("Launch the CallupSeedingMgr Tutorial."),
                    _("This open a sample Excel input file created into your home folder."
                      ),
                    _("This data in this sheet is made-up, although it does include some current rider's names."
                      ),
                    u"",
                    _("It will also open the Tutorial page in your browser.  If you can't see your browser, make sure you bring to the front."
                      ),
                    u"",
                    _("Continue?"),
                ])):
            return
        try:
            fname_excel = MakeExampleExcel()
            self.fileBrowse.SetValue(fname_excel)
        except Exception as e:
            Utils.MessageOK(
                self, u'{}\n\n{}\n\n{}'.format(
                    u'Problem creating Excel sheet.', e,
                    _('If the Excel file is open, please close it and try again'
                      )))
        self.doUpdate(event)
        Utils.LaunchApplication([
            fname_excel,
            os.path.join(Utils.getHtmlDocFolder(), 'Tutorial.html')
        ])

    def onItemSelected(self, event):
        currentItem = event.GetIndex()
        errors = self.errors[(currentItem + len(self.errors) - 1) %
                             len(self.errors)]
        if not errors:
            return
        dialog = ErrorDialog(self, errors=errors)
        dialog.ShowModal()
        dialog.Destroy()
        event.Skip()

    def onMouseOver(self, event):
        '''
		Method to calculate where the mouse is pointing and
		then set the tooltip dynamically.
		'''

        # Use CalcUnscrolledPosition() to get the mouse position
        # within the
        # entire grid including what's offscreen
        x, y = self.grid_area.CalcUnscrolledPosition(event.GetX(),
                                                     event.GetY())

        coords = self.grid_area.XYToCell(x, y)
        # you only need these if you need the value in the cell
        row = coords[0]
        col = coords[1]
        iRecord = int(
            self.grid.GetCellValue(row,
                                   self.grid.GetNumberCols() - 1))
        iSource = self.grid.GetNumberCols() - col
        try:
            v = self.callup_results[iRecord][-iSource + 1]
        except IndexError:
            event.Skip()
            return

        try:
            status = v.get_status()
        except AttributeError:
            event.Skip()
            return

        if status == v.NoMatch:
            event.Skip()
            return

        message = u'{}\n\n{}'.format(
            v.get_message(),
            _('Make changes in the Spreadsheet (if necessary), then press "Update" to refresh the screen.'
              ),
        )
        event.GetEventObject().SetToolTipString(message)
        event.Skip()

    def onGridCellClick(self, event):
        row = event.GetRow()
        col = event.GetCol()
        iRecord = int(
            self.grid.GetCellValue(row,
                                   self.grid.GetNumberCols() - 1))
        iSource = self.grid.GetNumberCols() - col
        try:
            v = self.callup_results[iRecord][-iSource + 1]
        except IndexError:
            return

        try:
            status = v.get_status()
        except AttributeError:
            return

        if status == v.NoMatch:
            return

        message = u'{}\n\n{}'.format(
            v.get_message(),
            _('Make changes in the Spreadsheet (if necessary), then press "Update" to refresh the screen.'
              ),
        )
        Utils.MessageOK(
            self, message,
            _('Soundalike Match')
            if status == v.SuccessSoundalike else _('Multiple Matches')
            if status == v.MultiMatch else _('Match Success'))

    def getTopRiders(self):
        i = self.topRiders.GetSelection()
        return 5 * i if i > 0 else 999999

    def getIsCallup(self):
        return self.callupSeedingRB.GetSelection() == 0

    def getIsSoundalike(self):
        return self.soundalikeCB.GetValue()

    def getUseUciId(self):
        return self.useUciIdCB.GetValue()

    def getUseLicense(self):
        return self.useLicenseCB.GetValue()

    def getOutputExcelName(self):
        fname = os.path.abspath(self.fname)
        dirname, basename = os.path.dirname(fname), os.path.basename(fname)
        fname_base, fname_suffix = os.path.splitext(basename)
        dirchild = 'CallupsOutput' if self.getIsCallup() else 'SeedingOutput'
        try:
            os.makedirs(os.path.join(dirname, dirchild))
        except Exception as e:
            pass
        fname_excel = os.path.join(
            dirname, dirchild,
            '{}{}{}'.format(fname_base,
                            '_Callups' if self.getIsCallup() else '_Seeding',
                            '.xlsx'))
        return fname_excel

    def doChangeCallback(self, event):
        fname = event.GetString()
        if not fname:
            self.setUpdated(False)
            return
        if fname != self.fname:
            wx.CallAfter(self.doUpdate, fnameNew=fname)

    def setUpdated(self, updated=True):
        self.updated = updated
        for w in [self.sourceList, self.grid, self.saveAsExcel]:
            w.Enable(updated)
        if not updated:
            self.sourceList.DeleteAllItems()
            Utils.DeleteAllGridRows(self.grid)

    def updateSourceList(self, sources=None, errors=None):
        self.sourceList.DeleteAllItems()
        sources = (sources or self.sources)
        errors = (errors or self.errors)
        if not sources:
            return

        def insert_source_info(source, errors, add_value_field=True):
            idx = self.sourceList.InsertItem(999999, source.sheet_name)
            fields = source.get_ordered_fields()
            if add_value_field and source.get_cmp_policy_field():
                fields = [source.get_cmp_policy_field()] + list(fields)
            self.sourceList.SetItem(idx, 1,
                                    u', '.join(make_title(f) for f in fields))
            match_fields = source.get_match_fields(
                sources[-1]) if source != sources[-1] else []
            self.sourceList.SetItem(
                idx, 2, u', '.join(make_title(f) for f in match_fields))
            self.sourceList.SetItem(idx, 3, u'{}'.format(len(source.results)))
            self.sourceList.SetItem(idx, 4, u'{}'.format(len(errors)))

        insert_source_info(sources[-1], errors[-1], False)
        for i, source in enumerate(sources[:-1]):
            insert_source_info(source, errors[i])

        for col in range(3):
            self.sourceList.SetColumnWidth(col, wx.LIST_AUTOSIZE)
        self.sourceList.SetColumnWidth(3, 52)
        self.sourceList.Refresh()

    def callbackUpdate(self, message):
        pass

    def doUpdate(self, event=None, fnameNew=None):
        try:
            self.fname = (fnameNew or event.GetString()
                          or self.fileBrowse.GetValue())
        except:
            self.fname = u''

        if not self.fname:
            Utils.MessageOK(
                self, _('Missing Excel file.  Please select an Excel file.'),
                _('Missing Excel File'))
            self.setUpdated(False)
            return

        if self.lastUpdateTime and (datetime.datetime.now() -
                                    self.lastUpdateTime).total_seconds() < 1.0:
            return

        try:
            with open(self.fname, 'rb') as f:
                pass
        except Exception as e:
            Utils.MessageOK(
                self,
                u'{}:\n\n    {}\n\n{}'.format(_('Cannot Open Excel file'),
                                              self.fname, e),
                _('Cannot Open Excel File'))
            self.setUpdated(False)
            return

        self.filehistory.AddFileToHistory(self.fname)
        self.filehistory.Save(self.config)

        wait = wx.BusyCursor()
        labelSave, backgroundColourSave = self.updateButton.GetLabel(
        ), self.updateButton.GetForegroundColour()

        try:
            self.registration_headers, self.callup_headers, self.callup_results, self.sources, self.errors = GetCallups(
                self.fname,
                soundalike=self.getIsSoundalike(),
                useUciId=self.getUseUciId(),
                useLicense=self.getUseLicense(),
                callbackfunc=self.updateSourceList,
                callbackupdate=self.callbackUpdate,
            )
        except Exception as e:
            traceback.print_exc()
            Utils.MessageOK(
                self, u'{}:\n\n    {}\n\n{}'.format(_('Excel File Error'),
                                                    self.fname, e),
                _('Excel File Error'))
            self.setUpdated(False)
            return

        self.setUpdated(True)

        self.updateSourceList()

        CallupResultsToGrid(
            self.grid,
            self.registration_headers,
            self.callup_headers,
            self.callup_results,
            is_callup=(self.callupSeedingRB.GetSelection() == 0),
            top_riders=self.getTopRiders(),
            exclude_unranked=self.excludeUnrankedCB.GetValue(),
        )
        self.GetSizer().Layout()
        self.lastUpdateTime = datetime.datetime.now()

    def doSaveAsExcel(self, event):
        if self.grid.GetNumberRows() == 0:
            return

        fname_excel = self.getOutputExcelName()
        if os.path.isfile(fname_excel):
            if not Utils.MessageOKCancel(
                    self,
                    u'"{}"\n\n{}'.format(fname_excel,
                                         _('File exists.  Replace?')),
                    _('Output Excel File Exists'),
            ):
                return

        user_sequence = [
            int(self.grid.GetCellValue(row,
                                       self.grid.GetNumberCols() - 1))
            for row in range(self.grid.GetNumberRows())
        ]
        user_callup_results = [self.callup_results[i] for i in user_sequence]

        try:
            CallupResultsToExcel(
                fname_excel,
                self.registration_headers,
                self.callup_headers,
                user_callup_results,
                is_callup=self.getIsCallup(),
                top_riders=self.getTopRiders(),
                exclude_unranked=self.excludeUnrankedCB.GetValue(),
            )
        except Exception as e:
            traceback.print_exc()
            Utils.MessageOK(
                self,
                u'{}: "{}"\n\n{}\n\n"{}"'.format(
                    _("Write Failed"), e,
                    _("If you have this file open, close it and try again."),
                    fname_excel),
                _("Excel Write Failed."),
                iconMask=wx.ICON_ERROR,
            )
            return

        webbrowser.open(fname_excel, new=2, autoraise=True)
Exemple #13
0
class Prizes(wx.Panel):
    rowsMax = 20

    def __init__(self, parent, id=wx.ID_ANY, size=wx.DefaultSize):
        super(Prizes, self).__init__(parent, id, size=size)

        vsOverall = wx.BoxSizer(wx.VERTICAL)

        self.grid = ReorderableGrid(self)
        self.grid.CreateGrid(0, 10)

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, self.onCellChange)

        #---------------------------------------------------------------

        vsOverall.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=4)
        self.SetSizer(vsOverall)

    def onCellChange(self, event):
        race = Model.race
        if not race:
            return
        row, col = event.GetRow(), event.GetCol()
        if col & 1:
            return
        categories = race.getCategories(startWaveOnly=False, publishOnly=True)
        if self.grid.GetNumberCols() != len(categories) * 2:
            Utils.AdjustGridSize(self.grid, self.rowsMax, len(categories) * 2)
        if row >= self.grid.GetNumberRows() or col >= self.grid.GetNumberCols(
        ):
            return
        self.copyToRace()
        try:
            category = categories[col // 2]
        except IndexError:
            return
        self.grid.SetCellValue(
            row, col + 1,
            self.getRecepient(self.grid.GetCellValue(row, col), row, category))
        wx.CallAfter(self.grid.AutoSizeColumns, False)

    def getRecepient(self, prize, row, category):
        if not prize:
            return ''
        name = ''
        results = GetResults(category)
        try:
            name = u'{}: {}'.format(results[row].num, results[row].full_name())
        except IndexError:
            pass
        return name

    def setCellPair(self, row, col, category):
        try:
            prize = getattr(category, 'prizes', [])[row]
        except IndexError:
            prize = ''
        self.grid.SetCellValue(row, col, prize)
        self.grid.SetCellValue(row, col + 1,
                               self.getRecepient(prize, row, category))

    def updateGrid(self):
        race = Model.race
        if not race:
            self.grid.ClearGrid()
            return
        categories = race.getCategories(startWaveOnly=False, publishOnly=True)
        Utils.AdjustGridSize(self.grid, self.rowsMax, len(categories) * 2)
        col = 0
        for category in categories:
            fullname = category.fullname
            ib = fullname.rfind('(')
            catname, catgender = fullname[:ib].strip(), fullname[ib:]
            colName = '{}\n{}'.format(catname, catgender)
            self.grid.SetColLabelValue(col, colName)
            attr = wx.grid.GridCellAttr()
            attr.SetReadOnly(False)
            attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
            self.grid.SetColAttr(col, attr)

            self.grid.SetColLabelValue(col + 1, _('Recipient'))
            attr = wx.grid.GridCellAttr()
            attr.SetReadOnly(True)
            attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
            attr.SetBackgroundColour(wx.Colour(152, 251, 152))
            self.grid.SetColAttr(col + 1, attr)

            for row in range(self.rowsMax):
                self.setCellPair(row, col, category)
            col += 2

        self.grid.AutoSizeColumns(False)  # Resize to fit the column name.
        self.grid.AutoSizeRows(False)

    def refresh(self):
        self.updateGrid()

    def copyToRace(self):
        race = Model.race
        if not race:
            return

        categories = race.getCategories(startWaveOnly=False, publishOnly=True)
        for i, category in enumerate(categories):
            prizes = []
            for row in range(self.rowsMax):
                v = self.grid.GetCellValue(row, i * 2).strip()
                if not v:
                    break
                prizes.append(v)
            category.prizes = prizes

    def commit(self):
        self.grid.SaveEditControlValue(
        )  # Make sure the current edit is committed.
        self.grid.DisableCellEditControl()
        self.copyToRace()
Exemple #14
0
class Chart(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        font = GetFont()

        self.title = wx.StaticText(self, wx.ID_ANY, "Competition Table:")
        self.title.SetFont(font)
        self.showNames = wx.ToggleButton(self, wx.ID_ANY, 'Show Names')
        self.showNames.SetFont(font)
        self.showNames.Bind(wx.EVT_TOGGLEBUTTON, self.onToggleShow)
        self.showTeams = wx.ToggleButton(self, wx.ID_ANY, 'Show Teams')
        self.showTeams.SetFont(font)
        self.showTeams.Bind(wx.EVT_TOGGLEBUTTON, self.onToggleShow)

        self.headerNames = [
            '', 'System', 'Event', 'Heats', 'In', 'Bib', 'Name', 'Team', 'H1',
            'H2', 'H3', 'Out', 'Bib', 'Name', 'Team'
        ]
        self.numericFields = set(['Event', 'Heats', 'Bib', 'In', 'Out'])

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(0)
        self.grid.EnableReorderRows(False)
        self.grid.CreateGrid(0, len(self.headerNames))
        self.setColNames()

        # Set a larger font for the table.
        # Set specialized editors for appropriate columns.
        self.grid.SetLabelFont(font)

        sizer = wx.BoxSizer(wx.VERTICAL)
        hs = wx.BoxSizer(wx.HORIZONTAL)
        hs.Add(self.title, 0, flag=wx.ALL | wx.ALIGN_CENTRE_VERTICAL, border=4)
        hs.Add(self.showNames, 0, flag=wx.ALL, border=4)
        hs.Add(self.showTeams, 0, flag=wx.ALL, border=4)

        sizer.Add(hs, flag=wx.ALL, border=4)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=6)
        self.SetSizer(sizer)

    def onToggleShow(self, e):
        model = Model.model
        model.chartShowNames = self.showNames.GetValue()
        model.chartShowTeams = self.showTeams.GetValue()
        self.refresh()

    def getHideCols(self, headerNames):
        model = Model.model
        toHide = set()
        for col, h in enumerate(headerNames):
            if h == 'Name' and not getattr(model, 'chartShowNames', True):
                toHide.add(col)
            elif h == 'Team' and not getattr(model, 'chartShowTeams', True):
                toHide.add(col)
        return toHide

    def setColNames(self):
        for col, headerName in enumerate(self.headerNames):
            self.grid.SetColLabelValue(col, headerName)

    def getGrid(self):
        return self.grid

    def refresh(self):
        model = Model.model
        competition = model.competition
        state = competition.state

        self.showNames.SetValue(getattr(model, 'chartShowNames', True))
        self.showTeams.SetValue(getattr(model, 'chartShowTeams', True))

        font = GetFont()

        self.headerNames = [
            '', 'System', 'Event', 'Heats', 'In', 'Bib', 'Name', 'Team', 'H1',
            'H2', 'H3', 'Out', 'Bib', 'Name', 'Team'
        ]
        hideCols = self.getHideCols(self.headerNames)
        self.headerNames = [
            h for c, h in enumerate(self.headerNames) if c not in hideCols
        ]
        Utils.AdjustGridSize(self.grid,
                             rowsRequired=sum(
                                 1 for t, s, e in competition.allEvents()),
                             colsRequired=len(self.headerNames))
        self.grid.ClearGrid()
        self.setColNames()

        for col in six.moves.range(self.grid.GetNumberCols()):
            attr = gridlib.GridCellAttr()
            attr.SetFont(font)
            attr.SetReadOnly(True)
            if col >= 4:
                attr.SetRenderer(GridCellMultiLineStringRenderer())
            if self.headerNames[col] in self.numericFields:
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
            elif self.headerNames[col].startswith('H'):
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
            self.grid.SetColAttr(col, attr)

        row = 0
        for tournament in competition.tournaments:
            self.grid.SetCellValue(row, 0, tournament.name)
            for system in tournament.systems:
                self.grid.SetCellValue(row, 1, system.name)
                for i, event in enumerate(system.events):
                    writeCell = WriteCell(self.grid, row, 2)

                    writeCell(u'{}'.format(i + 1))
                    writeCell(u' {}'.format(event.heatsMax))
                    writeCell(u'\n'.join(event.composition).replace(
                        u'\n', u' ({})\n'.format(len(event.composition)), 1))

                    riders = [
                        state.labels.get(c, None) for c in event.composition
                    ]
                    writeCell(u'\n'.join([
                        u'{}'.format(rider.bib if rider.bib else u'')
                        if rider else '' for rider in riders
                    ]))
                    if getattr(model, 'chartShowNames', True):
                        writeCell(u'\n'.join([
                            rider.full_name if rider else u''
                            for rider in riders
                        ]))
                    if getattr(model, 'chartShowTeams', True):
                        writeCell(u'\n'.join([
                            rider.team if rider else u'' for rider in riders
                        ]))

                    for heat in six.moves.range(3):
                        if event.heatsMax > 1:
                            writeCell(u'\n'.join(event.getHeatPlaces(heat +
                                                                     1)))
                        else:
                            writeCell(u'')

                    out = [event.winner] + event.others
                    writeCell(u'\n'.join(out).replace(
                        u'\n', u' ({})\n'.format(len(out)), 1))
                    riders = [state.labels.get(c, None) for c in out]
                    writeCell(u'\n'.join([
                        u'{}'.format(rider.bib if rider.bib else '')
                        if rider else '' for rider in riders
                    ]))
                    if getattr(model, 'chartShowNames', True):
                        writeCell('\n'.join([
                            rider.full_name if rider else ''
                            for rider in riders
                        ]))
                    if getattr(model, 'chartShowTeams', True):
                        writeCell('\n'.join(
                            [rider.team if rider else '' for rider in riders]))
                    row += 1

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

    def commit(self):
        pass
class CategorySequence(wx.Panel):
    CategoryCol = 0
    PublishCol = 1
    TeamNCol = 2
    UseNthScoreCol = 3
    TeamPublishCol = 4

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        self.alphaSort = wx.Button(self, label=u'Sort Alphabetically')
        self.alphaSort.Bind(wx.EVT_BUTTON, self.onSort)

        self.explanation = wx.StaticText(
            self,
            label=u'\n'.join([
                _("Change the Category order by dragging-and-dropping the first grey column in the table."
                  ),
                _("If 'Use Nth Result Only' is True, 'Team N' specifies the top Nth rider's time to use for the team's time (eg. Team TT, scored on 3rd rider's result)"
                  ),
                _("If 'Use Nth Result Only' if False, 'Team N' specifies the top riders' times to be totaled for the team result (eg. Team Stage Finish, scored on sum of top 3 results for each team)."
                  ),
            ]))

        self.headerNames = [
            'Category', 'Ind. Publish', 'Team N', 'Use Nth Result Only',
            'Team Publish'
        ]

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(64)
        self.grid.CreateGrid(0, len(self.headerNames))
        for col in range(self.grid.GetNumberCols()):
            self.grid.SetColLabelValue(col, self.headerNames[col])

        for col in range(self.grid.GetNumberCols()):
            attr = gridlib.GridCellAttr()
            if col == self.CategoryCol:
                attr.SetReadOnly(True)
            elif col in (self.PublishCol, self.UseNthScoreCol,
                         self.TeamPublishCol):
                editor = gridlib.GridCellBoolEditor()
                editor.UseStringValues('1', '0')
                attr.SetEditor(editor)
                attr.SetRenderer(gridlib.GridCellBoolRenderer())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
            elif col == self.TeamNCol:
                editor = gridlib.GridCellNumberEditor()
                attr.SetEditor(editor)
                attr.SetRenderer(gridlib.GridCellNumberRenderer())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)

            self.grid.SetColAttr(col, attr)

        self.Bind(gridlib.EVT_GRID_CELL_LEFT_CLICK, self.onGridLeftClick)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.alphaSort, 0, flag=wx.ALL | wx.ALIGN_RIGHT, border=4)
        sizer.Add(self.explanation, 0, flag=wx.ALL, border=4)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=6)
        self.SetSizer(sizer)

    def onGridLeftClick(self, event):
        if event.GetCol() in (self.PublishCol, self.UseNthScoreCol,
                              self.TeamPublishCol):
            r, c = event.GetRow(), event.GetCol()
            self.grid.SetCellValue(
                r, c, '1' if self.grid.GetCellValue(r, c)[:1] != '1' else '0')
        event.Skip()

    def getGrid(self):
        return self.grid

    def gridAutoSize(self):
        self.grid.AutoSize()
        self.grid.EnableDragGridSize(False)
        self.grid.EnableDragColSize(False)
        self.Layout()
        self.Refresh()

    def refresh(self):
        model = SeriesModel.model
        model.extractAllRaceResults()  # Also harmonizes the categorySequence
        categoryList = model.getCategoriesSorted()

        Utils.AdjustGridSize(self.grid, len(categoryList))
        for row, c in enumerate(categoryList):
            self.grid.SetCellValue(row, self.CategoryCol, c.name)
            self.grid.SetCellValue(row, self.PublishCol, u'01'[int(c.publish)])
            self.grid.SetCellValue(row, self.TeamNCol, six.text_type(c.teamN))
            self.grid.SetCellValue(row, self.UseNthScoreCol,
                                   u'01'[int(c.useNthScore)])
            self.grid.SetCellValue(row, self.TeamPublishCol,
                                   u'01'[int(c.teamPublish)])
        wx.CallAfter(self.gridAutoSize)

    def getCategoryList(self):
        Category = SeriesModel.Category
        gc = self.grid.GetCellValue
        categories = []
        for row in range(self.grid.GetNumberRows()):
            c = Category(name=gc(row, self.CategoryCol),
                         iSequence=row,
                         publish=gc(row, self.PublishCol) == u'1',
                         teamN=max(1, int(gc(row, self.TeamNCol))),
                         useNthScore=gc(row, self.UseNthScoreCol) == u'1',
                         teamPublish=gc(row, self.TeamPublishCol) == u'1')
            categories.append(c)
        return categories

    def commit(self):
        categoryList = self.getCategoryList()
        SeriesModel.model.setCategories(categoryList)

    def onSort(self, event):
        categoryList = self.getCategoryList()
        categoryList.sort(key=operator.attrgetter('name'))
        SeriesModel.model.setCategories(categoryList)
        wx.CallAfter(self.Refresh)
Exemple #16
0
class Races(wx.Panel):
    #----------------------------------------------------------------------
    headerNames = ['Race', 'Grade', 'Points', 'Team Pts', 'Race File']

    RaceCol = 0
    GradeCol = 1
    PointsCol = 2
    TeamPointsCol = 3
    RaceFileCol = 4
    RaceStatusCol = 5

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        self.seriesNameLabel = wx.StaticText(self, label='Series Name:')
        self.seriesName = wx.TextCtrl(self)

        self.organizerNameLabel = wx.StaticText(self, label='Organizer:')
        self.organizerName = wx.TextCtrl(self)

        self.explanation = wx.StaticText(
            self,
            label=u'\n'.join([
                _("Add all the races in your Series."),
                _("Make sure the races are in chronological order."),
                _("You can change the order by dragging-and-dropping the first grey column in the table."
                  ),
                u'',
                _("Configure the Points Structures or Time Scoring parameters on the Scoring Criteria page."
                  ),
                _("Each race can have its own Points Structure.  For example, you could create 'Double Points' for one race."
                  ),
                u'',
                _("Race results are shown Last-to-First in the output by default."
                  ),
                _("You can change this on the Options page."),
            ]))

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(64)
        self.grid.CreateGrid(0, len(self.headerNames))
        for col in range(self.grid.GetNumberCols()):
            self.grid.SetColLabelValue(col, self.headerNames[col])

        self.pointsChoiceEditor = gridlib.GridCellChoiceEditor(
            [], allowOthers=False)
        attr = gridlib.GridCellAttr()
        attr.SetEditor(self.pointsChoiceEditor)
        self.grid.SetColAttr(self.PointsCol, attr)

        self.teamPointsChoiceEditor = gridlib.GridCellChoiceEditor(
            [], allowOthers=False)
        attr = gridlib.GridCellAttr()
        attr.SetEditor(self.teamPointsChoiceEditor)
        self.grid.SetColAttr(self.TeamPointsCol, attr)

        attr = gridlib.GridCellAttr()
        attr.SetReadOnly(True)
        self.grid.SetColAttr(self.RaceCol, attr)

        attr = gridlib.GridCellAttr()
        attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
        self.grid.SetColAttr(self.GradeCol, attr)
        '''
		attr = gridlib.GridCellAttr()
		attr.SetReadOnly( True )
		self.grid.SetColAttr( self.RaceStatusCol, attr )
		'''

        attr = gridlib.GridCellAttr()
        attr.SetReadOnly(True)
        self.grid.SetColAttr(self.RaceFileCol, attr)

        self.grid.Bind(gridlib.EVT_GRID_CELL_CHANGED, self.onGridChange)
        self.gridAutoSize()
        self.grid.Bind(wx.grid.EVT_GRID_EDITOR_CREATED,
                       self.onGridEditorCreated)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK,
                       self.onEditRaceFileName)

        self.addButton = wx.Button(self, wx.ID_ANY, 'Add Races')
        self.addButton.Bind(wx.EVT_BUTTON, self.doAddRace)

        self.removeButton = wx.Button(self, wx.ID_ANY, 'Remove Race')
        self.removeButton.Bind(wx.EVT_BUTTON, self.doRemoveRace)

        fgs = wx.FlexGridSizer(rows=2, cols=2, vgap=2, hgap=2)
        fgs.AddGrowableCol(1, proportion=1)

        fgs.Add(self.seriesNameLabel,
                flag=wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT)
        fgs.Add(self.seriesName, flag=wx.EXPAND)
        fgs.Add(self.organizerNameLabel,
                flag=wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT)
        fgs.Add(self.organizerName, flag=wx.EXPAND)

        hs = wx.BoxSizer(wx.HORIZONTAL)
        hs.Add(self.addButton, 0, flag=wx.ALL, border=4)
        hs.Add(self.removeButton, 0, flag=wx.ALL, border=4)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(fgs, 0, flag=wx.EXPAND | wx.ALL, border=4)
        sizer.Add(self.explanation, 0, flag=wx.EXPAND | wx.ALL, border=4)
        sizer.Add(hs, 0, flag=wx.EXPAND)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=6)
        self.SetSizer(sizer)

    def getGrid(self):
        return self.grid

    wildcard = 'CrossMgr or Excel files (*.cmn, *.xlsx, *.xlsm, *.xls)|*.cmn;*.xlsx;*.xlsm;*.xls;'

    def onEditRaceFileName(self, event):
        col = event.GetCol()
        if col != self.RaceFileCol:
            event.Skip()
            return

        row = event.GetRow()
        dlg = wx.FileDialog(self,
                            message="Choose a CrossMgr or Excel file",
                            defaultFile='',
                            wildcard=self.wildcard,
                            style=wx.FD_OPEN | wx.FD_CHANGE_DIR)
        ret = dlg.ShowModal()
        fileName = ''
        if ret == wx.ID_OK:
            fileName = dlg.GetPath()
            self.grid.SetCellValue(row, self.RaceCol,
                                   SeriesModel.RaceNameFromPath(fileName))
            self.grid.SetCellValue(row, self.RaceFileCol, fileName)
        dlg.Destroy()
        self.commit()

    def doAddRace(self, event):
        dlg = wx.FileDialog(self,
                            message="Choose a CrossMgr or Excel file",
                            defaultFile='',
                            wildcard=self.wildcard,
                            style=wx.FD_OPEN | wx.FD_CHANGE_DIR
                            | wx.FD_MULTIPLE)
        ret = dlg.ShowModal()
        if ret == wx.ID_OK:
            for fileName in dlg.GetPaths():
                SeriesModel.model.addRace(fileName)
        dlg.Destroy()
        self.refresh()

    def doRemoveRace(self, event):
        row = self.grid.GetGridCursorRow()
        if row < 0:
            Utils.MessageOK(
                self, 'No Selected Race.\nPlease Select a Race to Remove.',
                'No Selected Race')
            return
        if Utils.MessageOKCancel(
                self, 'Confirm Remove Race:\n\n    {}'.format(
                    self.grid.GetCellValue(row, 0)), 'Remove Race'):
            self.grid.DeleteRows(row)
            self.commit()

    def updatePointsChoices(self):
        try:
            comboBox = self.comboBox
        except AttributeError:
            return
        comboBox.SetItems([p.name for p in SeriesModel.model.pointStructures])

    def updateTeamPointsChoices(self):
        try:
            teamComboBox = self.teamComboBox
        except AttributeError:
            return
        teamComboBox.SetItems(
            [''] + [p.name for p in SeriesModel.model.pointStructures])

    def onGridEditorCreated(self, event):
        if event.GetCol() == self.PointsCol:
            self.comboBox = event.GetControl()
            self.updatePointsChoices()
        elif event.GetCol() == self.TeamPointsCol:
            self.teamComboBox = event.GetControl()
            self.updateTeamPointsChoices()
        event.Skip()

    def gridAutoSize(self):
        self.grid.AutoSize()
        self.grid.EnableDragGridSize(False)
        self.grid.EnableDragColSize(False)
        self.Layout()
        self.Refresh()

    def onGridChange(self, event):
        wx.CallAfter(self.gridAutoSize)

    def refresh(self):
        model = SeriesModel.model
        Utils.AdjustGridSize(self.grid, len(model.races))
        for row, race in enumerate(model.races):
            self.grid.SetCellValue(row, self.RaceCol, race.getRaceName())
            self.grid.SetCellValue(row, self.GradeCol, race.grade)
            self.grid.SetCellValue(row, self.PointsCol,
                                   race.pointStructure.name)
            self.grid.SetCellValue(
                row, self.TeamPointsCol, race.teamPointStructure.name
                if race.teamPointStructure else u'')
            self.grid.SetCellValue(row, self.RaceFileCol, race.fileName)
        wx.CallAfter(self.gridAutoSize)

        self.seriesName.SetValue(SeriesModel.model.name)
        self.organizerName.SetValue(SeriesModel.model.organizer)

    def commit(self):
        self.grid.SaveEditControlValue()
        self.grid.DisableCellEditControl(
        )  # Make sure the current edit is committed.

        raceList = []
        for row in range(self.grid.GetNumberRows()):
            race = SeriesModel.model.races[row]
            fileName = self.grid.GetCellValue(row, self.RaceFileCol).strip()
            pname = self.grid.GetCellValue(row, self.PointsCol)
            pteamname = self.grid.GetCellValue(row, self.TeamPointsCol) or None
            grade = self.grid.GetCellValue(row,
                                           self.GradeCol).strip().upper()[:1]
            if not (grade and ord(u'A') <= ord(grade) <= ord(u'Z')):
                grade = u'A'
            if not fileName or not pname:
                continue
            raceList.append((fileName, pname, pteamname, grade))

        model = SeriesModel.model
        model.setRaces(raceList)

        if self.seriesName.GetValue() != model.name:
            model.name = self.seriesName.GetValue()
            model.changed = True

        if self.organizerName.GetValue() != model.organizer:
            model.organizer = self.organizerName.GetValue()
            model.changed = True

        wx.CallAfter(self.refresh)
	def writeIC( stage ):
		ic_fields = ['gap'] + list(Model.IndividualClassification._fields[1:-1])
		riderFields = set( model.registration.getFieldsInUse() )
		headers = (
			['place', 'bib', 'last_name', 'first_name', 'team'] +
			(['uci_id'] if 'uci_id' in riderFields else []) +
			(['license'] if 'license' in riderFields else []) +
			list(ic_fields)
		)
		
		grid = ReorderableGrid( notebook )
		grid.CreateGrid( len(getattr(stage, 'individual_gc', [])), len(headers) )
		grid.EnableReorderRows( False )
		
		for col, h in enumerate(headers):
			attr = gridlib.GridCellAttr()
			attr.SetReadOnly()
			if h in Model.Result.NumericFields or any(t in h for t in ('place', 'time')):
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_CENTRE )
			grid.SetColAttr( col, attr )
			grid.SetColLabelValue( col, Utils.fieldToHeader(h, True) )

		rowNum = 0
		gapLast = None
		timeLast = None
		for place, r in enumerate(stage.individual_gc, 1):
			try:
				rider = model.registration.bibToRider[r.bib]
			except KeyError:
				continue
		
			col = 0
			if r.retired_stage > 0:
				grid.SetCellValue( rowNum, col, u'AB' ); col += 1
			else:
				grid.SetCellValue( rowNum, col, unicode(place) ); col += 1
			
			grid.SetCellValue( rowNum, col, unicode(r.bib) ); col += 1
			grid.SetCellValue( rowNum, col, unicode(rider.last_name).upper()); col += 1
			grid.SetCellValue( rowNum, col, unicode(rider.first_name) ); col += 1
			grid.SetCellValue( rowNum, col, unicode(rider.team) ); col += 1
			
			if 'uci_id' in riderFields:
				grid.SetCellValue( rowNum, col, unicode(rider.uci_id) ); col += 1
			if 'license' in riderFields:
				grid.SetCellValue( rowNum, col, unicode(rider.license) ); col += 1
			
			if r.retired_stage == 0:
				grid.SetCellValue( rowNum, col, Utils.formatTime(r.gap, twoDigitHours=True) if gapLast != r.gap else sameGap )
				gapLast = r.gap
				col += 1
				
				timeCur = r.total_time_with_bonus_plus_penalty
				grid.SetCellValue( rowNum, col, Utils.formatTime(timeCur, twoDigitHours=True) if timeCur != timeLast else sameTime )
				timeLast = timeCur
				col += 1
				
				grid.SetCellValue( rowNum, col, Utils.formatTime(r.total_time_with_bonus_plus_penalty_plus_second_fraction, twoDigitHours=True, extraPrecision=True) ); col += 1
				grid.SetCellValue( rowNum, col, unicode(r.sum_of_places) ); col += 1
				grid.SetCellValue( rowNum, col, unicode(r.last_stage_place) ); col += 1
			
			rowNum +=1
			
		grid.GetGridWindow().Bind(wx.EVT_MOTION, getCommentCallback(grid))
		grid.AutoSize()
		return grid
Exemple #18
0
class Pulled(wx.Panel):
    def __init__(self, parent, id=wx.ID_ANY, size=wx.DefaultSize):
        super(Pulled, self).__init__(parent, id, size=size)

        self.state = RaceInputState()

        vsOverall = wx.BoxSizer(wx.VERTICAL)

        self.hbs = wx.BoxSizer(wx.HORIZONTAL)
        self.showingCategoryLabel = wx.StaticText(self,
                                                  label=u'{}:'.format(
                                                      _('Start Wave')))
        self.showingCategory = wx.StaticText(self)
        self.showingCategory.SetFont(self.showingCategory.GetFont().Bold())
        self.categoryLabel = wx.StaticText(self, label=_('Category:'))
        self.categoryChoice = wx.Choice(self)
        self.Bind(wx.EVT_CHOICE, self.doChooseCategory, self.categoryChoice)
        self.useTableToPullRidersCkBox = wx.CheckBox(
            self, label=_('Use this Table to Pull Riders'))
        self.useTableToPullRidersCkBox.SetToolTip(
            wx.ToolTip(
                _('Also requires Laps to be set in Categories screen.')))
        self.commitBtn = wx.Button(self, label=_('Commit'))
        self.commitBtn.Bind(wx.EVT_BUTTON, self.doCommit)
        self.hbs.Add(self.showingCategoryLabel,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=0)
        self.hbs.Add(self.showingCategory,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=2)
        self.hbs.Add(self.categoryLabel,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=18)
        self.hbs.Add(self.categoryChoice,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=2)
        self.hbs.Add(self.useTableToPullRidersCkBox,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=18)
        self.hbs.Add(self.commitBtn,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=32)

        #---------------------------------------------------------------
        self.colNameFields = (
            (_('Laps to Go'), 'lapsToGo', 'i'),
            (u'    ' + _('Bib'), 'pulledBib', 'i'),
            (u'Name', 'pulledName', 's'),
            (u'Team', 'pulledTeam', 's'),
            (u'Component', 'pulledComponent', 's'),
            (u'Error', 'pulledError', 's'),
        )
        self.colnames = [
            colName for colName, fieldName, dataType in self.colNameFields
        ]
        self.iCol = dict((fieldName, i)
                         for i, (colName, fieldName,
                                 dataType) in enumerate(self.colNameFields)
                         if fieldName)
        self.grid = ReorderableGrid(self)
        self.grid.CreateGrid(0, len(self.colNameFields))
        GetTranslation = _
        for col, (colName, fieldName,
                  dataType) in enumerate(self.colNameFields):
            self.grid.SetColLabelValue(col, colName)
            attr = wx.grid.GridCellAttr()
            if dataType == 'i':
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_TOP)
                attr.SetEditor(wx.grid.GridCellFloatEditor(precision=0))
                attr.SetRenderer(wx.grid.GridCellFloatRenderer(precision=0))
            elif dataType == 'f':
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_TOP)
                attr.SetEditor(wx.grid.GridCellFloatEditor(precision=2))
                attr.SetRenderer(wx.grid.GridCellFloatRenderer(precision=2))
            elif dataType == 't':
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
                attr.SetEditor(TimeEditor())

            self.grid.SetColAttr(col, attr)

        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, self.onCellChange)
        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

        #---------------------------------------------------------------

        vsOverall.Add(self.hbs, 0, flag=wx.EXPAND | wx.ALL, border=4)
        vsOverall.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=4)
        self.SetSizer(vsOverall)

    def setCategory(self, category):
        for i, c in enumerate(
                Model.race.getCategories(
                    startWaveOnly=False) if Model.race else [], 1):
            if c == category:
                SetCategory(self.categoryChoice, c)
                Model.setCategoryChoice(i, 'resultsCategory')
                return
        SetCategory(self.categoryChoice, None)
        Model.setCategoryChoice(0, 'resultsCategory')

    def doChooseCategory(self, event):
        Model.setCategoryChoice(self.categoryChoice.GetSelection(),
                                'resultsCategory')
        self.refresh()

    def doCommit(self, event):
        self.commit()
        self.refresh()

    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 getRaceInfo(self):
        race = Model.race
        if not race:
            return False, []

        category = self.getCategory()
        if not category:
            return False, []

        results = GetResults(category)
        if not results or not results[0].lapTimes:
            return False, []

        return True, [race, category, results, len(results[0].lapTimes)]

    def getError(self, bib, lapsToGo, laps):
        if not bib:
            return u''
        if not lapsToGo:
            lapsToGo = 1
        success, info = self.getRaceInfo()
        if not success:
            return u''
        race, category, results, laps = info

        if bib not in race.riders:
            return _(u'Bib not in Race')
        if race.getCategory(bib) != category:
            return _(u'Bib not in Category')
        rider = race.riders[bib]
        if rider.status not in (Model.Rider.Pulled, Model.Rider.Finisher):
            return u'{}: {}'.format(_('Bib has non-Finisher Status'),
                                    Model.Rider.statusNames[rider.status])
        if lapsToGo >= laps:
            return u'{}: {}'.format(_('Laps To Go exceeds for Race Laps'),
                                    laps)
        if lapsToGo <= 0:
            return u'{}'.format(_('Laps To Go must be >= 0'))
        return u''

    def onCellChange(self, event):
        row, col = event.GetRow(), event.GetCol()
        colName = self.colNameFields[col][1]
        GetTranslation = _

        if colName == 'pulledBib' or colName == 'lapsToGo':
            bib = int(
                '0' +
                re.sub('[^0-9]', '',
                       self.grid.GetCellValue(row, self.iCol['pulledBib'])))
            for r in range(row, -1, -1):
                lapsToGo = int(
                    '0' + self.grid.GetCellValue(r, self.iCol['lapsToGo']))
                if lapsToGo:
                    break
            if not lapsToGo:
                lapsToGo = 1

            success, info = self.getRaceInfo()
            if not success:
                return
            race, category, results, laps = info

            name, team, component = getRiderInfo(bib)
            self.grid.SetCellValue(row, self.iCol['pulledName'], name)
            self.grid.SetCellValue(row, self.iCol['pulledTeam'], team)
            self.grid.SetCellValue(row, self.iCol['pulledComponent'],
                                   component)
            self.grid.SetCellValue(row, self.iCol['pulledError'],
                                   self.getError(bib, lapsToGo, laps))

            wx.CallAfter(self.grid.AutoSizeColumns, False)

    def setRow(self, bib, lapsToGo, laps, row, updateGrid=True):
        name, team, component = getRiderInfo(bib)
        values = {
            'pulledBib': bib,
            'pulledName': name,
            'pulledTeam': team,
            'pulledComponent': component,
            'pulledError': self.getError(bib, lapsToGo, laps),
            'lapsToGo': lapsToGo
        }
        for col, (name, attr, valuesType) in enumerate(self.colNameFields):
            self.grid.SetCellValue(row, col, str(values[attr]))
        return values

    def getRow(self, row):
        values = {'row': row}
        for col, (name, attr, dataType) in enumerate(self.colNameFields):
            v = self.grid.GetCellValue(row, col).strip()
            if dataType == 'i':
                v = u''.join(c for c in v if c.isdigit())
                v = int(v or 0)
            elif dataType == 'f':
                v = u''.join(c for c in v if c.isdigit() or c == '.')
                v = float(v or 0.0)
            elif dataType == 't':
                v = Utils.StrToSeconds(v or '')

            values[attr] = v

        return values

    def updateGrid(self):
        self.grid.ClearGrid()

        success, info = self.getRaceInfo()
        if not success:
            return
        race, category, results, laps = info

        if race.isTimeTrial:
            return

        Pulled = Model.Rider.Pulled
        pulled = []
        for rr in results:
            if race.riders[rr.num].status == Pulled:
                pulled.append(
                    getPulledCmpTuple(rr, race.riders[rr.num], laps, False))
        pulled.sort()
        bibLapsToGo = {p[-1].num: abs(p[0]) for p in pulled}
        pulled = [p[-1] for p in pulled]

        Utils.AdjustGridSize(self.grid, len(pulled) + 20)
        for row, rr in enumerate(pulled):
            self.setRow(rr.num, bibLapsToGo[rr.num], laps, row)

        # Remove repeated lapsToGo entries.
        col = self.iCol['lapsToGo']
        for row in range(self.grid.GetNumberRows() - 1, 0, -1):
            if self.grid.GetCellValue(row, col) == self.grid.GetCellValue(
                    row - 1, col):
                self.grid.SetCellValue(row, col, u'')

        self.grid.AutoSizeColumns(False)  # Resize to fit the column name.
        self.grid.AutoSizeRows(False)

    def refresh(self):
        success, info = self.getRaceInfo()
        if not success:
            return self.updateGrid()
        race, category, results, laps = info
        if race.isTimeTrial:
            self.grid.SaveEditControlValue(
            )  # Make sure the current edit is committed.
            self.grid.DisableCellEditControl()
            self.grid.ClearGrid()
            return
        col = self.iCol['pulledBib']
        tableBibs = set(
            int(u'0' + self.grid.GetCellValue(row, col))
            for row in range(self.grid.GetNumberRows()))
        tableBibs.discard(0)
        if not tableBibs:
            return self.updateGrid()

        Pulled = Model.Rider.Pulled
        allBibs = set(rr.num for rr in results)
        if not allBibs >= tableBibs:
            return self.updateGrid()
        pulledBibs = set(rr.num for rr in results
                         if race.riders[rr.num].status == Pulled)
        if not tableBibs >= pulledBibs:
            return self.updateGrid()

    def commit(self):
        self.grid.SaveEditControlValue(
        )  # Make sure the current edit is committed.
        self.grid.DisableCellEditControl()

        race = Model.race
        if not race:
            return
        race.useTableToPullRiders = self.useTableToPullRidersCkBox.GetValue()
        if not race.useTableToPullRiders:
            self.grid.ClearGrid()
            Utils.AdjustGridSize(self.grid, 20)
            return

        rows = [self.getRow(r) for r in range(self.grid.GetNumberRows())]
        rows = [rv for rv in rows if rv['pulledBib']]

        # Fix any missing data lapsToGo in the table.
        lapsToGoLast = 1
        for rv in rows:
            if not rv['lapsToGo']:
                rv['lapsToGo'] = lapsToGoLast
            lapsToGoLast = rv['lapsToGo']

        success, info = self.getRaceInfo()
        if not success:
            return False
        race, category, results, laps = info
        rule80LapTime = race.getRule80LapTime(category)

        changed = False
        Finisher, Pulled = Model.Rider.Finisher, Model.Rider.Pulled
        for rr in results:
            rider = race.riders.get(rr.num, None)
            if not rider or race.getCategory(rr.num) != category:
                continue
            if rider.status == Pulled:
                rider.status = Finisher
                changed = True

        lapsToGoPulled = defaultdict(list)
        for rv in rows:
            lapsToGoPulled[rv['lapsToGo']].append(rv['pulledBib'])

        for lapsToGo, bibs in lapsToGoPulled.items():
            if lapsToGo <= 0:
                continue
            for seq, bib in enumerate(bibs):
                try:
                    rider = race.riders[bib]
                except KeyError:
                    continue

                rider.status = Pulled
                rider.pulledLapsToGo = lapsToGo
                rider.pulledSequence = seq
                changed = True

        if changed:
            race.setChanged()
        self.updateGrid()
Exemple #19
0
class TeamResults(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)

        self.categoryLabel = wx.StaticText(self, label='Category:')
        self.categoryChoice = wx.Choice(self, choices=['No Categories'])
        self.categoryChoice.SetSelection(0)
        self.categoryChoice.Bind(wx.EVT_CHOICE, self.onCategoryChoice)
        self.statsLabel = wx.StaticText(self, label='   /   ')
        self.refreshButton = wx.Button(self, label='Refresh')
        self.refreshButton.Bind(wx.EVT_BUTTON, self.onRefresh)
        self.publishToHtml = wx.Button(self, label='Publish to Html')
        self.publishToHtml.Bind(wx.EVT_BUTTON, self.onPublishToHtml)
        self.publishToFtp = wx.Button(self, label='Publish to Html with FTP')
        self.publishToFtp.Bind(wx.EVT_BUTTON, self.onPublishToFtp)
        self.publishToExcel = wx.Button(self, label='Publish to Excel')
        self.publishToExcel.Bind(wx.EVT_BUTTON, self.onPublishToExcel)

        self.postPublishCmdLabel = wx.StaticText(self,
                                                 label='Post Publish Cmd:')
        self.postPublishCmd = wx.TextCtrl(self, size=(300, -1))
        self.postPublishExplain = wx.StaticText(
            self,
            label=
            'Command to run after publish.  Use %* for all filenames (eg. "copy %* dirname")'
        )

        hs = wx.BoxSizer(wx.HORIZONTAL)
        hs.Add(self.categoryLabel, flag=wx.TOP, border=4)
        hs.Add(self.categoryChoice)
        hs.AddSpacer(4)
        hs.Add(self.statsLabel, flag=wx.TOP | wx.LEFT | wx.RIGHT, border=4)
        hs.AddStretchSpacer()
        hs.Add(self.refreshButton)
        hs.Add(self.publishToHtml, flag=wx.LEFT, border=48)
        hs.Add(self.publishToFtp, flag=wx.LEFT, border=4)
        hs.Add(self.publishToExcel, flag=wx.LEFT, border=4)

        hs2 = wx.BoxSizer(wx.HORIZONTAL)
        hs2.Add(self.postPublishCmdLabel, flag=wx.ALIGN_CENTRE_VERTICAL)
        hs2.Add(self.postPublishCmd, flag=wx.ALIGN_CENTRE_VERTICAL)
        hs2.Add(self.postPublishExplain,
                flag=wx.ALIGN_CENTRE_VERTICAL | wx.LEFT,
                border=4)

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(64)
        self.grid.CreateGrid(0, len(HeaderNamesTemplate) + 1)
        self.grid.SetRowLabelSize(0)
        self.grid.EnableReorderRows(False)
        self.grid.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.doLabelClick)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.doCellClick)
        self.sortCol = None

        self.setColNames(getHeaderNames())

        sizer = wx.BoxSizer(wx.VERTICAL)

        sizer.Add(hs, flag=wx.TOP | wx.LEFT | wx.RIGHT, border=4)
        sizer.Add(hs2,
                  flag=wx.ALIGN_RIGHT | wx.TOP | wx.LEFT | wx.RIGHT,
                  border=4)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.TOP | wx.ALL, border=4)
        self.SetSizer(sizer)

    def onRefresh(self, event):
        SeriesModel.model.clearCache()
        self.refresh()

    def onCategoryChoice(self, event):
        wx.CallAfter(self.refresh)

    def readReset(self):
        self.sortCol = None

    def doLabelClick(self, event):
        col = event.GetCol()
        label = self.grid.GetColLabelValue(col)
        if self.sortCol == col:
            self.sortCol = -self.sortCol
        elif self.sortCol == -col:
            self.sortCol = None
        else:
            self.sortCol = col

        if not self.sortCol:
            self.sortCol = None
        wx.CallAfter(self.refresh)

    def doCellClick(self, event):
        if not hasattr(self, 'popupInfo'):
            self.popupInfo = [
                (u'{}...'.format(_('Copy Team to Clipboard')), wx.NewId(),
                 self.onCopyTeam),
            ]
            for p in self.popupInfo:
                if p[2]:
                    self.Bind(wx.EVT_MENU, p[2], id=p[1])

        menu = wx.Menu()
        for i, p in enumerate(self.popupInfo):
            if p[2]:
                menu.Append(p[1], p[0])
            else:
                menu.AppendSeparator()

        self.rowCur, self.colCur = event.GetRow(), event.GetCol()
        self.PopupMenu(menu)
        menu.Destroy()

    def copyCellToClipboard(self, r, c):
        if wx.TheClipboard.Open():
            # Create a wx.TextDataObject
            do = wx.TextDataObject()
            do.SetText(self.grid.GetCellValue(r, c))

            # Add the data to the clipboard
            wx.TheClipboard.SetData(do)
            # Close the clipboard
            wx.TheClipboard.Close()
        else:
            wx.MessageBox(u"Unable to open the clipboard", u"Error")

    def onCopyTeam(self, event):
        self.copyCellToClipboard(self.rowCur, 1)

    def setColNames(self, headerNames):
        for col, headerName in enumerate(headerNames):
            self.grid.SetColLabelValue(col, headerName)
            attr = gridlib.GridCellAttr()
            if headerName in ('Team', ):
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_TOP)
            elif headerName in ('Pos', 'Points', 'Gap'):
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_TOP)
            else:
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)

            attr.SetReadOnly(True)
            self.grid.SetColAttr(col, attr)

    def getGrid(self):
        return self.grid

    def getTitle(self):
        return self.showResults.GetStringSelection() + ' Series Results'

    def fixCategories(self):
        model = SeriesModel.model
        categoryNames = model.getCategoryNamesSortedTeamPublish()
        lastSelection = self.categoryChoice.GetStringSelection()
        self.categoryChoice.SetItems(categoryNames)
        iCurSelection = 0
        for i, n in enumerate(categoryNames):
            if n == lastSelection:
                iCurSelection = i
                break
        self.categoryChoice.SetSelection(iCurSelection)
        self.GetSizer().Layout()

    def refresh(self):
        model = SeriesModel.model
        HeaderNames = getHeaderNames()
        scoreByPoints = model.scoreByPoints
        scoreByTime = model.scoreByTime

        self.postPublishCmd.SetValue(model.postPublishCmd)

        wait = wx.BusyCursor()
        self.raceResults = model.extractAllRaceResults(False)
        del wait

        self.fixCategories()

        categoryName = self.categoryChoice.GetStringSelection()
        if not categoryName or not (scoreByPoints or scoreByTime):
            Utils.AdjustGridSize(self.grid, 0, 0)
            return

        self.grid.ClearGrid()

        pointsForRank = {
            r.getFileName(): r.pointStructure
            for r in model.races
        }

        results, races = GetModelInfo.GetCategoryResultsTeam(
            categoryName,
            self.raceResults,
            pointsForRank,
            useMostEventsCompleted=model.useMostEventsCompleted,
            numPlacesTieBreaker=model.numPlacesTieBreaker,
        )
        results = [rr for rr in results if rr[1] > 0]

        headerNames = HeaderNames + [
            u'{}\n{}'.format(r[1], r[0].strftime('%Y-%m-%d') if r[0] else u'')
            for r in races
        ]

        Utils.AdjustGridSize(self.grid, len(results), len(headerNames))
        self.setColNames(headerNames)

        for row, (team, points, gap, rrs) in enumerate(results):
            self.grid.SetCellValue(row, 0, unicode(row + 1))
            self.grid.SetCellValue(row, 1, unicode(team))
            self.grid.SetCellValue(row, 2, unicode(points))
            self.grid.SetCellValue(row, 3, unicode(gap))
            for q, rt in enumerate(rrs):
                self.grid.SetCellValue(row, 4 + q,
                                       formatTeamResults(scoreByPoints, rt))

            for c in xrange(0, len(headerNames)):
                self.grid.SetCellBackgroundColour(row, c, wx.WHITE)
                self.grid.SetCellTextColour(row, c, wx.BLACK)

        if self.sortCol is not None:

            def getBracketedNumber(v):
                numberMax = 99999
                if not v:
                    return numberMax
                try:
                    return int(reNoDigits.sub('', v.split('(')[1]))
                except (IndexError, ValueError):
                    return numberMax

            data = []
            for r in xrange(0, self.grid.GetNumberRows()):
                rowOrig = [
                    self.grid.GetCellValue(r, c)
                    for c in xrange(0, self.grid.GetNumberCols())
                ]
                rowCmp = rowOrig[:]
                rowCmp[0] = int(rowCmp[0])
                rowCmp[4] = Utils.StrToSeconds(rowCmp[4])
                rowCmp[5:] = [getBracketedNumber(v) for v in rowCmp[5:]]
                rowCmp.extend(rowOrig)
                data.append(rowCmp)

            if self.sortCol > 0:
                fg = wx.WHITE
                bg = wx.Colour(0, 100, 0)
            else:
                fg = wx.BLACK
                bg = wx.Colour(255, 165, 0)

            iCol = abs(self.sortCol)
            data.sort(key=lambda x: x[iCol], reverse=(self.sortCol < 0))
            for r, row in enumerate(data):
                for c, v in enumerate(row[self.grid.GetNumberCols():]):
                    self.grid.SetCellValue(r, c, v)
                    if c == iCol:
                        self.grid.SetCellTextColour(r, c, fg)
                        self.grid.SetCellBackgroundColour(r, c, bg)
                        if c < 4:
                            halign = wx.ALIGN_LEFT
                        elif c == 4 or c == 5:
                            halign = wx.ALIGN_RIGHT
                        else:
                            halign = wx.ALIGN_CENTRE
                        self.grid.SetCellAlignment(r, c, halign, wx.ALIGN_TOP)

        self.statsLabel.SetLabel('{} / {}'.format(
            self.grid.GetNumberRows(),
            GetModelInfo.GetTotalUniqueTeams(self.raceResults)))

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

        self.GetSizer().Layout()

    def onPublishToExcel(self, event):
        model = SeriesModel.model

        scoreByPoints = model.scoreByPoints
        scoreByTime = model.scoreByTime
        scoreByPercent = model.scoreByPercent
        scoreByTrueSkill = model.scoreByTrueSkill
        HeaderNames = getHeaderNames()

        if Utils.mainWin:
            if not Utils.mainWin.fileName:
                Utils.MessageOK(self,
                                'You must save your Series to a file first.',
                                'Save Series')
                return

        self.raceResults = model.extractAllRaceResults(False)

        categoryNames = model.getCategoryNamesSortedTeamPublish()
        if not categoryNames:
            return

        pointsForRank = {
            r.getFileName(): r.pointStructure
            for r in model.races
        }

        wb = xlwt.Workbook()

        for categoryName in categoryNames:
            results, races = GetModelInfo.GetCategoryResultsTeam(
                categoryName,
                self.raceResults,
                pointsForRank,
                useMostEventsCompleted=model.useMostEventsCompleted,
                numPlacesTieBreaker=model.numPlacesTieBreaker,
            )
            results = [rr for rr in results if rr[1] > 0]

            headerNames = HeaderNames + [r[1] for r in races]

            ws = wb.add_sheet(re.sub('[:\\/?*\[\]]', ' ', categoryName))
            wsFit = FitSheetWrapper(ws)

            fnt = xlwt.Font()
            fnt.name = 'Arial'
            fnt.bold = True
            fnt.height = int(fnt.height * 1.5)

            headerStyle = xlwt.XFStyle()
            headerStyle.font = fnt

            rowCur = 0
            ws.write_merge(rowCur, rowCur, 0, 8, model.name, headerStyle)
            rowCur += 1
            if model.organizer:
                ws.write_merge(rowCur, rowCur, 0, 8,
                               u'by {}'.format(model.organizer), headerStyle)
                rowCur += 1
            rowCur += 1
            colCur = 0
            ws.write_merge(rowCur, rowCur, colCur, colCur + 4, categoryName,
                           xlwt.easyxf("font: name Arial, bold on;"))

            rowCur += 2
            for c, headerName in enumerate(headerNames):
                wsFit.write(rowCur, c, headerName, labelStyle, bold=True)
            rowCur += 1

            for pos, (team, points, gap, rrs) in enumerate(results):
                wsFit.write(rowCur, 0, pos + 1, numberStyle)
                wsFit.write(rowCur, 1, team, textStyle)
                wsFit.write(rowCur, 2, points, numberStyle)
                wsFit.write(rowCur, 3, gap, numberStyle)
                for q, rt in enumerate(rrs):
                    wsFit.write(rowCur, 4 + q,
                                formatTeamResults(scoreByPoints, rt),
                                centerStyle)
                rowCur += 1

            # Add branding at the bottom of the sheet.
            style = xlwt.XFStyle()
            style.alignment.horz = xlwt.Alignment.HORZ_LEFT
            ws.write(rowCur + 2, 0, brandText, style)

        if Utils.mainWin:
            xlfileName = os.path.splitext(
                Utils.mainWin.fileName)[0] + 'Team.xls'
        else:
            xlfileName = 'ResultsTestTeam.xls'

        try:
            wb.save(xlfileName)
            webbrowser.open(xlfileName, new=2, autoraise=True)
            Utils.MessageOK(
                self, 'Excel file written to:\n\n   {}'.format(xlfileName),
                'Excel Write')
            self.callPostPublishCmd(xlfileName)
        except IOError:
            Utils.MessageOK(
                self,
                'Cannot write "{}".\n\nCheck if this spreadsheet is open.\nIf so, close it, and try again.'
                .format(xlfileName),
                'Excel File Error',
                iconMask=wx.ICON_ERROR)

    def onPublishToHtml(self, event):
        if Utils.mainWin:
            if not Utils.mainWin.fileName:
                Utils.MessageOK(self,
                                'You must save your Series to a file first.',
                                'Save Series')
                return

        htmlfileName = getHtmlFileName()
        model = SeriesModel.model
        model.postPublishCmd = self.postPublishCmd.GetValue().strip()

        try:
            getHtml(htmlfileName)
            webbrowser.open(htmlfileName, new=2, autoraise=True)
            Utils.MessageOK(
                self, u'Html file written to:\n\n   {}'.format(htmlfileName),
                'html Write')
        except IOError:
            Utils.MessageOK(
                self,
                'Cannot write "%s".\n\nCheck if this file is open.\nIf so, close it, and try again.'
                % htmlfileName,
                'Html File Error',
                iconMask=wx.ICON_ERROR)
        self.callPostPublishCmd(htmlfileName)

    def onPublishToFtp(self, event):
        if Utils.mainWin:
            if not Utils.mainWin.fileName:
                Utils.MessageOK(self,
                                'You must save your Series to a file first.',
                                'Save Series')
                return

        htmlfileName = getHtmlFileName()

        try:
            getHtml(htmlfileName)
        except IOError:
            return

        html = io.open(htmlfileName, 'r', encoding='utf-8', newline='').read()
        with FtpWriteFile.FtpPublishDialog(self, html=html) as dlg:
            dlg.ShowModal()
        self.callPostPublishCmd(htmlfileName)

    def commit(self):
        model = SeriesModel.model
        if model.postPublishCmd != self.postPublishCmd.GetValue().strip():
            model.postPublishCmd = self.postPublishCmd.GetValue().strip()
            model.setChanged()

    def callPostPublishCmd(self, fname):
        self.commit()
        postPublishCmd = SeriesModel.model.postPublishCmd
        if postPublishCmd and fname:
            allFiles = [fname]
            if platform.system() == 'Windows':
                files = ' '.join('""{}""'.format(f) for f in allFiles)
            else:
                files = ' '.join('"{}"'.format(f) for f in allFiles)

            if '%*' in postPublishCmd:
                cmd = postPublishCmd.replace('%*', files)
            else:
                cmd = ' '.join([postPublishCmd, files])

            try:
                subprocess.check_call(cmd, shell=True)
            except subprocess.CalledProcessError as e:
                Utils.MessageOK(
                    self,
                    u'{}\n\n    {}\n{}: {}'.format('Post Publish Cmd Error', e,
                                                   'return code',
                                                   e.returncode),
                    _('Post Publish Cmd Error'))
            except Exception as e:
                Utils.MessageOK(
                    self, u'{}\n\n    {}'.format('Post Publish Cmd Error', e),
                    'Post Publish Cmd Error')
Exemple #20
0
class EventPosition(EnablePanel):
    def __init__(self, parent):
        EnablePanel.__init__(self, parent)
        self.box = wx.StaticBox(self, label='Start Positions')
        boxSizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)

        self.SetBackgroundColour(wx.WHITE)

        self.event = None

        self.drawLotsBitmap = wx.Bitmap(
            os.path.join(Utils.getImageFolder(), 'dice.png'),
            wx.BITMAP_TYPE_PNG)
        self.drawLotsGreyBitmap = wx.Bitmap(
            os.path.join(Utils.getImageFolder(), 'dice_grey.png'),
            wx.BITMAP_TYPE_PNG)
        self.emptyBitmap = wx.Bitmap(self.drawLotsBitmap.GetWidth(),
                                     self.drawLotsBitmap.GetHeight(),
                                     self.drawLotsBitmap.GetDepth())

        dc = wx.MemoryDC()
        dc.SelectObject(self.emptyBitmap)
        dc.SetBrush(wx.WHITE_BRUSH)
        dc.Clear()
        dc.SelectObject(wx.NullBitmap)

        self.activeBar = EnableBar(self)
        self.activeBar.SetToolTip(
            wx.ToolTip(u'\n'.join([
                u'Record the Start Positions by dragging the row numbers in the table.',
                u'Set any DNS in the Status column.',
                u'Then press Start or Cancel.',
            ])))

        vs = wx.BoxSizer(wx.VERTICAL)
        self.startButton = MakeRoundButton(self, 'Start')
        self.cancelButton = MakeRoundButton(self, 'Cancel', isCancel)
        vs.Add(self.startButton, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=4)
        vs.Add(self.cancelButton, flag=wx.ALL, border=4)

        self.headerNames = ['Bib', 'Name', 'Team', 'Status']
        self.iColStatus = 3
        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.CreateGrid(4, len(self.headerNames))

        font = wx.Font((0, FontSize), wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL,
                       wx.FONTWEIGHT_NORMAL)
        self.grid.SetLabelFont(font)
        for col in six.moves.range(self.grid.GetNumberCols()):
            self.grid.SetColLabelValue(col, self.headerNames[col])
            self.grid.SetCellValue(
                0, col, self.headerNames[col])  # Add the label as data.
            attr = gridlib.GridCellAttr()
            attr.SetFont(font)
            attr.SetReadOnly(True)
            if self.headerNames[col].startswith('Bib'):
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
            elif self.headerNames[col].startswith('Status'):
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(choices=['DNS', '']))
                attr.SetReadOnly(False)
            self.grid.SetColAttr(col, attr)

        self.grid.AutoSizeColumns(False)  # Resize to fit the column name.
        self.grid.AutoSizeRows(False)
        for col in six.moves.range(self.grid.GetNumberCols()):
            self.grid.SetCellValue(0, col, '')  # Remove the labels.

        self.drawLotsDisplay = wx.StaticBitmap(self, wx.ID_ANY,
                                               self.drawLotsBitmap)
        self.drawLotsDisplay.SetToolTip(
            wx.ToolTip(u'\n'.join([
                u"Dice are active when riders need to draw lots to select their positions.",
                u"Dice are inactive when riders' start positions are known.",
            ])))

        boxSizer.Add(self.activeBar, 0, flag=wx.ALL | wx.EXPAND, border=4)
        boxSizer.Add(vs, 0, flag=wx.ALL, border=4)
        boxSizer.Add(self.grid, 1, flag=wx.ALL | wx.EXPAND, border=4)
        boxSizer.Add(self.drawLotsDisplay,
                     0,
                     flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL,
                     border=4)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(boxSizer, 1, flag=wx.EXPAND)
        self.SetSizer(sizer)

    def SetEnable(self, enable):
        EnablePanel.SetEnable(self, enable)
        for b, t in [(self.startButton, isSelect),
                     (self.cancelButton, isCancel)]:
            EnableRoundButton(b, enable, t)
        self.activeBar.SetBackgroundColour(
            wx.Colour(0, 128, 0) if enable else wx.WHITE)
        self.Refresh()

    def setEvent(self, event):
        self.event = event

    def refresh(self):
        if not self.event:
            self.grid.ClearGrid()
            self.drawLotsDisplay.SetBitmap(self.emptyBitmap)
            return

        self.drawLotsDisplay.Show(not Model.model.competition.isMTB)

        DQs, DNSs, DNFs = Model.model.competition.getRiderStates()

        start = self.event.starts[-1]
        if self.startButton.IsEnabled():
            self.drawLotsDisplay.SetBitmap(
                self.drawLotsBitmap if start.canDrawLots else self.emptyBitmap)
        else:
            self.drawLotsDisplay.SetBitmap(self.drawLotsGreyBitmap if start.
                                           canDrawLots else self.emptyBitmap)

        startPositions = start.startPositions
        Utils.AdjustGridSize(self.grid, rowsRequired=len(startPositions))
        state = self.event.competition.state
        for row, p in enumerate(startPositions):
            rider = state.labels[p]
            for col, v in enumerate([
                    rider.bib, rider.full_name, rider.team, 'DNS' if
                (rider in DNSs or rider.bib in CacheDNSs) else ''
            ]):
                self.grid.SetCellValue(row, col, u' {}'.format(v))

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

    def commit(self):
        self.grid.SaveEditControlValue()
        self.grid.HideCellEditControl()

        if not self.event:
            self.grid.ClearGrid()
            return

        startPositions = []
        for row in six.moves.range(self.grid.GetNumberRows()):
            bib = self.grid.GetCellValue(row, 0)
            try:
                bib = int(bib)
            except:
                continue
            if self.grid.GetCellValue(row, self.iColStatus).strip():
                CacheDNSs.add(bib)
            else:
                CacheDNSs.discard(bib)
            startPositions.append((bib, ''))

        start = self.event.starts[-1]
        start.setStartPositions(startPositions)

        Model.model.setChanged(True)
        Utils.setTitle()
Exemple #21
0
class TeamResults( wx.Panel ):
	def __init__( self, parent, id=wx.ID_ANY, size=wx.DefaultSize ):
		super(TeamResults, self).__init__( parent, id, size=size )
		
		self.state = RaceInputState()
		
		vsOverall = wx.BoxSizer( wx.VERTICAL )
		
		#---------------------------------------------------------------
		self.colnames = (
			_('Pos'),
			_('Team'),
			_('Time'),
			_('Gap'),
		)
		self.grid = ReorderableGrid( self )
		self.grid.CreateGrid( 0, len(self.colnames) )
		self.grid.SetRowLabelSize( 0 )
		self.grid.SetMargins( 0, 0 )
		self.grid.AutoSizeColumns( True )
		self.grid.DisableDragColSize()
		self.grid.DisableDragRowSize()
		
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )

		#---------------------------------------------------------------
		self.hbs = wx.BoxSizer(wx.HORIZONTAL)
		
		self.categoryLabel = wx.StaticText( self, label = _('Category:') )
		self.categoryChoice = wx.Choice( self )
		self.Bind(wx.EVT_CHOICE, self.doChooseCategory, self.categoryChoice)
		
		self.exportButton = wx.Button( self, label='{} {}/{}'.format(_('Export'), _('Excel'), _('PDF')) )
		self.exportButton.Bind( wx.EVT_BUTTON, self.doExport )
		
		self.hbs.Add( self.categoryLabel, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.ALIGN_CENTRE_VERTICAL, border=4 )
		self.hbs.Add( self.categoryChoice, flag=wx.ALL|wx.ALIGN_CENTRE_VERTICAL, border=4 )
		self.hbs.Add( self.exportButton, flag=wx.ALL|wx.ALIGN_CENTRE_VERTICAL, border=4 )
		
		#---------------------------------------------------------------
		
		vsOverall.Add( self.hbs, flag=wx.ALL, border=4 )
		vsOverall.Add( self.grid, 1, flag=wx.EXPAND|wx.ALL, border=4 )
		self.SetSizer( vsOverall )
	
	def doChooseCategory( self, event ):
		Model.setCategoryChoice( self.categoryChoice.GetSelection(), 'resultsCategory' )
		self.refresh()
		
	@logCall
	def doExport( self, event=None ):
		race = Model.race
		if not race:
			return
		
		fileName = Utils.getMainWin().fileName if Utils.getMainWin() else 'Test.cmn'
		
		#---------------------------------------------------------------------------------
		# Create an Excel file.
		#
		xlFileName = os.path.splitext(fileName)[0] + '-TeamResults.xlsx'

		try:
			wb = xlsxwriter.Workbook( xlFileName )
			formats = ExportGrid.ExportGrid.getExcelFormatsXLSX( wb )
			
			ues = Utils.UniqueExcelSheetName()
			for category in race.getCategories( publishOnly=True ):			
				eg = self.toExportGrid( category )
				if eg:
					ws = wb.add_worksheet( ues.getSheetName(category.fullname) )
					eg.toExcelSheetXLSX( formats, ws )
			wb.close()
		except Exception as e:
			logException( e, sys.exc_info() )
		del wb
		
		#---------------------------------------------------------------------------------
		# Create a PDF file.
		#
		pdfFileName = os.path.splitext(fileName)[0] + '-TeamResults.pdf'
		
		try:
			pdf = PDF( orientation = 'P' )
			pdf.set_font( 'Arial', '', 12 )
			pdf.set_author( getpass.getuser() )
			pdf.set_keywords( 'CrossMgr Team Results' )
			pdf.set_creator( Version.AppVerName )
			pdf.set_title( os.path.splitext(pdfFileName)[0].replace('-', ' ') )
			for category in race.getCategories( publishOnly=True ):
				eg = self.toExportGrid( category )
				if eg:
					eg.drawToFitPDF( pdf, orientation=wx.PORTRAIT )
			pdf.output( pdfFileName, 'F' )
		except Exception as e:
			logException( e, sys.exc_info() )
		del pdf
	
	def getColNames( self ):
		race = Model.race
		colnames = list( self.colnames )
		
		col = 2
		if   race.teamRankOption == race.teamRankByRiderTime:
			colnames[col] = u'{}\n{}'.format(_('Time'), Utils.ordinal(race.nthTeamRider))
		elif race.teamRankOption == race.teamRankBySumPoints:
			colnames[col] = u'{}\n{} {}'.format(_('Sum Points'), _('Top'), race.topTeamResults)
		elif race.teamRankOption == race.teamRankBySumTime:
			colnames[col] = u'{}\n{} {}'.format(_('Sum Time'), _('Top'), race.topTeamResults)
		elif race.teamRankOption == race.teamRankBySumPercentTime:
			colnames[col] = u'{}\n{} {}'.format(_('Sum %'), _('Top'), race.topTeamResults)
			
		if race.showNumTeamParticipants:
			colnames.append( _('Participants') )
		return colnames
	
	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 toExportGrid( self, category ):
		race = Model.race
		if not race:
			self.grid.ClearGrid()
			return None
		
		title = '\n'.join( [race.title, _('Team Results'), category.fullname,] )
		colnames = [c.replace('\n', ' ') for c in self.getColNames()]
		data = [[] for c in colnames]
		for pos, r in enumerate( GetTeamResults(category), 1 ):
			data[0].append( u'{}'.format(pos) )
			data[1].append( r.team )
			data[2].append( r.criteria )
			data[3].append( r.gap )
			if race.showNumTeamParticipants:
				data[4].append( u'{}'.format(r.numParticipants) )
		
		return ExportGrid.ExportGrid( colnames=colnames, data=data, title=title, leftJustifyCols=[1] )
	
	def refresh( self ):
		self.updateGrid()
		
	def commit( self ):
		pass
Exemple #22
0
class EventFinishOrder(EnablePanel):
    def __init__(self, parent):
        EnablePanel.__init__(self, parent)
        self.box = wx.StaticBox(
            self,
            label='Result (right-click to enter Bib numbers from keyboard)')
        boxSizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)

        self.event = None

        self.SetBackgroundColour(wx.WHITE)

        self.activeBar = EnableBar(self)
        self.activeBar.SetToolTip(
            wx.ToolTip('.\n'.join([
                'Record the Finish Order by dragging the row numbers in the table.',
                'Record DNF and DQ in the Status column.',
                'Then press OK or Cancel.'
            ])))

        vs = wx.BoxSizer(wx.VERTICAL)
        self.okButton = MakeRoundButton(self, 'OK')
        self.cancelButton = MakeRoundButton(self, 'Cancel', isCancel)
        vs.Add(self.okButton, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=4)
        vs.Add(self.cancelButton, flag=wx.ALL, border=4)

        self.headerNames = [
            'Bib', 'Name', 'Team', 'Status', 'Warn', 'Rel', 'Time    '
        ]
        self.iColStatus = self.headerNames.index('Status')
        self.iColWarning = self.headerNames.index('Warn')
        self.iColRelegation = self.headerNames.index('Rel')

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.CreateGrid(4, len(self.headerNames))
        self.grid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnClick)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.OnRightClick)

        font = GetFont()
        self.grid.SetLabelFont(font)
        for col in six.moves.range(self.grid.GetNumberCols()):
            self.grid.SetColLabelValue(col, self.headerNames[col])
            self.grid.SetCellValue(
                0, col, self.headerNames[col])  # Add the label as data.
            attr = gridlib.GridCellAttr()
            attr.SetFont(font)
            if self.headerNames[col] == 'Bib':
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_TOP)
                attr.SetReadOnly(True)
            elif self.headerNames[col].startswith('Time'):
                attr.SetEditor(HighPrecisionTimeEditor())
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
            elif col == 1 or col == 2:
                attr.SetReadOnly(True)
            elif col == self.iColStatus:
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(
                        choices=['DQ', 'DNF', 'DNS', '']))
            elif col == self.iColWarning or col == self.iColRelegation:
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                attr.SetEditor(gridlib.GridCellBoolEditor())
                attr.SetRenderer(gridlib.GridCellBoolRenderer())
            self.grid.SetColAttr(col, attr)

        self.grid.AutoSizeColumns(False)  # Resize to fit the column name.
        self.grid.AutoSizeRows(False)
        for col in six.moves.range(self.grid.GetNumberCols()):
            self.grid.SetCellValue(0, col, '')  # Remove the labels.

        boxSizer.Add(self.activeBar, 0, flag=wx.ALL | wx.EXPAND, border=4)
        boxSizer.Add(vs, 0, flag=wx.ALL, border=4)
        boxSizer.Add(self.grid, 1, flag=wx.ALL | wx.EXPAND, border=4)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(boxSizer, 1, flag=wx.EXPAND)
        self.SetSizer(sizer)

    def SetEnable(self, enable):
        EnablePanel.SetEnable(self, enable)
        for b, t in [(self.okButton, isSelect), (self.cancelButton, isCancel)]:
            EnableRoundButton(b, enable, t)
        self.activeBar.SetBackgroundColour(
            wx.Colour(0, 128, 0) if enable else wx.WHITE)
        self.Refresh()

    def OnClick(self, event):
        row = event.GetRow()
        col = event.GetCol()
        if col == self.iColWarning or col == self.iColRelegation:
            self.grid.SetCellValue(
                row, col, u'0' if (self.grid.GetCellValue(row, col)
                                   or u'0')[:1] in u'1TtYy' else u'1')
        else:
            event.Skip()

    def OnRightClick(self, event):
        ted = wx.TextEntryDialog(
            self, 'Enter Bib Numbers separated by space or comma',
            'Enter Bibs')
        ret = ted.ShowModal()
        v = ted.GetValue()
        ted.Destroy()
        if ret != wx.ID_OK:
            return

        oldBibOrder = [
            int(self.grid.GetCellValue(row, 0))
            for row in six.moves.range(self.grid.GetNumberRows())
        ]
        oldBibs = set(oldBibOrder)

        v = re.sub(r'[^\d]', u' ', v)
        newBibOrder = [int(f) for f in v.split()]
        newBibOrder = [b for b in newBibOrder if b in oldBibs]

        newBibs = set(newBibOrder)
        newBibOrder.extend(b for b in oldBibOrder if b not in newBibs)

        for row, bib in enumerate(newBibOrder):
            if oldBibOrder[row] != bib:
                i = oldBibOrder.index(bib)
                oldBibOrder[i], oldBibOrder[row] = oldBibOrder[
                    row], oldBibOrder[i]
                Utils.SwapGridRows(self.grid, row, i)

    def setEvent(self, event):
        self.event = event

    def refresh(self):
        if not self.event:
            self.grid.ClearGrid()
            return

        # Propose finish order by qualifying time.
        state = self.event.competition.state
        finishPositions = sorted(self.event.starts[-1].startPositions,
                                 key=lambda r: state.labels[r].qualifyingTime)
        Utils.AdjustGridSize(self.grid, rowsRequired=len(finishPositions))

        state = self.event.competition.state
        row = 0
        # List DNS starters at the end.
        for b in [False, True]:
            for p in finishPositions:
                rider = state.labels[p]
                if (rider.bib in CacheDNSs) == b:
                    for col, v in enumerate([
                            rider.bib, rider.full_name, rider.team,
                            'DNS' if rider.bib in CacheDNSs else '', ''
                    ]):
                        self.grid.SetCellValue(row, col, u' {}'.format(v))
                    row += 1

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

    def commit(self):
        self.grid.SaveEditControlValue()

        if not self.event:
            self.grid.ClearGrid()
            return

        iColTime = self.grid.GetNumberCols() - 1

        places = []
        times = []
        for row in six.moves.range(self.grid.GetNumberRows()):
            bib = self.grid.GetCellValue(row, 0)
            try:
                bib = int(bib)
            except:
                continue

            status = self.grid.GetCellValue(row, self.iColStatus)
            warning = self.grid.GetCellValue(row, self.iColWarning)
            relegation = self.grid.GetCellValue(row, self.iColRelegation)

            places.append((bib, status, warning, relegation))

            try:
                t = Utils.StrToSeconds(self.grid.GetCellValue(row, iColTime))
            except ValueError:
                continue
            times.append((row + 1, t))

        start = self.event.starts[-1]
        start.setPlaces(places)
        start.setTimes(times)

        self.event.propagate()
        Model.model.setChanged(True)
        Model.model.competition.propagate()
        Utils.setTitle()
Exemple #23
0
class TimeTrialRecord( wx.Panel ):
	def __init__( self, parent, controller, id = wx.ID_ANY ):
		wx.Panel.__init__(self, parent, id)
		self.SetBackgroundColour( wx.WHITE )

		self.controller = controller

		self.headerNames = [_('Time'), _('Bib')]
		
		self.maxRows = 10
		
		fontSize = 18
		self.font = wx.FontFromPixelSize( wx.Size(0,fontSize), wx.FONTFAMILY_SWISS, wx.NORMAL, wx.FONTWEIGHT_NORMAL )
		self.bigFont = wx.FontFromPixelSize( wx.Size(0,int(fontSize*1.3)), wx.FONTFAMILY_SWISS, wx.NORMAL, wx.FONTWEIGHT_NORMAL )
		self.vbs = wx.BoxSizer(wx.VERTICAL)
		
		tapForTimeLabel = _('Tap for Time')
		if 'WXMAC' in wx.Platform:
			self.recordTimeButton = wx.lib.buttons.ThemedGenButton( self, label=tapForTimeLabel )
			self.recordTimeButton.Bind( wx.EVT_BUTTON, self.doRecordTime )
		else:
			self.recordTimeButton = wx.Button( self, label=tapForTimeLabel )
			self.recordTimeButton.Bind( wx.EVT_LEFT_DOWN, self.doRecordTime )
		
		self.recordTimeButton.SetFont( self.bigFont )
		self.recordTimeButton.SetToolTip(wx.ToolTip(u'\n'.join(
			[_('Tap to Record Times (or press the "t" key).'), _('Then enter the Bib numbers and Save as soon as possible.')]) ))
		
		hbs = wx.BoxSizer( wx.HORIZONTAL )
		hbs.Add( self.recordTimeButton, 0 )
		
		self.grid = ReorderableGrid( self, style = wx.BORDER_SUNKEN )
		self.grid.SetFont( self.font )
		self.grid.EnableReorderRows( False )
		
		dc = wx.WindowDC( self.grid )
		dc.SetFont( self.font )
		width, height = dc.GetTextExtent(" 999 ")
		self.rowLabelSize = width
		self.grid.SetRowLabelSize( self.rowLabelSize )
		
		self.grid.CreateGrid( self.maxRows, len(self.headerNames) )
		self.grid.Bind( gridlib.EVT_GRID_LABEL_LEFT_CLICK, self.doClickLabel )
		for col, name in enumerate(self.headerNames):
			self.grid.SetColLabelValue( col, name )
		self.grid.SetLabelFont( self.font )
		for col in xrange(self.grid.GetNumberCols()):
			attr = gridlib.GridCellAttr()
			attr.SetFont( self.font )
			if col == 0:
				attr.SetEditor( HighPrecisionTimeEditor() )
			elif col == 1:
				attr.SetRenderer( gridlib.GridCellNumberRenderer() )
				attr.SetEditor( gridlib.GridCellNumberEditor() )
			self.grid.SetColAttr( col, attr )
		
		saveLabel = _('Save')
		if 'WXMAC' in wx.Platform:
			self.commitButton = wx.lib.buttons.ThemedGenButton( self, label=saveLabel )
		else:
			self.commitButton = wx.Button( self, label=saveLabel )
		self.commitButton.Bind( wx.EVT_BUTTON, self.doCommit )
		self.commitButton.SetFont( self.bigFont )
		self.commitButton.SetToolTip(wx.ToolTip(_('Save Entries (or press the "s" key)')))
		
		self.vbs.Add( hbs, 0, flag=wx.ALL|wx.EXPAND, border = 4 )
		self.vbs.Add( self.grid, 1, flag=wx.ALL|wx.EXPAND, border = 4 )
		self.vbs.Add( self.commitButton, flag=wx.ALL|wx.ALIGN_RIGHT, border = 4 )
		
		idRecordAcceleratorId, idCommitAccelleratorId = wx.NewId(), wx.NewId()
		self.Bind(wx.EVT_MENU, self.doRecordTime, id=idRecordAcceleratorId)
		self.Bind(wx.EVT_MENU, self.doCommit, id=idCommitAccelleratorId)
		accel_tbl = wx.AcceleratorTable([
			(wx.ACCEL_NORMAL,  ord('T'), idRecordAcceleratorId),
			(wx.ACCEL_NORMAL,  ord('S'), idCommitAccelleratorId),
		])
		self.SetAcceleratorTable(accel_tbl)
		
		self.SetSizer(self.vbs)
		self.Fit()
		
	def doClickLabel( self, event ):
		if event.GetCol() == 0:
			self.doRecordTime( event )
	
	def doRecordTime( self, event ):
		t = Model.race.curRaceTime()
		
		# Trigger the camera.
		with Model.LockRace() as race:
			if not race:
				return
			if race.enableUSBCamera:
				race.photoCount += TakePhoto( 0, StrToSeconds(formatTime(t)) )
	
		# Find the last row without a time.
		self.grid.SetGridCursor( 0, 0, )
		
		emptyRow = self.grid.GetNumberRows() + 1
		success = False
		for i in xrange(2):
			for row in xrange(self.grid.GetNumberRows()):
				if not self.grid.GetCellValue(row, 0):
					emptyRow = row
					break
			if emptyRow >= self.grid.GetNumberRows():
				self.doCommit( event )
			else:
				success = True
				break
		
		if not success:
			Utils.MessageOK( self, u'\n'.join([
                _('Insufficient space to Record Time.'),
                _('Enter Bib numbers and press Commit.'),
                _('Or delete some entries')]), _('Record Time Failed.') )
			return
			
		self.grid.SetCellValue( emptyRow, 0, formatTime(t) )
		
		# Set the edit cursor at the first empty bib position.
		for row in xrange(self.grid.GetNumberRows()):
			text = self.grid.GetCellValue(row, 1)
			if not text or text == '0':
				self.grid.SetGridCursor( row, 1 )
				break
		
	def doCommit( self, event ):
		self.grid.SetGridCursor( 0, 0, )
	
		# Find the last row without a time.
		timesBibs = []
		timesNoBibs = []
		for row in xrange(self.grid.GetNumberRows()):
			tStr = self.grid.GetCellValue(row, 0).strip()
			if not tStr:
				continue
			
			bib = self.grid.GetCellValue(row, 1).strip()
			try:
				bib = int(bib)
			except (TypeError, ValueError):
				bib = 0
			
			if bib:
				timesBibs.append( (tStr, bib) )
			else:
				timesNoBibs.append( tStr )
				
		for row in xrange(self.grid.GetNumberRows()):
			for column in xrange(self.grid.GetNumberCols()):
				self.grid.SetCellValue(row, column, '' )
		
		'''
		for row, tStr in enumerate(timesNoBibs):
			self.grid.SetCellValue( row, 0, tStr )
		'''
			
		self.grid.SetGridCursor( 0, 1 )
			
		if timesBibs and Model.race:
			with Model.LockRace() as race:
				bibRaceSeconds = []
				
				for tStr, bib in timesBibs:
					raceSeconds = StrToSeconds(tStr)
					race.addTime( bib, raceSeconds )
					OutputStreamer.writeNumTime( bib, raceSeconds )
					bibRaceSeconds.append( (bib, raceSeconds) )
				
			wx.CallAfter( Utils.refresh )
			
		self.grid.SetGridCursor( 0, 1 )
	
	def refresh( self ):
		self.grid.AutoSizeRows( False )
		
		dc = wx.WindowDC( self.grid )
		dc.SetFont( self.font )
		
		widthTotal = self.rowLabelSize
		width, height = dc.GetTextExtent(" 00:00:00.000 ")
		self.grid.SetColSize( 0, width )
		widthTotal += width
		
		width, height = dc.GetTextExtent(" 9999 ")
		self.grid.SetColSize( 1, width )
		widthTotal += width
		
		scrollBarWidth = 48
		self.grid.SetSize( (widthTotal + scrollBarWidth, -1) )
		self.GetSizer().SetMinSize( (widthTotal + scrollBarWidth, -1) )
		
		self.grid.ForceRefresh()
		self.Fit()
		
		wx.CallAfter( self.recordTimeButton.SetFocus )
		
	def commit( self ):
		pass
Exemple #24
0
class Qualifiers(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        font = GetFont()
        self.title = wx.StaticText(
            self, wx.ID_ANY,
            "Enter each rider's qualifying time in hh:mm:ss.ddd format.  Use a colon ':' a space, or a dash '-' to separate hour, minute and seconds."
        )
        self.title.SetFont(font)

        self.renumberButton = wx.Button(self, wx.ID_ANY,
                                        'Renumber Bibs by Time')
        self.renumberButton.SetFont(font)
        self.renumberButton.Bind(wx.EVT_BUTTON, self.doRenumber)

        hs = wx.BoxSizer(wx.HORIZONTAL)
        hs.Add(self.title, 0, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=6)
        hs.AddStretchSpacer()
        hs.Add(self.renumberButton, 0, flag=wx.ALL, border=6)

        self.headerNames = ['Bib', 'Name', 'Team', 'Time', 'Status']
        self.iTime = next(i for i, n in enumerate(self.headerNames)
                          if n.startswith('Time'))
        self.iStatus = next(i for i, n in enumerate(self.headerNames)
                            if n.startswith('Status'))

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(64)
        self.grid.CreateGrid(0, len(self.headerNames))
        self.setColNames()
        self.grid.EnableReorderRows(False)

        # Set specialized editors for appropriate columns.
        self.grid.SetLabelFont(font)
        for col in six.moves.range(self.grid.GetNumberCols()):
            attr = gridlib.GridCellAttr()
            attr.SetFont(font)
            if col == self.iTime:
                attr.SetEditor(HighPrecisionTimeEditor())
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
            elif col == self.iStatus:
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(choices=['', 'DNQ']))
                attr.SetReadOnly(False)
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
            else:
                if col == 0:
                    attr.SetRenderer(gridlib.GridCellNumberRenderer())
                attr.SetReadOnly(True)
            self.grid.SetColAttr(col, attr)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(hs, 0, flag=wx.ALL | wx.EXPAND, border=6)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=6)
        self.SetSizer(sizer)

    def getGrid(self):
        return self.grid

    def setColNames(self):
        for col, headerName in enumerate(self.headerNames):
            self.grid.SetColLabelValue(col, headerName)

    def setTestData(self):
        self.grid.ClearGrid()

        testData = TestData.getTestData()
        Utils.AdjustGridSize(self.grid, rowsRequired=len(testData))

        for row, data in enumerate(testData):
            bib = data[0]
            name = data[1] + ' ' + data[2]
            team = data[3]
            time = data[-1]
            for col, d in enumerate([bib, name, team, time]):
                self.grid.SetCellValue(row, col, u' {}'.format(d))

        # Fix up the column and row sizes.
        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

    def refresh(self):
        model = Model.model
        riders = model.riders

        self.renumberButton.Show(model.competition.isMTB)

        Utils.AdjustGridSize(self.grid, rowsRequired=len(riders))
        for row, r in enumerate(riders):
            for col, value in enumerate([
                    u'{}'.format(r.bib), r.full_name, r.team,
                    r.qualifyingTimeText
            ]):
                self.grid.SetCellValue(row, col, value)

        # Fix up the column and row sizes.
        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)
        self.grid.SetColSize(self.grid.GetNumberCols() - 1, 96)

        self.Layout()
        self.Refresh()

    def setQT(self):
        # The qualifying times can be changed at any time, however, if the competition is under way, the events cannot
        # be adjusted.
        model = Model.model
        riders = model.riders

        self.grid.SaveEditControlValue()

        for row in six.moves.range(self.grid.GetNumberRows()):
            v = self.grid.GetCellValue(row, self.iTime).strip()
            if v:
                qt = Utils.StrToSeconds(v)
            else:
                qt = Model.QualifyingTimeDefault

            qt = min(qt, Model.QualifyingTimeDefault)
            status = self.grid.GetCellValue(row, self.iStatus).strip()

            rider = riders[row]
            if rider.qualifyingTime != qt or rider.status != status:
                rider.qualifyingTime = qt
                rider.status = status
                model.setChanged(True)

    def commit(self):
        # The qualifying times can be changed at any time, however, if the competition is underway, the events cannot
        # be adusted.
        model = Model.model
        riders = model.riders
        self.setQT()
        if model.canReassignStarters():
            model.setQualifyingTimes()
            Utils.getMainWin().resetEvents()

    def doRenumber(self, event):
        if not Utils.MessageOKCancel(
                self,
                'Sequence Bib numbers in Increasing Order by Qualifying Time.\n\nContinue?',
                'Renumber Riders'):
            return

        self.setQT()

        model = Model.model
        riders = sorted(model.riders, key=lambda x: x.keyQualifying())
        for r, rider in enumerate(riders, 1):
            rider.bib = r

        wx.CallAfter(self.refresh)
Exemple #25
0
class Seeding(wx.Panel):
	#----------------------------------------------------------------------
	
	phase = 'Seeding'

	def __init__(self, parent):
		wx.Panel.__init__(self, parent)
 
		font = wx.Font( (0,FontSize), wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL )
		
		self.title = wx.StaticText(self, wx.ID_ANY, u"Seeding" + u':')
		self.title.SetFont( font )
		
		self.communiqueLabel = wx.StaticText( self, label=u'Communiqu\u00E9:' )
		self.communiqueLabel.SetFont( font )
		self.communiqueNumber = wx.TextCtrl( self, size=(64,-1) )
		self.communiqueNumber.SetFont( font )
		
		self.randomizeButton = wx.Button( self, wx.ID_ANY, 'Randomize...' )
		self.randomizeButton.Bind( wx.EVT_BUTTON, self.doRandomize )
		self.randomizeButton.SetFont( font )
 
		self.importButton = wx.Button( self, wx.ID_ANY, 'Import From Excel' )
		self.importButton.Bind( wx.EVT_BUTTON, self.doImportFromExcel )
		self.importButton.SetFont( font )
 
		self.headerNames = ['Bib', 'First Name', 'Last Name', 'Team', 'Team Code', 'License']
		
		self.grid = ReorderableGrid( self, style = wx.BORDER_SUNKEN )
		self.grid.DisableDragRowSize()
		self.grid.SetRowLabelSize( 64 )
		self.grid.CreateGrid( 200, len(self.headerNames) )
		self.setColNames()

		# Set specialized editors for appropriate columns.
		self.grid.SetLabelFont( font )
		for col in six.moves.range(self.grid.GetNumberCols()):
			attr = gridlib.GridCellAttr()
			attr.SetFont( font )
			if col == 0:
				attr.SetRenderer( gridlib.GridCellNumberRenderer() )
				attr.SetEditor( gridlib.GridCellNumberEditor() )
			self.grid.SetColAttr( col, attr )
		
		hs = wx.BoxSizer( wx.HORIZONTAL )
		hs.Add( self.title, 0, flag=wx.ALIGN_CENTRE_VERTICAL|wx.RIGHT, border = 8 )
		hs.Add( self.communiqueLabel, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL, border=4 )
		hs.Add( self.communiqueNumber, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, border=4 )
		hs.AddStretchSpacer()
		hs.Add( self.randomizeButton, 0, flag=wx.ALL, border=4 )
		hs.Add( self.importButton, 0, flag=wx.ALL, border=4 )
		
		sizer = wx.BoxSizer(wx.VERTICAL)
		sizer.Add( hs, 0, flag=wx.ALL|wx.EXPAND, border=6 )
		sizer.Add( wx.StaticText(self, label=u'Set Bib to 0 to Delete row.  Drag-and-drop row numbers to Change Sequence.'), flag=wx.LEFT, border = 8 )
		sizer.Add(self.grid, 1, flag=wx.EXPAND|wx.ALL, border=6)
		self.SetSizer(sizer)
		
	def setColNames( self ):
		for col, headerName in enumerate(self.headerNames):
			self.grid.SetColLabelValue( col, headerName )
						
	def setTestData( self ):
		self.grid.ClearGrid()
		
		testData = TestData.getTestData()
		for row, data in enumerate(testData):
			for col, d in enumerate(data):
				self.grid.SetCellValue( row, col,u' {}'.format(d) )
		
		# Fix up the column and row sizes.
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )
		
	def getGrid( self ):
		return self.grid
	
	def doRandomize( self, event ):
		self.commit()
		self.refresh()
		
		selectedRows = self.grid.GetSelectedRows()
		if len(selectedRows) < 2:
			Utils.MessageOK( self, u'Please select some Rows to Randomize.', u'Insufficient Selected Rows' )
			return
			
		rMin = min( selectedRows )
		rMax = max( selectedRows ) + 1
		
		model = Model.model
		if len(model.riders) <= rMin:
			return
		rMax = min( rMax, len(model.riders) )
		
		if not Utils.MessageOKCancel( self, u'Randomize Rows {}-{} ?'.format(rMin+1, rMax), 'Confirm Randomize' ):
			return
		
		toRandomize = model.riders[rMin:rMax]
		random.shuffle( toRandomize )
		model.riders[rMin:rMax] = toRandomize
		self.refresh()
	
	def doImportFromExcel( self, event ):
		ImportStartList( self )
		
	def getTitle( self ):
		title = u'Communique: {}\n{} '.format(
					self.communiqueNumber.GetValue(),
					u'Qualifier Seeding' )
		return title
	
	def refresh( self ):
		riders = Model.model.riders
		for row, r in enumerate(riders):
			for col, value in enumerate([u'{}'.format(r.bib), r.first_name, r.last_name, r.team, r.team_code, r.license]):
				self.grid.SetCellValue( row, col, value )
				
		for row in six.moves.range(len(riders), self.grid.GetNumberRows()):
			for col in six.moves.range(self.grid.GetNumberCols()):
				self.grid.SetCellValue( row, col, u'' )
				
		# Fix up the column and row sizes.
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )
		
		# Sync the communique value.
		self.communiqueNumber.SetValue( Model.model.communique_number.get(self.phase, u'') )
		
		self.Layout()
		self.Refresh()
				
	def commit( self ):
		self.grid.SaveEditControlValue()

		riders = []
		for row in six.moves.range(self.grid.GetNumberRows()):
			fields = {}
			for col, attr in enumerate(['bib', 'first_name', 'last_name', 'team', 'team_code', 'license']):
				fields[attr] = self.grid.GetCellValue(row, col).strip()
				
			try:
				bib = int(fields['bib'])
			except ValueError:
				continue
				
			if bib:
				fields['bib'] = bib
				riders.append( Model.Rider(**fields) )
		
		model = Model.model
		
		cn = self.communiqueNumber.GetValue()
		if cn != model.communique_number.get(self.phase, ''):
			model.communique_number[self.phase] = self.communiqueNumber.GetValue()
			model.setChanged()
		
		oldRiders = dict( (r.bib, r) for r in model.riders )
		oldPosition = dict( (r.bib, p) for p, r in enumerate(model.riders) )
		
		# Check for changes to the seeding.
		changed =  (len(riders) != len(model.riders))
		changed |= any( position != oldPosition.get(r.bib, -1) for position, r in enumerate(riders) )
		changed |= any( r.keyDataFields() != oldRiders.get(r.bib, Model.Rider(-1)).keyDataFields() for r in riders )
		if not changed:
			return
		
		model.setChanged( True )
		
		# Update riders if the competition has not yet started.
		if model.canReassignStarters():
			for r in riders:
				try:
					oldRiders[r.bib].copyDataFields( r )
				except KeyError:
					oldRiders[r.bib] = r
			model.riders = [oldRiders[r.bib] for r in riders]
			model.setQualifyingTimes()
			Utils.getMainWin().resetEvents()
			return
		
		if len(riders) != len(model.riders):
			for r in riders:
				if r.bib in oldRiders:
					oldRiders[r.bib].copyDataFields( r )
			Utils.MessageOK( self, u'Cannot Add or Delete Riders after Competition has Started', u'Cannot Add or Delete Riders' )
			self.refresh()
			return
		
		if not all( r.bib in oldRiders for r in riders ):
			for r in riders:
				if r.bib in oldRiders:
					oldRiders[r.bib].copyDataFields( r )
			Utils.MessageOK( self, u'Cannot Change Bib Numbers after Competition has Started', u'Cannot Change Bib Numbers' )
			self.refresh()
			return
		
		# All the bib numbers match - just change the info and update the sequence.
		model.riders = [oldRiders[r.bib].copyDataFields(r) for r in riders]
		model.updateSeeding()
Exemple #26
0
class Categories(wx.Panel):
    CategoryTypeChoices = [
        _('Start Wave'), u'    ' + _('Component'),
        _('Custom')
    ]
    DistanceTypeChoices = [_('Lap'), _('Race')]

    def __init__(self, parent, id=wx.ID_ANY):
        wx.Panel.__init__(self, parent, id)

        self.state = RaceInputState()

        vs = wx.BoxSizer(wx.VERTICAL)

        self.ignoreColour = wx.Colour(80, 80, 80)
        self.inactiveColour = wx.Colour(200, 200, 200)

        border = 4
        flag = wx.ALL

        hs = wx.BoxSizer(wx.HORIZONTAL)

        self.activateAllButton = wx.Button(self,
                                           label=_('Activate All'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onActivateAll, self.activateAllButton)
        hs.Add(self.activateAllButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)

        self.newCategoryButton = wx.Button(self,
                                           label=_('New'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onNewCategory, self.newCategoryButton)
        hs.Add(self.newCategoryButton, 0, border=border, flag=flag)

        self.delCategoryButton = wx.Button(self,
                                           label=_('Delete'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onDelCategory, self.delCategoryButton)
        hs.Add(self.delCategoryButton,
               0,
               border=border,
               flag=(flag & ~wx.LEFT))

        hs.AddSpacer(6)

        self.upCategoryButton = wx.Button(self,
                                          label=u'\u2191',
                                          style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onUpCategory, self.upCategoryButton)
        hs.Add(self.upCategoryButton, 0, border=border, flag=flag)

        self.downCategoryButton = wx.Button(self,
                                            label=u'\u2193',
                                            style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onDownCategory, self.downCategoryButton)
        hs.Add(self.downCategoryButton,
               0,
               border=border,
               flag=(flag & ~wx.LEFT))

        hs.AddSpacer(6)

        self.setGpxDistanceButton = wx.Button(self,
                                              label=_('Set Gpx Distance'),
                                              style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onSetGpxDistance,
                  self.setGpxDistanceButton)
        hs.Add(self.setGpxDistanceButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)

        self.addExceptionsButton = wx.Button(self,
                                             label=_('Bib Exceptions'),
                                             style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onAddExceptions,
                  self.addExceptionsButton)
        hs.Add(self.addExceptionsButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)
        '''
		self.updateStartWaveNumbersButton = wx.Button(self, label=_('Update Start Wave Bibs'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onUpdateStartWaveNumbers, self.updateStartWaveNumbersButton )
		hs.Add( self.updateStartWaveNumbersButton, 0, border = border, flag = flag )
		'''

        self.normalizeButton = wx.Button(self,
                                         label=_('Normalize'),
                                         style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onNormalize, self.normalizeButton)
        hs.Add(self.normalizeButton, 0, border=border, flag=flag)

        hs.AddStretchSpacer()

        self.printButton = wx.Button(self,
                                     label=u'{}...'.format(_('Print')),
                                     style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onPrint, self.printButton)
        hs.Add(self.printButton, 0, border=border, flag=flag)

        self.excelButton = wx.Button(self,
                                     label=u'{}...'.format(_('Excel')),
                                     style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onExcel, self.excelButton)
        hs.Add(self.excelButton, 0, border=border, flag=flag)

        self.grid = ReorderableGrid(self)
        self.colNameFields = [
            (u'', None),
            (_('Category Type'), 'catType'),
            (_('Active'), 'active'),
            (_('Name'), 'name'),
            (_('Gender'), 'gender'),
            (_('Numbers'), 'catStr'),
            (_('Start\nOffset'), 'startOffset'),
            (_('Race\nLaps'), 'numLaps'),
            (_('Race\nMinutes'), 'raceMinutes'),
            (_('Lapped\nRiders\nContinue'), 'lappedRidersMustContinue'),
            (_('Distance'), 'distance'),
            (_('Dist.\nBy'), 'distanceType'),
            (_('First\nLap\nDist.'), 'firstLapDistance'),
            (_('80%\nLap\nTime'), 'rule80Time'),
            (_('CrossMgr\nEstimated\nLaps'), 'suggestedLaps'),
            (_('Publish'), 'publishFlag'),
            (_('Upload'), 'uploadFlag'),
            (_('Series'), 'seriesFlag'),
        ]
        self.computedFields = {'rule80Time', 'suggestedLaps'}
        self.colnames = [
            colName if not colName.startswith('_') else _('Name Copy')
            for colName, fieldName in self.colNameFields
        ]
        self.iCol = {
            fieldName: i
            for i, (colName, fieldName) in enumerate(self.colNameFields)
            if fieldName and not colName.startswith('_')
        }

        self.activeColumn = self.iCol['active']
        self.genderColumn = self.iCol['gender']
        self.numbersColumn = self.iCol['catStr']
        self.grid.CreateGrid(0, len(self.colnames))
        self.grid.SetRowLabelSize(32)
        self.grid.SetMargins(0, 0)
        for col, name in enumerate(self.colnames):
            self.grid.SetColLabelValue(col, name)

        self.cb = None

        self.boolCols = set()
        self.choiceCols = set()
        self.readOnlyCols = set()
        self.dependentCols = set()

        # Set column attributes for the table.
        for col, (colName, fieldName) in enumerate(self.colNameFields):
            attr = gridlib.GridCellAttr()

            if fieldName is None:
                attr.SetRenderer(CategoryIconRenderer())
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                attr.SetReadOnly(True)
                self.readOnlyCols.add(col)

            elif fieldName == 'catType':
                self.catTypeWidth = 64
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(self.CategoryTypeChoices,
                                                 False))
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                self.choiceCols.add(col)

            elif fieldName in {
                    'active', 'lappedRidersMustContinue', 'publishFlag',
                    'uploadFlag', 'seriesFlag'
            }:
                boolEditor = gridlib.GridCellBoolEditor()
                boolEditor.UseStringValues('1', '0')
                attr.SetEditor(boolEditor)
                attr.SetRenderer(gridlib.GridCellBoolRenderer())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.boolCols.add(col)
                if fieldName == 'lappedRidersMustContinue':
                    self.dependentCols.add(col)

            elif fieldName == 'gender':
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(
                        [_('Open'), _('Men'), _('Women')], False))
                self.choiceCols.add(col)

            elif fieldName == 'startOffset':
                attr.SetEditor(TimeEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'numLaps':
                attr.SetEditor(wx.grid.GridCellNumberEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'raceMinutes':
                attr.SetEditor(wx.grid.GridCellNumberEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName in ['rule80Time', 'suggestedLaps']:
                attr.SetReadOnly(True)
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.readOnlyCols.add(col)
                self.dependentCols.add(col)

            elif fieldName in ['distance', 'firstLapDistance']:
                attr.SetEditor(gridlib.GridCellFloatEditor(7, 3))
                attr.SetRenderer(gridlib.GridCellFloatRenderer(7, 3))
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'distanceType':
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(self.DistanceTypeChoices,
                                                 False))
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
                self.choiceCols.add(col)
                self.dependentCols.add(col)

            elif colName == '_name2':
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                attr.SetBackgroundColour(wx.Colour(240, 240, 240))
                attr.SetReadOnly(True)

            self.grid.SetColAttr(col, attr)

        self.Bind(gridlib.EVT_GRID_CELL_LEFT_CLICK, self.onGridLeftClick)
        self.Bind(gridlib.EVT_GRID_SELECT_CELL, self.onCellSelected)
        self.Bind(gridlib.EVT_GRID_CELL_CHANGED, self.onCellChanged)
        self.Bind(gridlib.EVT_GRID_EDITOR_CREATED, self.onEditorCreated)

        vs.Add(hs, 0, flag=wx.EXPAND | wx.ALL, border=4)
        vs.Add(self.grid, 1, flag=wx.GROW | wx.ALL | wx.EXPAND)

        self.rowCur = 0
        self.colCur = 0
        self.SetSizer(vs)

    def onPrint(self, event):
        self.commit()
        PrintCategories()

    def onExcel(self, event):
        self.commit()
        export = getExportGrid()
        xlFName = Utils.getMainWin().getFormatFilename('excel')
        xlFName = os.path.splitext(
            xlFName)[0] + '-Categories' + os.path.splitext(xlFName)[1]

        wb = xlwt.Workbook()
        sheetCur = wb.add_sheet(_('Categories'))
        export.toExcelSheet(sheetCur)
        try:
            wb.save(xlFName)
            if Utils.getMainWin().launchExcelAfterPublishingResults:
                Utils.LaunchApplication(xlFName)
            Utils.MessageOK(
                self, u'{}:\n\n   {}'.format(_('Excel file written to'),
                                             xlFName), _('Excel Write'))
        except IOError:
            Utils.MessageOK(
                self,
                u'{} "{}"\n\n{}\n{}'.format(
                    _('Cannot write'), xlFName,
                    _('Check if this spreadsheet is already open.'),
                    _('If so, close it, and try again.')),
                _('Excel File Error'),
                iconMask=wx.ICON_ERROR)

    def onSetGpxDistance(self, event):
        race = Model.race
        geoTrack = getattr(race, 'geoTrack', None)
        if not geoTrack:
            return
        if not Utils.MessageOK(self,
                               _('Set the GPX distance for all Categories?'),
                               _('Set GPX Distance'), wx.ICON_QUESTION):
            return
        distance = geoTrack.lengthKm if race.distanceUnit == Model.Race.UnitKm else geoTrack.lengthMiles
        for category in race.getCategories():
            category.distance = distance
        race.setChanged()
        self.refresh(forceRefresh=True)

    def onNormalize(self, event):
        self.commit()
        if Model.race:
            Model.race.normalizeCategories()
            self.state.reset()
            self.refresh()

    #------------------------------------------

    def onGridLeftClick(self, event):
        if event.GetCol() in self.boolCols:
            r, c = event.GetRow(), event.GetCol()
            if c == self.iCol['active']:
                active = (self.grid.GetCellValue(r,
                                                 self.iCol['active']) == u'1')
                wx.CallAfter(
                    self.fixRow, r,
                    self.CategoryTypeChoices.index(
                        self.grid.GetCellValue(r, self.iCol['catType'])),
                    not active)
            self.grid.SetCellValue(
                r, c, '1' if self.grid.GetCellValue(r, c)[:1] != '1' else '0')
        event.Skip()

    def onCellSelected(self, event):
        self.rowCur = event.GetRow()
        self.colCur = event.GetCol()
        if self.colCur in self.choiceCols or self.colCur in self.boolCols:
            wx.CallAfter(self.grid.EnableCellEditControl)
        event.Skip()

    def onCellChanged(self, event):
        self.rowCur = event.GetRow()
        self.colCur = event.GetCol()
        if self.colCur in [1, 2]:
            wx.CallAfter(self.fixCells)
        event.Skip()

    def onEditorCreated(self, event):
        if event.GetCol() == self.numbersColumn:
            ctrl = event.GetControl()
            ctrl.Bind(wx.EVT_KEY_DOWN, self.onNumbersKeyEvent)
            ctrl.Bind(wx.EVT_TEXT_PASTE, self.onPaste)
        event.Skip()

    def getCleanClipboardText(self):
        if wx.TheClipboard.Open():
            data = wx.TextDataObject()
            if wx.TheClipboard.GetData(data):
                txt = data.GetText()
                txt = re.sub('[^0-9,-]+', ',', txt)
            wx.TheClipboard.Close()
            return txt
        return None

    def isExternalChange(self):
        if not Model.race:
            return False

        categories = Model.race.getAllCategories()

        for cat in categories:
            try:
                cat.distance = float(cat.distance)
            except:
                cat.distance = None
            try:
                cat.firstLapDistance = float(cat.firstLapDistance)
            except:
                cat.firstLapDistance = None

        if self.grid.GetNumberRows() != len(categories):
            return True

        def distanceMatches(distance, cellValue):
            try:
                value = float(cellValue)
            except ValueError:
                value = None

            if not distance and not value:
                return True
            return u'{:.3f}'.format(distance or 0.0) == cellValue

        def numLapsMatches(numLaps, cellValue):
            v = u'{}'.format(numLaps if numLaps is not None else '')
            return v == cellValue

        return any((
            cat.name != self.grid.GetCellValue(r, self.iCol['name'])
            or cat.catStr != self.grid.GetCellValue(r, self.iCol['catStr'])
            or not distanceMatches(
                cat.distance, self.grid.GetCellValue(r, self.iCol['distance']))
            or not distanceMatches(
                cat.firstLapDistance,
                self.grid.GetCellValue(r, self.iCol['firstLapDistance']))
            or not numLapsMatches(
                cat.numLaps, self.grid.GetCellValue(r, self.iCol['numLaps'])))
                   for r, cat in enumerate(categories))

    def pasteFromClipboard(self, event):
        txt = self.getCleanClipboardText()
        if txt:
            event.GetEventObject().WriteText(txt)
            return True
        return False

    def onNumbersKeyEvent(self, event):
        # Handle column pastes from Excel when there are newlines.
        if event.GetModifiers() == wx.MOD_CONTROL and event.GetKeyCode() == 86:
            if self.pasteFromClipboard(event):
                return
        event.Skip()

    def onPaste(self, event):
        self.pasteFromClipboard(event)
        event.Skip()

    #------------------------------------------

    def onUpdateStartWaveNumbers(self, event):
        self.commit()
        undo.pushState()
        with Model.LockRace() as race:
            race.adjustAllCategoryWaveNumbers()
        self.state.reset()
        wx.CallAfter(self.refresh)
        wx.CallAfter(Utils.refreshForecastHistory)

    def onAddExceptions(self, event):
        with Model.LockRace() as race:
            if not race or not race.getAllCategories():
                return

        r = self.grid.GetGridCursorRow()
        if r is None or r < 0:
            Utils.MessageOK(self, _('You must select a Category first'),
                            _('Select a Category'))
            return

        with Model.LockRace() as race:
            categories = race.getAllCategories()
            category = categories[r]

        dlg = wx.TextEntryDialog(
            self, u'{}: {}'.format(
                category.name,
                _('''Add Bib Exceptions (comma separated).
This will add the given list of Bibs to this category,
and remove them from other categories.'''),
            ), _('Add Bib Exceptions'))
        good = (dlg.ShowModal() == wx.ID_OK)
        if good:
            response = dlg.GetValue()
        dlg.Destroy()
        if not good:
            return

        undo.pushState()
        response = re.sub('[^0-9,]', '', response.replace(' ', ','))
        with Model.LockRace() as race:
            for numException in response.split(','):
                race.addCategoryException(category, numException)

        self.state.reset()
        self.refresh()

    def _setRow(
        self,
        r,
        active,
        name,
        catStr,
        startOffset='00:00:00',
        numLaps=None,
        raceMinutes=None,
        lappedRidersMustContinue=False,
        distance=None,
        distanceType=None,
        firstLapDistance=None,
        gender=None,
        catType=Model.Category.CatWave,
        publishFlag=True,
        uploadFlag=True,
        seriesFlag=True,
    ):

        if len(startOffset) < len('00:00:00'):
            startOffset = '00:' + startOffset

        GetTranslation = _
        gender = gender if gender in ['Men', 'Women'] else 'Open'
        self.grid.SetRowLabelValue(r, u'')
        self.grid.SetCellValue(r, self.iCol['active'],
                               u'1' if active else u'0')
        self.grid.SetCellValue(r, self.iCol['catType'],
                               self.CategoryTypeChoices[catType])
        self.grid.SetCellValue(r, self.iCol['name'], name)
        self.grid.SetCellValue(r, self.iCol['gender'], GetTranslation(gender))
        self.grid.SetCellValue(r, self.iCol['catStr'], catStr)
        self.grid.SetCellValue(r, self.iCol['startOffset'], startOffset)
        self.grid.SetCellValue(r, self.iCol['numLaps'],
                               u'{}'.format(numLaps) if numLaps else u'')
        self.grid.SetCellValue(
            r, self.iCol['raceMinutes'],
            u'{}'.format(raceMinutes) if raceMinutes else u'')
        self.grid.SetCellValue(r, self.iCol['lappedRidersMustContinue'],
                               u'1' if lappedRidersMustContinue else u'0')
        self.grid.SetCellValue(r, self.iCol['rule80Time'], u'')
        self.grid.SetCellValue(r, self.iCol['suggestedLaps'], u'')
        self.grid.SetCellValue(r, self.iCol['distance'],
                               ('%.3f' % distance) if distance else u'')
        self.grid.SetCellValue(
            r, self.iCol['distanceType'],
            self.DistanceTypeChoices[distanceType if distanceType else 0])
        self.grid.SetCellValue(r, self.iCol['firstLapDistance'],
                               ('%.3f' %
                                firstLapDistance) if firstLapDistance else '')
        self.grid.SetCellValue(r, self.iCol['publishFlag'],
                               u'1' if publishFlag else u'0')
        self.grid.SetCellValue(r, self.iCol['uploadFlag'],
                               u'1' if uploadFlag else u'0')
        self.grid.SetCellValue(r, self.iCol['seriesFlag'],
                               u'1' if seriesFlag else u'0')

        race = Model.race
        category = race.categories.get(u'{} ({})'.format(name.strip(), gender),
                                       None) if race else None
        if not category or category.catType != Model.Category.CatWave:
            return

        # Get the 80% time cutoff.
        if not active or not Model.race:
            return

        rule80Time = race.getRule80CountdownTime(category) if race else None
        if rule80Time:
            self.grid.SetCellValue(r, self.iCol['rule80Time'],
                                   Utils.formatTime(rule80Time))

        laps = race.getCategoryRaceLaps().get(category, 0) if race else None
        if laps:
            self.grid.SetCellValue(r, self.iCol['suggestedLaps'],
                                   u'{}'.format(laps))

    def fixRow(self, row, catType, active):
        activeColour = wx.WHITE if active else self.inactiveColour
        colour = activeColour if catType == Model.Category.CatWave else self.ignoreColour
        for colName, fieldName in self.colNameFields:
            if not fieldName:
                continue
            col = self.iCol[fieldName]
            self.grid.SetCellBackgroundColour(
                row, col,
                colour if col in self.dependentCols else activeColour)

    def fixCells(self, event=None):
        for row in xrange(self.grid.GetNumberRows()):
            active = self.grid.GetCellValue(row,
                                            self.iCol['active'])[:1] in 'TtYy1'
            catType = self.CategoryTypeChoices.index(
                self.grid.GetCellValue(row, self.iCol['catType']))
            self.fixRow(row, catType, active)

    def onActivateAll(self, event):
        self.commit()
        if Model.race:
            for c in Model.race.getAllCategories():
                if not c.active:
                    c.active = True
                    Model.race.setChanged()
        self.state.reset()
        wx.CallAfter(self.refresh)

    def onDeactivateAll(self, event):
        self.commit()
        if Model.race:
            for c in Model.race.getAllCategories():
                if c.active:
                    c.active = False
                    Model.race.setChanged()
        self.state.reset()
        wx.CallAfter(self.refresh)

    def doAutosize(self):
        self.grid.AutoSizeColumns(False)
        colWidth = self.grid.GetColSize(self.iCol['catStr'])
        maxWidth = wx.GetDisplaySize().width / 3
        if colWidth > maxWidth:
            self.grid.SetColSize(self.iCol['catStr'], maxWidth)
            self.grid.ForceRefresh()

    def onNewCategory(self, event):
        self.grid.AppendRows(1)
        self._setRow(r=self.grid.GetNumberRows() - 1,
                     active=True,
                     name=u'<{}>     '.format(_('CategoryName')),
                     catStr='100-199,504,-128')
        self.doAutosize()

    def onDelCategory(self, event):
        r = self.grid.GetGridCursorRow()
        if r is None or r < 0:
            return
        if Utils.MessageOKCancel(
                self, u'{} "{} ({})"?'.format(
                    _('Delete Category'),
                    self.grid.GetCellValue(r, 3).strip(),
                    self.grid.GetCellValue(r, 4).strip(),
                ), _('Delete Category')):
            self.grid.DeleteRows(r, 1, True)

    def onUpCategory(self, event):
        self.grid.SaveEditControlValue()
        self.grid.DisableCellEditControl()
        r = self.grid.GetGridCursorRow()
        Utils.SwapGridRows(self.grid, r, r - 1)
        if r - 1 >= 0:
            self.grid.MoveCursorUp(False)
        self.grid.ClearSelection()
        self.grid.SelectRow(max(r - 1, 0), True)

    def onDownCategory(self, event):
        self.grid.SaveEditControlValue()
        self.grid.DisableCellEditControl()
        r = self.grid.GetGridCursorRow()
        Utils.SwapGridRows(self.grid, r, r + 1)
        if r + 1 < self.grid.GetNumberRows():
            self.grid.MoveCursorDown(False)
        self.grid.ClearSelection()
        self.grid.SelectRow(min(r + 1, self.grid.GetNumberRows() - 1), True)

    def refresh(self, forceRefresh=False):
        self.setGpxDistanceButton.Enable(hasattr(Model.race, 'geoTrack'))

        if not (forceRefresh or self.isExternalChange()
                or self.state.changed()):
            return

        # Fix the height of the column labels.
        dc = wx.WindowDC(self.grid)
        dc.SetFont(self.grid.GetLabelFont())
        textHeight = dc.GetTextExtent('Label')[1]
        self.colLabelHeight = textHeight * max(
            name.count('\n') + 1 for name in self.colnames) + textHeight // 4
        self.grid.SetColLabelSize(self.colLabelHeight)

        with Model.LockRace() as race:
            self.grid.ClearGrid()
            if race is None:
                return

            for c in xrange(self.grid.GetNumberCols()):
                if self.grid.GetColLabelValue(c).startswith(_('Distance')):
                    self.grid.SetColLabelValue(
                        c,
                        u'{}\n({})'.format(_('Distance'),
                                           ['km', 'miles'
                                            ][getattr(race, 'distanceUnit',
                                                      0)]))
                    break

            categories = race.getAllCategories()

            if self.grid.GetNumberRows() > 0:
                self.grid.DeleteRows(0, self.grid.GetNumberRows())
            self.grid.AppendRows(len(categories))

            for r, cat in enumerate(categories):
                self._setRow(
                    r=r,
                    active=cat.active,
                    name=cat.name,
                    gender=getattr(cat, 'gender', None),
                    catStr=cat.catStr,
                    catType=cat.catType,
                    startOffset=cat.startOffset,
                    numLaps=cat._numLaps,
                    raceMinutes=cat.raceMinutes,
                    lappedRidersMustContinue=getattr(
                        cat, 'lappedRidersMustContinue', False),
                    distance=getattr(cat, 'distance', None),
                    distanceType=getattr(cat, 'distanceType',
                                         Model.Category.DistanceByLap),
                    firstLapDistance=getattr(cat, 'firstLapDistance', None),
                    publishFlag=cat.publishFlag,
                    uploadFlag=cat.uploadFlag,
                    seriesFlag=cat.seriesFlag,
                )

            self.doAutosize()
            self.fixCells()

            # Force the grid to the correct size.
            self.grid.FitInside()
            self.GetSizer().Layout()

    def commit(self):
        undo.pushState()
        with Model.LockRace() as race:
            self.grid.SaveEditControlValue()
            self.grid.DisableCellEditControl(
            )  # Make sure the current edit is committed.
            if race is None:
                return
            numStrTuples = []
            for r in xrange(self.grid.GetNumberRows()):
                values = {
                    name: self.grid.GetCellValue(r, c)
                    for name, c in self.iCol.iteritems()
                    if name not in self.computedFields
                }
                values['catType'] = self.CategoryTypeChoices.index(
                    values['catType'])
                values['distanceType'] = self.DistanceTypeChoices.index(
                    values['distanceType'])
                numStrTuples.append(values)
            race.setCategories(numStrTuples)
            race.adjustAllCategoryWaveNumbers()
        wx.CallAfter(Utils.refreshForecastHistory)
Exemple #27
0
class TTResults(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        self.headerNames = ['Pos', 'Bib', 'Name', 'Team', 'Time']

        self.grid = ReorderableGrid(self, style=wx.BORDER_SUNKEN)
        self.grid.DisableDragRowSize()
        self.grid.SetRowLabelSize(0)
        self.grid.CreateGrid(0, len(self.headerNames))
        self.setColNames()
        self.grid.EnableReorderRows(False)

        # Set a larger font for the table.
        # Set specialized editors for appropriate columns.
        font = GetFont()
        self.grid.SetLabelFont(font)
        for col in six.moves.range(self.grid.GetNumberCols()):
            attr = gridlib.GridCellAttr()
            attr.SetFont(font)
            if col == self.grid.GetNumberCols() - 1:
                attr.SetRenderer(gridlib.GridCellFloatRenderer(-1, 3))
                attr.SetEditor(gridlib.GridCellFloatEditor(-1, 3))
            else:
                if col in (0, 1):
                    attr.SetRenderer(gridlib.GridCellNumberRenderer())
                attr.SetReadOnly(True)
            self.grid.SetColAttr(col, attr)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=6)
        self.SetSizer(sizer)

    def setColNames(self):
        for col, headerName in enumerate(self.headerNames):
            self.grid.SetColLabelValue(col, headerName)

    def getGrid(self):
        return self.grid

    def refresh(self):
        riders = sorted(Model.model.riders, key=lambda r: r.qualifyingTime)
        starters = Model.model.competition.starters

        Utils.AdjustGridSize(self.grid, rowsRequired=len(riders))
        for row, r in enumerate(riders):
            if row < starters:
                pos = u'{}'.format(row + 1)
            else:
                pos = 'DNQ'
                for col in six.moves.range(self.grid.GetNumberCols()):
                    self.grid.SetCellBackgroundColour(row, col,
                                                      wx.Colour(200, 200, 200))
            for col, value in enumerate([
                    pos, u' {}'.format(r.bib), r.full_name, r.team,
                    '%.3f' % r.qualifyingTime
            ]):
                self.grid.SetCellValue(row, col, value)

        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

    def commit(self):
        pass