mirror of
https://github.com/MoonlitJolteon/frc-stat-predictor.git
synced 2025-11-01 13:40:21 +00:00
Forgot to commit in small portions again
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
config.json
|
||||
*/**/__pycache__
|
||||
.venv
|
||||
__main__
|
||||
44
README.md
Normal file
44
README.md
Normal file
@ -0,0 +1,44 @@
|
||||
## Installation and Setup
|
||||
|
||||
1. **Prerequisites:**
|
||||
* Python 3.8+
|
||||
* [Ollama](https://ollama.com/)
|
||||
|
||||
2. **Clone the Repository:** (If you have not already, skip if you have the file contents)
|
||||
|
||||
3. **Create a Virtual Environment:**
|
||||
|
||||
```
|
||||
python -m venv .venv
|
||||
source venv/bin/activate # On Windows use `venv\Scripts\activate`
|
||||
```
|
||||
|
||||
4. **Install Dependencies:**
|
||||
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
5. **Set up Ollama:**
|
||||
|
||||
* Download and install Ollama from [https://ollama.com/](https://ollama.com/).
|
||||
* Run `ollama pull <model_name>` to download the desired language model (e.g., `ollama pull llama2`).
|
||||
|
||||
## Configuration
|
||||
|
||||
1. **Configure:**
|
||||
* The code uses `config.json.example` as a template. Copy this file to `config.json`.
|
||||
* Edit `config.json` to provide
|
||||
* The Blue Alliance (TBA) API key
|
||||
* Indiana Scouting Alliance (ISA) API key
|
||||
* Preferred Ollama model
|
||||
|
||||
|
||||
## Running the Application
|
||||
|
||||
1. **Run the Main Script:**
|
||||
|
||||
```
|
||||
python main.py
|
||||
```
|
||||
2. [**Open the page in your browser**](http://localhost:5000)
|
||||
101
classes.puml
Normal file
101
classes.puml
Normal file
@ -0,0 +1,101 @@
|
||||
@startuml
|
||||
|
||||
' Data Sources
|
||||
class DataSource {
|
||||
{abstract} +get_status() : tuple[DataSourceStatus, dict]
|
||||
{abstract} +get_team_info(team_number: int)
|
||||
{abstract} +get_event_matches(event_code: str, team_number: int | None = None)
|
||||
{abstract} +get_team_performance_metrics(team_number, event_code: str | None = None)
|
||||
}
|
||||
|
||||
enum DataSourceStatus {
|
||||
CONNECTED
|
||||
UNAUTHENTICATED
|
||||
NOT_FOUND
|
||||
}
|
||||
|
||||
class TheBlueAllianceConnector {
|
||||
+__init__(api_token: str, year: int = datetime.now().year)
|
||||
+get_status() : tuple[DataSourceStatus, dict]
|
||||
+get_team_info(team_number: int) : dict | None
|
||||
+get_event_matches(event_code: str, team_number: int | None = None) : dict | None
|
||||
+get_team_performance_metrics(team_number, event_code: str | None = None) : dict | None
|
||||
-__calculate_auto_performance(performance: dict, match_points: dict, match_record: dict, alliance_data: dict, robot_position: int) : void
|
||||
-__calculate_teleop_performance(performance: dict, match_points: dict, match_record: dict, alliance_data: dict, robot_position: int) : void
|
||||
-__calculate_endgame_performance(performance: dict, match_points: dict, match_record: dict, alliance_data: dict, robot_position: int) : void
|
||||
}
|
||||
|
||||
class IndianaScoutingAllianceConnector {
|
||||
+__init__(api_token: str, year=datetime.now().year)
|
||||
+get_status() : tuple[DataSourceStatus, dict]
|
||||
+get_event_matches(event_code: str, team_number: int | None = None)
|
||||
+get_robot_notes(team_number: int, event_code: str | None = None)
|
||||
+get_team_info(team_number: int)
|
||||
+get_team_performance_metrics(team_number, event_code: str | None = None)
|
||||
-__build_ISA_robot_url(include_flags: str, teams: list = [], event_key: str = "") : str
|
||||
-__build_ISA_human_url(include_flags: str, teams: list = [], event_key: str = "") : str
|
||||
}
|
||||
|
||||
DataSourceStatus <|.. DataSource
|
||||
DataSource <|-- TheBlueAllianceConnector
|
||||
DataSource <|-- IndianaScoutingAllianceConnector
|
||||
|
||||
' LLM Integration
|
||||
class AllianceSelectionAssistant {
|
||||
+__init__(llm: OLLAMAConnector)
|
||||
+select_alliance(teams: list, criteria: dict) : list
|
||||
}
|
||||
|
||||
class OLLAMAConnector {
|
||||
+__init__(model_name: str)
|
||||
+generate_text(prompt: str) : str
|
||||
}
|
||||
|
||||
class MatchPredictor {
|
||||
+__init__(llm: OLLAMAConnector)
|
||||
+predict_outcome(match_data: dict) : str
|
||||
}
|
||||
|
||||
class TeamRatingGenerator {
|
||||
+__init__(llm: OLLAMAConnector)
|
||||
+rate_team(team_data: dict) : str
|
||||
}
|
||||
|
||||
' Utils
|
||||
class ConfigurationManager {
|
||||
+get_config(key: str) : any
|
||||
+set_config(key: str, value: any)
|
||||
}
|
||||
|
||||
class Logger {
|
||||
+__init__(name: str)
|
||||
+log(message: str, level: str)
|
||||
+info(message: str)
|
||||
+warning(message: str)
|
||||
+error(message: str)
|
||||
+debug(message: str)
|
||||
}
|
||||
|
||||
' Main
|
||||
class FRCRatingApp {
|
||||
+__init__()
|
||||
+setup_routes() : void
|
||||
+index()
|
||||
+team_info(team_number: int)
|
||||
+run(debug: bool = True) : void
|
||||
}
|
||||
|
||||
' Relationships
|
||||
AllianceSelectionAssistant --> OLLAMAConnector : Has
|
||||
MatchPredictor --> OLLAMAConnector : Has
|
||||
TeamRatingGenerator --> OLLAMAConnector : Has
|
||||
|
||||
FRCRatingApp --> ConfigurationManager : Uses
|
||||
FRCRatingApp --> Logger : Uses
|
||||
FRCRatingApp --> TheBlueAllianceConnector : Uses
|
||||
FRCRatingApp --> IndianaScoutingAllianceConnector : Uses
|
||||
FRCRatingApp --> AllianceSelectionAssistant : Uses
|
||||
FRCRatingApp --> MatchPredictor : Uses
|
||||
FRCRatingApp --> TeamRatingGenerator : Uses
|
||||
|
||||
@enduml
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"TBA_TOKEN": "Get your read API token here: https://www.thebluealliance.com/account",
|
||||
"USE_ISA_DATA": false,
|
||||
"ISA_TOKEN": "If you are a member of the Indiana Scouting Alliance, put your token here. Reach out to the discord if you don't know how to get it",
|
||||
"OLLAMA_MODEL": "dolphin-mixtral:latest"
|
||||
"ISA_TOKEN": "If you are a member of the Indiana Scouting Alliance,put your token here. Reach out to the discord if you don't know how to get it",
|
||||
"OLLAMA_MODEL": "llama2-uncensored:7b-chat-q2_K"
|
||||
}
|
||||
15
llm_integration/alliance_selection.py
Normal file
15
llm_integration/alliance_selection.py
Normal file
@ -0,0 +1,15 @@
|
||||
import json
|
||||
|
||||
|
||||
class AllianceSelectionAssistant:
|
||||
"""Assists in alliance selection using LLM and team data."""
|
||||
|
||||
def __init__(self, llm_model):
|
||||
self.llm_model = llm_model
|
||||
|
||||
def generate_pick_list(self, teams_data, strategy):
|
||||
teams_data_str = json.dumps(
|
||||
teams_data
|
||||
) # Serialize the list of dictionaries to a string
|
||||
prompt = f"Given the following teams data: {teams_data_str} and pick strategy: {pick_strategy}, generate an alliance pick list."
|
||||
return self.query_ollama(prompt)
|
||||
32
llm_integration/llm_model.py
Normal file
32
llm_integration/llm_model.py
Normal file
@ -0,0 +1,32 @@
|
||||
import json
|
||||
import requests
|
||||
|
||||
|
||||
class OLLAMAConnector:
|
||||
"""
|
||||
Abstract class to interact with a local LLM using Ollama for predictions.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, model_name: str, ollama_base_url: str = "http://localhost:11434"
|
||||
):
|
||||
self.model_name = model_name
|
||||
self.ollama_base_url = ollama_base_url
|
||||
|
||||
def query_ollama(self, prompt: str):
|
||||
"""
|
||||
Helper function to query the Ollama API.
|
||||
"""
|
||||
url = f"{self.ollama_base_url}/api/generate"
|
||||
data = {
|
||||
"prompt": prompt,
|
||||
"model": self.model_name,
|
||||
"stream": False, # Set to False to get the full response at once
|
||||
}
|
||||
try:
|
||||
response = requests.post(url, json=data, stream=False)
|
||||
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
|
||||
return response.json()["response"]
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error querying Ollama: {e}")
|
||||
return None
|
||||
9
llm_integration/match_outcome_prediction.py
Normal file
9
llm_integration/match_outcome_prediction.py
Normal file
@ -0,0 +1,9 @@
|
||||
class MatchPredictor:
|
||||
"""Predicts match outcomes using LLM."""
|
||||
|
||||
def __init__(self, llm_model):
|
||||
self.llm_model = llm_model
|
||||
|
||||
def predict_outcome(self, blue_alliance_data, red_alliance_data):
|
||||
prompt = f"Given blue alliance data: {blue_alliance_data} and red alliance data: {red_alliance_data}, predict the match outcome."
|
||||
return self.query_ollama(prompt)
|
||||
17
llm_integration/team_subjective_rating.py
Normal file
17
llm_integration/team_subjective_rating.py
Normal file
@ -0,0 +1,17 @@
|
||||
class TeamRatingGenerator:
|
||||
"""Generates subjective team ratings using LLM predictions"""
|
||||
|
||||
def __init__(self, llm_model):
|
||||
self.llm_model = llm_model
|
||||
|
||||
def rate_team(
|
||||
self,
|
||||
perfomance_metrics: dict,
|
||||
raw_event_data: dict,
|
||||
isa_data: dict,
|
||||
isa_notes: dict,
|
||||
) -> str | None:
|
||||
"""Rates a team based on available data"""
|
||||
return self.llm_model.query_ollama(
|
||||
prompt=f"The following First Robotics Competition (FRC), data comes from three different sources covering the exact same team and event, please cross reference them to identify possible problems. ```{perfomance_metrics}```, ```{raw_event_data}```, ```{isa_data}``` Do note that while the data source is the same for all three, the presentation of the data doesn't match up perfectly. I also have the following notes about the team in the data: ```{isa_notes}```Once you've done so, please use the data you have collected and referenced to give a comprehensive subjective rating to the team. This is not an interactive conversation, so please give an output that covers everything that you think the user may want in a single message including examples to support the conclusions. THE DATA ONLY CONTAINS ONE TEAM, OUTPUT MUST BE IN HTML FORMAT."
|
||||
)
|
||||
154
main.py
Normal file
154
main.py
Normal file
@ -0,0 +1,154 @@
|
||||
# from flask import Flask, render_template, jsonify
|
||||
|
||||
|
||||
# from data_sources.tba import TheBlueAllianceConnector
|
||||
# from data_sources.isa import IndianaScoutingAllianceConnector
|
||||
# from llm_integration.llm_model import OLLAMAConnector
|
||||
# from llm_integration.team_subjective_rating import TeamRatingGenerator
|
||||
# from llm_integration.match_outcome_prediction import MatchPredictor
|
||||
# from llm_integration.alliance_selection import AllianceSelectionAssistant
|
||||
# from utils.config_manager import ConfigurationManager
|
||||
# from utils.logger import Logger
|
||||
|
||||
|
||||
# app = Flask(__name__)
|
||||
|
||||
# config = ConfigurationManager()
|
||||
# logger = Logger(__name__)
|
||||
|
||||
# # Initialize data sources
|
||||
# tba_api_key = config.get("TBA_TOKEN")
|
||||
# tba_connector = TheBlueAllianceConnector(tba_api_key)
|
||||
|
||||
# isa_api_key = config.get("ISA_TOKEN")
|
||||
# isa_connector = IndianaScoutingAllianceConnector(isa_api_key)
|
||||
|
||||
# # Initialize LLM model
|
||||
# llm_model = config.get("OLLAMA_MODEL")
|
||||
# llm = OLLAMAConnector(llm_model)
|
||||
|
||||
# # Initialize prediction and rating services
|
||||
# team_rater = TeamRatingGenerator(llm)
|
||||
# match_predictor = MatchPredictor(llm)
|
||||
# alliance_assistant = AllianceSelectionAssistant(llm)
|
||||
|
||||
|
||||
# @app.route("/")
|
||||
# def index():
|
||||
# return render_template("index.html")
|
||||
|
||||
|
||||
# @app.route("/team/<int:team_number>")
|
||||
# def team_info(team_number):
|
||||
# event_code = "2025incmp"
|
||||
# tba_team_performance_metrics = tba_connector.get_team_performance_metrics(
|
||||
# team_number, event_code
|
||||
# )
|
||||
# tba_raw_event_data = tba_connector.get_event_matches(event_code, team_number)
|
||||
# isa_data = isa_connector.get_event_matches(event_code, team_number)
|
||||
# isa_notes = isa_connector.get_robot_notes(team_number, event_code)
|
||||
|
||||
# if tba_team_performance_metrics:
|
||||
# # logger.info(f"Team {team_number} Metrics: {tba_team_performance_metrics}")
|
||||
|
||||
# # Generate subjective team rating
|
||||
# logger.info(f"Generating Team rating...")
|
||||
# team_rating = team_rater.rate_team(
|
||||
# tba_team_performance_metrics, tba_raw_event_data, isa_data, isa_notes
|
||||
# )
|
||||
# output = f"Subjective Team Rating: {team_rating}"
|
||||
# logger.info(output)
|
||||
# return output
|
||||
|
||||
# else:
|
||||
# output = (
|
||||
# f"Could not retrieve metrics for team {team_number} at event {event_code}"
|
||||
# )
|
||||
# logger.info(output)
|
||||
# return output
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
|
||||
|
||||
from flask import Flask, render_template, jsonify
|
||||
|
||||
|
||||
from data_sources.tba import TheBlueAllianceConnector
|
||||
from data_sources.isa import IndianaScoutingAllianceConnector
|
||||
from llm_integration.llm_model import OLLAMAConnector
|
||||
from llm_integration.team_subjective_rating import TeamRatingGenerator
|
||||
from llm_integration.match_outcome_prediction import MatchPredictor
|
||||
from llm_integration.alliance_selection import AllianceSelectionAssistant
|
||||
from utils.config_manager import ConfigurationManager
|
||||
from utils.logger import Logger
|
||||
|
||||
|
||||
class FrcRatingApp:
|
||||
def __init__(self):
|
||||
self.app = Flask(__name__)
|
||||
self.config = ConfigurationManager()
|
||||
self.logger = Logger(__name__)
|
||||
|
||||
# Initialize data sources
|
||||
tba_api_key = self.config.get("TBA_TOKEN")
|
||||
self.tba_connector = TheBlueAllianceConnector(tba_api_key)
|
||||
|
||||
isa_api_key = self.config.get("ISA_TOKEN")
|
||||
self.isa_connector = IndianaScoutingAllianceConnector(isa_api_key)
|
||||
|
||||
# Initialize LLM model
|
||||
llm_model = self.config.get("OLLAMA_MODEL")
|
||||
self.llm = OLLAMAConnector(llm_model)
|
||||
|
||||
# Initialize prediction and rating services
|
||||
self.team_rater = TeamRatingGenerator(self.llm)
|
||||
self.match_predictor = MatchPredictor(self.llm)
|
||||
self.alliance_assistant = AllianceSelectionAssistant(self.llm)
|
||||
|
||||
self.setup_routes()
|
||||
|
||||
def setup_routes(self):
|
||||
self.app.add_url_rule("/", "index", self.index)
|
||||
self.app.add_url_rule("/team/<int:team_number>", "team_info", self.team_info)
|
||||
|
||||
def index(self):
|
||||
return render_template("index.html")
|
||||
|
||||
def team_info(self, team_number):
|
||||
event_code = "2025incmp"
|
||||
tba_team_performance_metrics = self.tba_connector.get_team_performance_metrics(
|
||||
team_number, event_code
|
||||
)
|
||||
tba_raw_event_data = self.tba_connector.get_event_matches(
|
||||
event_code, team_number
|
||||
)
|
||||
isa_data = self.isa_connector.get_event_matches(event_code, team_number)
|
||||
isa_notes = self.isa_connector.get_robot_notes(team_number, event_code)
|
||||
|
||||
if tba_team_performance_metrics:
|
||||
# Generate subjective team rating
|
||||
self.logger.info(f"Generating Team rating...")
|
||||
team_rating = self.team_rater.rate_team(
|
||||
tba_team_performance_metrics,
|
||||
tba_raw_event_data,
|
||||
isa_data,
|
||||
isa_notes,
|
||||
)
|
||||
output = f"Subjective Team Rating: {team_rating}"
|
||||
self.logger.info(output)
|
||||
return output
|
||||
|
||||
else:
|
||||
output = f"Could not retrieve metrics for team {team_number} at event {event_code}"
|
||||
self.logger.info(output)
|
||||
return output
|
||||
|
||||
def run(self, debug=True):
|
||||
self.app.run(debug=debug)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
frc_rating_app = FrcRatingApp()
|
||||
frc_rating_app.run()
|
||||
12
requirements.txt
Normal file
12
requirements.txt
Normal file
@ -0,0 +1,12 @@
|
||||
blinker==1.9.0
|
||||
certifi==2025.1.31
|
||||
charset-normalizer==3.4.1
|
||||
click==8.1.8
|
||||
Flask==3.1.0
|
||||
idna==3.10
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.2
|
||||
requests==2.32.3
|
||||
urllib3==2.4.0
|
||||
Werkzeug==3.1.3
|
||||
290
templates/index.html
Normal file
290
templates/index.html
Normal file
@ -0,0 +1,290 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Team Info Lookup</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap 4 CDN for quick, nice styling -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
||||
<style>
|
||||
body {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
min-width: 50%;
|
||||
max-width: 90%;
|
||||
margin: 60px auto;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.07);
|
||||
padding: 32px 24px 24px 24px;
|
||||
}
|
||||
|
||||
.result-box {
|
||||
margin-top: 32px;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
background: #e9ecef;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">Team Lookup</a>
|
||||
</nav>
|
||||
<div class="main-container">
|
||||
<h2 class="mb-4 text-center">Lookup FRC Team Info</h2>
|
||||
<form id="team-form">
|
||||
<div class="form-group">
|
||||
<label for="team-number">Enter Team Number</label>
|
||||
<input type="number" class="form-control" id="team-number" name="team_number" min="1" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">Get Team Info</button>
|
||||
</form>
|
||||
<h2>The following result is generated using an LLM, please note that anything said in the following block of
|
||||
text does not reflect my own personal views and I do not vouch for it's accuracy.</h2>
|
||||
<div id="result" class="result-box mt-4" style="display:none;"></div>
|
||||
|
||||
<div class="team-list-section">
|
||||
<h4 class="mb-3">I have data about the following teams:</h4>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped team-table">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col">Team</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Location</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>45</td>
|
||||
<td>TechnoKats Robotics Team</td>
|
||||
<td>Kokomo, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>71</td>
|
||||
<td>Team Hammond</td>
|
||||
<td>Hammond, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>135</td>
|
||||
<td>Penn Robotics Black Knights</td>
|
||||
<td>Mishawaka, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>234</td>
|
||||
<td>Cyber Blue</td>
|
||||
<td>Indianapolis, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>292</td>
|
||||
<td>PantherTech</td>
|
||||
<td>Russiaville, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>328</td>
|
||||
<td>Penn Robotics Golden Rooks</td>
|
||||
<td>Mishawaka, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>447</td>
|
||||
<td>Team Roboto</td>
|
||||
<td>Anderson, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>461</td>
|
||||
<td>Westside Boiler Invasion</td>
|
||||
<td>West Lafayette, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>829</td>
|
||||
<td>The Digital Goats</td>
|
||||
<td>Indianapolis, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>868</td>
|
||||
<td>TechHOUNDS</td>
|
||||
<td>Carmel, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1018</td>
|
||||
<td>Pike RoboDevils</td>
|
||||
<td>Indianapolis, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1024</td>
|
||||
<td>Kil-A-Bytes</td>
|
||||
<td>Indianapolis, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1501</td>
|
||||
<td>Team THRUST</td>
|
||||
<td>Huntington, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1741</td>
|
||||
<td>Red Alert</td>
|
||||
<td>Greenwood, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1747</td>
|
||||
<td>Harrison Boiler Robotics</td>
|
||||
<td>West Lafayette, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2171</td>
|
||||
<td>RoboDogs</td>
|
||||
<td>Crown Point, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2197</td>
|
||||
<td>Las Pumas</td>
|
||||
<td>New Carlisle, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3176</td>
|
||||
<td>Purple Precision</td>
|
||||
<td>Brownsburg, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3487</td>
|
||||
<td>Red Pride Robotics</td>
|
||||
<td>Plainfield, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3494</td>
|
||||
<td>The Quadrangles</td>
|
||||
<td>Bloomington, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3940</td>
|
||||
<td>CyberTooth</td>
|
||||
<td>Kokomo, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4272</td>
|
||||
<td>Maverick Robotics</td>
|
||||
<td>Lafayette, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4485</td>
|
||||
<td>Tribe Tech Robotics</td>
|
||||
<td>Danville, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4926</td>
|
||||
<td>GalacTech</td>
|
||||
<td>Columbus, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5010</td>
|
||||
<td>Tiger Dynasty</td>
|
||||
<td>Fishers, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5188</td>
|
||||
<td>Area 5188: Classified Robotics</td>
|
||||
<td>Terre Haute, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5402</td>
|
||||
<td>Wreckless Robotics</td>
|
||||
<td>Loganpsport/Walton, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5484</td>
|
||||
<td>Career Academy Robotics - Wolf Pack</td>
|
||||
<td>South Bend, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6721</td>
|
||||
<td>Tindley Trailblazers</td>
|
||||
<td>Indianapolis, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6956</td>
|
||||
<td>SHAM-ROCK-BOTICS ☘</td>
|
||||
<td>Westfield, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7454</td>
|
||||
<td>Huskies on Hogs</td>
|
||||
<td>Evansville, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7457</td>
|
||||
<td>suPURDUEper Robotics</td>
|
||||
<td>Indianapolis, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7617</td>
|
||||
<td>RoboBlazers</td>
|
||||
<td>Carmel, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7657</td>
|
||||
<td>ThunderBots</td>
|
||||
<td>Evansville, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8103</td>
|
||||
<td>Knight Robotics</td>
|
||||
<td>Kendallville, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8430</td>
|
||||
<td>The Hatch Batch</td>
|
||||
<td>Washington, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10021</td>
|
||||
<td>Guerin Catholic Golden Gears</td>
|
||||
<td>Noblesville, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10332</td>
|
||||
<td>Carroll Charger Robotics</td>
|
||||
<td>Fort Wayne, Indiana, USA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10492</td>
|
||||
<td>Bosse BYTEForce</td>
|
||||
<td>Evansville, Indiana, USA</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('team-form').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
const teamNumber = document.getElementById('team-number').value;
|
||||
const resultDiv = document.getElementById('result');
|
||||
resultDiv.style.display = 'block';
|
||||
resultDiv.innerHTML = '<div class="text-center text-muted">Loading...</div>';
|
||||
fetch(`/team/${teamNumber}`, { signal: AbortSignal.timeout(50000000000) })
|
||||
.then(response => response.text())
|
||||
.then(rating => {
|
||||
resultDiv.innerHTML = rating;
|
||||
})
|
||||
.catch(err => {
|
||||
resultDiv.innerHTML = `<div class="alert alert-danger">Failed to fetch team info.</div>`;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user