Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,22 @@ def create_app():
# Initialize collections
if "users" not in db.list_collection_names():
db.create_collection("users")
logger.info("Created 'users' collection in MongoDB.")
if "teams" not in db.list_collection_names():
db.create_collection("teams")
logger.info("Created 'teams' collection in MongoDB.")
if "team_data" not in db.list_collection_names():
db.create_collection("team_data")
logger.info("Created 'team_data' collection in MongoDB.")
if "pit_scouting" not in db.list_collection_names():
db.create_collection("pit_scouting")
logger.info("Created 'pit_scouting' collection in MongoDB.")
if "assignments" not in db.list_collection_names():
db.create_collection("assignments")
logger.info("Created 'assignments' collection in MongoDB.")
if "assignment_subscriptions" not in db.list_collection_names():
db.create_collection("assignment_subscriptions")
logger.info("Created 'assignment_subscriptions' collection in MongoDB.")

login_manager.init_app(app)
login_manager.login_view = "auth.login"
Expand Down
1 change: 0 additions & 1 deletion app/auth/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import os
from functools import wraps
from urllib.parse import urljoin, urlparse
import hashlib

from bson import ObjectId
from flask import (Blueprint, current_app, flash, jsonify, redirect,
Expand Down
4 changes: 4 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ def __init__(self, data):
self.teleop_shift_3_fuel = data.get('teleop_shift_3_fuel', 0)
self.teleop_shift_4_fuel = data.get('teleop_shift_4_fuel', 0)
self.endgame_fuel = data.get('endgame_fuel', 0)
self.ferried_fuel = data.get('ferried_fuel', 0)

# Climb
self.auto_climb = data.get('auto_climb', False)
self.climb_level = data.get('climb_level', 0) # 0=None, 1-3
self.climb_success = data.get('climb_success', False)

Expand Down Expand Up @@ -128,6 +130,8 @@ def to_dict(self):
'teleop_shift_3_fuel': self.teleop_shift_3_fuel,
'teleop_shift_4_fuel': self.teleop_shift_4_fuel,
'endgame_fuel': self.endgame_fuel,
'ferried_fuel': self.ferried_fuel,
'auto_climb': self.auto_climb,
'climb_level': self.climb_level,

'climb_success': self.climb_success,
Expand Down
131 changes: 108 additions & 23 deletions app/scout/TBA.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get_team(self, team_key):
logger.error(f"Error fetching team from TBA: {e}")
return None

@lru_cache(maxsize=100)
@lru_cache(maxsize=5)
def get_event_matches(self, event_key):
"""Get matches for an event and format them by match number"""
try:
Expand Down Expand Up @@ -76,7 +76,7 @@ def get_event_matches(self, event_key):
logger.error(f"Error fetching event matches from TBA: {e}")
return None

@lru_cache(maxsize=100)
@lru_cache(maxsize=20)
def get_current_events(self, year):
"""Get all events for the specified year"""
try:
Expand Down Expand Up @@ -110,7 +110,7 @@ def get_current_events(self, year):
logger.error(f"Error fetching events from TBA: {e}")
return None

@lru_cache(maxsize=100)
@lru_cache(maxsize=20)
def get_team_status_at_event(self, team_key, event_key):
"""Get team status and ranking at a specific event"""
try:
Expand All @@ -124,7 +124,7 @@ def get_team_status_at_event(self, team_key, event_key):
logger.error(f"Error fetching team status from TBA: {e}")
return None

@lru_cache(maxsize=100)
@lru_cache(maxsize=20)
def get_team_matches_at_event(self, team_key, event_key):
"""Get a team's matches at a specific event with previous and upcoming separation"""
try:
Expand All @@ -133,22 +133,22 @@ def get_team_matches_at_event(self, team_key, event_key):
headers=self.headers,
timeout=self.timeout
)

if response.status_code != 200:
return None

matches = response.json()
current_time = datetime.now().timestamp()

previous_matches = []
upcoming_matches = []

for match in matches:
# Get match details
comp_level = match.get('comp_level', 'qm')
match_number = match.get('match_number')
set_number = match.get('set_number')

