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

Implement new penalty rules #3349

Open
wants to merge 4 commits 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,6 @@ google-credentials.json

# vscode
.vscode/

# mac
.DS_Store
Copy link
Contributor

@falbru falbru Sep 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.DS_Store
**/.DS_Store

Your current code only removes the .DS_Store in the root directory

Binary file added lego/.DS_Store
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this .DS_STORE file

Binary file not shown.
4 changes: 2 additions & 2 deletions lego/api/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
PasswordResetPerformViewSet,
PasswordResetRequestViewSet,
)
from lego.apps.users.views.penalties import PenaltyViewSet
from lego.apps.users.views.penalties import PenaltyGroupViewSet
from lego.apps.users.views.registration import UserRegistrationRequestViewSet
from lego.apps.users.views.student_confirmation import (
StudentConfirmationPerformViewSet,
Expand Down Expand Up @@ -179,7 +179,7 @@
PasswordResetRequestViewSet,
basename="password-reset-request",
)
router.register(r"penalties", PenaltyViewSet)
router.register(r"penalties", PenaltyGroupViewSet)
router.register(r"podcasts", PodcastViewSet, basename="podcasts")
router.register(r"polls", PollViewSet, basename="polls")
router.register(r"quotes", QuoteViewSet)
Expand Down
56 changes: 18 additions & 38 deletions lego/apps/events/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from lego.apps.followers.models import FollowEvent
from lego.apps.permissions.models import ObjectPermissionsModel
from lego.apps.users.constants import AUTUMN, SPRING
from lego.apps.users.models import AbakusGroup, Penalty, User
from lego.apps.users.models import AbakusGroup, PenaltyGroup, User
from lego.utils.models import BasisModel
from lego.utils.youtube_validator import youtube_validator

