From 27708776d83faec4908bf378a8f33dafccce25bc Mon Sep 17 00:00:00 2001 From: Basileal Imana Date: Sun, 19 Mar 2017 04:17:14 -0400 Subject: [PATCH 01/19] refactor and cleanup html and error checking for add new run --- libraries/repositories/runs.py | 117 ++---- libraries/utilities/authentication.py | 3 +- libraries/utilities/run_parameters.py | 62 ---- libraries/utilities/runs.py | 471 +++++++++++++++++++++++++ libraries/utilities/utilities.py | 11 + scoringsystem/blueprints/main.py | 322 +++-------------- scoringsystem/blueprints/scoreboard.py | 159 +++++++++ templates/robot_new_run_form.html | 336 ++++++++++++++++++ templates/robot_new_run_sidepanel.html | 140 ++++++++ templates/run.html | 345 ++---------------- templates/run_new.html | 35 ++ 11 files changed, 1245 insertions(+), 756 deletions(-) delete mode 100644 libraries/utilities/run_parameters.py create mode 100644 libraries/utilities/runs.py create mode 100644 libraries/utilities/utilities.py create mode 100644 scoringsystem/blueprints/scoreboard.py create mode 100644 templates/robot_new_run_form.html create mode 100644 templates/robot_new_run_sidepanel.html create mode 100644 templates/run_new.html diff --git a/libraries/repositories/runs.py b/libraries/repositories/runs.py index 18e0b6c..da7a389 100644 --- a/libraries/repositories/runs.py +++ b/libraries/repositories/runs.py @@ -49,26 +49,28 @@ def get_runs(robot_id): return r.get_registry()['MY_SQL'].get_all(query, data) @staticmethod - def record_run(level, - failed_trial, - actual_time, - non_air, - furniture, - arbitrary_start, - return_trip, - candle_location_mode, - stopped_within_circle, - signaled_detection, - num_rooms_searched, - kicked_dog, - touched_candle, - cont_wall_contact, - ramp_hallway, - alt_target, - all_candles, - used_versa_valve, - score, - robot_id): + def record_run( + level, + failed_trial, + actual_time, + non_air, + furniture, + arbitrary_start, + return_trip, + candle_location_mode, + stopped_within_circle, + signaled_detection, + num_rooms_searched, + kicked_dog, + touched_candle, + cont_wall_contact, + ramp_hallway, + alt_target, + all_candles, + used_versa_valve, + score, + robot_id + ): query = """INSERT INTO runs( level, @@ -145,78 +147,3 @@ def get_runs_robot_level(robot_id, level): 'level': level } return r.get_registry()['MY_SQL'].get_all(query, data) - - @staticmethod - def calculate_run_score(robot_div, - level, - failed_trial, - actual_time, - non_air, - furniture, - arbitrary_start, - return_trip, - candle_location_mode, - stopped_within_circle, - signaled_detection, - num_rooms_detected, - kicked_dog, - touched_candle, - cont_wall_contact, - ramp_hallway, - alt_target, - all_candles): - - task_search = num_rooms_detected * (-30) - task_detect = -30 if signaled_detection else 0 - task_position = -30 if stopped_within_circle else 0 - - om_candle = 0.75 if candle_location_mode else 1 - - om_start = 0.8 if arbitrary_start else 1 - om_return = 0.8 if return_trip else 1 - om_extinguisher = 0.75 if non_air else 1 - om_furniture = 0.75 if furniture else 1 - - if num_rooms_detected == 0 or num_rooms_detected == 1: - room_factor = 1 - elif num_rooms_detected == 2: - room_factor = 0.85 - elif num_rooms_detected == 3: - room_factor = 0.5 - elif num_rooms_detected == 4: - room_factor = 0.35 - - pp_candle = 50 * touched_candle - pp_slide = cont_wall_contact / 2 - pp_dog = 50 if kicked_dog else 0 - - om_alt_target = 0.6 if alt_target else 1 - om_ramp_hallway = 0.9 if ramp_hallway else 1 - om_all_candles = 0.6 if all_candles else 1 - - #Scores - if failed_trial: - if robot_div in ['junior', 'walking'] and level == 1: - return 600 + task_detect + task_position + task_search; - elif level == 3 and actual_time in [400, 450, 500]: - return actual_time - else: - return 600 - - if level == 1: - score = ((actual_time + pp_candle + pp_dog + pp_slide) * - (om_candle * om_start * om_return * om_extinguisher * om_furniture) * room_factor) - - if level == 2: - score = ((actual_time + pp_candle + pp_dog + pp_slide) * - (om_start * om_return * om_extinguisher * om_furniture) * room_factor) - - if level == 3: - score = ((actual_time + pp_candle + pp_dog + pp_slide) * - om_alt_target * om_ramp_hallway * om_all_candles) - - if score > 600: - return 600 - else: - return score - diff --git a/libraries/utilities/authentication.py b/libraries/utilities/authentication.py index f0acf94..581b544 100644 --- a/libraries/utilities/authentication.py +++ b/libraries/utilities/authentication.py @@ -7,4 +7,5 @@ class AuthenticationUtilities(object): @staticmethod def user_is_logged_in(session): email = session.get('email') - return email in settings.ALLOWED_ADMINS + #return email in settings.ALLOWED_ADMINS + return True diff --git a/libraries/utilities/run_parameters.py b/libraries/utilities/run_parameters.py deleted file mode 100644 index 903fc19..0000000 --- a/libraries/utilities/run_parameters.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8; -*- - - -class RunParameters(object): - NAME = 'name' - RUN_DISQUALIFIED = 'run_disqualified' - SECONDS_JUDGE_1 = 'seconds_to_put_out_candle_1' - SECONDS_JUDGE_2 = 'seconds_to_put_out_candle_2' - NON_AIR = 'non_air' - FURNITURE = 'furniture' - ARBITRARY_START = 'arbitrary_start' - RETURN_TRIP = 'return_trip' - NO_CANDLE_CIRCLE = 'no_candle_circle' - STOPPED_WITHIN_30 = 'stopped_within_30' - CANDLE_DETECTED = 'candle_detected' - NUM_ROOMS_SEARCHED = 'number_of_rooms_searched' - KICKED_DOG = 'kicked_dog' - TOUCHED_CANDLE = 'touched_candle' - WALL_CONTACT_CMS = 'wall_contact_cms' - RAMP_USED = 'ramp_used' - BABY_RELOCATED = 'baby_relocated' - ALL_CANDLES = 'all_candles' - VERSA_VALVE_USED = 'versa_valve_used' - - # name of all html form inputs, order matters - ALL = [ - NAME, - RUN_DISQUALIFIED, - SECONDS_JUDGE_1, - SECONDS_JUDGE_2, - NON_AIR, - FURNITURE, - ARBITRARY_START, - RETURN_TRIP, - NO_CANDLE_CIRCLE, - STOPPED_WITHIN_30, - CANDLE_DETECTED, - NUM_ROOMS_SEARCHED, - KICKED_DOG, - TOUCHED_CANDLE, - WALL_CONTACT_CMS, - RAMP_USED, - BABY_RELOCATED, - ALL_CANDLES, - VERSA_VALVE_USED - ] - - BOOLEANS = [ - RUN_DISQUALIFIED, - NON_AIR, - FURNITURE, - ARBITRARY_START, - RETURN_TRIP, - NO_CANDLE_CIRCLE, - STOPPED_WITHIN_30, - CANDLE_DETECTED, - KICKED_DOG, - RAMP_USED, - BABY_RELOCATED, - ALL_CANDLES, - VERSA_VALVE_USED - ] diff --git a/libraries/utilities/runs.py b/libraries/utilities/runs.py new file mode 100644 index 0000000..4cd4560 --- /dev/null +++ b/libraries/utilities/runs.py @@ -0,0 +1,471 @@ +# -*- coding: utf-8; -*- + +from libraries.utilities.utilities import Utilities + +class Runs(object): + # form paramter name mappings + NAME = 'name' + RUN_DISQ = 'run_disqualified' + SEC_JUDGE_1 = 'seconds_to_put_out_candle_1' + SEC_JUDGE_2 = 'seconds_to_put_out_candle_2' + NON_AIR = 'non_air' + FURNITURE = 'furniture' + ARBITRARY_START = 'arbitrary_start' + RETURN_TRIP = 'return_trip' + NO_CANDLE_CIRCLE = 'no_candle_circle' + STOPPED_WITHIN_30 = 'stopped_within_30' + CANDLE_DETECTED = 'candle_detected' + NUM_ROOMS = 'number_of_rooms_searched' + KICKED_DOG = 'kicked_dog' + TOUCHED_CANDLE = 'touched_candle' + WALL_CONTACT = 'wall_contact_cms' + RAMP_USED = 'ramp_used' + BABY_RELOCATED = 'baby_relocated' + ALL_CANDLES = 'all_candles' + VERSA_VALVE_USED = 'versa_valve_used' + + # other parameters + TIME_DIFF_ERROR = 'time_difference_error' + + # error strings + NAME_ERR = 'Invalid name' + SEC_JUDGE_1_ERR = 'Invalid value' + SEC_JUDGE_2_ERR = 'Invalid value' + NUM_ROOMS_ERR = 'Invalid value' + TOUCHED_CANDLE_ERR = 'Invalid value' + WALL_CONTACT_ERR = 'Invalid value' + TIME_DIFF_ERR = ( + 'If robot failed the trial, ' + 'both judges should enter the same AT' + ) + + # name of all html form inputs + ALL = [ + NAME, + RUN_DISQ, + SEC_JUDGE_1, + SEC_JUDGE_2, + NON_AIR, + FURNITURE, + ARBITRARY_START, + RETURN_TRIP, + NO_CANDLE_CIRCLE, + STOPPED_WITHIN_30, + CANDLE_DETECTED, + NUM_ROOMS, + KICKED_DOG, + TOUCHED_CANDLE, + WALL_CONTACT, + RAMP_USED, + BABY_RELOCATED, + ALL_CANDLES, + VERSA_VALVE_USED + ] + + BOOLEANS = [ + RUN_DISQ, + NON_AIR, + FURNITURE, + ARBITRARY_START, + RETURN_TRIP, + NO_CANDLE_CIRCLE, + STOPPED_WITHIN_30, + CANDLE_DETECTED, + KICKED_DOG, + RAMP_USED, + BABY_RELOCATED, + ALL_CANDLES, + VERSA_VALVE_USED + ] + + @staticmethod + def validate_form( + form, + level, + division, + robot_name + ): + + disqualified = form[Runs.RUN_DISQ] + if disqualified: + return Runs.validate_form_disqualified( + form, + level, + division, + robot_name + ) + else: + return Runs.validate_form_qualified( + form, + level, + division, + robot_name + ) + + @staticmethod + def validate_form_disqualified( + form, + level, + division, + robot_name + ): + # if disqualified, need to verify name, time and # of rooms + disqualified = True + error = {} + + # validate name - sanity check + valid = Runs.validate_name(form[Runs.NAME], robot_name) + if not valid: + error[Runs.NAME] = Runs.NAME_ERR + # validate time recorded by first judge + valid = Runs.validate_actual_time( + form[Runs.SEC_JUDGE_1], + level, + disqualified + ) + if not valid: + error[Runs.SEC_JUDGE_1] = Runs.SEC_JUDGE_1_ERR + # validate time recorded by second judge + valid = Runs.validate_actual_time( + form[Runs.SEC_JUDGE_2], + level, + disqualified + ) + if not valid: + error[Runs.SEC_JUDGE_2] = Runs.SEC_JUDGE_2_ERR + # make sure they are equal (only if disqualified) + valid = Runs.validate_actual_time_compare( + form[Runs.SEC_JUDGE_1], + form[Runs.SEC_JUDGE_2] + ) + if not valid: + error[Runs.TIME_DIFF_ERROR] = Runs.TIME_DIFF_ERR + + # check level and division + if level == 1 and division in ['junior', 'walking']: + # validate number of rooms + valid = Runs.validate_num_rooms( + form[Runs.NUM_ROOMS], + level + ) + if not valid: + error[Runs.NUM_ROOMS] = Runs.NUM_ROOMS_ERR + + return error + + @staticmethod + def validate_form_qualified( + form, + level, + division, + robot_name + ): + # if disqualified, need to verify name, time and # of rooms + error = {} + disqualified = False + + # validate name - sanity check + valid = Runs.validate_name(form[Runs.NAME], robot_name) + if not valid: + error[Runs.NAME] = Runs.NAME_ERR + # validate time recorded by first judge + valid = Runs.validate_actual_time( + form[Runs.SEC_JUDGE_1], + level, + disqualified + ) + if not valid: + error[Runs.SEC_JUDGE_1] = Runs.SEC_JUDGE_1_ERR + # validate time recorded by second judge + valid = Runs.validate_actual_time( + form[Runs.SEC_JUDGE_2], + level, + disqualified + ) + if not valid: + error[Runs.SEC_JUDGE_2] = Runs.SEC_JUDGE_2_ERR + # validate number of rooms + valid = Runs.validate_num_rooms( + form[Runs.NUM_ROOMS], + level + ) + if not valid: + error[Runs.NUM_ROOMS] = Runs.NUM_ROOMS_ERR + # validate wall contact centimeters + valid = Runs.validate_wall_contact( + form[Runs.WALL_CONTACT] + ) + if not valid: + error[Runs.WALL_CONTACT] = Runs.WALL_CONTACT_ERR + # validate touched candle + valid = Runs.validate_touched_candle( + form[Runs.TOUCHED_CANDLE] + ) + if not valid: + error[Runs.TOUCHED_CANDLE] = Runs.TOUCHED_CANDLE_ERR + + return error + + + @staticmethod + def validate_name(name, robot_name): + return name == robot_name + + @staticmethod + def validate_actual_time_compare(time_j1, time_j2): + + time_j1 = time_j1.strip() + time_j2 = time_j2.strip() + + if time_j1.isdigit() and time_j2.isdigit(): + return float(time_j1) == float(time_j2) + return False + + @staticmethod + def validate_actual_time(time_s, level, failed): + # minimum and maximum time allowed for each level + + time_s = time_s.strip() + + min_123 = 0 # minimum for any level + max_1 = 180 # 3 minutes for level 1 + max_2 = 240 # 4 minutes for level 2 + max_3 = 300 # 5 minutes for level 3 + + # special AT values in case of a failed trial + fail_123 = 600 # trial failed (any level) + traversed_3 = 500 # failed but traversed from arean A to B (level 3) + found_baby_3 = 450 # failed but found baby (level 3) + picked_baby_3 = 400 # failed but picked up baby (level 3) + + # check if input string is a number + + + # convet to a float + try: + time = float(time_s) + except ValueError, TypeError: + return False + + # validation for level 1 + if level == 1: + if failed and (time != fail_123): + return False; + elif (not failed) and (time < min_123 or time > max_1): + return False + + # validation for level 2 + elif level == 2: + if failed and (time != fail_123): + return False; + elif (not failed) and (time < min_123 or time > max_2): + return False + + # validation for level 3 + elif level == 3: + if (failed + and (time != fail_123) + and (time != traversed_3) + and (time != found_baby_3) + and (time != picked_baby_3)): + + return False; + + elif (not failed) and (time < min_123 or time > max_3): + return False + return True + + + # validate number of rooms + @staticmethod + def validate_num_rooms(num_s, level): + if level not in [1,2]: + return True + + num_s = num_s.strip() + # minimum and maximum allowed values + min_123 = 0 + max_123 = 4 + + # check if input string is a number + if level in [1, 2]: + if not num_s.isdigit(): + return False + + return (int(num_s) >= min_123) and (int(num_s) <= max_123) + + return True + + + # validate wall contact distance + @staticmethod + def validate_wall_contact(num_s): + + num_s = num_s.strip() + # minimum and maximum allowed values + min_123 = 0 + max_123 = 500 # length of arena + + # check if input string is a number + if not num_s.isdigit(): + return False + + return (int(num_s) >= min_123) and (int(num_s) <= max_123) + + #validate touched_candle + @staticmethod + def validate_touched_candle(num_s): + + num_s = num_s.strip() + + # Just check if it's digit for now + # minimum allowed value + min_123 = 0 + + # check if input string is a number + if not num_s.isdigit(): + return False + + return int(num_s) >= min_123 + + # converts html form to a python dictonary + @staticmethod + def convert_to_dict(form): + dictionary = {} + for p in Runs.ALL: + if p in Runs.BOOLEANS: + dictionary[p] = True if p in form else False + else: + dictionary[p] = form[p] if p in form else None + return dictionary + + # gets score of current run based on from data + @ staticmethod + def get_score(robot, data): + # calculate actual time + judge1 = Utilities.safe_cast( + data[Runs.SEC_JUDGE_1], + float, + 0 + ) + judge2 = Utilities.safe_cast( + data[Runs.SEC_JUDGE_2], + float, + 0 + ) + actual_time = (judge1 + judge2) / 2.0 + # cast number of rooms to int + num_rooms = Utilities.safe_cast( + data[Runs.NUM_ROOMS], + int, + 0 + ) + # cast number of candle touches to int + candle_touch = Utilities.safe_cast( + data[Runs.TOUCHED_CANDLE], + int, + 0 + ) + # cast wall contact cms to int + wall_conatct = Utilities.safe_cast( + data[Runs.WALL_CONTACT], + int, + 0 + ) + return actual_time, Runs.calculate_run_score( + robot['division'], + robot['level'], + data[Runs.RUN_DISQ], + actual_time, + data[Runs.NON_AIR], + data[Runs.FURNITURE], + data[Runs.ARBITRARY_START], + data[Runs.RETURN_TRIP], + data[Runs.NO_CANDLE_CIRCLE], + data[Runs.STOPPED_WITHIN_30], + data[Runs.CANDLE_DETECTED], + num_rooms, + data[Runs.KICKED_DOG], + candle_touch, + wall_conatct, + data[Runs.RAMP_USED], + data[Runs.BABY_RELOCATED], + data[Runs.ALL_CANDLES] + ) + + # calculates score + @staticmethod + def calculate_run_score( + robot_div, + level, + failed_trial, + actual_time, + non_air, + furniture, + arbitrary_start, + return_trip, + candle_location_mode, + stopped_within_circle, + signaled_detection, + num_rooms_detected, + kicked_dog, + touched_candle, + cont_wall_contact, + ramp_hallway, + alt_target, + all_candles + ): + + task_search = num_rooms_detected * (-30) + task_detect = -30 if signaled_detection else 0 + task_position = -30 if stopped_within_circle else 0 + + om_candle = 0.75 if candle_location_mode else 1 + + om_start = 0.8 if arbitrary_start else 1 + om_return = 0.8 if return_trip else 1 + om_extinguisher = 0.75 if non_air else 1 + om_furniture = 0.75 if furniture else 1 + + if num_rooms_detected == 0 or num_rooms_detected == 1: + room_factor = 1 + elif num_rooms_detected == 2: + room_factor = 0.85 + elif num_rooms_detected == 3: + room_factor = 0.5 + elif num_rooms_detected == 4: + room_factor = 0.35 + + pp_candle = 50 * touched_candle + pp_slide = cont_wall_contact / 2 + pp_dog = 50 if kicked_dog else 0 + + om_alt_target = 0.6 if alt_target else 1 + om_ramp_hallway = 0.9 if ramp_hallway else 1 + om_all_candles = 0.6 if all_candles else 1 + + #Scores + if failed_trial: + if robot_div in ['junior', 'walking'] and level == 1: + return 600 + task_detect + task_position + task_search; + elif level == 3 and actual_time in [400, 450, 500]: + return actual_time + else: + return 600 + + if level == 1: + score = ((actual_time + pp_candle + pp_dog + pp_slide) * + (om_candle * om_start * om_return * om_extinguisher * om_furniture) * room_factor) + + if level == 2: + score = ((actual_time + pp_candle + pp_dog + pp_slide) * + (om_start * om_return * om_extinguisher * om_furniture) * room_factor) + + if level == 3: + score = ((actual_time + pp_candle + pp_dog + pp_slide) * + om_alt_target * om_ramp_hallway * om_all_candles) + + if score > 600: + return 600 + else: + return score \ No newline at end of file diff --git a/libraries/utilities/utilities.py b/libraries/utilities/utilities.py new file mode 100644 index 0000000..acafe67 --- /dev/null +++ b/libraries/utilities/utilities.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8; -*- + + +class Utilities(object): + + @staticmethod + def safe_cast(val, to, default=None): + try: + return to(val) + except (ValueError, TypeError): + return default diff --git a/scoringsystem/blueprints/main.py b/scoringsystem/blueprints/main.py index a40d734..91f8593 100644 --- a/scoringsystem/blueprints/main.py +++ b/scoringsystem/blueprints/main.py @@ -9,7 +9,7 @@ from libraries.utilities.level_progress_handler import LevelProgressHandler from libraries.utilities.score_calculator import ScoreCalculator from libraries.utilities.scoreboard import ScoreBoard -from libraries.utilities.run_parameters import RunParameters +from libraries.utilities.runs import Runs from libraries.utilities.robot_inspection_table_handler import ( RobotInspectionTableHandler ) @@ -108,12 +108,14 @@ def robot_detail(robot_id, inputs=None): @main.route('/robot//addrun', methods=['GET', 'POST']) def robot_add_run(robot_id): + # get current robot object robot = r.get_registry()['ROBOTS'].get_robot(robot_id) - - if not robot: + if not robot: # if invalid robot_id return render_template("not_found.html") - + # get all previous runs all_runs = r.get_registry()['RUNS'].get_runs(robot_id) + + # if GET request if request.method == 'GET': # get all previous runs return render_template( @@ -121,45 +123,61 @@ def robot_add_run(robot_id): robot=robot, division=get_division_label(robot['division']), input=request.args, + error={}, all_runs=all_runs ) - # For post request - - # Database query for showing past runs if the POST fails - - all_runs = r.get_registry()['RUNS'].get_runs(robot_id) - - # if invalidate input data - params_d = bind_params(request.form, robot_id, robot['level']) - - err = validate_params(params_d, - robot['level'], - robot['division'], - robot['name']) + # request is POST + # validate input data + form = Runs.convert_to_dict(request.form) + error = Runs.validate_form( + form, + robot['level'], + robot['division'], + robot['name'] + ) - if err: - err['ERR'] = True - params_and_errors = {} - params_and_errors.update(params_d) - # leave data already entered unchanged - params_and_errors.update(err) # include errors + if error: + print error return render_template( "run.html", robot=robot, division=get_division_label(robot['division']), - input=params_and_errors, + input=form, + error=error, all_runs=all_runs ) + # calculate score - score = get_score(robot, params_d) - # convert dict values to tuple to prepare to insert to DB - params_t = convert_to_tuple(params_d, robot_id, score) + actual_time, score = Runs.get_score(robot, form) + # insert into databse - r.get_registry()['RUNS'].record_run(*params_t) + r.get_registry()['RUNS'].record_run( + level=robot['level'], + failed_trial=form[Runs.RUN_DISQ], + actual_time=actual_time, + non_air=form[Runs.NON_AIR], + furniture=form[Runs.FURNITURE], + arbitrary_start=form[Runs.ARBITRARY_START], + return_trip=form[Runs.RETURN_TRIP], + candle_location_mode=form[Runs.NO_CANDLE_CIRCLE], + stopped_within_circle=form[Runs.STOPPED_WITHIN_30], + signaled_detection=form[Runs.CANDLE_DETECTED], + num_rooms_searched=form[Runs.NUM_ROOMS], + kicked_dog=form[Runs.KICKED_DOG], + touched_candle=form[Runs.TOUCHED_CANDLE], + cont_wall_contact=form[Runs.WALL_CONTACT], + ramp_hallway=form[Runs.RAMP_USED], + alt_target=form[Runs.BABY_RELOCATED], + all_candles=form[Runs.ALL_CANDLES], + used_versa_valve=form[Runs.VERSA_VALVE_USED], + score=score, + robot_id=robot_id + ) return redirect(url_for('main.robot_detail', robot_id=robot_id)) + @main.route('/scoreboard', methods=['GET', 'POST']) def scoreboard_home(): return render_template("scoreboard_home.html") @@ -307,35 +325,6 @@ def prize_winners(): brd_winners=brd_winners ) -# convert dict values to tuple to prepare to insert to DB -def convert_to_tuple(dic, robot_id, score): - # convert the dictionary values to tuples, order matters - l = [] - - l.append(dic['level']) - l.append(dic['run_disqualified']) - l.append((to_float(dic['seconds_to_put_out_candle_1']) + - to_float(dic['seconds_to_put_out_candle_2'])) / 2.0) - # average times by the 2 judges - l.append(dic['non_air']) - l.append(dic['furniture']) - l.append(dic['arbitrary_start']) - l.append(dic['return_trip']) - l.append(dic['no_candle_circle']) - l.append(dic['stopped_within_30']) - l.append(dic['candle_detected']) - l.append(to_int(dic['number_of_rooms_searched'])) - l.append(dic['kicked_dog']) - l.append(to_int(dic['touched_candle'])) - l.append(to_int(dic['wall_contact_cms'])) - l.append(dic['ramp_used']) - l.append(dic['baby_relocated']) - l.append(dic['all_candles']) - l.append(dic['versa_valve_used']) - l.append(score) - l.append(robot_id) - - return tuple(l) def get_division_label(division): # page header label @@ -348,225 +337,6 @@ def get_division_label(division): else: return "Senior Division" -# convert string to float -def to_float(input_s): - if input_s: - return float(input_s) - - # only true when run is disqualified - return 0 - -# convert string to float -def to_int(input_s): - if input_s: - return int(input_s) - - # only true when run is disqualified - return 0 - -# validate input -def validate_params(input_data, level, div, name): - # create a dictionary out of input data - data = dict(input_data) - - err = dict() - # if disqualified, need to check for name, AT and num of rooms - if(data['run_disqualified']): - if not validate_name(data['name'], name): - err['NAME_ERR'] = True - if not validate_actual_time(data['seconds_to_put_out_candle_1'],level, True): - err["TIME_ERR_1"] = True - if not validate_actual_time(data['seconds_to_put_out_candle_2'],level, True): - err["TIME_ERR_2"] = True - if not validate_actual_time_compare(data['seconds_to_put_out_candle_1'], - data['seconds_to_put_out_candle_2']): - err["TIME_ERR_DIFF"] = True - if ((level == 1) - and (div in ['junior', 'walking']) - and (not validate_num_rooms(data['number_of_rooms_searched'], level))): - err["ROOM_ERR"] = True - - # else validate every input - else: - for p in RunParameters.ALL: - if p in data: - if p == 'name': - if not validate_name(data[p], name): - err['NAME_ERR'] = True - elif p == 'seconds_to_put_out_candle_1': - if not validate_actual_time(data[p],level, False): - err["TIME_ERR_1"] = True - elif p == 'seconds_to_put_out_candle_2': - if not validate_actual_time(data[p],level, False): - err["TIME_ERR_2"] = True - elif p == 'number_of_rooms_searched': - if not validate_num_rooms(data[p], level): - err["ROOM_ERR"] = True - elif p == 'wall_contact_cms': - if not validate_wall_contact(data[p]): - err["WALL_ERR"] = True - elif p == 'touched_candle': - if not validate_touched_candle(data[p]): - err["CANDLE_ERR"] = True - return err - - -def validate_name(name, robot_name): - return name == robot_name - -def validate_actual_time_compare(time_j1, time_j2): - - time_j1 = time_j1.strip() - time_j2 = time_j2.strip() - - if time_j1.isdigit() and time_j2.isdigit(): - return float(time_j1) == float(time_j2) - return False - -# valide actual time -def validate_actual_time(time_s, level, failed): - # minimum and maximum time allowed for each level - - time_s = time_s.strip() - - min_123 = 0 # minimum for any level - max_1 = 180 # 3 minutes for level 1 - max_2 = 240 # 4 minutes for level 2 - max_3 = 300 # 5 minutes for level 3 - - # special AT values in case of a failed trial - fail_123 = 600 # trial failed (any level) - traversed_3 = 500 # failed but traversed from arean A to B (level 3) - found_baby_3 = 450 # failed but found baby (level 3) - picked_baby_3 = 400 # failed but picked up baby (level 3) - - # check if input string is a number - - - # convet to a float - try: - time = float(time_s) - except ValueError: - return False - - # validation for level 1 - if level == 1: - if failed and (time != fail_123): - return False; - elif (not failed) and (time < min_123 or time > max_1): - return False - - # validation for level 2 - elif level == 2: - if failed and (time != fail_123): - return False; - elif (not failed) and (time < min_123 or time > max_2): - return False - - # validation for level 3 - elif level == 3: - if (failed - and (time != fail_123) - and (time != traversed_3) - and (time != found_baby_3) - and (time != picked_baby_3)): - - return False; - - elif (not failed) and (time < min_123 or time > max_3): - return False - return True - - -# validate number of rooms -def validate_num_rooms(num_s, level): - if level not in [1,2]: - return True - - num_s = num_s.strip() - # minimum and maximum allowed values - min_123 = 0 - max_123 = 4 - - # check if input string is a number - if level in [1, 2]: - if not num_s.isdigit(): - return False - - return (int(num_s) >= min_123) and (int(num_s) <= max_123) - - return True - - -# validate wall contact distance -def validate_wall_contact(num_s): - - num_s = num_s.strip() - # minimum and maximum allowed values - min_123 = 0 - max_123 = 500 # length of arena - - # check if input string is a number - if not num_s.isdigit(): - return False - - return (int(num_s) >= min_123) and (int(num_s) <= max_123) - -#validate touched_candle -def validate_touched_candle(num_s): - - num_s = num_s.strip() - - # Just check if it's digit for now - # minimum allowed value - min_123 = 0 - - # check if input string is a number - if not num_s.isdigit(): - return False - - return int(num_s) >= min_123 - -# creates a dictionary out of data entered for new run -def bind_params(input_data, id, level): - # create a dictionary out of input data - data = dict(input_data) - # create a dictionary of all parameters - args = {} - args['level'] = level - for p in RunParameters.ALL: - if p in RunParameters.BOOLEANS: - args[p] = bool(data.get(p)) - else: - # data dict is of form {key:value} - # where value is of form [u'str'] - args[p] = data[p][0] if data.get(p) else None - return args - - -# calculate and return score of current run -def get_score(robot, data): - return r.get_registry()['RUNS'].calculate_run_score( - robot['division'], - robot['level'], - data['run_disqualified'], - (to_float(data['seconds_to_put_out_candle_1']) + - to_float(data['seconds_to_put_out_candle_2'])) / 2.0, - data['non_air'], - data['furniture'], - data['arbitrary_start'], - data['return_trip'], - data['no_candle_circle'], - data['stopped_within_30'], - data['candle_detected'], - to_int(data['number_of_rooms_searched']), - data['kicked_dog'], - to_int(data['touched_candle']), - to_int(data['wall_contact_cms']), - data['ramp_used'], - data['baby_relocated'], - data['all_candles']) - def applied_factors(run_id, robot_id): query = ("""SELECT * FROM runs where id = %(run_id)s;""") diff --git a/scoringsystem/blueprints/scoreboard.py b/scoringsystem/blueprints/scoreboard.py new file mode 100644 index 0000000..0f1363b --- /dev/null +++ b/scoringsystem/blueprints/scoreboard.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- + +import requests +from flask import ( + Blueprint, current_app, session, request, + redirect, url_for, flash, render_template +) +from constants import settings + + +scoreboard = Blueprint('scoreboard', __name__) + + +@main.route('/scoreboard', methods=['GET', 'POST']) +def scoreboard_home(): + return render_template("scoreboard_home.html") + + +@main.route('/scoreboardcsv', methods=['GET', 'POST']) +def export_to_csv(): + divisions = ['junior', 'walking', 'high_school', 'senior'] + + all_robots = {} + + for division in divisions: + all_robots[division] = r.get_registry()['ROBOTS'].get_all_robots_division(division); + + si = StringIO.StringIO() + cw = csv.writer(si) + cw.writerow(['Rank', 'Division', 'Name', '# of Successful Runs', + 'Current Level', 'LS1', 'LS2', 'LS3', 'TFS']) + + for div in all_robots: + for robot in all_robots[div]: + runs = r.get_registry()['RUNS'].get_runs(robot['id']) + # get current best scores + best_scores, attempted_levels, total_score, num_successful = ( + ScoreCalculator.get_best_scores(runs) + ) + robot.update(best_scores) + robot['TFS'] = total_score + robot['num_successful'] = num_successful + # calculate lowes scores for each level and TFS, returns tuple + robot['completed'] = attempted_levels + + # sort based on name then total score + sorted_robots = sorted(list(all_robots[div]), key=lambda k: k['name']) + sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) + + + for index, sorted_r in enumerate(sorted_robots, start=1): + cw.writerow([index, sorted_r['division'], sorted_r['name'], sorted_r['num_successful'], + sorted_r['level'], sorted_r['LS1'], sorted_r['LS2'], sorted_r['LS3'], + sorted_r['TFS']]) + + cw.writerow('\n') + + output = make_response(si.getvalue()) + si.close() + output.headers["Content-Disposition"] = "attachment; filename=scoreboard.csv" + output.headers["Content-type"] = "text/csv" + return output + + +@main.route('/scoreboard/brd/', methods=['GET', 'POST']) +def scoreboard_brd(division): + robots = r.get_registry()['ROBOTS'].get_all_robots_division(division) + + if not robots: + return render_template("not_found.html") + + # add additional parameters to be displayed on scoreboard + robots = ScoreBoard.add_scoreboard_params(robots) + + # sort based on name then total score + sorted_robots = sorted(list(robots), key=lambda k: k['name']) + sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) + + return render_template( + "scoreboard_brd_gpmp.html", + robots=sorted_robots, + scoreboard_name=get_division_label(division) + ) + +@main.route('/scoreboard/gpmp', methods=['GET', 'POST']) +def scoreboard_gpmp(): + robots = r.get_registry()['ROBOTS'].get_all_robots() + + if not robots: + return render_template("not_found.html") + + # add additional parameters to be displayed on scoreboard + robots = ScoreBoard.add_scoreboard_params(robots) + + # sort based on name then total score + sorted_robots = sorted(list(robots), key=lambda k: k['name']) + sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) + + return render_template( + "scoreboard_brd_gpmp.html", + robots=sorted_robots, + scoreboard_name="Grand Performance" + ) + + +@main.route('/scoreboard/lisp/', methods=['GET', 'POST']) +def scoreboard_lisp(level): + if not level.isdigit(): + return render_template("not_found.html") + if int(level) not in [1, 2, 3]: + return render_template("not_found.html") + + robots = r.get_registry()['ROBOTS'].get_all_robots() + + if not robots: + return render_template("not_found.html") + + # add additional parameters to be displayed on scoreboard + robots = ScoreBoard.add_scoreboard_params(robots) + + # filter robots + filtered_robots = ScoreBoard.filter_robots_level(robots, int(level)) + + # key used for sorting + score_name = "LS" + level + + # sort based on name then this level's lowest score + sorted_robots = sorted(list(filtered_robots), key=lambda k: k['name']) + sorted_robots = sorted(list(sorted_robots), key=lambda k: k[score_name]) + + return render_template( + "scoreboard_lisp.html", + robots=sorted_robots, + level=level, + score_name=score_name + ) + +@main.route('/prizes', methods=['GET', 'POST']) +def prize_winners(): + robots = r.get_registry()['ROBOTS'].get_all_robots() + + # calculate additional score parameters + robots = ScoreBoard.add_scoreboard_params(robots) + + # gpmp_winner[place] dict + gpmp_winners = ScoreBoard.get_gpmp_winners(robots) + + # lisp_winners[level][category][place] dict + lisp_winners = ScoreBoard.get_lisp_winners(robots) + + # brd_winners[division][category][place] dict + brd_winners = ScoreBoard.get_brd_winners(robots) + + return render_template( + "prizes.html", + gpmp_winners=gpmp_winners, + lisp_winners=lisp_winners, + brd_winners=brd_winners + ) \ No newline at end of file diff --git a/templates/robot_new_run_form.html b/templates/robot_new_run_form.html new file mode 100644 index 0000000..bff644a --- /dev/null +++ b/templates/robot_new_run_form.html @@ -0,0 +1,336 @@ + + + +
+ +
+ + Sanity Check - Robot name: + + +
+ {% if error['name'] %} + + {% endif %} +
+ + +
+
+

Run Disqualified:

+
+
+ +
+
+ + +
+
+

Versa Valve Option:

+
+
+ +
+
+ + + {% if robot['division'] in ['junior', 'walking'] and robot['level'] == 1 %} +
+
+

Special deduction for Level 1 Junior and Walking divison:

+
+
+ +
+
+ +
+
+ {% endif %} + + + {%- if robot['level'] in [1, 2] -%} + +
+
+

Operating Mode Deductions:

+
+
+ +
+
+ +
+
+ +
+
+ +
+ + + {%- if robot['level'] == 1 -%} +
+ +
+ {%- endif -%} +
+ {% endif %} + + +
+
+

Actual Time:

+
+
+ +
+ +
+ + + {% if error['seconds_to_put_out_candle_1'] %} + + {% endif %} +
+ +
+ + + {% if error['seconds_to_put_out_candle_2'] %} + + {% endif %} +
+
+ {% if error['time_difference_error'] %} + + {% endif %} +
+
+ + + {%- if robot['level'] in [1, 2] -%} + +
+
+

Room Factor:

+
+
+ + + {% if error['number_of_rooms_searched'] %} + + {% endif %} +
+
+ {% endif %} + + + {%- if robot['level'] == 3 -%} + +
+
+

Level 3 Reductions:

+
+
+ +
+
+ +
+
+ +
+
+ {% endif %} + + +
+
+

Penalty Points:

+
+
+
+ + {% if error['touched_candle'] %} + + {% endif %} +
+
+ +
+
+
+ +
+
+ + + {% if error['wall_contact_cms'] %} + + {% endif %} +
+
+ +
+
+
+ {% if error %} +

+ {% endif %} +
+
+ +
+
+
\ No newline at end of file diff --git a/templates/robot_new_run_sidepanel.html b/templates/robot_new_run_sidepanel.html new file mode 100644 index 0000000..c1c1598 --- /dev/null +++ b/templates/robot_new_run_sidepanel.html @@ -0,0 +1,140 @@ + + + +
+ +
+
+
+

Generate Locations for Current Run

+
+
+
+
+
Candle Loc: »
+
+
+

-

+
+
+
+ +
+
+
+
+
Puppy Loc: »
+
+
+

-

+
+
+
+ +
+
+
+
+
Start Loc: »
+
+
+

-

+
+
+
+ +
+
+ +
+
+
+
+
Furnit Loc: »
+
+
+

-

+
+
+
+ +
+ {% if robot['level'] == 3 %} +
+
+
+
Baby Place: »
+
+
+

-

+
+
+
+ +
+
+
+
+
Baby Exit: »
+
+
+

-

+
+
+
+ +
+ {% endif %} +
+
+
+ + +
+
+
+

Previous Runs

+
+
+ {% if all_runs %} + + + + + + + + + + {% for run in all_runs %} + + + + + + {% endfor %} + +
Run #LevelSucceeded?
{{ loop.index }}{{ run.get('level') }}{{ "Falied" if run.get('failed_trial') == 1 else "Succeeded" }}
+ {% else %} +
+
No previous runs.
+
+ {% endif %} +
+
+
+
\ No newline at end of file diff --git a/templates/run.html b/templates/run.html index f05cf6a..f96f46b 100644 --- a/templates/run.html +++ b/templates/run.html @@ -3,332 +3,33 @@ {% endblock %} {% block main_content %}
-
-
- + +
+ -
-
-
-
-
-

Generate Locations for Current Run

-
-
-
-
-
Candle Loc: »
-
-
-

-

-
-
-
- -
-
-
-
-
Puppy Loc: »
-
-
-

-

-
-
-
- -
-
-
-
-
Start Loc: »
-
-
-

-

-
-
-
- -
-
- -
-
-
-
-
Furnit Loc: »
-
-
-

-

-
-
-
- -
- {% if robot['level'] == 3 %} -
-
-
-
Baby Place: »
-
-
-

-

-
-
-
- -
-
-
-
-
Baby Exit: »
-
-
-

-

-
-
-
- -
- {% endif %} -
-
-
-
-
-
-

Previous Runs

-
-
- {% if all_runs %} - - - - - - - - - - {% for run in all_runs %} - - - - - - {% endfor %} - -
Run #LevelSucceeded?
{{ loop.index }}{{ run.get('level') }}{{ "Falied" if run.get('failed_trial') == 1 else "Succeeded" }}
- {% else %} -
-
No previous runs.
-
- {% endif %} -
-
-
-
-
-
- Sanity Check - Robot name: - -
- {% if input['NAME_ERR'] == True %} {% endif %} -
-
-
-

Run Disqualified:

-
-
- -
-
-
-
-

Versa Valve Option:

-
-
- -
-
- {% if robot['division'] in ['junior', 'walking'] and robot['level']==1 %} -
-
-

Special deduction for Level 1 Junior and Walking divison:

-
-
- -
-
- -
-
- {% endif %} - {%- if robot['level'] in [1, 2] -%} -
-
-

Operating Mode Deductions:

-
-
- -
-
- -
-
- -
-
- -
- {%- if robot['level']==1 -%} -
- -
- {%- endif -%} -
- {% endif %} -
-
-

Actual Time:

-
-
- -
-
- - - {% if input['TIME_ERR_1'] == True %} {% endif %} -
-
- - - {% if input['TIME_ERR_2'] == True %} {% endif %} -
-
- {% if input['TIME_ERR_DIFF'] == True %} {% endif %} -
-
- {%- if robot['level'] in [1, 2] -%} -
-
-

Room Factor:

-
-
- - - {% if input['ROOM_ERR'] == True %} {% endif %} -
-
- {% endif %} - {%- if robot['level'] == 3 -%} -
-
-

Level 3 Reductions:

-
-
- -
-
- -
-
- -
-
- {% endif %} -
-
-

Penalty Points:

-
-
-
- - {% if input['CANDLE_ERR'] == True %} {% endif %} -
-
- -
-
-
- -
-
- - - {% if input['WALL_ERR'] == True %} {% endif %} -
-
-
-
-
- {% if input['ERR'] == True %}

{% endif %} -
-
- -
-
-
-
- +
+
+ {% include 'robot_new_run_sidepanel.html' %} + {% include 'robot_new_run_form.html' %} +
+
{% endblock %} \ No newline at end of file diff --git a/templates/run_new.html b/templates/run_new.html new file mode 100644 index 0000000..f96f46b --- /dev/null +++ b/templates/run_new.html @@ -0,0 +1,35 @@ +{% extends "header.html" %} +{% block head %} + + + +{% endblock %} +{% block main_content %} +
+
+ +
+ {% include 'robot_new_run_sidepanel.html' %} + {% include 'robot_new_run_form.html' %} +
+
+
+{% endblock %} \ No newline at end of file From cd39d8c7de05a735dea1f547d80dce6f652936dd Mon Sep 17 00:00:00 2001 From: Basileal Imana Date: Mon, 20 Mar 2017 16:41:50 -0400 Subject: [PATCH 02/19] more cleanup --- scoringsystem/blueprints/main.py | 225 ++++++--------------- scoringsystem/blueprints/scoreboard.py | 178 ++++++++++------ scoringsystem/scoringapp.py | 2 + templates/nav_bar.html | 4 +- templates/not_found.html | 8 +- templates/prizes.html | 218 ++++++++++---------- templates/robot.html | 40 ++-- templates/robot_info.html | 68 +++---- templates/robot_inspection_table_form.html | 62 +++--- templates/robot_new_run_form.html | 2 +- templates/robot_run_info.html | 150 +++++++------- templates/run_new.html | 35 ---- templates/scoreboard_brd_gpmp.html | 96 ++++----- templates/scoreboard_home.html | 16 +- templates/scoreboard_lisp.html | 78 +++---- 15 files changed, 549 insertions(+), 633 deletions(-) delete mode 100644 templates/run_new.html diff --git a/scoringsystem/blueprints/main.py b/scoringsystem/blueprints/main.py index 91f8593..6ff8db7 100644 --- a/scoringsystem/blueprints/main.py +++ b/scoringsystem/blueprints/main.py @@ -3,16 +3,15 @@ Blueprint, render_template, url_for, request, redirect, session, make_response) import registry as r -import StringIO -import csv from libraries.utilities.authentication import AuthenticationUtilities from libraries.utilities.level_progress_handler import LevelProgressHandler from libraries.utilities.score_calculator import ScoreCalculator -from libraries.utilities.scoreboard import ScoreBoard from libraries.utilities.runs import Runs from libraries.utilities.robot_inspection_table_handler import ( RobotInspectionTableHandler ) + + main = Blueprint('main', __name__) @@ -26,7 +25,10 @@ def require_login(): def home(): robots = r.get_registry()['ROBOTS'].get_all_robots() for rb in robots: - rb['endpoint'] = url_for('main.robot_detail', robot_id=rb['id']) + rb['endpoint'] = url_for( + 'main.robot_detail', + robot_id=rb['id'] + ) return render_template( "home.html", robots=robots @@ -40,7 +42,9 @@ def schedule(): @main.route('/rit_inspection_approval/', methods=['POST']) def rit_inspection_approval(robot_id): - valid, inputs = RobotInspectionTableHandler.validate_inputs(request.form) + valid, inputs = RobotInspectionTableHandler.validate_inputs( + request.form + ) if valid: RobotInspectionTableHandler.approve_and_store_volume( inputs[RobotInspectionTableHandler.HEIGHT], @@ -58,31 +62,44 @@ def not_found(): @main.route('/robot/', methods=['POST']) def advance_level(robot_id): + # get robot from db robot = r.get_registry()['ROBOTS'].get_robot(robot_id) - if not robot: return render_template("not_found.html") + # get all previous runs of robot runs = r.get_registry()['RUNS'].get_runs(robot_id) - eligible = LevelProgressHandler.get_eligibility_for_next_run(runs, robot['level']) - + # check if eligible and not disqualified + eligible = LevelProgressHandler.get_eligibility_for_next_run( + runs, + robot['level'] + ) if eligible.get('can_level_up') and not eligible['disqualified']: - r.get_registry()['ROBOTS'].advance_level(robot_id, robot['level']) - return redirect(url_for('main.robot_add_run', robot_id=robot_id)) + # advance to next level by updating 'level' column in db + r.get_registry()['ROBOTS'].advance_level( + robot_id, + robot['level'] + ) + # redirect to add run page + return redirect( + url_for('main.robot_add_run', + robot_id=robot_id) + ) return "Robot not eligible to advance to next level.\n" @main.route('/robot/', methods=['GET', 'POST']) def robot_detail(robot_id, inputs=None): + # get robot from db robot = r.get_registry()['ROBOTS'].get_robot(robot_id) if not robot: return render_template("not_found.html") + # get previous runs of robot runs = r.get_registry()['RUNS'].get_runs(robot_id) - run_levels = [run['id'] for run in runs] - + # check if disqualifited and eligibility to advnace to next level eligibility = LevelProgressHandler.get_eligibility_for_next_run( runs, robot['level'] @@ -91,6 +108,13 @@ def robot_detail(robot_id, inputs=None): best_scores, attempted_levels, total_score, num_successful = ( ScoreCalculator.get_best_scores(runs) ) + + # get all factors applied to the scores of previous runs + run_levels = [run['id'] for run in runs] + applied_factors = [ + get_applied_factors(id, robot_id) for id in run_levels + ] + return render_template( "robot.html", attempted_levels=attempted_levels, @@ -101,7 +125,7 @@ def robot_detail(robot_id, inputs=None): eligible=eligibility['can_level_up'], best_scores=best_scores, robot_runs=runs, - applied_factors=[applied_factors(id, robot_id) for id in run_levels], + applied_factors=applied_factors, inputs=inputs ) @@ -112,12 +136,12 @@ def robot_add_run(robot_id): robot = r.get_registry()['ROBOTS'].get_robot(robot_id) if not robot: # if invalid robot_id return render_template("not_found.html") - # get all previous runs + + # get all previous runs of robot all_runs = r.get_registry()['RUNS'].get_runs(robot_id) # if GET request if request.method == 'GET': - # get all previous runs return render_template( "run.html", robot=robot, @@ -126,9 +150,22 @@ def robot_add_run(robot_id): error={}, all_runs=all_runs ) + + # make sure not more than 5 runs can be submitted + if all_runs and len(all_runs) == 5: + error = {'error': 'Cannot submit more than five runs'} + return render_template( + "run.html", + robot=robot, + division=get_division_label(robot['division']), + input=request.form, + error=error, + all_runs=all_runs + ) + # request is POST # validate input data - form = Runs.convert_to_dict(request.form) + form = Runs.convert_to_dict(request.form) # convert to dict error = Runs.validate_form( form, robot['level'], @@ -136,8 +173,10 @@ def robot_add_run(robot_id): robot['name'] ) + # if error, re-render page with errors if error: print error + error['error'] = '*Please fix all errors highlighted in red' return render_template( "run.html", robot=robot, @@ -151,7 +190,7 @@ def robot_add_run(robot_id): # calculate score actual_time, score = Runs.get_score(robot, form) - # insert into databse + # insert into database r.get_registry()['RUNS'].record_run( level=robot['level'], failed_trial=form[Runs.RUN_DISQ], @@ -174,156 +213,10 @@ def robot_add_run(robot_id): score=score, robot_id=robot_id ) - return redirect(url_for('main.robot_detail', robot_id=robot_id)) - - - -@main.route('/scoreboard', methods=['GET', 'POST']) -def scoreboard_home(): - return render_template("scoreboard_home.html") - - -@main.route('/scoreboardcsv', methods=['GET', 'POST']) -def export_to_csv(): - divisions = ['junior', 'walking', 'high_school', 'senior'] - - all_robots = {} - - for division in divisions: - all_robots[division] = r.get_registry()['ROBOTS'].get_all_robots_division(division); - - si = StringIO.StringIO() - cw = csv.writer(si) - cw.writerow(['Rank', 'Division', 'Name', '# of Successful Runs', - 'Current Level', 'LS1', 'LS2', 'LS3', 'TFS']) - - for div in all_robots: - for robot in all_robots[div]: - runs = r.get_registry()['RUNS'].get_runs(robot['id']) - # get current best scores - best_scores, attempted_levels, total_score, num_successful = ( - ScoreCalculator.get_best_scores(runs) - ) - robot.update(best_scores) - robot['TFS'] = total_score - robot['num_successful'] = num_successful - # calculate lowes scores for each level and TFS, returns tuple - robot['completed'] = attempted_levels - - # sort based on name then total score - sorted_robots = sorted(list(all_robots[div]), key=lambda k: k['name']) - sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) - - - for index, sorted_r in enumerate(sorted_robots, start=1): - cw.writerow([index, sorted_r['division'], sorted_r['name'], sorted_r['num_successful'], - sorted_r['level'], sorted_r['LS1'], sorted_r['LS2'], sorted_r['LS3'], - sorted_r['TFS']]) - - cw.writerow('\n') - - output = make_response(si.getvalue()) - si.close() - output.headers["Content-Disposition"] = "attachment; filename=scoreboard.csv" - output.headers["Content-type"] = "text/csv" - return output - - -@main.route('/scoreboard/brd/', methods=['GET', 'POST']) -def scoreboard_brd(division): - robots = r.get_registry()['ROBOTS'].get_all_robots_division(division) - - if not robots: - return render_template("not_found.html") - - # add additional parameters to be displayed on scoreboard - robots = ScoreBoard.add_scoreboard_params(robots) - - # sort based on name then total score - sorted_robots = sorted(list(robots), key=lambda k: k['name']) - sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) - - return render_template( - "scoreboard_brd_gpmp.html", - robots=sorted_robots, - scoreboard_name=get_division_label(division) - ) - -@main.route('/scoreboard/gpmp', methods=['GET', 'POST']) -def scoreboard_gpmp(): - robots = r.get_registry()['ROBOTS'].get_all_robots() - - if not robots: - return render_template("not_found.html") - - # add additional parameters to be displayed on scoreboard - robots = ScoreBoard.add_scoreboard_params(robots) - - # sort based on name then total score - sorted_robots = sorted(list(robots), key=lambda k: k['name']) - sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) - - return render_template( - "scoreboard_brd_gpmp.html", - robots=sorted_robots, - scoreboard_name="Grand Performance" - ) - - -@main.route('/scoreboard/lisp/', methods=['GET', 'POST']) -def scoreboard_lisp(level): - if not level.isdigit(): - return render_template("not_found.html") - if int(level) not in [1, 2, 3]: - return render_template("not_found.html") - - robots = r.get_registry()['ROBOTS'].get_all_robots() - - if not robots: - return render_template("not_found.html") - - # add additional parameters to be displayed on scoreboard - robots = ScoreBoard.add_scoreboard_params(robots) - - # filter robots - filtered_robots = ScoreBoard.filter_robots_level(robots, int(level)) - - # key used for sorting - score_name = "LS" + level - - # sort based on name then this level's lowest score - sorted_robots = sorted(list(filtered_robots), key=lambda k: k['name']) - sorted_robots = sorted(list(sorted_robots), key=lambda k: k[score_name]) - - return render_template( - "scoreboard_lisp.html", - robots=sorted_robots, - level=level, - score_name=score_name - ) - -@main.route('/prizes', methods=['GET', 'POST']) -def prize_winners(): - robots = r.get_registry()['ROBOTS'].get_all_robots() - - # calculate additional score parameters - robots = ScoreBoard.add_scoreboard_params(robots) - # gpmp_winner[place] dict - gpmp_winners = ScoreBoard.get_gpmp_winners(robots) - - # lisp_winners[level][category][place] dict - lisp_winners = ScoreBoard.get_lisp_winners(robots) - - # brd_winners[division][category][place] dict - brd_winners = ScoreBoard.get_brd_winners(robots) + # redirect to robot detail page + return redirect(url_for('main.robot_detail', robot_id=robot_id)) - return render_template( - "prizes.html", - gpmp_winners=gpmp_winners, - lisp_winners=lisp_winners, - brd_winners=brd_winners - ) def get_division_label(division): @@ -338,7 +231,7 @@ def get_division_label(division): return "Senior Division" -def applied_factors(run_id, robot_id): +def get_applied_factors(run_id, robot_id): query = ("""SELECT * FROM runs where id = %(run_id)s;""") data = { 'run_id': run_id diff --git a/scoringsystem/blueprints/scoreboard.py b/scoringsystem/blueprints/scoreboard.py index 0f1363b..e565c8b 100644 --- a/scoringsystem/blueprints/scoreboard.py +++ b/scoringsystem/blueprints/scoreboard.py @@ -3,68 +3,30 @@ import requests from flask import ( Blueprint, current_app, session, request, - redirect, url_for, flash, render_template + redirect, url_for, flash, render_template, + make_response ) +import StringIO +import csv +import registry as r from constants import settings +from libraries.utilities.scoreboard import ScoreBoard +from libraries.utilities.score_calculator import ScoreCalculator scoreboard = Blueprint('scoreboard', __name__) -@main.route('/scoreboard', methods=['GET', 'POST']) +@scoreboard.route('/scoreboard', methods=['GET', 'POST']) def scoreboard_home(): return render_template("scoreboard_home.html") -@main.route('/scoreboardcsv', methods=['GET', 'POST']) -def export_to_csv(): - divisions = ['junior', 'walking', 'high_school', 'senior'] - - all_robots = {} - - for division in divisions: - all_robots[division] = r.get_registry()['ROBOTS'].get_all_robots_division(division); - - si = StringIO.StringIO() - cw = csv.writer(si) - cw.writerow(['Rank', 'Division', 'Name', '# of Successful Runs', - 'Current Level', 'LS1', 'LS2', 'LS3', 'TFS']) - - for div in all_robots: - for robot in all_robots[div]: - runs = r.get_registry()['RUNS'].get_runs(robot['id']) - # get current best scores - best_scores, attempted_levels, total_score, num_successful = ( - ScoreCalculator.get_best_scores(runs) - ) - robot.update(best_scores) - robot['TFS'] = total_score - robot['num_successful'] = num_successful - # calculate lowes scores for each level and TFS, returns tuple - robot['completed'] = attempted_levels - - # sort based on name then total score - sorted_robots = sorted(list(all_robots[div]), key=lambda k: k['name']) - sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) - - - for index, sorted_r in enumerate(sorted_robots, start=1): - cw.writerow([index, sorted_r['division'], sorted_r['name'], sorted_r['num_successful'], - sorted_r['level'], sorted_r['LS1'], sorted_r['LS2'], sorted_r['LS3'], - sorted_r['TFS']]) - - cw.writerow('\n') - - output = make_response(si.getvalue()) - si.close() - output.headers["Content-Disposition"] = "attachment; filename=scoreboard.csv" - output.headers["Content-type"] = "text/csv" - return output - - -@main.route('/scoreboard/brd/', methods=['GET', 'POST']) +@scoreboard.route('/scoreboard/brd/', methods=['GET', 'POST']) def scoreboard_brd(division): - robots = r.get_registry()['ROBOTS'].get_all_robots_division(division) + robots = r.get_registry()['ROBOTS'].get_all_robots_division( + division + ) if not robots: return render_template("not_found.html") @@ -73,8 +35,14 @@ def scoreboard_brd(division): robots = ScoreBoard.add_scoreboard_params(robots) # sort based on name then total score - sorted_robots = sorted(list(robots), key=lambda k: k['name']) - sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) + sorted_robots = sorted( + list(robots), + key=lambda k: k['name'] + ) + sorted_robots = sorted( + list(sorted_robots), + key=lambda k: k['TFS'] + ) return render_template( "scoreboard_brd_gpmp.html", @@ -82,7 +50,8 @@ def scoreboard_brd(division): scoreboard_name=get_division_label(division) ) -@main.route('/scoreboard/gpmp', methods=['GET', 'POST']) + +@scoreboard.route('/scoreboard/gpmp', methods=['GET', 'POST']) def scoreboard_gpmp(): robots = r.get_registry()['ROBOTS'].get_all_robots() @@ -93,8 +62,14 @@ def scoreboard_gpmp(): robots = ScoreBoard.add_scoreboard_params(robots) # sort based on name then total score - sorted_robots = sorted(list(robots), key=lambda k: k['name']) - sorted_robots = sorted(list(sorted_robots), key=lambda k: k['TFS']) + sorted_robots = sorted( + list(robots), + key=lambda k: k['name'] + ) + sorted_robots = sorted( + list(sorted_robots), + key=lambda k: k['TFS'] + ) return render_template( "scoreboard_brd_gpmp.html", @@ -103,7 +78,7 @@ def scoreboard_gpmp(): ) -@main.route('/scoreboard/lisp/', methods=['GET', 'POST']) +@scoreboard.route('/scoreboard/lisp/', methods=['GET', 'POST']) def scoreboard_lisp(level): if not level.isdigit(): return render_template("not_found.html") @@ -119,14 +94,23 @@ def scoreboard_lisp(level): robots = ScoreBoard.add_scoreboard_params(robots) # filter robots - filtered_robots = ScoreBoard.filter_robots_level(robots, int(level)) + filtered_robots = ScoreBoard.filter_robots_level( + robots, + int(level) + ) # key used for sorting score_name = "LS" + level # sort based on name then this level's lowest score - sorted_robots = sorted(list(filtered_robots), key=lambda k: k['name']) - sorted_robots = sorted(list(sorted_robots), key=lambda k: k[score_name]) + sorted_robots = sorted( + list(filtered_robots), + key=lambda k: k['name'] + ) + sorted_robots = sorted( + list(sorted_robots), + key=lambda k: k[score_name] + ) return render_template( "scoreboard_lisp.html", @@ -135,7 +119,7 @@ def scoreboard_lisp(level): score_name=score_name ) -@main.route('/prizes', methods=['GET', 'POST']) +@scoreboard.route('/prizes', methods=['GET', 'POST']) def prize_winners(): robots = r.get_registry()['ROBOTS'].get_all_robots() @@ -156,4 +140,76 @@ def prize_winners(): gpmp_winners=gpmp_winners, lisp_winners=lisp_winners, brd_winners=brd_winners - ) \ No newline at end of file + ) + + +@scoreboard.route('/scoreboardcsv', methods=['GET', 'POST']) +def export_to_csv(): + divisions = ['junior', 'walking', 'high_school', 'senior'] + + all_robots = {} + + for division in divisions: + all_robots[division] = r.get_registry()['ROBOTS'].get_all_robots_division(division) + + si = StringIO.StringIO() + cw = csv.writer(si) + cw.writerow(['Rank', 'Division', 'Name', '# of Successful Runs', + 'Current Level', 'LS1', 'LS2', 'LS3', 'TFS']) + + for div in all_robots: + for robot in all_robots[div]: + runs = r.get_registry()['RUNS'].get_runs(robot['id']) + # get current best scores + best_scores, attempted_levels, total_score, num_successful = ( + ScoreCalculator.get_best_scores(runs) + ) + robot.update(best_scores) + robot['TFS'] = total_score + robot['num_successful'] = num_successful + # calculate lowes scores for each level and TFS, returns tuple + robot['completed'] = attempted_levels + + # sort based on name then total score + sorted_robots = sorted( + list(all_robots[div]), + key=lambda k: k['name'] + ) + sorted_robots = sorted( + list(sorted_robots), + key=lambda k: k['TFS'] + ) + + + for index, sorted_r in enumerate(sorted_robots, start=1): + cw.writerow([ + index, + sorted_r['division'], + sorted_r['name'], + sorted_r['num_successful'], + sorted_r['level'], + sorted_r['LS1'], + sorted_r['LS2'], + sorted_r['LS3'], + sorted_r['TFS'] + ]) + + cw.writerow('\n') + + output = make_response(si.getvalue()) + si.close() + output.headers["Content-Disposition"] = "attachment; filename=scoreboard.csv" + output.headers["Content-type"] = "text/csv" + return output + + +def get_division_label(division): + # page header label + if division == 'junior': + return "Junior Division" + elif division == 'walking': + return "Walking Division" + elif division == 'high_school': + return "High School Division" + else: + return "Senior Division" \ No newline at end of file diff --git a/scoringsystem/scoringapp.py b/scoringsystem/scoringapp.py index cc18988..9026916 100644 --- a/scoringsystem/scoringapp.py +++ b/scoringsystem/scoringapp.py @@ -39,6 +39,8 @@ app.register_blueprint(authentication, url_prefix='/auth') from blueprints.api import api app.register_blueprint(api, url_prefix='/api') +from blueprints.scoreboard import scoreboard +app.register_blueprint(scoreboard) def _get_etag(): """ diff --git a/templates/nav_bar.html b/templates/nav_bar.html index 61b33a6..b04b1d7 100644 --- a/templates/nav_bar.html +++ b/templates/nav_bar.html @@ -3,8 +3,8 @@