Jury Results System Documentation¶
Version: 2.0 | Last Updated: 2026-01-28 | Status: β Fully Implemented
π Table of Contents¶
- Overview
- Concept & Motivation
- Database Architecture
- Formula System
- Automatic Calculation
- Data Flow
- API Integration
- UI Components
- Configuration
- Troubleshooting
Overview¶
Jury Results ist ein feldbasiertes Bewertungssystem fΓΌr Turnen-WettkΓ€mpfe (besonders P1-P9 Programme), das: - β Einzelne Bewertungsfelder erfasst (z.B. "Stufe", "Abzug AusfΓΌhrung") - β Formeln verwendet um den finalen Score zu berechnen - β Automatisch fehlende Endwerte berechnet und speichert - β Transparent die Berechnung in der UI anzeigt
Beispiel P-Wettkampf "Boden m. P1-P9":
Stufe: 6.0 Punkte (= A)
Abzug Ausf.: 2.0 Punkte (= B)
βββββββββββββββββββββββββββββββββ
Formel: (10 + A) - B
Endwert: (10 + 6) - 2 = 14.0 β
(automatisch berechnet)
Concept & Motivation¶
Warum Jury Results?¶
Problem der alten Implementierung:
- Nur ein Wert pro Disziplin gespeichert (tfx_wertungen_details.rel_leistung)
- Keine Transparenz ΓΌber wie der Score berechnet wurde
- Keine Einzelfeld-Informationen fΓΌr Jury-Mitglieder
- Schwierig zu debuggen bei falschen Berechnungen
LΓΆsung mit Jury Results:
- Mehrere Felder pro Disziplin mΓΆglich (z.B. Stufe, Abzug, Bonus, Endwert)
- Nachvollziehbar: Jedes Feld wird einzeln gespeichert
- Flexibel: Unterschiedliche Felder fΓΌr unterschiedliche Disziplinen
- Transparent: UI zeigt die Formel und Berechnung
- Kompatibel: tfx_wertungen_details.rel_leistung wird weiterhin aktualisiert fΓΌr Legacy-Code
Wann wird Jury Results verwendet?¶
Aktiviert durch: Configuration > Score Capture > useJuryResults = true (Standard)
Typische Disziplinen: - P-WettkΓ€mpfe (P1-P9): Stufe + Abzug β Endwert - KΓΌr-Programme: D-Note + E-Note β Endwert - Turn10 Programme: Verschiedene Komponenten
WICHTIG: Einstellung darf NICHT wΓ€hrend eines aktiven Wettkampfes geΓ€ndert werden!
Database Architecture¶
Table Overview¶
βββββββββββββββββββββββββββ
β tfx_disziplinen β Discipline Definition
β βββββββββββββββββββββ β
β int_disziplinenid (PK) β ID: 111 = "Boden m. P1-P9"
β var_name β Name: "Boden m. P1-P9"
β var_formel β Formula (direct): "(10 + A) - B" oder NULL
β int_formelid (FK) β Formula Reference: 4 = "P-Wettkampf"
β int_sportid β Sport: 11 = "P-Stufen"
β ... β
βββββββββ¬ββββββββββββββββββ
β
β 1:N
βΌ
βββββββββββββββββββββββββββββββββββ
β tfx_disziplinen_felder β Field Definitions per Discipline
β βββββββββββββββββββββββββββββββ β
β int_disziplinen_felderid (PK) β ID: 220, 221, 222
β int_disziplinenid (FK) β β 111 (Boden)
β var_name β "Stufe", "AbzugAusf.", "Endwert"
β int_sortierung β Sort Order: 1, 2, 3
β bol_endwert β Is Final Score: false, false, TRUE
β bol_ausgangswert β Is Starting Score: false
β int_gruppe β Group: 1
β bol_enabled β Enabled: true
βββββββββ¬ββββββββββββββββββββββββββ
β
β 1:N
βΌ
ββββββββββββββββββββββββββββββββββββ
β tfx_jury_results β Actual Performance Values
β βββββββββββββββββββββββββββββββββ
β int_juryresultsid (PK) β ID: 1, 2, 3
β int_wertungenid (FK) β β tfx_wertungen.int_wertungenid
β int_disziplinen_felderid (FK) β β 220 (Stufe)
β rel_leistung β Value: 6.0, 2.0, 14.0
β int_versuch β Attempt: 1
β int_kp β Control Point: 0
ββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββ
β tfx_formeln β Formula Definitions (Lookup Table)
β βββββββββββββββββββββ β
β int_formelid (PK) β ID: 4
β var_name β Name: "P-Wettkampf"
β var_formel β Formula: "(10 + A) - B"
β int_typ β Type: (unused)
βββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββ
β tfx_wertungen β Base Score Record
β βββββββββββββββββββββββββββ β
β int_wertungenid (PK) β ID: 116 (Luis Bader)
β int_teilnehmerid (FK) β β Participant
β int_wettkaempfeid (FK) β β Competition
β var_riege β Squad: "zz"
βββββββββββ¬ββββββββββββββββββββ
β
β 1:N
βΌ
βββββββββββββββββββββββββββββββ
β tfx_wertungen_details β Score per Discipline (Legacy Compatible)
β βββββββββββββββββββββββββββ β
β int_wertungen_detailsid(PK) β
β int_wertungenid (FK) β β 116
β int_disziplinenid (FK) β β 111 (Boden)
β rel_leistung β Final Score: 14.0 (auto-updated!)
β int_versuch β Attempt: 1
βββββββββββββββββββββββββββββββ
Key Relationships¶
- Discipline β Fields: Eine Disziplin hat mehrere Felder (1:N)
-
Beispiel: "Boden m. P1-P9" hat "Stufe", "AbzugAusf.", "Endwert"
-
Field β Jury Results: Ein Feld hat viele Wertungen (1:N)
-
Beispiel: Feld "Stufe" (220) hat Werte fΓΌr alle Turner
-
Wertung β Jury Results: Eine Wertung hat mehrere Feldwerte (1:N)
-
Beispiel: Luis Bader (wertungenId 116) hat 3 Feldwerte
-
Discipline β Formula: Formel wird ΓΌber
int_formelidreferenziert ODER direkt invar_formelgespeichert - PrioritΓ€t:
tfx_formeln.var_formel(viaint_formelid) >tfx_disziplinen.var_formel
Field Types¶
Jedes Feld hat Flags die seine Funktion definieren:
| Flag | Bedeutung | Beispiel |
|---|---|---|
bol_endwert = true |
Final Score Field - wird berechnet | "Endwert" |
bol_ausgangswert = true |
Starting Score - Basis fΓΌr Berechnung | "D-Note" |
int_sortierung |
Sort Order - Reihenfolge in UI | 1, 2, 3 |
bol_enabled = true |
Enabled - aktiv oder inaktiv | true |
int_gruppe |
Group - Gruppierung in UI | 1 |
Formula System¶
Formula Storage¶
Formeln kΓΆnnen an zwei Stellen gespeichert werden:
- In
tfx_formelnTabelle (bevorzugt): - VerknΓΌpft ΓΌber
tfx_disziplinen.int_formelid = 4 -
Vorteil: Wiederverwendbar, zentral gepflegt
-
Direkt in
tfx_disziplinen.var_formel: - Vorteil: Disziplin-spezifische Formel
Ladestrategie (Server: scores.ts):
// 1. Try loading from tfx_formeln via int_formelid (preferred)
const formulaResult = await prisma.$queryRawUnsafe(`
SELECT
d.var_formel as "disciplineFormula",
f.var_formel as "tableFormula"
FROM tfx_disziplinen d
LEFT JOIN tfx_formeln f ON d.int_formelid = f.int_formelid
WHERE d.int_disziplinenid = $1
`, disciplineId);
// 2. Prioritize tfx_formeln over discipline direct field
formula = formulaResult[0].tableFormula || formulaResult[0].disciplineFormula;
Formula Syntax¶
Format: Mathematischer Ausdruck mit Variablen A, B, C, ...
Beispiele:
| Disziplin | Formel | Bedeutung |
|---|---|---|
| P-Wettkampf | (10 + A) - B |
10 + Stufe - Abzug |
| AK | A - B - C |
Stufe - Abzug1 - Abzug2 |
| LK | A + B - C |
D-Note + E-Note - Abzug |
| D+E-Neutral | 1*x |
Direkter Wert (kein Jury Result) |
Variable Mapping:
- Variablen werden in alphabetischer Reihenfolge den nicht-finalen Feldern zugeordnet
- Reihenfolge basiert auf int_sortierung der Felder
Beispiel "Boden m. P1-P9":
Felder (sortiert nach int_sortierung):
1. Stufe (220) β A
2. AbzugAusf. (221) β B
3. Endwert (222) β (berechnet, nicht in Formel)
Formel: "(10 + A) - B"
Mapping: A = 6.0, B = 2.0
Berechnung: (10 + 6.0) - 2.0 = 14.0
Starting Value Extraction¶
StartValue ist die Basis-Punktzahl (meist 10) die aus der Formel extrahiert wird:
// Regex extracts first number (with optional parenthesis)
const startValueMatch = formula.match(/^[(\s]*(\d+\.?\d*)/);
// "(10 + A) - B" β matches "10"
// "A - B" β no match, use default 10.0
const startValue = startValueMatch ? parseFloat(startValueMatch[1]) : 10.0;
Verwendung in UI: - Anzeige in blauem Info-Box: "Ausgehend von 10.0 Punkten" - Hilft dem User die Formel zu verstehen
Automatic Calculation¶
When is Endwert Calculated?¶
Trigger: Server erkennt automatisch wenn:
1. β
Disziplin hat ein Feld mit bol_endwert = true (Final Score Field)
2. β
Es gibt Jury Results fΓΌr nicht-finale Felder (A, B, C, ...)
3. β
KEIN Jury Result fΓΌr das Final Score Field existiert
Beispiel:
Jury Results in DB:
- Stufe (220): 6.0 β
- AbzugAusf. (221): 2.0 β
- Endwert (222): β FEHLT!
β Server berechnet automatisch: (10 + 6) - 2 = 14.0
β Speichert in tfx_jury_results fΓΌr Feld 222
β Aktualisiert tfx_wertungen_details.rel_leistung = 14.0
Calculation Logic¶
Server: newWebBased/server/src/routes/scores.ts (lines ~220-280)
// 1. Detect missing final score
const hasFinalScore = juryResults.some(jr => jr.isFinalScore);
if (!hasFinalScore && formula && endwertFieldId) {
needsEndwertCalculation = true;
}
// 2. Build value map from non-final jury results
const valueMap: { [key: string]: number } = {};
const letters = formula.match(/[A-Z]/g) || [];
letters.forEach((letter, index) => {
if (nonFinalScores[index]) {
valueMap[letter] = parseFloat(nonFinalScores[index].performance);
}
});
// Result: { A: 6.0, B: 2.0 }
// 3. Replace variables in formula
let evalFormula = formula; // "(10 + A) - B"
Object.keys(valueMap).forEach(letter => {
evalFormula = evalFormula.replace(new RegExp(letter, 'g'),
valueMap[letter].toString());
});
// Result: "(10 + 6.0) - 2.0"
// 4. Evaluate using JavaScript Function constructor
const calculatedScore = new Function(`return ${evalFormula}`)();
// Result: 14.0
// 5. Insert into tfx_jury_results
await prisma.$executeRawUnsafe(`
INSERT INTO tfx_jury_results
(int_wertungenid, int_disziplinen_felderid, rel_leistung, int_versuch, int_kp)
VALUES ($1, $2, $3, $4, $5)
`, wertungenId, endwertFieldId, calculatedScore, 1, 0);
// 6. Update legacy table for compatibility
await prisma.$executeRawUnsafe(`
UPDATE tfx_wertungen_details
SET rel_leistung = $1
WHERE int_wertungenid = $2 AND int_disziplinenid = $3
`, calculatedScore, wertungenId, disciplineId);
Safety & Validation¶
Fehlerbehandlung:
try {
const calculatedScore = new Function(`return ${evalFormula}`)();
// Validation
if (isNaN(calculatedScore) || !isFinite(calculatedScore)) {
throw new Error('Invalid calculation result');
}
console.log('β
Calculated final score:', calculatedScore);
} catch (error) {
console.error('β Error calculating final score:', error);
// Falls back to manual entry or existing value
}
EinschrΓ€nkungen: - β οΈ Nur einfache mathematische Operationen (+, -, *, /, ()) - β οΈ Keine komplexen Funktionen (Math.max, if-else, etc.) - β οΈ Variablen A-Z mΓΌssen in Formel vorhanden sein
Data Flow¶
Score Capture β Database¶
βββββββββββββββββββββββ
β Score Capture UI β
β (Client) β
ββββββββββββ¬βββββββββββ
β POST /api/scores/capture
β {
β wertungenId: 116,
β fieldValues: [
β { fieldId: 220, value: 6.0 },
β { fieldId: 221, value: 2.0 }
β ]
β }
βΌ
βββββββββββββββββββββββββββ
β Server: scores.ts β
β (Backend) β
βββββββββββββββββββββββββββ€
β 1. Validate fields β
β 2. Insert into β
β tfx_jury_results β
β 3. Check for missing β
β final score β
β 4. β¨ AUTO-CALCULATE β¨ β
β 5. Insert Endwert β
β 6. Update β
β tfx_wertungen_detailsβ
ββββββββββββ¬βββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββ
β Database β
βββββββββββββββββββββββββββ€
β tfx_jury_results: β
β Row 1: Field 220 = 6 β
β Row 2: Field 221 = 2 β
β Row 3: Field 222 = 14 ββ Auto-inserted!
β β
β tfx_wertungen_details: β
β rel_leistung = 14 ββ Auto-updated!
βββββββββββββββββββββββββββ
Database β Results Display¶
βββββββββββββββββββββββββββ
β Database β
ββββββββββββ¬βββββββββββββββ
β GET /api/scores?eventId=1&squadName=zz
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Server: scores.ts β
βββββββββββββββββββββββββββββββββββββββ€
β 1. Load tfx_wertungen + details β
β 2. For each score: β
β a. Load jury results from β
β tfx_jury_results β
β b. Load formula from β
β tfx_formeln or β
β tfx_disziplinen β
β c. Extract startValue β
β 3. Map to camelCase β
ββββββββββββ¬βββββββββββββββββββββββββββ
β Response:
β {
β formula: "(10 + A) - B",
β startValue: 10,
β score: 14,
β juryResults: [
β { fieldName: "Stufe", performance: 6 },
β { fieldName: "AbzugAusf.", performance: 2 },
β { fieldName: "Endwert", performance: 14, isFinalScore: true }
β ]
β }
βΌ
βββββββββββββββββββββββββββ
β Results Page UI β
β (Client) β
βββββββββββββββββββββββββββ€
β JuryResultsDisplay: β
β β
β π 6.0 (A) - 2.0 (B) β
β = 14.00 β
β β
β Formel: (10 + A) - B β
β Ausgehend von 10 Punktenβ
βββββββββββββββββββββββββββ
API Integration¶
GET /api/scores¶
Request:
Response (relevant fields):
{
"results": [
{
"id": 116,
"participantId": 100,
"disciplineId": 111,
"score": 14.0,
"formula": "(10 + A) - B",
"startValue": 10,
"juryResults": [
{
"id": 1,
"disciplineFieldId": 220,
"fieldName": "Stufe",
"performance": 6.0,
"isFinalScore": false,
"isStartingScore": false,
"sortOrder": 1
},
{
"id": 2,
"disciplineFieldId": 221,
"fieldName": "AbzugAusf.",
"performance": 2.0,
"isFinalScore": false,
"isStartingScore": false,
"sortOrder": 2
},
{
"id": 3,
"disciplineFieldId": 222,
"fieldName": "Endwert",
"performance": 14.0,
"isFinalScore": true,
"isStartingScore": false,
"sortOrder": 3
}
]
}
]
}
Field Mapping (Database β API):
| Database Field | API Field | Type |
|----------------|-----------|------|
| d.var_formel / f.var_formel | formula | string |
| (extracted from formula) | startValue | number |
| jr.rel_leistung | juryResults[].performance | number |
| df.var_name | juryResults[].fieldName | string |
| df.bol_endwert | juryResults[].isFinalScore | boolean |
| df.int_sortierung | juryResults[].sortOrder | number |
POST /api/scores/capture¶
Request:
{
"wertungenId": 116,
"fieldValues": [
{ "fieldId": 220, "value": 6.0 },
{ "fieldId": 221, "value": 2.0 }
]
}
Logic:
1. Validate all fieldIds exist in tfx_disziplinen_felder
2. Insert/Update tfx_jury_results for each fieldId
3. Check if final score field exists but has no value
4. If yes: Auto-calculate using formula
5. Update tfx_wertungen_details.rel_leistung with final score
Response:
UI Components¶
1. JuryResultsDisplay Component¶
Location: client/src/pages/Results/components/JuryResultsDisplay.tsx
Purpose: Zeigt Jury Results mit Formel-Berechnung
Features: - π Anzeige aller nicht-finalen Felder mit Werten - π’ Formel-Darstellung mit dynamischen Symbolen (A, B, C) - β Finaler Score hervorgehoben - π Blau-Box mit StartValue Hinweis - π― Kompakt 2-zeilig fΓΌr Table View
Example Output:
Props Interface:
interface JuryResultsDisplayProps {
juryResults: JuryResult[]; // All field values
finalScore: number; // Total score
formula?: string; // Formula like "(10 + A) - B"
startValue?: number; // Starting value (e.g., 10)
isCompact?: boolean; // Compact mode for tables
}
Dynamic Symbol Extraction:
const getFormulaSymbol = (index: number): string => {
if (!formula) return String.fromCharCode(65 + index); // A, B, C
// Extract letters from formula in order
const letterMatches = formula.match(/[A-Z]/g);
return letterMatches?.[index] || String.fromCharCode(65 + index);
};
2. Results Page Integration¶
Location: client/src/pages/Results.tsx
Conditional Rendering:
{score.juryResults && score.juryResults.length > 0 ? (
<JuryResultsDisplay
juryResults={score.juryResults}
finalScore={score.score}
formula={score.formula}
startValue={score.startValue}
isCompact={true}
/>
) : (
<span className="text-lg font-semibold">
{score.score.toFixed(2)}
</span>
)}
3. Score Capture Page¶
Location: client/src/pages/ScoreCapture.tsx
Features:
- Multi-field input fΓΌr jedes definierte Feld
- Automatische Feld-Liste basierend auf tfx_disziplinen_felder
- Real-time Validierung
- Automatische Berechnung beim Speichern
Configuration¶
Central Setting¶
Location: Configuration Page β Score Capture Section
Setting:
{
key: 'useJuryResults',
label: 'Jury-Results verwenden',
description: 'Feldspezifische Jury-Results in Wertungserfassung, Ergebnisanzeige, Live-Results und PDF-Export verwenden',
type: 'boolean',
value: true // DEFAULT: ENABLED
}
β οΈ Warning Box:
WICHTIG: Datenbankstruktur
Diese Einstellung darf NICHT wΓ€hrend eines aktiven Wettkampfes geΓ€ndert werden!
β’ Jury-Results verwenden unterschiedliche Datenbanktabellen
β’ Γnderungen wΓ€hrend laufendem Wettkampf fΓΌhren zu Dateninkonsistenz
β’ Standard: Aktiviert (empfohlen)
Storage¶
Settings werden gespeichert in:
- Client: localStorage β appSettings.scoreCapture.useJuryResults
- Server: Nutzt nur Client-Config (keine Server-side Einstellung nΓΆtig)
Migration Path¶
Von altem System (nur tfx_wertungen_details) zu Jury Results:
- β
useJuryResults = truesetzen - β
Felder in
tfx_disziplinen_felderdefinieren - β
Formel in
tfx_formelnodertfx_disziplinen.var_formelhinterlegen - β Beim nΓ€chsten Score-Eintrag: Automatisch Jury Results erstellt
- β
tfx_wertungen_details.rel_leistungwird parallel aktualisiert (KompatibilitΓ€t!)
ZurΓΌck zu altem System:
β οΈ NUR zwischen WettkΓ€mpfen!
1. useJuryResults = false setzen
2. System verwendet wieder tfx_wertungen_details.rel_leistung
3. Jury Results bleiben in DB (werden nicht gelΓΆscht)
Troubleshooting¶
Problem: "Formula not loading"¶
Symptom: formula: null in API response
Checks:
1. β
Hat Disziplin int_formelid gesetzt?
-
β Existiert Formel in
tfx_formeln? -
β Oder ist Formel direkt in Disziplin?
-
β Server-Logs prΓΌfen:
Solution: Siehe Formula System fΓΌr Details zur Formel-Speicherung
Problem: "Endwert not calculated automatically"¶
Symptom: Nur Stufe + Abzug gespeichert, kein Endwert
Checks:
1. β
Existiert Feld mit bol_endwert = true?
-
β Ist Formel geladen? (siehe oben)
-
β Sind alle nicht-finalen Felder befΓΌllt?
-
β Server-Logs prΓΌfen:
Solution:
- Falls Feld fehlt: In tfx_disziplinen_felder anlegen mit bol_endwert = true
- Falls Formel fehlt: Siehe Formula System
- Falls Felder fehlen: Score Capture nochmal aufrufen
Problem: "DATABASE_URL not found (PM2)"¶
Symptom:
PrismaClientInitializationError:
error: Error validating datasource `db`:
the URL must start with the protocol `postgresql://`
Cause: PM2 liest .env nicht automatisch
Solution: DATABASE_URL in ecosystem.config.js eintragen:
{
name: 'turnfix-server',
env: {
DATABASE_URL: 'postgresql://user:pass@localhost:5432/dbname',
PORT: 3001,
DEBUG: 'true'
}
}
Problem: "Formula in response but not displayed in UI"¶
Symptom: API gibt formula: "(10 + A) - B" zurΓΌck, aber UI zeigt nichts
Checks:
1. β
Client-State aktualisiert?
- Hard-Refresh: Ctrl+Shift+R
- Check Network Tab: Formula in response?
-
β Component rendering conditional?
-
β TypeScript types erweitert?
Problem: "Wrong final score calculated"¶
Symptom: Endwert = 13.3 statt 14.0
Possible Causes: 1. Alte Daten: Endwert wurde VOR Auto-Calculation gespeichert - Solution: Endwert-Eintrag lΓΆschen, GET Request β Auto-Recalculation
- Falsche Formel: Disziplin verwendet falsche Formel
- Check:
SELECT var_formel FROM tfx_formeln WHERE int_formelid = ... -
Solution: Formel korrigieren oder richtige Formel zuweisen
-
Falsche Werte: Eingabe-Felder haben falsche Werte
- Check:
SELECT * FROM tfx_jury_results WHERE int_wertungenid = 116 -
Solution: Werte korrigieren in Score Capture
-
Variable Mapping falsch: Reihenfolge der Felder stimmt nicht
- Check:
SELECT * FROM tfx_disziplinen_felder WHERE int_disziplinenid = 111 ORDER BY int_sortierung - Solution:
int_sortierungkorrigieren
Development Notes¶
Server Implementation Details¶
Key Files:
- server/src/routes/scores.ts - Main GET /api/scores endpoint with auto-calculation
- server/src/routes/disciplineFields.ts - Field definitions API
- server/src/routes/formulas.ts - Formula lookup API
Important Functions:
// Formula loading (lines ~180-210)
if (disciplineId) {
const formulaResult = await prisma.$queryRawUnsafe(`
SELECT
d.var_formel as "disciplineFormula",
f.var_formel as "tableFormula"
FROM tfx_disziplinen d
LEFT JOIN tfx_formeln f ON d.int_formelid = f.int_formelid
WHERE d.int_disziplinenid = $1
`, disciplineId);
formula = formulaResult[0].tableFormula || formulaResult[0].disciplineFormula;
}
// Auto-calculation (lines ~220-280)
if (needsEndwertCalculation && formula && endwertFieldId) {
const valueMap = buildValueMap(juryResults, formula);
const evalFormula = replaceVariables(formula, valueMap);
const calculatedScore = evaluate(evalFormula);
await insertJuryResult(wertungenId, endwertFieldId, calculatedScore);
await updateWertungenDetails(wertungenId, disciplineId, calculatedScore);
}
Client Implementation Details¶
Key Files:
- client/src/pages/Results/components/JuryResultsDisplay.tsx - Display component
- client/src/pages/Results.tsx - Integration in results table
- client/src/pages/Configuration.tsx - Central toggle
State Management:
// Results page loads scores with jury results
const { data: scores } = useQuery(['/api/scores', filters], {
queryKey: ['/api/scores', eventId, squadName],
queryFn: () => fetchScores(eventId, squadName)
});
// Each score has juryResults array
scores.results.forEach(score => {
if (score.juryResults && score.juryResults.length > 0) {
// Display with JuryResultsDisplay component
}
});
Future Enhancements¶
Planned Features¶
- Live Score Calculation Preview
- Show calculated final score in real-time wΓ€hrend Score Capture
-
Validation feedback bevor Speichern
-
PDF Export with Jury Results
- Extended result sheets mit allen Feld-Werten
-
Formula display in Urkunden
-
Complex Formula Support
- Support fΓΌr
Math.max(),Math.min() - Conditional logic:
if (A > 10) then X else Y -
Bonus/Penalty calculations
-
Jury Member Assignment
- Tracking welcher Juror welches Feld bewertet hat
-
Multi-jury averaging fΓΌr fair scoring
-
Historical Tracking
- Γnderungshistorie fΓΌr Jury Results
- Audit trail: Wer hat wann welchen Wert geΓ€ndert
Known Limitations¶
- Formula Syntax: Nur einfache math (+ - * / ())
- Variable Names: Nur A-Z (26 Felder maximum)
- No Field Dependencies: Felder kΓΆnnen sich nicht gegenseitig referenzieren
- Single Attempt: Derzeit nur 1 Versuch pro Feld (int_versuch = 1)
Appendix: Example Data¶
Example 1: Luis Bader (Boden m. P1-P9)¶
-- Discipline
SELECT * FROM tfx_disziplinen WHERE int_disziplinenid = 111;
-- Result: "Boden m. P1-P9", int_formelid = 4
-- Formula
SELECT * FROM tfx_formeln WHERE int_formelid = 4;
-- Result: "P-Wettkampf", "(10 + A) - B"
-- Fields
SELECT * FROM tfx_disziplinen_felder WHERE int_disziplinenid = 111;
-- Results:
-- 220: "Stufe", sortierung=1, endwert=false
-- 221: "AbzugAusf.", sortierung=2, endwert=false
-- 222: "Endwert", sortierung=3, endwert=TRUE
-- Jury Results
SELECT * FROM tfx_jury_results WHERE int_wertungenid = 116;
-- Results:
-- Field 220: 6.0
-- Field 221: 2.0
-- Field 222: 14.0 β Auto-calculated!
-- Wertungen Details (Legacy)
SELECT * FROM tfx_wertungen_details
WHERE int_wertungenid = 116 AND int_disziplinenid = 111;
-- Result: rel_leistung = 14.0 β Auto-updated!
Example 2: API Response Structure¶
{
"results": [
{
"id": 116,
"participantId": 100,
"disciplineId": 111,
"competitionId": 14,
"score": 14.0,
"formula": "(10 + A) - B",
"startValue": 10,
"participant": {
"firstName": "Luis",
"lastName": "Bader"
},
"discipline": {
"name": "Boden m. P1-P9"
},
"juryResults": [
{
"id": 1,
"disciplineFieldId": 220,
"fieldName": "Stufe",
"performance": 6.0,
"isFinalScore": false,
"sortOrder": 1
},
{
"id": 2,
"disciplineFieldId": 221,
"fieldName": "AbzugAusf.",
"performance": 2.0,
"isFinalScore": false,
"sortOrder": 2
},
{
"id": 3,
"disciplineFieldId": 222,
"fieldName": "Endwert",
"performance": 14.0,
"isFinalScore": true,
"sortOrder": 3
}
]
}
],
"pagination": {
"total": 1,
"limit": 200,
"offset": 0,
"hasMore": false
}
}
Summary¶
Jury Results System ist ein vollstΓ€ndig implementiertes, feldbasiertes Bewertungssystem mit:
β Flexible Feld-Definitionen pro Disziplin β Automatische Endwert-Berechnung mit Formeln β Transparente Anzeige in UI mit Formel-Rendering β Legacy-KompatibilitΓ€t mit tfx_wertungen_details β Zentrale Konfiguration mit Warnung vor Γnderungen β Robuste Fehlerbehandlung und Logging
Das System ist produktionsbereit und wird bereits verwendet. Diese Dokumentation dient als Referenz fΓΌr weitere Entwicklung und Troubleshooting.
Document Version: 1.0
Last Updated: 2026-01-28
Author: AI Assistant (GitHub Copilot)
Review Status: β
Complete