We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG ]At new splitting: "QuerySet' object has no attribute 'extend'" - Workflow adding a custom field on adding new document leads to the error message
### Description

When trying out the new splitting functionality I get the error message: 

  'QuerySet' object has no attribute 'extend'

A Workflow for adding a custom field on adding new document leads to the error message.

Anybody with the same issue?


### Steps to reproduce

1. Go to Documents
2. Edit Document
3. Split
4. Split Pages 1 and 2
5. Error message

### Webserver logs

```bash
[2024-04-06 11:18:26,873] [DEBUG] [paperless.tasks] Executing plugin WorkflowTriggerPlugin

[2024-04-06 11:18:26,905] [INFO] [paperless.matching] Document matched WorkflowTrigger 3 from Workflow: Benutzerdefinierte Felder hinzufügen

[2024-04-06 11:18:26,910] [ERROR] [paperless.tasks] WorkflowTriggerPlugin failed: 'QuerySet' object has no attribute 'extend'

Traceback (most recent call last):

  File "/usr/src/paperless/src/documents/tasks.py", line 144, in consume_file

    msg = plugin.run()

          ^^^^^^^^^^^^

  File "/usr/src/paperless/src/documents/consumer.py", line 223, in run

    self.metadata.update(overrides)

  File "/usr/src/paperless/src/documents/data_models.py", line 64, in update

    self.view_users.extend(other.view_users)

    ^^^^^^^^^^^^^^^^^^^^^^

AttributeError: 'QuerySet' object has no attribute 'extend'
```


### Browser logs

_No response_

### Paperless-ngx version

2.7.0

### Host OS

Docker on Synology NAS - DSM 7.2

### Installation method

Docker - official image

### Browser

Firefox

### Configuration changes

_No response_

### Other

_No response_

### Please confirm the following

