Merge lp:~twom/launchpad/manually-rescan-link into lp:launchpad

Proposed by Tom Wardill
Status: Merged
Merged at revision: 18863
Proposed branch: lp:~twom/launchpad/manually-rescan-link
Merge into: lp:launchpad
Diff against target: 296 lines (+156/-3)
8 files modified
lib/lp/code/browser/branch.py (+24/-0)
lib/lp/code/browser/configure.zcml (+6/-0)
lib/lp/code/browser/tests/test_branch.py (+36/-1)
lib/lp/code/interfaces/branch.py (+3/-0)
lib/lp/code/model/branch.py (+14/-1)
lib/lp/code/model/tests/test_branch.py (+40/-0)
lib/lp/code/templates/branch-index.pt (+19/-1)
lib/lp/code/templates/branch-rescan.pt (+14/-0)
To merge this branch: bzr merge lp:~twom/launchpad/manually-rescan-link
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+362139@code.launchpad.net

Commit message

Add 'rescan' button when a scan of a branch has failed.

Description of the change

Adds a button to the Branch view, only when the latest scan has failed or there are no scan jobs for a branch.

Also add a view at +rescan so a rescan can be manually triggered otherwise.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Needs Fixing
Revision history for this message
Maximiliano Bertacchini (maxiberta) wrote :

