From b82cc8baba75fdad3e3cc0d616cdc6204ac251ae Mon Sep 17 00:00:00 2001 From: magsyg Date: Mon, 16 Dec 2024 23:42:29 +0100 Subject: [PATCH 1/3] first draft --- .../seed_scripts/recruitment_applications.py | 8 +- backend/samfundet/models/recruitment.py | 115 +++++--- .../models/tests/test_recruitment.py | 279 ++++++++++++------ backend/samfundet/tests/test_email.py | 3 +- backend/samfundet/views.py | 45 ++- 5 files changed, 311 insertions(+), 139 deletions(-) diff --git a/backend/root/management/commands/seed_scripts/recruitment_applications.py b/backend/root/management/commands/seed_scripts/recruitment_applications.py index 6f5be2a63..917495a33 100644 --- a/backend/root/management/commands/seed_scripts/recruitment_applications.py +++ b/backend/root/management/commands/seed_scripts/recruitment_applications.py @@ -1,6 +1,6 @@ from __future__ import annotations -from random import randint +from random import sample, randint from samfundet.models.general import User from samfundet.models.recruitment import RecruitmentPosition, RecruitmentApplication @@ -20,17 +20,17 @@ def seed(): yield 0, 'Deleted old applications' positions = RecruitmentPosition.objects.all() - users = User.objects.all() + users = list(User.objects.all()) created_count = 0 for position_index, position in enumerate(positions): - for _ in range(randint(0, 5)): # Create between 0 and 5 instances for each position + for user in sample(users, randint(0, 5)): # Create between 0 and 5 instances for each position application_data = APPLICATION_DATA.copy() application_data.update( { 'recruitment_position': position, 'recruitment': position.recruitment, - 'user': users[randint(0, len(users) - 1)], # random user from all users + 'user': user } ) _application, created = RecruitmentApplication.objects.get_or_create(**application_data) diff --git a/backend/samfundet/models/recruitment.py b/backend/samfundet/models/recruitment.py index 514fcda9d..aebd4a9b1 100644 --- a/backend/samfundet/models/recruitment.py +++ b/backend/samfundet/models/recruitment.py @@ -281,31 +281,48 @@ class RecruitmentApplication(CustomBaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) application_text = models.TextField(help_text='Application text') recruitment_position = models.ForeignKey( - RecruitmentPosition, on_delete=models.CASCADE, help_text='The position which is recruiting', related_name='applications' - ) - recruitment = models.ForeignKey(Recruitment, on_delete=models.CASCADE, help_text='The recruitment that is recruiting', related_name='applications') - user = models.ForeignKey(User, on_delete=models.CASCADE, help_text='The user that is applying', related_name='applications') - applicant_priority = models.PositiveIntegerField(null=True, blank=True, help_text='The priority of the application') + RecruitmentPosition, + on_delete=models.CASCADE, + help_text='The position which is recruiting', + related_name='applications') + recruitment = models.ForeignKey( + Recruitment, + on_delete=models.CASCADE, + help_text='The recruitment that is recruiting', + related_name='applications') + user = models.ForeignKey(User, + on_delete=models.CASCADE, + help_text='The user that is applying', + related_name='applications') + applicant_priority = models.PositiveIntegerField( + null=True, blank=True, help_text='The priority of the application') created_at = models.DateTimeField(null=True, blank=True, auto_now_add=True) # Foreign Key because UKA and KSG have shared interviews (multiple applications share the same interview) interview = models.ForeignKey( - Interview, on_delete=models.SET_NULL, null=True, blank=True, help_text='The interview for the application', related_name='applications' - ) + Interview, + on_delete=models.SET_NULL, + null=True, + blank=True, + help_text='The interview for the application', + related_name='applications') withdrawn = models.BooleanField(default=False, blank=True, null=True) # TODO: Important that the following is not sent along with the rest of the object whenever a user retrieves its application recruiter_priority = models.IntegerField( - choices=RecruitmentPriorityChoices.choices, default=RecruitmentPriorityChoices.NOT_SET, help_text='The priority of the application' - ) + choices=RecruitmentPriorityChoices.choices, + default=RecruitmentPriorityChoices.NOT_SET, + help_text='The priority of the application') recruiter_status = models.IntegerField( - choices=RecruitmentStatusChoices.choices, default=RecruitmentStatusChoices.NOT_SET, help_text='The status of the application' - ) + choices=RecruitmentStatusChoices.choices, + default=RecruitmentStatusChoices.NOT_SET, + help_text='The status of the application') applicant_state = models.IntegerField( - choices=RecruitmentApplicantStates.choices, default=RecruitmentApplicantStates.NOT_SET, help_text='The state of the applicant for the recruiter' - ) + choices=RecruitmentApplicantStates.choices, + default=RecruitmentApplicantStates.NOT_SET, + help_text='The state of the applicant for the recruiter') def resolve_org(self, *, return_id: bool = False) -> Organization | int: return self.recruitment.resolve_org(return_id=return_id) @@ -315,7 +332,9 @@ def resolve_gang(self, *, return_id: bool = False) -> Gang | int: def organize_priorities(self) -> None: """Organizes priorites from 1 to n, so that it is sequential with no gaps""" - applications_for_user = RecruitmentApplication.objects.filter(recruitment=self.recruitment, user=self.user).order_by('applicant_priority') + applications_for_user = RecruitmentApplication.objects.filter( + recruitment=self.recruitment, + user=self.user).order_by('applicant_priority') for i in range(len(applications_for_user)): correct_position = i + 1 if applications_for_user[i].applicant_priority != correct_position: @@ -332,16 +351,21 @@ def update_priority(self, direction: int) -> None: """ # Use order for more simple an unified for direction ordering = f"{'' if direction < 0 else '-' }applicant_priority" - applications_for_user = RecruitmentApplication.objects.filter(recruitment=self.recruitment, user=self.user).order_by(ordering) + applications_for_user = RecruitmentApplication.objects.filter( + recruitment=self.recruitment, user=self.user).order_by(ordering) direction = abs(direction) # convert to absolute for i in range(len(applications_for_user)): if applications_for_user[i].id == self.id: # find current # Find index of which to switch priority with - switch = len(applications_for_user) - 1 if i + direction >= len(applications_for_user) else i + direction + switch = len( + applications_for_user) - 1 if i + direction >= len( + applications_for_user) else i + direction new_priority = applications_for_user[switch].applicant_priority # Move priorites down in direction for ii in range(switch, i, -1): - applications_for_user[ii].applicant_priority = applications_for_user[ii - 1].applicant_priority + applications_for_user[ + ii].applicant_priority = applications_for_user[ + ii - 1].applicant_priority applications_for_user[ii].save() # update priority applications_for_user[i].applicant_priority = new_priority @@ -349,25 +373,37 @@ def update_priority(self, direction: int) -> None: break self.organize_priorities() + ALREADY_APPLIED_ERROR = 'Already created an application for this recruitment' + REAPPLY_TOO_MANY_APPLICATIONS_ERROR = 'Can not reapply application, too many active application' TOO_MANY_APPLICATIONS_ERROR = 'Too many applications for recruitment' - def clean(self, *args: tuple, **kwargs: dict) -> None: + def clean(self, *args: tuple, **kwargs: dict) -> None: # noqa: C901 super().clean() errors: dict[str, list[ValidationError]] = defaultdict(list) + # Cant use not self.pk, due to UUID generating it before save + current_application = RecruitmentApplication.objects.filter( + pk=self.pk).first() + # validates if there are not two applications for same user and same recruitmentposition + if not current_application and RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, recruitment_position=self.recruitment_position).first(): + errors['recruitment_position'].append(self.ALREADY_APPLIED_ERROR) # If there is max applications, check if applicant have applied to not to many - # Cant use not self.pk, due to UUID generating it before save. if self.recruitment.max_applications: - user_applications_count = RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, withdrawn=False).count() - current_application = RecruitmentApplication.objects.filter(pk=self.pk).first() + user_applications_count = RecruitmentApplication.objects.filter( + user=self.user, recruitment=self.recruitment, + withdrawn=False).count() + current_application = RecruitmentApplication.objects.filter( + pk=self.pk).first() if user_applications_count >= self.recruitment.max_applications: if not current_application: # attempts to create new application when too many applications - errors['recruitment'].append(self.TOO_MANY_APPLICATIONS_ERROR) + errors['recruitment'].append( + self.TOO_MANY_APPLICATIONS_ERROR) elif current_application.withdrawn and not self.withdrawn: # If it attempts to withdraw, when to many active applications - errors['recruitment'].append(self.REAPPLY_TOO_MANY_APPLICATIONS_ERROR) + errors['recruitment'].append( + self.REAPPLY_TOO_MANY_APPLICATIONS_ERROR) raise ValidationError(errors) def __str__(self) -> str: @@ -383,7 +419,8 @@ def save(self, *args: tuple, **kwargs: dict) -> None: # noqa: C901 # If the application is saved without an interview, try to find an interview from a shared position. if not self.applicant_priority: self.organize_priorities() - current_applications_count = RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment).count() + current_applications_count = RecruitmentApplication.objects.filter( + user=self.user, recruitment=self.recruitment).count() # Set the applicant_priority to the number of applications + 1 (for the current application) self.applicant_priority = current_applications_count + 1 # If the application is saved without an interview, try to find an interview from a shared position. @@ -391,27 +428,37 @@ def save(self, *args: tuple, **kwargs: dict) -> None: # noqa: C901 self.recruiter_priority = RecruitmentPriorityChoices.NOT_WANTED self.recruiter_status = RecruitmentStatusChoices.AUTOMATIC_REJECTION if not self.interview and self.recruitment_position.shared_interview_group: - shared_interview = ( - RecruitmentApplication.objects.filter(user=self.user, recruitment_position__in=self.recruitment_position.shared_interview_group.positions.all()) - .exclude(interview=None) - .first() - ) + shared_interview = (RecruitmentApplication.objects.filter( + user=self.user, + recruitment_position__in=self.recruitment_position. + shared_interview_group.positions.all()).exclude( + interview=None).first()) if shared_interview: self.interview = shared_interview.interview super().save(*args, **kwargs) def get_total_interviews(self) -> int: - return RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, withdrawn=False).exclude(interview=None).count() + return RecruitmentApplication.objects.filter( + user=self.user, recruitment=self.recruitment, + withdrawn=False).exclude(interview=None).count() def get_total_applications(self) -> int: - return RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, withdrawn=False).count() + return RecruitmentApplication.objects.filter( + user=self.user, recruitment=self.recruitment, + withdrawn=False).count() def update_applicant_state(self) -> None: - applications = RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment).order_by('applicant_priority') + applications = RecruitmentApplication.objects.filter( + user=self.user, + recruitment=self.recruitment).order_by('applicant_priority') # Get top priority - top_wanted = applications.filter(recruiter_priority=RecruitmentPriorityChoices.WANTED).order_by('applicant_priority').first() - top_reserved = applications.filter(recruiter_priority=RecruitmentPriorityChoices.RESERVE).order_by('applicant_priority').first() + top_wanted = applications.filter( + recruiter_priority=RecruitmentPriorityChoices.WANTED).order_by( + 'applicant_priority').first() + top_reserved = applications.filter( + recruiter_priority=RecruitmentPriorityChoices.RESERVE).order_by( + 'applicant_priority').first() with transaction.atomic(): for application in applications: # I hate conditionals, so instead of checking all forms of condtions diff --git a/backend/samfundet/models/tests/test_recruitment.py b/backend/samfundet/models/tests/test_recruitment.py index 5a754d239..5fafe1114 100644 --- a/backend/samfundet/models/tests/test_recruitment.py +++ b/backend/samfundet/models/tests/test_recruitment.py @@ -422,9 +422,45 @@ def test_check_withdraw_sets_unwanted(self, fixture_recruitment_application: Rec class TestRecruitmentApplicationStatus: + + def test_recruitmentstats_create( + self, fixture_user: User, + fixture_recruitment_position: RecruitmentPosition, + fixture_recruitment: Recruitment): + application = RecruitmentApplication.objects.create( + user=fixture_user, + recruitment_position=fixture_recruitment_position, + recruitment=fixture_recruitment, + application_text='I have applied', + applicant_priority=1, + ) + assert application.id + def test_recruitmentstats_no_doubleapplication_for_position( + self, fixture_user: User, + fixture_recruitment_position: RecruitmentPosition, + fixture_recruitment: Recruitment): + application = RecruitmentApplication.objects.create( + user=fixture_user, + recruitment_position=fixture_recruitment_position, + recruitment=fixture_recruitment, + application_text='I have applied', + applicant_priority=1, + ) + assert application.id + with pytest.raises(ValidationError) as error: + RecruitmentApplication.objects.create( + user=application.user, + recruitment_position=application.recruitment_position, + recruitment=application.recruitment, + application_text='I have applied a secound time!', + applicant_priority=1, + ) + e = dict(error.value) + assert RecruitmentApplication.ALREADY_APPLIED_ERROR in e['recruitment_position'] + def test_check_called_accepted_sets_auto_rejection( - self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication - ): + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -432,15 +468,17 @@ def test_check_called_accepted_sets_auto_rejection( fixture_recruitment_application.save() # Fetch most recent values - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_ACCEPTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION def test_check_called_rejected_sets_auto_rejection( - self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication - ): + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -448,15 +486,17 @@ def test_check_called_rejected_sets_auto_rejection( fixture_recruitment_application.save() # Fetch most recent values - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_REJECTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION def test_check_autorejection_sets_nothing( - self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication - ): + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -464,15 +504,17 @@ def test_check_autorejection_sets_nothing( fixture_recruitment_application.save() # Fetch most recent values - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET def test_check_revert_called_sets_unset_if_not_rejected( - self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication - ): + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -480,8 +522,10 @@ def test_check_revert_called_sets_unset_if_not_rejected( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_ACCEPTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION @@ -490,14 +534,16 @@ def test_check_revert_called_sets_unset_if_not_rejected( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET def test_check_revert_called_does_not_change_rejected( - self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication - ): + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -509,8 +555,10 @@ def test_check_revert_called_does_not_change_rejected( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_ACCEPTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.REJECTION @@ -519,14 +567,16 @@ def test_check_revert_called_does_not_change_rejected( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.REJECTION def test_check_revert_called_does_not_change_withdrawn( - self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication - ): + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -538,8 +588,10 @@ def test_check_revert_called_does_not_change_withdrawn( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_ACCEPTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION @@ -548,8 +600,10 @@ def test_check_revert_called_does_not_change_withdrawn( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION @@ -572,15 +626,17 @@ def test_check_applicant_state_all_not_set( fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application2.applicant_state == RecruitmentApplicantStates.NOT_SET assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.NOT_SET def test_check_applicant_state_wanted( - self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication - ): + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): """ Tests for each state where one application is wanted, and how that affects other applications state @@ -603,8 +659,10 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.applicant_priority = 2 fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_WANTED @@ -614,8 +672,10 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.recruiter_priority = RecruitmentPriorityChoices.RESERVE fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_WANTED @@ -625,8 +685,10 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.recruiter_priority = RecruitmentPriorityChoices.WANTED fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_WANTED @@ -639,8 +701,10 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority > fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.LESS_WANT_WANTED @@ -651,16 +715,18 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority > fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_WANTED assert fixture_recruitment_application2.applicant_state == RecruitmentApplicantStates.NOT_SET def test_check_applicant_state_reserve( - self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication - ): + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): """ Tests for each state where one application is wanted, and how that affects other applications state @@ -683,8 +749,10 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.applicant_priority = 2 fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_RESERVED @@ -694,8 +762,10 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.recruiter_priority = RecruitmentPriorityChoices.RESERVE fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_RESERVED @@ -705,8 +775,10 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.recruiter_priority = RecruitmentPriorityChoices.WANTED fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_RESERVED @@ -720,8 +792,10 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority > fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.LESS_RESERVE_RESERVED @@ -732,36 +806,50 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority > fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_RESERVED assert fixture_recruitment_application2.applicant_state == RecruitmentApplicantStates.NOT_SET - def test_priority_up(self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication): + def test_priority_up( + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): assert fixture_recruitment_application.applicant_priority == 1 assert fixture_recruitment_application2.applicant_priority == 2 # Test general up fixture_recruitment_application2.update_priority(1) - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 2 - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id).applicant_priority == 1 # Test up overloading - RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).update_priority(2) + RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id).update_priority(2) - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 1 - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id).applicant_priority == 2 # Test up from top position does not change anything - RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).update_priority(1) + RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id).update_priority(1) - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 1 - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id).applicant_priority == 2 - def test_priority_down(self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication): + def test_priority_down( + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_application2: RecruitmentApplication): # intial priority assert fixture_recruitment_application.applicant_priority == 1 assert fixture_recruitment_application2.applicant_priority == 2 @@ -769,22 +857,32 @@ def test_priority_down(self, fixture_recruitment_application: RecruitmentApplica # Test general up fixture_recruitment_application.update_priority(-1) - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 2 - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id).applicant_priority == 1 # Test up overloading - RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).update_priority(-2) + RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id).update_priority(-2) - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 1 - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id).applicant_priority == 2 # Test up from top position does not change anything - RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).update_priority(-1) + RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id).update_priority(-1) - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 1 - assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get( + id=fixture_recruitment_application2.id).applicant_priority == 2 - def test_auto_newest_lowest_pri(self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_position2: RecruitmentPosition): + def test_auto_newest_lowest_pri( + self, fixture_recruitment_application: RecruitmentApplication, + fixture_recruitment_position2: RecruitmentPosition): """Tests that the newest application gets automatically the lowest applicant priority""" # intial priority assert fixture_recruitment_application.applicant_priority == 1 @@ -797,27 +895,35 @@ def test_auto_newest_lowest_pri(self, fixture_recruitment_application: Recruitme ) assert new_application.applicant_priority == 2 - def test_recruitment_progress_no_applications(self, fixture_recruitment: Recruitment): - assert RecruitmentApplication.objects.filter(recruitment=fixture_recruitment).count() == 0 + def test_recruitment_progress_no_applications( + self, fixture_recruitment: Recruitment): + assert RecruitmentApplication.objects.filter( + recruitment=fixture_recruitment).count() == 0 assert fixture_recruitment.recruitment_progress() == 1 - def test_recruitment_progress_application_no_progress(self, fixture_recruitment: Recruitment, fixture_recruitment_application: RecruitmentApplication): - assert RecruitmentApplication.objects.filter(recruitment=fixture_recruitment).count() == 1 + def test_recruitment_progress_application_no_progress( + self, fixture_recruitment: Recruitment, + fixture_recruitment_application: RecruitmentApplication): + assert RecruitmentApplication.objects.filter( + recruitment=fixture_recruitment).count() == 1 assert fixture_recruitment.recruitment_progress() == 0 def test_recruitment_progress_application_complete_progress( - self, fixture_recruitment: Recruitment, fixture_recruitment_application: RecruitmentApplication - ): - assert RecruitmentApplication.objects.filter(recruitment=fixture_recruitment).count() == 1 + self, fixture_recruitment: Recruitment, + fixture_recruitment_application: RecruitmentApplication): + assert RecruitmentApplication.objects.filter( + recruitment=fixture_recruitment).count() == 1 assert fixture_recruitment.recruitment_progress() == 0 fixture_recruitment_application.recruiter_status = RecruitmentStatusChoices.CALLED_AND_ACCEPTED fixture_recruitment_application.save() assert fixture_recruitment.recruitment_progress() == 1 def test_recruitment_progress_applications_multiple_new_updates_progress( - self, fixture_recruitment: Recruitment, fixture_recruitment_application: RecruitmentApplication, fixture_user2: User - ): - assert RecruitmentApplication.objects.filter(recruitment=fixture_recruitment).count() == 1 + self, fixture_recruitment: Recruitment, + fixture_recruitment_application: RecruitmentApplication, + fixture_user2: User): + assert RecruitmentApplication.objects.filter( + recruitment=fixture_recruitment).count() == 1 assert fixture_recruitment.recruitment_progress() == 0 fixture_recruitment_application.recruiter_status = RecruitmentStatusChoices.CALLED_AND_ACCEPTED fixture_recruitment_application.save() @@ -825,7 +931,8 @@ def test_recruitment_progress_applications_multiple_new_updates_progress( new_application = RecruitmentApplication.objects.create( application_text='Test application text 2', - recruitment_position=fixture_recruitment_application.recruitment_position, + recruitment_position=fixture_recruitment_application. + recruitment_position, recruitment=fixture_recruitment_application.recruitment, user=fixture_user2, ) diff --git a/backend/samfundet/tests/test_email.py b/backend/samfundet/tests/test_email.py index d27d2fc9e..7639bfe37 100644 --- a/backend/samfundet/tests/test_email.py +++ b/backend/samfundet/tests/test_email.py @@ -63,6 +63,7 @@ def setUp(self): self.user_withdrawn = User.objects.create(username='withdrawn-user', email='withdrawn@example.com') self.user_contacted = User.objects.create(username='contacted-user', email='contacted@example.com') self.user_rejected_but_contacted = User.objects.create(username='skurra-user', email='hard.to.get@example.com') + self.user_rejected_but_contacted2 = User.objects.create(username='skurra-user2', email='hard2.to.get@example.com') self.admin_user = User.objects.create_superuser(username='admin', email='admin@example.com', password='adminpassword') @@ -111,7 +112,7 @@ def setUp(self): ) RecruitmentApplication.objects.create( - user=self.user_rejected_but_contacted, + user=self.user_rejected_but_contacted2, recruitment=self.recruitment, recruiter_status=RecruitmentStatusChoices.CALLED_AND_ACCEPTED, withdrawn=False, diff --git a/backend/samfundet/views.py b/backend/samfundet/views.py index 59666f8aa..68d987833 100644 --- a/backend/samfundet/views.py +++ b/backend/samfundet/views.py @@ -29,6 +29,7 @@ from django.utils.encoding import force_bytes from django.middleware.csrf import get_token from django.utils.decorators import method_decorator +from django.core.exceptions import ValidationError from django.contrib.auth.models import Group, Permission from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie @@ -881,30 +882,43 @@ class RecruitmentApplicationForApplicantView(ModelViewSet): queryset = RecruitmentApplication.objects.all() def update(self, request: Request, pk: int) -> Response: - data = request.data.dict() if isinstance(request.data, QueryDict) else request.data + data = request.data.dict() if isinstance(request.data, + QueryDict) else request.data recruitment_position = get_object_or_404(RecruitmentPosition, pk=pk) + existing_application = RecruitmentApplication.objects.filter( + user=request.user, recruitment_position=pk).first() + # If update + if existing_application: + try: + existing_application.withdrawn = False + existing_application.application_text = data['application_text'] + existing_application.save() + serializer = self.serializer_class(existing_application) + return Response(serializer.data, status.HTTP_200_OK) + except ValidationError as e: + return Response(e.message_dict, status=status.HTTP_400_BAD_REQUEST) + + # If create data['recruitment_position'] = recruitment_position.pk data['recruitment'] = recruitment_position.recruitment.pk data['user'] = request.user.pk - serializer = self.get_serializer(data=data) + serializer = self.get_serializer( + data=data) if serializer.is_valid(): - existing_application = RecruitmentApplication.objects.filter(user=request.user, recruitment_position=pk).first() - if existing_application: - existing_application.application_text = serializer.validated_data['application_text'] - existing_application.save() - serializer = self.get_serializer(existing_application) - return Response(serializer.data, status=status.HTTP_200_OK) serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.data, status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request: Request, pk: int) -> Response: - application = get_object_or_404(RecruitmentApplication, user=request.user, recruitment_position=pk) + application = get_object_or_404(RecruitmentApplication, + user=request.user, + recruitment_position=pk) user_id = request.query_params.get('user_id') if user_id: # TODO: Add permissions - application = RecruitmentApplication.objects.filter(recruitment_position=pk, user_id=user_id).first() + application = RecruitmentApplication.objects.filter( + recruitment_position=pk, user_id=user_id).first() serializer = self.get_serializer(application) return Response(serializer.data) @@ -914,7 +928,8 @@ def list(self, request: Request) -> Response: user_id = request.query_params.get('user_id') if not recruitment_id: - return Response({'error': 'A recruitment parameter is required'}, status=status.HTTP_400_BAD_REQUEST) + return Response({'error': 'A recruitment parameter is required'}, + status=status.HTTP_400_BAD_REQUEST) recruitment = get_object_or_404(Recruitment, id=recruitment_id) @@ -925,9 +940,11 @@ def list(self, request: Request) -> Response: if user_id: # TODO: Add permissions - applications = RecruitmentApplication.objects.filter(recruitment=recruitment, user_id=user_id) + applications = RecruitmentApplication.objects.filter( + recruitment=recruitment, user_id=user_id) else: - applications = RecruitmentApplication.objects.filter(recruitment=recruitment, user=request.user) + applications = RecruitmentApplication.objects.filter( + recruitment=recruitment, user=request.user) serializer = self.get_serializer(applications, many=True) return Response(serializer.data) From 7cb4bb5f5e642fad8ce57514d54b464f7fdbcacd Mon Sep 17 00:00:00 2001 From: magsyg Date: Mon, 16 Dec 2024 23:44:40 +0100 Subject: [PATCH 2/3] fix: only 1 application for position per user enforced --- backend/samfundet/models/recruitment.py | 5 ++++- backend/samfundet/views.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/samfundet/models/recruitment.py b/backend/samfundet/models/recruitment.py index aebd4a9b1..88a420629 100644 --- a/backend/samfundet/models/recruitment.py +++ b/backend/samfundet/models/recruitment.py @@ -386,7 +386,10 @@ def clean(self, *args: tuple, **kwargs: dict) -> None: # noqa: C901 current_application = RecruitmentApplication.objects.filter( pk=self.pk).first() # validates if there are not two applications for same user and same recruitmentposition - if not current_application and RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, recruitment_position=self.recruitment_position).first(): + if not current_application and RecruitmentApplication.objects.filter( + user=self.user, + recruitment=self.recruitment, + recruitment_position=self.recruitment_position).first(): errors['recruitment_position'].append(self.ALREADY_APPLIED_ERROR) # If there is max applications, check if applicant have applied to not to many if self.recruitment.max_applications: diff --git a/backend/samfundet/views.py b/backend/samfundet/views.py index 68d987833..7f815d5a8 100644 --- a/backend/samfundet/views.py +++ b/backend/samfundet/views.py @@ -27,9 +27,9 @@ from django.shortcuts import get_object_or_404 from django.contrib.auth import login, logout, update_session_auth_hash from django.utils.encoding import force_bytes +from django.core.exceptions import ValidationError from django.middleware.csrf import get_token from django.utils.decorators import method_decorator -from django.core.exceptions import ValidationError from django.contrib.auth.models import Group, Permission from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie From 439609565eae9ec8c9cd8c3cd6bf328245f234ff Mon Sep 17 00:00:00 2001 From: magsyg Date: Mon, 16 Dec 2024 23:49:21 +0100 Subject: [PATCH 3/3] ruff roo --- .../seed_scripts/recruitment_applications.py | 8 +- backend/samfundet/models/recruitment.py | 115 +++----- .../models/tests/test_recruitment.py | 255 +++++++----------- backend/samfundet/views.py | 25 +- 4 files changed, 136 insertions(+), 267 deletions(-) diff --git a/backend/root/management/commands/seed_scripts/recruitment_applications.py b/backend/root/management/commands/seed_scripts/recruitment_applications.py index 917495a33..4c9eab2db 100644 --- a/backend/root/management/commands/seed_scripts/recruitment_applications.py +++ b/backend/root/management/commands/seed_scripts/recruitment_applications.py @@ -26,13 +26,7 @@ def seed(): for position_index, position in enumerate(positions): for user in sample(users, randint(0, 5)): # Create between 0 and 5 instances for each position application_data = APPLICATION_DATA.copy() - application_data.update( - { - 'recruitment_position': position, - 'recruitment': position.recruitment, - 'user': user - } - ) + application_data.update({'recruitment_position': position, 'recruitment': position.recruitment, 'user': user}) _application, created = RecruitmentApplication.objects.get_or_create(**application_data) if created: diff --git a/backend/samfundet/models/recruitment.py b/backend/samfundet/models/recruitment.py index 88a420629..bc15db706 100644 --- a/backend/samfundet/models/recruitment.py +++ b/backend/samfundet/models/recruitment.py @@ -281,48 +281,31 @@ class RecruitmentApplication(CustomBaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) application_text = models.TextField(help_text='Application text') recruitment_position = models.ForeignKey( - RecruitmentPosition, - on_delete=models.CASCADE, - help_text='The position which is recruiting', - related_name='applications') - recruitment = models.ForeignKey( - Recruitment, - on_delete=models.CASCADE, - help_text='The recruitment that is recruiting', - related_name='applications') - user = models.ForeignKey(User, - on_delete=models.CASCADE, - help_text='The user that is applying', - related_name='applications') - applicant_priority = models.PositiveIntegerField( - null=True, blank=True, help_text='The priority of the application') + RecruitmentPosition, on_delete=models.CASCADE, help_text='The position which is recruiting', related_name='applications' + ) + recruitment = models.ForeignKey(Recruitment, on_delete=models.CASCADE, help_text='The recruitment that is recruiting', related_name='applications') + user = models.ForeignKey(User, on_delete=models.CASCADE, help_text='The user that is applying', related_name='applications') + applicant_priority = models.PositiveIntegerField(null=True, blank=True, help_text='The priority of the application') created_at = models.DateTimeField(null=True, blank=True, auto_now_add=True) # Foreign Key because UKA and KSG have shared interviews (multiple applications share the same interview) interview = models.ForeignKey( - Interview, - on_delete=models.SET_NULL, - null=True, - blank=True, - help_text='The interview for the application', - related_name='applications') + Interview, on_delete=models.SET_NULL, null=True, blank=True, help_text='The interview for the application', related_name='applications' + ) withdrawn = models.BooleanField(default=False, blank=True, null=True) # TODO: Important that the following is not sent along with the rest of the object whenever a user retrieves its application recruiter_priority = models.IntegerField( - choices=RecruitmentPriorityChoices.choices, - default=RecruitmentPriorityChoices.NOT_SET, - help_text='The priority of the application') + choices=RecruitmentPriorityChoices.choices, default=RecruitmentPriorityChoices.NOT_SET, help_text='The priority of the application' + ) recruiter_status = models.IntegerField( - choices=RecruitmentStatusChoices.choices, - default=RecruitmentStatusChoices.NOT_SET, - help_text='The status of the application') + choices=RecruitmentStatusChoices.choices, default=RecruitmentStatusChoices.NOT_SET, help_text='The status of the application' + ) applicant_state = models.IntegerField( - choices=RecruitmentApplicantStates.choices, - default=RecruitmentApplicantStates.NOT_SET, - help_text='The state of the applicant for the recruiter') + choices=RecruitmentApplicantStates.choices, default=RecruitmentApplicantStates.NOT_SET, help_text='The state of the applicant for the recruiter' + ) def resolve_org(self, *, return_id: bool = False) -> Organization | int: return self.recruitment.resolve_org(return_id=return_id) @@ -332,9 +315,7 @@ def resolve_gang(self, *, return_id: bool = False) -> Gang | int: def organize_priorities(self) -> None: """Organizes priorites from 1 to n, so that it is sequential with no gaps""" - applications_for_user = RecruitmentApplication.objects.filter( - recruitment=self.recruitment, - user=self.user).order_by('applicant_priority') + applications_for_user = RecruitmentApplication.objects.filter(recruitment=self.recruitment, user=self.user).order_by('applicant_priority') for i in range(len(applications_for_user)): correct_position = i + 1 if applications_for_user[i].applicant_priority != correct_position: @@ -351,21 +332,16 @@ def update_priority(self, direction: int) -> None: """ # Use order for more simple an unified for direction ordering = f"{'' if direction < 0 else '-' }applicant_priority" - applications_for_user = RecruitmentApplication.objects.filter( - recruitment=self.recruitment, user=self.user).order_by(ordering) + applications_for_user = RecruitmentApplication.objects.filter(recruitment=self.recruitment, user=self.user).order_by(ordering) direction = abs(direction) # convert to absolute for i in range(len(applications_for_user)): if applications_for_user[i].id == self.id: # find current # Find index of which to switch priority with - switch = len( - applications_for_user) - 1 if i + direction >= len( - applications_for_user) else i + direction + switch = len(applications_for_user) - 1 if i + direction >= len(applications_for_user) else i + direction new_priority = applications_for_user[switch].applicant_priority # Move priorites down in direction for ii in range(switch, i, -1): - applications_for_user[ - ii].applicant_priority = applications_for_user[ - ii - 1].applicant_priority + applications_for_user[ii].applicant_priority = applications_for_user[ii - 1].applicant_priority applications_for_user[ii].save() # update priority applications_for_user[i].applicant_priority = new_priority @@ -383,30 +359,24 @@ def clean(self, *args: tuple, **kwargs: dict) -> None: # noqa: C901 errors: dict[str, list[ValidationError]] = defaultdict(list) # Cant use not self.pk, due to UUID generating it before save - current_application = RecruitmentApplication.objects.filter( - pk=self.pk).first() + current_application = RecruitmentApplication.objects.filter(pk=self.pk).first() # validates if there are not two applications for same user and same recruitmentposition - if not current_application and RecruitmentApplication.objects.filter( - user=self.user, - recruitment=self.recruitment, - recruitment_position=self.recruitment_position).first(): + if ( + not current_application + and RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, recruitment_position=self.recruitment_position).first() + ): errors['recruitment_position'].append(self.ALREADY_APPLIED_ERROR) # If there is max applications, check if applicant have applied to not to many if self.recruitment.max_applications: - user_applications_count = RecruitmentApplication.objects.filter( - user=self.user, recruitment=self.recruitment, - withdrawn=False).count() - current_application = RecruitmentApplication.objects.filter( - pk=self.pk).first() + user_applications_count = RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, withdrawn=False).count() + current_application = RecruitmentApplication.objects.filter(pk=self.pk).first() if user_applications_count >= self.recruitment.max_applications: if not current_application: # attempts to create new application when too many applications - errors['recruitment'].append( - self.TOO_MANY_APPLICATIONS_ERROR) + errors['recruitment'].append(self.TOO_MANY_APPLICATIONS_ERROR) elif current_application.withdrawn and not self.withdrawn: # If it attempts to withdraw, when to many active applications - errors['recruitment'].append( - self.REAPPLY_TOO_MANY_APPLICATIONS_ERROR) + errors['recruitment'].append(self.REAPPLY_TOO_MANY_APPLICATIONS_ERROR) raise ValidationError(errors) def __str__(self) -> str: @@ -422,8 +392,7 @@ def save(self, *args: tuple, **kwargs: dict) -> None: # noqa: C901 # If the application is saved without an interview, try to find an interview from a shared position. if not self.applicant_priority: self.organize_priorities() - current_applications_count = RecruitmentApplication.objects.filter( - user=self.user, recruitment=self.recruitment).count() + current_applications_count = RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment).count() # Set the applicant_priority to the number of applications + 1 (for the current application) self.applicant_priority = current_applications_count + 1 # If the application is saved without an interview, try to find an interview from a shared position. @@ -431,37 +400,27 @@ def save(self, *args: tuple, **kwargs: dict) -> None: # noqa: C901 self.recruiter_priority = RecruitmentPriorityChoices.NOT_WANTED self.recruiter_status = RecruitmentStatusChoices.AUTOMATIC_REJECTION if not self.interview and self.recruitment_position.shared_interview_group: - shared_interview = (RecruitmentApplication.objects.filter( - user=self.user, - recruitment_position__in=self.recruitment_position. - shared_interview_group.positions.all()).exclude( - interview=None).first()) + shared_interview = ( + RecruitmentApplication.objects.filter(user=self.user, recruitment_position__in=self.recruitment_position.shared_interview_group.positions.all()) + .exclude(interview=None) + .first() + ) if shared_interview: self.interview = shared_interview.interview super().save(*args, **kwargs) def get_total_interviews(self) -> int: - return RecruitmentApplication.objects.filter( - user=self.user, recruitment=self.recruitment, - withdrawn=False).exclude(interview=None).count() + return RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, withdrawn=False).exclude(interview=None).count() def get_total_applications(self) -> int: - return RecruitmentApplication.objects.filter( - user=self.user, recruitment=self.recruitment, - withdrawn=False).count() + return RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment, withdrawn=False).count() def update_applicant_state(self) -> None: - applications = RecruitmentApplication.objects.filter( - user=self.user, - recruitment=self.recruitment).order_by('applicant_priority') + applications = RecruitmentApplication.objects.filter(user=self.user, recruitment=self.recruitment).order_by('applicant_priority') # Get top priority - top_wanted = applications.filter( - recruiter_priority=RecruitmentPriorityChoices.WANTED).order_by( - 'applicant_priority').first() - top_reserved = applications.filter( - recruiter_priority=RecruitmentPriorityChoices.RESERVE).order_by( - 'applicant_priority').first() + top_wanted = applications.filter(recruiter_priority=RecruitmentPriorityChoices.WANTED).order_by('applicant_priority').first() + top_reserved = applications.filter(recruiter_priority=RecruitmentPriorityChoices.RESERVE).order_by('applicant_priority').first() with transaction.atomic(): for application in applications: # I hate conditionals, so instead of checking all forms of condtions diff --git a/backend/samfundet/models/tests/test_recruitment.py b/backend/samfundet/models/tests/test_recruitment.py index 5fafe1114..fc55093d3 100644 --- a/backend/samfundet/models/tests/test_recruitment.py +++ b/backend/samfundet/models/tests/test_recruitment.py @@ -422,11 +422,7 @@ def test_check_withdraw_sets_unwanted(self, fixture_recruitment_application: Rec class TestRecruitmentApplicationStatus: - - def test_recruitmentstats_create( - self, fixture_user: User, - fixture_recruitment_position: RecruitmentPosition, - fixture_recruitment: Recruitment): + def test_recruitmentstats_create(self, fixture_user: User, fixture_recruitment_position: RecruitmentPosition, fixture_recruitment: Recruitment): application = RecruitmentApplication.objects.create( user=fixture_user, recruitment_position=fixture_recruitment_position, @@ -435,10 +431,10 @@ def test_recruitmentstats_create( applicant_priority=1, ) assert application.id + def test_recruitmentstats_no_doubleapplication_for_position( - self, fixture_user: User, - fixture_recruitment_position: RecruitmentPosition, - fixture_recruitment: Recruitment): + self, fixture_user: User, fixture_recruitment_position: RecruitmentPosition, fixture_recruitment: Recruitment + ): application = RecruitmentApplication.objects.create( user=fixture_user, recruitment_position=fixture_recruitment_position, @@ -459,8 +455,8 @@ def test_recruitmentstats_no_doubleapplication_for_position( assert RecruitmentApplication.ALREADY_APPLIED_ERROR in e['recruitment_position'] def test_check_called_accepted_sets_auto_rejection( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication + ): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -468,17 +464,15 @@ def test_check_called_accepted_sets_auto_rejection( fixture_recruitment_application.save() # Fetch most recent values - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_ACCEPTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION def test_check_called_rejected_sets_auto_rejection( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication + ): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -486,17 +480,15 @@ def test_check_called_rejected_sets_auto_rejection( fixture_recruitment_application.save() # Fetch most recent values - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_REJECTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION def test_check_autorejection_sets_nothing( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication + ): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -504,17 +496,15 @@ def test_check_autorejection_sets_nothing( fixture_recruitment_application.save() # Fetch most recent values - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET def test_check_revert_called_sets_unset_if_not_rejected( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication + ): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -522,10 +512,8 @@ def test_check_revert_called_sets_unset_if_not_rejected( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_ACCEPTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION @@ -534,16 +522,14 @@ def test_check_revert_called_sets_unset_if_not_rejected( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET def test_check_revert_called_does_not_change_rejected( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication + ): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -555,10 +541,8 @@ def test_check_revert_called_does_not_change_rejected( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_ACCEPTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.REJECTION @@ -567,16 +551,14 @@ def test_check_revert_called_does_not_change_rejected( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.REJECTION def test_check_revert_called_does_not_change_withdrawn( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication + ): assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.NOT_SET @@ -588,10 +570,8 @@ def test_check_revert_called_does_not_change_withdrawn( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.CALLED_AND_ACCEPTED assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION @@ -600,10 +580,8 @@ def test_check_revert_called_does_not_change_withdrawn( fixture_recruitment_application.save() # Fetch most recent values, check gets set to autorejection - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.recruiter_status == RecruitmentStatusChoices.NOT_SET assert fixture_recruitment_application2.recruiter_status == RecruitmentStatusChoices.AUTOMATIC_REJECTION @@ -626,17 +604,15 @@ def test_check_applicant_state_all_not_set( fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application2.applicant_state == RecruitmentApplicantStates.NOT_SET assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.NOT_SET def test_check_applicant_state_wanted( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication + ): """ Tests for each state where one application is wanted, and how that affects other applications state @@ -659,10 +635,8 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.applicant_priority = 2 fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_WANTED @@ -672,10 +646,8 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.recruiter_priority = RecruitmentPriorityChoices.RESERVE fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_WANTED @@ -685,10 +657,8 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.recruiter_priority = RecruitmentPriorityChoices.WANTED fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_WANTED @@ -701,10 +671,8 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority > fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.LESS_WANT_WANTED @@ -715,18 +683,16 @@ def test_check_applicant_state_wanted( fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority > fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_WANTED assert fixture_recruitment_application2.applicant_state == RecruitmentApplicantStates.NOT_SET def test_check_applicant_state_reserve( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication + ): """ Tests for each state where one application is wanted, and how that affects other applications state @@ -749,10 +715,8 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.applicant_priority = 2 fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_RESERVED @@ -762,10 +726,8 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.recruiter_priority = RecruitmentPriorityChoices.RESERVE fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_RESERVED @@ -775,10 +737,8 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.recruiter_priority = RecruitmentPriorityChoices.WANTED fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority < fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_RESERVED @@ -792,10 +752,8 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority > fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.LESS_RESERVE_RESERVED @@ -806,50 +764,36 @@ def test_check_applicant_state_reserve( fixture_recruitment_application2.save() fixture_recruitment_application.update_applicant_state() - fixture_recruitment_application = RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id) - fixture_recruitment_application2 = RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id) + fixture_recruitment_application = RecruitmentApplication.objects.get(id=fixture_recruitment_application.id) + fixture_recruitment_application2 = RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id) assert fixture_recruitment_application.applicant_priority > fixture_recruitment_application2.applicant_priority assert fixture_recruitment_application.applicant_state == RecruitmentApplicantStates.TOP_RESERVED assert fixture_recruitment_application2.applicant_state == RecruitmentApplicantStates.NOT_SET - def test_priority_up( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + def test_priority_up(self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication): assert fixture_recruitment_application.applicant_priority == 1 assert fixture_recruitment_application2.applicant_priority == 2 # Test general up fixture_recruitment_application2.update_priority(1) - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id).applicant_priority == 2 - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 1 # Test up overloading - RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id).update_priority(2) + RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).update_priority(2) - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id).applicant_priority == 1 - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 2 # Test up from top position does not change anything - RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id).update_priority(1) + RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).update_priority(1) - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id).applicant_priority == 1 - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 2 - def test_priority_down( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_application2: RecruitmentApplication): + def test_priority_down(self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_application2: RecruitmentApplication): # intial priority assert fixture_recruitment_application.applicant_priority == 1 assert fixture_recruitment_application2.applicant_priority == 2 @@ -857,32 +801,22 @@ def test_priority_down( # Test general up fixture_recruitment_application.update_priority(-1) - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id).applicant_priority == 2 - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 1 # Test up overloading - RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id).update_priority(-2) + RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).update_priority(-2) - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id).applicant_priority == 1 - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 2 # Test up from top position does not change anything - RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id).update_priority(-1) + RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).update_priority(-1) - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application.id).applicant_priority == 1 - assert RecruitmentApplication.objects.get( - id=fixture_recruitment_application2.id).applicant_priority == 2 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application.id).applicant_priority == 1 + assert RecruitmentApplication.objects.get(id=fixture_recruitment_application2.id).applicant_priority == 2 - def test_auto_newest_lowest_pri( - self, fixture_recruitment_application: RecruitmentApplication, - fixture_recruitment_position2: RecruitmentPosition): + def test_auto_newest_lowest_pri(self, fixture_recruitment_application: RecruitmentApplication, fixture_recruitment_position2: RecruitmentPosition): """Tests that the newest application gets automatically the lowest applicant priority""" # intial priority assert fixture_recruitment_application.applicant_priority == 1 @@ -895,35 +829,27 @@ def test_auto_newest_lowest_pri( ) assert new_application.applicant_priority == 2 - def test_recruitment_progress_no_applications( - self, fixture_recruitment: Recruitment): - assert RecruitmentApplication.objects.filter( - recruitment=fixture_recruitment).count() == 0 + def test_recruitment_progress_no_applications(self, fixture_recruitment: Recruitment): + assert RecruitmentApplication.objects.filter(recruitment=fixture_recruitment).count() == 0 assert fixture_recruitment.recruitment_progress() == 1 - def test_recruitment_progress_application_no_progress( - self, fixture_recruitment: Recruitment, - fixture_recruitment_application: RecruitmentApplication): - assert RecruitmentApplication.objects.filter( - recruitment=fixture_recruitment).count() == 1 + def test_recruitment_progress_application_no_progress(self, fixture_recruitment: Recruitment, fixture_recruitment_application: RecruitmentApplication): + assert RecruitmentApplication.objects.filter(recruitment=fixture_recruitment).count() == 1 assert fixture_recruitment.recruitment_progress() == 0 def test_recruitment_progress_application_complete_progress( - self, fixture_recruitment: Recruitment, - fixture_recruitment_application: RecruitmentApplication): - assert RecruitmentApplication.objects.filter( - recruitment=fixture_recruitment).count() == 1 + self, fixture_recruitment: Recruitment, fixture_recruitment_application: RecruitmentApplication + ): + assert RecruitmentApplication.objects.filter(recruitment=fixture_recruitment).count() == 1 assert fixture_recruitment.recruitment_progress() == 0 fixture_recruitment_application.recruiter_status = RecruitmentStatusChoices.CALLED_AND_ACCEPTED fixture_recruitment_application.save() assert fixture_recruitment.recruitment_progress() == 1 def test_recruitment_progress_applications_multiple_new_updates_progress( - self, fixture_recruitment: Recruitment, - fixture_recruitment_application: RecruitmentApplication, - fixture_user2: User): - assert RecruitmentApplication.objects.filter( - recruitment=fixture_recruitment).count() == 1 + self, fixture_recruitment: Recruitment, fixture_recruitment_application: RecruitmentApplication, fixture_user2: User + ): + assert RecruitmentApplication.objects.filter(recruitment=fixture_recruitment).count() == 1 assert fixture_recruitment.recruitment_progress() == 0 fixture_recruitment_application.recruiter_status = RecruitmentStatusChoices.CALLED_AND_ACCEPTED fixture_recruitment_application.save() @@ -931,8 +857,7 @@ def test_recruitment_progress_applications_multiple_new_updates_progress( new_application = RecruitmentApplication.objects.create( application_text='Test application text 2', - recruitment_position=fixture_recruitment_application. - recruitment_position, + recruitment_position=fixture_recruitment_application.recruitment_position, recruitment=fixture_recruitment_application.recruitment, user=fixture_user2, ) diff --git a/backend/samfundet/views.py b/backend/samfundet/views.py index 7f815d5a8..b90ff809b 100644 --- a/backend/samfundet/views.py +++ b/backend/samfundet/views.py @@ -882,11 +882,9 @@ class RecruitmentApplicationForApplicantView(ModelViewSet): queryset = RecruitmentApplication.objects.all() def update(self, request: Request, pk: int) -> Response: - data = request.data.dict() if isinstance(request.data, - QueryDict) else request.data + data = request.data.dict() if isinstance(request.data, QueryDict) else request.data recruitment_position = get_object_or_404(RecruitmentPosition, pk=pk) - existing_application = RecruitmentApplication.objects.filter( - user=request.user, recruitment_position=pk).first() + existing_application = RecruitmentApplication.objects.filter(user=request.user, recruitment_position=pk).first() # If update if existing_application: try: @@ -902,23 +900,19 @@ def update(self, request: Request, pk: int) -> Response: data['recruitment_position'] = recruitment_position.pk data['recruitment'] = recruitment_position.recruitment.pk data['user'] = request.user.pk - serializer = self.get_serializer( - data=data) + serializer = self.get_serializer(data=data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request: Request, pk: int) -> Response: - application = get_object_or_404(RecruitmentApplication, - user=request.user, - recruitment_position=pk) + application = get_object_or_404(RecruitmentApplication, user=request.user, recruitment_position=pk) user_id = request.query_params.get('user_id') if user_id: # TODO: Add permissions - application = RecruitmentApplication.objects.filter( - recruitment_position=pk, user_id=user_id).first() + application = RecruitmentApplication.objects.filter(recruitment_position=pk, user_id=user_id).first() serializer = self.get_serializer(application) return Response(serializer.data) @@ -928,8 +922,7 @@ def list(self, request: Request) -> Response: user_id = request.query_params.get('user_id') if not recruitment_id: - return Response({'error': 'A recruitment parameter is required'}, - status=status.HTTP_400_BAD_REQUEST) + return Response({'error': 'A recruitment parameter is required'}, status=status.HTTP_400_BAD_REQUEST) recruitment = get_object_or_404(Recruitment, id=recruitment_id) @@ -940,11 +933,9 @@ def list(self, request: Request) -> Response: if user_id: # TODO: Add permissions - applications = RecruitmentApplication.objects.filter( - recruitment=recruitment, user_id=user_id) + applications = RecruitmentApplication.objects.filter(recruitment=recruitment, user_id=user_id) else: - applications = RecruitmentApplication.objects.filter( - recruitment=recruitment, user=request.user) + applications = RecruitmentApplication.objects.filter(recruitment=recruitment, user=request.user) serializer = self.get_serializer(applications, many=True) return Response(serializer.data)