from django.contrib import admin from django.contrib import messages from .models import ( Case, CaseDetail, CaseCollection, NormalCase, Differential, Series, SeriesImage, Condition, ConditionRelationship, Subspecialty, Finding, SeriesFinding, Structure, PathologicalProcess, Presentation, Procedure, UncategorisedDicom, SeriesDetail, Resource, CaseDisplaySet, UserReportAnswer, CidReportAnswer, APIToken, ) from django.forms import ModelForm from reversion.admin import VersionAdmin from django.forms.widgets import RadioSelect from django.utils.html import format_html from django.urls import reverse from django.utils import timezone from datetime import timedelta from tinymce.widgets import TinyMCE # from generic.models import Examination, Sign, Site, Condition from generic.models import Examination try: from django_tasks_db.models import DBTaskResult except ImportError: DBTaskResult = None # Register your models here. # admin.site.register(Examination) # admin.site.register(Site) admin.site.register(SeriesImage) admin.site.register(Condition) admin.site.register(ConditionRelationship) admin.site.register(Subspecialty) admin.site.register(Finding) admin.site.register(Structure) admin.site.register(SeriesFinding) admin.site.register(Differential) admin.site.register(PathologicalProcess) admin.site.register(Presentation) admin.site.register(CaseDetail) admin.site.register(UncategorisedDicom) admin.site.register(SeriesDetail) admin.site.register(Resource) admin.site.register(CaseDisplaySet) admin.site.register(Procedure) admin.site.register(UserReportAnswer) admin.site.register(CidReportAnswer) class DifferentialInline(admin.TabularInline): model = Differential class CasesInline(admin.TabularInline): model = CaseDetail class CaseCollectionAdminForm(ModelForm): class Meta: model = CaseCollection exclude = [] class CaseCollectionAdmin(VersionAdmin): form = CaseCollectionAdminForm save_on_top = True save_as = True view_on_site = True inlines = (CasesInline,) admin.site.register(CaseCollection, CaseCollectionAdmin) class NormalCaseAdmin(VersionAdmin): list_display = ("case", "display_age", "examination", "modality", "added_date") list_filter = ("examination", "modality") search_fields = ("case__title", "case__pk") def display_age(self, obj): try: return obj.display_age() except Exception: return "" display_age.short_description = "Age" admin.site.register(NormalCase, NormalCaseAdmin) class AtlasAdminForm(ModelForm): class Meta: model = Case fields = [ "title", "description", "history", "discussion", "condition", "differential", "subspecialty", "author", "series", "cimar_uuid" ] widgets = { # "normal": RadioSelect( # choices=[(True, 'Yes'), # (False, 'No')]) "history": TinyMCE(attrs={"cols": 80, "rows": 30}), "discussion": TinyMCE(attrs={"cols": 80, "rows": 30}), } class AtlasAdmin(VersionAdmin): form = AtlasAdminForm save_on_top = True save_as = True view_on_site = True inlines = (DifferentialInline,) admin.site.register(Case, AtlasAdmin) class SeriesAdmin(VersionAdmin): pass admin.site.register(Series, SeriesAdmin) class APITokenAdmin(admin.ModelAdmin): list_display = ("user", "name", "revoked", "created", "expires", "last_used") search_fields = ("user__username", "name") readonly_fields = ("token_hash", "created", "last_used") admin.site.register(APIToken, APITokenAdmin) if DBTaskResult is not None: try: # Ensure package admin registration has run before we customize it. import django_tasks_db.admin # noqa: F401 except Exception: pass def traceback_link(self, obj): if not obj.traceback: return "-" url = reverse( f"admin:{obj._meta.app_label}_{obj._meta.model_name}_change", args=[obj.pk], ) return format_html('View full traceback', url) traceback_link.short_description = "Traceback" def traceback_pretty(self, obj): if not obj.traceback: return "" return format_html( '
{}
', obj.traceback, ) traceback_pretty.short_description = "Traceback" @admin.action(description="Reset FAILED tasks to READY") def reset_failed_to_ready(self, request, queryset): failed_qs = queryset.filter(status="FAILED") updated = failed_qs.update( status="READY", started_at=None, finished_at=None, worker_ids="", exception_class_path="", traceback="", return_value=None, ) self.message_user( request, f"Reset {updated} failed task(s) to READY.", level=messages.SUCCESS, ) @admin.action(description="Reset selected tasks to READY (except RUNNING)") def reset_selected_to_ready(self, request, queryset): blocked = queryset.filter(status="RUNNING").count() reset_qs = queryset.exclude(status="RUNNING") updated = reset_qs.update( status="READY", started_at=None, finished_at=None, worker_ids="", exception_class_path="", traceback="", return_value=None, ) msg = f"Reset {updated} task(s) to READY." if blocked: msg += f" Skipped {blocked} running task(s)." self.message_user(request, msg, level=messages.SUCCESS) @admin.action(description="Reset stale RUNNING tasks to READY (>30 min)") def reset_stale_running_to_ready(self, request, queryset): cutoff = timezone.now() - timedelta(minutes=30) updated = queryset.filter(status="RUNNING", started_at__lt=cutoff).update( status="READY", started_at=None, finished_at=None, worker_ids="", exception_class_path="", traceback="", return_value=None, ) self.message_user( request, f"Reset {updated} stale RUNNING task(s) to READY (older than 30 minutes).", level=messages.SUCCESS, ) @admin.action(description="Delete selected COMPLETED tasks") def delete_selected_completed(self, request, queryset): completed_qs = queryset.filter(status="SUCCESSFUL") deleted, _ = completed_qs.delete() self.message_user( request, f"Deleted {deleted} completed task record(s).", level=messages.SUCCESS, ) @admin.action(description="Delete selected FAILED tasks") def delete_selected_failed(self, request, queryset): failed_qs = queryset.filter(status="FAILED") deleted, _ = failed_qs.delete() self.message_user( request, f"Deleted {deleted} failed task record(s).", level=messages.SUCCESS, ) def has_add_permission(self, request): return False task_admin = admin.site._registry.get(DBTaskResult) if task_admin is not None: admin_cls = task_admin.__class__ admin_cls.list_display = ( "id", "task_path", "status", "queue_name", "priority", "enqueued_at", "started_at", "finished_at", "worker_ids", "exception_class_path", "traceback_link", ) admin_cls.list_filter = ("status", "queue_name", "task_path", "backend_name") admin_cls.search_fields = ( "id", "task_path", "queue_name", "worker_ids", "exception_class_path", "traceback", ) admin_cls.ordering = ("-enqueued_at",) admin_cls.actions = ( "reset_failed_to_ready", "reset_selected_to_ready", "reset_stale_running_to_ready", "delete_selected_completed", "delete_selected_failed", ) admin_cls.readonly_fields = ( "id", "status", "enqueued_at", "started_at", "finished_at", "args_kwargs", "priority", "task_path", "worker_ids", "queue_name", "backend_name", "run_after", "return_value", "exception_class_path", "traceback_pretty", ) admin_cls.fieldsets = ( ( "Task", { "fields": ( "id", "task_path", "status", "queue_name", "priority", "backend_name", ) }, ), ( "Timing", { "fields": ( "enqueued_at", "run_after", "started_at", "finished_at", "worker_ids", ) }, ), ( "Payload", {"fields": ("args_kwargs", "return_value")}, ), ( "Error", { "fields": ("exception_class_path", "traceback_pretty"), }, ), ) admin_cls.traceback_link = traceback_link admin_cls.traceback_pretty = traceback_pretty admin_cls.reset_failed_to_ready = reset_failed_to_ready admin_cls.reset_selected_to_ready = reset_selected_to_ready admin_cls.reset_stale_running_to_ready = reset_stale_running_to_ready admin_cls.delete_selected_completed = delete_selected_completed admin_cls.delete_selected_failed = delete_selected_failed admin_cls.has_add_permission = has_add_permission