Allows filtering email by the TO value(s) as well

This commit is contained in:
Trenton H 2023-01-25 14:06:36 -08:00
parent 70ac696f17
commit 3e467c517d
12 changed files with 80 additions and 9 deletions

View File

@ -9,6 +9,7 @@
"account": 2,
"folder": "INBOX",
"filter_from": null,
"filter_to": null,
"filter_subject": "[paperless]",
"filter_body": null,
"filter_attachment_filename": null,

View File

@ -18,6 +18,7 @@
<div class="col">
<p class="small" i18n>Paperless will only process mails that match <em>all</em> of the filters specified below.</p>
<app-input-text i18n-title title="Filter from" formControlName="filter_from" [error]="error?.filter_from"></app-input-text>
<app-input-text i18n-title title="Filter to" formControlName="filter_to" [error]="error?.filter_to"></app-input-text>
<app-input-text i18n-title title="Filter subject" formControlName="filter_subject" [error]="error?.filter_subject"></app-input-text>
<app-input-text i18n-title title="Filter body" formControlName="filter_body" [error]="error?.filter_body"></app-input-text>
<app-input-text i18n-title title="Filter attachment filename" formControlName="filter_attachment_filename" i18n-hint hint="Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive." [error]="error?.filter_attachment_filename"></app-input-text>

View File

@ -149,6 +149,7 @@ export class MailRuleEditDialogComponent extends EditDialogComponent<PaperlessMa
account: new FormControl(null),
folder: new FormControl('INBOX'),
filter_from: new FormControl(null),
filter_to: new FormControl(null),
filter_subject: new FormControl(null),
filter_body: new FormControl(null),
filter_attachment_filename: new FormControl(null),

View File

@ -386,6 +386,7 @@ export class SettingsComponent
account: rule.account,
folder: rule.folder,
filter_from: rule.filter_from,
filter_to: rule.filter_to,
filter_subject: rule.filter_subject,
filter_body: rule.filter_body,
filter_attachment_filename: rule.filter_attachment_filename,
@ -406,6 +407,7 @@ export class SettingsComponent
account: new FormControl(null),
folder: new FormControl(null),
filter_from: new FormControl(null),
filter_to: new FormControl(null),
filter_subject: new FormControl(null),
filter_body: new FormControl(null),
filter_attachment_filename: new FormControl(null),

View File

