Skip to content

Commit 63aeafd

Browse files
authored
feat: school & enrollment check (#53)
1 parent a01f5b7 commit 63aeafd

5 files changed

Lines changed: 57 additions & 13 deletions

File tree

seminare/problems/logic.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from seminare.problems.models import Problem, ProblemSet
66
from seminare.rules import Chip
7-
from seminare.users.models import Enrollment, User
7+
from seminare.users.models import User
88

99

1010
def inject_user_score(
@@ -17,7 +17,7 @@ def inject_user_score(
1717

1818
rule_engine = problem_set.get_rule_engine()
1919

20-
enrollment = Enrollment.objects.filter(user=user, problem_set=problem_set).first()
20+
enrollment = user.get_enrollment(problem_set)
2121

2222
if enrollment is None:
2323
return problems

seminare/problems/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ def get_rule_engine(self) -> RuleEngine:
5757
class_ = get_rule_engine_class(self.rule_engine)
5858
return class_(self)
5959

60+
@property
61+
def is_running(self) -> bool:
62+
now = timezone.now()
63+
return self.start_date <= now <= self.end_date
64+
6065
def close(self):
6166
"""Closes and finalizes the problem set."""
6267

seminare/problems/templates/problems/_submit_sidebar.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@
77
Pre odovzdávanie sa musíš <a href="{% url "oidc_authentication_init" %}?next={{ request.path_info|urlencode }}" class="underline hover:text-red-700">prihlásiť</a>.
88
</span>
99
</div>
10+
{% elif not user.current_grade or not user.current_school %}
11+
{# TODO: remove that login / logout step when no longer needed #}
12+
{% message 'Nemáš nastavenú školu a ročník. Nastav si ich prosím v <a class="link" href="https://id.trojsten.sk/accounts/school/">Trojsten ID</a> a potom sa odhlás a znovu prihlás.' "warning" %}
1013
{% else %}
14+
{% if enrollment_warning %}
15+
{% message 'Tvoja aktuálna škola alebo ročník sa zmenil. Ak sú údaje vo výsledkovej listine neaktuálne, napíš na <a class="link" href="mailto:info@trojsten.sk?subject=Neaktuálne údaje vo výsledkovej listine">info@trojsten.sk</a>.' "warning" %}
16+
{% endif %}
1117
<div data-submit--cta-target="submitForm" id="submit">
1218
{% for id, data in submits.items %}
1319
<div data-controller="submit--{{ id }}-submit">

seminare/problems/views.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from seminare.rules import RuleEngine
1111
from seminare.submits.models import FileSubmit, JudgeSubmit, TextSubmit
1212
from seminare.users.logic.permissions import is_contest_organizer
13-
from seminare.users.models import Enrollment, User
13+
from seminare.users.models import User
1414

1515

1616
class ProblemSetListView(ListView):
@@ -116,25 +116,19 @@ def get_object(self, queryset=None):
116116
ProblemSet.objects.for_user(self.request.user)
117117
.filter(contest=self.contest, slug=self.kwargs["problem_set_slug"])
118118
.select_related("contest")
119+
.prefetch_related("problems")
119120
).first()
120121

121122
return get_object_or_404(
122-
Problem.objects.select_related("problem_set"),
123+
self.problem_set.problems,
123124
number=self.kwargs["number"],
124-
problem_set=self.problem_set,
125125
)
126126

127-
def get_submits(self):
127+
def get_submits(self, enrollment):
128128
if not self.request.user.is_authenticated:
129129
return {}
130130

131131
rule_engine: RuleEngine = self.object.problem_set.get_rule_engine()
132-
enrollment = Enrollment.objects.filter(
133-
problem_set=self.object.problem_set, user=self.request.user
134-
).first()
135-
136-
if enrollment is not None:
137-
enrollment.user = self.request.user
138132

139133
return {
140134
id: {
@@ -160,6 +154,22 @@ def get_context_data(self, **kwargs):
160154
ctx = super().get_context_data(**kwargs)
161155
assert isinstance(self.request.user, User | AnonymousUser)
162156

157+
enrollment = None
158+
159+
if (user := self.request.user).is_authenticated:
160+
assert isinstance(user, User)
161+
enrollment = user.get_enrollment(self.object.problem_set)
162+
163+
if enrollment is not None:
164+
enrollment.user = user
165+
ctx["enrollment"] = enrollment
166+
167+
if self.problem_set.is_running and (
168+
enrollment.school_id != user.current_school_id
169+
or enrollment.grade != user.current_grade
170+
):
171+
ctx["enrollment_warning"] = True
172+
163173
chips = self.object.problem_set.get_rule_engine().get_chips(self.request.user)
164174

165175
ctx["texts"] = self.object.get_all_texts()
@@ -173,7 +183,7 @@ def get_context_data(self, **kwargs):
173183
ctx["chips"] = chips[self.object]
174184
if isinstance(self.request.user, User):
175185
ctx["is_organizer"] = is_contest_organizer(self.request.user, self.contest)
176-
ctx["submits"] = self.get_submits()
186+
ctx["submits"] = self.get_submits(enrollment)
177187
return ctx
178188

179189

seminare/users/models.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
from typing import TYPE_CHECKING
2+
13
from django.conf import settings
24
from django.contrib.auth.models import AbstractUser
35
from django.db import models
46

7+
if TYPE_CHECKING:
8+
from django.db.models.fields.related_descriptors import RelatedManager
9+
10+
from seminare.problems.models import ProblemSet
11+
512

613
class Grade(models.TextChoices):
714
YOUNG = "YNG", "<5zš"
@@ -35,6 +42,8 @@ class User(AbstractUser):
3542
current_school_id: int
3643
current_grade = models.CharField(choices=Grade.choices, max_length=3, blank=True)
3744

45+
enrollment_set: "RelatedManager[Enrollment]"
46+
3847
@property
3948
def profile_url(self):
4049
return f"https://id.trojsten.sk/profile/{self.username}/"
@@ -49,6 +58,20 @@ def display_name(self):
4958
return self.get_full_name()
5059
return self.username
5160

61+
def get_enrollment(self, problem_set: "ProblemSet"):
62+
if hasattr(self, f"enrollment_cache_{problem_set.slug}"):
63+
return getattr(self, f"enrollment_cache_{problem_set.slug}")
64+
65+
enrollment = self.enrollment_set.filter(
66+
user=self, problem_set=problem_set
67+
).first()
68+
69+
if enrollment is None:
70+
return None
71+
72+
setattr(self, f"enrollment_cache_{problem_set.slug}", enrollment)
73+
return enrollment
74+
5275

5376
class School(models.Model):
5477
name = models.CharField(blank=True, max_length=256)

0 commit comments

Comments
 (0)