Merge lp:~stevenk/launchpad/destroy-plus-daily-builds into lp:launchpad

Proposed by Steve Kowalik on 2012-09-12
Status: Merged
Approved by: Steve Kowalik on 2012-09-12
Approved revision: no longer in the source branch.
Merged at revision: 15943
Proposed branch: lp:~stevenk/launchpad/destroy-plus-daily-builds
Merge into: lp:launchpad
Diff against target: 367 lines (+1/-328)
5 files modified
lib/lp/code/browser/configure.zcml (+0/-8)
lib/lp/code/browser/recipebuildslisting.py (+0/-91)
lib/lp/code/browser/tests/test_recipebuildslisting.py (+0/-214)
lib/lp/code/stories/branches/xx-bazaar-home.txt (+0/-13)
lib/lp/code/templates/bazaar-index.pt (+1/-2)
To merge this branch: bzr merge lp:~stevenk/launchpad/destroy-plus-daily-builds
Reviewer Review Type Date Requested Status
Robert Collins (community) 2012-09-12 Approve on 2012-09-12
Review via email: mp+123888@code.launchpad.net

Commit Message

Destroy RootObject:+daily-builds. It has been broken for a long while, and is not visited much.

Description of the Change

Destroy RootObject:+daily-builds. Its main query is horrible with eight joins, takes 5 *minutes* on DF with a cold cache, has been visited 120 times only for the past month and has been broken for at least 12 months. It is time for it to die, so remove it.

To post a comment you must log in.
Robert Collins (lifeless) wrote :

I think its fine to remove this. One thing that occurs to me, if you wanted to fix it, would be to limit the build dates to the last 24 hours, which would dramatically shorten the amount of rows to be examined.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/configure.zcml'
2--- lib/lp/code/browser/configure.zcml 2012-08-28 04:16:32 +0000
3+++ lib/lp/code/browser/configure.zcml 2012-09-12 06:19:27 +0000
4@@ -141,14 +141,6 @@
5 name="+index"
6 template="../templates/bazaar-index.pt" />
7 <browser:page
8- for="lp.services.webapp.interfaces.ILaunchpadRoot"
9- layer="lp.code.publisher.CodeLayer"
10- permission="zope.Public"
11- class="lp.code.browser.recipebuildslisting.CompletedDailyBuildsView"
12- name="+daily-builds"
13- template="../templates/daily-builds-listing.pt" />
14-
15- <browser:page
16 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal"
17 layer="lp.code.publisher.CodeLayer"
18 name="+hierarchy"
19
20=== removed file 'lib/lp/code/browser/recipebuildslisting.py'
21--- lib/lp/code/browser/recipebuildslisting.py 2012-01-01 02:58:52 +0000
22+++ lib/lp/code/browser/recipebuildslisting.py 1970-01-01 00:00:00 +0000
23@@ -1,91 +0,0 @@
24-# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
25-# GNU Affero General Public License version 3 (see the file LICENSE).
26-
27-"""View for daily builds listings."""
28-
29-__metaclass__ = type
30-
31-__all__ = [
32- 'CompletedDailyBuildsView',
33- ]
34-
35-from lazr.enum import (
36- EnumeratedType,
37- Item,
38- )
39-from zope.component import getUtility
40-from zope.interface import Interface
41-from zope.schema import Choice
42-
43-from lp import _
44-from lp.app.browser.launchpadform import (
45- custom_widget,
46- LaunchpadFormView,
47- )
48-from lp.app.widgets.itemswidgets import LaunchpadDropdownWidget
49-from lp.code.interfaces.recipebuild import IRecipeBuildRecordSet
50-from lp.services.webapp.batching import BatchNavigator
51-
52-
53-class RecipeBuildFilter(EnumeratedType):
54- """Choices for how to filter recipe build listings."""
55-
56- ALL = Item("""
57- at any time
58-
59- Show all most recently completed recipe builds.
60- """)
61-
62- WITHIN_30_DAYS = Item("""
63- within last 30 days
64-
65- Show only recently completed recipe builds from within the last
66- 30 days.
67- """)
68-
69-
70-class RecipeBuildBatchNavigator(BatchNavigator):
71- """A Batch Navigator turn activate table sorting for single page views."""
72-
73- @property
74- def table_class(self):
75- if self.has_multiple_pages:
76- return "listing"
77- else:
78- return "listing sortable"
79-
80-
81-class CompletedDailyBuildsView(LaunchpadFormView):
82- """The view to show completed builds for source package recipes."""
83-
84- class schema(Interface):
85- when_completed_filter = Choice(
86- title=_('Recipe Build Filter'), vocabulary=RecipeBuildFilter,
87- default=RecipeBuildFilter.ALL,
88- description=_(
89- "Filter for selecting when recipe builds have completed."))
90- field_names = ['when_completed_filter']
91- custom_widget('when_completed_filter', LaunchpadDropdownWidget)
92-
93- @property
94- def page_title(self):
95- return 'Packages Built Daily With Recipes'
96-
97- def initialize(self):
98- LaunchpadFormView.initialize(self)
99- self.dailybuilds = self.getDailyBuilds()
100- self.batchnav = RecipeBuildBatchNavigator(
101- self.dailybuilds, self.request)
102-
103- def getDailyBuilds(self):
104- widget = self.widgets['when_completed_filter']
105- if widget.hasValidInput():
106- when_completed = widget.getInputValue()
107- if when_completed == RecipeBuildFilter.WITHIN_30_DAYS:
108- epoch_days = 30
109- else:
110- epoch_days = None
111- else:
112- epoch_days = None
113- recipe_build_set = getUtility(IRecipeBuildRecordSet)
114- return recipe_build_set.findCompletedDailyBuilds(epoch_days)
115
116=== removed file 'lib/lp/code/browser/tests/test_recipebuildslisting.py'
117--- lib/lp/code/browser/tests/test_recipebuildslisting.py 2012-01-01 02:58:52 +0000
118+++ lib/lp/code/browser/tests/test_recipebuildslisting.py 1970-01-01 00:00:00 +0000
119@@ -1,214 +0,0 @@
120-# Copyright 2010 Canonical Ltd. This software is licensed under the
121-# GNU Affero General Public License version 3 (see the file LICENSE).
122-
123-"""Tests for recipe build listings."""
124-
125-__metaclass__ = type
126-
127-
128-from zope.component import getUtility
129-
130-from lp.services.webapp.interfaces import ILaunchpadRoot
131-from lp.services.webapp.publisher import canonical_url
132-from lp.testing import (
133- ANONYMOUS,
134- BrowserTestCase,
135- login,
136- TestCaseWithFactory,
137- )
138-from lp.testing.layers import DatabaseFunctionalLayer
139-from lp.testing.matchers import BrowsesWithQueryLimit
140-from lp.testing.pages import (
141- extract_text,
142- find_tag_by_id,
143- )
144-from lp.testing.views import create_initialized_view
145-
146-
147-class TestRecipeBuildView(TestCaseWithFactory):
148- """Tests for `CompletedDailyBuildsView`."""
149-
150- layer = DatabaseFunctionalLayer
151-
152- def test_recipebuildrecords(self):
153- all_records, recent_records = (
154- self.factory.makeRecipeBuildRecords(10, 5))
155- login(ANONYMOUS)
156- root = getUtility(ILaunchpadRoot)
157- view = create_initialized_view(root, "+daily-builds", rootsite='code')
158- # It's easier to do it this way than sorted() since __lt__ doesn't
159- # work properly on zope proxies.
160- self.assertEqual(15, view.dailybuilds.count())
161- # By default, all build records will be included in the view.
162- self.assertEqual(set(all_records), set(view.dailybuilds))
163-
164-
165-class TestRecipeBuildListing(BrowserTestCase):
166- """Browser tests for the Recipe Build Listing page."""
167- layer = DatabaseFunctionalLayer
168-
169- def _extract_view_text(self, recipe_build_record):
170- text = '\n'.join(str(item) for item in (
171- recipe_build_record.sourcepackagename.name,
172- recipe_build_record.recipe.name,
173- recipe_build_record.recipeowner.displayname,
174- recipe_build_record.archive.displayname,
175- recipe_build_record.most_recent_build_time.strftime(
176- '%Y-%m-%d %H:%M:%S')))
177- return text
178-
179- def _test_recipebuild_listing(self, no_login=False):
180- # Test the text on a recipe build listing page is as expected.
181- all_records, [recent_record] = (
182- self.factory.makeRecipeBuildRecords(1, 5))
183- record_text = self._extract_view_text(recent_record)
184- root = getUtility(ILaunchpadRoot)
185- text = self.getMainText(
186- root, '+daily-builds', rootsite='code', no_login=no_login)
187- expected_text = """
188- Packages Built Daily With Recipes
189- .*
190- Source Package
191- Recipe
192- Recipe Owner
193- Archive
194- Most Recent Build Time
195- """ + record_text
196- self.assertTextMatchesExpressionIgnoreWhitespace(expected_text, text)
197-
198- def test_recipebuild_listing_no_records(self):
199- # Test the expected text when there is no data.
200- root = getUtility(ILaunchpadRoot)
201- text = self.getMainText(root, '+daily-builds', rootsite='code')
202- expected_text = "No recently completed daily builds found."
203- self.assertTextMatchesExpressionIgnoreWhitespace(expected_text, text)
204-
205- def test_recipebuild_listing_anonymous(self):
206- # Ensure we can see the listing when we are not logged in.
207- self._test_recipebuild_listing(no_login=True)
208-
209- def test_recipebuild_listing_with_user(self):
210- # Ensure we can see the listing when we are logged in.
211- self._test_recipebuild_listing()
212-
213- def test_recipebuild_listing_querycount(self):
214- # The query count on the recipe build listing page is small enough.
215- # There's a base query count of approx 30, but if the page template
216- # is not set up right, the query count can increases linearly with the
217- # number of records.
218- self.factory.makeRecipeBuildRecords(5, 0)
219- root = getUtility(ILaunchpadRoot)
220- browser_query_limit = BrowsesWithQueryLimit(
221- 35, self.user, view_name='+daily-builds', rootsite='code')
222- self.assertThat(root, browser_query_limit)
223-
224- def test_recipebuild_url(self):
225- # Check the browser URL is as expected.
226- root_url = self.layer.appserver_root_url(facet='code')
227- user_browser = self.getUserBrowser("%s/+daily-builds" % root_url)
228- self.assertEqual(
229- user_browser.url, "%s/+daily-builds" % root_url)
230-
231- def test_recentbuild_filter(self):
232- login(ANONYMOUS)
233- all_records, recent_records = (
234- self.factory.makeRecipeBuildRecords(3, 2))
235- records_text = set()
236- for record in recent_records:
237- record_text = self._extract_view_text(
238- record).replace(' ', '').replace('\n', '')
239- records_text.add(record_text)
240-
241- root_url = self.layer.appserver_root_url(facet='code')
242- browser = self.getUserBrowser("%s/+daily-builds" % root_url)
243- status_control = browser.getControl(
244- name='field.when_completed_filter')
245-
246- status_control.value = ['WITHIN_30_DAYS']
247- browser.getControl('Filter').click()
248- table = find_tag_by_id(browser.contents, 'daily-build-listing')
249-
250- view_records_text = set()
251- for row in table.tbody.fetch('tr'):
252- text = extract_text(row)
253- view_records_text.add(text.replace(' ', '').replace('\n', ''))
254- self.assertEquals(records_text, view_records_text)
255-
256- def test_recentbuild_filter_with_no_records(self):
257- # This test ensures that the filter control works properly when the
258- # filtered record set contains no records. We should be able to
259- # select All again and have the page re-display all records."
260-
261- login(ANONYMOUS)
262- # Create records all outside the filter time window.
263- all_records, recent_records = (
264- self.factory.makeRecipeBuildRecords(0, 2))
265- records_text = set()
266- for record in all_records:
267- record_text = self._extract_view_text(
268- record).replace(' ', '').replace('\n', '')
269- records_text.add(record_text)
270-
271- def check_build_records(table):
272- view_records_text = set()
273- for row in table.tbody.fetch('tr'):
274- text = extract_text(row)
275- view_records_text.add(text.replace(' ', '').replace('\n', ''))
276- self.assertEquals(records_text, view_records_text)
277-
278- # Initial rendering has all records.
279- root_url = self.layer.appserver_root_url(facet='code')
280- browser = self.getUserBrowser("%s/+daily-builds" % root_url)
281- table = find_tag_by_id(browser.contents, 'daily-build-listing')
282- check_build_records(table)
283-
284- # There are no filtered records.
285- status_control = browser.getControl(
286- name='field.when_completed_filter')
287- status_control.value = ['WITHIN_30_DAYS']
288- browser.getControl('Filter').click()
289- table = find_tag_by_id(browser.contents, 'daily-build-listing')
290- self.assertIs(None, table)
291-
292- # We can click All and see the record again.
293- status_control = browser.getControl(
294- name='field.when_completed_filter')
295- status_control.value = ['ALL']
296- browser.getControl('Filter').click()
297- table = find_tag_by_id(browser.contents, 'daily-build-listing')
298- check_build_records(table)
299-
300- def test_all_records_filter(self):
301- login(ANONYMOUS)
302- all_records, recent_records = (
303- self.factory.makeRecipeBuildRecords(3, 2))
304- records_text = set()
305- for record in all_records:
306- record_text = self._extract_view_text(
307- record).replace(' ', '').replace('\n', '')
308- records_text.add(record_text)
309-
310- root_url = self.layer.appserver_root_url(facet='code')
311- browser = self.getUserBrowser("%s/+daily-builds" % root_url)
312- status_control = browser.getControl(
313- name='field.when_completed_filter')
314-
315- status_control.value = ['ALL']
316- browser.getControl('Filter').click()
317- table = find_tag_by_id(browser.contents, 'daily-build-listing')
318-
319- view_records_text = set()
320- for row in table.tbody.fetch('tr'):
321- text = extract_text(row)
322- view_records_text.add(text.replace(' ', '').replace('\n', ''))
323- self.assertEquals(records_text, view_records_text)
324-
325- def test_one_recipe_redirects_to_recipe_page(self):
326- # Ensure that if the product or person has only one recipe, they are
327- # redirected right to the recipe page.
328- recipe = self.factory.makeSourcePackageRecipe()
329- root_url = self.layer.appserver_root_url(facet='code')
330- recipes_url = '%s/~%s/+recipes' % (root_url, recipe.owner.name)
331- expected_url = canonical_url(recipe)
332- browser = self.getUserBrowser(recipes_url)
333- self.assertEquals(expected_url, browser.url)
334
335=== modified file 'lib/lp/code/stories/branches/xx-bazaar-home.txt'
336--- lib/lp/code/stories/branches/xx-bazaar-home.txt 2012-03-01 17:42:28 +0000
337+++ lib/lp/code/stories/branches/xx-bazaar-home.txt 2012-09-12 06:19:27 +0000
338@@ -152,16 +152,3 @@
339 Project
340 Last Modified
341 Last Commit
342-
343-
344-Recently built recipes
345-......................
346-
347-Source package recipes have their own section on the home page.
348-
349- >>> browser.open('http://code.launchpad.dev/')
350- >>> changed = find_tag_by_id(browser.contents, 'build-recipes')
351- >>> print changed.fetch('a')[0]['href']
352- /+daily-builds
353- >>> print changed.fetch('a')[-1]['href']
354- https://help.launchpad.net/Packaging/SourceBuilds
355
356=== modified file 'lib/lp/code/templates/bazaar-index.pt'
357--- lib/lp/code/templates/bazaar-index.pt 2012-06-11 00:47:38 +0000
358+++ lib/lp/code/templates/bazaar-index.pt 2012-09-12 06:19:27 +0000
359@@ -53,8 +53,7 @@
360 </p>
361 <p id="build-recipes" class="application-summary" style="padding-top: 0.25em;">
362 Launchpad can build Ubuntu packages directly from branches using recipes.
363- We currently build over <a href="/+daily-builds">100 different packages</a>
364- into PPAs automatically using this method.
365+ We currently build over 100 different packages into PPAs automatically using this method.
366 (<a href="https://help.launchpad.net/Packaging/SourceBuilds">Learn more about recipes</a>)
367 </p>
368