Commit f2620c6f authored by Barry Warsaw's avatar Barry Warsaw

Digests improvements:

* digestable -> digests_enabled
* nondigestable: removed
* Exposed digests_enabled, digest_send_periodic, digest_volume_frequency in
  REST.
parent 9f14de31
......@@ -151,7 +151,7 @@ testing
def test_only_decorate_output(self):
# Ensure that decoration is not done on the archive, digest, or
# usenet copy of the message.
self.assertTrue(self._mlist.digestable)
self.assertTrue(self._mlist.digests_enabled)
# Set up NNTP.
self._mlist.gateway_to_news = True
self._mlist.linked_newsgroup = 'testing'
......
"""digests
Revision ID: 70af5a4e5790
Revises: 47294d3a604
Create Date: 2015-12-19 12:05:42.202998
"""
# revision identifiers, used by Alembic.
revision = '70af5a4e5790'
down_revision = '47294d3a604'
from alembic import op
import sqlalchemy as sa
def upgrade():
with op.batch_alter_table('mailinglist') as batch_op:
batch_op.alter_column('digestable', new_column_name='digests_enabled')
batch_op.drop_column('nondigestable')
def downgrade():
with op.batch_alter_table('mailinglist') as batch_op:
batch_op.alter_column('digests_enabled', new_column_name='digestable')
# The data for this column is lost, it's not used anyway.
batch_op.add_column(sa.Column('nondigestable', sa.Boolean))
......@@ -169,3 +169,30 @@ class TestMigrations(unittest.TestCase):
self.assertNotIn('type', results[i])
self.assertEqual(results[4]['type'], '"held message"')
self.assertEqual(results[5]['type'], '"registration"')
def test_70af5a4e5790_digests(self):
IDS_TO_DIGESTABLE = [
(1, True),
(2, False),
(3, False),
(4, True),
]
mlist_table = sa.sql.table(
'mailinglist',
sa.sql.column('id', sa.Integer),
sa.sql.column('digests_enabled', sa.Boolean)
)
# Downgrading.
for table_id, enabled in IDS_TO_DIGESTABLE:
config.db.store.execute(mlist_table.insert().values(
id=table_id, digests_enabled=enabled))
config.db.store.commit()
alembic.command.downgrade(alembic_cfg, '47294d3a604')
results = config.db.store.execute(
'SELECT id, digestable FROM mailinglist').fetchall()
self.assertEqual(results, IDS_TO_DIGESTABLE)
# Upgrading.
alembic.command.upgrade(alembic_cfg, '70af5a4e5790')
results = config.db.store.execute(
'SELECT id, digests_enabled FROM mailinglist').fetchall()
self.assertEqual(results, IDS_TO_DIGESTABLE)
......@@ -37,7 +37,7 @@ Short circuiting
When a message is posted to the mailing list, it is generally added to a
mailbox, unless the mailing list does not allow digests.
>>> mlist.digestable = False
>>> mlist.digests_enabled = False
>>> msg = next(message_factory)
>>> process = config.handlers['to-digest'].process
>>> process(mlist, msg, {})
......@@ -49,7 +49,7 @@ mailbox, unless the mailing list does not allow digests.
...or they may allow digests but the message is already a digest.
>>> mlist.digestable = True
>>> mlist.digests_enabled = True
>>> process(mlist, msg, dict(isdigest=True))
>>> sum(1 for msg in digest_mbox(mlist))
0
......
......@@ -44,8 +44,9 @@ class ToDigest:
def process(self, mlist, msg, msgdata):
"""See `IHandler`."""
# Short circuit for non-digestable messages.
if not mlist.digestable or msgdata.get('isdigest'):
# Short-circuit if digests are not enabled or if this message already
# is a digest.
if not mlist.digests_enabled or msgdata.get('isdigest'):
return
# Open the mailbox that will be used to collect the current digest.
mailbox_path = os.path.join(mlist.data_path, 'digest.mmdf')
......
......@@ -308,6 +308,19 @@ class IMailingList(Interface):
# Digests.
digests_enabled = Attribute(
"""Whether or not digests are enabled for this mailing list.""")
digest_size_threshold = Attribute(
"""The maximum (approximate) size in kilobytes of the digest currently
being collected.""")
digest_send_periodic = Attribute(
"Should a digest be sent daily even when the size threshold isn't met?")
digest_volume_frequency = Attribute(
"""How often should a new digest volume be started?""")
digest_last_sent_at = Attribute(
"""The date and time a digest of this mailing list was last sent.""")
......@@ -322,10 +335,6 @@ class IMailingList(Interface):
the digest volume number is bumped, the digest number is reset to
1.""")
digest_size_threshold = Attribute(
"""The maximum (approximate) size in kilobytes of the digest currently
being collected.""")
def send_one_last_digest_to(address, delivery_mode):
"""Make sure to send one last digest to an address.
......
......@@ -135,13 +135,13 @@ class MailingList(Model):
default_member_action = Column(Enum(Action))
default_nonmember_action = Column(Enum(Action))
description = Column(Unicode)
digests_enabled = Column(Boolean)
digest_footer_uri = Column(Unicode)
digest_header_uri = Column(Unicode)
digest_is_default = Column(Boolean)
digest_send_periodic = Column(Boolean)
digest_size_threshold = Column(Float)
digest_volume_frequency = Column(Enum(DigestFrequency))
digestable = Column(Boolean)
discard_these_nonmembers = Column(PickleType)
emergency = Column(Boolean)
encode_ascii_prefixes = Column(Boolean)
......@@ -164,7 +164,6 @@ class MailingList(Model):
moderator_password = Column(LargeBinary) # TODO : was RawStr()
newsgroup_moderation = Column(Enum(NewsgroupModeration))
nntp_prefix_subject_too = Column(Boolean)
nondigestable = Column(Boolean)
nonmember_rejection_notice = Column(Unicode)
obscure_addresses = Column(Boolean)
owner_chain = Column(Unicode)
......
......@@ -37,7 +37,10 @@ All readable attributes for a list are available on a sub-resource.
default_nonmember_action: hold
description:
digest_last_sent_at: None
digest_send_periodic: True
digest_size_threshold: 30.0
digest_volume_frequency: monthly
digests_enabled: True
display_name: Ant
filter_content: False
first_strip_reply_to: False
......@@ -97,7 +100,10 @@ When using ``PUT``, all writable attributes must be included.
... description='This is my mailing list',
... include_rfc2369_headers=False,
... allow_list_posts=False,
... digest_send_periodic=False,
... digest_size_threshold=10.5,
... digest_volume_frequency='yearly',
... digests_enabled=False,
... posting_pipeline='virgin',
... filter_content=True,
... first_strip_reply_to=True,
......@@ -145,7 +151,10 @@ These values are changed permanently.
default_nonmember_action: discard
description: This is my mailing list
...
digest_send_periodic: False
digest_size_threshold: 10.5
digest_volume_frequency: yearly
digests_enabled: False
display_name: Fnords
filter_content: True
first_strip_reply_to: True
......
......@@ -29,6 +29,7 @@ from mailman.core.errors import (
from mailman.interfaces.action import Action
from mailman.interfaces.archiver import ArchivePolicy
from mailman.interfaces.autorespond import ResponseAction
from mailman.interfaces.digests import DigestFrequency
from mailman.interfaces.mailinglist import (
IAcceptableAliasSet, ReplyToMunging, SubscriptionPolicy)
from mailman.rest.helpers import (
......@@ -112,7 +113,10 @@ ATTRIBUTES = dict(
default_nonmember_action=GetterSetter(enum_validator(Action)),
description=GetterSetter(str),
digest_last_sent_at=GetterSetter(None),
digest_send_periodic=GetterSetter(as_boolean),
digest_size_threshold=GetterSetter(float),
digest_volume_frequency=GetterSetter(enum_validator(DigestFrequency)),
digests_enabled=GetterSetter(as_boolean),
filter_content=GetterSetter(as_boolean),
first_strip_reply_to=GetterSetter(as_boolean),
fqdn_listname=GetterSetter(None),
......
......@@ -26,6 +26,7 @@ import unittest
from mailman.app.lifecycle import create_list
from mailman.database.transaction import transaction
from mailman.interfaces.digests import DigestFrequency
from mailman.interfaces.mailinglist import (
IAcceptableAliasSet, SubscriptionPolicy)
from mailman.testing.helpers import call_api
......@@ -58,9 +59,10 @@ RESOURCE = dict(
description='This is my mailing list',
include_rfc2369_headers=False,
allow_list_posts=False,
#digest_send_periodic='yes',
digest_send_periodic=True,
digest_size_threshold=10.5,
#digest_volume_frequency=1,
digest_volume_frequency='monthly',
digests_enabled=True,
posting_pipeline='virgin',
filter_content=True,
first_strip_reply_to=True,
......@@ -198,7 +200,7 @@ class TestConfiguration(unittest.TestCase):
def test_patch_attribute_double(self):
with self.assertRaises(HTTPError) as cm:
resource, response = call_api(
call_api(
'http://localhost:9001/3.0/lists/ant.example.com'
'/config/reply_to_address',
dict(display_name='[email protected]',
......@@ -255,3 +257,121 @@ class TestConfiguration(unittest.TestCase):
self.assertEqual(cm.exception.code, 400)
self.assertEqual(cm.exception.reason,
b'Cannot convert parameters: posting_pipeline')
def test_get_digest_send_periodic(self):
with transaction():
self._mlist.digest_send_periodic = False
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digest_send_periodic')
self.assertFalse(resource['digest_send_periodic'])
def test_patch_digest_send_periodic(self):
with transaction():
self._mlist.digest_send_periodic = False
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digest_send_periodic',
dict(digest_send_periodic=True),
'PATCH')
self.assertEqual(response.status, 204)
self.assertTrue(self._mlist.digest_send_periodic)
def test_put_digest_send_periodic(self):
with transaction():
self._mlist.digest_send_periodic = False
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digest_send_periodic',
dict(digest_send_periodic=True),
'PUT')
self.assertEqual(response.status, 204)
self.assertTrue(self._mlist.digest_send_periodic)
def test_get_digest_volume_frequency(self):
with transaction():
self._mlist.digest_volume_frequency = DigestFrequency.yearly
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digest_volume_frequency')
self.assertEqual(resource['digest_volume_frequency'], 'yearly')
def test_patch_digest_volume_frequency(self):
with transaction():
self._mlist.digest_volume_frequency = DigestFrequency.yearly
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digest_volume_frequency',
dict(digest_volume_frequency='monthly'),
'PATCH')
self.assertEqual(response.status, 204)
self.assertEqual(self._mlist.digest_volume_frequency,
DigestFrequency.monthly)
def test_put_digest_volume_frequency(self):
with transaction():
self._mlist.digest_volume_frequency = DigestFrequency.yearly
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digest_volume_frequency',
dict(digest_volume_frequency='monthly'),
'PUT')
self.assertEqual(response.status, 204)
self.assertEqual(self._mlist.digest_volume_frequency,
DigestFrequency.monthly)
def test_bad_patch_digest_volume_frequency(self):
with transaction():
self._mlist.digest_volume_frequency = DigestFrequency.yearly
with self.assertRaises(HTTPError) as cm:
call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digest_volume_frequency',
dict(digest_volume_frequency='once in a while'),
'PATCH')
self.assertEqual(cm.exception.code, 400)
self.assertEqual(cm.exception.reason,
b'Cannot convert parameters: digest_volume_frequency')
def test_bad_put_digest_volume_frequency(self):
with transaction():
self._mlist.digest_volume_frequency = DigestFrequency.yearly
with self.assertRaises(HTTPError) as cm:
call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digest_volume_frequency',
dict(digest_volume_frequency='once in a while'),
'PUT')
self.assertEqual(cm.exception.code, 400)
self.assertEqual(cm.exception.reason,
b'Cannot convert parameters: digest_volume_frequency')
def test_get_digests_enabled(self):
with transaction():
self._mlist.digests_enabled = False
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digests_enabled')
self.assertFalse(resource['digests_enabled'])
def test_patch_digests_enabled(self):
with transaction():
self._mlist.digests_enabled = False
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digests_enabled',
dict(digests_enabled=True),
'PATCH')
self.assertEqual(response.status, 204)
self.assertTrue(self._mlist.digests_enabled)
def test_put_digests_enabled(self):
with transaction():
self._mlist.digests_enabled = False
resource, response = call_api(
'http://localhost:9001/3.0/lists/ant.example.com/config'
'/digests_enabled',
dict(digests_enabled=True),
'PUT')
self.assertEqual(response.status, 204)
self.assertTrue(self._mlist.digests_enabled)
......@@ -87,7 +87,7 @@ class BasicOperation:
mlist.filter_action = FilterAction.discard
mlist.filter_content = False
# Digests.
mlist.digestable = True
mlist.digests_enabled = True
mlist.digest_is_default = False
mlist.mime_is_default_digest = False
mlist.digest_size_threshold = 30 # KB
......@@ -97,7 +97,6 @@ class BasicOperation:
'mailman:///$listname/$language/footer-generic.txt')
mlist.digest_volume_frequency = DigestFrequency.monthly
mlist.next_digest_number = 1
mlist.nondigestable = True
# NNTP gateway
mlist.nntp_host = ''
mlist.linked_newsgroup = ''
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment