Skip to content

Latest commit

Β 

History

349 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ℹ️ About

RacingTracker is a free and open source web application that allows you to manage races in the world of motor racing with an innovative scoring system.

Why RacingTracker?

In motor racing, the predominant scoring system is based exclusively on the final position of the participants. This traditional approach assigns fixed scores to each position without considering key factors such as relative performance, prior expectations or the particular conditions of each driver or team. As a result, the current system has two main shortcomings:

  1. Lack of recognition of relative performance: A driver or team with limited resources who achieves a modest result could be performing at an exceptional level based on their capabilities, but the system does not reflect this merit.

  2. Little appreciation for exceeding expectations: A pilot from a leading team who finishes in a high position simply meets expectations, without receiving additional recognition if his performance exceeds initial projections.

These limitations raise a fundamental question: is a scoring system that only considers final position without assessing context and relative performance really equitable? The answer, from my perspective, is no. I therefore propose the development of a fairer and more dynamic ranking system, capable of more accurately reflecting the true merit of each driver and team.

πŸ“ Project structure

πŸ“ root/
|-- app/                         # Application core
|   |-- Actions/                 # Business actions (e.g. race participation sync)
|   |-- Contracts/               # Interfaces (rating calculation strategies)
|   |-- Events/                  # Domain events
|   |-- Http/
|   |   |-- Controllers          # Route controllers
|   |   |-- Middleware           # HTTP middleware
|   |   \-- Requests             # Form request validation
|   |-- Listeners/               # Event listeners (rating calculations)
|   |-- Models                   # Eloquent models
|   |-- Providers                # Service providers
|   \-- Services                 # Domain services (ranking, stats, presentation)
|
|-- database/                    # Database files
|   |-- factories                # Model factories for seeding
|   |-- migrations               # Database schema migrations
|   |-- seeders                  # Database seeders
|   \-- database.sqlite          # SQLite database
|
|-- documents/                   # Project documentation
|   |-- resources                # Images, diagrams, etc.
|   \-- memoria.docx             # Project document
|
|-- public/                      # Publicly accessible files
|
|-- resources/                   # Frontend resources
|   |-- css                      # CSS styles
|   |-- js/                      # TypeScript frontend code
|   |   |-- components           # Reusable components
|   |   |-- hooks                # Custom React hooks
|   |   |-- layouts              # Layout components
|   |   |-- lib                  # Utility functions
|   |   |-- pages                # Page-level components
|   |   \-- types                # TypeScript types/interfaces
|   \-- views                    # Blade views
|
\-- routes/                      # Route definitions

compose.yaml                     # Docker Compose (Laravel Sail + Node/pnpm)
pint.json                        # PHP code style configuration
phpstan.neon                     # Static analysis configuration (Larastan)

πŸ“Š Points calculation

Variables

  • Ξ²: performance deviation (ΞΌ/6.0)
  • ΞΌ: current driver points (25.0)
  • Ο„: dynamic factor of change (ΞΌ/300.0)
  • Οƒ: current driver uncertainty (ΞΌ/3.0)
  • A: expected average position
  • F: final position in the race
  • P: number of race participants

Operations

Performance deviation:

$Ξ²=ΞΌ/6.0$

Dynamic factor of change:

$Ο„=ΞΌ/300.0$

Combined variance of performance:

$C=Οƒ^2+Ξ²^2$

Updating factor:

$K=Οƒ^2/C$

Error between expected and actual position:

$E=A-F$

Updating ΞΌ:

$ΞΌ_{new}=ΞΌ+KE$

Impact of error in Οƒ:

$I=∣E∣/P$

Proposed change in Οƒ:

$Οƒ_{change}=Ο„(0.5βˆ’I)$

Οƒ maximum change limit (15%):

$M_{change}=Οƒ(0.15)$

Application of limits to Οƒ change if $Οƒ_{change}>0$:

$Οƒ_{change}=min⁑(Οƒ_{change}, M_{change})$