# Format match name
if comp_level == 'qm':
match_name = f"Qualification {match_number}"
Expand All @@ -158,14 +158,15 @@ def get_team_matches_at_event(self, team_key, event_key):
match_name = f"Final {match_number}"
else:
match_name = f"{comp_level.upper()} {match_number}"

# Determine alliance
alliance = None
for color in ['red', 'blue']:
if team_key in match['alliances'][color]['team_keys']:
alliance = color
break


alliance = next(
(
color
for color in ['red', 'blue']
if team_key in match['alliances'][color]['team_keys']
),
None,
)
match_info = {
'match_name': match_name,
'time': match.get('predicted_time') or match.get('time', 0) or 0,
Expand All @@ -175,28 +176,28 @@ def get_team_matches_at_event(self, team_key, event_key):
'blue': match['alliances']['blue']['score']
}
}

# Sort into previous or upcoming
actual_time = match.get('actual_time')
if (actual_time is not None and actual_time > 0) or match_info['time'] < current_time:
previous_matches.append(match_info)
else:
upcoming_matches.append(match_info)

# Sort matches by time
previous_matches.sort(key=lambda m: m['time'])
upcoming_matches.sort(key=lambda m: m['time'])

return {
'previous': previous_matches,
'upcoming': upcoming_matches
}

except Exception as e:
logger.error(f"Error fetching team matches from TBA: {e}")
return None

@lru_cache(maxsize=100)
@lru_cache(maxsize=20)
def get_team_events(self, team_key, year=None):
"""Get all events a team is participating in for the given year"""
if year is None:
Expand Down Expand Up @@ -240,4 +241,88 @@ def get_most_recent_active_event(self, team_key):
return event

# If no current events, get the most recent event (events are already sorted by date, most recent first)
return events[0]
return events[0]

@lru_cache(maxsize=5)
def get_event_teams(self, event_key):
"""
Retrieve a list of teams attending a specific event.

Args:
event_key (str): The unique key identifying the event.

Returns:
list[dict] or None: A list of team objects, or None if the request fails.
"""
try:
response = requests.get(
f"{self.base_url}/event/{event_key}/teams",
headers=self.headers,
timeout=self.timeout
)

if response.status_code != 200:
return None

return response.json()
except Exception as e:
logger.error(f"Error fetching event teams from TBA: {e}")
return None

@lru_cache(maxsize=5)
def get_event_rankings(self, event_key):
"""
Retrieve the team rankings for a given event, including ranking points and match records.

Args:
event_key (str): The unique key identifying the event (e.g., '2023miket').

Returns:
list[dict] or None: A list of dictionaries containing team ranking information, or None if the request fails.
Each dictionary contains:
- 'rank' (int): The team's rank at the event.
- 'team_key' (str): The team's TBA key (e.g., 'frc254').
- 'team_number' (int): The team's number.
- 'ranking_points' (float|int): The team's ranking points.
- 'record' (dict): The team's win/loss/tie record.
- 'matches_played' (int): The number of matches played.

Exceptions:
Logs and returns None if an exception occurs during the request or response parsing.
"""
try:
response = requests.get(
f"{self.base_url}/event/{event_key}/rankings",
headers=self.headers,
timeout=self.timeout
)

if response.status_code != 200:
return None

data = response.json()
if not data:
return []

rankings = data.get('rankings', [])

# Format rankings with team info and ranking points
formatted_rankings = []
for rank in rankings:
team_key = rank.get('team_key', '')
team_number = team_key.replace('frc', '') if team_key.startswith('frc') else team_key

formatted_rankings.append({
'rank': rank.get('rank'),
'team_key': team_key,
'team_number': int(team_number) if team_number.isdigit() else 0,
'ranking_points': rank.get('sort_orders', [0])[0] if rank.get('sort_orders') else 0,
'record': rank.get('record', {}),
'matches_played': rank.get('matches_played', 0)
})

return formatted_rankings

except Exception as e:
logger.error(f"Error fetching event rankings from TBA: {e}")
return None
Loading
Loading