From 995a2cd6478ddd7afba093bfc1052c059146f4f1 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 14 Jun 2011 22:03:12 +0200 Subject: [PATCH] First working version with new data in backend; Configurable columns; Leaderboard live still not working --- .../SailingServer (No Proxy, No Tests).launch | 2 +- .../sailing/connector/provider.py | 94 +++++++++---------- .../sailing.db/sailing/db/model/__init__.py | 11 +-- .../sailing.db/sailing/db/model/entities.py | 4 +- .../sailing.web/sailing/web/browser/core.py | 23 ++++- .../sailing/web/browser/functions.py | 22 ++--- .../templates/general-competitor-info.pt | 4 +- .../browser/templates/general-race-info.pt | 2 +- .../web/browser/templates/leaderboard-info.pt | 12 +-- .../web/browser/templates/live-info-view.pt | 6 -- python/src/sailing.web/sailing/web/config.py | 38 +++++--- python/templates/db.ini | 8 ++ 12 files changed, 119 insertions(+), 107 deletions(-) diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch index c27228dbfae..a1930a00422 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch @@ -19,7 +19,7 @@ - + diff --git a/python/src/sailing.connector/sailing/connector/provider.py b/python/src/sailing.connector/sailing/connector/provider.py index aefd2cca228..27bbf883a12 100644 --- a/python/src/sailing.connector/sailing/connector/provider.py +++ b/python/src/sailing.connector/sailing/connector/provider.py @@ -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) diff --git a/python/src/sailing.db/sailing/db/model/__init__.py b/python/src/sailing.db/sailing/db/model/__init__.py index cadb1d0c8ad..51a25c58a5b 100644 --- a/python/src/sailing.db/sailing/db/model/__init__.py +++ b/python/src/sailing.db/sailing/db/model/__init__.py @@ -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] diff --git a/python/src/sailing.db/sailing/db/model/entities.py b/python/src/sailing.db/sailing/db/model/entities.py index 5a131414277..6532c06b754 100644 --- a/python/src/sailing.db/sailing/db/model/entities.py +++ b/python/src/sailing.db/sailing/db/model/entities.py @@ -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 = [ diff --git a/python/src/sailing.web/sailing/web/browser/core.py b/python/src/sailing.web/sailing/web/browser/core.py index 05495c4c9da..454f6014bc5 100644 --- a/python/src/sailing.web/sailing/web/browser/core.py +++ b/python/src/sailing.web/sailing/web/browser/core.py @@ -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 """ diff --git a/python/src/sailing.web/sailing/web/browser/functions.py b/python/src/sailing.web/sailing/web/browser/functions.py index 1b0d317da43..7b46134b5b2 100644 --- a/python/src/sailing.web/sailing/web/browser/functions.py +++ b/python/src/sailing.web/sailing/web/browser/functions.py @@ -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' diff --git a/python/src/sailing.web/sailing/web/browser/templates/general-competitor-info.pt b/python/src/sailing.web/sailing/web/browser/templates/general-competitor-info.pt index b8c8f71ab21..a9b1a05eb4b 100644 --- a/python/src/sailing.web/sailing/web/browser/templates/general-competitor-info.pt +++ b/python/src/sailing.web/sailing/web/browser/templates/general-competitor-info.pt @@ -30,8 +30,8 @@ ${event.races[repeat['race'].index]}
${race} »     ${mark} »  - - ${'%.2f' % value}| + + ${value[0]}:${value[1]}|
diff --git a/python/src/sailing.web/sailing/web/browser/templates/general-race-info.pt b/python/src/sailing.web/sailing/web/browser/templates/general-race-info.pt index 0ff44e7791d..d68d953a2a6 100644 --- a/python/src/sailing.web/sailing/web/browser/templates/general-race-info.pt +++ b/python/src/sailing.web/sailing/web/browser/templates/general-race-info.pt @@ -36,7 +36,7 @@ ${cr.wind_source and cr.wind_source[:3] or 'EXP'} ${cr.wind_bearing or ''} - Current Leg(s): ${','.join(cr.current_legs)}
+ Current Leg(s): ${cr.current_legs}

${markname}
diff --git a/python/src/sailing.web/sailing/web/browser/templates/leaderboard-info.pt b/python/src/sailing.web/sailing/web/browser/templates/leaderboard-info.pt index 7cf0578bd91..df9226dddaa 100644 --- a/python/src/sailing.web/sailing/web/browser/templates/leaderboard-info.pt +++ b/python/src/sailing.web/sailing/web/browser/templates/leaderboard-info.pt @@ -1,5 +1,5 @@ -
+
@@ -10,8 +10,8 @@ - - + + @@ -22,7 +22,7 @@ - + @@ -31,8 +31,8 @@ - - + + diff --git a/python/src/sailing.web/sailing/web/browser/templates/live-info-view.pt b/python/src/sailing.web/sailing/web/browser/templates/live-info-view.pt index 5a2a81dc1f7..295c72a71f0 100644 --- a/python/src/sailing.web/sailing/web/browser/templates/live-info-view.pt +++ b/python/src/sailing.web/sailing/web/browser/templates/live-info-view.pt @@ -4,10 +4,6 @@
-
-   -
-
          
     
@@ -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(' '); }); }); } diff --git a/python/src/sailing.web/sailing/web/config.py b/python/src/sailing.web/sailing/web/config.py index 52415765a81..5475b0971dc 100644 --- a/python/src/sailing.web/sailing/web/config.py +++ b/python/src/sailing.web/sailing/web/config.py @@ -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']] + } diff --git a/python/templates/db.ini b/python/templates/db.ini index e48ac879feb..eafaf23604b 100644 --- a/python/templates/db.ini +++ b/python/templates/db.ini @@ -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}
R${repeat['race'].number} (${race}) R${repeat['race'].number}M${repeat['mark'].number}${value}${value[0]}
${cp.total or 0} ${cp.name}${cp.races[repeat['race'].index]} ${cp.marks[repeat['race'].index][repeat['mark'].index]}${'%.2f' % cp.values[repeat['race'].index][repeat['mark'].index][repeat['value'].index]}${view.displayLegValue(name, cp.values[repeat['race'].index][repeat['mark'].index].get(name[-1]))}