Merge lp:~adeuring/launchpad/bug-513382 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Deryck Hodge
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~adeuring/launchpad/bug-513382
Merge into: lp:launchpad
Diff against target: 157 lines (+27/-35)
4 files modified
lib/lp/bugs/browser/bug.py (+6/-8)
lib/lp/bugs/browser/tests/bug-heat-view.txt (+12/-14)
lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt (+2/-4)
lib/lp/bugs/tests/bug.py (+7/-9)
To merge this branch: bzr merge lp:~adeuring/launchpad/bug-513382
Reviewer Review Type Date Requested Status
Michael Nelson (community) ui Approve
Deryck Hodge (community) code Approve
Brad Crittenden (community) code Approve
Abel Deuring (community) Needs Resubmitting
Review via email: mp+19970@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

This is a fix for bug 513382: grey (gray) heat icons are too bold.

I changed the file lib/canonical/launchpad/images/flame-bw-icon.png so that it contains an icon as provided in an atachment for bug 513382.

No tests.

No lint.

To see the changes, compare http://people.canonical.com/~adeuring/bug-heat-icons.png with the icons show for example on https://bugs.edge.launchpad.net/malone/+bugs

Revision history for this message
Brad Crittenden (bac) :
review: Approve (code)
Revision history for this message
Michael Nelson (michael.nelson) wrote :

Looks obviously grayed out now, thanks Abel.

review: Approve (ui)
Revision history for this message
Deryck Hodge (deryck) wrote :

Thanks for taking on this work, Abel, but this branch needs a bit more work to be ready.

The bug, which is actually bug 513380, has images linked. The color icon has also been updated there as well. These images were prepared by Martin A. to enable us to use a single image for each degree of heat. So the images attached to the bug should be used.

However, we need to add these to the sprites file so that they can be used in that way, rather than hard coding image tags. So some work will need to change the view function that generates the images or else changes to enable a formatter for heat icons.

Cheers,
deryck

review: Needs Fixing (code ui)
Revision history for this message
Michael Nelson (michael.nelson) wrote :

15:37 < noodles775> adeuring: I think you've got the wrong bug linked on your MP ;) ui=me, thanks!
15:42 < adeuring> noodles775: thanks! and right, the bug is 513380...

Sorry, I should have checked the correct bug before ui-approving it.

review: Needs Fixing (ui)
Revision history for this message
Abel Deuring (adeuring) wrote :

OK, the five different images showing various combinations of red and grey heat icon are now in use.

I did not add the images to the huge sprite image because the images themselves carry information that is not represented anywhere else. This information would be lost if the heat icons are only used as background images.

I've added alt and title attributes, fixed one unrelated lint complaint (line too long) and removed a trailing whitespace.

A test of the bug heat view class needed some changes, as well as a page test helper function.

tests:

./bin/test -t bug-heat-view.txt
./bin/test -t xx-listing-basics.txt

no lint

review: Needs Resubmitting
Revision history for this message
Brad Crittenden (bac) wrote :

I don't understand the second paragraph above where you explain why the images can't be in the sprite. Can you elaborate?

If indeed the images must be discrete then the code looks fine.

review: Needs Information (code)
Revision history for this message
Abel Deuring (adeuring) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi Brad,

On 23.02.2010 21:12, Brad Crittenden wrote:
> Review: Needs Information code
> I don't understand the second paragraph above where you explain why the images can't be in the sprite. Can you elaborate?
>
> If indeed the images must be discrete then the code looks fine.

The images carry unique information, the heat level: There is no other
content on the page that says "heat level 3". So, in order to adhere to
accessibility rules, we should provide an altternative text, like the
alt attribute of an <img> tag. Sprites on the other hand can be used
only for background images, as far as I understand it, and since
background images are purely "decorative", there is no way to associate
textual information with them.

Also, we want a title attribute where we show some more or less
interesting additional information, see bug 513219. IOW, a second reason
not to use a background image.

Abel
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFLhFRCekBPhm8NrtARAjVrAJ0ZOQDHD/RaRHyys+43kDe0053L9QCePMAf
KPT3EAvUBCkw0Zal4dePxDo=
=0xDU
-----END PGP SIGNATURE-----

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

> OK, the five different images showing various combinations of red and grey
> heat icon are now in use.
>
> I did not add the images to the huge sprite image because the images
> themselves carry information that is not represented anywhere else. This
> information would be lost if the heat icons are only used as background
> images.

I assume that the "information that is not represented anywhere else" is the TITLE and ALT attributes for <img>. The element with the sprite class could also have the TITLE set. Is the ALT really that important, or is there something else that I'm missing?

Revision history for this message
Michael Nelson (michael.nelson) wrote :

Hi Abel,

Sorry if I'm stepping into the code-review, but here are a few thoughts for implementation.

Just in case you haven't seen it, you can find out more details about sprites at: https://dev.launchpad.net/UI/CssSprites . As Edwin mentioned, you should be able to use something like:

<span class="sprite person" title="whatever"></span> (generating it from TAL of course),

but given that this will be used in multiple places, I think it would be worthwhile creating your own formatter, similar to the icon formatter that you've already got for bugs (in c/l/webapp/tales.py), so you could do something like:

<span tal:replace="structure bug/fmt:heat"></span>

That would allow you to generate the title too. We could even give the span textual content (ie. 'Heat level 4' or something more appropriate) for accessibility and hide it via the CSS, if you're keen.

What do you think?

@Edwin, are there instructions somewhere for using the sprite_image make target? (other than reading through bin/sprite-util ;) ). Can you please update the above sprite wiki page with a link to some instructions? Thanks!

Revision history for this message
Abel Deuring (adeuring) wrote :

Hi Edwin, hi Michael,

My reason to use the plain <img> tag is accessibility. See for example http://www.w3.org/TR/WCAG20/#text-equiv : "Non-text Content: All non-text content that is presented to the user has a text alternative that serves the equivalent purpose, except for the situations listed below. (Level A)"

<span class="sprite person" title="whatever"></span> is perhaps an alternative, but the main idea of the <img> tag's alt attribute is to provide a precise description of what is displayed ("3 out of 4 heat flames" in this case, which is quite precise).

