Coverage for documents/admin.py: 77%
144 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 09:07 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 09:07 +0000
1from django.conf import settings
2from django.contrib import admin
3from guardian.admin import GuardedModelAdmin
4from treenode.admin import TreeNodeModelAdmin
6from documents.models import Correspondent
7from documents.models import CustomField
8from documents.models import CustomFieldInstance
9from documents.models import Document
10from documents.models import DocumentType
11from documents.models import Note
12from documents.models import PaperlessTask
13from documents.models import SavedView
14from documents.models import SavedViewFilterRule
15from documents.models import ShareLink
16from documents.models import ShareLinkBundle
17from documents.models import StoragePath
18from documents.models import Tag
19from documents.tasks import update_document_parent_tags
21if settings.AUDIT_LOG_ENABLED: 21 ↛ 26line 21 didn't jump to line 26 because the condition on line 21 was always true
22 from auditlog.admin import LogEntryAdmin
23 from auditlog.models import LogEntry
26class CorrespondentAdmin(GuardedModelAdmin):
27 list_display = ("name", "match", "matching_algorithm")
28 list_filter = ("matching_algorithm",)
29 list_editable = ("match", "matching_algorithm")
32class TagAdmin(GuardedModelAdmin, TreeNodeModelAdmin):
33 list_display = ("name", "color", "match", "matching_algorithm")
34 list_filter = ("matching_algorithm",)
35 list_editable = ("color", "match", "matching_algorithm")
36 search_fields = ("color", "name")
38 def save_model(self, request, obj, form, change):
39 old_parent = None
40 if change and obj.pk:
41 tag = Tag.objects.get(pk=obj.pk)
42 old_parent = tag.get_parent() if tag else None
44 super().save_model(request, obj, form, change)
46 # sync parent tags on documents if changed
47 new_parent = obj.get_parent()
48 if new_parent and old_parent != new_parent:
49 update_document_parent_tags(obj, new_parent)
52class DocumentTypeAdmin(GuardedModelAdmin):
53 list_display = ("name", "match", "matching_algorithm")
54 list_filter = ("matching_algorithm",)
55 list_editable = ("match", "matching_algorithm")
58class DocumentAdmin(GuardedModelAdmin):
59 search_fields = ("correspondent__name", "title", "content", "tags__name")
60 readonly_fields = (
61 "added",
62 "modified",
63 "mime_type",
64 "filename",
65 "checksum",
66 "archive_filename",
67 "archive_checksum",
68 "original_filename",
69 "deleted_at",
70 )
72 list_display_links = ("title",)
74 list_display = ("id", "title", "mime_type", "filename", "archive_filename")
76 list_filter = (
77 ("mime_type"),
78 ("archive_serial_number", admin.EmptyFieldListFilter),
79 ("archive_filename", admin.EmptyFieldListFilter),
80 )
82 filter_horizontal = ("tags",)
84 ordering = ["-id"]
86 date_hierarchy = "created"
88 def has_add_permission(self, request):
89 return False
91 def created_(self, obj):
92 return obj.created.date().strftime("%Y-%m-%d")
94 created_.short_description = "Created"
96 def get_queryset(self, request): # pragma: no cover
97 """
98 Include trashed documents
99 """
100 return Document.global_objects.all()
102 def delete_queryset(self, request, queryset):
103 from documents.search import get_backend
105 with get_backend().batch_update() as batch:
106 for o in queryset:
107 batch.remove(o.pk)
108 super().delete_queryset(request, queryset)
110 def delete_model(self, request, obj):
111 from documents.search import get_backend
113 get_backend().remove(obj.pk)
114 super().delete_model(request, obj)
116 def save_model(self, request, obj, form, change):
117 from documents.search import get_backend
119 get_backend().add_or_update(obj)
120 super().save_model(request, obj, form, change)
123class RuleInline(admin.TabularInline):
124 model = SavedViewFilterRule
127class SavedViewAdmin(GuardedModelAdmin):
128 list_display = ("name", "owner")
130 inlines = [RuleInline]
132 def get_queryset(self, request): # pragma: no cover
133 return super().get_queryset(request).select_related("owner")
136class StoragePathInline(admin.TabularInline):
137 model = StoragePath
140class StoragePathAdmin(GuardedModelAdmin):
141 list_display = ("name", "path", "match", "matching_algorithm")
142 list_filter = ("path", "matching_algorithm")
143 list_editable = ("path", "match", "matching_algorithm")
146class TaskAdmin(admin.ModelAdmin):
147 list_display = (
148 "task_id",
149 "task_type",
150 "trigger_source",
151 "status",
152 "date_created",
153 "date_done",
154 "duration_seconds",
155 )
156 list_filter = ("status", "task_type", "trigger_source", "date_done")
157 search_fields = ("task_id", "task_type", "status")
158 readonly_fields = (
159 "task_id",
160 "task_type",
161 "trigger_source",
162 "status",
163 "date_created",
164 "date_started",
165 "date_done",
166 "duration_seconds",
167 "wait_time_seconds",
168 "input_data",
169 "result_data",
170 )
173class NotesAdmin(GuardedModelAdmin):
174 list_display = ("user", "created", "note", "document")
175 list_filter = ("created", "user")
176 list_display_links = ("created",)
177 raw_id_fields = ("document",)
178 search_fields = ("document__title",)
180 def get_queryset(self, request): # pragma: no cover
181 return (
182 super()
183 .get_queryset(request)
184 .select_related("user", "document__correspondent")
185 )
188class ShareLinksAdmin(GuardedModelAdmin):
189 list_display = ("created", "expiration", "document")
190 list_filter = ("created", "expiration", "owner")
191 list_display_links = ("created",)
192 raw_id_fields = ("document",)
194 def get_queryset(self, request): # pragma: no cover
195 return super().get_queryset(request).select_related("document__correspondent")
198class ShareLinkBundleAdmin(GuardedModelAdmin):
199 list_display = ("created", "status", "expiration", "owner", "slug")
200 list_filter = ("status", "created", "expiration", "owner")
201 readonly_fields = ("file_path",)
202 search_fields = ("slug",)
204 def get_queryset(self, request): # pragma: no cover
205 return (
206 super()
207 .get_queryset(request)
208 .select_related("owner")
209 .prefetch_related(
210 "documents",
211 )
212 )
215class CustomFieldsAdmin(GuardedModelAdmin):
216 fields = ("name", "created", "data_type")
217 readonly_fields = ("created", "data_type")
218 list_display = ("name", "created", "data_type")
219 list_filter = ("created", "data_type")
222class CustomFieldInstancesAdmin(GuardedModelAdmin):
223 fields = ("field", "document", "created", "value")
224 readonly_fields = ("field", "document", "created", "value")
225 list_display = ("field", "document", "value", "created")
226 search_fields = ("document__title",)
227 list_filter = ("created", "field")
229 def get_queryset(self, request): # pragma: no cover
230 return (
231 super()
232 .get_queryset(request)
233 .select_related("field", "document__correspondent")
234 )
237admin.site.register(Correspondent, CorrespondentAdmin)
238admin.site.register(Tag, TagAdmin)
239admin.site.register(DocumentType, DocumentTypeAdmin)
240admin.site.register(Document, DocumentAdmin)
241admin.site.register(SavedView, SavedViewAdmin)
242admin.site.register(StoragePath, StoragePathAdmin)
243admin.site.register(PaperlessTask, TaskAdmin)
244admin.site.register(Note, NotesAdmin)
245admin.site.register(ShareLink, ShareLinksAdmin)
246admin.site.register(ShareLinkBundle, ShareLinkBundleAdmin)
247admin.site.register(CustomField, CustomFieldsAdmin)
248admin.site.register(CustomFieldInstance, CustomFieldInstancesAdmin)
250if settings.AUDIT_LOG_ENABLED: 250 ↛ exitline 250 didn't exit the module because the condition on line 250 was always true
252 class LogEntryAUDIT(LogEntryAdmin):
253 def has_delete_permission(self, request, obj=None):
254 return False
256 admin.site.unregister(LogEntry)
257 admin.site.register(LogEntry, LogEntryAUDIT)