@ -42,6 +42,8 @@ export interface PaperlessMailRule extends ObjectWithId {
filter_from: string
filter_to: string
filter_subject: string
filter_body: string

View File

@ -53,6 +53,7 @@ class MailRuleAdmin(admin.ModelAdmin):
),
"fields": (
"filter_from",
"filter_to",
"filter_subject",
"filter_body",
"filter_attachment_filename",

View File

@ -82,7 +82,12 @@ class BaseMailAction:
"""
return {}
def post_consume(self, M: MailBox, message_uid: str, parameter: str):
def post_consume(
self,
M: MailBox,
message_uid: str,
parameter: str,
): # pragma: nocover
"""
Perform mail action on the given mail uid in the mailbox.
"""
@ -160,7 +165,7 @@ class TagMailAction(BaseMailAction):
return {"flagged": False}
elif self.keyword:
return AND(NOT(gmail_label=self.keyword), no_keyword=self.keyword)
else:
else: # pragma: nocover
raise ValueError("This should never happen.")
def post_consume(self, M: MailBox, message_uid: str, parameter: str):
@ -363,6 +368,8 @@ def make_criterias(rule):
criterias["date_gte"] = maximum_age
if rule.filter_from:
criterias["from_"] = rule.filter_from
if rule.filter_to:
criterias["to"] = rule.filter_to
if rule.filter_subject:
criterias["subject"] = rule.filter_subject
if rule.filter_body:

View File

@ -0,0 +1,19 @@
# Generated by Django 4.1.7 on 2023-03-11 21:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("paperless_mail", "0018_processedmail"),
]
operations = [
migrations.AddField(
model_name="mailrule",
name="filter_to",
field=models.CharField(
blank=True, max_length=256, null=True, verbose_name="filter to"
),
),
]

View File

@ -113,12 +113,21 @@ class MailRule(document_models.ModelWithOwner):
null=True,
blank=True,
)
filter_to = models.CharField(
_("filter to"),
max_length=256,
null=True,
blank=True,
)
filter_subject = models.CharField(
_("filter subject"),
max_length=256,
null=True,
blank=True,
)
filter_body = models.CharField(
_("filter body"),
max_length=256,

View File

@ -70,6 +70,7 @@ class MailRuleSerializer(OwnedObjectSerializer):
"account",
"folder",
"filter_from",
"filter_to",
"filter_subject",
"filter_body",
"filter_attachment_filename",

View File

@ -201,6 +201,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
account=account1,
folder="INBOX",
filter_from="from@example.com",
filter_to="someone@somewhere.com",
filter_subject="subject",
filter_body="body",
filter_attachment_filename="file.pdf",
@ -222,6 +223,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
self.assertEqual(returned_rule1["account"], account1.pk)
self.assertEqual(returned_rule1["folder"], rule1.folder)
self.assertEqual(returned_rule1["filter_from"], rule1.filter_from)
self.assertEqual(returned_rule1["filter_to"], rule1.filter_to)
self.assertEqual(returned_rule1["filter_subject"], rule1.filter_subject)
self.assertEqual(returned_rule1["filter_body"], rule1.filter_body)
self.assertEqual(
@ -275,6 +277,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
"account": account1.pk,
"folder": "INBOX",
"filter_from": "from@example.com",
"filter_to": "aperson@aplace.com",
"filter_subject": "subject",
"filter_body": "body",
"filter_attachment_filename": "file.pdf",
@ -307,6 +310,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
self.assertEqual(returned_rule1["account"], account1.pk)
self.assertEqual(returned_rule1["folder"], rule1["folder"])
self.assertEqual(returned_rule1["filter_from"], rule1["filter_from"])
self.assertEqual(returned_rule1["filter_to"], rule1["filter_to"])
self.assertEqual(returned_rule1["filter_subject"], rule1["filter_subject"])
self.assertEqual(returned_rule1["filter_body"], rule1["filter_body"])
self.assertEqual(

View File

@ -5,6 +5,7 @@ import uuid
from collections import namedtuple
from typing import ContextManager
from typing import List
from typing import Optional
from typing import Union
from unittest import mock
@ -131,12 +132,20 @@ class BogusMailBox(ContextManager):
from_ = criteria[criteria.index("FROM") + 1].strip('"')
msg = filter(lambda m: from_ in m.from_, msg)
if "TO" in criteria:
to_ = criteria[criteria.index("TO") + 1].strip('"')
msg = []
for m in self.messages:
for to_addrs in m.to:
if to_ in to_addrs:
msg.append(m)
if "UNFLAGGED" in criteria:
msg = filter(lambda m: not m.flagged, msg)
if "UNKEYWORD" in criteria:
tag = criteria[criteria.index("UNKEYWORD") + 1].strip("'")
msg = filter(lambda m: "processed" not in m.flags, msg)
msg = filter(lambda m: tag not in m.flags, msg)
if "(X-GM-LABELS" in criteria: # ['NOT', '(X-GM-LABELS', '"processed"']
msg = filter(lambda m: "processed" not in m.flags, msg)
@ -205,15 +214,21 @@ class TestMail(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
body: str = "",
subject: str = "the suject",
from_: str = "noone@mail.com",
to: Optional[List[str]] = None,
seen: bool = False,
flagged: bool = False,
processed: bool = False,
) -> MailMessage:
if to is None:
to = ["tosomeone@somewhere.com"]
email_msg = email.message.EmailMessage()
# TODO: This does NOT set the UID
email_msg["Message-ID"] = str(uuid.uuid4())
email_msg["Subject"] = subject
email_msg["From"] = from_
email_msg["To"] = str(" ,".join(to))
email_msg.set_content(body)
# Either add some default number of attachments
@ -266,6 +281,7 @@ class TestMail(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
self.create_message(
subject="Invoice 1",
from_="amazon@amazon.de",
to=["me@myselfandi.com", "helpdesk@mydomain.com"],
body="cables",
seen=True,
flagged=False,
@ -276,6 +292,7 @@ class TestMail(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
self.create_message(
subject="Invoice 2",
body="from my favorite electronic store",
to=["invoices@mycompany.com"],
seen=False,
flagged=True,
processed=True,
@ -285,6 +302,7 @@ class TestMail(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
self.create_message(
subject="Claim your $10M price now!",
from_="amazon@amazon-some-indian-site.org",
to="special@me.me",
seen=False,
),
)
@ -946,21 +964,26 @@ class TestMail(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
password="secret",
)
for (f_body, f_from, f_subject, expected_mail_count) in [
(None, None, "Claim", 1),
("electronic", None, None, 1),
(None, "amazon", None, 2),
("cables", "amazon", "Invoice", 1),
for (f_body, f_from, f_to, f_subject, expected_mail_count) in [
(None, None, None, "Claim", 1),
("electronic", None, None, None, 1),
(None, "amazon", None, None, 2),
("cables", "amazon", None, "Invoice", 1),
(None, None, "test@email.com", None, 0),
(None, None, "invoices@mycompany.com", None, 1),
("electronic", None, "invoices@mycompany.com", None, 1),
(None, "amazon", "me@myselfandi.com", None, 1),
]:
with self.subTest(f_body=f_body, f_from=f_from, f_subject=f_subject):
MailRule.objects.all().delete()
rule = MailRule.objects.create(
_ = MailRule.objects.create(
name="testrule3",
account=account,
action=MailRule.MailAction.DELETE,
filter_subject=f_subject,
filter_body=f_body,
filter_from=f_from,
filter_to=f_to,
)
self.reset_bogus_mailbox()
self._queue_consumption_tasks_mock.reset_mock()