Merge ~cjwatson/launchpad:fix-close-account-joins into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: a13a6a4ae1dda9f45da72219bbccf33107d720d2
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:fix-close-account-joins
Merge into: launchpad:master
Diff against target: 192 lines (+71/-42)
2 files modified
lib/lp/registry/scripts/closeaccount.py (+51/-41)
lib/lp/registry/scripts/tests/test_closeaccount.py (+20/-1)
Reviewer Review Type Date Requested Status
Guruprasad Approve
Review via email: mp+433735@code.launchpad.net

Commit message

close-account: Fix reported reference counts from several columns

Description of the change

In cases involving a nullable foreign key, we need to use left joins to get correct counts.

To post a comment you must log in.
Revision history for this message
Guruprasad (lgp171188) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/registry/scripts/closeaccount.py b/lib/lp/registry/scripts/closeaccount.py
2index 505f4e8..df4c8ad 100644
3--- a/lib/lp/registry/scripts/closeaccount.py
4+++ b/lib/lp/registry/scripts/closeaccount.py
5@@ -11,7 +11,7 @@ __all__ = [
6 from typing import List, Tuple
7
8 import six
9-from storm.expr import And, LeftJoin, Lower, Or
10+from storm.expr import Join, LeftJoin, Lower, Or
11 from zope.component import getUtility
12 from zope.security.proxy import removeSecurityProxy
13
14@@ -491,29 +491,37 @@ def close_account(username, log):
15
16 # Check announcements, skipping the ones
17 # that are related to inactive products.
18- count = store.find(
19- Announcement,
20- Or(
21- And(Announcement.product == Product.id, Product.active),
22- Announcement.product == None,
23- ),
24- Announcement.registrant == person,
25- ).count()
26+ count = (
27+ store.using(
28+ Announcement,
29+ LeftJoin(Product, Announcement.product == Product.id),
30+ )
31+ .find(
32+ Announcement,
33+ Or(Product.active, Announcement.product == None),
34+ Announcement.registrant == person,
35+ )
36+ .count()
37+ )
38 if count:
39 reference_counts.append(("announcement.registrant", count))
40 skip.add(("announcement", "registrant"))
41
42 # Check MilestoneTags, skipping the ones
43 # that are related to inactive products / product series.
44- count = store.find(
45- MilestoneTag,
46- MilestoneTag.milestone_id == Milestone.id,
47- Or(
48- And(Milestone.product == Product.id, Product.active),
49- Milestone.product == None,
50- ),
51- MilestoneTag.created_by_id == person.id,
52- ).count()
53+ count = (
54+ store.using(
55+ MilestoneTag,
56+ Join(Milestone, MilestoneTag.milestone_id == Milestone.id),
57+ LeftJoin(Product, Milestone.product == Product.id),
58+ )
59+ .find(
60+ MilestoneTag,
61+ Or(Product.active, Milestone.product == None),
62+ MilestoneTag.created_by_id == person.id,
63+ )
64+ .count()
65+ )
66 if count:
67 reference_counts.append(("milestonetag.created_by", count))
68 skip.add(("milestonetag", "created_by"))
69@@ -531,7 +539,7 @@ def close_account(username, log):
70 reference_counts.append(("productrelease.owner", count))
71 skip.add(("productrelease", "owner"))
72
73- # Check ProductReleases, skipping the ones
74+ # Check ProductReleaseFiles, skipping the ones
75 # that are related to inactive products / product series.
76 count = store.find(
77 ProductReleaseFile,
78@@ -547,34 +555,36 @@ def close_account(username, log):
79
80 # Check Branches, skipping the ones that are related to inactive products.
81 for col_name in "owner", "reviewer":
82- count = store.find(
83- Branch,
84- Or(
85- And(
86- Branch.product == Product.id,
87- Product.active,
88- ),
89- Branch.product == None,
90- ),
91- getattr(Branch, col_name) == person.id,
92- ).count()
93+ count = (
94+ store.using(
95+ Branch,
96+ LeftJoin(Product, Branch.product == Product.id),
97+ )
98+ .find(
99+ Branch,
100+ Or(Product.active, Branch.product == None),
101+ getattr(Branch, col_name) == person.id,
102+ )
103+ .count()
104+ )
105 if count:
106 reference_counts.append(("branch.{}".format(col_name), count))
107 skip.add(("branch", col_name))
108
109 # Check Specification, skipping the ones
110 # that are related to inactive products / product series.
111- count = store.find(
112- Specification,
113- Or(
114- And(
115- Specification.product == Product.id,
116- Product.active,
117- ),
118- Specification.product == None,
119- ),
120- Specification._assignee == person.id,
121- ).count()
122+ count = (
123+ store.using(
124+ Specification,
125+ LeftJoin(Product, Specification.product == Product.id),
126+ )
127+ .find(
128+ Specification,
129+ Or(Product.active, Specification.product == None),
130+ Specification._assignee == person.id,
131+ )
132+ .count()
133+ )
134 if count:
135 reference_counts.append(("specification.assignee", count))
136 skip.add(("specification", "assignee"))
137diff --git a/lib/lp/registry/scripts/tests/test_closeaccount.py b/lib/lp/registry/scripts/tests/test_closeaccount.py
138index 7a13f9d..f63aebf 100644
139--- a/lib/lp/registry/scripts/tests/test_closeaccount.py
140+++ b/lib/lp/registry/scripts/tests/test_closeaccount.py
141@@ -1044,10 +1044,14 @@ class TestCloseAccount(TestCaseWithFactory):
142 self.runScript,
143 script,
144 )
145+ self.assertIn(
146+ "ERROR User %s is still referenced by 1 announcement.registrant "
147+ "values" % person.name,
148+ script.logger.getLogBuffer(),
149+ )
150 self.assertNotRemoved(account_id, person_id)
151
152 def test_skip_milestone_tags_from_inactive_products(self):
153-
154 active_product = self.factory.makeProduct()
155 inactive_product = self.factory.makeProduct()
156 inactive_product.active = False
157@@ -1082,6 +1086,11 @@ class TestCloseAccount(TestCaseWithFactory):
158 self.runScript,
159 script,
160 )
161+ self.assertIn(
162+ "ERROR User %s is still referenced by 1 "
163+ "milestonetag.created_by values" % person.name,
164+ script.logger.getLogBuffer(),
165+ )
166 self.assertNotRemoved(account_id, person_id)
167 else:
168 self.runScript(script)
169@@ -1196,6 +1205,11 @@ class TestCloseAccount(TestCaseWithFactory):
170 self.runScript,
171 script,
172 )
173+ self.assertIn(
174+ "ERROR User %s is still referenced by 1 "
175+ "branch.%s values" % (person.name, col_name),
176+ script.logger.getLogBuffer(),
177+ )
178 self.assertNotRemoved(account_id, person_id)
179 else:
180 self.runScript(script)
181@@ -1227,6 +1241,11 @@ class TestCloseAccount(TestCaseWithFactory):
182 self.runScript,
183 script,
184 )
185+ self.assertIn(
186+ "ERROR User %s is still referenced by 1 "
187+ "specification.assignee values" % person.name,
188+ script.logger.getLogBuffer(),
189+ )
190 self.assertNotRemoved(account_id, person_id)
191 else:
192 self.runScript(script)

Subscribers

People subscribed via source and target branches

to status/vote changes: