arbitres.ca : assigner des arbitres de baseball sans Excel

arbitres.ca, application d'assignation d'arbitres de baseball

arbitres.ca : assigner des arbitres de baseball sans Excel

Partie 1 de la série “Dans les coulisses d’arbitres.ca” : Partie 2 – Comment je l’ai bâti avec l’IA | English

J’ai un proche qui arbitre au baseball mineur. C’est comme ça que j’ai vu comment les associations gèrent leurs assignations d’arbitres, souvent avec un fichier Excel et une série de textos. Je voulais aussi un projet assez gros pour tester pour vrai Blazor et Supabase, et ma façon de travailler avec l’IA.

Dans cet article, on fait le tour de ce que l’application fait et de la stack derrière. Le prochain article couvre comment je l’ai bâtie avec Claude et Copilot.

Assigner des arbitres à la main

Une partie de baseball a besoin d’officiels à des postes précis : un arbitre au marbre, un ou plusieurs aux buts, et un marqueur. Les arbitres soumettent leurs disponibilités et le responsable de l’association doit remplir chaque poste.

Ça a l’air simple, mais il y a plusieurs contraintes en même temps. L’arbitre doit être disponible. Il doit avoir le niveau de certification et l’âge requis pour le calibre de la partie, selon la grille de Baseball Québec. Il ne doit pas déjà être assigné à la même heure, ni à une partie qui finit trop proche sur un autre terrain. Le responsable veut aussi garder les assignations équilibrées entre ses arbitres pendant la saison.

Ajoutez les désistements de dernière minute. Avec un chiffrier, ça devient vite mêlant.

Ce que fait l’application

Il y a trois rôles. L’arbitre soumet ses disponibilités, voit ses assignations et peut brancher son horaire sur son calendrier avec un flux iCal. Le responsable gère les parties, les terrains et les arbitres de son association, fait les assignations, et consulte la balance et le taux de fiabilité de chacun. Le super-admin gère la grille d’éligibilité provinciale et approuve les nouvelles associations.

Tableau de bord du responsable : cartes de statistiques, bannière d’alerte de pénurie, liste des parties à assigner et balance des arbitres. L’accueil du responsable : les parties de la saison, celles qui restent à assigner, et la balance de chacun.

Quand une assignation est créée, l’arbitre reçoit un courriel avec des boutons pour accepter ou décliner directement, sans se connecter. Le responsable voit de son côté qui n’a pas encore ouvert son assignation et peut relancer les retardataires. Si l’arbitre se désiste, l’application peut envoyer une offre de remplacement aux autres arbitres disponibles et assigner le premier qui accepte.

Il y a aussi tout le reste : importer le calendrier par CSV, jumeler deux parties en programme double, annuler ou reporter des parties en lot, et suivre les paiements avec une grille de tarifs par calibre et par poste.

L’application va aussi chercher les prévisions météo de chaque terrain et marque les parties à risque quand la probabilité de pluie dépasse 70 % dans les 36 heures. Le responsable peut ensuite annuler le lot d’un coup. Les prévisions viennent d’Open-Meteo, sans clé API.

Blazor WebAssembly, Supabase et Azure Static Web Apps

Je suis seul sur le projet et je voulais garder les coûts proches de zéro. Ça a décidé pas mal tous mes choix.

Le frontend est en Blazor WebAssembly sur .NET 10. Je fais du C# depuis longtemps, et Blazor WASM se compile en fichiers statiques. Pas besoin d’un serveur .NET.

Le backend, c’est Supabase : PostgreSQL, l’authentification, le Realtime et les Edge Functions. Je n’ai pas d’API maison à écrire ni à héberger. Le client C# parle directement à Postgrest, et les Edge Functions couvrent ce qui doit rouler côté serveur, comme les courriels envoyés par Azure Communication Services.

L’hébergement est sur Azure Static Web Apps, tier gratuit, avec le déploiement continu par GitHub Actions. Chaque pull request a son propre environnement de préversion, ce qui me sert à valider avant de merger. J’ai couvert cette partie en détail dans la série sur Azure Static Web Apps avec Blazor, qui utilisait déjà ce projet comme exemple.

