diff --git a/build.bat b/build.bat index 2417ac8..bd2e187 100644 --- a/build.bat +++ b/build.bat @@ -68,6 +68,7 @@ if %errorlevel% neq 0 ( ) rmdir /S /Q mongo +rmdir /S /Q sessions cd .. rmdir /S /Q venv diff --git a/scripts/odroid-c.sh b/scripts/odroid-c.sh index cb6b0d9..092f000 100644 --- a/scripts/odroid-c.sh +++ b/scripts/odroid-c.sh @@ -23,8 +23,9 @@ cd "$(dirname "$0")" sudo rm /etc/localtime sudo ln -s /usr/share/zoneinfo/US/Eastern /etc/localtime - # Turn off fstab fsck (potentially dangerous) + # Turn off startup fsck (potentially dangerous) sudo sed -i 's/1$/0/g' /etc/fstab + sudo tune2fs -c 0 -i 0 -l /dev/mmcblk0p2 # ODROID C1/C2 LCD if [ "$(systemctl | grep odroid-lcd35)" == "" ]; then diff --git a/sharkscout/mongo.py b/sharkscout/mongo.py index 1d0d31b..3f7e453 100644 --- a/sharkscout/mongo.py +++ b/sharkscout/mongo.py @@ -233,7 +233,7 @@ def events_update(self, year): # TBA update an individual event def event_update(self, event_key): - event = self.tba_api.event(event_key) + event = self.tba_api.event(event_key, True) if event: # Info that can be known before an event starts event.update({k:v for k, v in { @@ -601,7 +601,7 @@ def team(self, team_key, year=None): # TBA update an individual team def team_update(self, team_key): - team = self.tba_api.team(team_key) + team = self.tba_api.team(team_key, True) if team: team.update({k:v for k, v in { 'awards': self.tba_api.team_history_awards(team_key), diff --git a/sharkscout/thebluealliance.py b/sharkscout/thebluealliance.py index 6a9d325..6fec3e0 100644 --- a/sharkscout/thebluealliance.py +++ b/sharkscout/thebluealliance.py @@ -38,7 +38,11 @@ def _get(self, endpoint, ignore_cache=False): if response.status_code == 304: return {} - content = response.json() + try: + content = response.json() + except json.JSONDecodeError as e: + print(endpoint, response.status_code, response.text) + raise e content = self._tba3_clean(content) content = self._tba3_to_tba2(content) @@ -211,9 +215,7 @@ def event_rankings_raw(self, event_key, ignore_cache=False): def event_rankings_v2(self, event_key, ignore_cache=False): rankings = self.event_rankings_raw(event_key, ignore_cache) - if rankings is None: - return rankings - if 'rankings' in rankings and rankings['rankings']: + if rankings and 'rankings' in rankings and rankings['rankings']: for idx, ranking in enumerate(rankings['rankings']): rankings['rankings'][idx] = [ ranking['rank'], @@ -223,7 +225,8 @@ def event_rankings_v2(self, event_key, ignore_cache=False): ranking['matches_played'] ] rankings['rankings'].insert(0, ['Rank', 'Team'] + [i['name'] for i in rankings['sort_order_info']] + ['Record (W-L-T)', 'Played']) - return rankings['rankings'] + return rankings['rankings'] + return [] def event_rankings(self, event_key, ignore_cache=False): rankings = self.event_rankings_v2(event_key, ignore_cache) diff --git a/sharkscout/webserver.py b/sharkscout/webserver.py index 8f8abd2..4da6d95 100644 --- a/sharkscout/webserver.py +++ b/sharkscout/webserver.py @@ -477,7 +477,7 @@ def _csv(self, prefix, items): filename = tempfile.gettempdir() + '/' + prefix + datetime.now().strftime('%Y%m%d-%H%M%S') + '.csv' with open(filename, 'w', newline='') as temp: writer = csv.DictWriter(temp, fieldnames=keys) - writer.writerow({k: re.sub(r'^[0-9]+_+', '', k) for k in keys}) + writer.writerow({k: k.lstrip('0123456789').strip(' _') for k in keys}) # Write each row for item in items: row = {} @@ -543,14 +543,14 @@ def received_message(self, message): self.send({ 'dequeue': {'scouting_match': data}, 'toast': { - 'message': 'You Match Scouted ' + data['match_key'] + ' ' + data['team_key'], + 'message': 'You match scouted ' + data['match_key'] + ' ' + data['team_key'], 'type': 'success' } }) self.broadcast({'show': '.match-listing .' + data['match_key'] + ' .' + data['team_key'] + ' .fa-check'}) self.broadcast_others({ 'toast': { - 'message': data['scouter'] + ' Match Scouted ' + data['match_key'] + ' ' + data['team_key'], + 'message': data['scouter'] + ' match scouted ' + data['match_key'] + ' ' + data['team_key'], 'type': 'success', 'mobile': False } @@ -563,14 +563,14 @@ def received_message(self, message): self.send({ 'dequeue': {'scouting_pit': data}, 'toast': { - 'message': 'You Pit Scouted ' + data['event_key'] + ' ' + data['team_key'], + 'message': 'You pit scouted ' + data['event_key'] + ' ' + data['team_key'], 'type': 'success' } }) self.broadcast({'show': '.team-listing .' + data['team_key'] + ' .fa-check'}) self.broadcast_others({ 'toast': { - 'message': data['scouter'] + ' Pit Scouted ' + data['event_key'] + ' ' + data['team_key'], + 'message': data['scouter'] + ' pit scouted ' + data['event_key'] + ' ' + data['team_key'], 'type': 'success', 'mobile': False } diff --git a/stats/2018.json b/stats/2018.json index f21e5b5..3cbb801 100644 --- a/stats/2018.json +++ b/stats/2018.json @@ -37,6 +37,45 @@ "matches.cubes_scale": {"$ifNull": ["$matches.cubes_scale", "$pit.avg_cubes_scale"]} }}, + // Change some strings to ints + {"$addFields": { + "matches.auton_crossed_baseline_int": {"$cond": { + "if": {"$eq": ["$matches.auton_crossed_baseline", "Y"]}, + "then": 5, + "else": 0 + }}, + "matches.auton_exchange": {"$cond": { + "if": {"$eq": ["$matches.auton_cube_position", "exchange"]}, + "then": 5, + "else": 0 + }}, + "matches.auton_switch": {"$cond": { + "if": {"$eq": ["$matches.auton_cube_position", "switch"]}, + "then": 10, + "else": 0 + }}, + "matches.auton_scale": {"$cond": { + "if": {"$eq": ["$matches.auton_cube_position", "scale"]}, + "then": 10, + "else": 0 + }}, + "matches.parked": {"$cond": { + "if": {"$eq": ["$matches.end_position", "parked"]}, + "then": 5, + "else": 0 + }}, + "matches.picked_up": {"$cond": { + "if": {"$eq": ["$matches.end_position", "picked_up"]}, + "then": 30, + "else": 0 + }}, + "matches.climbed": {"$cond": { + "if": {"$eq": ["$matches.end_position", "climbed"]}, + "then": 30, + "else": 0 + }} + }}, + // So $in operations can succeed {"$addFields": { "pit.cube_scoring_location": {"$ifNull": ["$pit.cube_scoring_location", []]} @@ -56,7 +95,7 @@ {"$cond": { "if": {"$ne": ["$pit.drivetrain", ""]}, "then": {"$concat": [ - "- ", + "• ", {"$substr": ["$pit.drivetrain", 0, -1]} ]}, "else": "" @@ -64,7 +103,7 @@ {"$cond": { "if": {"$ne": ["$pit.robot_height", ""]}, "then": {"$concat": [ - "
- ", + "
• ", {"$substr": ["$pit.robot_height", 0, -1]}, " in" ]}, @@ -73,7 +112,7 @@ {"$cond": { "if": {"$ne": ["$pit.robot_weight", ""]}, "then": {"$concat": [ - "
- ", + "
• ", {"$substr": ["$pit.robot_weight", 0, -1]}, " lb" ]}, @@ -82,7 +121,7 @@ {"$cond": { "if": {"$ne": ["$pit.cube_lifter", ""]}, "then": {"$concat": [ - "
- ", + "
• ", {"$substr": ["$pit.cube_lifter", 0, -1]}, " + ", {"$substr": ["$pit.cube_intake", 0, -1]} @@ -91,27 +130,27 @@ }}, {"$cond": { "if": {"$in": ["exchange", "$pit.cube_scoring_location"]}, - "then": "
- can do exchange", + "then": "
• can do exchange", "else": "" }}, {"$cond": { "if": {"$in": ["switch", "$pit.cube_scoring_location"]}, - "then": "
- can do switch", + "then": "
• can do switch", "else": "" }}, {"$cond": { "if": {"$in": ["scale", "$pit.cube_scoring_location"]}, - "then": "
- can do scale", + "then": "
• can do scale", "else": "" }}, {"$cond": { "if": {"$eq": ["$pit.climber", "Y"]}, - "then": "
- has climber", + "then": "
• has climber", "else": "" }}, {"$cond": { "if": {"$eq": ["$pit.can_lift", "Y"]}, - "then": "
- can lift others", + "then": "
• can lift others", "else": "" }} ]}}, @@ -120,74 +159,118 @@ "$concat": [ "$matches.number", " ", - "$matches.auton_strategy" - ] - }}, - "201_auton_baseline": {"$push": { - "$cond": { - "if": {"$eq": ["$matches.auton_crossed_baseline", "Y"]}, - "then": 1, - "else": 0 - } - }}, - "202_auton_cube_pos": {"$push": { - "$concat": [ - "$matches.number", - " ", - "$matches.auton_cube_position" + {"$substr": ["$matches.auton_strategy", 0, -1]} ] }}, + "_auton_crossed_baseline": {"$push": "$matches.auton_crossed_baseline_int"}, + "_auton_crossed_baseline_avg": {"$avg": {"$cond": { + "if": {"$eq": ["$matches.auton_crossed_baseline", "Y"]}, + "then": 1, + "else": 0 + }}}, + "_auton_exchange": {"$push": {"$add": [ + "$matches.auton_crossed_baseline_int", + "$matches.auton_exchange" + ]}}, + "_auton_exchange_avg": {"$avg": {"$cond": { + "if": {"$eq": ["$matches.auton_cube_position", "exchange"]}, + "then": 1, + "else": 0 + }}}, + "_auton_switch": {"$push": {"$add": [ + "$matches.auton_crossed_baseline_int", + "$matches.auton_exchange", + "$matches.auton_switch" + ]}}, + "_auton_switch_avg": {"$avg": {"$cond": { + "if": {"$eq": ["$matches.auton_cube_position", "switch"]}, + "then": 1, + "else": 0 + }}}, + "_auton_scale": {"$push": {"$add": [ + "$matches.auton_crossed_baseline_int", + "$matches.auton_exchange", + "$matches.auton_switch", + "$matches.auton_scale" + ]}}, + "_auton_scale_avg": {"$avg": {"$cond": { + "if": {"$eq": ["$matches.auton_cube_position", "scale"]}, + "then": 1, + "else": 0 + }}}, + "_auton_avg": {"$avg": {"$add": [ + "$matches.auton_crossed_baseline_int", + "$matches.auton_exchange", + "$matches.auton_switch", + "$matches.auton_scale" + ]}}, // Teleop "300_teleop_strat": {"$push": { "$concat": [ "$matches.number", " ", - "$matches.teleop_strategy" + {"$substr": ["$matches.teleop_strategy", 0, -1]} ] }}, "_exchange": {"$push": "$matches.cubes_exchange"}, - "_switch_own": {"$push": "$matches.cubes_switch_own"}, - "_scale": {"$push": "$matches.cubes_scale"}, - "_switch_opponent": {"$push": "$matches.cubes_switch_opponent"}, - "_cube_min": {"$min": {"$add": ["$matches.cubes_exchange", "$matches.cubes_switch_own", "$matches.cubes_scale", "$matches.cubes_switch_opponent"]}}, - "_cube_max": {"$max": {"$add": ["$matches.cubes_exchange", "$matches.cubes_switch_own", "$matches.cubes_scale", "$matches.cubes_switch_opponent"]}}, - "_cube_avg": {"$avg": {"$add": ["$matches.cubes_exchange", "$matches.cubes_switch_own", "$matches.cubes_scale", "$matches.cubes_switch_opponent"]}}, + "_switch_own": {"$push": {"$add": [ + "$matches.cubes_exchange", + "$matches.cubes_switch_own" + ]}}, + "_scale": {"$push": {"$add": [ + "$matches.cubes_exchange", + "$matches.cubes_switch_own", + "$matches.cubes_scale" + ]}}, + "_switch_opponent": {"$push": {"$add": [ + "$matches.cubes_exchange", + "$matches.cubes_switch_own", + "$matches.cubes_scale", + "$matches.cubes_switch_opponent" + ]}}, + "_cube_min": {"$min": {"$add": [ + "$matches.cubes_exchange", + "$matches.cubes_switch_own", + "$matches.cubes_scale", + "$matches.cubes_switch_opponent" + ]}}, + "_cube_max": {"$max": {"$add": [ + "$matches.cubes_exchange", + "$matches.cubes_switch_own", + "$matches.cubes_scale", + "$matches.cubes_switch_opponent" + ]}}, + "_cube_avg": {"$avg": {"$add": [ + "$matches.cubes_exchange", + "$matches.cubes_switch_own", + "$matches.cubes_scale", + "$matches.cubes_switch_opponent" + ]}}, // End Game - "_park": {"$push": { - "$cond": { - "if": {"$eq": ["$matches.end_position", "parked"]}, - "then": 1, - "else": 0 - } - }}, - "_park_avg": {"$avg": { - "$cond": { - "if": {"$eq": ["$matches.end_position", "parked"]}, - "then": 1, - "else": 0 - } - }}, - "_climb": {"$push": { - "$cond": { - "if": {"$eq": ["$matches.end_position", "climbed"]}, - "then": 1, - "else": 0 - } - }}, - "_climb_avg": {"$avg": { - "$cond": { - "if": {"$eq": ["$matches.end_position", "climbed"]}, - "then": 1, - "else": 0 - } - }}, - "_picked_up": {"$push": { - "$cond": { - "if": {"$eq": ["$matches.end_position", "picked_up"]}, - "then": 1, - "else": 0 - } - }}, + "_park": {"$push": "$matches.parked"}, + "_park_avg": {"$avg": {"$cond": { + "if": {"$eq": ["$matches.end_position", "parked"]}, + "then": 1, + "else": 0 + }}}, + "_picked_up": {"$push": {"$add": [ + "$matches.parked", + "$matches.picked_up" + ]}}, + "_climb": {"$push": {"$add": [ + "$matches.parked", + "$matches.picked_up", + "$matches.climbed" + ]}}, + "_climb_avg": {"$avg": {"$cond": { + "if": {"$eq": ["$matches.end_position", "climbed"]}, + "then": 1, + "else": 0 + }}}, + "_end_position_avg": {"$avg": {"$add": [ + "$matches.parked", + "$matches.climbed" + ]}}, // Comments "600_off_comments": {"$push": { "$cond": { @@ -215,27 +298,54 @@ // Combine some fields {"$addFields": { + "201_auton": { + "_sort": "$_auton_avg", + // Graph properties + "_name": "auton", + "_min": 0, + "_max": 15, + // Lines + "1 Baseline": "$_auton_crossed_baseline", + "2 Exchange": "$_auton_exchange", + "3 Switch": "$_auton_switch", + "4 Scale": "$_auton_scale", + // Strings + "1 Average Score": "$_auton_avg", + "2 Baseline %": "$_auton_crossed_baseline_avg", + "3 Exchange %": "$_auton_exchange_avg", + "4 Switch %": "$_auton_switch_avg", + "5 Scale %": "$_auton_scale_avg" + }, "301_cubes": { "_sort": "$_cube_avg", + // Graph properties + "_name": "cubes", + "_min": 0, + "_max": "$_cube_max", // Lines - "Exchange": "$_exchange", - "Own Switch": "$_switch_own", - "Opp Switch": "$_switch_opponent", - "Scale": "$_scale", + "1 Exchange": "$_exchange", + "2 Own Switch": "$_switch_own", + "3 Scale": "$_scale", + "4 Opp Switch": "$_switch_opponent", // Strings - "Minimum": "$_cube_min", - "Average": "$_cube_avg", - "Maximum": "$_cube_max" + "1 Cubes Minimum": "$_cube_min", + "2 Cubes Average": "$_cube_avg", + "3 Cubes Maximum": "$_cube_max" }, "500_end_game": { - "_sort": "$_climb_avg", + "_sort": "$_end_position_avg", + // Graph properties + "_name": "end_game", + "_min": 0, + "_max": 30, // Lines - "Park": "$_park", - "Climb": "$_climb", - "Picked Up": "$_picked_up", + "1 Park": "$_park", + "2 Picked Up": "$_picked_up", + "3 Climb": "$_climb", // Stirngs - "Park Average": "$_park_avg", - "Climb Average": "$_climb_avg" + "1 Average Score": "$_end_position_avg", + "2 Park %": "$_park_avg", + "3 Climb %": "$_climb_avg" } }} ] diff --git a/www/macros.html b/www/macros.html index b8ece94..14e6252 100644 --- a/www/macros.html +++ b/www/macros.html @@ -215,14 +215,13 @@
- -   - -   - ${match_name(match)} - + +  •  + ${match_name(match)} + +  •  -  ${video_icon(video, 'icon')} + ${video_icon(video, 'icon')} @@ -244,6 +243,10 @@
+ + + +  •  ${match_name(match)} @@ -280,7 +283,7 @@
- + @@ -288,10 +291,15 @@
-
+ @@ -308,7 +316,13 @@
- + +
Minimum: ${round(min(data),2)}
Average: ${round(statistics.mean(data),2)}
@@ -318,11 +332,17 @@
- - - -
- ${k}: ${stats_cell(data[k])} + + + + +
+ ${Markup(k.lstrip('0123456789').strip(' _'))}: ${stats_cell(data[k])}
${round(data,2)} diff --git a/www/scouting/2018/match.html b/www/scouting/2018/match.html index 4f2d2a9..2de2f52 100644 --- a/www/scouting/2018/match.html +++ b/www/scouting/2018/match.html @@ -269,10 +269,10 @@