Application of limits to Οƒ change if $Οƒ_{change}<0$:

$Οƒ_{change}=max⁑(Οƒ_{change}, -M_{change})$

Οƒ update (with minimum 0.001):

$Οƒ_{new}=max⁑(0.001, Οƒ+Οƒ_{change})$

πŸš€ Deployment locally

Prerequisites

Installation

  1. Clone the repository:
git clone https://github.com/Qv1ko/RacingTracker.git
  1. Change the working directory:
cd RacingTracker
  1. Install the dependencies:
composer install && pnpm install
  1. Copy the .env.example file to .env:
cp .env.example .env
  1. Generate the application key:
php artisan key:generate
  1. Run the migrations:
php artisan migrate
  1. Run the seeders:
php artisan db:seed
  1. Run the server:
pnpm run build && php artisan serve
  1. Open http://localhost:8000 in your browser.

🐳 Deployment with Docker

Prerequisites

Usage

  1. Start the containers (Laravel PHP 8.5 via Sail + Node/pnpm for assets):
./vendor/bin/sail up -d
  1. Install the PHP dependencies inside the container:
./vendor/bin/sail composer install
  1. Copy the .env.example file to .env, then generate the application key and run the migrations:
cp .env.example .env
./vendor/bin/sail artisan key:generate
./vendor/bin/sail artisan migrate --graceful
  1. The Node container automatically installs the frontend dependencies with pnpm and starts the Vite dev server on port 5173. To build the assets for production instead:
./vendor/bin/sail exec node pnpm run build
  1. Open http://localhost in your browser.

Useful commands: ./vendor/bin/sail down (stop), ./vendor/bin/sail pest (run tests), ./vendor/bin/sail composer lint / analyse (code quality).

🧹 Code quality

The project enforces code style and static analysis through Composer scripts:

# Check code style (Pint + oxfmt) without fixing
composer lint

# Fix code style automatically (Pint + oxfmt)
composer lint:fix

# Run static analysis (Larastan, level 5)
composer analyse

Additional frontend-only checks are available via pnpm:

pnpm run types   # TypeScript type checking
pnpm run lint    # oxlint
pnpm run fmt     # oxfmt formatter

πŸ† Rating algorithms

The application calculates its own driver and team ratings every time a race result is created or updated. The active algorithm is selected in your .env file:

RANKING_ALGORITHM=trueskill   # available: trueskill | classic | position
Algorithm Description
trueskill (default) TrueSkill-inspired rating system (see Points calculation). Each driver has points ΞΌ and uncertainty Οƒ that evolve with every result.
classic Fixed F1 points table by final position (25, 18, 15...). Configurable in config/ranking.php under classic_points.
position Rating based purely on the final position of each participant.

The mapping between algorithm names and classes lives in config/ranking.php under algorithms.

Changing the algorithm

  1. Set RANKING_ALGORITHM in your .env to one of the available options.
  2. Recalculate all stored ratings so existing races use the new system:
php artisan ranking:recalculate

You can also recalculate a single season or only races after a given date:

php artisan ranking:recalculate --season=2025
php artisan ranking:recalculate --from=2025-01-01

Adding a custom algorithm

  1. Create a class implementing the App\Contracts\RatingCalculation contract:
<?php

namespace App\Listeners\Calculations;

use App\Contracts\RatingCalculation;
use App\Events\RaceResultCalculated;

class MySystemCalculation implements RatingCalculation
{
    public function handle(RaceResultCalculated $event): void
    {
        foreach ($event->participations as $participation) {
            // $participation->position, $participation->race->date, etc.
            $participation->points = 0; // your formula here
            $participation->uncertainty = 0;
            $participation->save();
        }
    }
}
  1. Register it in config/ranking.php:
'algorithms' => [
    // ...
    'my-system' => MySystemCalculation::class,
],
  1. Select it with RANKING_ALGORITHM=my-system in .env and recalculate as explained above.

