164 lines
3.8 KiB
Markdown
164 lines
3.8 KiB
Markdown
# qualifiers-seeding
|
|
|
|
Automatisiert das Team-Seeding für UEFA-Qualifikationsturniere.
|
|
|
|
## Trigger
|
|
|
|
- Neue Saison erstellen
|
|
- Team-Import
|
|
- "seeding", "seed teams", "neue saison"
|
|
|
|
## Kontext
|
|
|
|
Das Seeding erstellt die Node-Struktur und weist Teams basierend auf UEFA-Koeffizienten zu. Die Hauptfunktion ist `seed_nodes25()` in [qualifiers/helpers.py](qualifiers/helpers.py).
|
|
|
|
## Regeln
|
|
|
|
1. **Node-Struktur folgt UEFA-Format**:
|
|
```
|
|
Champions Path:
|
|
└── UCL: Q1 → Q2 → Q3 → PO
|
|
└── UEL: Q2 → Q3 → PO
|
|
└── UECL: Q3 → PO
|
|
|
|
League Path:
|
|
└── UCL: Q2 → Q3 → PO
|
|
└── UEL: Q3 → PO
|
|
```
|
|
|
|
2. **Koeffizienten-Ranking**:
|
|
```python
|
|
# Formel: Koeffizient * 100000 - Position
|
|
# Höherer Wert = besseres Ranking
|
|
coefficients[team.id] = float(100000 * team.coefficient - team.position)
|
|
```
|
|
|
|
3. **State-Transitions**:
|
|
- `none` (0) → `seeded` (1): Nach `seeded_teams.set()`
|
|
- Vorgänger-Nodes müssen mindestens State `draw` (3) haben
|
|
|
|
4. **GroupsOfSize automatisch erstellen**:
|
|
```python
|
|
node.create_groupsofsize()
|
|
# Erstellt: 4, 6, 8, 10, 12, 20 mit number=0
|
|
```
|
|
|
|
## Beispiel: Nodes für neue Saison erstellen
|
|
|
|
```python
|
|
from qualifiers.models import QPath, QTier, QStage, QNode
|
|
|
|
# Path erstellen
|
|
cp = QPath.objects.create(scenario=scenario, name='Champions Path')
|
|
|
|
# Tiers erstellen
|
|
ucl = QTier.objects.create(
|
|
scenario=scenario,
|
|
path=cp,
|
|
name='UCL',
|
|
ypos=3,
|
|
optimize_prio=50
|
|
)
|
|
|
|
# Stages erstellen
|
|
q1 = QStage.objects.create(scenario=scenario, path=cp, name='Q1', xpos=1)
|
|
q2 = QStage.objects.create(scenario=scenario, path=cp, name='Q2', xpos=2)
|
|
|
|
# Nodes erstellen mit Navigation
|
|
node_q1 = QNode.objects.create(
|
|
scenario=scenario,
|
|
name='Q1',
|
|
tier=ucl,
|
|
stage=q1,
|
|
type=0, # Grouping
|
|
state=1, # Seeded
|
|
)
|
|
|
|
node_q2 = QNode.objects.create(
|
|
scenario=scenario,
|
|
name='Q2',
|
|
tier=ucl,
|
|
stage=q2,
|
|
type=0,
|
|
state=0, # Wartet auf Q1
|
|
)
|
|
|
|
# Navigation setzen
|
|
node_q1.winners = node_q2
|
|
node_q1.save()
|
|
```
|
|
|
|
## Beispiel: Teams seeden
|
|
|
|
```python
|
|
from scheduler.models import Team
|
|
from qualifiers.models import QNode
|
|
|
|
node = QNode.objects.get(
|
|
scenario=scenario,
|
|
tier__name='UCL',
|
|
stage__name='Q1'
|
|
)
|
|
|
|
# Top 16 Teams nach Koeffizient
|
|
teams = Team.objects.filter(
|
|
season=scenario.season,
|
|
active=True
|
|
).order_by('-coefficient')[:16]
|
|
|
|
# Teams zuweisen
|
|
node.seeded_teams.set(teams)
|
|
node.state = 1 # Seeded
|
|
node.save()
|
|
|
|
# Gruppengrößen erstellen
|
|
node.create_groupsofsize()
|
|
```
|
|
|
|
## Beispiel: Upcomers von Vorgänger-Node
|
|
|
|
```python
|
|
# Nach Q1 Draw: Teams für Q2 sammeln
|
|
q1_node = QNode.objects.get(scenario=scenario, tier__name='UCL', stage__name='Q1')
|
|
q2_node = QNode.objects.get(scenario=scenario, tier__name='UCL', stage__name='Q2')
|
|
|
|
# Upcomers sind finalisierte Matches aus Q1
|
|
upcomers = q2_node.upcomers(scenario)
|
|
|
|
# Format:
|
|
# [
|
|
# {'id': 123, 'team': <Team>, 'coeff': 45.5, 'countries': ['GER'], ...},
|
|
# {'id': 456, 'match': <QMatch>, 'coeff': 30.2, 'countries': ['ESP', 'ITA'], ...},
|
|
# ]
|
|
```
|
|
|
|
## Validierung
|
|
|
|
```python
|
|
def validate_node_seeding(node):
|
|
"""Prüft Seeding-Voraussetzungen."""
|
|
errors = []
|
|
|
|
# Mindestens 2 Teams
|
|
total_teams = node.nTeams(node.upcomers(node.scenario))
|
|
if total_teams < 2:
|
|
errors.append("Mindestens 2 Teams erforderlich")
|
|
|
|
# Gerade Anzahl
|
|
if total_teams % 2 != 0:
|
|
errors.append("Ungerade Teamanzahl")
|
|
|
|
# Alle Vorgänger finalisiert
|
|
for pred in node.qnode_winners.all() | node.qnode_losers.all():
|
|
if pred.current_state(node.scenario) < 3:
|
|
errors.append(f"Vorgänger {pred.which()} nicht im Draw-State")
|
|
|
|
return errors
|
|
```
|
|
|
|
## Referenz-Dateien
|
|
|
|
- [qualifiers/helpers.py](qualifiers/helpers.py) - seed_nodes25()
|
|
- [qualifiers/uefadigitalapi/seed_2025.py](qualifiers/uefadigitalapi/seed_2025.py)
|
|
- [docs/qualifiers/workflows/seeding.md](docs/qualifiers/workflows/seeding.md)
|