launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #13266
[Merge] lp:~sinzui/launchpad/moderated-messages-0 into lp:launchpad
Curtis Hovey has proposed merging lp:~sinzui/launchpad/moderated-messages-0 into lp:launchpad.
Commit message:
Fix message moderation timeouts by avoiding the librarian.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #736012 in Launchpad itself: "Person:+mailinglist-moderate timeout"
https://bugs.launchpad.net/launchpad/+bug/736012
For more details, see:
https://code.launchpad.net/~sinzui/launchpad/moderated-messages-0/+merge/129013
The mailing list moderation view can timeout showing a list of messages
even though it shows fewer messages than most listings.
--------------------------------------------------------------------
RULES
Pre-implementation: jcsackett
* Lp is calling the librarian for each message to learn which
email address was used to send the message.
* This is not relevant because message approval is based on user,
not email addresses.
* The view is making a link to the lp user, but does extra work
to craft the link to look like an email address.
* HeldMessageDetails.email_message implicitly calls the librarian.
* Only one callsite knows about this cachedproperty,
HeldMessageDetails.sender
* Only one callsite knows about the HeldMessageDetails.sender
property.
* Change the view to just use standard links for the the message
author.
* Remove .sender and .email_message from the HeldMessageDetails
class
ADDENDUM
* The query that gets the MessageApproval also precaches the Message,
but not the Person that sent the message. The Person is always
accessed when working with MessageApproval.
QA
* Ask a webops to make you an admin of
https://launchpad.net/~ubuntu-cyclists
* Visit https://launchpad.net/~ubuntu-cyclists/+moderation
* Verify the page loads
* Discard the messages prior to 2012 (the spam)
* verify the page loads.
LINT
lib/lp/registry/browser/mailinglists.py
lib/lp/registry/browser/tests/mailinglist-message-views.txt
lib/lp/registry/interfaces/mailinglist.py
lib/lp/registry/model/mailinglist.py
lib/lp/registry/tests/test_mailinglist.py
LoC
This branch removes code that is unneeded by this refactoring.
TEST
./bin/test -vvc -t HeldMessage -t getReviewableMessages \
lp.registry.tests.test_mailinglist
./bin/test -vvc -t HeldMessage lp.registry.browser.tests.test_mailinglist
IMPLEMENTATION
I updated the view to make a standard Lp link to the sender, which is faster
to format and provides consistency.
lib/lp/registry/browser/mailinglists.py
lib/lp/registry/browser/tests/mailinglist-message-views.txt
I updated MailingList.getReviewableMessages() to also cache the user in
MessageApproval.posted_by, then change HeldMessageDetails.posted_by to
use that same user. So the moderated view never asks the librarian
for the sender's email address, and the sender is cached along with the
Lp copy of the message. I removed .sender and .email_message because the
attributes are no longer unused.
lib/lp/registry/interfaces/mailinglist.py
lib/lp/registry/model/mailinglist.py
lib/lp/registry/tests/test_mailinglist.py
--
https://code.launchpad.net/~sinzui/launchpad/moderated-messages-0/+merge/129013
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~sinzui/launchpad/moderated-messages-0 into lp:launchpad.
=== modified file 'lib/lp/registry/browser/mailinglists.py'
--- lib/lp/registry/browser/mailinglists.py 2012-02-21 22:46:28 +0000
+++ lib/lp/registry/browser/mailinglists.py 2012-10-10 18:00:38 +0000
@@ -16,13 +16,13 @@
from zope.component import getUtility
+from lp.app.browser.tales import PersonFormatterAPI
from lp.registry.interfaces.mailinglist import (
IHeldMessageDetails,
IMailingListSet,
)
from lp.registry.interfaces.person import ITeam
from lp.services.webapp import (
- canonical_url,
LaunchpadView,
)
@@ -44,11 +44,7 @@
self.subject = self.details.subject
self.date = self.details.date
self.widget_name = 'field.' + quote(self.message_id)
- # The author field is very close to what the details has, except that
- # the view wants to include a link to the person's overview page.
- self.author = '<a href="%s">%s</a>' % (
- canonical_url(self.details.author),
- escape(self.details.sender))
+ self.author = PersonFormatterAPI(self.details.author).link(None)
def initialize(self):
"""See `LaunchpadView`."""
=== modified file 'lib/lp/registry/browser/tests/mailinglist-message-views.txt'
--- lib/lp/registry/browser/tests/mailinglist-message-views.txt 2012-04-10 14:01:17 +0000
+++ lib/lp/registry/browser/tests/mailinglist-message-views.txt 2012-10-10 18:00:38 +0000
@@ -38,7 +38,7 @@
>>> print view.date
2000-08-01 01:09:00+00:00
>>> print view.author
- <a href="http://launchpad.dev/~carlos">Carlos <carlos@xxxxxxxxxxxxx></a>
+ <a href="/~carlos" class="sprite person">Carlos Perell... Marín</a>
>>> print view.body_summary
First paragraph.
>>> print view.body_details
=== modified file 'lib/lp/registry/interfaces/mailinglist.py'
--- lib/lp/registry/interfaces/mailinglist.py 2012-09-14 22:39:54 +0000
+++ lib/lp/registry/interfaces/mailinglist.py 2012-10-10 18:00:38 +0000
@@ -834,13 +834,6 @@
description=_('The RFC 2822 Subject header.'),
required=True, readonly=True)
- sender = Text(
- title=_('Message author'),
- description=_('The message originator (i.e. author), formatted as '
- 'per RFC 2822 and derived from the RFC 2822 originator '
- 'fields From and Reply-To. This is a unicode string.'),
- required=True, readonly=True)
-
author = Object(
schema=IPerson,
title=_('Message author'),
@@ -857,12 +850,6 @@
description=_('The message body as plain text.'),
required=True, readonly=True)
- email_message = Text(
- title=_('The email message object'),
- description=_('The email.message.Message object created from the '
- "original message's raw text."),
- required=True, readonly=True)
-
class BaseSubscriptionErrors(Exception):
"""Base class for subscription exceptions."""
=== modified file 'lib/lp/registry/model/mailinglist.py'
--- lib/lp/registry/model/mailinglist.py 2012-09-15 03:39:21 +0000
+++ lib/lp/registry/model/mailinglist.py 2012-10-10 18:00:38 +0000
@@ -16,12 +16,6 @@
import collections
-from email import message_from_string
-from email.Header import (
- decode_header,
- make_header,
- )
-from itertools import repeat
import operator
from socket import getfqdn
from string import Template
@@ -456,11 +450,12 @@
MessageApproval.mailing_listID == self.id,
MessageApproval.status == PostedMessageStatus.NEW,
MessageApproval.messageID == Message.id,
+ MessageApproval.posted_byID == Person.id
]
if message_id_filter is not None:
clauses.append(Message.rfc822msgid.is_in(message_id_filter))
- results = store.find((MessageApproval, Message),
- *clauses)
+ results = store.find(
+ (MessageApproval, Message, Person), *clauses)
results.order_by(MessageApproval.posted_date, Message.rfc822msgid)
return DecoratedResultSet(results, operator.itemgetter(0))
@@ -797,31 +792,7 @@
self.message_id = message_approval.message_id
self.subject = self.message.subject
self.date = self.message.datecreated
- self.author = self.message.owner
-
- @cachedproperty
- def email_message(self):
- self.message.raw.open()
- try:
- return message_from_string(self.message.raw.read())
- finally:
- self.message.raw.close()
-
- @cachedproperty
- def sender(self):
- """See `IHeldMessageDetails`."""
- originators = self.email_message.get_all('from', [])
- originators.extend(self.email_message.get_all('reply-to', []))
- if len(originators) == 0:
- return 'n/a'
- unicode_parts = []
- for bytes, charset in decode_header(originators[0]):
- if charset is None:
- charset = 'us-ascii'
- unicode_parts.append(
- bytes.decode(charset, 'replace').encode('utf-8'))
- header = make_header(zip(unicode_parts, repeat('utf-8')))
- return unicode(header)
+ self.author = self.message_approval.posted_by
@cachedproperty
def body(self):
=== modified file 'lib/lp/registry/tests/test_mailinglist.py'
--- lib/lp/registry/tests/test_mailinglist.py 2012-09-28 06:15:58 +0000
+++ lib/lp/registry/tests/test_mailinglist.py 2012-10-10 18:00:38 +0000
@@ -8,6 +8,7 @@
import transaction
from zope.component import getUtility
+from testtools.matchers import Equals
from lp.registry.interfaces.mailinglist import (
CannotChangeSubscription,
@@ -34,6 +35,7 @@
from lp.testing import (
login_celebrity,
person_logged_in,
+ StormStatementRecorder,
TestCaseWithFactory,
)
from lp.testing.layers import (
@@ -41,6 +43,7 @@
LaunchpadFunctionalLayer,
)
from lp.testing.mail_helpers import pop_notifications
+from lp.testing.matchers import HasQueryCount
class PersonMailingListTestCase(TestCaseWithFactory):
@@ -745,6 +748,20 @@
self.assertEqual(1, held_messages.count())
self.assertEqual(held_message.message_id, held_messages[0].message_id)
+ def test_getReviewableMessages_queries(self):
+ # The Message and user that posted it are retrieved with the query
+ # that get the MessageApproval.
+ test_objects = self.makeMailingListAndHeldMessage()
+ team, member, sender, held_message = test_objects
+ held_messages = team.mailing_list.getReviewableMessages()
+ with StormStatementRecorder() as recorder:
+ held_message = held_messages[0]
+ self.assertThat(recorder, HasQueryCount(Equals(1)))
+ with StormStatementRecorder() as recorder:
+ held_message.message
+ held_message.posted_by
+ self.assertThat(recorder, HasQueryCount(Equals(0)))
+
class MessageApprovalTestCase(MailingListMessageTestCase):
"""Test the MessageApproval data behaviour."""
@@ -876,17 +893,6 @@
self.assertEqual(held_message.message.datecreated, details.date)
self.assertEqual(held_message.message.owner, details.author)
- def test_email_message(self):
- held_message = self.makeMailingListAndHeldMessage()[-1]
- details = IHeldMessageDetails(held_message)
- self.assertEqual('A question', details.email_message['subject'])
-
- def test_sender(self):
- test_objects = self.makeMailingListAndHeldMessage()
- team, member, sender, held_message = test_objects
- details = IHeldMessageDetails(held_message)
- self.assertEqual(sender.preferredemail.email, details.sender)
-
def test_body(self):
held_message = self.makeMailingListAndHeldMessage()[-1]
details = IHeldMessageDetails(held_message)
Follow ups