A website that records completely customizable user input!

This was a project I did during the FRC (First Robotics Composition) season of my junior year of high school (2019 season - FRC Destination: Deep Space).

In FRC, it was important to my team that on competition days, we scout other teams–that is to assign some of our team members to watch a specific robotics team in a match and log all the point scoring actions they make. This data is later used to help choose teams that would work well with our own later on in the competition.

Normally, we would make a Google Form to log this information with drop-down menus and multiple choice questions, but I thought I could make the interface more interactive and fun, so I did! I wrote code for the webpage during free time in class and during robotics meetings. On my webpage, rather than choosing from drop-down menus, users interact with representations of the field elements in the game. My code then tallies up this information and appends it to a Google Sheet just like a Google Form would.

I was able to get the webpage to an acceptable level, and my team ended up using it instead of a Google Form. It was met with really positive reviews, with people saying it was easy to use and some saying that they loved it–It was liked well enough that mentors wanted to make our own scouting webpage again the next year and assigned it as a task for our programming sub-team.

This project uses HTML and CSS to create representations of the game’s field elements for the form. Javascript is used to handle the game logic of the webpage (to keep everything in line with the rules of the game), read the input, and pass on the data to the backend. Python (with the Flask framework) is used on the backend to post the data to a Google Sheets using a the Google Sheets API to manipulate a service account (a kind of dummy account manipulated by code to edit and write to Google Files).

Code Snippets:

JS:
Game Logic:
// when a cargo element (of id number idN) is clicked on (onClick event)
function cargo(idN) {
    // there are 2 phases of the game, first the sandstorm, second is the main game
    if (mainGame) {
        // 2 is the maximum number of cargo that can be placed into a rocket's cargo port
        if (cargoes[idN] === 2) {
            // if you go past the maximum value, set it back to the minimum
            // (which is the number of cargo in the port at the end of the sandstorm phase)
            cargoes[idN] = ss_cargoes[idN];
        } else {
            cargoes[idN] += 1;
        }
        refreshCargoes();  // shows the change visually by manipulating HTML and CSS
    } else {
        // if you're in the sandstorm phase, and you reach the maximum, set the values back to 0
        if (ss_cargoes[idN] === 2) {
            ss_cargoes[idN] = 0;
            cargoes[idN] = 0;
        } else {
            ss_cargoes[idN] += 1;
            cargoes[idN] = ss_cargoes[idN];
        }
        refreshCargoes();
    }
}
Collecting and posting data
// Getting all the data (individual variables defined earlier in the code)
// and putting them in order in an array
var input = [document.getElementById("tn").value, document.getElementById("rn").value, startingPos,
                sandstorm, ss_cycleTime, main, endingPos, cycleTime, endClimb, genNotes];
// Posting the data to Python-Flask
var req = new XMLHttpRequest();
var data = JSON.stringify({'values': input});
req.open("POST", "/v/", true);
req.setRequestHeader("Content-Type", "application/json");
req.send(data);
Python:
Intaking data (from JS) and appending it to a sheets file
from flask import Flask, render_template, request
from googleapiclient.discovery import build
from google.oauth2 import service_account

SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
# Append to the Sheets file
# (SPREADSHEET_ID, RANGE_NAME defined earlier in the program)
@app.route('/v/', methods = ['POST'])
def intake():
    SERVICE_ACCOUNT_FILE = 'service_account.json'
    creds = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

    service = build('sheets', 'v4', credentials=creds)
    sheet = service.spreadsheets()

    body = request.json

    result = sheet.values().append(
        spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME,
        valueInputOption='USER_ENTERED', body=body).execute()
    return ''

Comparing UI to Game Elements:

UI Representation Actual Game Element
UI Cargo Ship (top-down view) Actual Cargo Ship

The greyed-out circles in the UI represent the “cargo ports,” which are the containers to drop the orange balls (cargo) into. The greyed-out rectangles represent the circular holes where you must place the yellow disks (called hatch panels).