Skip to content

Commit 6b41259

Browse files
authored
Merge pull request #1285 from makeabilitylab/1284-refactor-view-project-people
Refactor view_project_people: fix director hardcoding + N+1 query storm
2 parents d8ab255 + 9cd1ad1 commit 6b41259

2 files changed

Lines changed: 388 additions & 196 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""
2+
Regression tests for the internal "view project people" page
3+
(:func:`website.views.view_project_people.view_project_people`).
4+
5+
This page builds a JSON payload of every Person plus per-project role data and
6+
renders an entirely client-side grid (used to generate acknowledgment slides for
7+
talks). These tests lock the contract of that payload — project-role aggregation,
8+
director identification (by ``Title.DIRECTOR`` position), PhD-advisee flagging, and
9+
the publication indicators — so the view can be refactored safely.
10+
"""
11+
12+
import json
13+
from datetime import date, timedelta
14+
15+
from django.urls import reverse
16+
17+
from website.models import Person, Project, Publication
18+
from website.models.position import Position, Role, Title
19+
from website.models.project_role import ProjectRole
20+
from website.models.publication import PubType
21+
from website.tests.base import DatabaseTestCase
22+
23+
24+
class ViewProjectPeopleTests(DatabaseTestCase):
25+
"""Integration tests that exercise the real view, queryset, and template."""
26+
27+
def _get_people_by_id(self):
28+
"""GET the page and return (response, {person_id: person_payload})."""
29+
response = self.client.get(reverse("website:view_project_people"))
30+
self.assertEqual(response.status_code, 200)
31+
people = json.loads(response.context["people_json"])
32+
return response, {p["id"]: p for p in people}
33+
34+
def _add_position(self, person, title, start, end=None, **kwargs):
35+
"""Create a Position for `person` (Member role unless overridden)."""
36+
kwargs.setdefault("role", Role.MEMBER)
37+
return Position.objects.create(
38+
person=person, title=title, start_date=start, end_date=end, **kwargs
39+
)
40+
41+
def test_page_renders_and_payloads_parse(self):
42+
"""The view returns 200 and all three context JSON blobs are valid JSON."""
43+
person = self.make_person(first_name="Ada", last_name="Lovelace")
44+
self._add_position(person, Title.PHD_STUDENT, date(2020, 1, 1))
45+
46+
response = self.client.get(reverse("website:view_project_people"))
47+
self.assertEqual(response.status_code, 200)
48+
for key in ("people_json", "projects_json", "abstracted_titles_json"):
49+
parsed = json.loads(response.context[key])
50+
self.assertIsInstance(parsed, list)
51+
52+
def test_project_role_aggregation(self):
53+
"""project_roles aggregates total days and start/end dates per project."""
54+
person = self.make_person(first_name="Grace", last_name="Hopper")
55+
self._add_position(person, Title.PHD_STUDENT, date(2019, 1, 1))
56+
project = Project.objects.create(name="Test Project", short_name="TestProj")
57+
start, end = date(2020, 1, 1), date(2020, 12, 31)
58+
ProjectRole.objects.create(
59+
person=person, project=project, start_date=start, end_date=end
60+
)
61+
62+
_, people = self._get_people_by_id()
63+
roles = people[person.id]["project_roles"]
64+
self.assertIn("TestProj", roles)
65+
self.assertEqual(roles["TestProj"]["total_days"], (end - start).days)
66+
self.assertEqual(roles["TestProj"]["start_date"], "2020-01-01")
67+
self.assertEqual(roles["TestProj"]["end_date"], "2020-12-31")
68+
69+
def test_director_flagged_by_title(self):
70+
"""is_director is true for whoever holds a Title.DIRECTOR position."""
71+
director = self.make_person(first_name="Dee", last_name="Rector")
72+
self._add_position(director, Title.DIRECTOR, date(2010, 1, 1))
73+
other = self.make_person(first_name="Reg", last_name="Ular")
74+
self._add_position(other, Title.PHD_STUDENT, date(2020, 1, 1))
75+
76+
_, people = self._get_people_by_id()
77+
self.assertTrue(people[director.id]["is_director"])
78+
self.assertFalse(people[other.id]["is_director"])
79+
80+
def test_phd_advisee_logic_matches_model(self):
81+
"""
82+
is_phd_advisee mirrors Person.is_phd_advisee_of: current advisee -> true,
83+
past advisee without dissertation -> false, past advisee with one -> true.
84+
"""
85+
director = self.make_person(first_name="Dee", last_name="Rector")
86+
self._add_position(director, Title.DIRECTOR, date(2010, 1, 1))
87+
88+
current = self.make_person(first_name="Cur", last_name="Rent")
89+
self._add_position(current, Title.PHD_STUDENT, date(2022, 1, 1), advisor=director)
90+
91+
past = date.today() - timedelta(days=400)
92+
past_no_diss = self.make_person(first_name="Past", last_name="Nodiss")
93+
self._add_position(
94+
past_no_diss, Title.PHD_STUDENT, date(2015, 1, 1), end=past, advisor=director
95+
)
96+
97+
past_with_diss = self.make_person(first_name="Past", last_name="Withdiss")
98+
self._add_position(
99+
past_with_diss, Title.PHD_STUDENT, date(2015, 1, 1), end=past, co_advisor=director
100+
)
101+
dissertation = self.make_publication(
102+
title="A Dissertation", pub_venue_type=PubType.PHD_DISSERTATION
103+
)
104+
dissertation.authors.add(past_with_diss)
105+
106+
_, people = self._get_people_by_id()
107+
self.assertTrue(people[current.id]["is_phd_advisee"])
108+
self.assertFalse(people[past_no_diss.id]["is_phd_advisee"])
109+
self.assertTrue(people[past_with_diss.id]["is_phd_advisee"])
110+
# Sanity-check against the canonical model method.
111+
self.assertEqual(
112+
people[current.id]["is_phd_advisee"], current.is_phd_advisee_of(director)
113+
)
114+
self.assertEqual(
115+
people[past_no_diss.id]["is_phd_advisee"],
116+
past_no_diss.is_phd_advisee_of(director),
117+
)
118+
119+
def test_publication_indicators(self):
120+
"""has_any_publication and projects_published_on reflect authored pubs."""
121+
project = Project.objects.create(name="Pub Project", short_name="PubProj")
122+
author = self.make_person(first_name="Wri", last_name="Ter")
123+
self._add_position(author, Title.PHD_STUDENT, date(2020, 1, 1))
124+
pub = self.make_publication(title="Authored Paper")
125+
pub.authors.add(author)
126+
pub.projects.add(project)
127+
128+
non_author = self.make_person(first_name="Non", last_name="Author")
129+
self._add_position(non_author, Title.UGRAD, date(2021, 1, 1))
130+
131+
_, people = self._get_people_by_id()
132+
self.assertTrue(people[author.id]["has_any_publication"])
133+
self.assertIn("PubProj", people[author.id]["projects_published_on"])
134+
self.assertFalse(people[non_author.id]["has_any_publication"])
135+
self.assertEqual(people[non_author.id]["projects_published_on"], [])

0 commit comments

Comments
 (0)