------------------------------------------------------------
revno: 6695
committer: Barry Warsaw <[email protected]>
branch nick: hacking
timestamp: Thu 2009-02-19 20:46:57 -0500
message:
  Complete the porting of the autoresponse implementation, with no need of the
  old pickle attributes.
  
  Fix a typo in the datetime.py module.
modified:
  src/mailman/pipeline/docs/replybot.txt
  src/mailman/pipeline/replybot.py
  src/mailman/utilities/datetime.py

=== modified file 'src/mailman/pipeline/docs/replybot.txt'
--- src/mailman/pipeline/docs/replybot.txt      2009-02-13 01:36:21 +0000
+++ src/mailman/pipeline/docs/replybot.txt      2009-02-20 01:46:57 +0000
@@ -2,9 +2,9 @@
 ==================
 
 Mailman has an auto-reply handler that sends automatic responses to messages
-it receives on its posting address, or special robot addresses.  Automatic
-responses are subject to various conditions, such as headers in the original
-message or the amount of time since the last auto-response.
+it receives on its posting address, owner address, or robot address.
+Automatic responses are subject to various conditions, such as headers in the
+original message or the amount of time since the last auto-response.
 
     >>> mlist = create_list(u'[email protected]')
     >>> mlist.real_name = u'XTest'
@@ -16,19 +16,20 @@
     []
 
 
-Basic autoresponding
---------------------
+Basic automatic responding
+--------------------------
 
-Basic autoresponding occurs when the list is set up to respond to either its
--owner address, its -request address, or to the posting address, and a message
-is sent to one of these addresses.  A mailing list also has an autoresponse
-grace period which describes how much time must pass before a second response
-will be sent, with 0 meaning "there is no grace period".
+Basic automatic responding occurs when the list is set up to respond to either
+its -owner address, its -request address, or to the posting address, and a
+message is sent to one of these addresses.  A mailing list also has an
+automatic response grace period which specifies how much time must pass before
+a second response will be sent, with 0 meaning "there is no grace period".
 
     >>> import datetime
     >>> mlist.autorespond_admin = True
     >>> mlist.autoresponse_graceperiod = datetime.timedelta()
     >>> mlist.autoresponse_admin_text = u'admin autoresponse text'
+
     >>> msg = message_from_string("""\
     ... From: [email protected]
     ... To: [email protected]
@@ -36,20 +37,25 @@
     ... help
     ... """)
 
+The preceding message to the mailing list's owner will trigger an automatic
+response.
+
     >>> from mailman.pipeline.replybot import process
+    >>> from mailman.testing.helpers import get_queue_messages
     >>> process(mlist, msg, dict(toowner=True))
-    >>> len(virginq.files)
+    >>> messages = get_queue_messages('virgin')
+    >>> len(messages)
     1
-    >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
-    >>> # Print only some of the meta data.  The rest is uninteresting.
-    >>> qdata['listname']
-    u'[email protected]'
-    >>> sorted(qdata['recips'])
-    [u'[email protected]']
-    >>> # Delete data that is time dependent or random
-    >>> del qmsg['message-id']
-    >>> del qmsg['date']
-    >>> print qmsg.as_string()
+
+    >>> dump_msgdata(messages[0].msgdata)
+    _parsemsg           : False
+    listname            : [email protected]
+    nodecorate          : True
+    recips              : [u'[email protected]']
+    reduced_list_headers: True
+    version             : 3
+
+    >>> print messages[0].msg.as_string()
     MIME-Version: 1.0
     Content-Type: text/plain; charset="us-ascii"
     Content-Transfer-Encoding: 7bit
@@ -58,19 +64,19 @@
     To: [email protected]
     X-Mailer: The Mailman Replybot
     X-Ack: No
+    Message-ID: <...>
+    Date: ...
     Precedence: bulk
     <BLANKLINE>
     admin autoresponse text
-    >>> virginq.files
-    []
 
 
 Short circuiting
 ----------------
 
