Added refs
This commit is contained in:
@@ -173,6 +173,106 @@ def update_schedule_times(db: Session, t: models.Tournament):
|
||||
else:
|
||||
break
|
||||
|
||||
# ==========================================
|
||||
# --- DYNAMIC REFEREE ASSIGNMENT ---
|
||||
# ==========================================
|
||||
all_matches = sorted(list(matches), key=lambda x: (x.start_time, x.court_id))
|
||||
duty_counts = defaultdict(int)
|
||||
active_refs = []
|
||||
|
||||
def is_busy(outcome_tuple, source_m, start, end):
|
||||
next_m_id = source_m.winner_next_match_id if outcome_tuple[0] == "W" else source_m.loser_next_match_id
|
||||
if next_m_id:
|
||||
next_m = match_map[next_m_id]
|
||||
next_start = next_m.start_time
|
||||
if next_start:
|
||||
next_end = next_start + timedelta(minutes=t.duration)
|
||||
if not (next_end <= start or next_start >= end):
|
||||
return True
|
||||
for r_outcome, r_start, r_end in active_refs:
|
||||
if r_outcome == outcome_tuple:
|
||||
if not (r_end <= start or r_start >= end):
|
||||
return True
|
||||
return False
|
||||
|
||||
for m in all_matches:
|
||||
if not m.start_time:
|
||||
continue
|
||||
|
||||
m_start = m.start_time
|
||||
m_end = m_start + timedelta(minutes=t.duration)
|
||||
|
||||
if m.bracket_type == BracketTypes.FINALS:
|
||||
prev_final = next((p for p in all_matches if p.bracket_type == BracketTypes.FINALS and p.winner_next_match_id == m.id), None)
|
||||
|
||||
if prev_final:
|
||||
m.ref_team_id = prev_final.ref_team_id
|
||||
m.ref_label = prev_final.ref_label
|
||||
|
||||
identifier = f"TEAM_{m.ref_team_id}" if m.ref_team_id else prev_final.ref_label
|
||||
active_refs.append((identifier, m_start, m_end))
|
||||
continue
|
||||
|
||||
best_outcome = None
|
||||
best_score = float('inf')
|
||||
|
||||
for prev_m in all_matches:
|
||||
if not prev_m.start_time:
|
||||
continue
|
||||
prev_end = prev_m.start_time + timedelta(minutes=t.duration)
|
||||
|
||||
if prev_end <= m_start:
|
||||
l_outcome = ("L", prev_m.id)
|
||||
if not is_busy(l_outcome, prev_m, m_start, m_end):
|
||||
wait_mins = (m_start - prev_end).total_seconds() / 60.0
|
||||
score = (duty_counts[l_outcome] * 120) + wait_mins
|
||||
if prev_m.court_id == m.court_id:
|
||||
score -= 30
|
||||
if score < best_score:
|
||||
best_score = score
|
||||
best_outcome = (l_outcome, prev_m)
|
||||
|
||||
w_outcome = ("W", prev_m.id)
|
||||
if not is_busy(w_outcome, prev_m, m_start, m_end):
|
||||
wait_mins = (m_start - prev_end).total_seconds() / 60.0
|
||||
score = (duty_counts[w_outcome] * 120) + wait_mins + 60
|
||||
if prev_m.court_id == m.court_id:
|
||||
score -= 30
|
||||
if score < best_score:
|
||||
best_score = score
|
||||
best_outcome = (w_outcome, prev_m)
|
||||
|
||||
if best_outcome:
|
||||
outcome_tuple, prev_m = best_outcome
|
||||
duty_counts[outcome_tuple] += 1
|
||||
active_refs.append((outcome_tuple, m_start, m_end))
|
||||
role = "Winner" if outcome_tuple[0] == "W" else "Loser"
|
||||
m.ref_label = f"{role} of #{prev_m.match_number}"
|
||||
else:
|
||||
m.ref_label = "Staff / Volunteers"
|
||||
for future_m in all_matches:
|
||||
if future_m.start_time and future_m.start_time >= m_end:
|
||||
assigned = False
|
||||
|
||||
for t_id in [future_m.p1_team_id, future_m.p2_team_id]:
|
||||
if t_id:
|
||||
team_identifier = f"TEAM_{t_id}"
|
||||
|
||||
is_team_busy = any(
|
||||
r_outcome == team_identifier and not (r_end <= m_start or r_start >= m_end)
|
||||
for r_outcome, r_start, r_end in active_refs
|
||||
)
|
||||
|
||||
if not is_team_busy:
|
||||
m.ref_team_id = t_id
|
||||
m.ref_label = None
|
||||
active_refs.append((team_identifier, m_start, m_end))
|
||||
assigned = True
|
||||
break
|
||||
if assigned:
|
||||
break
|
||||
# ----------------------------------
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
@@ -182,6 +282,14 @@ def advance_winner(db: Session, match: models.Match, winner_id: int):
|
||||
|
||||
loser_id = match.p1_team_id if match.p1_team_id != winner_id else match.p2_team_id
|
||||
|
||||
for m in match.tournament.matches:
|
||||
if m.ref_label == f"Loser of #{match.match_number}":
|
||||
m.ref_team_id = loser_id
|
||||
db.add(m)
|
||||
elif m.ref_label == f"Winner of #{match.match_number}":
|
||||
m.ref_team_id = winner_id
|
||||
db.add(m)
|
||||
|
||||
if (
|
||||
match.bracket_type == BracketTypes.FINALS
|
||||
and match.winner_next_match
|
||||
@@ -246,6 +354,11 @@ def undo_advancement(db: Session, match: models.Match):
|
||||
if not match.winner_team_id:
|
||||
return
|
||||
|
||||
for m in match.tournament.matches:
|
||||
if m.ref_label == f"Loser of #{match.match_number}" or m.ref_label == f"Winner of #{match.match_number}":
|
||||
m.ref_team_id = None
|
||||
db.add(m)
|
||||
|
||||
winner_id = match.winner_team_id
|
||||
loser_id = match.p1_team_id if match.p1_team_id != winner_id else match.p2_team_id
|
||||
|
||||
@@ -272,3 +385,5 @@ def undo_advancement(db: Session, match: models.Match):
|
||||
clear_from_next(match.winner_next_match, match.winner_next_match_slot)
|
||||
if match.loser_next_match and loser_id:
|
||||
clear_from_next(match.loser_next_match, match.loser_next_match_slot)
|
||||
|
||||
db.commit()
|
||||
+18
-37
@@ -70,51 +70,32 @@ class Match(Base):
|
||||
|
||||
bracket_type: Mapped[BracketTypes] = mapped_column(SqlEnum(BracketTypes))
|
||||
|
||||
court_id: Mapped[Optional[int]] = mapped_column(
|
||||
ForeignKey("courts.id"), nullable=True
|
||||
)
|
||||
court_id: Mapped[Optional[int]] = mapped_column(ForeignKey("courts.id"), nullable=True)
|
||||
start_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
|
||||
|
||||
status: Mapped[MatchStatus] = mapped_column(
|
||||
SqlEnum(MatchStatus), default=MatchStatus.SCHEDULED
|
||||
)
|
||||
status: Mapped[MatchStatus] = mapped_column(SqlEnum(MatchStatus), default=MatchStatus.SCHEDULED)
|
||||
|
||||
p1_team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("teams.id"), nullable=True)
|
||||
p2_team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("teams.id"), nullable=True)
|
||||
|
||||
ref_team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("teams.id"), nullable=True)
|
||||
ref_label: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
||||
|
||||
p1_team_id: Mapped[Optional[int]] = mapped_column(
|
||||
ForeignKey("teams.id"), nullable=True
|
||||
)
|
||||
p2_team_id: Mapped[Optional[int]] = mapped_column(
|
||||
ForeignKey("teams.id"), nullable=True
|
||||
)
|
||||
sets: Mapped[list[dict]] = mapped_column(JSON, default=list)
|
||||
winner_team_id: Mapped[Optional[int]] = mapped_column(
|
||||
ForeignKey("teams.id"), nullable=True
|
||||
)
|
||||
winner_next_match_id: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("matches.id"), nullable=True
|
||||
)
|
||||
loser_next_match_id: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("matches.id"), nullable=True
|
||||
)
|
||||
winner_team_id: Mapped[Optional[int]] = mapped_column(ForeignKey("teams.id"), nullable=True)
|
||||
|
||||
winner_next_match_id: Mapped[Optional[str]] = mapped_column(ForeignKey("matches.id"), nullable=True)
|
||||
winner_next_match_slot: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
|
||||
|
||||
winner_next_match_id: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("matches.id"), nullable=True
|
||||
)
|
||||
winner_next_match_slot: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True
|
||||
)
|
||||
|
||||
loser_next_match_id: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("matches.id"), nullable=True
|
||||
)
|
||||
loser_next_match_id: Mapped[Optional[str]] = mapped_column(ForeignKey("matches.id"), nullable=True)
|
||||
loser_next_match_slot: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
|
||||
|
||||
tournament: Mapped["Tournament"] = relationship(back_populates="matches")
|
||||
court: Mapped[Optional["Court"]] = relationship()
|
||||
p1_team: Mapped["Team"] = relationship("Team", foreign_keys=[p1_team_id])
|
||||
p2_team: Mapped["Team"] = relationship("Team", foreign_keys=[p2_team_id])
|
||||
winner_next_match: Mapped["Match"] = relationship(
|
||||
"Match", remote_side=[id], foreign_keys=[winner_next_match_id]
|
||||
)
|
||||
loser_next_match: Mapped["Match"] = relationship(
|
||||
"Match", remote_side=[id], foreign_keys=[loser_next_match_id]
|
||||
)
|
||||
|
||||
ref_team: Mapped[Optional["Team"]] = relationship("Team", foreign_keys=[ref_team_id])
|
||||
|
||||
winner_next_match: Mapped["Match"] = relationship("Match", remote_side=[id], foreign_keys=[winner_next_match_id])
|
||||
loser_next_match: Mapped["Match"] = relationship("Match", remote_side=[id], foreign_keys=[loser_next_match_id])
|
||||
@@ -49,6 +49,10 @@ class MatchOut(BaseModel):
|
||||
p1_team_id: int | None = None
|
||||
p2_team_id: int | None = None
|
||||
winner_team_id: int | None = None
|
||||
|
||||
ref_team_id: int | None = None
|
||||
ref_label: str | None = None
|
||||
ref_team: TeamSchema | None = None
|
||||
|
||||
winner_next_match_id: str | None = None
|
||||
winner_next_match_slot: int | None = None
|
||||
|
||||
Reference in New Issue
Block a user