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);
}
// 6.5 Consolidate workshops that did not reach their minimal Teilnehmerzahl
// Load minimal requirements for workshops in this Wahl
// 6.5 Verbesserte Mindestanzahl-Sicherung: Unterbesetzte Workshops auflösen
$ws_ids_in_wahl = array_keys($workshops);
if (!empty($ws_ids_in_wahl)) {
// min_teilnehmer laden (einmalig)
$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 = [];
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 = [];
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 = [];
foreach($ws_ids_in_wahl as $wsid) {
$min_req = intval($min_map[$wsid] ?? 0);
$cnt = intval($assigned_counts[$wsid] ?? 0);
if ($cnt > 0 && $min_req > 0 && $cnt < $min_req) {
$min_req = $min_map[$wsid] ?? 0;
$cnt = $assigned_counts[$wsid] ?? 0;
if ($min_req > 0 && $cnt < $min_req) { // ← auch wenn cnt == 0 !
$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)) {
// 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 = [];
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));
foreach($rows as $r) $to_reassign[] = intval($r->teilnehmer_id);
$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
));
foreach($rows as $r) {
$to_reassign[] = intval($r->teilnehmer_id);
}
}
if (!empty($to_reassign)) {
// remove those assignments
$fw_placeholders = 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)");
// Alte Zuweisungen löschen
$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_list)");
// free capacity for the failing workshops
// Kapazitäten wieder freigeben
foreach($failing as $fw) {
$freed = intval($assigned_counts[$fw] ?? 0);
if (!isset($caps[$fw])) $caps[$fw] = 0;
$caps[$fw] += $freed;
$freed = $assigned_counts[$fw] ?? 0;
$caps[$fw] = ($caps[$fw] ?? 0) + $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) {
$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;
$reassigned = false;
// Nochmal Wunsch 13 versuchen
for($w = 1; $w <= 3; $w++) {
$choice = intval($tn->{"wunsch$w"});
if ($choice > 0 && isset($caps[$choice]) && $caps[$choice] > 0) {
$assigned = $assign($tn, $choice, $w);
if ($assigned) { $reassigned = true; break; }
if ($assign($tn, $choice, $w)) {
$reassigned = true;
break;
}
}
}
if ($reassigned) continue;
// otherwise assign to any workshop with free capacity
$available = array_keys(array_filter($caps, function($c){return $c>0;}));
// Falls kein Wunsch frei → irgendein freier Workshop
$available = array_keys(array_filter($caps, fn($c) => $c > 0));
if (!empty($available)) {
$target = $available[array_rand($available)];
$assign($tn, $target, 99);
continue;
if ($assign($tn, $target, 99)) {
$reassigned = true;
}
}
// lastly, mark as unassigned
// Wenn immer noch nichts frei → unassigned
if (!$reassigned) {
$wpdb->insert("{$prefix}kc_zuteilung", [
'wahl_id' => $wahl_id,
'teilnehmer_id' => $tn->id,
@@ -211,6 +272,7 @@ function kc_run_zuteilung($wahl_id) {
}
}
}
}
// 7) Kapazitätsprüfung (Debug / Log)
foreach($caps as $wsid=>$capleft) {