- [X] I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation.
- [X] I have already searched for relevant existing issues and discussions before opening this report.
- [X] I have updated the title field above with a concise description.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/documents/data_models.py`
Content:
```
1 import dataclasses
2 import datetime
3 from enum import IntEnum
4 from pathlib import Path
5 from typing import Optional
6 
7 import magic
8 from guardian.shortcuts import get_groups_with_perms
9 from guardian.shortcuts import get_users_with_perms
10 
11 
12 @dataclasses.dataclass
13 class DocumentMetadataOverrides:
14     """
15     Manages overrides for document fields which normally would
16     be set from content or matching.  All fields default to None,
17     meaning no override is happening
18     """
19 
20     filename: Optional[str] = None
21     title: Optional[str] = None
22     correspondent_id: Optional[int] = None
23     document_type_id: Optional[int] = None
24     tag_ids: Optional[list[int]] = None
25     storage_path_id: Optional[int] = None
26     created: Optional[datetime.datetime] = None
27     asn: Optional[int] = None
28     owner_id: Optional[int] = None
29     view_users: Optional[list[int]] = None
30     view_groups: Optional[list[int]] = None
31     change_users: Optional[list[int]] = None
32     change_groups: Optional[list[int]] = None
33     custom_field_ids: Optional[list[int]] = None
34 
35     def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides":
36         """
37         Merges two DocumentMetadataOverrides objects such that object B's overrides
38         are applied to object A or merged if multiple are accepted.
39 
40         The update is an in-place modification of self
41         """
42         # only if empty
43         if other.title is not None:
44             self.title = other.title
45         if other.correspondent_id is not None:
46             self.correspondent_id = other.correspondent_id
47         if other.document_type_id is not None:
48             self.document_type_id = other.document_type_id
49         if other.storage_path_id is not None:
50             self.storage_path_id = other.storage_path_id
51         if other.owner_id is not None:
52             self.owner_id = other.owner_id
53 
54         # merge
55         if self.tag_ids is None:
56             self.tag_ids = other.tag_ids
57         elif other.tag_ids is not None:
58             self.tag_ids.extend(other.tag_ids)
59             self.tag_ids = list(set(self.tag_ids))
60 
61         if self.view_users is None:
62             self.view_users = other.view_users
63         elif other.view_users is not None:
64             self.view_users.extend(other.view_users)
65             self.view_users = list(set(self.view_users))
66 
67         if self.view_groups is None:
68             self.view_groups = other.view_groups
69         elif other.view_groups is not None:
70             self.view_groups.extend(other.view_groups)
71             self.view_groups = list(set(self.view_groups))
72 
73         if self.change_users is None:
74             self.change_users = other.change_users
75         elif other.change_users is not None:
76             self.change_users.extend(other.change_users)
77             self.change_users = list(set(self.change_users))
78 
79         if self.change_groups is None:
80             self.change_groups = other.change_groups
81         elif other.change_groups is not None:
82             self.change_groups.extend(other.change_groups)
83             self.change_groups = list(set(self.change_groups))
84 
85         if self.custom_field_ids is None:
86             self.custom_field_ids = other.custom_field_ids
87         elif other.custom_field_ids is not None:
88             self.custom_field_ids.extend(other.custom_field_ids)
89             self.custom_field_ids = list(set(self.custom_field_ids))
90 
91         return self
92 
93     @staticmethod
94     def from_document(doc) -> "DocumentMetadataOverrides":
95         """
96         Fills in the overrides from a document object
97         """
98         overrides = DocumentMetadataOverrides()
99         overrides.title = doc.title
100         overrides.correspondent_id = doc.correspondent.id if doc.correspondent else None
101         overrides.document_type_id = doc.document_type.id if doc.document_type else None
102         overrides.storage_path_id = doc.storage_path.id if doc.storage_path else None
103         overrides.owner_id = doc.owner.id if doc.owner else None
104         overrides.tag_ids = list(doc.tags.values_list("id", flat=True))
105 
106         overrides.view_users = get_users_with_perms(
107             doc,
108             only_with_perms_in=["view_document"],
109         ).values_list("id", flat=True)
110         overrides.change_users = get_users_with_perms(
111             doc,
112             only_with_perms_in=["change_document"],
113         ).values_list("id", flat=True)
114         overrides.custom_field_ids = list(
115             doc.custom_fields.values_list("id", flat=True),
116         )
117 
118         groups_with_perms = get_groups_with_perms(
119             doc,
120             attach_perms=True,
121         )
122         overrides.view_groups = [
123             group.id for group, perms in groups_with_perms if "view_document" in perms
124         ]
125         overrides.change_groups = [
126             group.id for group, perms in groups_with_perms if "change_document" in perms
127         ]
128 
129         return overrides
130 
131 
132 class DocumentSource(IntEnum):
133     """
134     The source of an incoming document.  May have other uses in the future
135     """
136 
137     ConsumeFolder = 1
138     ApiUpload = 2
139     MailFetch = 3
140 
141 
142 @dataclasses.dataclass
143 class ConsumableDocument:
144     """
145     Encapsulates an incoming document, either from consume folder, API upload
146     or mail fetching and certain useful operations on it.
147     """
148 
149     source: DocumentSource
150     original_file: Path
151     mailrule_id: Optional[int] = None
152     mime_type: str = dataclasses.field(init=False, default=None)
153 
154     def __post_init__(self):
155         """
156         After a dataclass is initialized, this is called to finalize some data
157         1. Make sure the original path is an absolute, fully qualified path
158         2. Get the mime type of the file
159         """
160         # Always fully qualify the path first thing
161         # Just in case, convert to a path if it's a str
162         self.original_file = Path(self.original_file).resolve()
163 
164         # Get the file type once at init
165         # Note this function isn't called when the object is unpickled
166         self.mime_type = magic.from_file(self.original_file, mime=True)
167
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/documents/data_models.py b/src/documents/data_models.py
--- a/src/documents/data_models.py
+++ b/src/documents/data_models.py
@@ -103,14 +103,18 @@
         overrides.owner_id = doc.owner.id if doc.owner else None
         overrides.tag_ids = list(doc.tags.values_list("id", flat=True))
 
-        overrides.view_users = get_users_with_perms(
-            doc,
-            only_with_perms_in=["view_document"],
-        ).values_list("id", flat=True)
-        overrides.change_users = get_users_with_perms(
-            doc,
-            only_with_perms_in=["change_document"],
-        ).values_list("id", flat=True)
+        overrides.view_users = list(
+            get_users_with_perms(
+                doc,
+                only_with_perms_in=["view_document"],
+            ).values_list("id", flat=True),
+        )
+        overrides.change_users = list(
+            get_users_with_perms(
+                doc,
+                only_with_perms_in=["change_document"],
+            ).values_list("id", flat=True),
+        )
         overrides.custom_field_ids = list(
             doc.custom_fields.values_list("id", flat=True),
         )