-Several headers in the original message determine whether an autoresponse
-should even be sent.  For example, if the message has an "X-Ack: No" header,
-no auto-response is sent.
+Several headers in the original message determine whether an automatic
+response should even be sent.  For example, if the message has an "X-Ack: No"
+header, no auto-response is sent.
 
     >>> msg = message_from_string("""\
     ... From: [email protected]
@@ -78,24 +84,26 @@
     ...
     ... help me
     ... """)
+
     >>> process(mlist, msg, dict(toowner=True))
-    >>> virginq.files
+    >>> get_queue_messages('virgin')
     []
 
-Mailman itself can suppress autoresponses for certain types of internally
-crafted messages, by setting the 'noack' metadata key.
+Mailman itself can suppress automatic responses for certain types of
+internally crafted messages, by setting the 'noack' metadata key.
 
     >>> msg = message_from_string("""\
     ... From: [email protected]
     ...
     ... help for you
     ... """)
+
     >>> process(mlist, msg, dict(noack=True, toowner=True))
-    >>> virginq.files
+    >>> get_queue_messages('virgin')
     []
 
 If there is a Precedence: header with any of the values 'bulk', 'junk', or
-'list', then the autoresponse is also suppressed.
+'list', then the automatic response is also suppressed.
 
     >>> msg = message_from_string("""\
     ... From: [email protected]
@@ -103,17 +111,19 @@
     ...
     ... hey!
     ... """)
+
     >>> process(mlist, msg, dict(toowner=True))
-    >>> virginq.files
+    >>> get_queue_messages('virgin')
     []
 
     >>> msg.replace_header('precedence', 'junk')
     >>> process(mlist, msg, dict(toowner=True))
-    >>> virginq.files
+    >>> get_queue_messages('virgin')
     []
+
     >>> msg.replace_header('precedence', 'list')
     >>> process(mlist, msg, dict(toowner=True))
-    >>> virginq.files
+    >>> get_queue_messages('virgin')
     []
 
 Unless the X-Ack: header has a value of "yes", in which case, the Precedence
@@ -121,12 +131,19 @@
 
     >>> msg['X-Ack'] = 'yes'
     >>> process(mlist, msg, dict(toowner=True))
-    >>> len(virginq.files)
+    >>> messages = get_queue_messages('virgin')
+    >>> len(messages)
     1
-    >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
-    >>> del qmsg['message-id']
-    >>> del qmsg['date']
-    >>> print qmsg.as_string()
+
+    >>> dump_msgdata(messages[0].msgdata)
+    _parsemsg           : False
+    listname            : [email protected]
+    nodecorate          : True
+    recips              : [u'[email protected]']
+    reduced_list_headers: True
+    version             : 3
+
+    >>> print messages[0].msg.as_string()
     MIME-Version: 1.0
     Content-Type: text/plain; charset="us-ascii"
     Content-Transfer-Encoding: 7bit
@@ -135,6 +152,8 @@
     To: [email protected]
     X-Mailer: The Mailman Replybot
     X-Ack: No
+    Message-ID: <...>
+    Date: ...
     Precedence: bulk
     <BLANKLINE>
     admin autoresponse text
@@ -149,19 +168,20 @@
 
     >>> mlist.autorespond_requests = True
     >>> mlist.autoresponse_request_text = u'robot autoresponse text'
+
     >>> msg = message_from_string("""\
     ... From: [email protected]
     ... To: [email protected]
     ...
     ... help me
     ... """)
+
     >>> process(mlist, msg, dict(torequest=True))
-    >>> len(virginq.files)
+    >>> messages = get_queue_messages('virgin')
+    >>> len(messages)
     1
-    >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
-    >>> del qmsg['message-id']
-    >>> del qmsg['date']
-    >>> print qmsg.as_string()
+
+    >>> print messages[0].msg.as_string()
     MIME-Version: 1.0
     Content-Type: text/plain; charset="us-ascii"
     Content-Transfer-Encoding: 7bit
@@ -170,6 +190,8 @@
     To: [email protected]
     X-Mailer: The Mailman Replybot
     X-Ack: No
+    Message-ID: <...>
+    Date: ...
     Precedence: bulk
     <BLANKLINE>
     robot autoresponse text
