Coverage for documents/admin.py: 77%

144 statements  

« 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 

5 

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 

20 

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 

24 

25 

26class CorrespondentAdmin(GuardedModelAdmin): 

27 list_display = ("name", "match", "matching_algorithm") 

28 list_filter = ("matching_algorithm",) 

29 list_editable = ("match", "matching_algorithm") 

30 

31 

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") 

37 

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 

43 

44 super().save_model(request, obj, form, change) 

45 

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) 

50 

51 

52class DocumentTypeAdmin(GuardedModelAdmin): 

53 list_display = ("name", "match", "matching_algorithm") 

54 list_filter = ("matching_algorithm",) 

55 list_editable = ("match", "matching_algorithm") 

56 

57 

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 ) 

71 

72 list_display_links = ("title",) 

73 

74 list_display = ("id", "title", "mime_type", "filename", "archive_filename") 

75 

76 list_filter = ( 

77 ("mime_type"), 

78 ("archive_serial_number", admin.EmptyFieldListFilter), 

79 ("archive_filename", admin.EmptyFieldListFilter), 

80 ) 

81 

82 filter_horizontal = ("tags",) 

83 

84 ordering = ["-id"] 

85 

86 date_hierarchy = "created" 

87 

88 def has_add_permission(self, request): 

89 return False 

90 

91 def created_(self, obj): 

92 return obj.created.date().strftime("%Y-%m-%d") 

93 

94 created_.short_description = "Created" 

95 

96 def get_queryset(self, request): # pragma: no cover 

97 """ 

98 Include trashed documents 

99 """ 

100 return Document.global_objects.all() 

101 

102 def delete_queryset(self, request, queryset): 

103 from documents.search import get_backend 

104 

105 with get_backend().batch_update() as batch: 

106 for o in queryset: 

107 batch.remove(o.pk) 

108 super().delete_queryset(request, queryset) 

109 

110 def delete_model(self, request, obj): 

111 from documents.search import get_backend 

112 

113 get_backend().remove(obj.pk) 

114 super().delete_model(request, obj) 

115 

116 def save_model(self, request, obj, form, change): 

117 from documents.search import get_backend 

118 

119 get_backend().add_or_update(obj) 

120 super().save_model(request, obj, form, change) 

121 

122 

123class RuleInline(admin.TabularInline): 

124 model = SavedViewFilterRule 

125 

126 

127class SavedViewAdmin(GuardedModelAdmin): 

128 list_display = ("name", "owner") 

129 

130 inlines = [RuleInline] 

131 

132 def get_queryset(self, request): # pragma: no cover 

133 return super().get_queryset(request).select_related("owner") 

134 

135 

136class StoragePathInline(admin.TabularInline): 

137 model = StoragePath 

138 

139 

140class StoragePathAdmin(GuardedModelAdmin): 

141 list_display = ("name", "path", "match", "matching_algorithm") 

142 list_filter = ("path", "matching_algorithm") 

143 list_editable = ("path", "match", "matching_algorithm") 

144 

145 

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 ) 

171 

172 

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",) 

179 

180 def get_queryset(self, request): # pragma: no cover 

181 return ( 

182 super() 

183 .get_queryset(request) 

184 .select_related("user", "document__correspondent") 

185 ) 

186 

187 

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",) 

193 

194 def get_queryset(self, request): # pragma: no cover 

195 return super().get_queryset(request).select_related("document__correspondent") 

196 

197 

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",) 

203 

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 ) 

213 

214 

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") 

220 

221 

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") 

228 

229 def get_queryset(self, request): # pragma: no cover 

230 return ( 

231 super() 

232 .get_queryset(request) 

233 .select_related("field", "document__correspondent") 

234 ) 

235 

236 

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) 

249 

250if settings.AUDIT_LOG_ENABLED: 250 ↛ exitline 250 didn't exit the module because the condition on line 250 was always true

251 

252 class LogEntryAUDIT(LogEntryAdmin): 

253 def has_delete_permission(self, request, obj=None): 

254 return False 

255 

256 admin.site.unregister(LogEntry) 

257 admin.site.register(LogEntry, LogEntryAUDIT)