Expand Down Expand Up @@ -232,13 +232,15 @@ def get_earliest_registration_time(
if len(pools) == 0:
return None
reg_time: date = min(pool.activation_date for pool in pools)

if self.heed_penalties:
if penalties is None:
penalties = user.number_of_penalties()
if penalties == 2:
return reg_time + timedelta(hours=12)
elif penalties == 1:
return reg_time + timedelta(hours=3)
return (
reg_time + timedelta(hours=settings.PENALTY_DELAY_DURATION)
if penalties >= 1
else reg_time
)
return reg_time

def get_possible_pools(
Expand Down Expand Up @@ -328,9 +330,6 @@ def register(self, registration: Registration) -> Registration:
# Make the user follow the event
FollowEvent.objects.get_or_create(follower=user, target=self)

if penalties >= 3:
return registration.add_to_waiting_list()

# If the event is merged or has only one pool we can skip a lot of logic
if all_pools.count() == 1:
return registration.add_to_pool(possible_pools[0])
Expand Down Expand Up @@ -390,8 +389,10 @@ def unregister(
and self.heed_penalties
and self.passed_unregistration_deadline
):
if not registration.user.penalties.filter(source_event=self).exists():
Penalty.objects.create(
if not registration.user.penalty_groups.filter(
source_event=self
).exists():
PenaltyGroup.objects.create(
user=registration.user,
reason=f"Meldte seg av {self.title} for sent.",
weight=1,
Expand Down Expand Up @@ -469,8 +470,6 @@ def early_bump(self, opening_pool: Pool) -> None:
for reg in self.waiting_registrations:
if opening_pool.is_full:
break
if self.heed_penalties and reg.user.number_of_penalties() >= 3:
continue
if self.can_register(reg.user, opening_pool, future=True):
reg.pool = opening_pool
reg.save()
Expand All @@ -491,8 +490,6 @@ def bump_on_pool_creation_or_expansion(self) -> None:
for reg in self.waiting_registrations:
if self.is_full or pool.is_full:
break
if self.heed_penalties and reg.user.number_of_penalties() >= 3:
continue
if self.can_register(reg.user, pool, future=True):
reg.pool = pool
reg.save()
Expand Down Expand Up @@ -576,28 +573,9 @@ def pop_from_waiting_list(

if to_pool:
for registration in self.waiting_registrations:
if self.heed_penalties:
penalties: int = registration.user.number_of_penalties()
earliest_reg: Optional[date] = self.get_earliest_registration_time(
registration.user, [to_pool], penalties
)
if penalties < 3 and earliest_reg and earliest_reg < timezone.now():
if self.can_register(registration.user, to_pool):
return registration
elif self.can_register(registration.user, to_pool):
Arashfa0301 marked this conversation as resolved.
Show resolved Hide resolved
return registration
return None

if self.heed_penalties:
for registration in self.waiting_registrations:
penalties = registration.user.number_of_penalties()
earliest_reg = self.get_earliest_registration_time(
registration.user, None, penalties
)
if penalties < 3 and earliest_reg and earliest_reg < timezone.now():
if self.can_register(registration.user, to_pool):
return registration
return None

return self.waiting_registrations.first()

@staticmethod
Expand Down Expand Up @@ -934,16 +912,18 @@ def handle_user_penalty(self, presence: constants.PRESENCE_CHOICES) -> None:
and presence == constants.PRESENCE_CHOICES.NOT_PRESENT
and self.event.penalty_weight_on_not_present
):
if not self.user.penalties.filter(source_event=self.event).exists():
Penalty.objects.create(
if not self.user.penalty_groups.filter(source_event=self.event).exists():
PenaltyGroup.objects.create(
user=self.user,
reason=f"Møtte ikke opp på {self.event.title}.",
weight=self.event.penalty_weight_on_not_present,
source_event=self.event,
)
else:
for penalty in self.user.penalties.filter(source_event=self.event):
penalty.delete()
for penalty_group in self.user.penalty_groups.filter(
source_event=self.event
):
penalty_group.delete()

def add_to_pool(self, pool: Pool) -> Registration:
allowed: bool = False
Expand Down
82 changes: 14 additions & 68 deletions lego/apps/events/tests/test_async_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
notify_user_when_payment_soon_overdue,
set_all_events_ready_and_bump,
)
from lego.apps.events.tests.utils import get_dummy_users, make_penalty_expire
from lego.apps.users.models import AbakusGroup, Penalty
from lego.apps.events.tests.utils import get_dummy_users, make_penalty_group_expire
from lego.apps.users.models import AbakusGroup, PenaltyGroup
from lego.utils.test_utils import BaseAPITestCase, BaseTestCase


Expand Down Expand Up @@ -223,36 +223,6 @@ def test_isnt_bumped_without_permission(self):
self.assertEqual(self.pool_two.registrations.count(), 0)
self.assertEqual(self.event.waiting_registrations.count(), 1)

def test_isnt_bumped_with_penalties(self):
"""Users should not be bumped if they have 3 penalties."""
self.event.start_time = timezone.now() + timedelta(days=1)
self.event.merge_time = timezone.now() + timedelta(hours=12)
self.event.save()

self.pool_one.activation_date = timezone.now() - timedelta(days=1)
self.pool_one.save()

self.pool_two.activation_date = timezone.now() + timedelta(minutes=30)
self.pool_two.save()

users = get_dummy_users(2)

Penalty.objects.create(
user=users[1], reason="test", weight=3, source_event=self.event
)

for user in users:
AbakusGroup.objects.get(name="Webkom").add_user(user)
registration = Registration.objects.get_or_create(
event=self.event, user=user
)[0]
self.event.register(registration)

bump_waiting_users_to_new_pool()

self.assertEqual(self.pool_two.registrations.count(), 0)
self.assertEqual(self.event.waiting_registrations.count(), 1)

def test_isnt_bumped_if_activation_is_far_into_the_future(self):
"""Users should not be bumped if the pool is activated more than
35 minutes in the future."""
Expand Down Expand Up @@ -380,7 +350,7 @@ def test_is_automatically_bumped_after_penalty_expiration(self):
user = get_dummy_users(1)[0]
AbakusGroup.objects.get(name="Abakus").add_user(user)

p1 = Penalty.objects.create(
penalty_group = PenaltyGroup.objects.create(
user=user, reason="test", weight=3, source_event=self.event
)

Expand All @@ -389,7 +359,7 @@ def test_is_automatically_bumped_after_penalty_expiration(self):
]
async_register(registration.id)

make_penalty_expire(p1)
make_penalty_group_expire(penalty_group)
check_events_for_registrations_with_expired_penalties.delay()

self.assertIsNotNone(Registration.objects.get(id=registration.id).pool)
Expand All @@ -401,10 +371,10 @@ def test_is_bumped_with_multiple_penalties(self):
user = get_dummy_users(1)[0]
AbakusGroup.objects.get(name="Abakus").add_user(user)

p1 = Penalty.objects.create(
p1 = PenaltyGroup.objects.create(
user=user, reason="test", weight=2, source_event=self.event
)
Penalty.objects.create(
PenaltyGroup.objects.create(
user=user, reason="test2", weight=2, source_event=self.event
)

Expand All @@ -413,44 +383,20 @@ def test_is_bumped_with_multiple_penalties(self):
]
async_register(registration.id)

make_penalty_expire(p1)
make_penalty_group_expire(p1)
check_events_for_registrations_with_expired_penalties.delay()

self.assertIsNotNone(Registration.objects.get(id=registration.id).pool)
self.assertEqual(self.event.number_of_registrations, 1)

def test_isnt_bumped_with_too_many_penalties(self):
"""Tests that a user isn't bumped when going from 4 to 3 active penalties"""

user = get_dummy_users(1)[0]
AbakusGroup.objects.get(name="Abakus").add_user(user)

p1 = Penalty.objects.create(
user=user, reason="test", weight=1, source_event=self.event
)
Penalty.objects.create(
user=user, reason="test2", weight=3, source_event=self.event
)

registration = Registration.objects.get_or_create(event=self.event, user=user)[
0
]
async_register(registration.id)

make_penalty_expire(p1)
check_events_for_registrations_with_expired_penalties.delay()

self.assertIsNone(Registration.objects.get(id=registration.id).pool)
self.assertEqual(self.event.number_of_registrations, 0)

def test_isnt_bumped_when_full(self):
"""Tests that a user isnt bumped when the event is full when penalties expire."""

users = get_dummy_users(2)
for user in users:
AbakusGroup.objects.get(name="Abakus").add_user(user)

p1 = Penalty.objects.create(
p1 = PenaltyGroup.objects.create(
user=users[1], reason="test", weight=3, source_event=self.event
)

Expand All @@ -460,7 +406,7 @@ def test_isnt_bumped_when_full(self):
)[0]
async_register(registration.id)

make_penalty_expire(p1)
make_penalty_group_expire(p1)
check_events_for_registrations_with_expired_penalties.delay()

self.assertIsNone(Registration.objects.get(user=users[1]).pool)
Expand All @@ -476,7 +422,7 @@ def test_isnt_bumped_when_not_first_in_line(self):
for user in users:
AbakusGroup.objects.get(name="Abakus").add_user(user)

p1 = Penalty.objects.create(
p1 = PenaltyGroup.objects.create(
user=users[2], reason="test", weight=3, source_event=self.event
)

Expand All @@ -486,7 +432,7 @@ def test_isnt_bumped_when_not_first_in_line(self):
)[0]
async_register(registration.id)

make_penalty_expire(p1)
make_penalty_group_expire(p1)
check_events_for_registrations_with_expired_penalties.delay()

self.assertIsNone(Registration.objects.get(user=users[1]).pool)
Expand All @@ -502,7 +448,7 @@ def test_async_bump_post_merge(self):
for user in users:
AbakusGroup.objects.get(name="Abakus").add_user(user)

p1 = Penalty.objects.create(
penalty_group = PenaltyGroup.objects.create(
user=users[1], reason="test", weight=3, source_event=self.event
)

Expand All @@ -512,7 +458,7 @@ def test_async_bump_post_merge(self):
)[0]
async_register(registration.id)

make_penalty_expire(p1)
make_penalty_group_expire(penalty_group)
check_events_for_registrations_with_expired_penalties.delay()

self.assertIsNotNone(Registration.objects.get(user=users[1]).pool)
Expand Down Expand Up @@ -580,7 +526,7 @@ def test_initiate_payment_in_waiting_list(self, mock_initiate_payment):
user = get_dummy_users(1)[0]
AbakusGroup.objects.get(name="Abakus").add_user(user)

Penalty.objects.create(
PenaltyGroup.objects.create(
user=user, reason="test", weight=3, source_event=self.event
)

Expand Down
8 changes: 4 additions & 4 deletions lego/apps/events/tests/test_events_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
check_events_for_registrations_with_expired_penalties,
stripe_webhook_event,
)
from lego.apps.events.tests.utils import get_dummy_users, make_penalty_expire
from lego.apps.events.tests.utils import get_dummy_users, make_penalty_group_expire
from lego.apps.followers.models import FollowEvent
from lego.apps.surveys.models import Submission, Survey
from lego.apps.users.constants import GROUP_GRADE, PHOTO_CONSENT_DOMAINS
from lego.apps.users.models import AbakusGroup, Penalty, PhotoConsent, User
from lego.apps.users.models import AbakusGroup, PenaltyGroup, PhotoConsent, User
from lego.utils.test_utils import BaseAPITestCase, BaseAPITransactionTestCase

_test_event_data = [
Expand Down Expand Up @@ -1727,7 +1727,7 @@ def test_create_payment_intent_when_bump_from_waitlist(self, mock_notify):

self.client.force_authenticate(self.abakus_user_4)

p1 = Penalty.objects.create(
p1 = PenaltyGroup.objects.create(
user=self.abakus_user_4, reason="test", weight=3, source_event=self.event
)

Expand All @@ -1742,7 +1742,7 @@ def test_create_payment_intent_when_bump_from_waitlist(self, mock_notify):
res = self.get_payment_intent()
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)

make_penalty_expire(p1)
make_penalty_group_expire(p1)
check_events_for_registrations_with_expired_penalties.delay()

res = self.get_payment_intent()
Expand Down
Loading