Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor contest join checks #1886

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
49 changes: 27 additions & 22 deletions judge/models/contest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
from django.db import models, transaction
Expand Down Expand Up @@ -382,35 +384,30 @@ def access_check(self, user):
return
raise self.PrivateContest()

# Assumes the user can access, to avoid the cost again
def is_live_joinable_by(self, user):
if not self.started:
return False
def get_join_type(self, user) -> Optional[int]:
if self.ended:
return None # Virtual Join should not be LIVE or SPECTATE

if not user.is_authenticated:
return False
return None

if user.profile.id in self.editor_ids or user.profile.id in self.tester_ids:
return False
return ContestParticipation.SPECTATE

if self.has_completed_contest(user):
return False
if not self.started:
return None

if self.limit_join_organizations:
return self.join_organizations.filter(id__in=user.profile.organizations.all()).exists()
return True
if user.profile.id in self.spectator_ids:
return ContestParticipation.SPECTATE

# Also skips access check
def is_spectatable_by(self, user):
if not user.is_authenticated:
return False
if (self.limit_join_organizations and
not self.join_organizations.filter(id__in=user.profile.organizations.all()).exists()):
return None

if user.profile.id in self.editor_ids or user.profile.id in self.tester_ids:
return True
if self.has_completed_contest(user):
return ContestParticipation.SPECTATE

if self.limit_join_organizations:
return self.join_organizations.filter(id__in=user.profile.organizations.all()).exists()
return True
return ContestParticipation.LIVE

def is_accessible_by(self, user):
try:
Expand Down Expand Up @@ -522,13 +519,21 @@ def set_disqualified(self, disqualified):
self.contest.banned_users.remove(self.user)
set_disqualified.alters_data = True

@classmethod
def is_live(cls, participation_type: int) -> bool:
return participation_type == cls.LIVE

@classmethod
def is_spectate(cls, participation_type: int) -> bool:
return participation_type == cls.SPECTATE

@property
def live(self):
return self.virtual == self.LIVE
return self.is_live(self.virtual)

@property
def spectate(self):
return self.virtual == self.SPECTATE
return self.is_spectate(self.virtual)

@cached_property
def start(self):
Expand Down
97 changes: 47 additions & 50 deletions judge/models/tests/test_contest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from django.core.exceptions import ValidationError
from django.test import SimpleTestCase, TestCase
from django.utils import timezone
Expand Down Expand Up @@ -319,35 +321,31 @@ def test_basic_contest_methods(self):
'superuser': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertFalse, # author
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertSpectate, # author
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertTrue,
'is_in_contest': self.assertFalse,
},
'staff_contest_edit_own': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertFalse, # author
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertSpectate, # author
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertTrue,
'is_in_contest': self.assertFalse,
},
'staff_contest_see_all': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertTrue,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertLive,
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'staff_contest_edit_all': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertTrue,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertLive,
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertTrue,
'is_in_contest': self.assertFalse,
Expand All @@ -356,26 +354,23 @@ def test_basic_contest_methods(self):
# scoreboard checks don't do accessibility checks
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertTrue,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertLive,
'is_accessible_by': self.assertFalse,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'non_staff_tester': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertSpectate,
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'anonymous': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertFalse,
'get_join_type': self.assertCantJoin,
'is_accessible_by': self.assertFalse,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
Expand Down Expand Up @@ -454,8 +449,7 @@ def test_contest_hidden_scoreboard_contest_methods(self):
'normal_before_window': {
'can_see_own_scoreboard': self.assertFalse,
'can_see_full_scoreboard': self.assertFalse,
'is_live_joinable_by': self.assertTrue,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertLive,
'has_completed_contest': self.assertFalse,
},
'normal_during_window': {
Expand All @@ -466,15 +460,13 @@ def test_contest_hidden_scoreboard_contest_methods(self):
'normal_after_window': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertFalse,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertSpectate,
'has_completed_contest': self.assertTrue,
},
'non_staff_tester': {
'can_see_own_scoreboard': self.assertFalse,
'can_see_full_scoreboard': self.assertFalse,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertSpectate,
'has_completed_contest': self.assertFalse,
},
}
Expand Down Expand Up @@ -588,32 +580,25 @@ def test_tester_see_scoreboard_contest_methods(self):
def test_public_limit_organization_join_contest(self):
data = {
'non_staff_tester': {
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertSpectate,
},
'non_staff_author': {
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertSpectate,
},
'staff_contest_edit_own': {
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue, # curator
'get_join_type': self.assertSpectate, # curator
},
'staff_contest_edit_all': {
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertFalse,
'get_join_type': self.assertCantJoin,
},
'normal': {
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertFalse,
'get_join_type': self.assertCantJoin,
},
'normal_open_org': {
'is_live_joinable_by': self.assertTrue,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertLive,
},
'normal_after_window': {
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertFalse, # not in org
'get_join_type': self.assertCantJoin, # not in org
},
}
self._test_object_methods_with_users(self.public_limit_organization_join_contest, data)
Expand All @@ -623,26 +608,32 @@ def test_future_contest_methods(self):
'non_staff_spectator': {
'can_see_own_scoreboard': self.assertFalse,
'can_see_full_scoreboard': self.assertFalse,
'get_join_type': self.assertCantJoin,
},
'non_staff_tester': {
'can_see_own_scoreboard': self.assertFalse,
'can_see_full_scoreboard': self.assertFalse,
'get_join_type': self.assertSpectate,
},
'non_staff_author': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'get_join_type': self.assertSpectate,
},
'staff_contest_edit_own': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'get_join_type': self.assertSpectate,
},
'staff_contest_edit_all': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'get_join_type': self.assertCantJoin,
},
'normal': {
'can_see_own_scoreboard': self.assertFalse,
'can_see_full_scoreboard': self.assertFalse,
'get_join_type': self.assertCantJoin,
},
}
self._test_object_methods_with_users(self.future_contest, data)
Expand All @@ -656,12 +647,11 @@ def test_private_contest_methods(self):
self.private_contest.access_check(self.users['normal_open_org'])
self.private_contest.organizations.add(self.organizations['open'])

# Join checks are moot, since the contest is over.
data = {
'normal_open_org': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertTrue,
'is_spectatable_by': self.assertTrue,
Riolku marked this conversation as resolved.
Show resolved Hide resolved
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
Expand Down Expand Up @@ -696,43 +686,47 @@ def test_organization_private_contest_methods(self):
# scoreboard checks don't do accessibility checks
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'get_join_type': self.assertLive, # Not an editor on the contest
'is_accessible_by': self.assertFalse,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'staff_contest_see_all': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'get_join_type': self.assertLive, # Not an editor on the contest
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'staff_contest_edit_all': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'get_join_type': self.assertLive, # Not an editor on the contest
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertTrue,
'is_in_contest': self.assertFalse,
},
'normal': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertTrue,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertLive,
Riolku marked this conversation as resolved.
Show resolved Hide resolved
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'non_staff_tester': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'get_join_type': self.assertSpectate,
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'anonymous': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'get_join_type': self.assertCantJoin,
'is_accessible_by': self.assertFalse,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
Expand All @@ -745,35 +739,31 @@ def test_future_organization_private_contest_methods(self):
'staff_contest_edit_own': {
'can_see_own_scoreboard': self.assertFalse,
'can_see_full_scoreboard': self.assertFalse,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertCantJoin,
'is_accessible_by': self.assertFalse,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'staff_contest_see_all': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertCantJoin,
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
},
'staff_contest_edit_all': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertCantJoin,
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertTrue,
'is_in_contest': self.assertFalse,
},
'normal': {
'can_see_own_scoreboard': self.assertTrue,
'can_see_full_scoreboard': self.assertTrue,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertCantJoin,
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
Expand All @@ -782,8 +772,7 @@ def test_future_organization_private_contest_methods(self):
# False because contest has not begun
'can_see_own_scoreboard': self.assertFalse,
'can_see_full_scoreboard': self.assertFalse,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertTrue,
'get_join_type': self.assertSpectate,
'is_accessible_by': self.assertTrue,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
Expand All @@ -792,8 +781,7 @@ def test_future_organization_private_contest_methods(self):
# False because contest has not begun
'can_see_own_scoreboard': self.assertFalse,
'can_see_full_scoreboard': self.assertFalse,
'is_live_joinable_by': self.assertFalse,
'is_spectatable_by': self.assertFalse,
'get_join_type': self.assertCantJoin,
'is_accessible_by': self.assertFalse,
'is_editable_by': self.assertFalse,
'is_in_contest': self.assertFalse,
Expand Down Expand Up @@ -1002,6 +990,15 @@ def test_virtual_participation(self):
self.assertEqual(participation.start, participation.real_start)
self.assertIsInstance(participation.end_time, timezone.datetime)

def assertLive(self, arg: Optional[int], msg: Optional[str] = None) -> None:
self.assertEqual(arg, ContestParticipation.LIVE, msg=msg)

def assertSpectate(self, arg: Optional[int], msg: Optional[str] = None) -> None:
self.assertEqual(arg, ContestParticipation.SPECTATE, msg=msg)

def assertCantJoin(self, arg: Optional[int], msg: Optional[str] = None) -> None:
self.assertIsNone(arg, msg=msg)


class ContestTagTestCase(TestCase):
@classmethod
Expand Down
Loading