The title attribute, on the other hand, should "offer advisory information about the element for which it is set" (http://www.w3.org/TR/1999/REC-html401-19991224/struct/global.html#adef-title), which is slightly different.

Regarding accessibility, textual content that is hidden via CSS does not work at all as a an alternative for <img alt="...">: Screen readers and similar technology are supposed to present the _visible_ content in some other form, but not the invisible content.

So I think we should keep the <img> tag in this case.

Revision history for this message
Michael Nelson (michael.nelson) wrote :

Hi Abel,

OK, I'll butt-back-out of the implementation discussion ;), but just to address the points you raised:

> Hi Edwin, hi Michael,
>
> My reason to use the plain <img> tag is accessibility. See for example
> http://www.w3.org/TR/WCAG20/#text-equiv : "Non-text Content: All non-text
> content that is presented to the user has a text alternative that serves the
> equivalent purpose, except for the situations listed below. (Level A)"

Yes, but I'm thinking of this around the other way: the text content (within the span produced by the formatter) would be "3 out of 4 heat flames", we'd just hide it in an accessible way (see below).

>
> <span class="sprite person" title="whatever"></span> is perhaps an
> alternative, but the main idea of the <img> tag's alt attribute is to provide
> a precise description of what is displayed ("3 out of 4 heat flames" in this
> case, which is quite precise).
>
> The title attribute, on the other hand, should "offer advisory information
> about the element for which it is set" (http://www.w3.org/TR/1999/REC-
> html401-19991224/struct/global.html#adef-title), which is slightly different.

Yes, the title can be read by screen-readers (like JAWS), but afaics, it's not a default option. So you're right - it's not an alternative for an alt attribute on an image.

>
> Regarding accessibility, textual content that is hidden via CSS does not work
> at all as a an alternative for <img alt="...">: Screen readers and similar
> technology are supposed to present the _visible_ content in some other form,
> but not the invisible content.

That's not really true. I mean, it is if you use visibility:hidden in your css, but I've always positioned the hidden text absolutely off the screen. Like this:

<span class="sprite heat heat4" title="3 out of 4 heat flames"><em>3 out of 4 heat flames</em></span>

span.heat em {
  position: absolute;
  left: -1000px;
  }

or similar. More info on accessible invisible content at: http://www.webaim.org/techniques/css/invisiblecontent/

>
> So I think we should keep the <img> tag in this case.

I'll leave it up to you guys to decide on the code/implementation. Just let me know when it's settled and i'll run the branch again locally for the UI review.

Revision history for this message
Abel Deuring (adeuring) wrote :

Hi Michael,

you are right, I meant that a "visibility:hidden" text is not a good alternative for the alt attribute of <img>. Positioning the text off screen avoids this, but I'd argue that we exploit a bug of screen readers in this case ;)

Revision history for this message
Abel Deuring (adeuring) wrote :

While implementing a variant of the heat icon display that uses

<span class="sprite heat heat0" title="Heat: 0"><em>0 out of 4 heat flames</em></span>

and hides the text via the CSS rule "position: absolute; left: -1000px;" I noticed that Firefox allows to search for this hidden text.

That makes searches for the words "heat" or "flame" somehwat confusing, if not useless on longer bug listings...

Revision history for this message
Abel Deuring (adeuring) wrote :

to reproduce: use revision 10375 of this branch, open https://bugs.launchpad.dev/ubuntu/+bugs in Firefox, start a full text search in firefox for the word "heat", and count how often you must hit F3 until Firefox marks the column header "Heat" as the "current search position"

Revision history for this message
Michael Nelson (michael.nelson) wrote :

Hi Abel,

Yep, but I'm not sure how important that is? I'll let you guys decide that. Also, regarding searching/keyboard navigation, I would recommend going with the full solution at:

http://www.webaim.org/techniques/css/invisiblecontent/

rather than my simplified example. You'll see there that they identify:

"A previous version of this article recommended using left:0px; top:-500px. While this worked by positioning the content above the top of the page, we've since discovered that if the hidden element contained a link or form element, that upon receiving keyboard focus, the browser would attempt to scroll to the element - thus scrolling the browser to the top of the page. This could result in confusion for sighted keyboard users. By positioning directly to the left, the browser will not scroll to the top of the page."

And finally, I think it's worth bringing this up at the next UI meeting to discuss (on Monday)... others might disagree with me :)

Revision history for this message
Abel Deuring (adeuring) wrote :

Hi Michael,

Perhaps I am a bit thick, but I don't see how the problem mentioned there and implementing all CSS rules affects the confusing search behaviour.

And agreed, not being able to efficiently search for two words is not a big problem -- but it is a problem. (And a funny side effect is that browser searches for a word in the title of the UI bug 513380 I'm fixing will become useless by the fix of the bug -- IOW, we are knowingly introducing another UI bug...)

I short: I understand that using CSS sprites is quite successful to reduce page load times, probably also to reduce the server load and whatever else -- but using sprites by all means for all images where is it technically possible is wrong. If we have an image that is the "main or only carrier of information", we should keep the <img> tag, together with alt and title attributes. Trying anything else is likely to have some kind of weird or annoying side effect.

But anyway, I'll keep the sprite implementation for the heat icons for now...

Revision history for this message
Brad Crittenden (bac) wrote :

Though I haven't commented on this review lately I've been keeping up with the discussion. Based on Abel's latest discovery that the work-around will cause the psuedo-ALT text to be included in our site-wide search results I'd argue for reverting the CSS changes and going back to the original <IMG> approach so this change can get landed. It seems we need to explore a general solution to this problem, starting with the UI call on Monday, and once that is done we can retrofit this change.

I vote 'Approve' on the original version.

Abel please save the work you've done as it will be a good starting point for the broader discussion.

review: Approve (code)
Revision history for this message
Deryck Hodge (deryck) wrote :

I agree with bac completely. I think we should just use img tags and let the interested parties debate a fix for this on the UI call.

I'm marking Approve for the img implementation using beuno's images as well.

Michael, or you ok to review the end-result of the UI -- i.e. that the images look right on the site -- and defer implementation details to code review and a UI call discussion?

review: Approve (code)
Revision history for this message
Michael Nelson (michael.nelson) wrote :

> Michael, or you ok to review the end-result of the UI -- i.e. that the images
> look right on the site -- and defer implementation details to code review and
> a UI call discussion?

Yep, the UI looks great (as it did at the start :) ), and needs to land!

I *do* think you should check with Curtis, as it seems that we already have a standard way to do this (with the discussed pros/cons already evaluated), and it's pretty much what Abel has implemented with r10375 (it only needs to be updated to use the span.invisible that's already there for this exact reason). But again, that's up to you guys.