Note: ratings are reset at the start of every season, so all drivers and teams begin each season from the same neutral value and the season champion is simply the one with the most points at the end of the last race.

πŸ”„ Automation

The scheduler keeps data and ratings up to date automatically (see routes/console.php):

Schedule Command Description
Daily at 04:00 f1:sync Imports missing seasons, races and results from the Jolpica API (current and previous season by default). Existing data is never overwritten, so manual edits always win.
Weekly on Mondays at 05:00 ranking:recalculate Full ratings recalculation as a consistency safety net after each Grand Prix weekend.

In production, make sure the Laravel scheduler runs every minute via cron:

* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

For local development you can run it with:

php artisan schedule:work

To import the full history from scratch at any time:

php artisan f1:sync --from=1950

☁️ Deployment on AWS

Prerequisites

  • AWS account
  • Fork the RacingTracker repository

Installation

Create the AWS infrastructure

  1. Create a new VPC in AWS.

  2. Create a new EC2 instance in AWS:

    • Select Ubuntu OS
    • Create a new key pair
    • Select the VPC you created in the previous step in the network settings
    • Select the public subnet in the network settings
    • Enable the auto-assign public IP option in the network settings
    • Create two new security groups rules:
      • Type: HTTP (80), Source type: Anywhere
      • Type: HTTPS (443), Source type: Anywhere
  3. Edit key pair permissions in your local machine:

sudo chmod 0600 "path/to/your/key.pem"

Prepare Ubuntu

  1. Connect with SSH to the EC2 instance:
