Skip to content

Commit f4ff605

Browse files
committed
Only show unique checks
Commit e5c641f optimized fetching of checks when displaying a patch by prefetching these checks ahead of time. Unfortunately we missed that this should exclude older versions of checks for a given context. Make the code used to generate the unique checks generic and allow us to use that as a filter to the checks provided to the template, restoring the correct behavior. Signed-off-by: Stephen Finucane <[email protected]> Closes: #398 Fixes: e5c641f ("Optimise fetching checks when displaying a patch") (cherry picked from commit a43d6ac)
1 parent d5b10d4 commit f4ff605

File tree

3 files changed

+89
-43
lines changed

3 files changed

+89
-43
lines changed

patchwork/models.py

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -518,50 +518,13 @@ def is_editable(self, user):
518518
return True
519519
return False
520520

521-
@property
522-
def combined_check_state(self):
523-
"""Return the combined state for all checks.
524-
525-
Generate the combined check's state for this patch. This check
526-
is one of the following, based on the value of each unique
527-
check:
528-
529-
* failure, if any context's latest check reports as failure
530-
* warning, if any context's latest check reports as warning
531-
* pending, if there are no checks, or a context's latest
532-
Check reports as pending
533-
* success, if latest checks for all contexts reports as
534-
success
535-
"""
536-
state_names = dict(Check.STATE_CHOICES)
537-
states = [check.state for check in self.checks]
538-
539-
if not states:
540-
return state_names[Check.STATE_PENDING]
541-
542-
for state in [Check.STATE_FAIL, Check.STATE_WARNING,
543-
Check.STATE_PENDING]: # order sensitive
544-
if state in states:
545-
return state_names[state]
546-
547-
return state_names[Check.STATE_SUCCESS]
548-
549-
@property
550-
def checks(self):
551-
"""Return the list of unique checks.
552-
553-
Generate a list of checks associated with this patch for each
554-
type of Check. Only "unique" checks are considered,
555-
identified by their 'context' field. This means, given n
556-
checks with the same 'context', the newest check is the only
557-
one counted regardless of its value. The end result will be a
558-
association of types to number of unique checks for said
559-
type.
560-
"""
521+
@staticmethod
522+
def filter_unique_checks(checks):
523+
"""Filter the provided checks to generate the unique list."""
561524
unique = {}
562525
duplicates = []
563526

564-
for check in self.check_set.all():
527+
for check in checks:
565528
ctx = check.context
566529
user = check.user_id
567530

@@ -588,7 +551,50 @@ def checks(self):
588551
# prefetch_related.) So, do it 'by hand' in Python. We can
589552
# also be confident that this won't be worse, seeing as we've
590553
# just iterated over self.check_set.all() *anyway*.
591-
return [c for c in self.check_set.all() if c.id not in duplicates]
554+
return [c for c in checks if c.id not in duplicates]
555+
556+
@property
557+
def checks(self):
558+
"""Return the list of unique checks.
559+
560+
Generate a list of checks associated with this patch for each
561+
type of Check. Only "unique" checks are considered,
562+
identified by their 'context' field. This means, given n
563+
checks with the same 'context', the newest check is the only
564+
one counted regardless of its value. The end result will be a
565+
association of types to number of unique checks for said
566+
type.
567+
"""
568+
return self.filter_unique_checks(self.check_set.all())
569+
570+
@property
571+
def combined_check_state(self):
572+
"""Return the combined state for all checks.
573+
574+
Generate the combined check's state for this patch. This check
575+
is one of the following, based on the value of each unique
576+
check:
577+
578+
* failure, if any context's latest check reports as failure
579+
* warning, if any context's latest check reports as warning
580+
* pending, if there are no checks, or a context's latest check reports
581+
as pending
582+
* success, if latest checks for all contexts reports as success
583+
"""
584+
state_names = dict(Check.STATE_CHOICES)
585+
states = [check.state for check in self.checks]
586+
587+
if not states:
588+
return state_names[Check.STATE_PENDING]
589+
590+
# order sensitive
591+
for state in (
592+
Check.STATE_FAIL, Check.STATE_WARNING, Check.STATE_PENDING,
593+
):
594+
if state in states:
595+
return state_names[state]
596+
597+
return state_names[Check.STATE_SUCCESS]
592598

593599
@property
594600
def check_count(self):

patchwork/tests/test_detail.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@
33
#
44
# SPDX-License-Identifier: GPL-2.0-or-later
55

6+
from datetime import datetime as dt
7+
from datetime import timedelta
8+
69
from django.test import TestCase
710
from django.urls import reverse
811

12+
from patchwork.models import Check
13+
from patchwork.tests.utils import create_check
914
from patchwork.tests.utils import create_cover
1015
from patchwork.tests.utils import create_cover_comment
1116
from patchwork.tests.utils import create_patch
1217
from patchwork.tests.utils import create_patch_comment
1318
from patchwork.tests.utils import create_project
19+
from patchwork.tests.utils import create_user
1420

1521

1622
class CoverViewTest(TestCase):
@@ -157,6 +163,38 @@ def test_invalid_patch_id(self):
157163
response = self.client.get(requested_url)
158164
self.assertEqual(response.status_code, 404)
159165

166+
def test_patch_with_checks(self):
167+
user = create_user()
168+
patch = create_patch()
169+
check_a = create_check(
170+
patch=patch, user=user, context='foo', state=Check.STATE_FAIL,
171+
date=(dt.utcnow() - timedelta(days=1)))
172+
create_check(
173+
patch=patch, user=user, context='foo', state=Check.STATE_SUCCESS)
174+
check_b = create_check(
175+
patch=patch, user=user, context='bar', state=Check.STATE_PENDING)
176+
requested_url = reverse(
177+
'patch-detail',
178+
kwargs={
179+
'project_id': patch.project.linkname,
180+
'msgid': patch.url_msgid,
181+
},
182+
)
183+
response = self.client.get(requested_url)
184+
185+
# the response should contain checks
186+
self.assertContains(response, '<h2>Checks</h2>')
187+
188+
# and it should only show the unique checks
189+
self.assertEqual(
190+
1, response.content.decode().count(
191+
f'<td>{check_a.user}/{check_a.context}</td>'
192+
))
193+
self.assertEqual(
194+
1, response.content.decode().count(
195+
f'<td>{check_b.user}/{check_b.context}</td>'
196+
))
197+
160198

161199
class CommentRedirectTest(TestCase):
162200

patchwork/views/patch.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ def patch_detail(request, project_id, msgid):
124124
related_different_project = []
125125

126126
context['comments'] = comments
127-
context['checks'] = patch.check_set.all().select_related('user')
127+
context['checks'] = Patch.filter_unique_checks(
128+
patch.check_set.all().select_related('user'),
129+
)
128130
context['submission'] = patch
129131
context['patchform'] = form
130132
context['createbundleform'] = createbundleform

0 commit comments

Comments
 (0)