Merge ~cjwatson/launchpad:rename-conjoined-bug-tasks into launchpad:master
- Git
- lp:~cjwatson/launchpad
- rename-conjoined-bug-tasks
- Merge into master
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 5d65b0763acbe446c92844a7ec3b2676366d3424 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:rename-conjoined-bug-tasks |
Merge into: | launchpad:master |
Diff against target: |
1323 lines (+238/-238) 26 files modified
lib/lp/bugs/browser/bugnomination.py (+1/-1) lib/lp/bugs/browser/bugtask.py (+14/-14) lib/lp/bugs/configure.zcml (+3/-3) lib/lp/bugs/doc/bug-set-status.txt (+4/-4) lib/lp/bugs/doc/bugtask-expiration.txt (+8/-8) lib/lp/bugs/doc/bugwatch.txt (+12/-12) lib/lp/bugs/interfaces/bug.py (+2/-2) lib/lp/bugs/interfaces/bugtask.py (+6/-6) lib/lp/bugs/model/bug.py (+6/-6) lib/lp/bugs/model/bugtask.py (+45/-45) lib/lp/bugs/model/bugtasksearch.py (+23/-23) lib/lp/bugs/model/bugwatch.py (+2/-2) lib/lp/bugs/model/tests/test_bugtask.py (+23/-24) lib/lp/bugs/model/tests/test_bugtasksearch.py (+1/-1) lib/lp/bugs/scripts/bugexpire.py (+2/-2) lib/lp/bugs/scripts/tests/test_bugimport.py (+1/-1) lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt (+5/-5) lib/lp/bugs/tests/bugtarget-questiontarget.txt (+1/-1) lib/lp/bugs/tests/test_bugsearch_conjoined.py (+41/-41) lib/lp/bugs/tests/test_bugwatch.py (+1/-1) lib/lp/registry/browser/__init__.py (+2/-2) lib/lp/registry/browser/milestone.py (+5/-5) lib/lp/registry/model/milestone.py (+2/-2) lib/lp/registry/model/person.py (+12/-11) lib/lp/registry/tests/test_milestonetag.py (+1/-1) lib/lp/registry/tests/test_person.py (+15/-15) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ioana Lasc (community) | Approve | ||
Review via email: mp+414348@code.launchpad.net |
Commit message
Rename conjoined master/slave bug tasks to primary/replica
Description of the change
Launchpad bugs have a peculiar feature called "conjoined tasks". For example, the tasks on a bug that's being primarily worked on in the jammy series of Ubuntu but whose fix then needs to be backported to focal and impish might look like this:
base-files (Ubuntu) Status tracked in Jammy
-> Focal Triaged Critical
-> Impish Triaged Critical
-> Jammy In Progress Critical
In this case, the task on base-files (Ubuntu) is currently referred to as a "conjoined slave", and the task on base-files (Ubuntu Jammy) is its corresponding "conjoined master". The metadata of the conjoined slave cannot be updated independently; instead, changes are automatically propagated from the conjoined master.
This terminology obviously doesn't fit with inclusive naming standards. Rename "conjoined master" to "conjoined primary" and "conjoined slave" to "conjoined replica", which I think also expresses the metadata replication relationship a little more clearly.
I also considered renaming "conjoined master" to "conjoined series-specific task" (or `conjoined_
Preview Diff
1 | diff --git a/lib/lp/bugs/browser/bugnomination.py b/lib/lp/bugs/browser/bugnomination.py | |||
2 | index 1c2319e..40dddbd 100644 | |||
3 | --- a/lib/lp/bugs/browser/bugnomination.py | |||
4 | +++ b/lib/lp/bugs/browser/bugnomination.py | |||
5 | @@ -143,7 +143,7 @@ class BugNominationTableRowView(LaunchpadView): | |||
6 | 143 | """Browser view class for rendering a nomination table row.""" | 143 | """Browser view class for rendering a nomination table row.""" |
7 | 144 | 144 | ||
8 | 145 | # This method will be called to render the bug nomination. | 145 | # This method will be called to render the bug nomination. |
10 | 146 | renderNonConjoinedSlave = LaunchpadView.__call__ | 146 | renderNonConjoinedReplica = LaunchpadView.__call__ |
11 | 147 | 147 | ||
12 | 148 | def getNominationPerson(self): | 148 | def getNominationPerson(self): |
13 | 149 | """Return the IPerson associated with this nomination. | 149 | """Return the IPerson associated with this nomination. |
14 | diff --git a/lib/lp/bugs/browser/bugtask.py b/lib/lp/bugs/browser/bugtask.py | |||
15 | index f0197a2..1ae7e7c 100644 | |||
16 | --- a/lib/lp/bugs/browser/bugtask.py | |||
17 | +++ b/lib/lp/bugs/browser/bugtask.py | |||
18 | @@ -1937,17 +1937,17 @@ class BugTasksTableView(LaunchpadView): | |||
19 | 1937 | )) | 1937 | )) |
20 | 1938 | 1938 | ||
21 | 1939 | def _getTableRowView(self, context, is_converted_to_question, | 1939 | def _getTableRowView(self, context, is_converted_to_question, |
23 | 1940 | is_conjoined_slave): | 1940 | is_conjoined_replica): |
24 | 1941 | """Get the view for the context, and initialize it. | 1941 | """Get the view for the context, and initialize it. |
25 | 1942 | 1942 | ||
27 | 1943 | The view's is_conjoined_slave and is_converted_to_question | 1943 | The view's is_conjoined_replica and is_converted_to_question |
28 | 1944 | attributes are set, as well as the edit view. | 1944 | attributes are set, as well as the edit view. |
29 | 1945 | """ | 1945 | """ |
30 | 1946 | view = getMultiAdapter( | 1946 | view = getMultiAdapter( |
31 | 1947 | (context, self.request), | 1947 | (context, self.request), |
32 | 1948 | name='+bugtasks-and-nominations-table-row') | 1948 | name='+bugtasks-and-nominations-table-row') |
33 | 1949 | view.is_converted_to_question = is_converted_to_question | 1949 | view.is_converted_to_question = is_converted_to_question |
35 | 1950 | view.is_conjoined_slave = is_conjoined_slave | 1950 | view.is_conjoined_replica = is_conjoined_replica |
36 | 1951 | 1951 | ||
37 | 1952 | view.edit_view = getMultiAdapter( | 1952 | view.edit_view = getMultiAdapter( |
38 | 1953 | (context, self.request), name='+edit-form') | 1953 | (context, self.request), name='+edit-form') |
39 | @@ -1991,7 +1991,7 @@ class BugTasksTableView(LaunchpadView): | |||
40 | 1991 | list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( | 1991 | list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( |
41 | 1992 | ids, need_validity=True)) | 1992 | ids, need_validity=True)) |
42 | 1993 | 1993 | ||
44 | 1994 | # Build a cache we can pass on to getConjoinedMaster(), so that | 1994 | # Build a cache we can pass on to getConjoinedPrimary(), so that |
45 | 1995 | # it doesn't have to iterate over all the bug tasks in each loop | 1995 | # it doesn't have to iterate over all the bug tasks in each loop |
46 | 1996 | # iteration. | 1996 | # iteration. |
47 | 1997 | bugtasks_by_package = bug.getBugTasksByPackageName(all_bugtasks) | 1997 | bugtasks_by_package = bug.getBugTasksByPackageName(all_bugtasks) |
48 | @@ -2017,11 +2017,11 @@ class BugTasksTableView(LaunchpadView): | |||
49 | 2017 | (parent, self.request), | 2017 | (parent, self.request), |
50 | 2018 | name='+bugtasks-and-nominations-table-row')) | 2018 | name='+bugtasks-and-nominations-table-row')) |
51 | 2019 | 2019 | ||
53 | 2020 | conjoined_master = bugtask.getConjoinedMaster( | 2020 | conjoined_primary = bugtask.getConjoinedPrimary( |
54 | 2021 | all_bugtasks, bugtasks_by_package) | 2021 | all_bugtasks, bugtasks_by_package) |
55 | 2022 | view = self._getTableRowView( | 2022 | view = self._getTableRowView( |
56 | 2023 | bugtask, is_converted_to_question, | 2023 | bugtask, is_converted_to_question, |
58 | 2024 | conjoined_master is not None) | 2024 | conjoined_primary is not None) |
59 | 2025 | bugtask_and_nomination_views.append(view) | 2025 | bugtask_and_nomination_views.append(view) |
60 | 2026 | target = bugtask.product or bugtask.distribution | 2026 | target = bugtask.product or bugtask.distribution |
61 | 2027 | if not target: | 2027 | if not target: |
62 | @@ -2042,7 +2042,7 @@ class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin, | |||
63 | 2042 | BugTaskPrivilegeMixin): | 2042 | BugTaskPrivilegeMixin): |
64 | 2043 | """Browser class for rendering a bugtask row on the bug page.""" | 2043 | """Browser class for rendering a bugtask row on the bug page.""" |
65 | 2044 | 2044 | ||
67 | 2045 | is_conjoined_slave = None | 2045 | is_conjoined_replica = None |
68 | 2046 | is_converted_to_question = None | 2046 | is_converted_to_question = None |
69 | 2047 | target_link_title = None | 2047 | target_link_title = None |
70 | 2048 | many_bugtasks = False | 2048 | many_bugtasks = False |
71 | @@ -2074,7 +2074,7 @@ class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin, | |||
72 | 2074 | # time. | 2074 | # time. |
73 | 2075 | expandable=(not self.many_bugtasks and self.canSeeTaskDetails()), | 2075 | expandable=(not self.many_bugtasks and self.canSeeTaskDetails()), |
74 | 2076 | indent_task=ISeriesBugTarget.providedBy(self.context.target), | 2076 | indent_task=ISeriesBugTarget.providedBy(self.context.target), |
76 | 2077 | is_conjoined_slave=self.is_conjoined_slave, | 2077 | is_conjoined_replica=self.is_conjoined_replica, |
77 | 2078 | task_link=task_link, | 2078 | task_link=task_link, |
78 | 2079 | edit_link=edit_link, | 2079 | edit_link=edit_link, |
79 | 2080 | can_edit=can_edit, | 2080 | can_edit=can_edit, |
80 | @@ -2112,13 +2112,13 @@ class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin, | |||
81 | 2112 | It is independent of whether they can *change* the status; you | 2112 | It is independent of whether they can *change* the status; you |
82 | 2113 | need to expand the details to see any milestone set. | 2113 | need to expand the details to see any milestone set. |
83 | 2114 | """ | 2114 | """ |
86 | 2115 | assert self.is_conjoined_slave is not None, ( | 2115 | assert self.is_conjoined_replica is not None, ( |
87 | 2116 | 'is_conjoined_slave should be set before rendering the page.') | 2116 | 'is_conjoined_replica should be set before rendering the page.') |
88 | 2117 | assert self.is_converted_to_question is not None, ( | 2117 | assert self.is_converted_to_question is not None, ( |
89 | 2118 | 'is_converted_to_question should be set before rendering the' | 2118 | 'is_converted_to_question should be set before rendering the' |
90 | 2119 | ' page.') | 2119 | ' page.') |
91 | 2120 | return (self.displayEditForm() and | 2120 | return (self.displayEditForm() and |
93 | 2121 | not self.is_conjoined_slave and | 2121 | not self.is_conjoined_replica and |
94 | 2122 | self.context.bug.duplicateof is None and | 2122 | self.context.bug.duplicateof is None and |
95 | 2123 | not self.is_converted_to_question) | 2123 | not self.is_converted_to_question) |
96 | 2124 | 2124 | ||
97 | @@ -2133,9 +2133,9 @@ class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin, | |||
98 | 2133 | """Get the series to which this task is targeted.""" | 2133 | """Get the series to which this task is targeted.""" |
99 | 2134 | return self._getSeriesTargetNameHelper(self.context) | 2134 | return self._getSeriesTargetNameHelper(self.context) |
100 | 2135 | 2135 | ||
104 | 2136 | def getConjoinedMasterName(self): | 2136 | def getConjoinedPrimaryName(self): |
105 | 2137 | """Get the conjoined master's name for displaying.""" | 2137 | """Get the conjoined primary's name for displaying.""" |
106 | 2138 | return self._getSeriesTargetNameHelper(self.context.conjoined_master) | 2138 | return self._getSeriesTargetNameHelper(self.context.conjoined_primary) |
107 | 2139 | 2139 | ||
108 | 2140 | @property | 2140 | @property |
109 | 2141 | def bugtask_icon(self): | 2141 | def bugtask_icon(self): |
110 | diff --git a/lib/lp/bugs/configure.zcml b/lib/lp/bugs/configure.zcml | |||
111 | index b2a54ac..5b46af7 100644 | |||
112 | --- a/lib/lp/bugs/configure.zcml | |||
113 | +++ b/lib/lp/bugs/configure.zcml | |||
114 | @@ -226,10 +226,10 @@ | |||
115 | 226 | getDelta | 226 | getDelta |
116 | 227 | pillar | 227 | pillar |
117 | 228 | bugtask_branches | 228 | bugtask_branches |
120 | 229 | conjoined_master | 229 | conjoined_primary |
121 | 230 | conjoined_slave | 230 | conjoined_replica |
122 | 231 | subscribe | 231 | subscribe |
124 | 232 | getConjoinedMaster | 232 | getConjoinedPrimary |
125 | 233 | findSimilarBugs | 233 | findSimilarBugs |
126 | 234 | getContributorInfo"/> | 234 | getContributorInfo"/> |
127 | 235 | <require | 235 | <require |
128 | diff --git a/lib/lp/bugs/doc/bug-set-status.txt b/lib/lp/bugs/doc/bug-set-status.txt | |||
129 | index 4c4f033..f89dee0 100644 | |||
130 | --- a/lib/lp/bugs/doc/bug-set-status.txt | |||
131 | +++ b/lib/lp/bugs/doc/bug-set-status.txt | |||
132 | @@ -109,14 +109,14 @@ is edited. | |||
133 | 109 | >>> firefox_trunk_bugtask.status.name | 109 | >>> firefox_trunk_bugtask.status.name |
134 | 110 | 'INCOMPLETE' | 110 | 'INCOMPLETE' |
135 | 111 | 111 | ||
138 | 112 | If the target bugtask has a conjoined master bugtask, the conjoined | 112 | If the target bugtask has a conjoined primary bugtask, the conjoined |
139 | 113 | master will be edited and returned. The conjoined slave is of course | 113 | primary will be edited and returned. The conjoined replica is of course |
140 | 114 | updated automatically. | 114 | updated automatically. |
141 | 115 | 115 | ||
143 | 116 | >>> firefox_bugtask = firefox_trunk_bugtask.conjoined_slave | 116 | >>> firefox_bugtask = firefox_trunk_bugtask.conjoined_replica |
144 | 117 | >>> print(firefox_bugtask.target.name) | 117 | >>> print(firefox_bugtask.target.name) |
145 | 118 | firefox | 118 | firefox |
147 | 119 | >>> firefox_bugtask.conjoined_master is not None | 119 | >>> firefox_bugtask.conjoined_primary is not None |
148 | 120 | True | 120 | True |
149 | 121 | >>> firefox_bugtask.status.name | 121 | >>> firefox_bugtask.status.name |
150 | 122 | 'INCOMPLETE' | 122 | 'INCOMPLETE' |
151 | diff --git a/lib/lp/bugs/doc/bugtask-expiration.txt b/lib/lp/bugs/doc/bugtask-expiration.txt | |||
152 | index f34bb17..9478b1c 100644 | |||
153 | --- a/lib/lp/bugs/doc/bugtask-expiration.txt | |||
154 | +++ b/lib/lp/bugs/doc/bugtask-expiration.txt | |||
155 | @@ -91,7 +91,7 @@ that no bug tasks can be expired. | |||
156 | 91 | ... 'test@canonical.com') | 91 | ... 'test@canonical.com') |
157 | 92 | 92 | ||
158 | 93 | # A expirable bugtask. It will be expired because its conjoined | 93 | # A expirable bugtask. It will be expired because its conjoined |
160 | 94 | # master can be expired. | 94 | # primary can be expired. |
161 | 95 | >>> from lp.bugs.tests.bug import create_old_bug | 95 | >>> from lp.bugs.tests.bug import create_old_bug |
162 | 96 | >>> ubuntu_bugtask = create_old_bug('expirable_distro', 351, ubuntu) | 96 | >>> ubuntu_bugtask = create_old_bug('expirable_distro', 351, ubuntu) |
163 | 97 | >>> ubuntu_bugtask.bug.permits_expiration | 97 | >>> ubuntu_bugtask.bug.permits_expiration |
164 | @@ -100,10 +100,10 @@ that no bug tasks can be expired. | |||
165 | 100 | True | 100 | True |
166 | 101 | 101 | ||
167 | 102 | # An expirable bugtask, a distroseries. The ubuntu bugtask is its | 102 | # An expirable bugtask, a distroseries. The ubuntu bugtask is its |
169 | 103 | # conjoined slave. | 103 | # conjoined replica. |
170 | 104 | >>> hoary_bugtask = bugtaskset.createTask( | 104 | >>> hoary_bugtask = bugtaskset.createTask( |
171 | 105 | ... ubuntu_bugtask.bug, sample_person, ubuntu.currentseries) | 105 | ... ubuntu_bugtask.bug, sample_person, ubuntu.currentseries) |
173 | 106 | >>> ubuntu_bugtask.conjoined_master == hoary_bugtask | 106 | >>> ubuntu_bugtask.conjoined_primary == hoary_bugtask |
174 | 107 | True | 107 | True |
175 | 108 | >>> ubuntu_bugtask.bug.permits_expiration | 108 | >>> ubuntu_bugtask.bug.permits_expiration |
176 | 109 | True | 109 | True |
177 | @@ -318,13 +318,13 @@ will be expirable. | |||
178 | 318 | ubuntu True 351 Incomplete False False False False | 318 | ubuntu True 351 Incomplete False False False False |
179 | 319 | hoary True 351 Incomplete False False False False | 319 | hoary True 351 Incomplete False False False False |
180 | 320 | 320 | ||
183 | 321 | The ubuntu bugtask is never returned; it is a conjoined slave to the | 321 | The ubuntu bugtask is never returned; it is a conjoined replica to the |
184 | 322 | hoary bugtask. Slave bugtasks cannot be directly expired, so they are | 322 | hoary bugtask. Replica bugtasks cannot be directly expired, so they are |
185 | 323 | not returned by findExpirableBugTasks(). | 323 | not returned by findExpirableBugTasks(). |
186 | 324 | 324 | ||
187 | 325 | >>> ubuntu_bugtask.status.title | 325 | >>> ubuntu_bugtask.status.title |
188 | 326 | 'Incomplete' | 326 | 'Incomplete' |
190 | 327 | >>> ubuntu_bugtask.conjoined_master == hoary_bugtask | 327 | >>> ubuntu_bugtask.conjoined_primary == hoary_bugtask |
191 | 328 | True | 328 | True |
192 | 329 | 329 | ||
193 | 330 | Reducing the age to 60 days old, both hoary and jokosher bugtasks | 330 | Reducing the age to 60 days old, both hoary and jokosher bugtasks |
194 | @@ -531,7 +531,7 @@ After the script has run | |||
195 | 531 | 531 | ||
196 | 532 | There are three Expired bugtasks. Jokosher, hoary and ubuntu were | 532 | There are three Expired bugtasks. Jokosher, hoary and ubuntu were |
197 | 533 | expired by the expiration process. Although ubuntu was never returned | 533 | expired by the expiration process. Although ubuntu was never returned |
199 | 534 | by findExpirableBugTasks(), it was expired because its master (hoary) | 534 | by findExpirableBugTasks(), it was expired because its primary (hoary) |
200 | 535 | was expired. The remaining bugtasks are unchanged. | 535 | was expired. The remaining bugtasks are unchanged. |
201 | 536 | 536 | ||
202 | 537 | >>> summarize_bugtasks(bugtasks) | 537 | >>> summarize_bugtasks(bugtasks) |
203 | @@ -551,7 +551,7 @@ was expired. The remaining bugtasks are unchanged. | |||
204 | 551 | 551 | ||
205 | 552 | The message explaining the reason for the expiration was posted by the | 552 | The message explaining the reason for the expiration was posted by the |
206 | 553 | Launchpad Janitor celebrity. Only one message was created for when the | 553 | Launchpad Janitor celebrity. Only one message was created for when the |
208 | 554 | master and slave bugtasks were expired. | 554 | primary and replica bugtasks were expired. |
209 | 555 | 555 | ||
210 | 556 | >>> starting_bug_messages_count | 556 | >>> starting_bug_messages_count |
211 | 557 | 2 | 557 | 2 |
212 | diff --git a/lib/lp/bugs/doc/bugwatch.txt b/lib/lp/bugs/doc/bugwatch.txt | |||
213 | index 17e6f6a..4070e12 100644 | |||
214 | --- a/lib/lp/bugs/doc/bugwatch.txt | |||
215 | +++ b/lib/lp/bugs/doc/bugwatch.txt | |||
216 | @@ -417,15 +417,15 @@ The Bug Watch Updater can transition a bug to any status or importance: | |||
217 | 417 | ... debian_bugwatch.updateImportance(u'nothing', importance) | 417 | ... debian_bugwatch.updateImportance(u'nothing', importance) |
218 | 418 | 418 | ||
219 | 419 | 419 | ||
222 | 420 | BugWatches against BugTasks with conjoined masters | 420 | BugWatches against BugTasks with conjoined primaries |
223 | 421 | -------------------------------------------------- | 421 | ---------------------------------------------------- |
224 | 422 | 422 | ||
229 | 423 | A conjoined bugtask involves a master and slave in in a conjoined | 423 | A conjoined bugtask involves a primary and replica in a conjoined |
230 | 424 | relationship. The slave is a generic product or distribution task; the | 424 | relationship. The replica is a generic product or distribution task; the |
231 | 425 | master is a series-specific task. If a BugWatch is linked to a BugTask | 425 | primary is a series-specific task. If a BugWatch is linked to a BugTask |
232 | 426 | with a conjoined master, that bug task will not be updated when the | 426 | with a conjoined primary, that bug task will not be updated when the |
233 | 427 | BugWatch's status or importance are updated. We can demonstrate this by | 427 | BugWatch's status or importance are updated. We can demonstrate this by |
235 | 428 | creating a bug task with a conjoined master. | 428 | creating a bug task with a conjoined primary. |
236 | 429 | 429 | ||
237 | 430 | >>> from zope.component import getUtility | 430 | >>> from zope.component import getUtility |
238 | 431 | >>> from lp.services.database.sqlbase import flush_database_updates | 431 | >>> from lp.services.database.sqlbase import flush_database_updates |
239 | @@ -441,15 +441,15 @@ creating a bug task with a conjoined master. | |||
240 | 441 | >>> firefox = ubuntu.getSourcePackage('mozilla-firefox') | 441 | >>> firefox = ubuntu.getSourcePackage('mozilla-firefox') |
241 | 442 | >>> bug = firefox.createBug(CreateBugParams( | 442 | >>> bug = firefox.createBug(CreateBugParams( |
242 | 443 | ... owner=sample_person, title='Yet another test bug', | 443 | ... owner=sample_person, title='Yet another test bug', |
244 | 444 | ... comment="A sample bug for conjoined master tests.")) | 444 | ... comment="A sample bug for conjoined primary tests.")) |
245 | 445 | 445 | ||
246 | 446 | >>> targeted_bugtask = getUtility(IBugTaskSet).createTask( | 446 | >>> targeted_bugtask = getUtility(IBugTaskSet).createTask( |
247 | 447 | ... bug, sample_person, firefox.development_version) | 447 | ... bug, sample_person, firefox.development_version) |
248 | 448 | 448 | ||
250 | 449 | >>> targeted_bugtask.conjoined_master is None | 449 | >>> targeted_bugtask.conjoined_primary is None |
251 | 450 | True | 450 | True |
252 | 451 | 451 | ||
254 | 452 | >>> targeted_bugtask.conjoined_slave == bug.bugtasks[0] | 452 | >>> targeted_bugtask.conjoined_replica == bug.bugtasks[0] |
255 | 453 | True | 453 | True |
256 | 454 | 454 | ||
257 | 455 | We use ensureBugTracker() to populate in the parameters that we don't | 455 | We use ensureBugTracker() to populate in the parameters that we don't |
258 | @@ -467,8 +467,8 @@ specifiy, such as the bug tracker's name. | |||
259 | 467 | Now that we have our conjoined bug tasks we can use a test | 467 | Now that we have our conjoined bug tasks we can use a test |
260 | 468 | implementation of the Roundup ExternalBugTracker to try and update | 468 | implementation of the Roundup ExternalBugTracker to try and update |
261 | 469 | them. In fact, updating the bug watch will do nothing to the bug task to | 469 | them. In fact, updating the bug watch will do nothing to the bug task to |
264 | 470 | which it is linked since that bug task is a conjoined slave. Conjoined | 470 | which it is linked since that bug task is a conjoined replica. Conjoined |
265 | 471 | slaves must be updated through their conjoined master. | 471 | replicas must be updated through their conjoined primary. |
266 | 472 | 472 | ||
267 | 473 | >>> bug.bugtasks[0].status.title | 473 | >>> bug.bugtasks[0].status.title |
268 | 474 | 'New' | 474 | 'New' |
269 | diff --git a/lib/lp/bugs/interfaces/bug.py b/lib/lp/bugs/interfaces/bug.py | |||
270 | index 057b9eb..db94b63 100644 | |||
271 | --- a/lib/lp/bugs/interfaces/bug.py | |||
272 | +++ b/lib/lp/bugs/interfaces/bug.py | |||
273 | @@ -578,7 +578,7 @@ class IBugView(Interface): | |||
274 | 578 | """Return a mapping from `ISourcePackageName` to its bug tasks. | 578 | """Return a mapping from `ISourcePackageName` to its bug tasks. |
275 | 579 | 579 | ||
276 | 580 | This mapping is suitable to pass as the bugtasks_by_package | 580 | This mapping is suitable to pass as the bugtasks_by_package |
278 | 581 | cache to getConjoinedMaster(). | 581 | cache to getConjoinedPrimary(). |
279 | 582 | 582 | ||
280 | 583 | The mapping is from a `ISourcePackageName` to all the bug tasks | 583 | The mapping is from a `ISourcePackageName` to all the bug tasks |
281 | 584 | that are targeted to such a package name, no matter which | 584 | that are targeted to such a package name, no matter which |
282 | @@ -750,7 +750,7 @@ class IBugAppend(Interface): | |||
283 | 750 | to questions. This is also true for bugs that are being developed. | 750 | to questions. This is also true for bugs that are being developed. |
284 | 751 | 751 | ||
285 | 752 | The `IQuestionTarget` is provided by the `IBugTask` that is not | 752 | The `IQuestionTarget` is provided by the `IBugTask` that is not |
287 | 753 | Invalid and is not a conjoined slave. Only one question can be | 753 | Invalid and is not a conjoined replica. Only one question can be |
288 | 754 | made from a bug. | 754 | made from a bug. |
289 | 755 | 755 | ||
290 | 756 | An AssertionError is raised if the bug has zero or many BugTasks | 756 | An AssertionError is raised if the bug has zero or many BugTasks |
291 | diff --git a/lib/lp/bugs/interfaces/bugtask.py b/lib/lp/bugs/interfaces/bugtask.py | |||
292 | index 141034f..5d22a40 100644 | |||
293 | --- a/lib/lp/bugs/interfaces/bugtask.py | |||
294 | +++ b/lib/lp/bugs/interfaces/bugtask.py | |||
295 | @@ -575,9 +575,9 @@ class IBugTask(IHasBug, IBugTaskDelete): | |||
296 | 575 | title=_("A list of IPersons subscribed to the bug, whether directly " | 575 | title=_("A list of IPersons subscribed to the bug, whether directly " |
297 | 576 | "or indirectly."), readonly=True) | 576 | "or indirectly."), readonly=True) |
298 | 577 | 577 | ||
300 | 578 | conjoined_master = Attribute( | 578 | conjoined_primary = Attribute( |
301 | 579 | "The series-specific bugtask in a conjoined relationship") | 579 | "The series-specific bugtask in a conjoined relationship") |
303 | 580 | conjoined_slave = Attribute( | 580 | conjoined_replica = Attribute( |
304 | 581 | "The generic bugtask in a conjoined relationship") | 581 | "The generic bugtask in a conjoined relationship") |
305 | 582 | 582 | ||
306 | 583 | is_complete = exported( | 583 | is_complete = exported( |
307 | @@ -614,18 +614,18 @@ class IBugTask(IHasBug, IBugTaskDelete): | |||
308 | 614 | calling context does not have access to the person or pillar names. | 614 | calling context does not have access to the person or pillar names. |
309 | 615 | """ | 615 | """ |
310 | 616 | 616 | ||
313 | 617 | def getConjoinedMaster(bugtasks, bugtasks_by_package=None): | 617 | def getConjoinedPrimary(bugtasks, bugtasks_by_package=None): |
314 | 618 | """Return the conjoined master in the given bugtasks, if any. | 618 | """Return the conjoined primary in the given bugtasks, if any. |
315 | 619 | 619 | ||
316 | 620 | :param bugtasks: The bugtasks to be considered when looking for | 620 | :param bugtasks: The bugtasks to be considered when looking for |
318 | 621 | the conjoined master. | 621 | the conjoined primary. |
319 | 622 | :param bugtasks_by_package: A cache, mapping a | 622 | :param bugtasks_by_package: A cache, mapping a |
320 | 623 | `ISourcePackageName` to a list of bug tasks targeted to such | 623 | `ISourcePackageName` to a list of bug tasks targeted to such |
321 | 624 | a package name. Both distribution and distro series tasks | 624 | a package name. Both distribution and distro series tasks |
322 | 625 | should be included in this list. | 625 | should be included in this list. |
323 | 626 | 626 | ||
324 | 627 | This method exists mainly to allow calculating the conjoined | 627 | This method exists mainly to allow calculating the conjoined |
326 | 628 | master from a cached list of bug tasks, reducing the number of | 628 | primary from a cached list of bug tasks, reducing the number of |
327 | 629 | db queries needed. | 629 | db queries needed. |
328 | 630 | """ | 630 | """ |
329 | 631 | 631 | ||
330 | diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py | |||
331 | index 0a3ff8c..f7c588c 100644 | |||
332 | --- a/lib/lp/bugs/model/bug.py | |||
333 | +++ b/lib/lp/bugs/model/bug.py | |||
334 | @@ -1484,18 +1484,18 @@ class Bug(SQLBase, InformationTypeMixin): | |||
335 | 1484 | 1484 | ||
336 | 1485 | The bugtask is selected by these rules: | 1485 | The bugtask is selected by these rules: |
337 | 1486 | 1. It's status is not Invalid. | 1486 | 1. It's status is not Invalid. |
339 | 1487 | 2. It is not a conjoined slave. | 1487 | 2. It is not a conjoined replica. |
340 | 1488 | Only one bugtask must meet both conditions to be return. When | 1488 | Only one bugtask must meet both conditions to be return. When |
341 | 1489 | zero or many bugtasks match, None is returned. | 1489 | zero or many bugtasks match, None is returned. |
342 | 1490 | """ | 1490 | """ |
344 | 1491 | # We may want to removed the bugtask.conjoined_master check | 1491 | # We may want to removed the bugtask.conjoined_primary check |
345 | 1492 | # below. It is used to simplify the task of converting | 1492 | # below. It is used to simplify the task of converting |
347 | 1493 | # conjoined bugtasks to question--since slaves cannot be | 1493 | # conjoined bugtasks to question--since replicas cannot be |
348 | 1494 | # directly updated anyway. | 1494 | # directly updated anyway. |
349 | 1495 | non_invalid_bugtasks = [ | 1495 | non_invalid_bugtasks = [ |
350 | 1496 | bugtask for bugtask in self.bugtasks | 1496 | bugtask for bugtask in self.bugtasks |
351 | 1497 | if (bugtask.status != BugTaskStatus.INVALID | 1497 | if (bugtask.status != BugTaskStatus.INVALID |
353 | 1498 | and bugtask.conjoined_master is None)] | 1498 | and bugtask.conjoined_primary is None)] |
354 | 1499 | if len(non_invalid_bugtasks) != 1: | 1499 | if len(non_invalid_bugtasks) != 1: |
355 | 1500 | return None | 1500 | return None |
356 | 1501 | [valid_bugtask] = non_invalid_bugtasks | 1501 | [valid_bugtask] = non_invalid_bugtasks |
357 | @@ -1755,8 +1755,8 @@ class Bug(SQLBase, InformationTypeMixin): | |||
358 | 1755 | if bugtask is None: | 1755 | if bugtask is None: |
359 | 1756 | return None | 1756 | return None |
360 | 1757 | 1757 | ||
363 | 1758 | if bugtask.conjoined_master is not None: | 1758 | if bugtask.conjoined_primary is not None: |
364 | 1759 | bugtask = bugtask.conjoined_master | 1759 | bugtask = bugtask.conjoined_primary |
365 | 1760 | 1760 | ||
366 | 1761 | if bugtask.status == status: | 1761 | if bugtask.status == status: |
367 | 1762 | return None | 1762 | return None |
368 | diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py | |||
369 | index 2cd2b35..7b31d6d 100644 | |||
370 | --- a/lib/lp/bugs/model/bugtask.py | |||
371 | +++ b/lib/lp/bugs/model/bugtask.py | |||
372 | @@ -281,25 +281,25 @@ class PassthroughValue: | |||
373 | 281 | 281 | ||
374 | 282 | @block_implicit_flushes | 282 | @block_implicit_flushes |
375 | 283 | def validate_conjoined_attribute(self, attr, value): | 283 | def validate_conjoined_attribute(self, attr, value): |
381 | 284 | # If this is a conjoined slave then call setattr on the master. | 284 | # If this is a conjoined replica then call setattr on the primary. |
382 | 285 | # Effectively this means that making a change to the slave will | 285 | # Effectively this means that making a change to the replica will |
383 | 286 | # actually make the change to the master (which will then be passed | 286 | # actually make the change to the primary (which will then be passed |
384 | 287 | # down to the slave, of course). This helps to prevent OOPSes when | 287 | # down to the replica, of course). This helps to prevent OOPSes when |
385 | 288 | # people try to update the conjoined slave via the API. | 288 | # people try to update the conjoined replica via the API. |
386 | 289 | 289 | ||
387 | 290 | # If the value has been wrapped in a _PassthroughValue instance, | 290 | # If the value has been wrapped in a _PassthroughValue instance, |
389 | 291 | # then we are being updated by our conjoined master: pass the | 291 | # then we are being updated by our conjoined primary: pass the |
390 | 292 | # value through without any checking. | 292 | # value through without any checking. |
391 | 293 | if isinstance(value, PassthroughValue): | 293 | if isinstance(value, PassthroughValue): |
392 | 294 | return value.value | 294 | return value.value |
393 | 295 | 295 | ||
397 | 296 | conjoined_master = self.conjoined_master | 296 | conjoined_primary = self.conjoined_primary |
398 | 297 | if conjoined_master is not None: | 297 | if conjoined_primary is not None: |
399 | 298 | setattr(conjoined_master, attr, value) | 298 | setattr(conjoined_primary, attr, value) |
400 | 299 | return value | 299 | return value |
401 | 300 | 300 | ||
404 | 301 | # If there is a conjoined slave, update that. | 301 | # If there is a conjoined replica, update that. |
405 | 302 | conjoined_bugtask = self.conjoined_slave | 302 | conjoined_bugtask = self.conjoined_replica |
406 | 303 | if conjoined_bugtask: | 303 | if conjoined_bugtask: |
407 | 304 | setattr(conjoined_bugtask, attr, PassthroughValue(value)) | 304 | setattr(conjoined_bugtask, attr, PassthroughValue(value)) |
408 | 305 | 305 | ||
409 | @@ -727,26 +727,26 @@ class BugTask(StormBase): | |||
410 | 727 | result['pillar_name'] = self.pillar.displayname | 727 | result['pillar_name'] = self.pillar.displayname |
411 | 728 | return result | 728 | return result |
412 | 729 | 729 | ||
414 | 730 | def getConjoinedMaster(self, bugtasks, bugtasks_by_package=None): | 730 | def getConjoinedPrimary(self, bugtasks, bugtasks_by_package=None): |
415 | 731 | """See `IBugTask`.""" | 731 | """See `IBugTask`.""" |
417 | 732 | conjoined_master = None | 732 | conjoined_primary = None |
418 | 733 | if self.distribution: | 733 | if self.distribution: |
419 | 734 | if bugtasks_by_package is None: | 734 | if bugtasks_by_package is None: |
420 | 735 | bugtasks_by_package = ( | 735 | bugtasks_by_package = ( |
421 | 736 | self.bug.getBugTasksByPackageName(bugtasks)) | 736 | self.bug.getBugTasksByPackageName(bugtasks)) |
422 | 737 | bugtasks = bugtasks_by_package[self.sourcepackagename] | 737 | bugtasks = bugtasks_by_package[self.sourcepackagename] |
424 | 738 | possible_masters = [ | 738 | possible_primaries = [ |
425 | 739 | bugtask for bugtask in bugtasks | 739 | bugtask for bugtask in bugtasks |
426 | 740 | if (bugtask.distroseries is not None and | 740 | if (bugtask.distroseries is not None and |
427 | 741 | bugtask.sourcepackagename == self.sourcepackagename)] | 741 | bugtask.sourcepackagename == self.sourcepackagename)] |
428 | 742 | # Return early, so that we don't have to get currentseries, | 742 | # Return early, so that we don't have to get currentseries, |
429 | 743 | # which is expensive. | 743 | # which is expensive. |
431 | 744 | if len(possible_masters) == 0: | 744 | if len(possible_primaries) == 0: |
432 | 745 | return None | 745 | return None |
433 | 746 | current_series = self.distribution.currentseries | 746 | current_series = self.distribution.currentseries |
435 | 747 | for bugtask in possible_masters: | 747 | for bugtask in possible_primaries: |
436 | 748 | if bugtask.distroseries == current_series: | 748 | if bugtask.distroseries == current_series: |
438 | 749 | conjoined_master = bugtask | 749 | conjoined_primary = bugtask |
439 | 750 | break | 750 | break |
440 | 751 | elif self.product: | 751 | elif self.product: |
441 | 752 | assert self.product.development_focusID is not None, ( | 752 | assert self.product.development_focusID is not None, ( |
442 | @@ -754,26 +754,26 @@ class BugTask(StormBase): | |||
443 | 754 | devel_focusID = self.product.development_focusID | 754 | devel_focusID = self.product.development_focusID |
444 | 755 | for bugtask in bugtasks: | 755 | for bugtask in bugtasks: |
445 | 756 | if bugtask.productseries_id == devel_focusID: | 756 | if bugtask.productseries_id == devel_focusID: |
447 | 757 | conjoined_master = bugtask | 757 | conjoined_primary = bugtask |
448 | 758 | break | 758 | break |
449 | 759 | 759 | ||
454 | 760 | if (conjoined_master is not None and | 760 | if (conjoined_primary is not None and |
455 | 761 | conjoined_master.status in self._NON_CONJOINED_STATUSES): | 761 | conjoined_primary.status in self._NON_CONJOINED_STATUSES): |
456 | 762 | conjoined_master = None | 762 | conjoined_primary = None |
457 | 763 | return conjoined_master | 763 | return conjoined_primary |
458 | 764 | 764 | ||
459 | 765 | def _get_shortlisted_bugtasks(self): | 765 | def _get_shortlisted_bugtasks(self): |
460 | 766 | return shortlist(self.bug.bugtasks, longest_expected=200) | 766 | return shortlist(self.bug.bugtasks, longest_expected=200) |
461 | 767 | 767 | ||
462 | 768 | @property | 768 | @property |
464 | 769 | def conjoined_master(self): | 769 | def conjoined_primary(self): |
465 | 770 | """See `IBugTask`.""" | 770 | """See `IBugTask`.""" |
467 | 771 | return self.getConjoinedMaster(self._get_shortlisted_bugtasks()) | 771 | return self.getConjoinedPrimary(self._get_shortlisted_bugtasks()) |
468 | 772 | 772 | ||
469 | 773 | @property | 773 | @property |
471 | 774 | def conjoined_slave(self): | 774 | def conjoined_replica(self): |
472 | 775 | """See `IBugTask`.""" | 775 | """See `IBugTask`.""" |
474 | 776 | conjoined_slave = None | 776 | conjoined_replica = None |
475 | 777 | if self.distroseries: | 777 | if self.distroseries: |
476 | 778 | distribution = self.distroseries.distribution | 778 | distribution = self.distroseries.distribution |
477 | 779 | if self.distroseries != distribution.currentseries: | 779 | if self.distroseries != distribution.currentseries: |
478 | @@ -782,7 +782,7 @@ class BugTask(StormBase): | |||
479 | 782 | for bugtask in self._get_shortlisted_bugtasks(): | 782 | for bugtask in self._get_shortlisted_bugtasks(): |
480 | 783 | if (bugtask.distribution == distribution and | 783 | if (bugtask.distribution == distribution and |
481 | 784 | bugtask.sourcepackagename == self.sourcepackagename): | 784 | bugtask.sourcepackagename == self.sourcepackagename): |
483 | 785 | conjoined_slave = bugtask | 785 | conjoined_replica = bugtask |
484 | 786 | break | 786 | break |
485 | 787 | elif self.productseries: | 787 | elif self.productseries: |
486 | 788 | product = self.productseries.product | 788 | product = self.productseries.product |
487 | @@ -791,29 +791,29 @@ class BugTask(StormBase): | |||
488 | 791 | return None | 791 | return None |
489 | 792 | for bugtask in self._get_shortlisted_bugtasks(): | 792 | for bugtask in self._get_shortlisted_bugtasks(): |
490 | 793 | if bugtask.product == product: | 793 | if bugtask.product == product: |
492 | 794 | conjoined_slave = bugtask | 794 | conjoined_replica = bugtask |
493 | 795 | break | 795 | break |
494 | 796 | 796 | ||
496 | 797 | if (conjoined_slave is not None and | 797 | if (conjoined_replica is not None and |
497 | 798 | self.status in self._NON_CONJOINED_STATUSES): | 798 | self.status in self._NON_CONJOINED_STATUSES): |
500 | 799 | conjoined_slave = None | 799 | conjoined_replica = None |
501 | 800 | return conjoined_slave | 800 | return conjoined_replica |
502 | 801 | 801 | ||
505 | 802 | def _syncFromConjoinedSlave(self): | 802 | def _syncFromConjoinedReplica(self): |
506 | 803 | """Ensure the conjoined master is synched from its slave. | 803 | """Ensure the conjoined primary is synched from its replica. |
507 | 804 | 804 | ||
508 | 805 | This method should be used only directly after when the | 805 | This method should be used only directly after when the |
510 | 806 | conjoined master has been created after the slave, to ensure | 806 | conjoined primary has been created after the replica, to ensure |
511 | 807 | that they are in sync from the beginning. | 807 | that they are in sync from the beginning. |
512 | 808 | """ | 808 | """ |
514 | 809 | conjoined_slave = self.conjoined_slave | 809 | conjoined_replica = self.conjoined_replica |
515 | 810 | 810 | ||
516 | 811 | for synched_attr in self._CONJOINED_ATTRIBUTES: | 811 | for synched_attr in self._CONJOINED_ATTRIBUTES: |
518 | 812 | slave_attr_value = getattr(conjoined_slave, synched_attr) | 812 | replica_attr_value = getattr(conjoined_replica, synched_attr) |
519 | 813 | # Bypass our checks that prevent setting attributes on | 813 | # Bypass our checks that prevent setting attributes on |
521 | 814 | # conjoined masters by calling the underlying sqlobject | 814 | # conjoined primaries by calling the underlying sqlobject |
522 | 815 | # setter methods directly. | 815 | # setter methods directly. |
524 | 816 | setattr(self, synched_attr, PassthroughValue(slave_attr_value)) | 816 | setattr(self, synched_attr, PassthroughValue(replica_attr_value)) |
525 | 817 | 817 | ||
526 | 818 | def transitionToMilestone(self, new_milestone, user): | 818 | def transitionToMilestone(self, new_milestone, user): |
527 | 819 | """See `IBugTask`.""" | 819 | """See `IBugTask`.""" |
528 | @@ -1643,8 +1643,8 @@ class BugTaskSet: | |||
529 | 1643 | del get_property_cache(bug).bugtasks | 1643 | del get_property_cache(bug).bugtasks |
530 | 1644 | for bugtask in tasks: | 1644 | for bugtask in tasks: |
531 | 1645 | bugtask.updateTargetNameCache() | 1645 | bugtask.updateTargetNameCache() |
534 | 1646 | if bugtask.conjoined_slave: | 1646 | if bugtask.conjoined_replica: |
535 | 1647 | bugtask._syncFromConjoinedSlave() | 1647 | bugtask._syncFromConjoinedReplica() |
536 | 1648 | else: | 1648 | else: |
537 | 1649 | # Set date_* properties, if we're not conjoined. | 1649 | # Set date_* properties, if we're not conjoined. |
538 | 1650 | bugtask._setStatusDateProperties( | 1650 | bugtask._setStatusDateProperties( |
539 | @@ -1739,11 +1739,11 @@ class BugTaskSet: | |||
540 | 1739 | Bugtasks cannot transition to Invalid automatically unless they meet | 1739 | Bugtasks cannot transition to Invalid automatically unless they meet |
541 | 1740 | all the rules stated above. | 1740 | all the rules stated above. |
542 | 1741 | 1741 | ||
548 | 1742 | This implementation returns the master of the master-slave conjoined | 1742 | This implementation returns the primary of the primary-replica |
549 | 1743 | pairs of bugtasks. Slave conjoined bugtasks are not included in the | 1743 | conjoined pairs of bugtasks. Replica conjoined bugtasks are not |
550 | 1744 | list because they can only be expired by calling the master bugtask's | 1744 | included in the list because they can only be expired by calling the |
551 | 1745 | transitionToStatus() method. See 'Conjoined Bug Tasks' in | 1745 | primary bugtask's transitionToStatus() method. See |
552 | 1746 | c.l.doc/bugtasks.txt. | 1746 | lp.bugs.model.tests.test_bugtask.TestConjoinedBugTasks. |
553 | 1747 | 1747 | ||
554 | 1748 | Only bugtasks the specified user has permission to view are | 1748 | Only bugtasks the specified user has permission to view are |
555 | 1749 | returned. The Janitor celebrity has permission to view all bugs. | 1749 | returned. The Janitor celebrity has permission to view all bugs. |
556 | diff --git a/lib/lp/bugs/model/bugtasksearch.py b/lib/lp/bugs/model/bugtasksearch.py | |||
557 | index 1bf2eed..03b306f 100644 | |||
558 | --- a/lib/lp/bugs/model/bugtasksearch.py | |||
559 | +++ b/lib/lp/bugs/model/bugtasksearch.py | |||
560 | @@ -931,38 +931,38 @@ def _build_status_clause(col, status): | |||
561 | 931 | 931 | ||
562 | 932 | 932 | ||
563 | 933 | def _build_exclude_conjoined_clause(milestone): | 933 | def _build_exclude_conjoined_clause(milestone): |
565 | 934 | """Exclude bugtasks with a conjoined master. | 934 | """Exclude bugtasks with a conjoined primary. |
566 | 935 | 935 | ||
567 | 936 | This search option only makes sense when searching for bugtasks | 936 | This search option only makes sense when searching for bugtasks |
568 | 937 | for a milestone. Only bugtasks for a project or a distribution | 937 | for a milestone. Only bugtasks for a project or a distribution |
570 | 938 | can have a conjoined master bugtask, which is a bugtask on the | 938 | can have a conjoined primary bugtask, which is a bugtask on the |
571 | 939 | project's development focus series or the distribution's | 939 | project's development focus series or the distribution's |
572 | 940 | currentseries. The project bugtask or the distribution bugtask | 940 | currentseries. The project bugtask or the distribution bugtask |
574 | 941 | will always have the same milestone set as its conjoined master | 941 | will always have the same milestone set as its conjoined primary |
575 | 942 | bugtask, if it exists on the bug. Therefore, this prevents a lot | 942 | bugtask, if it exists on the bug. Therefore, this prevents a lot |
576 | 943 | of bugs having two bugtasks listed in the results. However, it | 943 | of bugs having two bugtasks listed in the results. However, it |
577 | 944 | is ok if a bug has multiple bugtasks in the results as long as | 944 | is ok if a bug has multiple bugtasks in the results as long as |
578 | 945 | those other bugtasks are on other series. | 945 | those other bugtasks are on other series. |
579 | 946 | """ | 946 | """ |
580 | 947 | # XXX: EdwinGrubbs 2010-12-15 bug=682989 | 947 | # XXX: EdwinGrubbs 2010-12-15 bug=682989 |
583 | 948 | # (ConjoinedMaster.bug == X) produces the wrong sql, but | 948 | # (ConjoinedPrimary.bug == X) produces the wrong sql, but |
584 | 949 | # (ConjoinedMaster.bugID == X) works right. This bug applies to | 949 | # (ConjoinedPrimary.bugID == X) works right. This bug applies to |
585 | 950 | # all foreign keys on the ClassAlias. | 950 | # all foreign keys on the ClassAlias. |
586 | 951 | 951 | ||
591 | 952 | # Perform a LEFT JOIN to the conjoined master bugtask. If the | 952 | # Perform a LEFT JOIN to the conjoined primary bugtask. If the |
592 | 953 | # conjoined master is not null, it gets filtered out. | 953 | # conjoined primary is not null, it gets filtered out. |
593 | 954 | ConjoinedMaster = ClassAlias(BugTask, 'ConjoinedMaster') | 954 | ConjoinedPrimary = ClassAlias(BugTask, 'ConjoinedPrimary') |
594 | 955 | extra_clauses = [ConjoinedMaster.id == None] | 955 | extra_clauses = [ConjoinedPrimary.id == None] |
595 | 956 | if milestone.distribution is not None: | 956 | if milestone.distribution is not None: |
596 | 957 | current_series = milestone.distribution.currentseries | 957 | current_series = milestone.distribution.currentseries |
597 | 958 | join = LeftJoin( | 958 | join = LeftJoin( |
600 | 959 | ConjoinedMaster, | 959 | ConjoinedPrimary, |
601 | 960 | And(ConjoinedMaster.bug_id == BugTaskFlat.bug_id, | 960 | And(ConjoinedPrimary.bug_id == BugTaskFlat.bug_id, |
602 | 961 | BugTaskFlat.distribution_id == milestone.distribution.id, | 961 | BugTaskFlat.distribution_id == milestone.distribution.id, |
605 | 962 | ConjoinedMaster.distroseries_id == current_series.id, | 962 | ConjoinedPrimary.distroseries_id == current_series.id, |
606 | 963 | Not(ConjoinedMaster._status.is_in( | 963 | Not(ConjoinedPrimary._status.is_in( |
607 | 964 | BugTask._NON_CONJOINED_STATUSES)))) | 964 | BugTask._NON_CONJOINED_STATUSES)))) |
609 | 965 | join_tables = [(ConjoinedMaster, join)] | 965 | join_tables = [(ConjoinedPrimary, join)] |
610 | 966 | else: | 966 | else: |
611 | 967 | if IProjectGroupMilestone.providedBy(milestone): | 967 | if IProjectGroupMilestone.providedBy(milestone): |
612 | 968 | # Since an IProjectGroupMilestone could have bugs with | 968 | # Since an IProjectGroupMilestone could have bugs with |
613 | @@ -973,11 +973,11 @@ def _build_exclude_conjoined_clause(milestone): | |||
614 | 973 | Join(Milestone, BugTaskFlat.milestone_id == Milestone.id), | 973 | Join(Milestone, BugTaskFlat.milestone_id == Milestone.id), |
615 | 974 | LeftJoin(Product, BugTaskFlat.product_id == Product.id), | 974 | LeftJoin(Product, BugTaskFlat.product_id == Product.id), |
616 | 975 | LeftJoin( | 975 | LeftJoin( |
620 | 976 | ConjoinedMaster, | 976 | ConjoinedPrimary, |
621 | 977 | And(ConjoinedMaster.bug_id == BugTaskFlat.bug_id, | 977 | And(ConjoinedPrimary.bug_id == BugTaskFlat.bug_id, |
622 | 978 | ConjoinedMaster.productseries_id | 978 | ConjoinedPrimary.productseries_id |
623 | 979 | == Product.development_focusID, | 979 | == Product.development_focusID, |
625 | 980 | Not(ConjoinedMaster._status.is_in( | 980 | Not(ConjoinedPrimary._status.is_in( |
626 | 981 | BugTask._NON_CONJOINED_STATUSES)))), | 981 | BugTask._NON_CONJOINED_STATUSES)))), |
627 | 982 | ] | 982 | ] |
628 | 983 | # join.right is the table name. | 983 | # join.right is the table name. |
629 | @@ -986,13 +986,13 @@ def _build_exclude_conjoined_clause(milestone): | |||
630 | 986 | dev_focus_id = ( | 986 | dev_focus_id = ( |
631 | 987 | milestone.product.development_focusID) | 987 | milestone.product.development_focusID) |
632 | 988 | join = LeftJoin( | 988 | join = LeftJoin( |
635 | 989 | ConjoinedMaster, | 989 | ConjoinedPrimary, |
636 | 990 | And(ConjoinedMaster.bug_id == BugTaskFlat.bug_id, | 990 | And(ConjoinedPrimary.bug_id == BugTaskFlat.bug_id, |
637 | 991 | BugTaskFlat.product_id == milestone.product.id, | 991 | BugTaskFlat.product_id == milestone.product.id, |
640 | 992 | ConjoinedMaster.productseries_id == dev_focus_id, | 992 | ConjoinedPrimary.productseries_id == dev_focus_id, |
641 | 993 | Not(ConjoinedMaster._status.is_in( | 993 | Not(ConjoinedPrimary._status.is_in( |
642 | 994 | BugTask._NON_CONJOINED_STATUSES)))) | 994 | BugTask._NON_CONJOINED_STATUSES)))) |
644 | 995 | join_tables = [(ConjoinedMaster, join)] | 995 | join_tables = [(ConjoinedPrimary, join)] |
645 | 996 | else: | 996 | else: |
646 | 997 | raise AssertionError( | 997 | raise AssertionError( |
647 | 998 | "A milestone must always have either a project, " | 998 | "A milestone must always have either a project, " |
648 | diff --git a/lib/lp/bugs/model/bugwatch.py b/lib/lp/bugs/model/bugwatch.py | |||
649 | index e9dc359..8b564a3 100644 | |||
650 | --- a/lib/lp/bugs/model/bugwatch.py | |||
651 | +++ b/lib/lp/bugs/model/bugwatch.py | |||
652 | @@ -148,8 +148,8 @@ class BugWatch(SQLBase): | |||
653 | 148 | """Yield the bug tasks that are eligible for update.""" | 148 | """Yield the bug tasks that are eligible for update.""" |
654 | 149 | for bugtask in self.bugtasks: | 149 | for bugtask in self.bugtasks: |
655 | 150 | # We don't update conjoined bug tasks; they must be | 150 | # We don't update conjoined bug tasks; they must be |
658 | 151 | # updated through their conjoined masters. | 151 | # updated through their conjoined primaries. |
659 | 152 | if bugtask.conjoined_master is not None: | 152 | if bugtask.conjoined_primary is not None: |
660 | 153 | continue | 153 | continue |
661 | 154 | # We don't update tasks of duplicate bugs. | 154 | # We don't update tasks of duplicate bugs. |
662 | 155 | if bugtask.bug.duplicateof is not None: | 155 | if bugtask.bug.duplicateof is not None: |
663 | diff --git a/lib/lp/bugs/model/tests/test_bugtask.py b/lib/lp/bugs/model/tests/test_bugtask.py | |||
664 | index f50033f..4089d86 100644 | |||
665 | --- a/lib/lp/bugs/model/tests/test_bugtask.py | |||
666 | +++ b/lib/lp/bugs/model/tests/test_bugtask.py | |||
667 | @@ -1685,10 +1685,10 @@ class TestConjoinedBugTasks(TestCaseWithFactory): | |||
668 | 1685 | return BugData(owner, distro, distro_release, source_package, bug, | 1685 | return BugData(owner, distro, distro_release, source_package, bug, |
669 | 1686 | generic_task, series_task) | 1686 | generic_task, series_task) |
670 | 1687 | 1687 | ||
673 | 1688 | def test_editing_generic_status_reflects_upon_conjoined_master(self): | 1688 | def test_editing_generic_status_reflects_upon_conjoined_primary(self): |
674 | 1689 | # If a change is made to the status of a conjoined slave | 1689 | # If a change is made to the status of a conjoined replica |
675 | 1690 | # (generic) task, that change is reflected upon the conjoined | 1690 | # (generic) task, that change is reflected upon the conjoined |
677 | 1691 | # master. | 1691 | # primary. |
678 | 1692 | data = self._setupBugData() | 1692 | data = self._setupBugData() |
679 | 1693 | with person_logged_in(data.owner): | 1693 | with person_logged_in(data.owner): |
680 | 1694 | # Both the generic task and the series task start off with the | 1694 | # Both the generic task and the series task start off with the |
681 | @@ -1704,10 +1704,10 @@ class TestConjoinedBugTasks(TestCaseWithFactory): | |||
682 | 1704 | self.assertEqual(BugTaskStatus.CONFIRMED, | 1704 | self.assertEqual(BugTaskStatus.CONFIRMED, |
683 | 1705 | data.series_task.status) | 1705 | data.series_task.status) |
684 | 1706 | 1706 | ||
687 | 1707 | def test_editing_generic_importance_reflects_upon_conjoined_master(self): | 1707 | def test_editing_generic_importance_reflects_upon_conjoined_primary(self): |
688 | 1708 | # If a change is made to the importance of a conjoined slave | 1708 | # If a change is made to the importance of a conjoined replica |
689 | 1709 | # (generic) task, that change is reflected upon the conjoined | 1709 | # (generic) task, that change is reflected upon the conjoined |
691 | 1710 | # master. | 1710 | # primary. |
692 | 1711 | data = self._setupBugData() | 1711 | data = self._setupBugData() |
693 | 1712 | with person_logged_in(data.owner): | 1712 | with person_logged_in(data.owner): |
694 | 1713 | data.generic_task.transitionToImportance(BugTaskImportance.HIGH, | 1713 | data.generic_task.transitionToImportance(BugTaskImportance.HIGH, |
695 | @@ -1715,19 +1715,19 @@ class TestConjoinedBugTasks(TestCaseWithFactory): | |||
696 | 1715 | self.assertEqual(BugTaskImportance.HIGH, | 1715 | self.assertEqual(BugTaskImportance.HIGH, |
697 | 1716 | data.series_task.importance) | 1716 | data.series_task.importance) |
698 | 1717 | 1717 | ||
701 | 1718 | def test_editing_generic_assignee_reflects_upon_conjoined_master(self): | 1718 | def test_editing_generic_assignee_reflects_upon_conjoined_primary(self): |
702 | 1719 | # If a change is made to the assignee of a conjoined slave | 1719 | # If a change is made to the assignee of a conjoined replica |
703 | 1720 | # (generic) task, that change is reflected upon the conjoined | 1720 | # (generic) task, that change is reflected upon the conjoined |
705 | 1721 | # master. | 1721 | # primary. |
706 | 1722 | data = self._setupBugData() | 1722 | data = self._setupBugData() |
707 | 1723 | with person_logged_in(data.owner): | 1723 | with person_logged_in(data.owner): |
708 | 1724 | data.generic_task.transitionToAssignee(data.owner) | 1724 | data.generic_task.transitionToAssignee(data.owner) |
709 | 1725 | self.assertEqual(data.owner, data.series_task.assignee) | 1725 | self.assertEqual(data.owner, data.series_task.assignee) |
710 | 1726 | 1726 | ||
713 | 1727 | def test_editing_generic_package_reflects_upon_conjoined_master(self): | 1727 | def test_editing_generic_package_reflects_upon_conjoined_primary(self): |
714 | 1728 | # If a change is made to the source package of a conjoined slave | 1728 | # If a change is made to the source package of a conjoined replica |
715 | 1729 | # (generic) task, that change is reflected upon the conjoined | 1729 | # (generic) task, that change is reflected upon the conjoined |
717 | 1730 | # master. | 1730 | # primary. |
718 | 1731 | data = self._setupBugData() | 1731 | data = self._setupBugData() |
719 | 1732 | source_package_name = self.factory.makeSourcePackageName("ham") | 1732 | source_package_name = self.factory.makeSourcePackageName("ham") |
720 | 1733 | self.factory.makeSourcePackagePublishingHistory( | 1733 | self.factory.makeSourcePackagePublishingHistory( |
721 | @@ -1777,7 +1777,7 @@ class TestConjoinedBugTasks(TestCaseWithFactory): | |||
722 | 1777 | self.assertEqual(con_devel_task.milestone.name, 'test') | 1777 | self.assertEqual(con_devel_task.milestone.name, 'test') |
723 | 1778 | 1778 | ||
724 | 1779 | def test_non_current_dev_lacks_conjoined(self): | 1779 | def test_non_current_dev_lacks_conjoined(self): |
726 | 1780 | """Tasks not the current dev focus lacks conjoined masters or slaves. | 1780 | """Tasks not the current dev focus lack conjoined primaries/replicas. |
727 | 1781 | """ | 1781 | """ |
728 | 1782 | # Only owners, experts, or admins can create a series. | 1782 | # Only owners, experts, or admins can create a series. |
729 | 1783 | login('foo.bar@canonical.com') | 1783 | login('foo.bar@canonical.com') |
730 | @@ -1801,8 +1801,8 @@ class TestConjoinedBugTasks(TestCaseWithFactory): | |||
731 | 1801 | 1801 | ||
732 | 1802 | stable_netapplet_task = getUtility(IBugTaskSet).createTask( | 1802 | stable_netapplet_task = getUtility(IBugTaskSet).createTask( |
733 | 1803 | ubuntu_netapplet_bug, launchbag.user, alsa_utils_stable) | 1803 | ubuntu_netapplet_bug, launchbag.user, alsa_utils_stable) |
736 | 1804 | self.assertIsNone(stable_netapplet_task.conjoined_master) | 1804 | self.assertIsNone(stable_netapplet_task.conjoined_primary) |
737 | 1805 | self.assertIsNone(stable_netapplet_task.conjoined_slave) | 1805 | self.assertIsNone(stable_netapplet_task.conjoined_replica) |
738 | 1806 | 1806 | ||
739 | 1807 | warty = ubuntu.getSeries('warty') | 1807 | warty = ubuntu.getSeries('warty') |
740 | 1808 | self.assertNotEqual(warty, ubuntu.currentseries) | 1808 | self.assertNotEqual(warty, ubuntu.currentseries) |
741 | @@ -1811,12 +1811,11 @@ class TestConjoinedBugTasks(TestCaseWithFactory): | |||
742 | 1811 | ubuntu_netapplet_bug, launchbag.user, | 1811 | ubuntu_netapplet_bug, launchbag.user, |
743 | 1812 | warty.getSourcePackage(ubuntu_netapplet.sourcepackagename)) | 1812 | warty.getSourcePackage(ubuntu_netapplet.sourcepackagename)) |
744 | 1813 | 1813 | ||
747 | 1814 | self.assertIsNone(warty_netapplet_task.conjoined_master) | 1814 | self.assertIsNone(warty_netapplet_task.conjoined_primary) |
748 | 1815 | self.assertIsNone(warty_netapplet_task.conjoined_slave) | 1815 | self.assertIsNone(warty_netapplet_task.conjoined_replica) |
749 | 1816 | 1816 | ||
750 | 1817 | def test_no_conjoined_without_current_series(self): | 1817 | def test_no_conjoined_without_current_series(self): |
753 | 1818 | """Distributions without current series lack a conjoined master/slave. | 1818 | """Distros without current series lack a conjoined primary/replica.""" |
752 | 1819 | """ | ||
754 | 1820 | login('foo.bar@canonical.com') | 1819 | login('foo.bar@canonical.com') |
755 | 1821 | launchbag = getUtility(ILaunchBag) | 1820 | launchbag = getUtility(ILaunchBag) |
756 | 1822 | ubuntu = getUtility(IDistributionSet).get(1) | 1821 | ubuntu = getUtility(IDistributionSet).get(1) |
757 | @@ -1832,13 +1831,13 @@ class TestConjoinedBugTasks(TestCaseWithFactory): | |||
758 | 1832 | gentoo_netapplet_task = getUtility(IBugTaskSet).createTask( | 1831 | gentoo_netapplet_task = getUtility(IBugTaskSet).createTask( |
759 | 1833 | ubuntu_netapplet_bug, launchbag.user, | 1832 | ubuntu_netapplet_bug, launchbag.user, |
760 | 1834 | gentoo.getSourcePackage(ubuntu_netapplet.sourcepackagename)) | 1833 | gentoo.getSourcePackage(ubuntu_netapplet.sourcepackagename)) |
763 | 1835 | self.assertIsNone(gentoo_netapplet_task.conjoined_master) | 1834 | self.assertIsNone(gentoo_netapplet_task.conjoined_primary) |
764 | 1836 | self.assertIsNone(gentoo_netapplet_task.conjoined_slave) | 1835 | self.assertIsNone(gentoo_netapplet_task.conjoined_replica) |
765 | 1837 | 1836 | ||
766 | 1838 | def test_conjoined_broken_relationship(self): | 1837 | def test_conjoined_broken_relationship(self): |
767 | 1839 | """A conjoined relationship can be broken, though. | 1838 | """A conjoined relationship can be broken, though. |
768 | 1840 | 1839 | ||
770 | 1841 | If the development task (i.e the conjoined master) is Won't Fix, it | 1840 | If the development task (i.e the conjoined primary) is Won't Fix, it |
771 | 1842 | means that the bug is deferred to the next series. In this case the | 1841 | means that the bug is deferred to the next series. In this case the |
772 | 1843 | development task should be Won't Fix, while the generic task keeps the | 1842 | development task should be Won't Fix, while the generic task keeps the |
773 | 1844 | value it had before, allowing it to stay open. | 1843 | value it had before, allowing it to stay open. |
774 | @@ -1874,8 +1873,8 @@ class TestConjoinedBugTasks(TestCaseWithFactory): | |||
775 | 1874 | self.assertIsNotNone(current_series_netapplet_task.date_closed) | 1873 | self.assertIsNotNone(current_series_netapplet_task.date_closed) |
776 | 1875 | 1874 | ||
777 | 1876 | # And the bugtasks are no longer conjoined: | 1875 | # And the bugtasks are no longer conjoined: |
780 | 1877 | self.assertIsNone(generic_netapplet_task.conjoined_master) | 1876 | self.assertIsNone(generic_netapplet_task.conjoined_primary) |
781 | 1878 | self.assertIsNone(current_series_netapplet_task.conjoined_slave) | 1877 | self.assertIsNone(current_series_netapplet_task.conjoined_replica) |
782 | 1879 | 1878 | ||
783 | 1880 | # If the current development release is marked as Invalid, then the | 1879 | # If the current development release is marked as Invalid, then the |
784 | 1881 | # bug is invalid for all future series too, and so the general bugtask | 1880 | # bug is invalid for all future series too, and so the general bugtask |
785 | diff --git a/lib/lp/bugs/model/tests/test_bugtasksearch.py b/lib/lp/bugs/model/tests/test_bugtasksearch.py | |||
786 | index 8c9d81c..a8bd7a7 100644 | |||
787 | --- a/lib/lp/bugs/model/tests/test_bugtasksearch.py | |||
788 | +++ b/lib/lp/bugs/model/tests/test_bugtasksearch.py | |||
789 | @@ -2272,7 +2272,7 @@ class TestBugTaskSearch(TestCaseWithFactory): | |||
790 | 2272 | # on the bug that would normally trigger lazy evaluation for security | 2272 | # on the bug that would normally trigger lazy evaluation for security |
791 | 2273 | # checking. Note that the 'id' attribute does not trigger a check. | 2273 | # checking. Note that the 'id' attribute does not trigger a check. |
792 | 2274 | with StormStatementRecorder() as recorder: | 2274 | with StormStatementRecorder() as recorder: |
794 | 2275 | [task.getConjoinedMaster for task in tasks] | 2275 | [task.getConjoinedPrimary for task in tasks] |
795 | 2276 | self.assertThat(recorder, has_expected_queries) | 2276 | self.assertThat(recorder, has_expected_queries) |
796 | 2277 | 2277 | ||
797 | 2278 | def test_omit_targeted_default_is_false(self): | 2278 | def test_omit_targeted_default_is_false(self): |
798 | diff --git a/lib/lp/bugs/scripts/bugexpire.py b/lib/lp/bugs/scripts/bugexpire.py | |||
799 | index 89d656e..2d4e090 100644 | |||
800 | --- a/lib/lp/bugs/scripts/bugexpire.py | |||
801 | +++ b/lib/lp/bugs/scripts/bugexpire.py | |||
802 | @@ -78,8 +78,8 @@ class BugJanitor: | |||
803 | 78 | self.log.info( | 78 | self.log.info( |
804 | 79 | 'Found %d bugtasks to expire.' % incomplete_bugtasks.count()) | 79 | 'Found %d bugtasks to expire.' % incomplete_bugtasks.count()) |
805 | 80 | for bugtask in incomplete_bugtasks: | 80 | for bugtask in incomplete_bugtasks: |
808 | 81 | # We don't expire bugtasks with conjoined masters. | 81 | # We don't expire bugtasks with conjoined primaries. |
809 | 82 | if bugtask.conjoined_master: | 82 | if bugtask.conjoined_primary: |
810 | 83 | continue | 83 | continue |
811 | 84 | 84 | ||
812 | 85 | with notify_modified(bugtask, ['status'], user=self.janitor): | 85 | with notify_modified(bugtask, ['status'], user=self.janitor): |
813 | diff --git a/lib/lp/bugs/scripts/tests/test_bugimport.py b/lib/lp/bugs/scripts/tests/test_bugimport.py | |||
814 | index f4f5f7e..35d1196 100644 | |||
815 | --- a/lib/lp/bugs/scripts/tests/test_bugimport.py | |||
816 | +++ b/lib/lp/bugs/scripts/tests/test_bugimport.py | |||
817 | @@ -826,7 +826,7 @@ class TestBugWatch: | |||
818 | 826 | def updateStatus(self, new_remote_status, new_malone_status): | 826 | def updateStatus(self, new_remote_status, new_malone_status): |
819 | 827 | """See `IBugWatch`.""" | 827 | """See `IBugWatch`.""" |
820 | 828 | for bugtask in self.bug.bugtasks: | 828 | for bugtask in self.bug.bugtasks: |
822 | 829 | if bugtask.conjoined_master is not None: | 829 | if bugtask.conjoined_primary is not None: |
823 | 830 | continue | 830 | continue |
824 | 831 | bugtask = removeSecurityProxy(bugtask) | 831 | bugtask = removeSecurityProxy(bugtask) |
825 | 832 | bugtask._status = new_malone_status | 832 | bugtask._status = new_malone_status |
826 | diff --git a/lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt b/lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt | |||
827 | index 61e716f..4992385 100644 | |||
828 | --- a/lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt | |||
829 | +++ b/lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt | |||
830 | @@ -12,7 +12,7 @@ | |||
831 | 12 | <td style="padding: 0.3em 0em 0.3em 1.5em" | 12 | <td style="padding: 0.3em 0em 0.3em 1.5em" |
832 | 13 | tal:condition="data/indent_task"> | 13 | tal:condition="data/indent_task"> |
833 | 14 | <span class="sprite milestone"></span> | 14 | <span class="sprite milestone"></span> |
835 | 15 | <tal:not-conjoined-task condition="not: data/is_conjoined_slave"> | 15 | <tal:not-conjoined-task condition="not: data/is_conjoined_replica"> |
836 | 16 | <a | 16 | <a |
837 | 17 | tal:attributes="href data/target_link" | 17 | tal:attributes="href data/target_link" |
838 | 18 | tal:content="view/getSeriesTargetName" | 18 | tal:content="view/getSeriesTargetName" |
839 | @@ -46,18 +46,18 @@ | |||
840 | 46 | </span> | 46 | </span> |
841 | 47 | </td> | 47 | </td> |
842 | 48 | 48 | ||
844 | 49 | <tal:conjoined-task condition="data/is_conjoined_slave"> | 49 | <tal:conjoined-task condition="data/is_conjoined_replica"> |
845 | 50 | <td colspan="5" style="vertical-align: middle"> | 50 | <td colspan="5" style="vertical-align: middle"> |
846 | 51 | <span class="lesser"> | 51 | <span class="lesser"> |
847 | 52 | Status tracked in | 52 | Status tracked in |
849 | 53 | <tal:master tal:replace="view/getConjoinedMasterName"> | 53 | <tal:primary tal:replace="view/getConjoinedPrimaryName"> |
850 | 54 | Hoary | 54 | Hoary |
852 | 55 | </tal:master> | 55 | </tal:primary> |
853 | 56 | </span> | 56 | </span> |
854 | 57 | </td> | 57 | </td> |
855 | 58 | </tal:conjoined-task> | 58 | </tal:conjoined-task> |
856 | 59 | 59 | ||
858 | 60 | <tal:not-conjoined-task condition="not:data/is_conjoined_slave"> | 60 | <tal:not-conjoined-task condition="not:data/is_conjoined_replica"> |
859 | 61 | <td style="width: 20%; vertical-align: middle"> | 61 | <td style="width: 20%; vertical-align: middle"> |
860 | 62 | <div class="status-content" | 62 | <div class="status-content" |
861 | 63 | style="width: 100%; float: left" | 63 | style="width: 100%; float: left" |
862 | diff --git a/lib/lp/bugs/tests/bugtarget-questiontarget.txt b/lib/lp/bugs/tests/bugtarget-questiontarget.txt | |||
863 | index 5a7ae5b..4643a0a 100644 | |||
864 | --- a/lib/lp/bugs/tests/bugtarget-questiontarget.txt | |||
865 | +++ b/lib/lp/bugs/tests/bugtarget-questiontarget.txt | |||
866 | @@ -205,7 +205,7 @@ provided | |||
867 | 205 | 205 | ||
868 | 206 | >>> evo_bugtask.transitionToStatus(BugTaskStatus.INVALID, sample_person) | 206 | >>> evo_bugtask.transitionToStatus(BugTaskStatus.INVALID, sample_person) |
869 | 207 | >>> len([bt for bt in bugtasks | 207 | >>> len([bt for bt in bugtasks |
871 | 208 | ... if bt.status.title == 'New' and bt.conjoined_master is None]) | 208 | ... if bt.status.title == 'New' and bt.conjoined_primary is None]) |
872 | 209 | 1 | 209 | 1 |
873 | 210 | 210 | ||
874 | 211 | >>> big_bug.canBeAQuestion() | 211 | >>> big_bug.canBeAQuestion() |
875 | diff --git a/lib/lp/bugs/tests/test_bugsearch_conjoined.py b/lib/lp/bugs/tests/test_bugsearch_conjoined.py | |||
876 | index 6e28a4a..1407278 100644 | |||
877 | --- a/lib/lp/bugs/tests/test_bugsearch_conjoined.py | |||
878 | +++ b/lib/lp/bugs/tests/test_bugsearch_conjoined.py | |||
879 | @@ -36,7 +36,7 @@ class TestSearchBase(TestCaseWithFactory): | |||
880 | 36 | return bug | 36 | return bug |
881 | 37 | 37 | ||
882 | 38 | 38 | ||
884 | 39 | class TestProjectExcludeConjoinedMasterSearch(TestSearchBase): | 39 | class TestProjectExcludeConjoinedPrimarySearch(TestSearchBase): |
885 | 40 | """Tests of exclude_conjoined_tasks param for project milestones.""" | 40 | """Tests of exclude_conjoined_tasks param for project milestones.""" |
886 | 41 | 41 | ||
887 | 42 | layer = DatabaseFunctionalLayer | 42 | layer = DatabaseFunctionalLayer |
888 | @@ -55,7 +55,7 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase): | |||
889 | 55 | user=None, milestone=self.milestone, exclude_conjoined_tasks=True) | 55 | user=None, milestone=self.milestone, exclude_conjoined_tasks=True) |
890 | 56 | 56 | ||
891 | 57 | def test_search_results_count_simple(self): | 57 | def test_search_results_count_simple(self): |
893 | 58 | # Verify number of results with no conjoined masters. | 58 | # Verify number of results with no conjoined primaries. |
894 | 59 | self.assertEqual( | 59 | self.assertEqual( |
895 | 60 | self.bug_count, | 60 | self.bug_count, |
896 | 61 | self.bugtask_set.search(self.params).count()) | 61 | self.bugtask_set.search(self.params).count()) |
897 | @@ -70,7 +70,7 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase): | |||
898 | 70 | self.assertThat(recorder, HasQueryCount(Equals(4))) | 70 | self.assertThat(recorder, HasQueryCount(Equals(4))) |
899 | 71 | 71 | ||
900 | 72 | def test_search_results_count_with_other_productseries_tasks(self): | 72 | def test_search_results_count_with_other_productseries_tasks(self): |
902 | 73 | # Test with zero conjoined masters and bugtasks targeted to | 73 | # Test with zero conjoined primaries and bugtasks targeted to |
903 | 74 | # productseries that are not the development focus. | 74 | # productseries that are not the development focus. |
904 | 75 | productseries = self.factory.makeProductSeries(product=self.product) | 75 | productseries = self.factory.makeProductSeries(product=self.product) |
905 | 76 | extra_bugtasks = 0 | 76 | extra_bugtasks = 0 |
906 | @@ -84,14 +84,14 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase): | |||
907 | 84 | self.bug_count + extra_bugtasks, | 84 | self.bug_count + extra_bugtasks, |
908 | 85 | self.bugtask_set.search(self.params).count()) | 85 | self.bugtask_set.search(self.params).count()) |
909 | 86 | 86 | ||
913 | 87 | def test_search_results_count_with_conjoined_masters(self): | 87 | def test_search_results_count_with_conjoined_primarys(self): |
914 | 88 | # Test with increasing numbers of conjoined masters. | 88 | # Test with increasing numbers of conjoined primaries. |
915 | 89 | # The conjoined masters will exclude the conjoined slaves from | 89 | # The conjoined primaries will exclude the conjoined replicas from |
916 | 90 | # the results. | 90 | # the results. |
917 | 91 | tasks = list(self.bugtask_set.search(self.params)) | 91 | tasks = list(self.bugtask_set.search(self.params)) |
918 | 92 | for bug in self.bugs: | 92 | for bug in self.bugs: |
919 | 93 | # The product bugtask is in the results before the conjoined | 93 | # The product bugtask is in the results before the conjoined |
921 | 94 | # master is added. | 94 | # primary is added. |
922 | 95 | self.assertIn( | 95 | self.assertIn( |
923 | 96 | (bug.id, self.product), | 96 | (bug.id, self.product), |
924 | 97 | [(task.bug.id, task.product) for task in tasks]) | 97 | [(task.bug.id, task.product) for task in tasks]) |
925 | @@ -104,17 +104,17 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase): | |||
926 | 104 | (bug.id, self.product), | 104 | (bug.id, self.product), |
927 | 105 | [(task.bug.id, task.product) for task in tasks]) | 105 | [(task.bug.id, task.product) for task in tasks]) |
928 | 106 | 106 | ||
931 | 107 | def test_search_results_count_with_wontfix_conjoined_masters(self): | 107 | def test_search_results_count_with_wontfix_conjoined_primarys(self): |
932 | 108 | # Test that conjoined master bugtasks in the WONTFIX status | 108 | # Test that conjoined primary bugtasks in the WONTFIX status |
933 | 109 | # don't cause the bug to be excluded. | 109 | # don't cause the bug to be excluded. |
935 | 110 | masters = [ | 110 | primaries = [ |
936 | 111 | self.factory.makeBugTask( | 111 | self.factory.makeBugTask( |
937 | 112 | bug=bug, target=self.product.development_focus) | 112 | bug=bug, target=self.product.development_focus) |
938 | 113 | for bug in self.bugs] | 113 | for bug in self.bugs] |
939 | 114 | tasks = list(self.bugtask_set.search(self.params)) | 114 | tasks = list(self.bugtask_set.search(self.params)) |
943 | 115 | wontfix_masters_count = 0 | 115 | wontfix_primaries_count = 0 |
944 | 116 | for bugtask in masters: | 116 | for bugtask in primaries: |
945 | 117 | wontfix_masters_count += 1 | 117 | wontfix_primaries_count += 1 |
946 | 118 | self.assertNotIn( | 118 | self.assertNotIn( |
947 | 119 | (bugtask.bug.id, self.product), | 119 | (bugtask.bug.id, self.product), |
948 | 120 | [(task.bug.id, task.product) for task in tasks]) | 120 | [(task.bug.id, task.product) for task in tasks]) |
949 | @@ -122,14 +122,14 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase): | |||
950 | 122 | bugtask.transitionToStatus( | 122 | bugtask.transitionToStatus( |
951 | 123 | BugTaskStatus.WONTFIX, self.product.owner) | 123 | BugTaskStatus.WONTFIX, self.product.owner) |
952 | 124 | tasks = list(self.bugtask_set.search(self.params)) | 124 | tasks = list(self.bugtask_set.search(self.params)) |
954 | 125 | self.assertEqual(self.bug_count + wontfix_masters_count, | 125 | self.assertEqual(self.bug_count + wontfix_primaries_count, |
955 | 126 | len(tasks)) | 126 | len(tasks)) |
956 | 127 | self.assertIn( | 127 | self.assertIn( |
957 | 128 | (bugtask.bug.id, self.product), | 128 | (bugtask.bug.id, self.product), |
958 | 129 | [(task.bug.id, task.product) for task in tasks]) | 129 | [(task.bug.id, task.product) for task in tasks]) |
959 | 130 | 130 | ||
960 | 131 | 131 | ||
962 | 132 | class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase): | 132 | class TestProjectGroupExcludeConjoinedPrimarySearch(TestSearchBase): |
963 | 133 | """Tests of exclude_conjoined_tasks param for project group milestones.""" | 133 | """Tests of exclude_conjoined_tasks param for project group milestones.""" |
964 | 134 | 134 | ||
965 | 135 | layer = DatabaseFunctionalLayer | 135 | layer = DatabaseFunctionalLayer |
966 | @@ -151,7 +151,7 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase): | |||
967 | 151 | user=None, milestone=self.milestone, exclude_conjoined_tasks=True) | 151 | user=None, milestone=self.milestone, exclude_conjoined_tasks=True) |
968 | 152 | 152 | ||
969 | 153 | def test_search_results_count_simple(self): | 153 | def test_search_results_count_simple(self): |
971 | 154 | # Verify number of results with no conjoined masters. | 154 | # Verify number of results with no conjoined primaries. |
972 | 155 | self.assertEqual( | 155 | self.assertEqual( |
973 | 156 | self.bug_count, | 156 | self.bug_count, |
974 | 157 | self.bugtask_set.search(self.params).count()) | 157 | self.bugtask_set.search(self.params).count()) |
975 | @@ -166,7 +166,7 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase): | |||
976 | 166 | self.assertThat(recorder, HasQueryCount(Equals(4))) | 166 | self.assertThat(recorder, HasQueryCount(Equals(4))) |
977 | 167 | 167 | ||
978 | 168 | def test_search_results_count_with_other_productseries_tasks(self): | 168 | def test_search_results_count_with_other_productseries_tasks(self): |
980 | 169 | # Test with zero conjoined masters and bugtasks targeted to | 169 | # Test with zero conjoined primaries and bugtasks targeted to |
981 | 170 | # productseries that are not the development focus. | 170 | # productseries that are not the development focus. |
982 | 171 | extra_bugtasks = 0 | 171 | extra_bugtasks = 0 |
983 | 172 | for bug, product in self.bug_products.items(): | 172 | for bug, product in self.bug_products.items(): |
984 | @@ -180,8 +180,8 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase): | |||
985 | 180 | self.bug_count + extra_bugtasks, | 180 | self.bug_count + extra_bugtasks, |
986 | 181 | self.bugtask_set.search(self.params).count()) | 181 | self.bugtask_set.search(self.params).count()) |
987 | 182 | 182 | ||
990 | 183 | def test_search_results_count_with_conjoined_masters(self): | 183 | def test_search_results_count_with_conjoined_primarys(self): |
991 | 184 | # Test with increasing numbers of conjoined masters. | 184 | # Test with increasing numbers of conjoined primaries. |
992 | 185 | tasks = list(self.bugtask_set.search(self.params)) | 185 | tasks = list(self.bugtask_set.search(self.params)) |
993 | 186 | for bug, product in self.bug_products.items(): | 186 | for bug, product in self.bug_products.items(): |
994 | 187 | self.assertIn( | 187 | self.assertIn( |
995 | @@ -197,8 +197,8 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase): | |||
996 | 197 | (bug.id, product), | 197 | (bug.id, product), |
997 | 198 | [(task.bug.id, task.product) for task in tasks]) | 198 | [(task.bug.id, task.product) for task in tasks]) |
998 | 199 | 199 | ||
1001 | 200 | def test_search_results_count_with_irrelevant_conjoined_masters(self): | 200 | def test_search_results_count_with_irrelevant_conjoined_primarys(self): |
1002 | 201 | # Verify that a conjoined master in one project of the project | 201 | # Verify that a conjoined primary in one project of the project |
1003 | 202 | # group doesn't cause a bugtask on another project in the group | 202 | # group doesn't cause a bugtask on another project in the group |
1004 | 203 | # to be excluded from the project group milestone's bugs. | 203 | # to be excluded from the project group milestone's bugs. |
1005 | 204 | extra_bugtasks = 0 | 204 | extra_bugtasks = 0 |
1006 | @@ -216,7 +216,7 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase): | |||
1007 | 216 | with person_logged_in(other_product.owner): | 216 | with person_logged_in(other_product.owner): |
1008 | 217 | other_product_bugtask.transitionToMilestone( | 217 | other_product_bugtask.transitionToMilestone( |
1009 | 218 | other_product_milestone, other_product.owner) | 218 | other_product_milestone, other_product.owner) |
1011 | 219 | # Add conjoined master for the milestone on the new product. | 219 | # Add conjoined primary for the milestone on the new product. |
1012 | 220 | self.factory.makeBugTask( | 220 | self.factory.makeBugTask( |
1013 | 221 | bug=bug, target=other_product.development_focus) | 221 | bug=bug, target=other_product.development_focus) |
1014 | 222 | # The bug count should not change, since we are just adding | 222 | # The bug count should not change, since we are just adding |
1015 | @@ -225,15 +225,15 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase): | |||
1016 | 225 | self.bug_count + extra_bugtasks, | 225 | self.bug_count + extra_bugtasks, |
1017 | 226 | self.bugtask_set.search(self.params).count()) | 226 | self.bugtask_set.search(self.params).count()) |
1018 | 227 | 227 | ||
1021 | 228 | def test_search_results_count_with_wontfix_conjoined_masters(self): | 228 | def test_search_results_count_with_wontfix_conjoined_primarys(self): |
1022 | 229 | # Test that conjoined master bugtasks in the WONTFIX status | 229 | # Test that conjoined primary bugtasks in the WONTFIX status |
1023 | 230 | # don't cause the bug to be excluded. | 230 | # don't cause the bug to be excluded. |
1025 | 231 | masters = [ | 231 | primaries = [ |
1026 | 232 | self.factory.makeBugTask( | 232 | self.factory.makeBugTask( |
1027 | 233 | bug=bug, target=product.development_focus) | 233 | bug=bug, target=product.development_focus) |
1028 | 234 | for bug, product in self.bug_products.items()] | 234 | for bug, product in self.bug_products.items()] |
1029 | 235 | unexcluded_count = 0 | 235 | unexcluded_count = 0 |
1031 | 236 | for bugtask in masters: | 236 | for bugtask in primaries: |
1032 | 237 | unexcluded_count += 1 | 237 | unexcluded_count += 1 |
1033 | 238 | with person_logged_in(bugtask.target.owner): | 238 | with person_logged_in(bugtask.target.owner): |
1034 | 239 | bugtask.transitionToStatus( | 239 | bugtask.transitionToStatus( |
1035 | @@ -243,7 +243,7 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase): | |||
1036 | 243 | self.bugtask_set.search(self.params).count()) | 243 | self.bugtask_set.search(self.params).count()) |
1037 | 244 | 244 | ||
1038 | 245 | 245 | ||
1040 | 246 | class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase): | 246 | class TestDistributionExcludeConjoinedPrimarySearch(TestSearchBase): |
1041 | 247 | """Tests of exclude_conjoined_tasks param for distribution milestones.""" | 247 | """Tests of exclude_conjoined_tasks param for distribution milestones.""" |
1042 | 248 | 248 | ||
1043 | 249 | layer = DatabaseFunctionalLayer | 249 | layer = DatabaseFunctionalLayer |
1044 | @@ -262,7 +262,7 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase): | |||
1045 | 262 | user=None, milestone=self.milestone, exclude_conjoined_tasks=True) | 262 | user=None, milestone=self.milestone, exclude_conjoined_tasks=True) |
1046 | 263 | 263 | ||
1047 | 264 | def test_search_results_count_simple(self): | 264 | def test_search_results_count_simple(self): |
1049 | 265 | # Verify number of results with no conjoined masters. | 265 | # Verify number of results with no conjoined primaries. |
1050 | 266 | self.assertEqual( | 266 | self.assertEqual( |
1051 | 267 | self.bug_count, | 267 | self.bug_count, |
1052 | 268 | self.bugtask_set.search(self.params).count()) | 268 | self.bugtask_set.search(self.params).count()) |
1053 | @@ -278,7 +278,7 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase): | |||
1054 | 278 | self.assertThat(recorder, HasQueryCount(Equals(5))) | 278 | self.assertThat(recorder, HasQueryCount(Equals(5))) |
1055 | 279 | 279 | ||
1056 | 280 | def test_search_results_count_with_other_productseries_tasks(self): | 280 | def test_search_results_count_with_other_productseries_tasks(self): |
1058 | 281 | # Test with zero conjoined masters and bugtasks targeted to | 281 | # Test with zero conjoined primaries and bugtasks targeted to |
1059 | 282 | # productseries that are not the development focus. | 282 | # productseries that are not the development focus. |
1060 | 283 | distroseries = self.factory.makeDistroSeries( | 283 | distroseries = self.factory.makeDistroSeries( |
1061 | 284 | distribution=self.distro, status=SeriesStatus.SUPPORTED) | 284 | distribution=self.distro, status=SeriesStatus.SUPPORTED) |
1062 | @@ -293,12 +293,12 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase): | |||
1063 | 293 | self.bug_count + extra_bugtasks, | 293 | self.bug_count + extra_bugtasks, |
1064 | 294 | self.bugtask_set.search(self.params).count()) | 294 | self.bugtask_set.search(self.params).count()) |
1065 | 295 | 295 | ||
1068 | 296 | def test_search_results_count_with_conjoined_masters(self): | 296 | def test_search_results_count_with_conjoined_primarys(self): |
1069 | 297 | # Test with increasing numbers of conjoined masters. | 297 | # Test with increasing numbers of conjoined primaries. |
1070 | 298 | tasks = list(self.bugtask_set.search(self.params)) | 298 | tasks = list(self.bugtask_set.search(self.params)) |
1071 | 299 | for bug in self.bugs: | 299 | for bug in self.bugs: |
1072 | 300 | # The distro bugtask is in the results before the conjoined | 300 | # The distro bugtask is in the results before the conjoined |
1074 | 301 | # master is added. | 301 | # primary is added. |
1075 | 302 | self.assertIn( | 302 | self.assertIn( |
1076 | 303 | (bug.id, self.distro), | 303 | (bug.id, self.distro), |
1077 | 304 | [(task.bug.id, task.distribution) for task in tasks]) | 304 | [(task.bug.id, task.distribution) for task in tasks]) |
1078 | @@ -311,19 +311,19 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase): | |||
1079 | 311 | (bug.id, self.distro), | 311 | (bug.id, self.distro), |
1080 | 312 | [(task.bug.id, task.distribution) for task in tasks]) | 312 | [(task.bug.id, task.distribution) for task in tasks]) |
1081 | 313 | 313 | ||
1084 | 314 | def test_search_results_count_with_wontfix_conjoined_masters(self): | 314 | def test_search_results_count_with_wontfix_conjoined_primarys(self): |
1085 | 315 | # Test that conjoined master bugtasks in the WONTFIX status | 315 | # Test that conjoined primary bugtasks in the WONTFIX status |
1086 | 316 | # don't cause the bug to be excluded. | 316 | # don't cause the bug to be excluded. |
1088 | 317 | masters = [ | 317 | primaries = [ |
1089 | 318 | self.factory.makeBugTask( | 318 | self.factory.makeBugTask( |
1090 | 319 | bug=bug, target=self.distro.currentseries) | 319 | bug=bug, target=self.distro.currentseries) |
1091 | 320 | for bug in self.bugs] | 320 | for bug in self.bugs] |
1093 | 321 | wontfix_masters_count = 0 | 321 | wontfix_primaries_count = 0 |
1094 | 322 | tasks = list(self.bugtask_set.search(self.params)) | 322 | tasks = list(self.bugtask_set.search(self.params)) |
1097 | 323 | for bugtask in masters: | 323 | for bugtask in primaries: |
1098 | 324 | wontfix_masters_count += 1 | 324 | wontfix_primaries_count += 1 |
1099 | 325 | # The distro bugtask is still excluded by the conjoined | 325 | # The distro bugtask is still excluded by the conjoined |
1101 | 326 | # master. | 326 | # primary. |
1102 | 327 | self.assertNotIn( | 327 | self.assertNotIn( |
1103 | 328 | (bugtask.bug.id, self.distro), | 328 | (bugtask.bug.id, self.distro), |
1104 | 329 | [(task.bug.id, task.distribution) for task in tasks]) | 329 | [(task.bug.id, task.distribution) for task in tasks]) |
1105 | @@ -332,10 +332,10 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase): | |||
1106 | 332 | BugTaskStatus.WONTFIX, self.distro.owner) | 332 | BugTaskStatus.WONTFIX, self.distro.owner) |
1107 | 333 | tasks = list(self.bugtask_set.search(self.params)) | 333 | tasks = list(self.bugtask_set.search(self.params)) |
1108 | 334 | self.assertEqual( | 334 | self.assertEqual( |
1110 | 335 | self.bug_count + wontfix_masters_count, | 335 | self.bug_count + wontfix_primaries_count, |
1111 | 336 | self.bugtask_set.search(self.params).count()) | 336 | self.bugtask_set.search(self.params).count()) |
1112 | 337 | # The distro bugtask is no longer excluded by the conjoined | 337 | # The distro bugtask is no longer excluded by the conjoined |
1114 | 338 | # master, since its status is WONTFIX. | 338 | # primary, since its status is WONTFIX. |
1115 | 339 | self.assertIn( | 339 | self.assertIn( |
1116 | 340 | (bugtask.bug.id, self.distro), | 340 | (bugtask.bug.id, self.distro), |
1117 | 341 | [(task.bug.id, task.distribution) for task in tasks]) | 341 | [(task.bug.id, task.distribution) for task in tasks]) |
1118 | diff --git a/lib/lp/bugs/tests/test_bugwatch.py b/lib/lp/bugs/tests/test_bugwatch.py | |||
1119 | index d595d08..402253b 100644 | |||
1120 | --- a/lib/lp/bugs/tests/test_bugwatch.py | |||
1121 | +++ b/lib/lp/bugs/tests/test_bugwatch.py | |||
1122 | @@ -360,7 +360,7 @@ class TestBugWatch(TestCaseWithFactory): | |||
1123 | 360 | [product_task], list( | 360 | [product_task], list( |
1124 | 361 | removeSecurityProxy(watch).bugtasks_to_update)) | 361 | removeSecurityProxy(watch).bugtasks_to_update)) |
1125 | 362 | # If we add a task such that the existing task becomes a | 362 | # If we add a task such that the existing task becomes a |
1127 | 363 | # conjoined slave, only thr master task will be eligible for | 363 | # conjoined replica, only the primary task will be eligible for |
1128 | 364 | # update. | 364 | # update. |
1129 | 365 | product_series_task = self.factory.makeBugTask( | 365 | product_series_task = self.factory.makeBugTask( |
1130 | 366 | bug=bug, target=product.development_focus) | 366 | bug=bug, target=product.development_focus) |
1131 | diff --git a/lib/lp/registry/browser/__init__.py b/lib/lp/registry/browser/__init__.py | |||
1132 | index cb372fb..c9b6f86 100644 | |||
1133 | --- a/lib/lp/registry/browser/__init__.py | |||
1134 | +++ b/lib/lp/registry/browser/__init__.py | |||
1135 | @@ -236,8 +236,8 @@ class RegistryDeleteViewMixin: | |||
1136 | 236 | # milestone, since it's still referenced. | 236 | # milestone, since it's still referenced. |
1137 | 237 | for bugtask in self._getBugtasks(milestone, ignore_privacy=True): | 237 | for bugtask in self._getBugtasks(milestone, ignore_privacy=True): |
1138 | 238 | nb = removeSecurityProxy(bugtask) | 238 | nb = removeSecurityProxy(bugtask) |
1141 | 239 | if nb.conjoined_master is not None: | 239 | if nb.conjoined_primary is not None: |
1142 | 240 | Store.of(bugtask).remove(nb.conjoined_master) | 240 | Store.of(bugtask).remove(nb.conjoined_primary) |
1143 | 241 | else: | 241 | else: |
1144 | 242 | nb.milestone = None | 242 | nb.milestone = None |
1145 | 243 | removeSecurityProxy(milestone.all_specifications).set(milestoneID=None) | 243 | removeSecurityProxy(milestone.all_specifications).set(milestoneID=None) |
1146 | diff --git a/lib/lp/registry/browser/milestone.py b/lib/lp/registry/browser/milestone.py | |||
1147 | index fb645c2..865b7f9 100644 | |||
1148 | --- a/lib/lp/registry/browser/milestone.py | |||
1149 | +++ b/lib/lp/registry/browser/milestone.py | |||
1150 | @@ -218,22 +218,22 @@ class MilestoneViewMixin: | |||
1151 | 218 | """The list of non-conjoined bugtasks targeted to this milestone.""" | 218 | """The list of non-conjoined bugtasks targeted to this milestone.""" |
1152 | 219 | # Put the results in a list so that iterating over it multiple | 219 | # Put the results in a list so that iterating over it multiple |
1153 | 220 | # times in this method does not make multiple queries. | 220 | # times in this method does not make multiple queries. |
1155 | 221 | non_conjoined_slaves = self.context.bugtasks(self.user) | 221 | non_conjoined_replicas = self.context.bugtasks(self.user) |
1156 | 222 | # Checking bug permissions is expensive. We know from the query that | 222 | # Checking bug permissions is expensive. We know from the query that |
1157 | 223 | # the user has at least launchpad.View on the bugtasks and their bugs. | 223 | # the user has at least launchpad.View on the bugtasks and their bugs. |
1158 | 224 | # NB: this is in principle unneeded due to injection of permission in | 224 | # NB: this is in principle unneeded due to injection of permission in |
1159 | 225 | # the model layer now. | 225 | # the model layer now. |
1160 | 226 | precache_permission_for_objects( | 226 | precache_permission_for_objects( |
1162 | 227 | self.request, 'launchpad.View', non_conjoined_slaves) | 227 | self.request, 'launchpad.View', non_conjoined_replicas) |
1163 | 228 | precache_permission_for_objects( | 228 | precache_permission_for_objects( |
1164 | 229 | self.request, 'launchpad.View', | 229 | self.request, 'launchpad.View', |
1166 | 230 | [task.bug for task in non_conjoined_slaves]) | 230 | [task.bug for task in non_conjoined_replicas]) |
1167 | 231 | # We want the assignees loaded as we show them in the milestone home | 231 | # We want the assignees loaded as we show them in the milestone home |
1168 | 232 | # page. | 232 | # page. |
1169 | 233 | list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( | 233 | list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( |
1171 | 234 | [bug.assignee_id for bug in non_conjoined_slaves], | 234 | [bug.assignee_id for bug in non_conjoined_replicas], |
1172 | 235 | need_validity=True)) | 235 | need_validity=True)) |
1174 | 236 | return non_conjoined_slaves | 236 | return non_conjoined_replicas |
1175 | 237 | 237 | ||
1176 | 238 | @cachedproperty | 238 | @cachedproperty |
1177 | 239 | def _bug_badge_properties(self): | 239 | def _bug_badge_properties(self): |
1178 | diff --git a/lib/lp/registry/model/milestone.py b/lib/lp/registry/model/milestone.py | |||
1179 | index ed418c1..c6b5631 100644 | |||
1180 | --- a/lib/lp/registry/model/milestone.py | |||
1181 | +++ b/lib/lp/registry/model/milestone.py | |||
1182 | @@ -203,10 +203,10 @@ class MilestoneData: | |||
1183 | 203 | """The list of non-conjoined bugtasks targeted to this milestone.""" | 203 | """The list of non-conjoined bugtasks targeted to this milestone.""" |
1184 | 204 | # Put the results in a list so that iterating over it multiple | 204 | # Put the results in a list so that iterating over it multiple |
1185 | 205 | # times in this method does not make multiple queries. | 205 | # times in this method does not make multiple queries. |
1187 | 206 | non_conjoined_slaves = list( | 206 | non_conjoined_replicas = list( |
1188 | 207 | getUtility(IBugTaskSet).getPrecachedNonConjoinedBugTasks( | 207 | getUtility(IBugTaskSet).getPrecachedNonConjoinedBugTasks( |
1189 | 208 | user, self)) | 208 | user, self)) |
1191 | 209 | return non_conjoined_slaves | 209 | return non_conjoined_replicas |
1192 | 210 | 210 | ||
1193 | 211 | 211 | ||
1194 | 212 | @implementer(IHasBugs, IMilestone, IBugSummaryDimension) | 212 | @implementer(IHasBugs, IMilestone, IBugSummaryDimension) |
1195 | diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py | |||
1196 | index 09e1bca..b544ae3 100644 | |||
1197 | --- a/lib/lp/registry/model/person.py | |||
1198 | +++ b/lib/lp/registry/model/person.py | |||
1199 | @@ -1532,11 +1532,11 @@ class Person( | |||
1200 | 1532 | bulk.load_related(Milestone, tasks, ['milestone_id']) | 1532 | bulk.load_related(Milestone, tasks, ['milestone_id']) |
1201 | 1533 | 1533 | ||
1202 | 1534 | for task in tasks: | 1534 | for task in tasks: |
1208 | 1535 | # We skip masters (instead of slaves) from conjoined relationships | 1535 | # We skip primaries (instead of replicas) from conjoined |
1209 | 1536 | # because we can do that without hittind the DB, which would not | 1536 | # relationships because we can do that without hitting the DB, |
1210 | 1537 | # be possible if we wanted to skip the slaves. The simple (but | 1537 | # which would not be possible if we wanted to skip the replicas. |
1211 | 1538 | # expensive) way to skip the slaves would be to skip any tasks | 1538 | # The simple (but expensive) way to skip the replicas would be |
1212 | 1539 | # that have a non-None .conjoined_master. | 1539 | # to skip any tasks that have a non-None .conjoined_primary. |
1213 | 1540 | productseries = task.productseries | 1540 | productseries = task.productseries |
1214 | 1541 | distroseries = task.distroseries | 1541 | distroseries = task.distroseries |
1215 | 1542 | if productseries is not None and task.product is None: | 1542 | if productseries is not None and task.product is None: |
1216 | @@ -1546,10 +1546,11 @@ class Person( | |||
1217 | 1546 | continue | 1546 | continue |
1218 | 1547 | elif distroseries is not None: | 1547 | elif distroseries is not None: |
1219 | 1548 | candidate = None | 1548 | candidate = None |
1222 | 1549 | for possible_slave in tasks: | 1549 | for possible_replica in tasks: |
1223 | 1550 | sourcepackagename_id = possible_slave.sourcepackagename_id | 1550 | sourcepackagename_id = ( |
1224 | 1551 | possible_replica.sourcepackagename_id) | ||
1225 | 1551 | if sourcepackagename_id == task.sourcepackagename_id: | 1552 | if sourcepackagename_id == task.sourcepackagename_id: |
1227 | 1552 | candidate = possible_slave | 1553 | candidate = possible_replica |
1228 | 1553 | # Distribution.currentseries is expensive to run for every | 1554 | # Distribution.currentseries is expensive to run for every |
1229 | 1554 | # bugtask (as it goes through every series of that | 1555 | # bugtask (as it goes through every series of that |
1230 | 1555 | # distribution), but it's a cached property and there's only | 1556 | # distribution), but it's a cached property and there's only |
1231 | @@ -2258,10 +2259,10 @@ class Person( | |||
1232 | 2258 | coc.active = False | 2259 | coc.active = False |
1233 | 2259 | params = BugTaskSearchParams(self, assignee=self) | 2260 | params = BugTaskSearchParams(self, assignee=self) |
1234 | 2260 | for bug_task in self.searchTasks(params): | 2261 | for bug_task in self.searchTasks(params): |
1236 | 2261 | # If the bugtask has a conjoined master we don't try to | 2262 | # If the bugtask has a conjoined primary we don't try to |
1237 | 2262 | # update it, since we will update it correctly when we | 2263 | # update it, since we will update it correctly when we |
1240 | 2263 | # update its conjoined master (see bug 193983). | 2264 | # update its conjoined primary (see bug 193983). |
1241 | 2264 | if bug_task.conjoined_master is not None: | 2265 | if bug_task.conjoined_primary is not None: |
1242 | 2265 | continue | 2266 | continue |
1243 | 2266 | 2267 | ||
1244 | 2267 | # XXX flacoste 2007-11-26 bug=164635 The comparison using id in | 2268 | # XXX flacoste 2007-11-26 bug=164635 The comparison using id in |
1245 | diff --git a/lib/lp/registry/tests/test_milestonetag.py b/lib/lp/registry/tests/test_milestonetag.py | |||
1246 | index 10ff914..d946961 100644 | |||
1247 | --- a/lib/lp/registry/tests/test_milestonetag.py | |||
1248 | +++ b/lib/lp/registry/tests/test_milestonetag.py | |||
1249 | @@ -161,7 +161,7 @@ class ProjectGroupMilestoneTagTest(TestCaseWithFactory): | |||
1250 | 161 | target=self.project_group, tags=tags) | 161 | target=self.project_group, tags=tags) |
1251 | 162 | return items, milestonetag | 162 | return items, milestonetag |
1252 | 163 | 163 | ||
1254 | 164 | # Add a test similar to TestProjectExcludeConjoinedMasterSearch in | 164 | # Add a test similar to TestProjectExcludeConjoinedPrimarySearch in |
1255 | 165 | # lp.bugs.tests.test_bugsearch_conjoined. | 165 | # lp.bugs.tests.test_bugsearch_conjoined. |
1256 | 166 | 166 | ||
1257 | 167 | def test_bugtask_retrieve_single_milestone(self): | 167 | def test_bugtask_retrieve_single_milestone(self): |
1258 | diff --git a/lib/lp/registry/tests/test_person.py b/lib/lp/registry/tests/test_person.py | |||
1259 | index 1e9ef75..10d8456 100644 | |||
1260 | --- a/lib/lp/registry/tests/test_person.py | |||
1261 | +++ b/lib/lp/registry/tests/test_person.py | |||
1262 | @@ -1770,7 +1770,7 @@ class Test_getAssignedBugTasksDueBefore(TestCaseWithFactory): | |||
1263 | 1770 | 1770 | ||
1264 | 1771 | self.assertEqual(private_bug2.bugtasks, bugtasks) | 1771 | self.assertEqual(private_bug2.bugtasks, bugtasks) |
1265 | 1772 | 1772 | ||
1267 | 1773 | def test_skips_distroseries_task_that_is_a_conjoined_master(self): | 1773 | def test_skips_distroseries_task_that_is_a_conjoined_primary(self): |
1268 | 1774 | distroseries = self.factory.makeDistroSeries() | 1774 | distroseries = self.factory.makeDistroSeries() |
1269 | 1775 | sourcepackagename = self.factory.makeSourcePackageName() | 1775 | sourcepackagename = self.factory.makeSourcePackageName() |
1270 | 1776 | sp = distroseries.getSourcePackage(sourcepackagename.name) | 1776 | sp = distroseries.getSourcePackage(sourcepackagename.name) |
1271 | @@ -1780,38 +1780,38 @@ class Test_getAssignedBugTasksDueBefore(TestCaseWithFactory): | |||
1272 | 1780 | milestone=milestone, target=sp.distribution_sourcepackage) | 1780 | milestone=milestone, target=sp.distribution_sourcepackage) |
1273 | 1781 | removeSecurityProxy(bug).addTask(bug.owner, sp) | 1781 | removeSecurityProxy(bug).addTask(bug.owner, sp) |
1274 | 1782 | self.assertEqual(2, len(bug.bugtasks)) | 1782 | self.assertEqual(2, len(bug.bugtasks)) |
1281 | 1783 | slave, master = bug.bugtasks | 1783 | replica, primary = bug.bugtasks |
1282 | 1784 | self._assignBugTaskToTeamOwner(master) | 1784 | self._assignBugTaskToTeamOwner(primary) |
1283 | 1785 | self.assertEqual(None, master.conjoined_master) | 1785 | self.assertEqual(None, primary.conjoined_primary) |
1284 | 1786 | self.assertEqual(master, slave.conjoined_master) | 1786 | self.assertEqual(primary, replica.conjoined_primary) |
1285 | 1787 | self.assertEqual(slave.milestone, master.milestone) | 1787 | self.assertEqual(replica.milestone, primary.milestone) |
1286 | 1788 | self.assertEqual(slave.assignee, master.assignee) | 1788 | self.assertEqual(replica.assignee, primary.assignee) |
1287 | 1789 | 1789 | ||
1288 | 1790 | bugtasks = list(self.team.getAssignedBugTasksDueBefore( | 1790 | bugtasks = list(self.team.getAssignedBugTasksDueBefore( |
1289 | 1791 | self.today + timedelta(days=1), user=None)) | 1791 | self.today + timedelta(days=1), user=None)) |
1290 | 1792 | 1792 | ||
1292 | 1793 | self.assertEqual([slave], bugtasks) | 1793 | self.assertEqual([replica], bugtasks) |
1293 | 1794 | 1794 | ||
1295 | 1795 | def test_skips_productseries_task_that_is_a_conjoined_master(self): | 1795 | def test_skips_productseries_task_that_is_a_conjoined_primary(self): |
1296 | 1796 | milestone = self.factory.makeMilestone(dateexpected=self.today) | 1796 | milestone = self.factory.makeMilestone(dateexpected=self.today) |
1297 | 1797 | removeSecurityProxy(milestone.product).development_focus = ( | 1797 | removeSecurityProxy(milestone.product).development_focus = ( |
1298 | 1798 | milestone.productseries) | 1798 | milestone.productseries) |
1299 | 1799 | bug = self.factory.makeBug( | 1799 | bug = self.factory.makeBug( |
1300 | 1800 | series=milestone.productseries, milestone=milestone) | 1800 | series=milestone.productseries, milestone=milestone) |
1301 | 1801 | self.assertEqual(2, len(bug.bugtasks)) | 1801 | self.assertEqual(2, len(bug.bugtasks)) |
1303 | 1802 | slave, master = bug.bugtasks | 1802 | replica, primary = bug.bugtasks |
1304 | 1803 | 1803 | ||
1305 | 1804 | # This will cause the assignee to propagate to the other bugtask as | 1804 | # This will cause the assignee to propagate to the other bugtask as |
1306 | 1805 | # well since they're conjoined. | 1805 | # well since they're conjoined. |
1311 | 1806 | self._assignBugTaskToTeamOwner(slave) | 1806 | self._assignBugTaskToTeamOwner(replica) |
1312 | 1807 | self.assertEqual(master, slave.conjoined_master) | 1807 | self.assertEqual(primary, replica.conjoined_primary) |
1313 | 1808 | self.assertEqual(slave.milestone, master.milestone) | 1808 | self.assertEqual(replica.milestone, primary.milestone) |
1314 | 1809 | self.assertEqual(slave.assignee, master.assignee) | 1809 | self.assertEqual(replica.assignee, primary.assignee) |
1315 | 1810 | 1810 | ||
1316 | 1811 | bugtasks = list(self.team.getAssignedBugTasksDueBefore( | 1811 | bugtasks = list(self.team.getAssignedBugTasksDueBefore( |
1317 | 1812 | self.today + timedelta(days=1), user=None)) | 1812 | self.today + timedelta(days=1), user=None)) |
1318 | 1813 | 1813 | ||
1320 | 1814 | self.assertEqual([slave], bugtasks) | 1814 | self.assertEqual([replica], bugtasks) |
1321 | 1815 | 1815 | ||
1322 | 1816 | def _assignBugTaskToTeamOwnerAndSetMilestone(self, task, milestone): | 1816 | def _assignBugTaskToTeamOwnerAndSetMilestone(self, task, milestone): |
1323 | 1817 | self._assignBugTaskToTeamOwner(task) | 1817 | self._assignBugTaskToTeamOwner(task) |
Conjoined primary and replica sound good to me.