Nice feature, thanks! Just a quick review, with a couple of questions.

Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/browser/branch.py'
--- lib/lp/code/browser/branch.py 2018-07-16 00:49:00 +0000
+++ lib/lp/code/browser/branch.py 2019-01-28 13:31:58 +0000
@@ -118,6 +118,7 @@
118 english_list,118 english_list,
119 truncate_text,119 truncate_text,
120 )120 )
121from lp.services.job.interfaces.job import JobStatus
121from lp.services.propertycache import cachedproperty122from lp.services.propertycache import cachedproperty
122from lp.services.webapp import (123from lp.services.webapp import (
123 canonical_url,124 canonical_url,
@@ -587,6 +588,16 @@
587 """Only show the link if there are more than five."""588 """Only show the link if there are more than five."""
588 return len(self.landing_candidates) > 5589 return len(self.landing_candidates) > 5
589590
591 @property
592 def show_rescan_link(self):
593 """Only show the rescan button if the latest scan has failed"""
594 scan_job = self.context.getLatestScanJob()
595 # If there are no jobs, we failed to create one for some reason,
596 # so we should allow a rescan
597 if not scan_job:
598 return True
599 return scan_job.job.status == JobStatus.FAILED
600
590 @cachedproperty601 @cachedproperty
591 def linked_bugtasks(self):602 def linked_bugtasks(self):
592 """Return a list of bugtasks linked to the branch."""603 """Return a list of bugtasks linked to the branch."""
@@ -635,6 +646,19 @@
635 return self.context.getSpecificationLinks(self.user)646 return self.context.getSpecificationLinks(self.user)
636647
637648
649class BranchRescanView(LaunchpadEditFormView):
650
651 schema = Interface
652
653 field_names = []
654
655 @action('Rescan', name='rescan')
656 def rescan(self, action, data):
657 self.context.unscan(rescan=True)
658 self.request.response.addNotification("Branch scan scheduled")
659 self.next_url = canonical_url(self.context)
660
661
638class BranchEditFormView(LaunchpadEditFormView):662class BranchEditFormView(LaunchpadEditFormView):
639 """Base class for forms that edit a branch."""663 """Base class for forms that edit a branch."""
640664
641665
=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml 2018-11-09 22:46:32 +0000
+++ lib/lp/code/browser/configure.zcml 2019-01-28 13:31:58 +0000
@@ -437,6 +437,12 @@
437 permission="launchpad.Edit"437 permission="launchpad.Edit"
438 template="../../app/templates/generic-edit.pt"/>438 template="../../app/templates/generic-edit.pt"/>
439 <browser:page439 <browser:page
440 name="+rescan"
441 for="lp.code.interfaces.branch.IBranch"
442 class="lp.code.browser.branch.BranchRescanView"
443 permission="launchpad.Edit"
444 template="../templates/branch-rescan.pt"/>
445 <browser:page
440 name="+edit-import"446 name="+edit-import"
441 for="lp.code.interfaces.branch.IBranch"447 for="lp.code.interfaces.branch.IBranch"
442 class="lp.code.browser.codeimport.CodeImportEditView"448 class="lp.code.browser.codeimport.CodeImportEditView"
443449
=== modified file 'lib/lp/code/browser/tests/test_branch.py'
--- lib/lp/code/browser/tests/test_branch.py 2018-09-10 14:10:26 +0000
+++ lib/lp/code/browser/tests/test_branch.py 2019-01-28 13:31:58 +0000
@@ -34,6 +34,7 @@
34 RepositoryFormat,34 RepositoryFormat,
35 )35 )
36from lp.code.enums import BranchType36from lp.code.enums import BranchType
37from lp.code.model.branchjob import BranchScanJob
37from lp.code.tests.helpers import BranchHostingFixture38from lp.code.tests.helpers import BranchHostingFixture
38from lp.registry.enums import BranchSharingPolicy39from lp.registry.enums import BranchSharingPolicy
39from lp.registry.interfaces.accesspolicy import IAccessPolicySource40from lp.registry.interfaces.accesspolicy import IAccessPolicySource
@@ -43,6 +44,7 @@
43from lp.services.database.constants import UTC_NOW44from lp.services.database.constants import UTC_NOW
44from lp.services.features.testing import FeatureFixture45from lp.services.features.testing import FeatureFixture
45from lp.services.helpers import truncate_text46from lp.services.helpers import truncate_text
47from lp.services.job.interfaces.job import JobStatus
46from lp.services.webapp.publisher import canonical_url48from lp.services.webapp.publisher import canonical_url
47from lp.services.webapp.servers import LaunchpadTestRequest49from lp.services.webapp.servers import LaunchpadTestRequest
48from lp.testing import (50from lp.testing import (
@@ -299,6 +301,39 @@
299 '<a href="+recipes">2 recipes</a> using this branch.',301 '<a href="+recipes">2 recipes</a> using this branch.',
300 view.recipes_link)302 view.recipes_link)
301303
304 def test_show_rescan_link(self):
305 branch = self.factory.makeAnyBranch()
306 job = BranchScanJob.create(branch)
307 job.job._status = JobStatus.FAILED
308 view = create_initialized_view(branch, '+index')
309 result = view.show_rescan_link
310 self.assertTrue(result)
311
312 def test_show_rescan_link_no_failures(self):
313 branch = self.factory.makeAnyBranch()
314 job = BranchScanJob.create(branch)
315 job.job._status = JobStatus.COMPLETED
316 job.job.date_finished = UTC_NOW
317 view = create_initialized_view(branch, '+index')
318 result = view.show_rescan_link
319 self.assertFalse(result)
320
321 def test_show_rescan_link_no_scan_jobs(self):
322 branch = self.factory.makeAnyBranch()
323 view = create_initialized_view(branch, '+index')
324 result = view.show_rescan_link
325 self.assertTrue(result)
326
327 def test_show_rescan_link_latest_didnt_fail(self):
328 branch = self.factory.makeAnyBranch()
329 job = BranchScanJob.create(branch)
330 job.job._status = JobStatus.FAILED
331 job = BranchScanJob.create(branch)
332 job.job._status = JobStatus.COMPLETED
333 view = create_initialized_view(branch, '+index')
334 result = view.show_rescan_link
335 self.assertTrue(result)
336
302 def _addBugLinks(self, branch):337 def _addBugLinks(self, branch):
303 for status in BugTaskStatus.items:338 for status in BugTaskStatus.items:
304 bug = self.factory.makeBug(status=status)339 bug = self.factory.makeBug(status=status)
@@ -611,7 +646,7 @@
611 logout()646 logout()
612 with StormStatementRecorder() as recorder:647 with StormStatementRecorder() as recorder:
613 browser.open(branch_url)648 browser.open(branch_url)
614 self.assertThat(recorder, HasQueryCount(Equals(28)))649 self.assertThat(recorder, HasQueryCount(Equals(29)))
615650
616651
617class TestBranchViewPrivateArtifacts(BrowserTestCase):652class TestBranchViewPrivateArtifacts(BrowserTestCase):
618653
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py 2018-05-17 14:10:29 +0000
+++ lib/lp/code/interfaces/branch.py 2019-01-28 13:31:58 +0000
@@ -1034,6 +1034,9 @@
1034 detail page.1034 detail page.
1035 """1035 """
10361036
1037 def getLatestScanJob():
1038 """Get the latest IBranchScanJob for this branch"""
1039
1037 def checkUpgrade():1040 def checkUpgrade():
1038 """Check whether an upgrade should be performed, and raise if not.1041 """Check whether an upgrade should be performed, and raise if not.
10391042
10401043
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2018-12-10 13:54:34 +0000
+++ lib/lp/code/model/branch.py 2019-01-28 13:31:58 +0000
@@ -167,7 +167,10 @@
167from lp.services.database.datetimecol import UtcDateTimeCol167from lp.services.database.datetimecol import UtcDateTimeCol
168from lp.services.database.decoratedresultset import DecoratedResultSet168from lp.services.database.decoratedresultset import DecoratedResultSet
169from lp.services.database.enumcol import EnumCol169from lp.services.database.enumcol import EnumCol
170from lp.services.database.interfaces import IMasterStore170from lp.services.database.interfaces import (
171 IMasterStore,
172 IStore,
173 )
171from lp.services.database.sqlbase import (174from lp.services.database.sqlbase import (
172 SQLBase,175 SQLBase,
173 sqlvalues,176 sqlvalues,
@@ -1295,6 +1298,16 @@
1295 job.celeryRunOnCommit()1298 job.celeryRunOnCommit()
1296 return (self.last_mirrored_id, old_scanned_id)1299 return (self.last_mirrored_id, old_scanned_id)
12971300
1301 def getLatestScanJob(self):
1302 from lp.code.model.branchjob import BranchJob, BranchScanJob
1303 latest_job = IStore(BranchJob).find(
1304 BranchJob,
1305 BranchJob.branch == self,
1306 Job.date_finished != None,
1307 BranchJob.job_type == BranchScanJob.class_job_type).order_by(
1308 Desc(Job.date_finished)).first()
1309 return latest_job
1310
1298 def requestMirror(self):1311 def requestMirror(self):
1299 """See `IBranch`."""1312 """See `IBranch`."""
1300 if self.branch_type in (BranchType.REMOTE, BranchType.HOSTED):1313 if self.branch_type in (BranchType.REMOTE, BranchType.HOSTED):
13011314
=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py 2018-07-09 09:27:06 +0000
+++ lib/lp/code/model/tests/test_branch.py 2019-01-28 13:31:58 +0000
@@ -105,6 +105,7 @@
105from lp.code.model.branchjob import (105from lp.code.model.branchjob import (
106 BranchJob,106 BranchJob,
107 BranchJobType,107 BranchJobType,
108 BranchScanJob,
108 ReclaimBranchSpaceJob,109 ReclaimBranchSpaceJob,
109 )110 )
110from lp.code.model.branchmergeproposal import BranchMergeProposal111from lp.code.model.branchmergeproposal import BranchMergeProposal
@@ -3502,6 +3503,45 @@
3502 getUtility(ILaunchpadCelebrities).commercial_admin):3503 getUtility(ILaunchpadCelebrities).commercial_admin):
3503 branch.unscan()3504 branch.unscan()
35043505
3506 def test_getLatestScanJob(self):
3507 complete_date = datetime.now(UTC)
3508
3509 branch = self.factory.makeAnyBranch()
3510 failed_job = BranchScanJob.create(branch)
3511 failed_job.job._status = JobStatus.FAILED
3512 failed_job.job.date_finished = complete_date
3513 completed_job = BranchScanJob.create(branch)
3514 completed_job.job._status = JobStatus.COMPLETED
3515 completed_job.job.date_finished = complete_date - timedelta(seconds=10)
3516 result = branch.getLatestScanJob()
3517 self.assertEqual(failed_job.id, result.id)
3518
3519 def test_getLatestScanJob_no_scans(self):
3520 branch = self.factory.makeAnyBranch()
3521 result = branch.getLatestScanJob()
3522 self.assertIsNone(result)
3523
3524 def test_getLatestScanJob_correct_branch(self):
3525 complete_date = datetime.now(UTC)
3526
3527 main_branch = self.factory.makeAnyBranch()
3528 second_branch = self.factory.makeAnyBranch()
3529 failed_job = BranchScanJob.create(second_branch)
3530 failed_job.job._status = JobStatus.FAILED
3531 failed_job.job.date_finished = complete_date
3532 completed_job = BranchScanJob.create(main_branch)
3533 completed_job.job._status = JobStatus.COMPLETED
3534 completed_job.job.date_finished = complete_date - timedelta(seconds=10)
3535 result = main_branch.getLatestScanJob()
3536 self.assertEqual(completed_job.id, result.id)
3537
3538 def test_getLatestScanJob_without_completion_date(self):
3539 branch = self.factory.makeAnyBranch()
3540 failed_job = BranchScanJob.create(branch)
3541 failed_job.job._status = JobStatus.FAILED
3542 result = branch.getLatestScanJob()
3543 self.assertFalse(result)
3544
35053545
3506class TestWebservice(TestCaseWithFactory):3546class TestWebservice(TestCaseWithFactory):
3507 """Tests for the webservice."""3547 """Tests for the webservice."""
35083548
=== modified file 'lib/lp/code/templates/branch-index.pt'
--- lib/lp/code/templates/branch-index.pt 2017-11-06 09:32:45 +0000
+++ lib/lp/code/templates/branch-index.pt 2019-01-28 13:31:58 +0000
@@ -130,7 +130,7 @@
130130
131 </div>131 </div>
132132
133 <div class="yui-g" tal:condition="view/pending_updates">133 <div class="yui-g" tal:condition="python: not view.show_rescan_link and view.pending_updates">
134 <div class="portlet">134 <div class="portlet">
135 <div id="branch-pending-updates" class="pending-update">135 <div id="branch-pending-updates" class="pending-update">
136 <h3>Updating branch...</h3>136 <h3>Updating branch...</h3>
@@ -142,6 +142,24 @@
142 </div>142 </div>
143 </div>143 </div>
144144
145 <div class="yui-g" tal:condition="view/show_rescan_link">
146 <div class="portlet">
147 <div id="branch-scan-failed" class="pending-update">
148 <h3>Branch scan failed</h3>
149 <p>
150 Scanning this branch for changes failed. You can manually rescan if required.
151 </p>
152 <p>
153 <form action="+rescan" name="launchpadform" method="post" enctype="multipart/form-data" accept-charset="UTF-8">
154 <input id="field.actions.rescan" class="button" type="submit"
155 name="field.actions.rescan" value="Rescan" />
156 </form>
157 </p>
158 </div>
159 </div>
160 </div>
161
162
145 <div class="yui-g">163 <div class="yui-g">
146 <div class="portlet" id="recent-revisions">164 <div class="portlet" id="recent-revisions">
147 <h2>Recent revisions</h2>165 <h2>Recent revisions</h2>
148166
=== added file 'lib/lp/code/templates/branch-rescan.pt'
--- lib/lp/code/templates/branch-rescan.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/code/templates/branch-rescan.pt 2019-01-28 13:31:58 +0000
@@ -0,0 +1,14 @@
1<html
2 xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:tal="http://xml.zope.org/namespaces/tal"
4 xmlns:metal="http://xml.zope.org/namespaces/metal"
5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
6 metal:use-macro="view/macro:page/main_only"
7 i18n:domain="launchpad">
8 <body>
9 <div metal:fill-slot="main">
10 <p>You can schedule a rescan for this branch if it appears the branch is out of date.</p>
11 <div metal:use-macro="context/@@launchpad_form/form" />
12 </div>
13 </body>
14</html>