Update draw with the new team repartition
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
parent
e9ae1fcb60
commit
df036ba384
|
@ -152,7 +152,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
|
|||
|
||||
try:
|
||||
# Parse format from string
|
||||
fmt: list[int] = sorted(map(int, fmt.split('+')), reverse=True)
|
||||
fmt: list[int] = sorted(map(int, fmt.split('+')))
|
||||
except ValueError:
|
||||
return await self.alert(_("Invalid format"), 'danger')
|
||||
|
||||
|
@ -416,10 +416,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
|
|||
|
||||
# For each pool of size N, put the N next teams into this pool
|
||||
async for p in Pool.objects.filter(round_id=self.tournament.draw.current_round_id).order_by('letter').all():
|
||||
# Fetch the N teams, then order them in a new order for the passages inside the pool
|
||||
# We multiply the dice scores by 27 mod 100 (which order is 20 mod 100) for this new order
|
||||
# This simulates a deterministic shuffle
|
||||
pool_tds = sorted(tds_copy[:p.size], key=lambda td: (td.passage_dice * 27) % 100)
|
||||
# Fetch the N teams
|
||||
pool_tds = tds_copy[:p.size].copy()
|
||||
# Remove the head
|
||||
tds_copy = tds_copy[p.size:]
|
||||
for i, td in enumerate(pool_tds):
|
||||
|
@ -428,34 +426,64 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
|
|||
td.passage_index = i
|
||||
await td.asave()
|
||||
|
||||
# The passages of the second round are determined from the scores of the dices
|
||||
# The team that has the lowest dice score goes to the first pool, then the team
|
||||
# that has the second-lowest score goes to the second pool, etc.
|
||||
# This also determines the passage order, in the natural order this time.
|
||||
# If there is a 5-teams pool, we force the last team to be in the first pool,
|
||||
# which is this specific pool since they are ordered by decreasing size.
|
||||
# This is not true for the final tournament, which considers the scores of the
|
||||
# first round.
|
||||
# The passages of the second round are determined from the order of the passages of the first round.
|
||||
# We order teams by increasing passage index, and then by decreasing pool number.
|
||||
# We keep teams that were at the last position in a 5-teams pool apart, as "jokers".
|
||||
# Then, we fill pools one team by one team.
|
||||
# As we fill one pool for the second round, we check if we can place a joker in it.
|
||||
# We can add a joker team if there is not already a team in the pool that was in the same pool
|
||||
# in the first round, and such that the number of such jokers is exactly the free space of the current pool.
|
||||
# Exception: if there is one only pool with 5 teams, we exchange the first and the last teams of the pool.
|
||||
if not self.tournament.final:
|
||||
tds_copy = tds.copy()
|
||||
tds_copy = sorted(tds, key=lambda td: (td.passage_index, -td.pool.letter,))
|
||||
jokers = [td for td in tds if td.passage_index == 4]
|
||||
round2 = await self.tournament.draw.round_set.filter(number=2).aget()
|
||||
round2_pools = [p async for p in Pool.objects.filter(round__draw__tournament=self.tournament, round=round2)
|
||||
.order_by('letter').all()]
|
||||
current_pool_id, current_passage_index = 0, 0
|
||||
for i, td in enumerate(tds_copy):
|
||||
if i == len(tds) - 1 and round2_pools[0].size == 5:
|
||||
current_pool_id = 0
|
||||
current_passage_index = 4
|
||||
|
||||
td2 = await TeamDraw.objects.filter(participation=td.participation, round=round2).aget()
|
||||
td2.pool = round2_pools[current_pool_id]
|
||||
td2.passage_index = current_passage_index
|
||||
current_pool_id += 1
|
||||
if current_pool_id == len(round2_pools):
|
||||
current_pool_id = 0
|
||||
if len(round2_pools) == 1 and len(tds) == 5:
|
||||
# Exchange teams 1 and 5 if there is only one pool with 5 teams
|
||||
if i == 0:
|
||||
td2.passage_index = 4
|
||||
elif i == 4:
|
||||
td2.passage_index = 0
|
||||
current_passage_index += 1
|
||||
await td2.asave()
|
||||
|
||||
valid_jokers = []
|
||||
# A joker is valid if it was not in the same pool in the first round
|
||||
# as a team that is already in the current pool in the second round
|
||||
for joker in jokers:
|
||||
async for td2 in round2_pools[current_pool_id].teamdraw_set.all():
|
||||
if await joker.pool.teamdraw_set.filter(participation_id=td2.participation_id).aexists():
|
||||
break
|
||||
else:
|
||||
valid_jokers.append(joker)
|
||||
|
||||
# We can add a joker if there is exactly enough free space in the current pool
|
||||
if valid_jokers and current_passage_index + len(valid_jokers) == td2.pool.size:
|
||||
for joker in valid_jokers:
|
||||
tds_copy.remove(joker)
|
||||
jokers.remove(joker)
|
||||
td2_joker = await TeamDraw.objects.filter(participation_id=joker.participation_id,
|
||||
round=round2).aget()
|
||||
td2_joker.pool = round2_pools[current_pool_id]
|
||||
td2_joker.passage_index = current_passage_index
|
||||
current_passage_index += 1
|
||||
await td2_joker.asave()
|
||||
jokers = []
|
||||
|
||||
current_passage_index = 0
|
||||
current_pool_id += 1
|
||||
|
||||
if current_passage_index == round2_pools[current_pool_id].size:
|
||||
current_passage_index = 0
|
||||
current_pool_id += 1
|
||||
|
||||
# The current pool is the first pool of the current (first) round
|
||||
pool = await Pool.objects.filter(round=self.tournament.draw.current_round, letter=1).aget()
|
||||
self.tournament.draw.current_round.current_pool = pool
|
||||
|
|
|
@ -93,7 +93,7 @@ class TeamAdmin(admin.ModelAdmin):
|
|||
class ParticipationAdmin(admin.ModelAdmin):
|
||||
list_display = ('team', 'tournament', 'valid', 'final',)
|
||||
search_fields = ('team__name', 'team__trigram',)
|
||||
list_filter = ('valid',)
|
||||
list_filter = ('valid', 'tournament',)
|
||||
autocomplete_fields = ('team', 'tournament',)
|
||||
inlines = (SolutionInline, SynthesisInline,)
|
||||
|
||||
|
|
|
@ -407,7 +407,7 @@ class Tournament(models.Model):
|
|||
def best_format(self):
|
||||
n = len(self.participations.filter(valid=True).all())
|
||||
fmt = [n] if n <= 5 else [3] * (n // 3 - 1) + [3 + n % 3]
|
||||
return '+'.join(map(str, sorted(fmt, reverse=True)))
|
||||
return '+'.join(map(str, sorted(fmt)))
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy("participation:tournament_detail", args=(self.pk,))
|
||||
|
|
Loading…
Reference in New Issue