launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #07777
[Merge] lp:~wgrant/launchpad/bugtaskflat-by-default-3 into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/bugtaskflat-by-default-3 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/bugtaskflat-by-default-3/+merge/105445
This branch ports a couple of things away from BugSet.searchAsUser, allowing it to be deleted. It was the second-last holdout for non-BugTaskFlat get_bug_privacy_filter usage.
latest_bugs gets replaced with a getMostRecentlyReportedBugs similar to getMostRecentlyFixedBugs, with its definition slightly changed: it's now the 5 distinct bugs with the most recently created tasks, rather than the 5 most recently created bugs. bug_count gets replaced with a SELECT MAX(id) FROM Bug, sufficiently similar that nobody should notice the different. Both are satisfiable far more rapidly than the queries they replace, and they let us remove deprecated non-BugTaskFlat code.
I also simplified getMostRecentlyFixedBugs while I was there. It previously filtered on date_closed with backoff until it found enough bugs, but with BugTaskFlat it's now fast enough to sort without a filter.
--
https://code.launchpad.net/~wgrant/launchpad/bugtaskflat-by-default-3/+merge/105445
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/bugtaskflat-by-default-3 into lp:launchpad.
=== modified file 'lib/lp/bugs/browser/bug.py'
--- lib/lp/bugs/browser/bug.py 2012-05-02 04:44:01 +0000
+++ lib/lp/bugs/browser/bug.py 2012-05-11 07:07:20 +0000
@@ -25,10 +25,6 @@
'MaloneView',
]
-from datetime import (
- datetime,
- timedelta,
- )
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
import re
@@ -45,7 +41,6 @@
)
from lazr.restful.interface import copy_field
from lazr.restful.interfaces import IJSONRequestCache
-import pytz
from simplejson import dumps
from zope import formlib
from zope.app.form.browser import TextWidget
@@ -105,10 +100,7 @@
from lp.services.librarian.browser import ProxiedLibraryFileAlias
from lp.services.mail.mailwrapper import MailWrapper
from lp.services.propertycache import cachedproperty
-from lp.services.searchbuilder import (
- any,
- greater_than,
- )
+from lp.services.searchbuilder import any
from lp.services.webapp import (
canonical_url,
ContextMenu,
@@ -425,39 +417,19 @@
else:
return self.request.response.redirect(canonical_url(bug))
- def getMostRecentlyFixedBugs(self, limit=5, when=None):
- """Return the ten most recently fixed bugs."""
- if when is None:
- when = datetime.now(pytz.timezone('UTC'))
- date_closed_limits = [
- timedelta(days=1),
- timedelta(days=7),
- timedelta(days=30),
- None,
- ]
- for date_closed_limit in date_closed_limits:
- fixed_bugs = []
- search_params = BugTaskSearchParams(
- self.user, status=BugTaskStatus.FIXRELEASED,
- orderby='-date_closed')
- if date_closed_limit is not None:
- search_params.date_closed = greater_than(
- when - date_closed_limit)
- fixed_bugtasks = self.context.searchTasks(search_params)
- # XXX: Bjorn Tillenius 2006-12-13:
- # We might end up returning less than :limit: bugs, but in
- # most cases we won't, and '4*limit' is here to prevent
- # this page from timing out in production. Later I'll fix
- # this properly by selecting bugs instead of bugtasks.
- # If fixed_bugtasks isn't sliced, it will take a long time
- # to iterate over it, even over just 10, because
- # Transaction.iterSelect() listifies the result.
- for bugtask in fixed_bugtasks[:4 * limit]:
- if bugtask.bug not in fixed_bugs:
- fixed_bugs.append(bugtask.bug)
- if len(fixed_bugs) >= limit:
- return fixed_bugs
- return fixed_bugs
+ def getMostRecentlyFixedBugs(self, limit=5):
+ """Return the five most recently fixed bugs."""
+ params = BugTaskSearchParams(
+ self.user, status=BugTaskStatus.FIXRELEASED,
+ orderby='-date_closed')
+ return getUtility(IBugSet).getDistinctBugsForBugTasks(
+ self.context.searchTasks(params), self.user, limit)
+
+ def getMostRecentlyReportedBugs(self, limit=5):
+ """Return the five most recently reported bugs."""
+ params = BugTaskSearchParams(self.user, orderby='-datecreated')
+ return getUtility(IBugSet).getDistinctBugsForBugTasks(
+ self.context.searchTasks(params), self.user, limit)
def getCveBugLinkCount(self):
"""Return the number of links between bugs and CVEs there are."""
=== removed file 'lib/lp/bugs/browser/tests/recently-fixed-bugs.txt'
--- lib/lp/bugs/browser/tests/recently-fixed-bugs.txt 2009-06-12 16:36:02 +0000
+++ lib/lp/bugs/browser/tests/recently-fixed-bugs.txt 1970-01-01 00:00:00 +0000
@@ -1,104 +0,0 @@
-= Displaying recently fixed bugs =
-
-On the Bugs frontpage, the 5 most recently fixed bugs are displayed.
-There's a view class called MaloneView that helps getting the list of
-bugs.
-
- >>> from lp.bugs.browser.bug import MaloneView
- >>> class TestMaloneView(MaloneView):
- ... """Test version of MaloneView.
- ...
- ... Override the user attribute, so we don't need the component
- ... architecture set up.
- ... """
- ... user = None
-
-MaloneView.getMostRecentlyFixedBugs() calls the context's searchTasks()
-method, getting all the FIXRELEASED bugs, ordered by date_closed, with
-the newly closed bugs first.
-
- >>> class FakeBugTask:
- ... def __init__(self):
- ... self.bug = object()
-
- >>> class FakeContext:
- ... def searchTasks(self, search_params):
- ... print "status: %s" % search_params.status.name
- ... print "order by: %s" % search_params.orderby
- ... return [FakeBugTask() for dummy in range(5)]
- >>> view = TestMaloneView(FakeContext(), None)
- >>> view.user = None
- >>> fixed_bugs = view.getMostRecentlyFixedBugs()
- status: FIXRELEASED
- order by: -date_closed
- >>> len(fixed_bugs)
- 5
-
-Searching through all the bug tasks can be quite slow. It's much faster
-(compare 4 seconds to less than 100 ms) to limit the search, so that
-only the very recently closed bugs are considered. It starts with
-searching for bugs that have been closed during the last day.
-
- >>> import pytz
- >>> from datetime import datetime, timedelta
- >>> utc_now = datetime(2008, 8, 31, 12, 0, 0, tzinfo=pytz.timezone('UTC'))
- >>> class LimitByDateClosedContext:
- ... def __init__(self, date_closed_limit):
- ... self.date_closed_limit = date_closed_limit
- ... def searchTasks(self, search_params):
- ... print "Searching for bugs with date_closed: %s" % (
- ... search_params.date_closed)
- ... if (search_params.date_closed is not None and
- ... search_params.date_closed.value < self.date_closed_limit):
- ... return [FakeBugTask() for dummy in range(5)]
- ... else:
- ... return [FakeBugTask() for dummy in range(4)]
- >>> view = TestMaloneView(LimitByDateClosedContext(utc_now), None)
- >>> fixed_bugs = view.getMostRecentlyFixedBugs(when=utc_now)
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 30, 12, 0, tzinfo=<UTC>))
-
-Since we can't guarantee that enough bugs have been closed during a
-certain time period, the search is limited with regard to date_closed
-using increasing ranges. If not enough bugs have been closed during the
-last day, the search is increased to a week.
-
- >>> view = TestMaloneView(
- ... LimitByDateClosedContext(utc_now-timedelta(days=2)), None)
- >>> fixed_bugs = view.getMostRecentlyFixedBugs(when=utc_now)
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 30, 12, 0, tzinfo=<UTC>))
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 24, 12, 0, tzinfo=<UTC>))
-
-If not enough bugs are closed during the last week, the search is
-extended to the last 30 days.
-
- >>> view = TestMaloneView(
- ... LimitByDateClosedContext(utc_now-timedelta(days=8)), None)
- >>> fixed_bugs = view.getMostRecentlyFixedBugs(when=utc_now)
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 30, 12, 0, tzinfo=<UTC>))
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 24, 12, 0, tzinfo=<UTC>))
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 1, 12, 0, tzinfo=<UTC>))
-
-And finally, if not enough bugs have been closed during the last 30
-days, the date_closed limit is removed.
-
- >>> view = TestMaloneView(
- ... LimitByDateClosedContext(utc_now-timedelta(days=31)), None)
- >>> fixed_bugs = view.getMostRecentlyFixedBugs(when=utc_now)
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 30, 12, 0, tzinfo=<UTC>))
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 24, 12, 0, tzinfo=<UTC>))
- Searching for bugs with date_closed:
- greater_than(datetime.datetime(2008, 8, 1, 12, 0, tzinfo=<UTC>))
- Searching for bugs with date_closed: None
-
-It doesn't matter that less than 4 bugs were returned.
-
- >>> len(fixed_bugs)
- 4
=== removed file 'lib/lp/bugs/browser/tests/test_recentlyfixedbugs.py'
--- lib/lp/bugs/browser/tests/test_recentlyfixedbugs.py 2011-12-22 05:09:10 +0000
+++ lib/lp/bugs/browser/tests/test_recentlyfixedbugs.py 1970-01-01 00:00:00 +0000
@@ -1,19 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Test harness for running the recently-fixed-bugs.txt tests."""
-
-__metaclass__ = type
-
-__all__ = []
-
-import unittest
-
-from lp.testing.systemdocs import LayeredDocFileSuite
-
-
-def test_suite():
- suite = unittest.TestSuite()
- test = LayeredDocFileSuite('recently-fixed-bugs.txt')
- suite.addTest(test)
- return suite
=== modified file 'lib/lp/bugs/doc/bug.txt'
--- lib/lp/bugs/doc/bug.txt 2012-05-02 12:45:53 +0000
+++ lib/lp/bugs/doc/bug.txt 2012-05-11 07:07:20 +0000
@@ -172,44 +172,6 @@
security proxied, which doesn't make sense to test here.)
-Searching for Bugs
-------------------
-
-To search for bugs matching specific criteria, use IBugSet.searchAsUser:
-
- >>> from lp.services.database.sqlbase import flush_database_updates
- >>> from lp.services.webapp.interfaces import ILaunchBag
-
- >>> def current_user():
- ... return getUtility(ILaunchBag).user
-
- >>> login("foo.bar@xxxxxxxxxxxxx")
-
- >>> from lp.registry.interfaces.product import IProductSet
- >>> productset = getUtility(IProductSet)
- >>> firefox = productset.get(4)
- >>> firefox_test_bug = bugset.get(3)
- >>> firefox_master_bug = factory.makeBug(product=firefox)
- >>> firefox_test_bug.markAsDuplicate(firefox_master_bug)
- >>> flush_database_updates()
-
- >>> dups_of_bug_six = bugset.searchAsUser(
- ... duplicateof=firefox_master_bug, user=current_user())
- >>> print dups_of_bug_six.count()
- 1
- >>> dups_of_bug_six[0].id
- 3
-
- >>> firefox_test_bug.markAsDuplicate(None)
- >>> flush_database_updates()
- >>> dups_of_bug_six = bugset.searchAsUser(
- ... duplicateof=firefox_crashes, user=current_user())
- >>> print dups_of_bug_six.count()
- 0
-
- >>> login(ANONYMOUS)
-
-
Absolute URLs
-------------
@@ -217,6 +179,7 @@
include a URL to the bug inside the email.
>>> from lp.services.webapp import canonical_url
+ >>> login(ANONYMOUS)
>>> print canonical_url(firefox_crashes)
http://.../bugs/6
@@ -235,6 +198,11 @@
For the purposes of demonstration, we'll make the firefox crashing bug
private. A bug cannot be made private by an anonymous user.
+ >>> from lp.services.webapp.interfaces import ILaunchBag
+
+ >>> def current_user():
+ ... return getUtility(ILaunchBag).user
+
>>> firefox_crashes.private = True
Traceback (most recent call last):
...
@@ -316,14 +284,18 @@
Note that a search will return all public bugs, omitting bug 14 which is
private:
+ >>> from lp.bugs.interfaces.bugtask import (
+ ... IBugTaskSet, BugTaskSearchParams)
+ >>> from lp.bugs.model.bug import Bug
>>> from lp.services.database.lpstorm import IStore
- >>> from lp.bugs.model.bug import Bug
+
>>> all_bugs = set(IStore(Bug).find(Bug).values(Bug.id))
+ >>> taskset = getUtility(IBugTaskSet)
>>> def hidden_bugs():
... found_bugs = set(
- ... bug.id for bug in bugset.searchAsUser(
- ... user=current_user()))
+ ... task.bug.id for task in taskset.search(
+ ... BugTaskSearchParams(current_user())))
... return sorted(all_bugs - found_bugs)
>>> login("test@xxxxxxxxxxxxx")
@@ -424,6 +396,9 @@
When a public bug is filed:
+ >>> from lp.registry.interfaces.product import IProductSet
+ >>> productset = getUtility(IProductSet)
+ >>> firefox = productset.get(4)
>>> foobar = personset.getByEmail('foo.bar@xxxxxxxxxxxxx')
>>> params = CreateBugParams(
... title="test firefox bug", comment="blah blah blah", owner=foobar)
@@ -855,7 +830,7 @@
>>> print firefox_bug.duplicateof
None
- >>> firefox_bug.markAsDuplicate(firefox_master_bug)
+ >>> firefox_bug.markAsDuplicate(factory.makeBug())
>>> bug_duplicateof_changed = ObjectModifiedEvent(
... firefox_bug, bug_before_modification, ["duplicateof"])
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2012-05-11 00:39:47 +0000
+++ lib/lp/bugs/interfaces/bug.py 2012-05-11 07:07:20 +0000
@@ -1199,13 +1199,6 @@
If it can't be found, NotFoundError will be raised.
"""
- def searchAsUser(user, duplicateof=None, orderBy=None, limit=None):
- """Find bugs matching the search criteria provided.
-
- To search as an anonymous user, the user argument passed
- should be None.
- """
-
def queryByRemoteBug(bugtracker, remotebug):
"""Find one or None bugs for the BugWatch and bug tracker.
=== modified file 'lib/lp/bugs/interfaces/malone.py'
--- lib/lp/bugs/interfaces/malone.py 2012-04-26 23:21:35 +0000
+++ lib/lp/bugs/interfaces/malone.py 2012-05-11 07:07:20 +0000
@@ -45,7 +45,6 @@
"products and distributions")
bugtracker_count = Attribute("The number of bug trackers in Launchpad")
top_bugtrackers = Attribute("The BugTrackers with the most watches.")
- latest_bugs = Attribute("The latest 5 bugs filed.")
@collection_default_content()
def empty_list():
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2012-05-11 00:39:47 +0000
+++ lib/lp/bugs/model/bug.py 2012-05-11 07:07:20 +0000
@@ -2050,7 +2050,7 @@
If bug privacy rights are changed here, corresponding changes need
to be made to the queries which screen for privacy. See
- Bug.searchAsUser and BugTask.get_bug_privacy_filter_with_decorator.
+ bugtasksearch's get_bug_privacy_filter.
"""
from lp.bugs.interfaces.bugtask import BugTaskSearchParams
@@ -2667,27 +2667,6 @@
"Unable to locate bug with nickname %s." % bugid)
return bug
- def searchAsUser(self, user, duplicateof=None, orderBy=None, limit=None):
- """See `IBugSet`."""
- from lp.bugs.model.bugtasksearch import get_bug_privacy_filter
-
- where_clauses = []
- if duplicateof:
- where_clauses.append("Bug.duplicateof = %d" % duplicateof.id)
-
- privacy_filter = get_bug_privacy_filter(user)
- if privacy_filter:
- where_clauses.append(privacy_filter)
-
- other_params = {}
- if orderBy:
- other_params['orderBy'] = orderBy
- if limit:
- other_params['limit'] = limit
-
- return Bug.select(
- ' AND '.join(where_clauses), **other_params)
-
def queryByRemoteBug(self, bugtracker, remotebug):
"""See `IBugSet`."""
bug = Bug.selectFirst("""
=== modified file 'lib/lp/bugs/templates/malone-index.pt'
--- lib/lp/bugs/templates/malone-index.pt 2011-05-26 22:12:51 +0000
+++ lib/lp/bugs/templates/malone-index.pt 2012-05-11 07:07:20 +0000
@@ -59,7 +59,7 @@
<div class="portlet">
<h2>Recently reported</h2>
<ul id="reported-bugs">
- <li tal:repeat="bug context/latest_bugs"
+ <li tal:repeat="bug view/getMostRecentlyReportedBugs"
tal:replace="structure bug/@@+listing-detailed" />
</ul>
</div>
=== modified file 'lib/lp/systemhomes.py'
--- lib/lp/systemhomes.py 2012-04-26 23:21:35 +0000
+++ lib/lp/systemhomes.py 2012-05-11 07:07:20 +0000
@@ -21,6 +21,7 @@
from lazr.restful import ServiceRootResource
from lazr.restful.interfaces import ITopLevelEntryLink
+from storm.expr import Max
from zope.component import getUtility
from zope.interface import implements
@@ -40,6 +41,7 @@
IMaloneApplication,
IPrivateMaloneApplication,
)
+from lp.bugs.model.bug import Bug
from lp.code.interfaces.codehosting import (
IBazaarApplication,
ICodehostingApplication,
@@ -67,6 +69,7 @@
IProductSet,
)
from lp.services.config import config
+from lp.services.database.lpstorm import IStore
from lp.services.feeds.interfaces.application import IFeedsApplication
from lp.services.statistics.interfaces.statistic import ILaunchpadStatisticSet
from lp.services.webapp.interfaces import (
@@ -146,8 +149,7 @@
@property
def bug_count(self):
- user = getUtility(ILaunchBag).user
- return getUtility(IBugSet).searchAsUser(user=user).count()
+ return IStore(Bug).find(Max(Bug.id)).one()
@property
def bugwatch_count(self):
@@ -175,12 +177,6 @@
def top_bugtrackers(self):
return getUtility(IBugTrackerSet).getMostActiveBugTrackers(limit=5)
- @property
- def latest_bugs(self):
- user = getUtility(ILaunchBag).user
- return getUtility(IBugSet).searchAsUser(
- user=user, orderBy=['-datecreated'], limit=5)
-
def empty_list(self):
"""See `IMaloneApplication`."""
return []
Follow ups