ssh -i "path/to/your/key.pem" ubuntu@ec2-public-ip-address`
  1. Update the system:
sudo apt update && sudo apt upgrade -y
  1. Reboot the instance in AWS.

  2. Install the necessary packages:

sudo apt install nginx -y && sudo add-apt-repository ppa:ondrej/php -y && sudo apt install -y php8.4-fpm php8.4-curl php8.4-xml php8.4-mbstring php8.4-zip php8.4-mysql php8.4-sqlite3 php8.4-redis zip unzip && sudo curl -sS https://getcomposer.org/installer | php && sudo mv ~/composer.phar /usr/local/bin/composer && curl -fsSL https://deb.nodesource.com/setup_26.x | sudo -E bash - && sudo apt install nodejs -y && sudo npm install -g pnpm && composer --version && node -v && pnpm -v
  1. Create a new user to make deployment more secure:
sudo adduser deploy-user
  1. Access the new user home directory:
sudo su deploy-user && cd ~
  1. Generate GitHub ssh keys for the new user:
ssh-keygen -f /home/deploy-user/.ssh/github_rsa -t rsa
  1. Create a new GitHub config file:
nano /home/deploy-user/.ssh/config
Host github.com
  IdentityFile ~/.ssh/github_rsa
  IdentitiesOnly yes
  1. Change ssh directory and files permissions:
chmod 700 ~/.ssh && chmod 600 ~/.ssh/*

Application deployment

  1. Add a new deploy key in the forked GitHub repository where its value will be the key github_rsa.pub:
cat ~/.ssh/github_rsa.pub
  1. Clone the forked repository:
git clone git@github.com:your-username/RacingTracker.git code && cd code
  1. Install the dependencies:
composer install && pnpm install
  1. Copy the .env.example file to .env:
cp .env.example .env
  1. Generate the application key:
php artisan key:generate
  1. Run the migrations:
php artisan migrate
  1. Run the seeders:
php artisan db:seed
  1. Build the frontend:
pnpm run build
  1. Remove default file in sites-enabled:
exit
sudo rm /etc/nginx/sites-enabled/default
  1. Create a config file in sites-available:
sudo nano /etc/nginx/sites-available/racingtracker.conf
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    root /home/deploy-user/code/public; # Change deploy-user to your username
    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
  1. Create a symlink to the config file in sites-enabled:
sudo ln -s /etc/nginx/sites-available/racingtracker.conf /etc/nginx/sites-enabled/
  1. Test if the configuration is correct:
sudo nginx -t
  1. Reload the nginx service:
sudo service nginx reload
  1. Add the nginx user in a new group with deploy-user:
sudo usermod -aG deploy-user www-data
  1. Modify php-fpm pool configuration:
sudo nano /etc/php/8.4/fpm/pool.d/www.conf
; pool name ('www' here)
[deploy-user]

...

user = deploy-user
group = deploy-user

...

listen.owner = deploy-user
listen.group = deploy-user
  1. Restart the php-fpm service:
sudo service php8.4-fpm restart
  1. You can now access the application at http://ec2-public-ip-address.

Installing a self-signed SSL/TLS certificate

  1. Install the necessary dependencies:
sudo apt install openssl -y
  1. Create a new directory for the SSL/TLS certificate:
sudo su deploy-user
mkdir ~/.ssl && cd ~/.ssl
  1. Create a config file in a new directory:
nano san.cnf
[req]
default_bits       = 2048
prompt             = no
default_md         = sha256
req_extensions     = req_ext
distinguished_name = dn

[dn]
C  = # Country
ST = # State
L  = # Locality
O  = # Organization
OU = # Organizational Unit
CN = XX.XX.XX.XX # EC2 instance public IP address

[req_ext]
subjectAltName = @alt_names

[alt_names]
IP.1 = XX.XX.XX.XX # EC2 instance public IP address
  1. Generate a RSA key pair:
openssl genrsa -out server.key 2048
  1. Generate the certificate signing request (CSR):
openssl req -x509 -nodes -newkey rsa:2048 \
  -keyout server.key \
  -out server.crt \
  -days 365 \
  -subj "/CN=XX.XX.XX.XX"
  1. Edit nginx configuration file:
exit
sudo nano /etc/nginx/sites-available/racingtracker.conf
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    listen 443 ssl;
    server_name XX.XX.XX.XX; # EC2 instance public IP address
    root /home/deploy-user/code/public; # Change deploy-user to your username
    index index.html index.htm index.php;

    ssl_certificate     /home/deploy-user/.ssl/server.crt; # Change deploy-user to your username
    ssl_certificate_key /home/deploy-user/.ssl/server.key; # Change deploy-user to your username

    # Redirect HTTP to HTTPS
    if ($scheme != "https") {
      return 301 https://$server_name$request_uri;
    }

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
  1. Check if the configuration is correct and reload the nginx service:
sudo nginx -t && sudo systemctl reload nginx
  1. You can now access the application at https://ec2-public-ip-address.

🧭 Application features

RacingTracker provides public read-only access to historical racing data and a set of authenticated editing tools:

  • Season, race, driver and team standings with cumulative points and gaps.
  • Interactive cumulative-points charts, finishing-position charts and race position tracking.
  • Driver and team activity heatmaps, plus team win distribution on the home dashboard.
  • Head-to-head driver comparison at /compare, including season-specific points histories.
  • Global season selection from the application header on dashboard and index pages.
  • Responsive data tables with sorting, pagination, column visibility and contextual empty states.
  • Public pages can be browsed without an account. Creating, editing and deleting races, drivers and teams requires a verified account.

Maintaining calculated points

When imported or manually edited results leave cumulative points inconsistent, normalize stored values with:

php artisan points:normalize

The command is idempotent and only raises a driver's cumulative points when a later race is lower than a previous result in the same season. Team points are calculated from each driver's race-to-race delta, including team changes and shared-car historical entries.

User accounts

Users can register, log in, reset passwords, verify email addresses, update their profile and password, and delete their account from profile settings. Authenticated and verified users can manage the racing data through the create, edit and delete actions.

πŸ“„ License

Distributed under the MIT License. See LICENSE for more information.

πŸ“š References

About

Web application to manage motorsport races.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages