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
diff --git a/lib/lp/registry/scripts/closeaccount.py b/lib/lp/registry/scripts/closeaccount.py
index 505f4e8..df4c8ad 100644
--- a/lib/lp/registry/scripts/closeaccount.py
+++ b/lib/lp/registry/scripts/closeaccount.py
@@ -11,7 +11,7 @@ __all__ = [
11from typing import List, Tuple11from typing import List, Tuple
1212
13import six13import six
14from storm.expr import And, LeftJoin, Lower, Or14from storm.expr import Join, LeftJoin, Lower, Or
15from zope.component import getUtility15from zope.component import getUtility
16from zope.security.proxy import removeSecurityProxy16from zope.security.proxy import removeSecurityProxy
1717
@@ -491,29 +491,37 @@ def close_account(username, log):
491491
492 # Check announcements, skipping the ones492 # Check announcements, skipping the ones
493 # that are related to inactive products.493 # that are related to inactive products.
494 count = store.find(494 count = (
495 Announcement,495 store.using(
496 Or(496 Announcement,
497 And(Announcement.product == Product.id, Product.active),497 LeftJoin(Product, Announcement.product == Product.id),
498 Announcement.product == None,498 )
499 ),499 .find(
500 Announcement.registrant == person,500 Announcement,
501 ).count()501 Or(Product.active, Announcement.product == None),
502 Announcement.registrant == person,
503 )
504 .count()
505 )
502 if count:506 if count:
503 reference_counts.append(("announcement.registrant", count))507 reference_counts.append(("announcement.registrant", count))
504 skip.add(("announcement", "registrant"))508 skip.add(("announcement", "registrant"))
505509
506 # Check MilestoneTags, skipping the ones510 # Check MilestoneTags, skipping the ones
507 # that are related to inactive products / product series.511 # that are related to inactive products / product series.
508 count = store.find(512 count = (
509 MilestoneTag,513 store.using(
510 MilestoneTag.milestone_id == Milestone.id,514 MilestoneTag,
511 Or(515 Join(Milestone, MilestoneTag.milestone_id == Milestone.id),
512 And(Milestone.product == Product.id, Product.active),516 LeftJoin(Product, Milestone.product == Product.id),
513 Milestone.product == None,517 )
514 ),518 .find(
515 MilestoneTag.created_by_id == person.id,519 MilestoneTag,
516 ).count()520 Or(Product.active, Milestone.product == None),
521 MilestoneTag.created_by_id == person.id,
522 )
523 .count()
524 )
517 if count:525 if count:
518 reference_counts.append(("milestonetag.created_by", count))526 reference_counts.append(("milestonetag.created_by", count))
519 skip.add(("milestonetag", "created_by"))527 skip.add(("milestonetag", "created_by"))
@@ -531,7 +539,7 @@ def close_account(username, log):
531 reference_counts.append(("productrelease.owner", count))539 reference_counts.append(("productrelease.owner", count))
532 skip.add(("productrelease", "owner"))540 skip.add(("productrelease", "owner"))
533541
534 # Check ProductReleases, skipping the ones542 # Check ProductReleaseFiles, skipping the ones
535 # that are related to inactive products / product series.543 # that are related to inactive products / product series.
536 count = store.find(544 count = store.find(
537 ProductReleaseFile,545 ProductReleaseFile,
@@ -547,34 +555,36 @@ def close_account(username, log):
547555
548 # Check Branches, skipping the ones that are related to inactive products.556 # Check Branches, skipping the ones that are related to inactive products.
549 for col_name in "owner", "reviewer":557 for col_name in "owner", "reviewer":
550 count = store.find(558 count = (
551 Branch,559 store.using(
552 Or(560 Branch,
553 And(561 LeftJoin(Product, Branch.product == Product.id),
554 Branch.product == Product.id,562 )
555 Product.active,563 .find(
556 ),564 Branch,
557 Branch.product == None,565 Or(Product.active, Branch.product == None),
558 ),566 getattr(Branch, col_name) == person.id,
559 getattr(Branch, col_name) == person.id,567 )
560 ).count()568 .count()
569 )
561 if count:570 if count:
562 reference_counts.append(("branch.{}".format(col_name), count))571 reference_counts.append(("branch.{}".format(col_name), count))
563 skip.add(("branch", col_name))572 skip.add(("branch", col_name))
564573
565 # Check Specification, skipping the ones574 # Check Specification, skipping the ones
566 # that are related to inactive products / product series.575 # that are related to inactive products / product series.
567 count = store.find(576 count = (
568 Specification,577 store.using(
569 Or(578 Specification,
570 And(579 LeftJoin(Product, Specification.product == Product.id),
571 Specification.product == Product.id,580 )
572 Product.active,581 .find(
573 ),582 Specification,
574 Specification.product == None,583 Or(Product.active, Specification.product == None),
575 ),584 Specification._assignee == person.id,
576 Specification._assignee == person.id,585 )
577 ).count()586 .count()
587 )
578 if count:588 if count:
579 reference_counts.append(("specification.assignee", count))589 reference_counts.append(("specification.assignee", count))
580 skip.add(("specification", "assignee"))590 skip.add(("specification", "assignee"))
diff --git a/lib/lp/registry/scripts/tests/test_closeaccount.py b/lib/lp/registry/scripts/tests/test_closeaccount.py
index 7a13f9d..f63aebf 100644
--- a/lib/lp/registry/scripts/tests/test_closeaccount.py
+++ b/lib/lp/registry/scripts/tests/test_closeaccount.py
@@ -1044,10 +1044,14 @@ class TestCloseAccount(TestCaseWithFactory):
1044 self.runScript,1044 self.runScript,
1045 script,1045 script,
1046 )1046 )
1047 self.assertIn(
1048 "ERROR User %s is still referenced by 1 announcement.registrant "
1049 "values" % person.name,
1050 script.logger.getLogBuffer(),
1051 )
1047 self.assertNotRemoved(account_id, person_id)1052 self.assertNotRemoved(account_id, person_id)
10481053
1049 def test_skip_milestone_tags_from_inactive_products(self):1054 def test_skip_milestone_tags_from_inactive_products(self):
1050
1051 active_product = self.factory.makeProduct()1055 active_product = self.factory.makeProduct()
1052 inactive_product = self.factory.makeProduct()1056 inactive_product = self.factory.makeProduct()
1053 inactive_product.active = False1057 inactive_product.active = False
@@ -1082,6 +1086,11 @@ class TestCloseAccount(TestCaseWithFactory):
1082 self.runScript,1086 self.runScript,
1083 script,1087 script,
1084 )1088 )
1089 self.assertIn(
1090 "ERROR User %s is still referenced by 1 "
1091 "milestonetag.created_by values" % person.name,
1092 script.logger.getLogBuffer(),
1093 )
1085 self.assertNotRemoved(account_id, person_id)1094 self.assertNotRemoved(account_id, person_id)
1086 else:1095 else:
1087 self.runScript(script)1096 self.runScript(script)
@@ -1196,6 +1205,11 @@ class TestCloseAccount(TestCaseWithFactory):
1196 self.runScript,1205 self.runScript,
1197 script,1206 script,
1198 )1207 )
1208 self.assertIn(
1209 "ERROR User %s is still referenced by 1 "
1210 "branch.%s values" % (person.name, col_name),
1211 script.logger.getLogBuffer(),
1212 )
1199 self.assertNotRemoved(account_id, person_id)1213 self.assertNotRemoved(account_id, person_id)
1200 else:1214 else:
1201 self.runScript(script)1215 self.runScript(script)
@@ -1227,6 +1241,11 @@ class TestCloseAccount(TestCaseWithFactory):
1227 self.runScript,1241 self.runScript,
1228 script,1242 script,
1229 )1243 )
1244 self.assertIn(
1245 "ERROR User %s is still referenced by 1 "
1246 "specification.assignee values" % person.name,
1247 script.logger.getLogBuffer(),
1248 )
1230 self.assertNotRemoved(account_id, person_id)1249 self.assertNotRemoved(account_id, person_id)
1231 else:1250 else:
1232 self.runScript(script)1251 self.runScript(script)

Subscribers

People subscribed via source and target branches

to status/vote changes: