Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion bullet/bullet_admin/forms/review.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@ def clean(self):


def get_review_formset(team: Team):
problems = team.venue.category.problems.count()
problems = (
team.venue.category.competition.problem_count
- team.venue.category.first_problem
+ 1
)
return formset_factory(ReviewForm, min_num=problems, max_num=problems)
6 changes: 0 additions & 6 deletions bullet/bullet_admin/urls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
archive,
auth,
category,
categoryproblems,
competition,
content,
education,
Expand Down Expand Up @@ -277,11 +276,6 @@
education.SchoolCreateView.as_view(),
name="school_create",
),
path(
"problems/generate/",
categoryproblems.ProblemsGenerateView.as_view(),
name="problems_generate",
),
path("gallery/albums/", albums.AlbumListView.as_view(), name="album_list"),
path("gallery/albums/new/", albums.AlbumCreateView.as_view(), name="album_create"),
path(
Expand Down
45 changes: 0 additions & 45 deletions bullet/bullet_admin/views/categoryproblems.py

This file was deleted.

44 changes: 20 additions & 24 deletions bullet/bullet_admin/views/scanning.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
mark_problem_unsolved,
)
from problems.logic.scanner import ScannedBarcode, parse_barcode, save_scan
from problems.models import CategoryProblem, Problem, ScannerLog, SolvedProblem
from problems.models import Problem, ScannerLog, SolvedProblem
from users.models import Team

from bullet_admin.forms.review import get_review_formset
Expand Down Expand Up @@ -161,11 +161,9 @@ def scan(self, request):
if scanned_barcode.team.is_reviewed:
raise ValueError("The team was already reviewed.")

problem_count = CategoryProblem.objects.filter(
category=scanned_barcode.team.venue.category
).count()
max_problem_number = self.venue.category.competition.problem_count
last = get_last_problem_for_team(scanned_barcode.team)
if last < problem_count:
if last < max_problem_number:
if scanned_barcode.problem_number != last + 1:
raise ValueError(
f"Expected problem number {last + 1}, got "
Expand Down Expand Up @@ -291,22 +289,22 @@ def get_form_class(self):
return get_review_formset(self.team)

def get_initial(self):
category_problems = {
cp.number: cp.problem_id
for cp in CategoryProblem.objects.filter(
category=self.team.venue.category
).order_by("number")
}
competition = get_active_competition(self.request)
problems = Problem.objects.filter(competition=competition)

solved_timestamps = {
sp.problem_id: sp.competition_time for sp in self.team.solved_problems.all()
}
initial = []

for num, id in category_problems.items():
row = {"number": num}
if id in solved_timestamps:
for problem in problems:
if problem.number < self.team.venue.category.first_problem:
continue

row = {"number": problem.number}
if problem.id in solved_timestamps:
row["is_solved"] = True
row["competition_time"] = solved_timestamps[id]
row["competition_time"] = solved_timestamps[problem.id]

initial.append(row)

Expand All @@ -318,11 +316,9 @@ def get_context_data(self, **kwargs):
return ctx

def form_valid(self, form):
category_problems: dict[int, Problem] = {
cp.number: cp.problem
for cp in CategoryProblem.objects.filter(category=self.team.venue.category)
.order_by("number")
.select_related("problem")
competition = get_active_competition(self.request)
problems: dict[int, Problem] = {
p.number: p for p in Problem.objects.filter(competition=competition)
}
solved: dict[int, SolvedProblem] = {
sp.problem_id: sp for sp in self.team.solved_problems.all()
Expand All @@ -331,9 +327,10 @@ def form_valid(self, form):
changed = False
for row in form:
num = row.cleaned_data.get("number")
if num not in category_problems:
if num not in problems or num < self.team.venue.category.first_problem:
continue
problem: Problem = category_problems[num]

problem: Problem = problems[num]
is_solved: bool = row.cleaned_data.get("is_solved")
competition_time: timedelta = row.cleaned_data.get("competition_time")

Expand Down Expand Up @@ -383,8 +380,7 @@ def post(self, request, *args, **kwargs):
continue
problem = Problem.objects.filter(
competition=team.venue.category.competition,
category_problems__category=team.venue.category,
category_problems__number=solve["problem"],
number=solve["problem"],
).first()
if not problem:
continue
Expand Down
10 changes: 3 additions & 7 deletions bullet/bullet_admin/views/venues.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from documents.generators.tearoff import TearoffGenerator, TearoffRequirementMissing
from documents.models import TexJob
from problems.logic.results import save_country_ranks, save_venue_ranks
from problems.models import CategoryProblem, Problem
from problems.models import Problem
from users.logic import get_venue_waiting_list, move_eligible_teams
from users.models import Team

Expand Down Expand Up @@ -205,14 +205,10 @@ def get_form_kwargs(self):
kw = super().get_form_kwargs()
competition = get_active_competition(self.request)
problem_count = Problem.objects.filter(competition=competition).count()
first_problem = (
CategoryProblem.objects.filter(category=self.venue.category)
.order_by("number")
.first()
)

kw["problems"] = problem_count
kw["venue"] = self.venue
kw["first_problem"] = first_problem.number if first_problem else 1
kw["first_problem"] = self.venue.category.first_problem
return kw

def form_valid(self, form):
Expand Down
3 changes: 2 additions & 1 deletion bullet/competitions/models/venues.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
from typing import TYPE_CHECKING

from bullet_admin.utils import get_active_competition
Expand Down Expand Up @@ -136,7 +137,7 @@ def contact_email(self):
return self.email

@property
def start_time(self):
def start_time(self) -> datetime:
if self.local_start:
return self.local_start
return self.category.competition.competition_start
Expand Down
8 changes: 4 additions & 4 deletions bullet/problems/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
from problems import models


class CategoryProblemAdminInline(admin.TabularInline):
model = models.CategoryProblem
class ProblemStatementInline(admin.StackedInline):
model = models.ProblemStatement


@admin.register(models.Problem)
class ProblemAdmin(admin.ModelAdmin):
list_filter = ("competition", "competition__branch")
list_display = ("name", "competition")
inlines = (CategoryProblemAdminInline,)
list_display = ("number", "competition")
inlines = (ProblemStatementInline,)


@admin.register(models.ProblemStatement)
Expand Down
3 changes: 1 addition & 2 deletions bullet/problems/factories/generate.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from competitions.models import Competition

from problems.factories.problems import CategoryProblemFactory, ProblemFactory
from problems.factories.problems import ProblemFactory
from problems.models import Problem


def create_problems(competition: Competition) -> list[Problem]:
problems = ProblemFactory.create_batch(42, competition=competition)
CategoryProblemFactory.create_batch(42)
return problems
16 changes: 3 additions & 13 deletions bullet/problems/factories/problems.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import factory
from competitions.models import Category, Competition
from competitions.models import Competition
from factory.django import DjangoModelFactory

from problems.models import CategoryProblem, Problem
from problems.models import Problem


class ProblemFactory(DjangoModelFactory):
class Meta:
model = Problem

name = factory.Faker("sentence")
competition = factory.Faker("random_element", elements=Competition.objects.all())


class CategoryProblemFactory(DjangoModelFactory):
class Meta:
model = CategoryProblem
django_get_or_create = ("problem", "category", "number")

problem = factory.Faker("random_element", elements=Problem.objects.all())
category = factory.Faker("random_element", elements=Category.objects.all())
number = factory.Faker("random_int")
competition = factory.Faker("random_element", elements=Competition.objects.all())
3 changes: 3 additions & 0 deletions bullet/problems/logic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@


def get_last_problem_for_team(team: Team):
"""
Return the number of the highest problem the team should have on their table.
"""
return (
SolvedProblem.objects.filter(team=team).count()
+ team.venue.category.problems_per_team
Expand Down
50 changes: 16 additions & 34 deletions bullet/problems/logic/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
from django_rq import job
from users.models import Team

from problems.models import CategoryProblem, ResultRow, SolvedProblem
from problems.models import ResultRow, SolvedProblem


def get_results(
team_filter: Q,
time: timedelta = None,
time: timedelta | None = None,
) -> QuerySet[ResultRow]:
rows = ResultRow.objects.filter(team__is_disqualified=False)

Expand All @@ -33,12 +33,14 @@ def get_results(
)


def get_venue_results(venue: Venue, time: timedelta = None) -> QuerySet[ResultRow]:
def get_venue_results(
venue: Venue, time: timedelta | None = None
) -> QuerySet[ResultRow]:
return get_results(Q(team__venue=venue), time)


def get_country_results(
country: str | Country, category: Category, time: timedelta = None
country: str | Country, category: Category, time: timedelta | None = None
) -> QuerySet[ResultRow]:
return get_results(
Q(
Expand All @@ -51,7 +53,7 @@ def get_country_results(


def get_category_results(
category: Category, time: timedelta = None
category: Category, time: timedelta | None = None
) -> QuerySet[ResultRow]:
return get_results(
Q(team__venue__category=category, team__venue__is_isolated=False),
Expand All @@ -70,7 +72,7 @@ class ResultsTime:
def results_time(
competition: Competition,
realtime: datetime,
start_time: datetime = None,
start_time: datetime | None = None,
is_admin: bool = False,
) -> ResultsTime:
if not start_time:
Expand All @@ -97,18 +99,13 @@ def _set_solved_problems(rr: ResultRow):
problems = (
SolvedProblem.objects.select_for_update()
.filter(team=rr.team, competition_time__lte=rr.competition_time)
.values_list("problem")
)
solved_problems = set(
CategoryProblem.objects.filter(
problem__in=problems, category=rr.team.venue.category
).values_list("number", flat=True)
.values_list("problem__number", flat=True)
)

rr.solved_count = len(solved_problems)
rr.solved_count = len(problems)

solved_bin = 0
for p in solved_problems:
for p in problems:
solved_bin |= 1 << (p - 1)

rr.solved_problems = solved_bin.to_bytes(16, "big")
Expand Down Expand Up @@ -139,35 +136,20 @@ def squash_results(competition: Competition | int):

teams = Team.objects.filter(venue__category__competition=competition)
for team in teams:
problems = (
SolvedProblem.objects.filter(team=team)
.values_list("problem")
.order_by("-competition_time")
)
last_problem = (
SolvedProblem.objects.filter(team=team)
.order_by("-competition_time")
.first()
)
solved_problems = set(
CategoryProblem.objects.filter(
problem__in=problems, category=team.venue.category
).values_list("number", flat=True)
)

if not last_problem:
continue

result_row = ResultRow()
result_row.team = team
result_row.solved_count = len(solved_problems)

solved_bin = 0
for p in solved_problems:
solved_bin |= 1 << (p - 1)
result_row.solved_problems = solved_bin.to_bytes(16, "big")

result_row.competition_time = last_problem.competition_time
result_row = ResultRow(
team=team,
competition_time=last_problem.competition_time,
)
_set_solved_problems(result_row)
result_row.save()


Expand Down
Loading