@@ -178,19 +200,20 @@
 
     >>> mlist.autorespond_postings = True
     >>> mlist.autoresponse_postings_text = u'postings autoresponse text'
+
     >>> msg = message_from_string("""\
     ... From: [email protected]
     ... To: [email protected]
     ...
     ... help me
     ... """)
+
     >>> process(mlist, msg, {})
-    >>> len(virginq.files)
+    >>> messages = get_queue_messages('virgin')
+    >>> len(messages)
     1
-    >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
-    >>> del qmsg['message-id']
-    >>> del qmsg['date']
-    >>> print qmsg.as_string()
+
+    >>> print messages[0].msg.as_string()
     MIME-Version: 1.0
     Content-Type: text/plain; charset="us-ascii"
     Content-Transfer-Encoding: 7bit
@@ -199,6 +222,8 @@
     To: [email protected]
     X-Mailer: The Mailman Replybot
     X-Ack: No
+    Message-ID: <...>
+    Date: ...
     Precedence: bulk
     <BLANKLINE>
     postings autoresponse text
@@ -207,8 +232,103 @@
 Grace periods
 -------------
 
-Auto-responses have a grace period, during which no additional responses will
-be sent.  This is so as not to bombard the sender with responses.  The grace
-period is measured in days.
-
-XXX Add grace period tests.
+Automatic responses have a grace period, during which no additional responses
+will be sent.  This is so as not to bombard the sender with responses.  The
+grace period is measured in days.
+
+    >>> mlist.autoresponse_graceperiod = datetime.timedelta(days=10)
+
+When a response is sent to a person via any of the owner, request, or postings
+addresses, the response date is recorded.  The grace period is usually
+measured in days.
+
+    >>> msg = message_from_string("""\
+    ... From: [email protected]
+    ... To: [email protected]
+    ...
+    ... help
+    ... """)
+
+This is the first response to bperson, so it gets sent.
+
+    >>> process(mlist, msg, dict(toowner=True))
+    >>> print len(get_queue_messages('virgin'))
+    1
+
+But with a grace period greater than zero, no subsequent response will be sent
+right now.
+
+    >>> process(mlist, msg, dict(toowner=True))
+    >>> print len(get_queue_messages('virgin'))
+    0
+
+Fast forward 9 days and you still don't get a response.
+
+    >>> from mailman.utilities.datetime import factory
+    >>> factory.fast_forward(days=9)
+
+    >>> process(mlist, msg, dict(toowner=True))
+    >>> print len(get_queue_messages('virgin'))
+    0
+
+But tomorrow, the sender will get a new auto-response.
+
+    >>> factory.fast_forward()
+    >>> process(mlist, msg, dict(toowner=True))
+    >>> print len(get_queue_messages('virgin'))
+    1
+
+Of course, everything works the same way for messages to the request
+address, even if the sender is the same person...
+
+    >>> msg = message_from_string("""\
+    ... From: [email protected]
+    ... To: [email protected]
+    ...
+    ... help
+    ... """)
+
+    >>> process(mlist, msg, dict(torequest=True))
+    >>> print len(get_queue_messages('virgin'))
+    1
+
+    >>> process(mlist, msg, dict(torequest=True))
+    >>> print len(get_queue_messages('virgin'))
+    0
+
+    >>> factory.fast_forward(days=9)
+    >>> process(mlist, msg, dict(torequest=True))
+    >>> print len(get_queue_messages('virgin'))
+    0
+
+    >>> factory.fast_forward()
+    >>> process(mlist, msg, dict(torequest=True))
+    >>> print len(get_queue_messages('virgin'))
+    1
+
+...and for messages to the posting address.
+
+    >>> msg = message_from_string("""\
+    ... From: [email protected]
+    ... To: [email protected]
+    ...
+    ... help
+    ... """)
+
+    >>> process(mlist, msg, {})
+    >>> print len(get_queue_messages('virgin'))
+    1
+
+    >>> process(mlist, msg, {})
+    >>> print len(get_queue_messages('virgin'))
+    0
+
+    >>> factory.fast_forward(days=9)
+    >>> process(mlist, msg, {})
+    >>> print len(get_queue_messages('virgin'))
+    0
+
+    >>> factory.fast_forward()
+    >>> process(mlist, msg, {})
+    >>> print len(get_queue_messages('virgin'))
+    1

