agdar/mdt/admin.py
Marwan Alwali 2f1681b18c update
2025-11-11 13:44:48 +03:00

267 lines
9.4 KiB
Python

"""
Django admin configuration for MDT app.
"""
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from django.utils.html import format_html
from simple_history.admin import SimpleHistoryAdmin
from .models import (
MDTNote,
MDTContribution,
MDTApproval,
MDTMention,
MDTAttachment,
)
class MDTContributionInline(admin.TabularInline):
"""Inline admin for MDT contributions."""
model = MDTContribution
extra = 0
fields = ['contributor', 'clinic', 'content', 'is_final', 'edited_at']
readonly_fields = ['edited_at']
def has_delete_permission(self, request, obj=None):
"""Only allow deletion if MDT note is not finalized."""
if obj and obj.mdt_note.status == MDTNote.Status.FINALIZED:
return False
return True
class MDTApprovalInline(admin.TabularInline):
"""Inline admin for MDT approvals."""
model = MDTApproval
extra = 0
fields = ['approver', 'clinic', 'approved', 'approved_at', 'comments']
readonly_fields = ['approved_at']
class MDTAttachmentInline(admin.TabularInline):
"""Inline admin for MDT attachments."""
model = MDTAttachment
extra = 0
fields = ['file', 'file_type', 'description', 'uploaded_by']
readonly_fields = ['uploaded_by']
@admin.register(MDTNote)
class MDTNoteAdmin(SimpleHistoryAdmin):
"""Admin interface for MDT Notes."""
list_display = ['title', 'patient', 'status', 'get_contributor_count', 'get_approval_status', 'initiated_by', 'created_at']
list_filter = ['status', 'tenant', 'created_at', 'finalized_at']
search_fields = ['title', 'purpose', 'patient__mrn', 'patient__first_name_en', 'patient__last_name_en']
readonly_fields = ['id', 'version', 'created_at', 'updated_at', 'finalized_at', 'get_approval_status', 'get_contributor_list']
inlines = [MDTContributionInline, MDTApprovalInline, MDTAttachmentInline]
fieldsets = (
(None, {
'fields': ('patient', 'tenant', 'title', 'purpose', 'status')
}),
(_('Contributors'), {
'fields': ('initiated_by', 'get_contributor_list')
}),
(_('Summary & Recommendations'), {
'fields': ('summary', 'recommendations')
}),
(_('Approval Status'), {
'fields': ('get_approval_status', 'finalized_at'),
'classes': ('collapse',)
}),
(_('Metadata'), {
'fields': ('id', 'version', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def get_contributor_count(self, obj):
"""Display number of contributors."""
count = obj.contributions.count()
return format_html(
'<span style="background-color: #17a2b8; color: white; padding: 3px 8px; border-radius: 3px;">{} Contributors</span>',
count
)
get_contributor_count.short_description = _('Contributors')
def get_contributor_list(self, obj):
"""Display list of contributors."""
contributions = obj.contributions.select_related('contributor', 'clinic')
if not contributions:
return '-'
html = '<ul style="margin: 0; padding-left: 20px;">'
for contrib in contributions:
html += f'<li><strong>{contrib.contributor.get_full_name()}</strong> ({contrib.clinic.name_en})</li>'
html += '</ul>'
return format_html(html)
get_contributor_list.short_description = _('Contributor List')
def get_approval_status(self, obj):
"""Display approval status with visual indicators."""
approvals = obj.approvals.filter(approved=True)
total_approvals = approvals.count()
# Check departments
departments = set()
for approval in approvals:
if approval.approver.role:
departments.add(approval.approver.role)
can_finalize = total_approvals >= 2 and len(departments) >= 2
if obj.status == MDTNote.Status.FINALIZED:
return format_html(
'<span style="background-color: #28a745; color: white; padding: 5px 10px; border-radius: 3px; font-weight: bold;">✓ Finalized</span>'
)
elif can_finalize:
return format_html(
'<span style="background-color: #ffc107; color: black; padding: 5px 10px; border-radius: 3px; font-weight: bold;">⚠ Ready to Finalize</span><br/>'
'<small>{} approvals from {} departments</small>',
total_approvals, len(departments)
)
else:
return format_html(
'<span style="background-color: #dc3545; color: white; padding: 5px 10px; border-radius: 3px;">✗ Needs Approval</span><br/>'
'<small>{} approvals from {} departments (need 2 from 2 different)</small>',
total_approvals, len(departments)
)
get_approval_status.short_description = _('Approval Status')
def save_model(self, request, obj, form, change):
"""Set initiated_by on creation."""
if not change:
obj.initiated_by = request.user
super().save_model(request, obj, form, change)
def has_change_permission(self, request, obj=None):
"""Only allow editing if not finalized."""
if obj and obj.status == MDTNote.Status.FINALIZED:
return request.user.is_superuser
return True
@admin.register(MDTContribution)
class MDTContributionAdmin(SimpleHistoryAdmin):
"""Admin interface for MDT Contributions."""
list_display = ['mdt_note', 'contributor', 'clinic', 'is_final', 'created_at', 'edited_at']
list_filter = ['is_final', 'clinic', 'created_at']
search_fields = ['mdt_note__title', 'contributor__first_name', 'contributor__last_name', 'content']
readonly_fields = ['id', 'created_at', 'updated_at', 'edited_at']
filter_horizontal = ['mentioned_users']
fieldsets = (
(None, {
'fields': ('mdt_note', 'contributor', 'clinic', 'is_final')
}),
(_('Content'), {
'fields': ('content',)
}),
(_('Mentions'), {
'fields': ('mentioned_users',),
'classes': ('collapse',)
}),
(_('Metadata'), {
'fields': ('id', 'created_at', 'updated_at', 'edited_at'),
'classes': ('collapse',)
}),
)
def has_change_permission(self, request, obj=None):
"""Only allow editing if MDT note is not finalized."""
if obj and obj.mdt_note.status == MDTNote.Status.FINALIZED:
return request.user.is_superuser
return True
@admin.register(MDTApproval)
class MDTApprovalAdmin(admin.ModelAdmin):
"""Admin interface for MDT Approvals."""
list_display = ['mdt_note', 'approver', 'clinic', 'get_approval_badge', 'approved_at']
list_filter = ['approved', 'clinic', 'approved_at']
search_fields = ['mdt_note__title', 'approver__first_name', 'approver__last_name', 'comments']
readonly_fields = ['id', 'created_at', 'updated_at', 'approved_at']
fieldsets = (
(None, {
'fields': ('mdt_note', 'approver', 'clinic')
}),
(_('Approval'), {
'fields': ('approved', 'approved_at', 'comments')
}),
(_('Metadata'), {
'fields': ('id', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def get_approval_badge(self, obj):
"""Display approval status with badge."""
if obj.approved:
return format_html(
'<span style="background-color: #28a745; color: white; padding: 3px 8px; border-radius: 3px;">✓ Approved</span>'
)
else:
return format_html(
'<span style="background-color: #6c757d; color: white; padding: 3px 8px; border-radius: 3px;">⏳ Pending</span>'
)
get_approval_badge.short_description = _('Status')
@admin.register(MDTMention)
class MDTMentionAdmin(admin.ModelAdmin):
"""Admin interface for MDT Mentions."""
list_display = ['mentioned_user', 'contribution', 'notified_at', 'viewed_at']
list_filter = ['notified_at', 'viewed_at', 'created_at']
search_fields = ['mentioned_user__username', 'mentioned_user__first_name', 'mentioned_user__last_name']
readonly_fields = ['id', 'created_at', 'updated_at', 'notified_at', 'viewed_at']
fieldsets = (
(None, {
'fields': ('contribution', 'mentioned_user')
}),
(_('Tracking'), {
'fields': ('notified_at', 'viewed_at')
}),
(_('Metadata'), {
'fields': ('id', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def has_add_permission(self, request):
"""Mentions should be created via the system."""
return False
@admin.register(MDTAttachment)
class MDTAttachmentAdmin(admin.ModelAdmin):
"""Admin interface for MDT Attachments."""
list_display = ['file', 'file_type', 'mdt_note', 'uploaded_by', 'created_at']
list_filter = ['file_type', 'created_at']
search_fields = ['description', 'mdt_note__title']
readonly_fields = ['id', 'created_at', 'updated_at']
fieldsets = (
(None, {
'fields': ('mdt_note', 'file', 'file_type', 'description', 'uploaded_by')
}),
(_('Metadata'), {
'fields': ('id', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)