First working version with new data in backend; Configurable columns; Leaderboard live still not working

This commit is contained in:
Simon Pamies committed 2011-06-14 22:03:12 +02:00
1 parent 2100945078
commit 995a2cd647
12 files changed
+119 -107

No files matched your search

@@ -58,6 +58,8 @@ class LiveDataReceiver(threading.Thread):
try:
# need to lock because other threads could overwrite information
with lock:
print >>sys.stderr, '*',
sys.stderr.flush()
updatecount = liveRaceInformation(conf)
print >>sys.stderr, '.',
sys.stderr.flush()
@@ -76,7 +78,7 @@ class LiveDataReceiver(threading.Thread):
continue
# avoid filling console with errors
time.sleep(2)
time.sleep(5)
# avoid hammering java server - can occur if call does not block correctly
time.sleep(1)
@@ -93,6 +95,7 @@ def eventConfiguration(configurator):
events = jsonByUrl(configurator)
for event in events:
# erroneous data
if not event.has_key('boatclass'):
continue
@@ -142,12 +145,11 @@ def eventConfiguration(configurator):
rcnames.sort(lambda x,y: cmp(x[1], y[1]))
# fill competitors with empty data
# fill competitor ranks and leg values with empty data
for cp in cobjects:
races = [0.0 for r in range(raceindex)]
marks = cp.marks; values = cp.values;
upordown = cp.upordownwind; additional = cp.additional
marknames = cp.marknames
# update races and marks - support add operation
@@ -162,15 +164,9 @@ def eventConfiguration(configurator):
if len(marknames) < r+1:
marknames.append([])
if len(upordown) < r+1:
upordown.append([])
if len(values) < r+1:
values.append([])
if len(additional) < r+1:
additional.append([])
for m in range(len(rcmarks[r])):
if len(marks[r]) < m+1:
marks[r].append(0.0)
@@ -178,14 +174,8 @@ def eventConfiguration(configurator):
if len(marknames[r]) < m+1:
marknames[r].append('')
if len(upordown[r]) < m+1:
upordown[r].append(False)
if len(values[r]) < m+1:
values[r].append([])
if len(additional[r]) < m+1:
additional[r].append([])
values[r].append({})
# recognize if races and/or marks changed
if cp.races and len(cp.races) != len(races):
@@ -194,7 +184,7 @@ def eventConfiguration(configurator):
if cp.marks and len(cp.marks) != len(marks):
log.error('Difference between marks for competitor %s found. Old: %s New: %s' % (cp.name, cp.marks, marks))
cp.update(dict(races=races, marks=marks, upordownwind=upordown, additional=additional, marknames=marknames))
cp.update(dict(races=races, marks=marks, marknames=marknames))
# finally update event
dbevent.update(dict(name=event['name'],
@@ -252,12 +242,21 @@ def liveRaceInformation(configurator):
# strange things can happen - make such errors visible
raise Exception, 'Could not find competitor %s for event %s' % (competitor['name'], eventname)
# compute total rank for competitor
c_current_rank = competitor.get('rank', 0)
# search thru ranks provided and use the
# rank there as the current one
if data.has_key('ranks'):
for c in data['ranks']:
if c['competitor'] == competitor['name']:
c_current_rank = c['rank']
break
c_races = comp.races
c_marks = comp.marks
c_values = comp.values
c_total_rank = comp.total
c_additional = comp.additional
c_upordown = comp.upordownwind
c_marknames = comp.marknames
# if competitor hasn't started yet we just ignore this leg
@@ -305,55 +304,48 @@ def liveRaceInformation(configurator):
current_legs[lcount] = 1
# now update values for the current position
for key in config.SORTED_LEG_VALUE_NAMES:
# dynamically extend this one
attr = config.LEG_VALUE_DATA[key]
if len(c_values[raceindex][lcount]) < attr[0]+1:
c_values[raceindex][lcount].append(0.0)
k = competitor.get(key, None)
dc_leg_values = {}
for key in competitor.keys():
kvalue = competitor.get(key, None)
# treat nulled numbers as unset
if k in [None, 'null', 'Null', 'nUlL', '']:
k = 0.0
if kvalue in [None, 'null', 'Null', 'nUlL', '']:
kvalue = None
# check for cases where calculations from backend yield
# strange numbers - this happens in cases where the competitor
# has no speed during mark passing
if k in ['Infinity', 'Infinite'] or float(k) > 150000.0:
if isinstance(kvalue, (float, int)) and (kvalue in ['Infinity', 'Infinite'] or float(kvalue) > 150000.0):
# use a magic number (that is very unlikely to occur)
# to indicate that competitor has no speed
# hint: it is not the question it is the answer
k = 42.260426041982
kvalue = 42.260426041982
c_values[raceindex][lcount][attr[0]] = k
dc_leg_values[key] = kvalue
if len(c_additional[raceindex][lcount]) == 0:
c_additional[raceindex][lcount] = [0, 0]
dc_leg_values['upOrDownWindLeg'] = leg['upordownwindleg']
c_additional[raceindex][lcount][0] = competitor['started']
c_additional[raceindex][lcount][1] = competitor['finished']
# compute gains and losses (places, )
if legcount == 0:
# for the first leg just put 0 into data
dc_leg_values['gainsAndLossesInPlaces'] = 0
else:
# search for the last place
last_rank_computed = c_marks[raceindex][lcount-1]
if last_rank_computed > c_current_rank:
dc_leg_values['gainsAndLossesInPlaces'] = c_current_rank - last_rank_computed
else: dc_leg_values['gainsAndLossesInPlaces'] = last_rank_computed - c_current_rank
c_upordown[raceindex][lcount] = leg['upordownwindleg']
c_values[raceindex][lcount] = dc_leg_values
# save marknames for the given leg
c_marknames[raceindex][lcount] = (leg['from'], leg['to'])
# compute total rank for competitor
rank = competitor.get('rank', 0)
# search thru ranks provided and use the
# rank as the current one
if data.has_key('ranks'):
for c in data['ranks']:
if c['competitor'] == competitor['name']:
rank = c['rank']
break
comp.update(dict(current_rank=rank,races=c_races, marks=c_marks, values=c_values,
additional=c_additional, upordownwind=c_upordown,
comp.update(dict(current_rank=c_current_rank,
races=c_races,
marks=c_marks,
values=c_values,
total=c_total_rank))
# next leg
@@ -369,7 +361,7 @@ def liveRaceInformation(configurator):
if data.get('wind'):
mdup['wind_source'] = data['wind']['source']
mdup['wind_bearing'] = data['wind']['truebearingdeg']
mdup['wind_speed'] = data['wind']['knowspeed']
mdup['wind_speed'] = data['wind']['knotspeed']
mdup['current_legs'] = current_legs.keys()
dbrace.update(mdup)
@@ -26,7 +26,7 @@ class CompetitorImpl(ModelBase):
}
@classmethod
def sortedBy(cls, eventname, raceindex=None, markindex=None, valueindex=None):
def sortedBy(cls, eventname, raceindex=None, markindex=None, valueindex=None, columns=None):
""" Returns competitors sorted by given index. If index
is None no sorting is performed on this index. If all index
params are None sorting is performed by total. """
@@ -36,12 +36,9 @@ class CompetitorImpl(ModelBase):
yval = y.total
if valueindex is not None:
try:
xval = x.values[raceindex][markindex][valueindex]
yval = y.values[raceindex][markindex][valueindex]
except IndexError:
xval = 0.0
yval = 0.0
valuekey = columns[valueindex][-1]
xval = x.values[raceindex][markindex].get(valuekey, 0.0)
yval = y.values[raceindex][markindex].get(valuekey, 0.0)
elif markindex is not None:
xval = x.marks[raceindex][markindex]
@@ -72,9 +72,7 @@ class Competitor(BaseDocument):
'races' : [float, ],
'marks' : [[float, ], ], # ranks for marks
'marknames' : [[[unicode,] ]], # marknames for legs
'upordownwind' : [[bool], ], # indicates wheter the leg is up or downwind
'additional' : [[[float]]], # additional information regarding one leg (finished, started)
'values' : [[[float, ]]]
'values' : [[{}]] # values is a hashset that for each leg contains values
}
indexes = [
@@ -141,7 +141,7 @@ class BaseView(object):
def allCompetitorsFor(self, event):
return model.CompetitorImpl.queryBy(event=event)
def competitorsSortedBy(self, eventname, sortparam):
def competitorsSortedBy(self, eventname, sortparam, columnmode):
param = sortparam
if param == 'name':
competitors = model.CompetitorImpl.sortedBy(eventname=eventname)
@@ -156,7 +156,8 @@ class BaseView(object):
if len(params) == 2:
competitors = model.CompetitorImpl.sortedBy(eventname=eventname, raceindex=int(params[0])-1, markindex=int(params[1])-1)
elif len(params) == 3:
competitors = model.CompetitorImpl.sortedBy(eventname=eventname, raceindex=int(params[0])-1, markindex=int(params[1])-1, valueindex=int(params[2])-1)
columns = self.configuredColumns(columnmode)
competitors = model.CompetitorImpl.sortedBy(eventname=eventname, raceindex=int(params[0])-1, markindex=int(params[1])-1, valueindex=int(params[2])-1, columns=columns)
else:
param = int(param.strip())
@@ -164,6 +165,24 @@ class BaseView(object):
return competitors
def configuredColumns(self, modename):
return config.COLUMN_MODE_NAMES.get(modename)
def displayLegValue(self, name, value):
if isinstance(value, float) \
and value == 42.260426041982:
val = 'ANCH'
else:
if value in [None, 'None']:
return ''
val = name[2] % value
if name[0] == 'ETASEC':
val = '%.f:%.f' % (int(value) / 60, int(value)-( (int(value)/60)*60 ))
return val
def callFunction(context, request):
""" Calls a function dynamically """
@@ -497,36 +497,26 @@ def adminLiveData(context, request):
t_up = threaded_listener.values()[0].last_update.strftime('%H:%M:%S')
t_upcount = threaded_listener.values()[0].updatecount
columns = view.configuredColumns('TOP')
results = ''
for pos in range(len(currentlegs)):
legpos = currentlegs[pos]
results += '\nLAST SHOWRACE CALL: %s (UPCOUNT PARAM: %s)\nLEG: %s (FROM: %s TO: %s)\nRACE-START: %s NEWEST EVENT: %s WIND: (%s %s %s)\n' % (t_up, t_upcount, legpos+1, competitors[0].marknames[racepos][legpos][0], competitors[0].marknames[racepos][legpos][1], view.millisToDatetime(race.start), view.millisToDatetime(race.timeofnewestevent), race.wind_source, race.wind_bearing, race.wind_speed)
results += 'NAME'.ljust(16) + 'TOTAL'.ljust(7) + 'CRANK'.ljust(7) + 'RRANK'.ljust(7)
results += 'MRANK'.ljust(7) + 'LRANK'.ljust(9) + 'SPD'.ljust(9) + 'DSTTRV'.ljust(9) + 'VMG'.ljust(9) + 'AVMG'.ljust(9) + 'SGAP'.ljust(9) + 'ETA'.ljust(9) + 'DSTGO'.ljust(9) + 'STARTD'.ljust(9) + 'FINISHD'.ljust(9) + 'UPDOWNWIND'.ljust(12)
results += 'MRANK'.ljust(7) + ''.join([title[0].ljust(9) for title in columns])
results += '\n'
# sort competitors by rank in current leg
competitors.sort(lambda x,y:cmp(x.values[racepos][legpos] and x.values[racepos][legpos][0] or 6000, y.values[racepos][legpos] and y.values[racepos][legpos][0] or 6000))
competitors.sort(lambda x,y:cmp(x.values[racepos][legpos] and x.values[racepos][legpos].get('rank') or 6000, y.values[racepos][legpos] and y.values[racepos][legpos].get('rank') or 6000))
for c in competitors:
results += '%s %s %s' % (c.name.ljust(15), str(c.total).ljust(6), str(c.current_rank).ljust(6))
results += '%s %s' % (str(c.races[racepos]).ljust(7), str(c.marks[racepos][legpos]).ljust(7))
for v in c.values[racepos][legpos]:
if v == 42.260426041982:
results += 'ANCHOR'.ljust(9)
else:
results += str('%.2f' % v).ljust(9)
try:
results += str(c.additional[racepos][legpos][0]).ljust(9)
results += str(c.additional[racepos][legpos][1]).ljust(9)
except:
results += "N/A".ljust(9)
results += "N/A".ljust(9)
results += str(c.upordownwind[racepos][legpos]).ljust(12)
for v in columns:
results += view.displayLegValue(v, c.values[racepos][legpos].get(v[-1])).ljust(9)
results += '\n'
@@ -30,8 +30,8 @@
${event.races[repeat['race'].index]}<br/><b style="color: red;">${race}</b>&nbsp;&raquo;&nbsp;
<tal:for repeat="mark cp.marks[repeat['race'].index]">
&nbsp;&nbsp;&nbsp;<b>${mark}</b>&nbsp;&raquo;&nbsp;
<tal:for repeat="value cp.values[repeat['race'].index][repeat['mark'].index]">
<b style="color: green" tal:on-error="value">${'%.2f' % value}</b>|
<tal:for repeat="value cp.values[repeat['race'].index][repeat['mark'].index].items()">
<b style="color: green">${value[0]}:${value[1]}</b>|
</tal:for>
</tal:for>
<br/>
@@ -36,7 +36,7 @@
<td valign="top">${cr.wind_source and cr.wind_source[:3] or 'EXP'}</td>
<td valign="top">${cr.wind_bearing or ''}</td>
<td valign="top">
Current Leg(s): ${','.join(cr.current_legs)}<br/>
Current Leg(s): ${cr.current_legs}<br/><br/>
<tal:for repeat="markname event.marks[racepos]">
${markname}<br/>
</tal:for>
@@ -1,5 +1,5 @@
<div class="info">
<div class="info" tal:define="columns view.configuredColumns('LEADERBOARD-STATIC')">
<div tal:repeat="event view.allEvents()">
<table width="100%">
<tr>
@@ -10,8 +10,8 @@
<td style="color:white;font-size: 18px;font-weight: bold;background-color: #DE8A2E;">R${repeat['race'].number} (${race})</td>
<tal:for repeat="mark event.marks[repeat['race'].index]">
<td style="color:white;font-size: 14px;font-weight: bold;background-color: #95AA31;">R${repeat['race'].number}M${repeat['mark'].number}</td>
<tal:for repeat="value ['Rank', 'AvgSpeed', 'DstTrav', 'Vel', 'AvgVel', 'Gap', 'EstNxt', 'DstToGo']">
<td style="color: white;background-color: #FEC631">${value}</td>
<tal:for repeat="value columns">
<td style="color: white;background-color: #FEC631">${value[0]}</td>
</tal:for>
</tal:for>
@@ -22,7 +22,7 @@
</tal:for>
</tr>
<tal:for repeat="cp view.competitorsSortedBy(eventname=request.params.get('eventname'), sortparam=request.params.get('sortname'))">
<tal:for repeat="cp view.competitorsSortedBy(eventname=request.params.get('eventname'), sortparam=request.params.get('sortname'), columnmode='LEADERBOARD-STATIC')">
<tr>
<td valign="top">${cp.total or 0}</td>
<td valign="top">${cp.name}</td>
@@ -31,8 +31,8 @@
<td style="color:white;font-size: 18px;font-weight: bold;background-color: #DE8A2E;">${cp.races[repeat['race'].index]}</td>
<tal:for repeat="mark event.marks[repeat['race'].index]">
<td style="color:white;font-size: 14px;font-weight: bold;background-color: #95AA31;">${cp.marks[repeat['race'].index][repeat['mark'].index]}</td>
<tal:for repeat="value ['Rank', 'AvgSpeed', 'DstTrav', 'Vel', 'AvgVel', 'Gap', 'EstNxt', 'DstToGo']">
<td style="color:white;background-color: #FEC631"><b tal:on-error="string:">${'%.2f' % cp.values[repeat['race'].index][repeat['mark'].index][repeat['value'].index]}</b></td>
<tal:for repeat="name columns">
<td style="color:white;background-color: #FEC631"><b tal:on-error="string:">${view.displayLegValue(name, cp.values[repeat['race'].index][repeat['mark'].index].get(name[-1]))}</b></td>
</tal:for>
</tal:for>
@@ -4,10 +4,6 @@
<div>
<input type="button" value="Pause" onclick="$(this).attr('value', togglePauseLive())"/>
<div id="live-data-bar" style="height: 30px;">
&nbsp;
</div>
<pre id="live-data-receiver" style="font-size:11px;height:1630px; width: 1024px;">
&nbsp;
</pre>
@@ -30,12 +26,10 @@
function refreshLive() {
if (paused_live == false) {
displayLoader('#live-data-bar');
GET('adminLiveData', {}, function(data) {
$('#live-data-receiver').fadeTo('normal', 0.1, function() {
$('#live-data-receiver').html(data);
$('#live-data-receiver').fadeTo('fast', 1.0);
$('#live-data-bar').html('&nbsp;');
});
});
}
+26 -12
View File
@@ -27,18 +27,32 @@ LIST_WIND_TRACKERS = 'listwindtrackers'
STOP_EXP_WIND = 'stopreceivingexpeditionwind'
START_EXP_WIND = 'receiveexpeditionwind'
# keys for competitor board leg values
LEG_VALUE_DATA = {
'rank': [0, ],
'averageSpeedOverGroundInKnots': [1, ],
'distanceTraveledOverGroundInMeters': [2, ],
'velocityMadeGoodInKnots': [3, ],
'averageVelocityMadeGoodInKnots': [4, ],
'gapToLeaderInSeconds': [5, ],
'estimatedTimeToNextMarkInSeconds': [6, ],
'windwardDistanceToGoInMeters' : [7, ],
VN = {
'CSPDKNOT': ('CSPEEDKN', 'Current Speed (kn)', '%.1f', 'currentSpeedOverGroundInKnots'),
'CSPDMS' : ('CSPDMS', 'Current Speed (m/s)', '%.1f', 'currentSpeedOverGroundInMetersPerSecond'),
'ASPDKNOT': ('ASPDKN', 'Avg Speed (kn)', '%.1f', 'averageSpeedOverGroundInKnots'),
'ASPDMS' : ('ASPDMS', 'Avg Speed (m/s)', '%.1f', 'averageSpeedOverGroundInMetersPerSecond'),
'DSTTRV' : ('DSTTRV', 'Distance Travelled (m)', '%.f', 'distanceTraveledOverGroundInMeters'),
'VMGKNOT': ('VMGKN', 'VMG (kn)', '%.1f', 'velocityMadeGoodInKnots'),
'VMGMS' : ('VMGMS', 'VMG (m/s)', '%.1f', 'velocityMadeGoodInMetersPerSecond'),
'AVMGKNOT': ('AVMGKN', 'Avg VMG (kn)', '%.1f', 'averageVelocityMadeGoodInKnots'),
'AVMGMS' : ('AVMGMS', 'Avg VMG (m/s)', '%.1f', 'averageVelocityMadeGoodInMetersPerSecond'),
'ETA' : ('ETASEC', 'ETA (sec.)', '%.f', 'estimatedTimeToNextMarkInSeconds'),
'DSTGO' : ('DSTGO', 'Distance To Go (m)', '%.f', 'windwardDistanceToGoInMeters'),
'GAPSEC' : ('SGAP', 'Gap To Leader (sec)', '%.1f','gapToLeaderInSeconds'),
'UPDOWNWIND' : ('UPDOWN', 'Up or Downwind?', '%s', 'upOrDownWindLeg'),
'GLP' : ('GLP', 'Gains and Losses (Places)', '%s', 'gainsAndLossesInPlaces'),
'RANK' : ('LRANK', 'Rank', '%s', 'rank'),
'STARTED' : ('STARTD', 'Started?', '%s','started'),
'FINISHED' : ('FINISHD', 'Finished?', '%s','finished')
}
SORTED_LEG_VALUE_NAMES = LEG_VALUE_DATA.keys()
SORTED_LEG_VALUE_NAMES.sort(lambda x,y: cmp(LEG_VALUE_DATA[x][0], LEG_VALUE_DATA[y][0]))
COLUMN_MODE_NAMES = {
'TOP':
[VN['RANK'], VN['CSPDKNOT'], VN['ASPDKNOT'], VN['DSTTRV'], VN['VMGKNOT'], VN['DSTGO'], VN['ETA'], VN['STARTED'], VN['FINISHED'], VN['UPDOWNWIND']],
'LEADERBOARD-STATIC':
[VN['RANK'], VN['CSPDMS'], VN['DSTTRV'], VN['VMGMS'], VN['GLP']],
'LEADERBOARD-F1':
[VN['RANK'], VN['GAPSEC'], VN['ASPDMS'], VN['DSTTRV'], VN['GLP']]
}
+8
View File
@@ -18,6 +18,14 @@ username =
password =
collections = events races competitors
[winddb]
host = localhost
port = 10200
dbname = winddb
username =
password =
collections = WIND_TRACKS
[biography]
host = ${conf:mongodb_host}
port = ${conf:mongodb_port}