End Game

Parked
diff --git a/www/scouting/2018/pit.html b/www/scouting/2018/pit.html index af75450..4fff666 100644 --- a/www/scouting/2018/pit.html +++ b/www/scouting/2018/pit.html @@ -101,6 +101,7 @@
+ diff --git a/www/static/js/sharkscout.js b/www/static/js/sharkscout.js index 8a92d3f..d09e7f3 100644 --- a/www/static/js/sharkscout.js +++ b/www/static/js/sharkscout.js @@ -405,25 +405,49 @@ $(document).ready(function() { // Initialize Chart.js $('canvas.chart').each(function() { + var $chart = $(this); // Get array of labels - var labels = _.split($(this).siblings('input[name="labels"]').val(), ','); + var labels = _.split($chart.siblings('input[name="labels"]').val(), ','); if(labels.length == 1 && labels[0] == '') { labels = []; } // Get array of array of values - var values = $(this).siblings('input[name="values"]').map(function() { + var values = $chart.siblings('input[name="values"]').map(function() { // (Array() because jQuery flattens arrays...) return Array(_.map(_.split(this.value, ','), function(val) { return parseFloat(val); })); }).get(); var maxValues = _.max(_.map(values, function(v){return v.length;})); + // Find min value + var minValue = 0; + if($chart.attr('data-min') && $chart.attr('data-name')) { + minValue = _.min($('canvas[data-name="' + $chart.attr('data-name') + '"]') + .filter(function() { + return $(this).attr('data-min'); + }) + .map(function() { + return parseFloat($(this).attr('data-min')); + }) + .get()); + } // Find max value var maxValue = 0; - for(var i = 0; i < maxValues; i++) { - for(var j = 0; j < values.length; j++) { - if(i < values[j].length && values[j][i] > maxValue) { - maxValue = values[j][i]; + if($chart.attr('data-max') && $chart.attr('data-name')) { + maxValue = _.max($('canvas[data-name="' + $chart.attr('data-name') + '"]') + .filter(function() { + return $(this).attr('data-max'); + }) + .map(function() { + return parseFloat($(this).attr('data-max')); + }) + .get()); + } else { + for(var i = 0; i < maxValues; i++) { + for(var j = 0; j < values.length; j++) { + if(i < values[j].length && values[j][i] > maxValue) { + maxValue = values[j][i]; + } } } } @@ -456,10 +480,12 @@ $(document).ready(function() { return { 'label': labels.length == values.length ? labels[idx] : '', 'data': val, + 'lineTension': 0, 'borderColor': ['#337ab7','#5cb85c','#5bc0de','#f0ad4e','#d9534f'][idx%5], 'backgroundColor': ['rgba(51,122,182,0.5)','rgba(92,184,92,0.5)','rgba(91,192,222,0.5)','rgba(240,173,78,0.5)','rgba(217,83,79,0.5)'][idx%5], 'borderWidth': 3, - 'fill': false + 'pointRadius': 2, + 'fill': idx ? '-1' : 'origin' }; }).concat([{ 'data': _.map(Array(maxValues), function(y, x) { @@ -490,7 +516,8 @@ $(document).ready(function() { 'yAxes': [{ 'ticks': { 'display': false, - 'min': 0, + 'stepSize': 1, + 'min': minValue, 'max': maxValue }, 'gridLines': {
${re.sub(r'^[0-9]+_+', '', key).replace('_',' ').title()}${key.lstrip('0123456789').strip(' _').replace('_',' ').title()}
${stats_cell(team[key])} + ${stats_cell(team[key])} +