267 lines
9.4 KiB
Python
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',)
|
|
}),
|
|
)
|