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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions apps/nominations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,15 @@ def visible(self, user=None):
if self.accepted and self.approved and not self.election.nominations_open:
return True

if user is None:
if user is None or not user.is_authenticated:
return False

return bool(user.is_staff or user == self.user)
if user.is_staff or user == self.user:
return True

# A nominator can see the person they nominated, so that the links
# in the nominee list preview work while nominations are open.
return self.nominations.filter(nominator=user, election=self.election).exists()
Comment thread
JacobCoffee marked this conversation as resolved.
Resolved


class Nomination(models.Model):
Expand Down
70 changes: 70 additions & 0 deletions apps/nominations/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,73 @@ def test_accept_404s_under_wrong_election(self):

def test_edit_still_works_under_own_election(self):
self.assertEqual(self.client.get(self._url("nominations:nomination_edit", self.election)).status_code, 200)


class NomineeListPreviewTests(TestCase):
"""While nominations are open, the list previews the nominees relevant to the user."""

def setUp(self):
self.nominator = UserFactory(first_name="Alan", last_name="Turing")
self.election = open_election("2026 Board Election")
self.other_election = open_election("2026 Packaging Council Election", kind=packaging_council_kind())

self.nominee = self._nominee(self.election, "Grace", "Hopper")
self._nomination(self.election, self.nominator, self.nominee)

# The same user is a candidate in an unrelated election.
self.nominator_as_nominee = Nominee.objects.create(user=self.nominator, election=self.other_election)
self._nomination(
self.other_election, UserFactory(first_name="Barbara", last_name="Liskov"), self.nominator_as_nominee
)

# Somebody else's nomination in the election under test.
self.unrelated_nominee = self._nominee(self.election, "Ada", "Lovelace")
self._nomination(self.election, UserFactory(first_name="Ken", last_name="Thompson"), self.unrelated_nominee)

def _nominee(self, election, first_name, last_name):
user = UserFactory(first_name=first_name, last_name=last_name)
return Nominee.objects.create(user=user, election=election)

def _nomination(self, election, nominator, nominee):
return Nomination.objects.create(
election=election,
nominator=nominator,
nominee=nominee,
name=f"{nominee.user.first_name} {nominee.user.last_name}",
email=nominee.user.email,
nomination_statement="A strong candidate.",
)

def _url(self, election):
return reverse("nominations:nominees_list", kwargs={"election": election.slug})

def test_preview_lists_people_the_user_nominated(self):
self.client.force_login(self.nominator)
response = self.client.get(self._url(self.election))
self.assertEqual(response.status_code, 200)
self.assertIn(self.nominee, response.context["object_list"])

def test_preview_excludes_other_elections(self):
self.client.force_login(self.nominator)
response = self.client.get(self._url(self.election))
self.assertNotIn(self.nominator_as_nominee, response.context["object_list"])

def test_preview_excludes_nominations_by_other_people(self):
self.client.force_login(self.nominator)
response = self.client.get(self._url(self.election))
self.assertNotIn(self.unrelated_nominee, response.context["object_list"])

def test_preview_includes_the_user_as_a_candidate(self):
self.client.force_login(self.nominator)
response = self.client.get(self._url(self.other_election))
self.assertIn(self.nominator_as_nominee, response.context["object_list"])

def test_nominator_can_open_the_nominee_they_nominated(self):
self.client.force_login(self.nominator)
response = self.client.get(self.nominee.get_absolute_url())
self.assertEqual(response.status_code, 200)

def test_other_users_still_cannot_open_the_nominee(self):
self.client.force_login(UserFactory(first_name="Guido", last_name="van Rossum"))
response = self.client.get(self.nominee.get_absolute_url())
self.assertEqual(response.status_code, 404)
14 changes: 13 additions & 1 deletion apps/nominations/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.contrib import messages
from django.contrib.auth.mixins import UserPassesTestMixin
from django.db.models import Q
from django.http import Http404, JsonResponse
from django.shortcuts import get_object_or_404
from django.urls import reverse
Expand Down Expand Up @@ -72,7 +73,18 @@ def get_queryset(self, *args, **kwargs):
return Nominee.objects.filter(accepted=True, approved=True, election=election).exclude(user=None)

if self.request.user.is_authenticated:
return Nominee.objects.filter(user=self.request.user)
# Before the results are public, preview the nominees relevant to
# this user in this election: the people they nominated, plus
# themselves when somebody nominated them.
return (
Nominee.objects.filter(
Q(user=self.request.user) | Q(nominations__nominator=self.request.user),
election=election,
)
Comment thread
Copilot marked this conversation as resolved.
Resolved
.exclude(user=None)
.distinct()
.select_related("user")
)
Comment thread
Copilot marked this conversation as resolved.
Resolved
return None


Expand Down
Loading