Improve workshop minimum participant handling and reassignment logic
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s

This commit is contained in:
Blitz08
2026-03-02 12:49:37 +01:00
parent 90d29cf500
commit 0f5d575cb9

View File

@@ -131,73 +131,134 @@ function kc_run_zuteilung($wahl_id) {
$assign($tn, $ws_id, 99); $assign($tn, $ws_id, 99);
} }
// 6.5 Consolidate workshops that did not reach their minimal Teilnehmerzahl // 6.5 Verbesserte Mindestanzahl-Sicherung: Unterbesetzte Workshops auflösen
// Load minimal requirements for workshops in this Wahl
$ws_ids_in_wahl = array_keys($workshops); $ws_ids_in_wahl = array_keys($workshops);
if (!empty($ws_ids_in_wahl)) { if (!empty($ws_ids_in_wahl)) {
// min_teilnehmer laden (einmalig)
$placeholders = implode(',', array_fill(0, count($ws_ids_in_wahl), '%d')); $placeholders = implode(',', array_fill(0, count($ws_ids_in_wahl), '%d'));
$min_rows = $wpdb->get_results($wpdb->prepare("SELECT id, min_teilnehmer FROM {$prefix}kc_workshops WHERE id IN ($placeholders)", $ws_ids_in_wahl)); $min_rows = $wpdb->get_results($wpdb->prepare(
"SELECT id, min_teilnehmer FROM {$prefix}kc_workshops WHERE id IN ($placeholders)",
$ws_ids_in_wahl
));
$min_map = []; $min_map = [];
foreach($min_rows as $r) $min_map[intval($r->id)] = intval($r->min_teilnehmer); foreach($min_rows as $r) {
$min_map[intval($r->id)] = intval($r->min_teilnehmer);
}
// Aktuelle Belegung zählen
$assigned_counts_raw = $wpdb->get_results($wpdb->prepare(
"SELECT workshop_id, COUNT(*) AS cnt
FROM {$prefix}kc_zuteilung
WHERE wahl_id=%d AND phase=%d AND workshop_id IS NOT NULL
GROUP BY workshop_id",
$wahl_id, $phase
), 'ARRAY_A');
// Count current assignments per workshop
$assigned_counts_raw = $wpdb->get_results($wpdb->prepare("SELECT workshop_id, COUNT(*) AS cnt FROM {$prefix}kc_zuteilung WHERE wahl_id=%d AND phase=%d AND workshop_id IS NOT NULL GROUP BY workshop_id", $wahl_id, $phase), ARRAY_A);
$assigned_counts = []; $assigned_counts = [];
foreach($assigned_counts_raw as $ar) $assigned_counts[intval($ar['workshop_id'])] = intval($ar['cnt']); foreach($assigned_counts_raw as $ar) {
$assigned_counts[intval($ar['workshop_id'])] = intval($ar['cnt']);
}
// Find failing workshops (assigned >0 but < min) $assigned_counts = [];
foreach($assigned_counts_raw as $ar) {
$assigned_counts[intval($ar['workshop_id'])] = intval($ar['cnt']);
}
// Unterbesetzte Workshops finden (mit Zuweisung > 0 aber < min)
$failing = []; $failing = [];
foreach($ws_ids_in_wahl as $wsid) { foreach($ws_ids_in_wahl as $wsid) {
$min_req = intval($min_map[$wsid] ?? 0); $min_req = $min_map[$wsid] ?? 0;
$cnt = intval($assigned_counts[$wsid] ?? 0); $cnt = $assigned_counts[$wsid] ?? 0;
if ($cnt > 0 && $min_req > 0 && $cnt < $min_req) { if ($min_req > 0 && $cnt < $min_req) { // ← auch wenn cnt == 0 !
$failing[] = $wsid; $failing[] = $wsid;
} }
} }
// ──────────────── WICHTIGE DIAGNOSE ────────────────
error_log("=== DIAGNOSE Mindestanzahl Phase $phase ===");
error_log("Workshops insgesamt: " . count($ws_ids_in_wahl));
foreach ($ws_ids_in_wahl as $wsid) {
$name = $workshops[$wsid]->name ?? 'ID '.$wsid;
$min = $min_map[$wsid] ?? 0;
$cnt = $assigned_counts[$wsid] ?? 0;
$cap = $caps[$wsid] ?? 'unbekannt';
$status = ($min > 0 && $cnt > 0 && $cnt < $min) ? 'UNTER MIN → wird umverteilt' :
(($min > 0 && $cnt >= $min) ? 'OK' :
($cnt == 0 ? 'leer (ignoriert)' : 'keine min-Anforderung'));
error_log("WS $wsid ($name) | min=$min | jetzt=$cnt | Restkap=$cap | $status");
}
error_log("Anzahl failing Workshops: " . count($failing));
if (!empty($failing)) { if (!empty($failing)) {
// collect participants from failing workshops error_log("Umverteilung startet für WS: " . implode(', ', $failing));
} else {
error_log("Keine Workshops unter Mindestanzahl mit Teilnehmern → nichts zu tun");
}
error_log("============================");
if (!empty($failing)) {
error_log("Phase $phase: Folgende Workshops unter Mindestanzahl → Teilnehmer werden umverteilt: " . implode(', ', $failing));
// Alle betroffenen Teilnehmer sammeln
$to_reassign = []; $to_reassign = [];
foreach($failing as $fw) { foreach($failing as $fw) {
$rows = $wpdb->get_results($wpdb->prepare("SELECT teilnehmer_id FROM {$prefix}kc_zuteilung WHERE wahl_id=%d AND phase=%d AND workshop_id=%d", $wahl_id, $phase, $fw)); $rows = $wpdb->get_results($wpdb->prepare(
foreach($rows as $r) $to_reassign[] = intval($r->teilnehmer_id); "SELECT teilnehmer_id FROM {$prefix}kc_zuteilung
WHERE wahl_id=%d AND phase=%d AND workshop_id=%d",
$wahl_id, $phase, $fw
));
foreach($rows as $r) {
$to_reassign[] = intval($r->teilnehmer_id);
}
} }
if (!empty($to_reassign)) { if (!empty($to_reassign)) {
// remove those assignments // Alte Zuweisungen löschen
$fw_placeholders = implode(',', array_map('intval', $failing)); $fw_list = implode(',', array_map('intval', $failing));
$wpdb->query("DELETE FROM {$prefix}kc_zuteilung WHERE wahl_id=".intval($wahl_id)." AND phase=".intval($phase)." AND workshop_id IN ($fw_placeholders)"); $wpdb->query("DELETE FROM {$prefix}kc_zuteilung
WHERE wahl_id = " . intval($wahl_id) . "
AND phase = " . intval($phase) . "
AND workshop_id IN ($fw_list)");
// free capacity for the failing workshops // Kapazitäten wieder freigeben
foreach($failing as $fw) { foreach($failing as $fw) {
$freed = intval($assigned_counts[$fw] ?? 0); $freed = $assigned_counts[$fw] ?? 0;
if (!isset($caps[$fw])) $caps[$fw] = 0; $caps[$fw] = ($caps[$fw] ?? 0) + $freed;
$caps[$fw] += $freed;
} }
// Try to reassign each participant preferring their wishes 1..3 // Teilnehmer erneut versuchen zuzuweisen zuerst Wünsche, dann Rest
foreach($to_reassign as $tid) { foreach($to_reassign as $tid) {
$tn = isset($all_teilnehmer_by_id[$tid]) ? $all_teilnehmer_by_id[$tid] : $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_teilnehmer WHERE id=%d", $tid)); $tn = $all_teilnehmer_by_id[$tid] ?? $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$prefix}kc_teilnehmer WHERE id = %d", $tid
));
if (!$tn) continue; if (!$tn) continue;
$reassigned = false; $reassigned = false;
for($w=1;$w<=3;$w++) {
// Nochmal Wunsch 13 versuchen
for($w = 1; $w <= 3; $w++) {
$choice = intval($tn->{"wunsch$w"}); $choice = intval($tn->{"wunsch$w"});
if ($choice > 0 && isset($caps[$choice]) && $caps[$choice] > 0) { if ($choice > 0 && isset($caps[$choice]) && $caps[$choice] > 0) {
$assigned = $assign($tn, $choice, $w); if ($assign($tn, $choice, $w)) {
if ($assigned) { $reassigned = true; break; } $reassigned = true;
break;
} }
} }
}
if ($reassigned) continue; if ($reassigned) continue;
// otherwise assign to any workshop with free capacity // Falls kein Wunsch frei → irgendein freier Workshop
$available = array_keys(array_filter($caps, function($c){return $c>0;})); $available = array_keys(array_filter($caps, fn($c) => $c > 0));
if (!empty($available)) { if (!empty($available)) {
$target = $available[array_rand($available)]; $target = $available[array_rand($available)];
$assign($tn, $target, 99); if ($assign($tn, $target, 99)) {
continue; $reassigned = true;
}
} }
// lastly, mark as unassigned // Wenn immer noch nichts frei → unassigned
if (!$reassigned) {
$wpdb->insert("{$prefix}kc_zuteilung", [ $wpdb->insert("{$prefix}kc_zuteilung", [
'wahl_id' => $wahl_id, 'wahl_id' => $wahl_id,
'teilnehmer_id' => $tn->id, 'teilnehmer_id' => $tn->id,
@@ -211,6 +272,7 @@ function kc_run_zuteilung($wahl_id) {
} }
} }
} }
}
// 7) Kapazitätsprüfung (Debug / Log) // 7) Kapazitätsprüfung (Debug / Log)
foreach($caps as $wsid=>$capleft) { foreach($caps as $wsid=>$capleft) {