A dynamic, production-ready scouting system for FIRST Robotics Competition (FRC) built with Django and modern JavaScript.
- Dynamic Configuration System: Change game metrics without code changes via
game_config.json - Hot-Reload: Config changes apply instantly without server restart
- Google OAuth Authentication: Secure login with team Google accounts
- Admin Panel: User management, scout leaderboard, and match data editor
- QR Code Scanner: Fast data collection from scouting sheets
- Strategy Dashboard: Real-time match analysis and team rankings
- Pit Scouting: Dynamic forms generated from configuration
- Legacy Key Support: Rename metrics mid-season without losing historical data
- Backend: Django 5.1, PostgreSQL
- Frontend: Vanilla JavaScript, Webpack
- Authentication: Google OAuth 2.0
- Hosting: Self-hosted Ubuntu VM
- Storage: Cloudinary (images), PostgreSQL (data)
- Python 3.10+
- Node.js 18+ and npm
- PostgreSQL database
- Google Cloud OAuth credentials
- Cloudinary account (for image uploads)
- The Blue Alliance API key
git clone https://github.com/your-org/scouting-backend-2025.git
cd scouting-backend-2025Copy the example environment file and fill in your credentials:
cp env.txt .envEdit .env with your credentials:
DATABASE_URL=postgresql://user:password@host:port/database
X_TBA_AUTH_KEY=your_tba_api_key
CLOUD_NAME=your_cloudinary_name
CLOUD_API_KEY=your_cloudinary_key
CLOUD_API_SECRET=your_cloudinary_secret
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
SECRET_KEY=your_secret_key_here
DEBUG=TrueGenerate a secure SECRET_KEY:
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'# Install Python dependencies
pip install -r requirements.txt
# Install Node.js dependencies
npm installnpm run build# Run migrations
python manage.py migrate
# Create first admin user (via Django shell)
python manage.py shellIn the shell:
- Note of course that it is not strictly bound to Team2073 and any emails can be used
from authenticate.models import AuthorizedUser
AuthorizedUser.objects.create(email='your-email@team2073.com')
exit()python manage.py collectstatic --no-inputpython manage.py runserverVisit http://localhost:8000 and log in with Google OAuth.
This system is designed to run on a self-hosted Ubuntu server.
Initial Setup:
- Install system dependencies:
sudo apt update
sudo apt install python3 python3-pip python3-venv nodejs npm postgresql nginx- Clone and setup:
git clone https://github.com/your-org/scouting-backend-2025.git
cd scouting-backend-2025
cp env.txt .env
# Edit .env with your credentials- Run build script:
chmod +x build.sh
./build.sh- Create systemd service for auto-start:
sudo nano /etc/systemd/system/scouting.serviceAdd:
[Unit]
Description=FRC Scouting System
After=network.target postgresql.service
[Service]
User=your-username
WorkingDirectory=/path/to/scouting-backend-2025
Environment="PATH=/usr/bin:/usr/local/bin"
ExecStart=/usr/bin/python3 manage.py runserver 0.0.0.0:8000
Restart=always
[Install]
WantedBy=multi-user.target- Enable and start service:
sudo systemctl enable scouting
sudo systemctl start scouting
sudo systemctl status scoutingUsing Gunicorn (Recommended for Production):
- Install gunicorn:
pip install gunicorn- Update systemd service ExecStart:
ExecStart=/usr/local/bin/gunicorn scouting_backend.wsgi:application --bind 0.0.0.0:8000 --workers 3- Setup Nginx reverse proxy:
sudo nano /etc/nginx/sites-available/scoutingAdd:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /path/to/scouting-backend-2025/staticfiles/;
}
}- Enable site:
sudo ln -s /etc/nginx/sites-available/scouting /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginxUpdating the Application:
cd /path/to/scouting-backend-2025
git pull
./build.sh
sudo systemctl restart scoutingSet these in your .env file:
DATABASE_URL- PostgreSQL connection stringSECRET_KEY- Django secret key (keep secure!)DEBUG- Set toFalsein productionGOOGLE_CLIENT_ID- OAuth client IDGOOGLE_CLIENT_SECRET- OAuth client secretX_TBA_AUTH_KEY- The Blue Alliance API keyCLOUD_NAME,CLOUD_API_KEY,CLOUD_API_SECRET- Cloudinary credentials
Edit game_config.json to customize for each season:
{
"version": "2025_reefscape",
"home_team": 2073,
"year": 2025,
"metrics": [
{
"key": "auto_leave",
"type": "number",
"category": "auto",
"aggregation": "avg",
"display_name": "Auto Leave",
"min": 0,
"max": 1
}
],
"pit_questions": [...]
}Changes apply immediately without restart (hot-reload).
To rename a metric without losing data, add legacy_keys:
{
"key": "endgame_climb",
"legacy_keys": ["climb", "old_climb"],
"type": "number",
...
}Access at /admin-panel/ after logging in.
Features:
- Add/remove authorized users
- View scout leaderboard by competition
- Edit match data (fix typos, correct values)
scouting-backend-2025/
├── api/ # The Blue Alliance API integration
├── authenticate/ # OAuth login, user management, admin panel
├── frontend/ # JavaScript source files (Webpack)
├── scanner/ # QR code scanning views
├── scouting_backend/ # Django project settings
├── strategy/ # Rankings, dashboard, picklist
├── teams/ # Team pages, pit scouting
├── templates/ # Base HTML templates
├── game_config.json # Dynamic game configuration
├── utils.py # Config loader with hot-reload
├── constants.py # Hardcoded admin emails
├── build.sh # Production build script
└── requirements.txt # Python dependencies
# Watch mode (auto-rebuild on changes)
npm run watch
# Development server with hot reload
npm run dev
# Lint JavaScript
npm run lint
# Format code
npm run format# Create migration after model changes
python manage.py makemigrations
# Apply migrations
python manage.py migrateVia admin panel at /admin-panel/ or Django shell:
from authenticate.models import AuthorizedUser
AuthorizedUser.objects.create(email='user@team2073.com')If using Supabase or cloud PostgreSQL without IPv4:
- Use connection pooler URL
- Or run migrations locally with local PostgreSQL
Ensure your Google Cloud Console has these redirect URIs:
http://localhost:8000/auth/oauth2callback/(development)https://your-domain.com/auth/oauth2callback/(production)
python manage.py collectstatic --no-inputEnsure STATIC_ROOT is set in settings.py.
- Configuration Guide - Complete guide to creating game configurations
- QR Scanner Testing - Testing guide for QR code scanner
- Phase 4 Complete - Final implementation summary
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see LICENSE file for details
Developed by FRC Team 2073 EagleForce
For issues or questions:
- Open a GitHub issue
- Contact team leadership
- ChatGPT