review: Approve (ui)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'lib/canonical/launchpad/images/bug-heat-0.png'
0Binary files lib/canonical/launchpad/images/bug-heat-0.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-0.png 2010-02-25 19:52:21 +0000 differ0Binary files lib/canonical/launchpad/images/bug-heat-0.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-0.png 2010-02-25 19:52:21 +0000 differ
=== added file 'lib/canonical/launchpad/images/bug-heat-1.png'
1Binary files lib/canonical/launchpad/images/bug-heat-1.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-1.png 2010-02-25 19:52:21 +0000 differ1Binary files lib/canonical/launchpad/images/bug-heat-1.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-1.png 2010-02-25 19:52:21 +0000 differ
=== added file 'lib/canonical/launchpad/images/bug-heat-2.png'
2Binary files lib/canonical/launchpad/images/bug-heat-2.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-2.png 2010-02-25 19:52:21 +0000 differ2Binary files lib/canonical/launchpad/images/bug-heat-2.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-2.png 2010-02-25 19:52:21 +0000 differ
=== added file 'lib/canonical/launchpad/images/bug-heat-3.png'
3Binary files lib/canonical/launchpad/images/bug-heat-3.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-3.png 2010-02-25 19:52:21 +0000 differ3Binary files lib/canonical/launchpad/images/bug-heat-3.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-3.png 2010-02-25 19:52:21 +0000 differ
=== added file 'lib/canonical/launchpad/images/bug-heat-4.png'
4Binary files lib/canonical/launchpad/images/bug-heat-4.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-4.png 2010-02-25 19:52:21 +0000 differ4Binary files lib/canonical/launchpad/images/bug-heat-4.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/bug-heat-4.png 2010-02-25 19:52:21 +0000 differ
=== modified file 'lib/canonical/launchpad/images/flame-bw-icon.png'
5Binary files lib/canonical/launchpad/images/flame-bw-icon.png 2010-01-18 21:44:59 +0000 and lib/canonical/launchpad/images/flame-bw-icon.png 2010-02-25 19:52:21 +0000 differ5Binary files lib/canonical/launchpad/images/flame-bw-icon.png 2010-01-18 21:44:59 +0000 and lib/canonical/launchpad/images/flame-bw-icon.png 2010-02-25 19:52:21 +0000 differ
=== modified file 'lib/lp/bugs/browser/bug.py'
--- lib/lp/bugs/browser/bug.py 2010-02-02 17:26:53 +0000
+++ lib/lp/bugs/browser/bug.py 2010-02-25 19:52:21 +0000
@@ -229,7 +229,8 @@
229 text = 'Subscribe someone else'229 text = 'Subscribe someone else'
230 return Link(230 return Link(
231 '+addsubscriber', text, icon='add', summary=(231 '+addsubscriber', text, icon='add', summary=(
232 'Launchpad will email that person whenever this bugs changes'))232 'Launchpad will email that person whenever this bugs '
233 'changes'))
233234
234 def nominate(self):235 def nominate(self):
235 """Return the 'Target/Nominate for release' Link."""236 """Return the 'Target/Nominate for release' Link."""
@@ -976,11 +977,8 @@
976 def __call__(self):977 def __call__(self):
977 """Render the bug heat representation."""978 """Render the bug heat representation."""
978 heat_ratio = floor((self.context.heat / MAX_HEAT) * 4)979 heat_ratio = floor((self.context.heat / MAX_HEAT) * 4)
979 html = '<span>'980 html = (
980 for flame in range(1, 5):981 '<img src="/@@/bug-heat-%(ratio)i.png" '
981 if flame <= heat_ratio:982 'alt="%(ratio)i out of 4 heat flames" title="Heat: %(heat)i" />'
982 html += '<img src="/@@/flame-icon" />'983 % {'ratio': heat_ratio, 'heat': self.context.heat})
983 else:
984 html += '<img src="/@@/flame-bw-icon" />'
985 html += '</span>'
986 return html984 return html
987985
=== modified file 'lib/lp/bugs/browser/tests/bug-heat-view.txt'
--- lib/lp/bugs/browser/tests/bug-heat-view.txt 2010-01-18 22:11:00 +0000
+++ lib/lp/bugs/browser/tests/bug-heat-view.txt 2010-02-25 19:52:21 +0000
@@ -6,13 +6,14 @@
66
7 >>> from lp.bugs.browser.bug import MAX_HEAT7 >>> from lp.bugs.browser.bug import MAX_HEAT
8 >>> from canonical.launchpad.ftests import login, logout8 >>> from canonical.launchpad.ftests import login, logout
9 >>> from canonical.launchpad.webapp import canonical_url
10 >>> from zope.security.proxy import removeSecurityProxy9 >>> from zope.security.proxy import removeSecurityProxy
11 >>> from BeautifulSoup import BeautifulSoup10 >>> from BeautifulSoup import BeautifulSoup
12 >>> def print_flames(html):11 >>> def print_flames(html):
13 ... soup = BeautifulSoup(html)12 ... soup = BeautifulSoup(html)
14 ... for img in soup.span.contents:13 ... for img in soup.contents:
15 ... print img['src']14 ... print img['src']
15 ... print img['alt']
16 ... print img['title']
16 >>> login('foo.bar@canonical.com')17 >>> login('foo.bar@canonical.com')
17 >>> bug = factory.makeBug()18 >>> bug = factory.makeBug()
1819
@@ -23,29 +24,26 @@
23 >>> removeSecurityProxy(bug).heat = MAX_HEAT / 224 >>> removeSecurityProxy(bug).heat = MAX_HEAT / 2
24 >>> bug_heat_view = create_initialized_view(bug, name='+bug-heat')25 >>> bug_heat_view = create_initialized_view(bug, name='+bug-heat')
25 >>> print_flames(bug_heat_view())26 >>> print_flames(bug_heat_view())
26 /@@/flame-icon27 /@@/bug-heat-2.png
27 /@@/flame-icon28 2 out of 4 heat flames
28 /@@/flame-bw-icon29 Heat: 2500
29 /@@/flame-bw-icon
3030
31A bug with a maximum heat will display all four flames coloured.31A bug with a maximum heat will display all four flames coloured.
3232
33 >>> removeSecurityProxy(bug).heat = MAX_HEAT33 >>> removeSecurityProxy(bug).heat = MAX_HEAT
34 >>> bug_heat_view = create_initialized_view(bug, name='+bug-heat')34 >>> bug_heat_view = create_initialized_view(bug, name='+bug-heat')
35 >>> print_flames(bug_heat_view())35 >>> print_flames(bug_heat_view())
36 /@@/flame-icon36 /@@/bug-heat-4.png
37 /@@/flame-icon37 4 out of 4 heat flames
38 /@@/flame-icon38 Heat: 5000
39 /@@/flame-icon
4039
41A heat of less than a quarter of the maximum will display no coloured flames.40A heat of less than a quarter of the maximum will display no coloured flames.
4241
43 >>> removeSecurityProxy(bug).heat = 0.1 * MAX_HEAT42 >>> removeSecurityProxy(bug).heat = 0.1 * MAX_HEAT
44 >>> bug_heat_view = create_initialized_view(bug, name='+bug-heat')43 >>> bug_heat_view = create_initialized_view(bug, name='+bug-heat')
45 >>> print_flames(bug_heat_view())44 >>> print_flames(bug_heat_view())
46 /@@/flame-bw-icon45 /@@/bug-heat-0.png
47 /@@/flame-bw-icon46 0 out of 4 heat flames
48 /@@/flame-bw-icon47 Heat: 500
49 /@@/flame-bw-icon
5048
51 >>> logout()49 >>> logout()
5250
=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt 2010-01-22 15:44:43 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt 2010-02-25 19:52:21 +0000
@@ -3,7 +3,5 @@
3The bug heat appears on the bug index page as four flames.3The bug heat appears on the bug index page as four flames.
44
5 >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')5 >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
6 >>> print find_tag_by_id(anon_browser.contents, 'registration').fetch('span')[-1]6 >>> print find_tag_by_id(anon_browser.contents, 'registration').fetch('img')[0]
7 <span><img src="/@@/flame-bw-icon" /><img src="/@@/flame-bw-icon" /><img src="/@@/flame-bw-icon" /><img src="/@@/flame-bw-icon" /></span>7 <img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 0" />
8
9
108
=== modified file 'lib/lp/bugs/tests/bug.py'
--- lib/lp/bugs/tests/bug.py 2010-01-19 20:45:38 +0000
+++ lib/lp/bugs/tests/bug.py 2010-02-25 19:52:21 +0000
@@ -3,6 +3,7 @@
33
4"""Helper functions for bug-related doctests and pagetests."""4"""Helper functions for bug-related doctests and pagetests."""
55
6import re
6import textwrap7import textwrap
78
8from datetime import datetime, timedelta9from datetime import datetime, timedelta
@@ -124,15 +125,12 @@
124 if tr.td is not None:125 if tr.td is not None:
125 row_text = extract_text(tr)126 row_text = extract_text(tr)
126 if show_heat:127 if show_heat:
127 heat_text = ''
128 for img in tr.findAll('img'):128 for img in tr.findAll('img'):
129 if img['src'] == '/@@/flame-icon':129 mo = re.search('^/@@/bug-heat-([0-4]).png$', img['src'])
130 heat_text += '*'130 if mo:
131 elif img['src'] == '/@@/flame-bw-icon':131 flames = int(mo.group(1))
132 heat_text += '-'132 heat_text = flames * '*' + (4 - flames) * '-'
133 else:133 row_text += '\n' + heat_text
134 pass
135 row_text += '\n' + heat_text
136 rows.append(row_text)134 rows.append(row_text)
137 return rows135 return rows
138136
@@ -209,7 +207,7 @@
209 bugtask.transitionToAssignee(assignee)207 bugtask.transitionToAssignee(assignee)
210 bugtask.milestone = milestone208 bugtask.milestone = milestone
211 if external_bugtracker is not None:209 if external_bugtracker is not None:
212 getUtility(IBugWatchSet).createBugWatch(bug=bug, owner=sample_person, 210 getUtility(IBugWatchSet).createBugWatch(bug=bug, owner=sample_person,
213 bugtracker=external_bugtracker, remotebug='1234')211 bugtracker=external_bugtracker, remotebug='1234')
214 date = datetime.now(UTC) - timedelta(days=days_old)212 date = datetime.now(UTC) - timedelta(days=days_old)
215 removeSecurityProxy(bug).date_last_updated = date213 removeSecurityProxy(bug).date_last_updated = date