=== modified file 'src/mailman/pipeline/replybot.py'
--- src/mailman/pipeline/replybot.py    2009-02-10 03:19:18 +0000
+++ src/mailman/pipeline/replybot.py    2009-02-20 01:46:57 +0000
@@ -25,16 +25,18 @@
     ]
 
 
-import time
 import logging
 import datetime
 
 from zope.interface import implements
 
 from mailman import Utils
+from mailman.config import config
 from mailman.email.message import Message, UserNotification
 from mailman.i18n import _
+from mailman.interfaces.autorespond import IAutoResponseSet, Response
 from mailman.interfaces.handler import IHandler
+from mailman.utilities.datetime import today
 from mailman.utilities.string import expand
 
 
@@ -65,22 +67,25 @@
     toadmin = msgdata.get('toowner')
     torequest = msgdata.get('torequest')
     if ((toadmin and not mlist.autorespond_admin) or
-           (torequest and not mlist.autorespond_requests) or \
-           (not toadmin and not torequest and not mlist.autorespond_postings)):
+        (torequest and not mlist.autorespond_requests) or \
+        (not toadmin and not torequest and not mlist.autorespond_postings)):
         return
     # Now see if we're in the grace period for this sender.  graceperiod <= 0
     # means always autorespond, as does an "X-Ack: yes" header (useful for
     # debugging).
-    now = time.time()
-    graceperiod = mlist.autoresponse_graceperiod
-    if graceperiod > NODELTA and ack <> 'yes':
+    response_set = IAutoResponseSet(mlist)
+    address = config.db.user_manager.get_address(msg.sender)
+    if address is None:
+        address = config.db.user_manager.create_address(msg.sender)
+    grace_period = mlist.autoresponse_graceperiod
+    if grace_period > NODELTA and ack <> 'yes':
         if toadmin:
-            quiet_until = mlist.admin_responses.get(msg.sender, 0)
+            last = response_set.last_response(address, Response.owner)
         elif torequest:
-            quiet_until = mlist.request_responses.get(msg.sender, 0)
+            last = response_set.last_response(address, Response.command)
         else:
-            quiet_until = mlist.postings_responses.get(msg.sender, 0)
-        if quiet_until > now:
+            last = response_set.last_response(address, Response.postings)
+        if last is not None and last.date_sent + grace_period > today():
             return
     # Okay, we know we're going to auto-respond to this sender, craft the
     # message, send it, and update the database.
@@ -108,15 +113,14 @@
     outmsg['X-Ack'] = 'No'
     outmsg.send(mlist)
     # update the grace period database
-    if graceperiod > NODELTA:
+    if grace_period > NODELTA:
         # graceperiod is in days, we need # of seconds
-        quiet_until = now + graceperiod * 24 * 60 * 60
         if toadmin:
-            mlist.admin_responses[msg.sender] = quiet_until
+            response_set.response_sent(address, Response.owner)
         elif torequest:
-            mlist.request_responses[msg.sender] = quiet_until
+            response_set.response_sent(address, Response.command)
         else:
-            mlist.postings_responses[msg.sender] = quiet_until
+            response_set.response_sent(address, Response.postings)
 
 
 

=== modified file 'src/mailman/utilities/datetime.py'
--- src/mailman/utilities/datetime.py   2009-02-19 05:18:35 +0000
+++ src/mailman/utilities/datetime.py   2009-02-20 01:46:57 +0000
@@ -44,7 +44,7 @@
     predictable_today = None
 
     def now(self, tz=None):
-        return (yself.predictable_now
+        return (self.predictable_now
                 if self.testing_mode
                 else datetime.datetime.now(tz))
 



--
Primary development focus
https://code.launchpad.net/~mailman-coders/mailman/3.0

Your team Mailman Checkins is subscribed to branch lp:mailman.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+edit-subscription.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to