@@ -120,10 +124,14 @@
             attach_perms=True,
         )
         overrides.view_groups = [
-            group.id for group, perms in groups_with_perms if "view_document" in perms
+            group.id
+            for group in groups_with_perms
+            if "view_document" in groups_with_perms[group]
         ]
         overrides.change_groups = [
-            group.id for group, perms in groups_with_perms if "change_document" in perms
+            group.id
+            for group in groups_with_perms
+            if "change_document" in groups_with_perms[group]
         ]
 
         return overrides

{"golden_diff": "diff --git a/src/documents/data_models.py b/src/documents/data_models.py\n--- a/src/documents/data_models.py\n+++ b/src/documents/data_models.py\n@@ -103,14 +103,18 @@\n         overrides.owner_id = doc.owner.id if doc.owner else None\n         overrides.tag_ids = list(doc.tags.values_list(\"id\", flat=True))\n \n-        overrides.view_users = get_users_with_perms(\n-            doc,\n-            only_with_perms_in=[\"view_document\"],\n-        ).values_list(\"id\", flat=True)\n-        overrides.change_users = get_users_with_perms(\n-            doc,\n-            only_with_perms_in=[\"change_document\"],\n-        ).values_list(\"id\", flat=True)\n+        overrides.view_users = list(\n+            get_users_with_perms(\n+                doc,\n+                only_with_perms_in=[\"view_document\"],\n+            ).values_list(\"id\", flat=True),\n+        )\n+        overrides.change_users = list(\n+            get_users_with_perms(\n+                doc,\n+                only_with_perms_in=[\"change_document\"],\n+            ).values_list(\"id\", flat=True),\n+        )\n         overrides.custom_field_ids = list(\n             doc.custom_fields.values_list(\"id\", flat=True),\n         )\n@@ -120,10 +124,14 @@\n             attach_perms=True,\n         )\n         overrides.view_groups = [\n-            group.id for group, perms in groups_with_perms if \"view_document\" in perms\n+            group.id\n+            for group in groups_with_perms\n+            if \"view_document\" in groups_with_perms[group]\n         ]\n         overrides.change_groups = [\n-            group.id for group, perms in groups_with_perms if \"change_document\" in perms\n+            group.id\n+            for group in groups_with_perms\n+            if \"change_document\" in groups_with_perms[group]\n         ]\n \n         return overrides\n", "issue": "[BUG ]At new splitting: \"QuerySet' object has no attribute 'extend'\" - Workflow adding a custom field on adding new document leads to the error message\n### Description\n\nWhen trying out the new splitting functionality I get the error message: \r\n\r\n  'QuerySet' object has no attribute 'extend'\r\n\r\nA Workflow for adding a custom field on adding new document leads to the error message.\r\n\r\nAnybody with the same issue?\r\n\n\n### Steps to reproduce\n\n1. Go to Documents\r\n2. Edit Document\r\n3. Split\r\n4. Split Pages 1 and 2\r\n5. Error message\n\n### Webserver logs\n\n```bash\n[2024-04-06 11:18:26,873] [DEBUG] [paperless.tasks] Executing plugin WorkflowTriggerPlugin\r\n\r\n[2024-04-06 11:18:26,905] [INFO] [paperless.matching] Document matched WorkflowTrigger 3 from Workflow: Benutzerdefinierte Felder hinzuf\u00fcgen\r\n\r\n[2024-04-06 11:18:26,910] [ERROR] [paperless.tasks] WorkflowTriggerPlugin failed: 'QuerySet' object has no attribute 'extend'\r\n\r\nTraceback (most recent call last):\r\n\r\n  File \"/usr/src/paperless/src/documents/tasks.py\", line 144, in consume_file\r\n\r\n    msg = plugin.run()\r\n\r\n          ^^^^^^^^^^^^\r\n\r\n  File \"/usr/src/paperless/src/documents/consumer.py\", line 223, in run\r\n\r\n    self.metadata.update(overrides)\r\n\r\n  File \"/usr/src/paperless/src/documents/data_models.py\", line 64, in update\r\n\r\n    self.view_users.extend(other.view_users)\r\n\r\n    ^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAttributeError: 'QuerySet' object has no attribute 'extend'\n```\n\n\n### Browser logs\n\n_No response_\n\n### Paperless-ngx version\n\n2.7.0\n\n### Host OS\n\nDocker on Synology NAS - DSM 7.2\n\n### Installation method\n\nDocker - official image\n\n### Browser\n\nFirefox\n\n### Configuration changes\n\n_No response_\n\n### Other\n\n_No response_\n\n### Please confirm the following\n\n- [X] I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation.\n- [X] I have already searched for relevant existing issues and discussions before opening this report.\n- [X] I have updated the title field above with a concise description.\n", "before_files": [{"content": "import dataclasses\nimport datetime\nfrom enum import IntEnum\nfrom pathlib import Path\nfrom typing import Optional\n\nimport magic\nfrom guardian.shortcuts import get_groups_with_perms\nfrom guardian.shortcuts import get_users_with_perms\n\n\n@dataclasses.dataclass\nclass DocumentMetadataOverrides:\n    \"\"\"\n    Manages overrides for document fields which normally would\n    be set from content or matching.  All fields default to None,\n    meaning no override is happening\n    \"\"\"\n\n    filename: Optional[str] = None\n    title: Optional[str] = None\n    correspondent_id: Optional[int] = None\n    document_type_id: Optional[int] = None\n    tag_ids: Optional[list[int]] = None\n    storage_path_id: Optional[int] = None\n    created: Optional[datetime.datetime] = None\n    asn: Optional[int] = None\n    owner_id: Optional[int] = None\n    view_users: Optional[list[int]] = None\n    view_groups: Optional[list[int]] = None\n    change_users: Optional[list[int]] = None\n    change_groups: Optional[list[int]] = None\n    custom_field_ids: Optional[list[int]] = None\n\n    def update(self, other: \"DocumentMetadataOverrides\") -> \"DocumentMetadataOverrides\":\n        \"\"\"\n        Merges two DocumentMetadataOverrides objects such that object B's overrides\n        are applied to object A or merged if multiple are accepted.\n\n        The update is an in-place modification of self\n        \"\"\"\n        # only if empty\n        if other.title is not None:\n            self.title = other.title\n        if other.correspondent_id is not None:\n            self.correspondent_id = other.correspondent_id\n        if other.document_type_id is not None:\n            self.document_type_id = other.document_type_id\n        if other.storage_path_id is not None:\n            self.storage_path_id = other.storage_path_id\n        if other.owner_id is not None:\n            self.owner_id = other.owner_id\n\n        # merge\n        if self.tag_ids is None:\n            self.tag_ids = other.tag_ids\n        elif other.tag_ids is not None:\n            self.tag_ids.extend(other.tag_ids)\n            self.tag_ids = list(set(self.tag_ids))\n\n        if self.view_users is None:\n            self.view_users = other.view_users\n        elif other.view_users is not None:\n            self.view_users.extend(other.view_users)\n            self.view_users = list(set(self.view_users))\n\n        if self.view_groups is None:\n            self.view_groups = other.view_groups\n        elif other.view_groups is not None:\n            self.view_groups.extend(other.view_groups)\n            self.view_groups = list(set(self.view_groups))\n\n        if self.change_users is None:\n            self.change_users = other.change_users\n        elif other.change_users is not None:\n            self.change_users.extend(other.change_users)\n            self.change_users = list(set(self.change_users))\n\n        if self.change_groups is None:\n            self.change_groups = other.change_groups\n        elif other.change_groups is not None:\n            self.change_groups.extend(other.change_groups)\n            self.change_groups = list(set(self.change_groups))\n\n        if self.custom_field_ids is None:\n            self.custom_field_ids = other.custom_field_ids\n        elif other.custom_field_ids is not None:\n            self.custom_field_ids.extend(other.custom_field_ids)\n            self.custom_field_ids = list(set(self.custom_field_ids))\n\n        return self\n\n    @staticmethod\n    def from_document(doc) -> \"DocumentMetadataOverrides\":\n        \"\"\"\n        Fills in the overrides from a document object\n        \"\"\"\n        overrides = DocumentMetadataOverrides()\n        overrides.title = doc.title\n        overrides.correspondent_id = doc.correspondent.id if doc.correspondent else None\n        overrides.document_type_id = doc.document_type.id if doc.document_type else None\n        overrides.storage_path_id = doc.storage_path.id if doc.storage_path else None\n        overrides.owner_id = doc.owner.id if doc.owner else None\n        overrides.tag_ids = list(doc.tags.values_list(\"id\", flat=True))\n\n        overrides.view_users = get_users_with_perms(\n            doc,\n            only_with_perms_in=[\"view_document\"],\n        ).values_list(\"id\", flat=True)\n        overrides.change_users = get_users_with_perms(\n            doc,\n            only_with_perms_in=[\"change_document\"],\n        ).values_list(\"id\", flat=True)\n        overrides.custom_field_ids = list(\n            doc.custom_fields.values_list(\"id\", flat=True),\n        )\n\n        groups_with_perms = get_groups_with_perms(\n            doc,\n            attach_perms=True,\n        )\n        overrides.view_groups = [\n            group.id for group, perms in groups_with_perms if \"view_document\" in perms\n        ]\n        overrides.change_groups = [\n            group.id for group, perms in groups_with_perms if \"change_document\" in perms\n        ]\n\n        return overrides\n\n\nclass DocumentSource(IntEnum):\n    \"\"\"\n    The source of an incoming document.  May have other uses in the future\n    \"\"\"\n\n    ConsumeFolder = 1\n    ApiUpload = 2\n    MailFetch = 3\n\n\n@dataclasses.dataclass\nclass ConsumableDocument:\n    \"\"\"\n    Encapsulates an incoming document, either from consume folder, API upload\n    or mail fetching and certain useful operations on it.\n    \"\"\"\n\n    source: DocumentSource\n    original_file: Path\n    mailrule_id: Optional[int] = None\n    mime_type: str = dataclasses.field(init=False, default=None)\n\n    def __post_init__(self):\n        \"\"\"\n        After a dataclass is initialized, this is called to finalize some data\n        1. Make sure the original path is an absolute, fully qualified path\n        2. Get the mime type of the file\n        \"\"\"\n        # Always fully qualify the path first thing\n        # Just in case, convert to a path if it's a str\n        self.original_file = Path(self.original_file).resolve()\n\n        # Get the file type once at init\n        # Note this function isn't called when the object is unpickled\n        self.mime_type = magic.from_file(self.original_file, mime=True)\n", "path": "src/documents/data_models.py"}], "after_files": [{"content": "import dataclasses\nimport datetime\nfrom enum import IntEnum\nfrom pathlib import Path\nfrom typing import Optional\n\nimport magic\nfrom guardian.shortcuts import get_groups_with_perms\nfrom guardian.shortcuts import get_users_with_perms\n\n\n@dataclasses.dataclass\nclass DocumentMetadataOverrides:\n    \"\"\"\n    Manages overrides for document fields which normally would\n    be set from content or matching.  All fields default to None,\n    meaning no override is happening\n    \"\"\"\n\n    filename: Optional[str] = None\n    title: Optional[str] = None\n    correspondent_id: Optional[int] = None\n    document_type_id: Optional[int] = None\n    tag_ids: Optional[list[int]] = None\n    storage_path_id: Optional[int] = None\n    created: Optional[datetime.datetime] = None\n    asn: Optional[int] = None\n    owner_id: Optional[int] = None\n    view_users: Optional[list[int]] = None\n    view_groups: Optional[list[int]] = None\n    change_users: Optional[list[int]] = None\n    change_groups: Optional[list[int]] = None\n    custom_field_ids: Optional[list[int]] = None\n\n    def update(self, other: \"DocumentMetadataOverrides\") -> \"DocumentMetadataOverrides\":\n        \"\"\"\n        Merges two DocumentMetadataOverrides objects such that object B's overrides\n        are applied to object A or merged if multiple are accepted.\n\n        The update is an in-place modification of self\n        \"\"\"\n        # only if empty\n        if other.title is not None:\n            self.title = other.title\n        if other.correspondent_id is not None:\n            self.correspondent_id = other.correspondent_id\n        if other.document_type_id is not None:\n            self.document_type_id = other.document_type_id\n        if other.storage_path_id is not None:\n            self.storage_path_id = other.storage_path_id\n        if other.owner_id is not None:\n            self.owner_id = other.owner_id\n\n        # merge\n        if self.tag_ids is None:\n            self.tag_ids = other.tag_ids\n        elif other.tag_ids is not None:\n            self.tag_ids.extend(other.tag_ids)\n            self.tag_ids = list(set(self.tag_ids))\n\n        if self.view_users is None:\n            self.view_users = other.view_users\n        elif other.view_users is not None:\n            self.view_users.extend(other.view_users)\n            self.view_users = list(set(self.view_users))\n\n        if self.view_groups is None:\n            self.view_groups = other.view_groups\n        elif other.view_groups is not None:\n            self.view_groups.extend(other.view_groups)\n            self.view_groups = list(set(self.view_groups))\n\n        if self.change_users is None:\n            self.change_users = other.change_users\n        elif other.change_users is not None:\n            self.change_users.extend(other.change_users)\n            self.change_users = list(set(self.change_users))\n\n        if self.change_groups is None:\n            self.change_groups = other.change_groups\n        elif other.change_groups is not None:\n            self.change_groups.extend(other.change_groups)\n            self.change_groups = list(set(self.change_groups))\n\n        if self.custom_field_ids is None:\n            self.custom_field_ids = other.custom_field_ids\n        elif other.custom_field_ids is not None:\n            self.custom_field_ids.extend(other.custom_field_ids)\n            self.custom_field_ids = list(set(self.custom_field_ids))\n\n        return self\n\n    @staticmethod\n    def from_document(doc) -> \"DocumentMetadataOverrides\":\n        \"\"\"\n        Fills in the overrides from a document object\n        \"\"\"\n        overrides = DocumentMetadataOverrides()\n        overrides.title = doc.title\n        overrides.correspondent_id = doc.correspondent.id if doc.correspondent else None\n        overrides.document_type_id = doc.document_type.id if doc.document_type else None\n        overrides.storage_path_id = doc.storage_path.id if doc.storage_path else None\n        overrides.owner_id = doc.owner.id if doc.owner else None\n        overrides.tag_ids = list(doc.tags.values_list(\"id\", flat=True))\n\n        overrides.view_users = list(\n            get_users_with_perms(\n                doc,\n                only_with_perms_in=[\"view_document\"],\n            ).values_list(\"id\", flat=True),\n        )\n        overrides.change_users = list(\n            get_users_with_perms(\n                doc,\n                only_with_perms_in=[\"change_document\"],\n            ).values_list(\"id\", flat=True),\n        )\n        overrides.custom_field_ids = list(\n            doc.custom_fields.values_list(\"id\", flat=True),\n        )\n\n        groups_with_perms = get_groups_with_perms(\n            doc,\n            attach_perms=True,\n        )\n        overrides.view_groups = [\n            group.id\n            for group in groups_with_perms\n            if \"view_document\" in groups_with_perms[group]\n        ]\n        overrides.change_groups = [\n            group.id\n            for group in groups_with_perms\n            if \"change_document\" in groups_with_perms[group]\n        ]\n\n        return overrides\n\n\nclass DocumentSource(IntEnum):\n    \"\"\"\n    The source of an incoming document.  May have other uses in the future\n    \"\"\"\n\n    ConsumeFolder = 1\n    ApiUpload = 2\n    MailFetch = 3\n\n\n@dataclasses.dataclass\nclass ConsumableDocument:\n    \"\"\"\n    Encapsulates an incoming document, either from consume folder, API upload\n    or mail fetching and certain useful operations on it.\n    \"\"\"\n\n    source: DocumentSource\n    original_file: Path\n    mailrule_id: Optional[int] = None\n    mime_type: str = dataclasses.field(init=False, default=None)\n\n    def __post_init__(self):\n        \"\"\"\n        After a dataclass is initialized, this is called to finalize some data\n        1. Make sure the original path is an absolute, fully qualified path\n        2. Get the mime type of the file\n        \"\"\"\n        # Always fully qualify the path first thing\n        # Just in case, convert to a path if it's a str\n        self.original_file = Path(self.original_file).resolve()\n\n        # Get the file type once at init\n        # Note this function isn't called when the object is unpickled\n        self.mime_type = magic.from_file(self.original_file, mime=True)\n", "path": "src/documents/data_models.py"}]}