Vœu de compagnon : traité comme réciproque par le solveur
Le vœu reste à sens unique en base (A choisit B sur sa fiche, B n'a rien à faire) mais si A souhaite B, B doit désormais profiter aussi de la présence de A — dans les deux sens : affecter A d'abord fait remonter B comme candidat, et affecter B d'abord fait remonter A, même si B n'a rien demandé. - Solveur (`loader.py`) : le vœu (A → B) alimente maintenant l'ensemble effectif des deux bénévoles (A et B), pas seulement celui du demandeur. Aucun changement dans `engine.py` : il ne fait que lire un ensemble déjà symétrisé. - Mock (`buildEffectiveCompanions`) et classement par défaut (`buildEffectiveCompanionIds` dans `lib/planning.ts`, utilisé par `AssignmentPanel.tsx`) : même symétrisation, pour rester cohérents avec le vrai solveur. - La fiche bénévole (vœux affichés/édités) n'est pas touchée : c'est le seul endroit qui reste à sens unique, par construction (la réciprocité n'est qu'une lecture, jamais une donnée enregistrée — sinon éditer sa propre liste risquerait d'effacer le vœu de quelqu'un d'autre). - Nouveau test d'intégration : le bénévole qui n'a rien demandé profite quand même de la présence de celui qui l'a choisi. - CLAUDE.md à jour. Vérifié : 193 tests backend pertinents (dont le nouveau), ruff, alembic check ; typecheck/lint/build/smoke frontend. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
166e2c3366
commit
11bc8cbe36
@@ -6,6 +6,7 @@ import type {
|
||||
PositionCategory,
|
||||
ShiftRead,
|
||||
Violation,
|
||||
VolunteerRead,
|
||||
} from '@/api/schema';
|
||||
import { t } from '@/i18n';
|
||||
|
||||
@@ -112,6 +113,28 @@ export function windowsOverlap(a: TimeWindow, b: TimeWindow): boolean {
|
||||
return a.starts_at < b.ends_at && b.starts_at < a.ends_at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vœu de compagnon traité comme réciproque : si A souhaite B, B profite
|
||||
* aussi de la présence de A, même s'il ne l'a pas demandé (le vœu reste à
|
||||
* sens unique en base — voir `VolunteerCompanionWish` côté back-end — cette
|
||||
* réciprocité n'est calculée qu'à l'usage, jamais enregistrée). Indexé par
|
||||
* id de bénévole.
|
||||
*/
|
||||
export function buildEffectiveCompanionIds(
|
||||
volunteers: Pick<VolunteerRead, 'id' | 'wished_companion_ids'>[],
|
||||
): Map<string, Set<string>> {
|
||||
const effective = new Map<string, Set<string>>();
|
||||
for (const v of volunteers) {
|
||||
effective.set(v.id, new Set(v.wished_companion_ids));
|
||||
}
|
||||
for (const v of volunteers) {
|
||||
for (const companionId of v.wished_companion_ids) {
|
||||
effective.get(companionId)?.add(v.id);
|
||||
}
|
||||
}
|
||||
return effective;
|
||||
}
|
||||
|
||||
const CHANCE_WISH_POSITION_BONUS = 30;
|
||||
const CHANCE_AVAIL_PENALTY_PER_HALF_HOUR = 5;
|
||||
const CHANCE_ARTIST_CONFLICT_PENALTY = 80;
|
||||
|
||||
Reference in New Issue
Block a user