Pour la connexion, j’utilise le lien magique par courriel, avec Google OAuth en option. Il n’y a pas d’auto-inscription pour les arbitres : le responsable les invite, et les associations s’inscrivent par un formulaire public que j’approuve manuellement.

Le RLS comme seule source de vérité

Toutes les associations partagent la même base de données. L’isolation passe par les politiques Row Level Security de Postgres, avec une colonne association_id sur les tables et deux fonctions SQL qui donnent le contexte de l’utilisateur connecté :

-- l'association de l'utilisateur connecté
select mon_association_id();

-- son rôle : arbitre, responsable ou super_admin
select mon_role();

Ça change la façon d’écrire le code avec Blazor WebAssembly. L’application roule dans le navigateur, donc un filtre dans un composant Blazor sert juste à l’affichage. N’importe qui peut appeler l’API directement avec la même clé. Le RLS est la seule vraie barrière.

Les règles du domaine sont aussi dans la base. Une fonction arbitre_a_conflit(arbitre_id, partie_id) détecte les chevauchements et les parties consécutives sur des terrains différents, et ce conflit bloque l’assignation. L’éligibilité ne bloque pas : un arbitre non éligible affiche un avertissement, mais le responsable peut quand même l’assigner. La grille provinciale sert de valeur par défaut et chaque association peut la surcharger.

De l’alerte de pénurie au remplissage automatique

Beaucoup d’associations québécoises reçoivent leur calendrier par Spordle Play, la plateforme utilisée par Baseball Québec. Elle fait bien la gestion du calendrier. Ce que j’ai voulu ajouter, c’est l’assignation quand il manque du monde.

Trois fonctionnalités viennent de là. La première est une alerte de pénurie. C’est une vue SQL qui regarde les 14 prochains jours et sort chaque poste à risque. Un poste sans aucun candidat éligible et disponible ressort en rouge. Un poste couvert seulement par des disponibilités générées par un gabarit récurrent ressort en orange, parce que c’est moins fiable qu’une disponibilité saisie à la main.

Page d’alerte de pénurie : la liste des postes à risque des 14 prochains jours, chacun avec un niveau rouge ou orange. Chaque poste à risque des 14 prochains jours. Ici, ils sont tous rouges parce qu’aucun candidat n’est confirmé.

La deuxième est un score de suggestion sur la page d’assignation. Les candidats sont triés par éligibilité, puis par un score qui tient compte de la charge de la saison, du taux de fiabilité, des préférences de calibre et de partenaire, et de la proximité des terrains. Chaque candidat affiche une rangée de pastilles qui explique son classement.

Page d’assignation : chaque candidat affiche une rangée de pastilles avec son niveau, sa charge et son taux de fiabilité. Le classement des candidats, avec l’explication du score sur la même rangée.

La troisième réunit les deux autres. Depuis la page de pénurie, le bouton « Générer les propositions » lance un solveur qui remplit les postes vides. Il trie les postes du plus contraint au moins contraint, puis place les candidats un par un, avec la balance et le maximum hebdomadaire qui bougent à chaque placement. Le classement vient du même score que la page d’assignation, pas d’une logique parallèle.

Le responsable révise la liste, retire ce qu’il ne veut pas, et accepte le reste d’un coup. Chaque proposition est soit une assignation directe, soit une offre ciblée envoyée par courriel quand la disponibilité n’est pas confirmée. Le solveur ne touche jamais une assignation existante, il remplit seulement les trous. Rien n’est stocké non plus : le résultat vit le temps de la révision.

Je me suis imposé une contrainte pour ces trois fonctionnalités : aucun écran de configuration. Les seuils, l’horizon de 14 jours et les poids du score sont codés en dur. Un responsable bénévole n’a pas envie de calibrer un algorithme avant de s’en servir. J’ai préféré expliquer le classement à l’écran.

L’application est déployée et fonctionnelle. Vous pouvez aller voir à arbitres.ca.

Bonne programmation, et dans le prochain article je passe au travers de la méthode que j’ai utilisée pour bâtir tout ça avec Claude et Copilot.


Cet article a été rédigé avec l’aide de l’IA et révisé par moi.


Suggestions de lecture :