Corregge il dettaglio MVP in classifica: solo partite, non tutti gli eventi
Il sottotitolo "N partite giocate" per il criterio MVP usava totaliEventiGiocatore(), che conta anche gli allenamenti: un numero fuorviante perché l'MVP si vota solo alle partite del calendario interno (eventi_app), non a quelle del sito CSI. Aggiunge contaPartiteGiocate() (presenze.ts) e Giocatore.partiteGiocate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+3
-1
@@ -101,7 +101,9 @@ Prima versione, pre-release.
|
||||
scollegato dal criterio scelto: ora segue il criterio selezionato (`dettaglioClassifica` in
|
||||
`src/lib/rosa.ts`). Per Palloni mostra le volte consecutive in cui il giocatore li ha
|
||||
portati (nuovo campo `Giocatore.seriePalloni`, calcolato da `serieConsecutivaPalloni` in
|
||||
`src/lib/palloni-core.ts`), non più le presenze.
|
||||
`src/lib/palloni-core.ts`), non più le presenze. Per MVP mostra le partite giocate — solo
|
||||
partite, non più allenamenti compresi (nuovo campo `Giocatore.partiteGiocate`, da
|
||||
`contaPartiteGiocate()` in `src/lib/presenze.ts`).
|
||||
|
||||
### Sicurezza
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ usePresenzeUltimoMese() → src/lib/presenze-mese.ts
|
||||
contaPresenzeGiocatore() alimenta il campo `presenze` del `Giocatore` in `useRosa()`, mostrato
|
||||
come StatTile nel profilo e in home (sezione «Colpo d'occhio», `index.tsx`).
|
||||
|
||||
contaPartiteGiocate() è contaPresenzeGiocatore() ristretto alle sole partite (non
|
||||
allenamenti): alimenta `Giocatore.partiteGiocate`, usato nel sottotitolo della classifica
|
||||
interna di Squadra quando si ordina per MVP — un conteggio di eventi generico (allenamenti
|
||||
compresi) sarebbe fuorviante lì, perché l'MVP si vota solo alle partite.
|
||||
|
||||
--- sollecito (solo admin) ---
|
||||
Bottone "Sollecita" (RosaPresenze.tsx) → POST /api/public/sollecita-presenze
|
||||
↓
|
||||
|
||||
@@ -28,6 +28,8 @@ export type Giocatore = {
|
||||
nascita: string;
|
||||
presenze: number;
|
||||
totaliEventi: number;
|
||||
/** Solo partite (non allenamenti) a cui era presente o in ritardo. */
|
||||
partiteGiocate: number;
|
||||
streak: number;
|
||||
/** Serie consecutive per tipo: si azzerano in modo indipendente. */
|
||||
serieAllenamenti: number;
|
||||
@@ -103,6 +105,7 @@ export const giocatori: Giocatore[] = rosaCSI
|
||||
iniziali: inizialiDa(r.nome),
|
||||
presenze: 0,
|
||||
totaliEventi: 0,
|
||||
partiteGiocate: 0,
|
||||
streak: 0,
|
||||
serieAllenamenti: 0,
|
||||
seriePartite: 0,
|
||||
|
||||
+28
-3
@@ -14,28 +14,53 @@ export type MappaPresenze = Record<string, Record<string, Stato>>;
|
||||
export type MappaTempiRisposta = Record<string, Record<string, string>>;
|
||||
|
||||
/** Allenamenti e partite CrAPP già passati, che contano per le statistiche di presenza. */
|
||||
function eventiContanoPresenze(eventi: Evento[], giocatoreId?: string, oggi = dataOggi()) {
|
||||
function eventiContanoPresenze(
|
||||
eventi: Evento[],
|
||||
giocatoreId?: string,
|
||||
oggi = dataOggi(),
|
||||
tipo?: "partita" | "allenamento",
|
||||
) {
|
||||
return eventi.filter(
|
||||
(e) =>
|
||||
(e.tipo === "partita" || e.tipo === "allenamento") &&
|
||||
(tipo === undefined || e.tipo === tipo) &&
|
||||
e.data < oggi &&
|
||||
(giocatoreId === undefined || e.convocati.length === 0 || e.convocati.includes(giocatoreId)),
|
||||
);
|
||||
}
|
||||
|
||||
/** Presenze effettive (presente o in ritardo) su eventi CrAPP. Senza eventi rilevanti restituisce 0. */
|
||||
/**
|
||||
* Presenze effettive (presente o in ritardo) su eventi CrAPP. Senza eventi rilevanti
|
||||
* restituisce 0. `tipo` filtra a un solo tipo di evento (es. solo partite); di default
|
||||
* conta partite e allenamenti insieme, come il resto delle statistiche di presenza.
|
||||
*/
|
||||
export function contaPresenzeGiocatore(
|
||||
giocatoreId: string,
|
||||
eventi: Evento[],
|
||||
presenze: MappaPresenze,
|
||||
oggi: string = dataOggi(),
|
||||
tipo?: "partita" | "allenamento",
|
||||
): number {
|
||||
return eventiContanoPresenze(eventi, giocatoreId, oggi).filter((e) => {
|
||||
return eventiContanoPresenze(eventi, giocatoreId, oggi, tipo).filter((e) => {
|
||||
const stato = presenze[e.id]?.[giocatoreId];
|
||||
return stato === "presente" || stato === "ritardo";
|
||||
}).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Partite (non allenamenti) a cui il giocatore era presente o in ritardo: il dato giusto
|
||||
* per contesti legati alle prestazioni in campo (es. MVP), a differenza di
|
||||
* `totaliEventiGiocatore()` che è il denominatore delle presenze e include gli allenamenti.
|
||||
*/
|
||||
export function contaPartiteGiocate(
|
||||
giocatoreId: string,
|
||||
eventi: Evento[],
|
||||
presenze: MappaPresenze,
|
||||
oggi: string = dataOggi(),
|
||||
): number {
|
||||
return contaPresenzeGiocatore(giocatoreId, eventi, presenze, oggi, "partita");
|
||||
}
|
||||
|
||||
/** Eventi CrAPP rilevanti per il denominatore presenze di un giocatore. */
|
||||
export function totaliEventiGiocatore(
|
||||
giocatoreId: string,
|
||||
|
||||
+4
-2
@@ -10,6 +10,7 @@ import { useInfortuniERitardi } from "./infortuni";
|
||||
import { useGiocatoreId } from "./user-store";
|
||||
import { useEventi } from "./eventi";
|
||||
import {
|
||||
contaPartiteGiocate,
|
||||
contaPresenzeGiocatore,
|
||||
serieConferme,
|
||||
serieConsecutiva,
|
||||
@@ -88,6 +89,7 @@ export function useRosa(): Giocatore[] {
|
||||
iniziali: iniziali(g.nome, g.cognome),
|
||||
presenze: contaPresenzeGiocatore(g.id, eventi, mappaPresenze),
|
||||
totaliEventi: totaliEventiGiocatore(g.id, eventi),
|
||||
partiteGiocate: contaPartiteGiocate(g.id, eventi, mappaPresenze),
|
||||
streak: serieConsecutiva(g.id, eventi, mappaPresenze),
|
||||
serieAllenamenti: serieConsecutiva(g.id, eventi, mappaPresenze, "allenamento"),
|
||||
seriePartite: serieConsecutiva(g.id, eventi, mappaPresenze, "partita"),
|
||||
@@ -128,7 +130,7 @@ export function dettaglioClassifica(
|
||||
g: {
|
||||
streak: number;
|
||||
votiPagella: number;
|
||||
totaliEventi: number;
|
||||
partiteGiocate: number;
|
||||
cacche: number;
|
||||
seriePalloni: number;
|
||||
},
|
||||
@@ -138,7 +140,7 @@ export function dettaglioClassifica(
|
||||
case "mediaVoto":
|
||||
return `${g.votiPagella} voti pagella`;
|
||||
case "mvp":
|
||||
return `${g.totaliEventi} partite giocate`;
|
||||
return `${g.partiteGiocate} partite giocate`;
|
||||
case "cacchePartita":
|
||||
return `${g.cacche} giornate top`;
|
||||
case "palloni":
|
||||
|
||||
@@ -3,6 +3,7 @@ import assert from "node:assert/strict";
|
||||
import type { Evento } from "@/lib/eventi";
|
||||
import {
|
||||
conRisposta,
|
||||
contaPartiteGiocate,
|
||||
contaPresenzeGiocatore,
|
||||
destinatariSollecito,
|
||||
serieConferme,
|
||||
@@ -91,6 +92,19 @@ assert.equal(totaliEventiGiocatore("g1", eventi, OGGI), 5, "gli eventi futuri no
|
||||
assert.equal(contaPresenzeGiocatore("g2", eventi, presenze, OGGI), 0, "chi non risponde è a zero");
|
||||
assert.equal(totaliEventiGiocatore("g2", eventi, OGGI), 5, "il denominatore è uguale per tutti");
|
||||
|
||||
// contaPartiteGiocate esclude gli allenamenti: su 5 eventi passati (4 allenamenti, 1 partita
|
||||
// p1, presente), conta solo p1.
|
||||
assert.equal(
|
||||
contaPartiteGiocate("g1", eventi, presenze, OGGI),
|
||||
1,
|
||||
"solo la partita p1: gli allenamenti non contano come partite giocate",
|
||||
);
|
||||
assert.equal(
|
||||
contaPresenzeGiocatore("g1", eventi, presenze, OGGI, "allenamento"),
|
||||
3,
|
||||
"contaPresenzeGiocatore con tipo esplicito filtra come contaPartiteGiocate ma per allenamenti",
|
||||
);
|
||||
|
||||
// Solo partite e allenamenti: compleanni e altri eventi restano fuori.
|
||||
const conAltri: Evento[] = [
|
||||
...eventi,
|
||||
|
||||
@@ -28,7 +28,7 @@ assert.deepEqual(classificaRank([0, 0]), [1, 1], "parità anche a valore zero");
|
||||
|
||||
// --- dettaglioClassifica: il sottotitolo segue il criterio selezionato -------
|
||||
|
||||
const g = { streak: 12, votiPagella: 18, totaliEventi: 22, cacche: 3, seriePalloni: 4 };
|
||||
const g = { streak: 12, votiPagella: 18, partiteGiocate: 15, cacche: 3, seriePalloni: 4 };
|
||||
|
||||
assert.equal(
|
||||
dettaglioClassifica(g, "presenze"),
|
||||
@@ -47,8 +47,8 @@ assert.equal(
|
||||
);
|
||||
assert.equal(
|
||||
dettaglioClassifica(g, "mvp"),
|
||||
"22 partite giocate",
|
||||
"mvp: mostra le partite giocate, non le presenze consecutive",
|
||||
"15 partite giocate",
|
||||
"mvp: mostra le partite giocate (non gli eventi totali, che includerebbero gli allenamenti)",
|
||||
);
|
||||
assert.equal(
|
||||
dettaglioClassifica(g, "cacchePartita"),
|
||||
|
||||
Reference in New Issue
Block a user