mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Allows filtering email by the TO value(s) as well
This commit is contained in:
		| @@ -9,6 +9,7 @@ | ||||
|             "account": 2, | ||||
|             "folder": "INBOX", | ||||
|             "filter_from": null, | ||||
|             "filter_to": null, | ||||
|             "filter_subject": "[paperless]", | ||||
|             "filter_body": null, | ||||
|             "filter_attachment_filename": null, | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -42,6 +42,8 @@ export interface PaperlessMailRule extends ObjectWithId { | ||||
|  | ||||
|   filter_from: string | ||||
|  | ||||
|   filter_to: string | ||||
|  | ||||
|   filter_subject: string | ||||
|  | ||||
|   filter_body: string | ||||
|   | ||||
| @@ -53,6 +53,7 @@ class MailRuleAdmin(admin.ModelAdmin): | ||||
|                 ), | ||||
|                 "fields": ( | ||||
|                     "filter_from", | ||||
|                     "filter_to", | ||||
|                     "filter_subject", | ||||
|                     "filter_body", | ||||
|                     "filter_attachment_filename", | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
							
								
								
									
										19
									
								
								src/paperless_mail/migrations/0019_mailrule_filter_to.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/paperless_mail/migrations/0019_mailrule_filter_to.py
									
									
									
									
									
										Normal 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" | ||||
|             ), | ||||
|         ), | ||||
|     ] | ||||
| @@ -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, | ||||
|   | ||||
| @@ -70,6 +70,7 @@ class MailRuleSerializer(OwnedObjectSerializer): | ||||
|             "account", | ||||
|             "folder", | ||||
|             "filter_from", | ||||
|             "filter_to", | ||||
|             "filter_subject", | ||||
|             "filter_body", | ||||
|             "filter_attachment_filename", | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Trenton H
					Trenton H