Merge pull request #550 from stumpylog/feature-mail-consume-improve-docs

Feature mail consume improve docs
This commit is contained in:
shamoon 2022-04-10 08:46:45 -07:00 committed by GitHub
commit 8f98cb4860
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 96 deletions

View File

@ -178,6 +178,14 @@ These are as follows:
automatically or manually and tell paperless to move them to yet another folder automatically or manually and tell paperless to move them to yet another folder
after consumption. It's up to you. after consumption. It's up to you.
.. note::
When defining a mail rule with a folder, you may need to try different characters to
define how the sub-folders are separated. Common values include ".", "/" or "|", but
this varies by the mail server. Unfortunately, this isn't a value we can determine
automatically. Either check the documentation for your mail server, or check for
errors in the logs and try different folder separator values.
.. note:: .. note::
Paperless will process the rules in the order defined in the admin page. Paperless will process the rules in the order defined in the admin page.

View File

@ -61,13 +61,13 @@ class FlagMailAction(BaseMailAction):
def get_rule_action(rule): def get_rule_action(rule):
if rule.action == MailRule.ACTION_FLAG: if rule.action == MailRule.AttachmentAction.FLAG:
return FlagMailAction() return FlagMailAction()
elif rule.action == MailRule.ACTION_DELETE: elif rule.action == MailRule.AttachmentAction.DELETE:
return DeleteMailAction() return DeleteMailAction()
elif rule.action == MailRule.ACTION_MOVE: elif rule.action == MailRule.AttachmentAction.MOVE:
return MoveMailAction() return MoveMailAction()
elif rule.action == MailRule.ACTION_MARK_READ: elif rule.action == MailRule.AttachmentAction.MARK_READ:
return MarkReadMailAction() return MarkReadMailAction()
else: else:
raise NotImplementedError("Unknown action.") # pragma: nocover raise NotImplementedError("Unknown action.") # pragma: nocover
@ -89,11 +89,11 @@ def make_criterias(rule):
def get_mailbox(server, port, security): def get_mailbox(server, port, security):
if security == MailAccount.IMAP_SECURITY_NONE: if security == MailAccount.ImapSecurity.NONE:
mailbox = MailBoxUnencrypted(server, port) mailbox = MailBoxUnencrypted(server, port)
elif security == MailAccount.IMAP_SECURITY_STARTTLS: elif security == MailAccount.ImapSecurity.STARTTLS:
mailbox = MailBox(server, port, starttls=True) mailbox = MailBox(server, port, starttls=True)
elif security == MailAccount.IMAP_SECURITY_SSL: elif security == MailAccount.ImapSecurity.SSL:
mailbox = MailBox(server, port) mailbox = MailBox(server, port)
else: else:
raise NotImplementedError("Unknown IMAP security") # pragma: nocover raise NotImplementedError("Unknown IMAP security") # pragma: nocover
@ -112,10 +112,10 @@ class MailAccountHandler(LoggingMixin):
return None return None
def get_title(self, message, att, rule): def get_title(self, message, att, rule):
if rule.assign_title_from == MailRule.TITLE_FROM_SUBJECT: if rule.assign_title_from == MailRule.TitleSource.FROM_SUBJECT:
return message.subject return message.subject
elif rule.assign_title_from == MailRule.TITLE_FROM_FILENAME: elif rule.assign_title_from == MailRule.TitleSource.FROM_FILENAME:
return os.path.splitext(os.path.basename(att.filename))[0] return os.path.splitext(os.path.basename(att.filename))[0]
else: else:
@ -126,20 +126,20 @@ class MailAccountHandler(LoggingMixin):
def get_correspondent(self, message: MailMessage, rule): def get_correspondent(self, message: MailMessage, rule):
c_from = rule.assign_correspondent_from c_from = rule.assign_correspondent_from
if c_from == MailRule.CORRESPONDENT_FROM_NOTHING: if c_from == MailRule.CorrespondentSource.FROM_NOTHING:
return None return None
elif c_from == MailRule.CORRESPONDENT_FROM_EMAIL: elif c_from == MailRule.CorrespondentSource.FROM_EMAIL:
return self._correspondent_from_name(message.from_) return self._correspondent_from_name(message.from_)
elif c_from == MailRule.CORRESPONDENT_FROM_NAME: elif c_from == MailRule.CorrespondentSource.FROM_NAME:
from_values = message.from_values from_values = message.from_values
if from_values is not None and len(from_values.name) > 0: if from_values is not None and len(from_values.name) > 0:
return self._correspondent_from_name(from_values.name) return self._correspondent_from_name(from_values.name)
else: else:
return self._correspondent_from_name(message.from_) return self._correspondent_from_name(message.from_)
elif c_from == MailRule.CORRESPONDENT_FROM_CUSTOM: elif c_from == MailRule.CorrespondentSource.FROM_CUSTOM:
return rule.assign_correspondent return rule.assign_correspondent
else: else:
@ -274,7 +274,8 @@ class MailAccountHandler(LoggingMixin):
if ( if (
not att.content_disposition == "attachment" not att.content_disposition == "attachment"
and rule.attachment_type == MailRule.ATTACHMENT_TYPE_ATTACHMENTS_ONLY and rule.attachment_type
== MailRule.AttachmentProcessing.ATTACHMENTS_ONLY
): ):
self.log( self.log(
"debug", "debug",

View File

@ -0,0 +1,37 @@
# Generated by Django 4.0.3 on 2022-03-28 17:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("paperless_mail", "0008_auto_20210516_0940"),
]
operations = [
migrations.AlterField(
model_name="mailrule",
name="action",
field=models.PositiveIntegerField(
choices=[
(1, "Mark as read, don't process read mails"),
(2, "Flag the mail, don't process flagged mails"),
(3, "Move to specified folder"),
(4, "Delete"),
],
default=3,
verbose_name="action",
),
),
migrations.AlterField(
model_name="mailrule",
name="folder",
field=models.CharField(
default="INBOX",
help_text="Subfolders must be separated by a delimiter, often a dot ('.') or slash ('/'), but it varies by mail server.",
max_length=256,
verbose_name="folder",
),
),
]

View File

@ -8,15 +8,10 @@ class MailAccount(models.Model):
verbose_name = _("mail account") verbose_name = _("mail account")
verbose_name_plural = _("mail accounts") verbose_name_plural = _("mail accounts")
IMAP_SECURITY_NONE = 1 class ImapSecurity(models.IntegerChoices):
IMAP_SECURITY_SSL = 2 NONE = 1, _("No encryption")
IMAP_SECURITY_STARTTLS = 3 SSL = 2, _("Use SSL")
STARTTLS = 3, _("Use STARTTLS")
IMAP_SECURITY_OPTIONS = (
(IMAP_SECURITY_NONE, _("No encryption")),
(IMAP_SECURITY_SSL, _("Use SSL")),
(IMAP_SECURITY_STARTTLS, _("Use STARTTLS")),
)
name = models.CharField(_("name"), max_length=256, unique=True) name = models.CharField(_("name"), max_length=256, unique=True)
@ -34,8 +29,8 @@ class MailAccount(models.Model):
imap_security = models.PositiveIntegerField( imap_security = models.PositiveIntegerField(
_("IMAP security"), _("IMAP security"),
choices=IMAP_SECURITY_OPTIONS, choices=ImapSecurity.choices,
default=IMAP_SECURITY_SSL, default=ImapSecurity.SSL,
) )
username = models.CharField(_("username"), max_length=256) username = models.CharField(_("username"), max_length=256)
@ -61,48 +56,25 @@ class MailRule(models.Model):
verbose_name = _("mail rule") verbose_name = _("mail rule")
verbose_name_plural = _("mail rules") verbose_name_plural = _("mail rules")
ATTACHMENT_TYPE_ATTACHMENTS_ONLY = 1 class AttachmentProcessing(models.IntegerChoices):
ATTACHMENT_TYPE_EVERYTHING = 2 ATTACHMENTS_ONLY = 1, _("Only process attachments.")
EVERYTHING = 2, _("Process all files, including 'inline' " "attachments.")
ATTACHMENT_TYPES = ( class AttachmentAction(models.IntegerChoices):
(ATTACHMENT_TYPE_ATTACHMENTS_ONLY, _("Only process attachments.")), DELETE = 1, _("Mark as read, don't process read mails")
( MOVE = 2, _("Flag the mail, don't process flagged mails")
ATTACHMENT_TYPE_EVERYTHING, MARK_READ = 3, _("Move to specified folder")
_("Process all files, including 'inline' " "attachments."), FLAG = 4, _("Delete")
),
)
ACTION_DELETE = 1 class TitleSource(models.IntegerChoices):
ACTION_MOVE = 2 FROM_SUBJECT = 1, _("Use subject as title")
ACTION_MARK_READ = 3 FROM_FILENAME = 2, _("Use attachment filename as title")
ACTION_FLAG = 4
ACTIONS = ( class CorrespondentSource(models.IntegerChoices):
(ACTION_MARK_READ, _("Mark as read, don't process read mails")), FROM_NOTHING = 1, _("Do not assign a correspondent")
(ACTION_FLAG, _("Flag the mail, don't process flagged mails")), FROM_EMAIL = 2, _("Use mail address")
(ACTION_MOVE, _("Move to specified folder")), FROM_NAME = 3, _("Use name (or mail address if not available)")
(ACTION_DELETE, _("Delete")), FROM_CUSTOM = 4, _("Use correspondent selected below")
)
TITLE_FROM_SUBJECT = 1
TITLE_FROM_FILENAME = 2
TITLE_SELECTOR = (
(TITLE_FROM_SUBJECT, _("Use subject as title")),
(TITLE_FROM_FILENAME, _("Use attachment filename as title")),
)
CORRESPONDENT_FROM_NOTHING = 1
CORRESPONDENT_FROM_EMAIL = 2
CORRESPONDENT_FROM_NAME = 3
CORRESPONDENT_FROM_CUSTOM = 4
CORRESPONDENT_SELECTOR = (
(CORRESPONDENT_FROM_NOTHING, _("Do not assign a correspondent")),
(CORRESPONDENT_FROM_EMAIL, _("Use mail address")),
(CORRESPONDENT_FROM_NAME, _("Use name (or mail address if not available)")),
(CORRESPONDENT_FROM_CUSTOM, _("Use correspondent selected below")),
)
name = models.CharField(_("name"), max_length=256, unique=True) name = models.CharField(_("name"), max_length=256, unique=True)
@ -119,7 +91,10 @@ class MailRule(models.Model):
_("folder"), _("folder"),
default="INBOX", default="INBOX",
max_length=256, max_length=256,
help_text=_("Subfolders must be separated by dots."), help_text=_(
"Subfolders must be separated by a delimiter, often a dot ('.') or"
" slash ('/'), but it varies by mail server.",
),
) )
filter_from = models.CharField( filter_from = models.CharField(
@ -161,8 +136,8 @@ class MailRule(models.Model):
attachment_type = models.PositiveIntegerField( attachment_type = models.PositiveIntegerField(
_("attachment type"), _("attachment type"),
choices=ATTACHMENT_TYPES, choices=AttachmentProcessing.choices,
default=ATTACHMENT_TYPE_ATTACHMENTS_ONLY, default=AttachmentProcessing.ATTACHMENTS_ONLY,
help_text=_( help_text=_(
"Inline attachments include embedded images, so it's best " "Inline attachments include embedded images, so it's best "
"to combine this option with a filename filter.", "to combine this option with a filename filter.",
@ -171,8 +146,8 @@ class MailRule(models.Model):
action = models.PositiveIntegerField( action = models.PositiveIntegerField(
_("action"), _("action"),
choices=ACTIONS, choices=AttachmentAction.choices,
default=ACTION_MARK_READ, default=AttachmentAction.MARK_READ,
) )
action_parameter = models.CharField( action_parameter = models.CharField(
@ -190,8 +165,8 @@ class MailRule(models.Model):
assign_title_from = models.PositiveIntegerField( assign_title_from = models.PositiveIntegerField(
_("assign title from"), _("assign title from"),
choices=TITLE_SELECTOR, choices=TitleSource.choices,
default=TITLE_FROM_SUBJECT, default=TitleSource.FROM_SUBJECT,
) )
assign_tag = models.ForeignKey( assign_tag = models.ForeignKey(
@ -212,8 +187,8 @@ class MailRule(models.Model):
assign_correspondent_from = models.PositiveIntegerField( assign_correspondent_from = models.PositiveIntegerField(
_("assign correspondent from"), _("assign correspondent from"),
choices=CORRESPONDENT_SELECTOR, choices=CorrespondentSource.choices,
default=CORRESPONDENT_FROM_NOTHING, default=CorrespondentSource.FROM_NOTHING,
) )
assign_correspondent = models.ForeignKey( assign_correspondent = models.ForeignKey(

View File

@ -246,13 +246,13 @@ class TestMail(DirectoriesMixin, TestCase):
rule = MailRule( rule = MailRule(
name="a", name="a",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_NOTHING, assign_correspondent_from=MailRule.CorrespondentSource.FROM_NOTHING,
) )
self.assertIsNone(handler.get_correspondent(message, rule)) self.assertIsNone(handler.get_correspondent(message, rule))
rule = MailRule( rule = MailRule(
name="b", name="b",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_EMAIL, assign_correspondent_from=MailRule.CorrespondentSource.FROM_EMAIL,
) )
c = handler.get_correspondent(message, rule) c = handler.get_correspondent(message, rule)
self.assertIsNotNone(c) self.assertIsNotNone(c)
@ -264,7 +264,7 @@ class TestMail(DirectoriesMixin, TestCase):
rule = MailRule( rule = MailRule(
name="c", name="c",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_NAME, assign_correspondent_from=MailRule.CorrespondentSource.FROM_NAME,
) )
c = handler.get_correspondent(message, rule) c = handler.get_correspondent(message, rule)
self.assertIsNotNone(c) self.assertIsNotNone(c)
@ -275,7 +275,7 @@ class TestMail(DirectoriesMixin, TestCase):
rule = MailRule( rule = MailRule(
name="d", name="d",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_CUSTOM, assign_correspondent_from=MailRule.CorrespondentSource.FROM_CUSTOM,
assign_correspondent=someone_else, assign_correspondent=someone_else,
) )
c = handler.get_correspondent(message, rule) c = handler.get_correspondent(message, rule)
@ -289,9 +289,15 @@ class TestMail(DirectoriesMixin, TestCase):
handler = MailAccountHandler() handler = MailAccountHandler()
rule = MailRule(name="a", assign_title_from=MailRule.TITLE_FROM_FILENAME) rule = MailRule(
name="a",
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
)
self.assertEqual(handler.get_title(message, att, rule), "this_is_the_file") self.assertEqual(handler.get_title(message, att, rule), "this_is_the_file")
rule = MailRule(name="b", assign_title_from=MailRule.TITLE_FROM_SUBJECT) rule = MailRule(
name="b",
assign_title_from=MailRule.TitleSource.FROM_SUBJECT,
)
self.assertEqual(handler.get_title(message, att, rule), "the message title") self.assertEqual(handler.get_title(message, att, rule), "the message title")
def test_handle_message(self): def test_handle_message(self):
@ -302,7 +308,10 @@ class TestMail(DirectoriesMixin, TestCase):
) )
account = MailAccount() account = MailAccount()
rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account) rule = MailRule(
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account,
)
result = self.mail_account_handler.handle_message(message, rule) result = self.mail_account_handler.handle_message(message, rule)
@ -346,7 +355,10 @@ class TestMail(DirectoriesMixin, TestCase):
) )
account = MailAccount() account = MailAccount()
rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account) rule = MailRule(
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account,
)
result = self.mail_account_handler.handle_message(message, rule) result = self.mail_account_handler.handle_message(message, rule)
@ -369,7 +381,10 @@ class TestMail(DirectoriesMixin, TestCase):
) )
account = MailAccount() account = MailAccount()
rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account) rule = MailRule(
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account,
)
result = self.mail_account_handler.handle_message(message, rule) result = self.mail_account_handler.handle_message(message, rule)
@ -392,9 +407,9 @@ class TestMail(DirectoriesMixin, TestCase):
account = MailAccount() account = MailAccount()
rule = MailRule( rule = MailRule(
assign_title_from=MailRule.TITLE_FROM_FILENAME, assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account, account=account,
attachment_type=MailRule.ATTACHMENT_TYPE_EVERYTHING, attachment_type=MailRule.AttachmentProcessing.EVERYTHING,
) )
result = self.mail_account_handler.handle_message(message, rule) result = self.mail_account_handler.handle_message(message, rule)
@ -427,7 +442,7 @@ class TestMail(DirectoriesMixin, TestCase):
self.async_task.reset_mock() self.async_task.reset_mock()
account = MailAccount() account = MailAccount()
rule = MailRule( rule = MailRule(
assign_title_from=MailRule.TITLE_FROM_FILENAME, assign_title_from=MailRule.TitleSource.FROM_FILENAME,
account=account, account=account,
filter_attachment_filename=pattern, filter_attachment_filename=pattern,
) )
@ -452,7 +467,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create( _ = MailRule.objects.create(
name="testrule", name="testrule",
account=account, account=account,
action=MailRule.ACTION_MARK_READ, action=MailRule.AttachmentAction.MARK_READ,
) )
self.assertEqual(len(self.bogus_mailbox.messages), 3) self.assertEqual(len(self.bogus_mailbox.messages), 3)
@ -475,7 +490,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create( _ = MailRule.objects.create(
name="testrule", name="testrule",
account=account, account=account,
action=MailRule.ACTION_DELETE, action=MailRule.AttachmentAction.DELETE,
filter_subject="Invoice", filter_subject="Invoice",
) )
@ -496,7 +511,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create( _ = MailRule.objects.create(
name="testrule", name="testrule",
account=account, account=account,
action=MailRule.ACTION_FLAG, action=MailRule.AttachmentAction.FLAG,
filter_subject="Invoice", filter_subject="Invoice",
) )
@ -519,7 +534,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create( _ = MailRule.objects.create(
name="testrule", name="testrule",
account=account, account=account,
action=MailRule.ACTION_MOVE, action=MailRule.AttachmentAction.MOVE,
action_parameter="spam", action_parameter="spam",
filter_subject="Claim", filter_subject="Claim",
) )
@ -565,7 +580,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create( _ = MailRule.objects.create(
name="testrule", name="testrule",
account=account, account=account,
action=MailRule.ACTION_MOVE, action=MailRule.AttachmentAction.MOVE,
action_parameter="spam", action_parameter="spam",
filter_subject="Claim", filter_subject="Claim",
) )
@ -586,7 +601,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create( _ = MailRule.objects.create(
name="testrule", name="testrule",
account=account, account=account,
action=MailRule.ACTION_MOVE, action=MailRule.AttachmentAction.MOVE,
action_parameter="spam", action_parameter="spam",
filter_subject="Claim", filter_subject="Claim",
order=1, order=1,
@ -595,7 +610,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create( _ = MailRule.objects.create(
name="testrule2", name="testrule2",
account=account, account=account,
action=MailRule.ACTION_MOVE, action=MailRule.AttachmentAction.MOVE,
action_parameter="spam", action_parameter="spam",
filter_subject="Claim", filter_subject="Claim",
order=2, order=2,
@ -625,7 +640,7 @@ class TestMail(DirectoriesMixin, TestCase):
_ = MailRule.objects.create( _ = MailRule.objects.create(
name="testrule", name="testrule",
account=account, account=account,
action=MailRule.ACTION_MOVE, action=MailRule.AttachmentAction.MOVE,
action_parameter="spam", action_parameter="spam",
) )
@ -650,9 +665,9 @@ class TestMail(DirectoriesMixin, TestCase):
name="testrule", name="testrule",
filter_from="amazon@amazon.de", filter_from="amazon@amazon.de",
account=account, account=account,
action=MailRule.ACTION_MOVE, action=MailRule.AttachmentAction.MOVE,
action_parameter="spam", action_parameter="spam",
assign_correspondent_from=MailRule.CORRESPONDENT_FROM_EMAIL, assign_correspondent_from=MailRule.CorrespondentSource.FROM_EMAIL,
) )
self.mail_account_handler.handle_mail_account(account) self.mail_account_handler.handle_mail_account(account)
@ -687,7 +702,7 @@ class TestMail(DirectoriesMixin, TestCase):
rule = MailRule.objects.create( rule = MailRule.objects.create(
name="testrule3", name="testrule3",
account=account, account=account,
action=MailRule.ACTION_DELETE, action=MailRule.AttachmentAction.DELETE,
filter_subject="Claim", filter_subject="Claim",
) )