Merge lp:~gary/launchpad/bug724025 into lp:launchpad

Proposed by Gary Poster
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 13772
Proposed branch: lp:~gary/launchpad/bug724025
Merge into: lp:launchpad
Diff against target: 522 lines (+144/-162)
5 files modified
lib/lp/bugs/browser/bugtask.py (+72/-81)
lib/lp/bugs/browser/configure.zcml (+1/-2)
lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt (+66/-74)
setup.py (+1/-2)
versions.cfg (+4/-3)
To merge this branch: bzr merge lp:~gary/launchpad/bug724025
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+72484@code.launchpad.net

Commit message

[incr] [r=gmb][bug=724025] Speed up an inner loop for BugTask:+index with many tasks.

Description of the change

This branch takes the previous work to reduce the Python cost of many tasks on the BugTask:+index page and extends it a bit more. This time, we optimize the inner loop of rendering many tasks by calculating the shared data for the view carefully at initialization, and by using a Chameleon template for the rendering.

Note that this code has *no* SQL queries while the inner loop is run, thanks to previous work by Robert. This is entirely rendering time.

The initialization saves a bit less than half a second on my system with 160/320 bugtasks (the doubled number represents the parent bugtask for a sourcepackage). Hopefully it is fairly straightforward to read: I collect the data, and then the template is simplified a bit.

The switch to Chameleon is even less to read. It also saves around half a second in the conditions I described above. You might know that I disabled Chameleon earlier for ++profile++. The new version does not write compiled files to disk by default. While I would prefer it to do this in the future, it is simpler to do what I have done here. The first time BugTask:+index is rendered, a bit of time will be spent to calculate the bytecode for the template; subsequently, the cost will be gone until the process restarts.

There are no tests, because it is existing functionality, and we already were not generating SQL in the inner loop.

Lint is happy.

To QA, go to any BugTask:+index page. The first time a +index page is rendered for that process, it will be a bit slower, as described above. Subsequently, you should have a modest speed gain.

This bug will get one or two more changes before I move on. Minimally, I plan to make only the first 50 bugtasks render initially, with the current context bugtask at the top, and then JS to render the remaining tasks on demand. Ideally, I'll also address an unrelated aspect of why this page is slow: we need to pre-load associated branches, using code that Danilo worked on in another branch.

Thank you

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve (code)
Revision history for this message
Gary Poster (gary) wrote :

This should have been [incr]. :-/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py 2011-08-22 13:13:28 +0000
+++ lib/lp/bugs/browser/bugtask.py 2011-08-23 01:53:28 +0000
@@ -75,7 +75,7 @@
75from lazr.uri import URI75from lazr.uri import URI
76from pytz import utc76from pytz import utc
77from simplejson import dumps77from simplejson import dumps
78from z3c.ptcompat import ViewPageTemplateFile78from z3c.pt.pagetemplate import ViewPageTemplateFile
79from zope import (79from zope import (
80 component,80 component,
81 formlib,81 formlib,
@@ -1067,7 +1067,7 @@
1067class BugTaskBugWatchMixin:1067class BugTaskBugWatchMixin:
1068 """A mixin to be used where a BugTask view displays BugWatch data."""1068 """A mixin to be used where a BugTask view displays BugWatch data."""
10691069
1070 @property1070 @cachedproperty
1071 def bug_watch_error_message(self):1071 def bug_watch_error_message(self):
1072 """Return a browser-useable error message for a bug watch."""1072 """Return a browser-useable error message for a bug watch."""
1073 if not self.context.bugwatch:1073 if not self.context.bugwatch:
@@ -3383,10 +3383,50 @@
3383 target_link_title = None3383 target_link_title = None
3384 many_bugtasks = False3384 many_bugtasks = False
33853385
3386 template = ViewPageTemplateFile(
3387 '../templates/bugtask-tasks-and-nominations-table-row.pt')
3388
3386 def __init__(self, context, request):3389 def __init__(self, context, request):
3387 super(BugTaskTableRowView, self).__init__(context, request)3390 super(BugTaskTableRowView, self).__init__(context, request)
3388 self.milestone_source = MilestoneVocabulary3391 self.milestone_source = MilestoneVocabulary
33893392
3393 def initialize(self):
3394 super(BugTaskTableRowView, self).initialize()
3395 link = canonical_url(self.context)
3396 edit_link = link + '/+editstatus'
3397 view_link = link + '/+viewstatus'
3398 can_edit = check_permission('launchpad.Edit', self.context)
3399 task_link = edit_link if can_edit else view_link
3400 bugtask_id = self.context.id
3401 launchbag = getUtility(ILaunchBag)
3402 is_primary = self.context.id == launchbag.bugtask.id
3403 self.data = dict(
3404 # Looking at many_bugtasks is an important optimization. With
3405 # 150+ bugtasks, it can save three or four seconds of rendering
3406 # time.
3407 expandable=(not self.many_bugtasks and self.canSeeTaskDetails()),
3408 indent_task=ISeriesBugTarget.providedBy(self.context.target),
3409 is_conjoined_slave=self.is_conjoined_slave,
3410 task_link=task_link,
3411 edit_link=edit_link,
3412 view_link=view_link,
3413 can_edit=can_edit,
3414 link=link,
3415 id=bugtask_id,
3416 row_id='tasksummary%d' % bugtask_id,
3417 form_row_id='task%d' % bugtask_id,
3418 row_css_class='highlight' if is_primary else None,
3419 target_link=canonical_url(self.context.target),
3420 target_link_title=self.target_link_title,
3421 user_can_edit_importance=self.context.userCanEditImportance(
3422 self.user),
3423 importance_css_class='importance' + self.context.importance.name,
3424 importance_title=self.context.importance.title,
3425 # We always look up all milestones, so there's no harm
3426 # using len on the list here and avoid the COUNT query.
3427 target_has_milestones=len(self._visible_milestones) > 0,
3428 )
3429
3390 def canSeeTaskDetails(self):3430 def canSeeTaskDetails(self):
3391 """Whether someone can see a task's status details.3431 """Whether someone can see a task's status details.
33923432
@@ -3405,42 +3445,6 @@
3405 self.context.bug.duplicateof is None and3445 self.context.bug.duplicateof is None and
3406 not self.is_converted_to_question)3446 not self.is_converted_to_question)
34073447
3408 def expandable(self):
3409 """Can the task's details be expanded?
3410
3411 They can if there are not too many bugtasks, and if the user can see
3412 the task details."""
3413 # Looking at many_bugtasks is an important optimization. With 150+
3414 # bugtasks, it can save three or four seconds of rendering time.
3415 return not self.many_bugtasks and self.canSeeTaskDetails()
3416
3417 def getTaskRowCSSClass(self):
3418 """The appropriate CSS class for the row in the Affects table.
3419
3420 Currently this consists solely of highlighting the current context.
3421 """
3422 bugtask = self.context
3423 if bugtask == getUtility(ILaunchBag).bugtask:
3424 return 'highlight'
3425 else:
3426 return None
3427
3428 def shouldIndentTask(self):
3429 """Should this task be indented in the task listing on the bug page?
3430
3431 Returns True or False.
3432 """
3433 return ISeriesBugTarget.providedBy(self.context.target)
3434
3435 def taskLink(self):
3436 """Return the proper link to the bugtask whether it's editable."""
3437 user = getUtility(ILaunchBag).user
3438 bugtask = self.context
3439 if check_permission('launchpad.Edit', user):
3440 return canonical_url(bugtask) + "/+editstatus"
3441 else:
3442 return canonical_url(bugtask) + "/+viewstatus"
3443
3444 def _getSeriesTargetNameHelper(self, bugtask):3448 def _getSeriesTargetNameHelper(self, bugtask):
3445 """Return the short name of bugtask's targeted series."""3449 """Return the short name of bugtask's targeted series."""
3446 series = bugtask.distroseries or bugtask.productseries3450 series = bugtask.distroseries or bugtask.productseries
@@ -3535,15 +3539,6 @@
35353539
3536 return items3540 return items
35373541
3538 @cachedproperty
3539 def target_has_milestones(self):
3540 """Are there any milestones we can target?
3541
3542 We always look up all milestones, so there's no harm
3543 using len on the list here and avoid the COUNT query.
3544 """
3545 return len(self._visible_milestones) > 0
3546
3547 def bugtask_canonical_url(self):3542 def bugtask_canonical_url(self):
3548 """Return the canonical url for the bugtask."""3543 """Return the canonical url for the bugtask."""
3549 return canonical_url(self.context)3544 return canonical_url(self.context)
@@ -3564,7 +3559,7 @@
3564 """3559 """
3565 return self.user is not None3560 return self.user is not None
35663561
3567 @property3562 @cachedproperty
3568 def user_can_edit_milestone(self):3563 def user_can_edit_milestone(self):
3569 """Can the user edit the Milestone field?3564 """Can the user edit the Milestone field?
35703565
@@ -3602,44 +3597,40 @@
3602 'title': filter.title,3597 'title': filter.title,
3603 'description': filter.description,3598 'description': filter.description,
3604 })3599 })
3605
3606 # Display the search field only if the user can set any person3600 # Display the search field only if the user can set any person
3607 # or team3601 # or team
3608 user = getUtility(ILaunchBag).user3602 user = self.user
3609 hide_assignee_team_selection = (3603 hide_assignee_team_selection = (
3610 not self.context.userCanSetAnyAssignee(user) and3604 not self.context.userCanSetAnyAssignee(user) and
3611 (user is None or user.teams_participated_in.count() == 0))3605 (user is None or user.teams_participated_in.count() == 0))
3612 return dumps({3606 cx = self.context
3613 'row_id': 'tasksummary%s' % self.context.id,3607 return dumps(dict(
3614 'bugtask_path': '/'.join(3608 row_id=self.data['row_id'],
3615 [''] + canonical_url(self.context).split('/')[3:]),3609 bugtask_path='/'.join([''] + self.data['link'].split('/')[3:]),
3616 'prefix': get_prefix(self.context),3610 prefix=get_prefix(cx),
3617 'assignee_value': self.context.assignee3611 assignee_value=cx.assignee and cx.assignee.name,
3618 and self.context.assignee.name,3612 assignee_is_team=cx.assignee and cx.assignee.is_team,
3619 'assignee_is_team': self.context.assignee3613 assignee_vocabulary=assignee_vocabulary,
3620 and self.context.assignee.is_team,3614 assignee_vocabulary_filters=filter_details,
3621 'assignee_vocabulary': assignee_vocabulary,3615 hide_assignee_team_selection=hide_assignee_team_selection,
3622 'assignee_vocabulary_filters': filter_details,3616 user_can_unassign=cx.userCanUnassign(user),
3623 'hide_assignee_team_selection': hide_assignee_team_selection,3617 target_is_product=IProduct.providedBy(cx.target),
3624 'user_can_unassign': self.context.userCanUnassign(user),3618 status_widget_items=self.status_widget_items,
3625 'target_is_product': IProduct.providedBy(self.context.target),3619 status_value=cx.status.title,
3626 'status_widget_items': self.status_widget_items,3620 importance_widget_items=self.importance_widget_items,
3627 'status_value': self.context.status.title,3621 importance_value=cx.importance.title,
3628 'importance_widget_items': self.importance_widget_items,3622 milestone_widget_items=self.milestone_widget_items,
3629 'importance_value': self.context.importance.title,3623 milestone_value=(
3630 'milestone_widget_items': self.milestone_widget_items,3624 canonical_url(
3631 'milestone_value': (self.context.milestone and3625 cx.milestone,
3632 canonical_url(3626 request=IWebServiceClientRequest(self.request))
3633 self.context.milestone,3627 if cx.milestone else None),
3634 request=IWebServiceClientRequest(3628 user_can_edit_assignee=self.user_can_edit_assignee,
3635 self.request)) or3629 user_can_edit_milestone=self.user_can_edit_milestone,
3636 None),3630 user_can_edit_status=not cx.bugwatch,
3637 'user_can_edit_assignee': self.user_can_edit_assignee,3631 user_can_edit_importance=(
3638 'user_can_edit_milestone': self.user_can_edit_milestone,3632 self.user_can_edit_importance and not cx.bugwatch)
3639 'user_can_edit_status': not self.context.bugwatch,3633 ))
3640 'user_can_edit_importance': (
3641 self.user_can_edit_importance and
3642 not self.context.bugwatch)})
36433634
36443635
3645class BugsBugTaskSearchListingView(BugTaskSearchListingView):3636class BugsBugTaskSearchListingView(BugTaskSearchListingView):
36463637
=== modified file 'lib/lp/bugs/browser/configure.zcml'
--- lib/lp/bugs/browser/configure.zcml 2011-08-16 14:21:39 +0000
+++ lib/lp/bugs/browser/configure.zcml 2011-08-23 01:53:28 +0000
@@ -564,8 +564,7 @@
564 for="lp.bugs.interfaces.bugtask.IBugTask"564 for="lp.bugs.interfaces.bugtask.IBugTask"
565 class="lp.bugs.browser.bugtask.BugTaskTableRowView"565 class="lp.bugs.browser.bugtask.BugTaskTableRowView"
566 permission="launchpad.View"566 permission="launchpad.View"
567 name="+bugtasks-and-nominations-table-row"567 name="+bugtasks-and-nominations-table-row"/>
568 template="../templates/bugtask-tasks-and-nominations-table-row.pt"/>
569 <browser:page568 <browser:page
570 for="lp.bugs.interfaces.bugtarget.IBugTarget"569 for="lp.bugs.interfaces.bugtarget.IBugTarget"
571 permission="zope.Public"570 permission="zope.Public"
572571
=== modified file 'lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt'
--- lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt 2011-08-10 16:14:13 +0000
+++ lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt 2011-08-23 01:53:28 +0000
@@ -1,44 +1,30 @@
1<tal:bugtask1<tal:bugtask
2 xmlns:tal="http://xml.zope.org/namespaces/tal"2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 define="expandable view/expandable;4 define="data view/data">
5 indent_task view/shouldIndentTask;5 <tr tal:attributes="class data/row_css_class; id data/row_id">
6 is_conjoined_slave view/is_conjoined_slave;
7 tasklink view/taskLink;
8 row_id string:tasksummary${context/id};
9 form_row_id string:task${context/id}">
10 <tr tal:define="editstatus_url string:${context/fmt:url}/+editstatus"
11 tal:attributes="class view/getTaskRowCSSClass; id row_id">
12 <td>6 <td>
13 <metal:expander7 <a tal:condition="data/expandable"
14 metal:define-macro="expander"8 tal:attributes="href data/task_link" class="bugtask-expander">
15 tal:define="expander_link_text expander_link_text|nothing">9 &#8203;
16 <a tal:condition="expandable"10 </a>
17 tal:attributes="href tasklink" class="bugtask-expander">
18 <tal:link_text tal:replace="expander_link_text" />
19 &#8203;
20 </a>
21 <tal:no_expander tal:condition="not: expandable"
22 tal:replace="expander_link_text" />
23 </metal:expander>
24 </td>11 </td>
25 <td style="padding: 0.3em 0em 0.3em 1.5em"12 <td style="padding: 0.3em 0em 0.3em 1.5em"
26 tal:condition="indent_task"13 tal:condition="data/indent_task">
27 tal:define="series_targetname view/getSeriesTargetName">
28 <span class="sprite milestone"></span>14 <span class="sprite milestone"></span>
29 <tal:not-conjoined-task condition="not: is_conjoined_slave">15 <tal:not-conjoined-task condition="not: data/is_conjoined_slave">
30 <a16 <a
31 tal:attributes="href context/target/fmt:url"17 tal:attributes="href data/target_link"
32 tal:content="series_targetname"18 tal:content="view/getSeriesTargetName"
33 />19 />
34 </tal:not-conjoined-task>20 </tal:not-conjoined-task>
35 </td>21 </td>
36 <td tal:condition="not:indent_task">22 <td tal:condition="not:data/indent_task">
37 <span tal:attributes="id string:bugtarget-picker-${row_id}">23 <span tal:attributes="id string:bugtarget-picker-${data/row_id}">
38 <span class="yui3-activator-data-box">24 <span class="yui3-activator-data-box">
39 <span title="This project&rsquo;s license has not been specified.">25 <span title="This project&rsquo;s license has not been specified.">
40 <a tal:attributes="href context/target/fmt:url;26 <a tal:attributes="href data/target_link;
41 title view/target_link_title;27 title data/target_link_title;
42 class context/target/image:sprite_css"28 class context/target/image:sprite_css"
43 tal:content="context/bugtargetdisplayname" />29 tal:content="context/bugtargetdisplayname" />
44 </span>30 </span>
@@ -50,7 +36,7 @@
50 </span>36 </span>
51 </td>37 </td>
5238
53 <tal:conjoined-task condition="is_conjoined_slave">39 <tal:conjoined-task condition="data/is_conjoined_slave">
54 <td colspan="5" style="vertical-align: middle">40 <td colspan="5" style="vertical-align: middle">
55 <span class="discreet lesser" style="font-size: 100%">41 <span class="discreet lesser" style="font-size: 100%">
56 Status tracked in42 Status tracked in
@@ -61,79 +47,85 @@
61 </td>47 </td>
62 </tal:conjoined-task>48 </tal:conjoined-task>
6349
64 <tal:not-conjoined-task condition="not:is_conjoined_slave">50 <tal:not-conjoined-task condition="not:data/is_conjoined_slave">
65 <td style="width: 20%; vertical-align: middle">51 <td style="width: 20%; vertical-align: middle">
66 <div class="status-content"52 <div class="status-content"
67 style="width: 100%; float: left">53 style="width: 100%; float: left"
54 tal:define="status context/status">
68 <a href="+editstatus"55 <a href="+editstatus"
69 tal:attributes="class string:value status${context/status/name};56 tal:attributes="class string:value status${status/name};
70 href editstatus_url"57 href data/edit_link"
71 style="float: left"58 style="float: left"
72 tal:content="context/status/title" />59 tal:content="status/title" />
73 <a href="+editstatus" style="margin-left: 3px"60 <a href="+editstatus" style="margin-left: 3px"
74 tal:attributes="href editstatus_url">61 tal:attributes="href data/edit_link">
75 <img class="editicon" src="/@@/edit" />62 <img class="editicon" src="/@@/edit" />
76 </a>63 </a>
77 </div>64 </div>
78 </td>65 </td>
7966
80 <td tal:condition="view/user_can_edit_importance"67 <td tal:condition="data/user_can_edit_importance"
81 style="width: 20%; vertical-align: middle">68 style="width: 20%; vertical-align: middle">
82 <div class="importance-content"69 <div class="importance-content"
83 style="width: 100%; float: left">70 style="width: 100%; float: left">
84 <a href="+editstatus"71 <a href="+editstatus"
85 tal:attributes="class string:value importance${context/importance/name};72 tal:attributes="class string:value ${data/importance_css_class};
86 href editstatus_url"73 href data/edit_link"
87 style="float: left"74 style="float: left"
88 tal:content="context/importance/title" />75 tal:content="data/importance_title" />
89 <a href="+editstatus" style="margin-left: 3px"76 <a href="+editstatus" style="margin-left: 3px"
90 tal:attributes="href editstatus_url">77 tal:attributes="href data/edit_link">
91 <img class="editicon" src="/@@/edit" />78 <img class="editicon" src="/@@/edit" />
92 </a>79 </a>
93 </div>80 </div>
94 </td>81 </td>
95 <td tal:condition="not: view/user_can_edit_importance"82 <td tal:condition="not: data/user_can_edit_importance"
96 style="width: 15em; vertical-align: middle">83 style="width: 15em; vertical-align: middle">
97 <div class="importance-content"84 <div class="importance-content"
98 style="width: 100%; float: left">85 style="width: 100%; float: left">
99 <span86 <span
100 tal:attributes="class string:value importance${context/importance/name}"87 tal:attributes="class string:value ${data/importance_css_class}"
101 style="float: left"88 style="float: left"
102 tal:content="context/importance/title" />89 tal:content="data/importance_title" />
103 </div>90 </div>
104 </td>91 </td>
10592
106 <td style="width:20%; margin: 0; padding: 0;93 <td style="width:20%; margin: 0; padding: 0;
107 vertical-align: middle; padding-left: 0.5em">94 vertical-align: middle; padding-left: 0.5em"
108 <tal:has_watch condition="context/bugwatch">95 tal:define="bugwatch context/bugwatch;">
109 <div style="text-decoration: none; padding: 0.25em">96 <tal:has_watch condition="bugwatch">
110 <tal:bugtracker-active97 <div style="text-decoration: none; padding: 0.25em"
111 condition="context/bugwatch/bugtracker/active">98 tal:define="active_bugtracker bugwatch/bugtracker/active;">
112 <span tal:condition="not:context/bugwatch/last_error_type"99 <tal:bugtracker-active condition="active_bugtracker">
113 class="sprite bug-remote"></span>100 <tal:block define="last_error_type bugwatch/last_error_type;">
114 <a tal:condition="context/bugwatch/last_error_type"101 <span tal:condition="not:last_error_type"
115 tal:attributes="href view/bug_watch_error_message/help_url"102 class="sprite bug-remote"></span>
116 target="help"103 <a tal:condition="last_error_type"
117 class="icon help">104 tal:attributes="href view/bug_watch_error_message/help_url"
118 <span class="sprite warning-icon"105 target="help"
119 tal:attributes="title view/bug_watch_error_message/message"106 class="icon help">
120 id="bugwatch-error-sprite"></span>107 <span class="sprite warning-icon"
121 </a>108 tal:attributes=
109 "title view/bug_watch_error_message/message"
110 id="bugwatch-error-sprite"></span>
111 </a>
112 </tal:block>
122 </tal:bugtracker-active>113 </tal:bugtracker-active>
123 <span tal:condition="not:context/bugwatch/bugtracker/active"114 <span tal:condition="not:active_bugtracker"
124 class="sprite warning-icon"></span>115 class="sprite warning-icon"></span>
125 <a tal:replace="structure context/bugwatch/fmt:external-link" />116 <a tal:replace="structure bugwatch/fmt:external-link" />
126 </div>117 </div>
127 </tal:has_watch>118 </tal:has_watch>
128119
129 <tal:has_no_watch condition="not: context/bugwatch">120 <tal:has_no_watch condition="not: bugwatch">
130 <span tal:attributes="id string:assignee-picker-${row_id}">121 <span tal:attributes="id string:assignee-picker-${data/row_id}"
122 tal:define="assignee context/assignee">
131 <span class="yui3-activator-data-box">123 <span class="yui3-activator-data-box">
132 <a tal:condition="context/assignee"124 <a tal:condition="assignee"
133 tal:attributes="href context/assignee/fmt:url;125 tal:attributes="href assignee/fmt:url;
134 class context/assignee/image:sprite_css"126 class assignee/image:sprite_css"
135 tal:content="context/assignee/fmt:displayname" />127 tal:content="assignee/fmt:displayname" />
136 <tal:unassigned condition="not: context/assignee">128 <tal:unassigned condition="not: assignee">
137 Unassigned129 Unassigned
138 </tal:unassigned>130 </tal:unassigned>
139 </span>131 </span>
@@ -147,11 +139,11 @@
147139
148 <td style="width: 20%; vertical-align: middle">140 <td style="width: 20%; vertical-align: middle">
149 <div class="milestone-content"141 <div class="milestone-content"
150 tal:condition="view/target_has_milestones"142 tal:condition="data/target_has_milestones"
151 style="width: 100%; float: left">143 style="width: 100%; float: left">
152 <a tal:condition="view/user_can_edit_milestone"144 <a tal:condition="view/user_can_edit_milestone"
153 class="nulltext addicon js-action sprite add"145 class="nulltext addicon js-action sprite add"
154 tal:attributes="href editstatus_url;146 tal:attributes="href data/edit_link;
155 style view/style_for_add_milestone">147 style view/style_for_add_milestone">
156 Target to milestone148 Target to milestone
157 </a>149 </a>
@@ -159,7 +151,7 @@
159 tal:attributes="href context/milestone/fmt:url | nothing"151 tal:attributes="href context/milestone/fmt:url | nothing"
160 tal:content="context/milestone/title | nothing" />152 tal:content="context/milestone/title | nothing" />
161 <a tal:condition="view/user_can_edit_milestone"153 <a tal:condition="view/user_can_edit_milestone"
162 tal:attributes="href editstatus_url"154 tal:attributes="href data/edit_link"
163 ><img class="editicon" src="/@@/edit"155 ><img class="editicon" src="/@@/edit"
164 tal:attributes="style view/style_for_edit_milestone"156 tal:attributes="style view/style_for_edit_milestone"
165 /></a>157 /></a>
@@ -178,8 +170,8 @@
178 Y.lp.bugs.bugtask_index.setup_bugtask_row(${view/js_config});170 Y.lp.bugs.bugtask_index.setup_bugtask_row(${view/js_config});
179171
180 // Set-up the expander on the bug task summary row.172 // Set-up the expander on the bug task summary row.
181 var icon_node = Y.one('tr#${row_id} a.bugtask-expander');173 var icon_node = Y.one('tr#${data/row_id} a.bugtask-expander');
182 var row_node = Y.one('tr#${form_row_id}');174 var row_node = Y.one('tr#${data/form_row_id}');
183 if (Y.Lang.isValue(row_node)) {175 if (Y.Lang.isValue(row_node)) {
184 // When no row is present, this is bug task on a project with176 // When no row is present, this is bug task on a project with
185 // multiple per-series tasks, so we do not need to set177 // multiple per-series tasks, so we do not need to set
@@ -195,8 +187,8 @@
195187
196 <tal:form condition="view/displayEditForm">188 <tal:form condition="view/displayEditForm">
197 <tr189 <tr
198 tal:attributes="id form_row_id"190 tal:attributes="id data/form_row_id"
199 tal:condition="expandable"191 tal:condition="data/expandable"
200 class="bugtask-collapsible-content unseen"192 class="bugtask-collapsible-content unseen"
201 >193 >
202 <td colspan="7" tal:content="structure view/edit_view" />194 <td colspan="7" tal:content="structure view/edit_view" />
203195
=== modified file 'setup.py'
--- setup.py 2011-08-21 22:06:09 +0000
+++ setup.py 2011-08-23 01:53:28 +0000
@@ -29,8 +29,7 @@
29 'ampoule',29 'ampoule',
30 'BeautifulSoup',30 'BeautifulSoup',
31 'bzr',31 'bzr',
32 'chameleon.core',32 'Chameleon',
33 'chameleon.zpt',
34 'cssutils',33 'cssutils',
35 # Required for pydkim34 # Required for pydkim
36 'dnspython',35 'dnspython',
3736
=== modified file 'versions.cfg'
--- versions.cfg 2011-08-22 07:09:21 +0000
+++ versions.cfg 2011-08-23 01:53:28 +0000
@@ -8,8 +8,7 @@
8amqplib = 0.6.18amqplib = 0.6.1
9BeautifulSoup = 3.1.0.19BeautifulSoup = 3.1.0.1
10bzr = 2.3.310bzr = 2.3.3
11chameleon.core = 1.0b3511Chameleon = 2.4.0
12chameleon.zpt = 1.0b17
13ClientForm = 0.2.1012ClientForm = 0.2.10
14cssutils = 0.9.613cssutils = 0.9.6
15docutils = 0.514docutils = 0.5
@@ -52,6 +51,7 @@
52oops = 0.0.651oops = 0.0.6
53oops-datedir-repo = 0.0.352oops-datedir-repo = 0.0.3
54oops-wsgi = 0.0.253oops-wsgi = 0.0.2
54ordereddict = 1.1
55paramiko = 1.7.455paramiko = 1.7.4
56Paste = 1.7.256Paste = 1.7.2
57PasteDeploy = 1.3.357PasteDeploy = 1.3.3
@@ -91,6 +91,7 @@
91transaction = 1.0.091transaction = 1.0.0
92txamqp = 0.492txamqp = 0.4
93Twisted = 11.0.093Twisted = 11.0.0
94unittest2 = 0.5.1
94uuid = 1.3095uuid = 1.30
95van.testing = 2.0.196van.testing = 2.0.1
96wadllib = 1.2.097wadllib = 1.2.0
@@ -116,7 +117,7 @@
116z3c.menu = 0.2.0117z3c.menu = 0.2.0
117z3c.optionstorage = 1.0.4118z3c.optionstorage = 1.0.4
118z3c.pagelet = 1.0.2119z3c.pagelet = 1.0.2
119z3c.pt = 1.0b16120z3c.pt = 2.1.3
120z3c.ptcompat = 0.5.3121z3c.ptcompat = 0.5.3
121z3c.recipe.filetemplate = 2.1.0122z3c.recipe.filetemplate = 2.1.0
122z3c.recipe.i18n = 0.5.3123z3c.recipe.i18n = 0.5.3