All checks were successful
Build and Push Docker Image / build (push) Successful in 4m14s
Reference numbers (unified scheme PREFIX-YYYYMM-HOSP-NNNN, e.g. CMP-202606-HHN-0001): - new ReferenceSequence model + generate_reference() helper (apps/core) - Complaint/Inquiry/Observation/Appreciation/Suggestion emit unified refs via save() - prefix-based auto-routing in public track API (CMP/INQ/OBS trackable; APR/SGT internal-only) - removed legacy CMP-/INQ- generators in ui_views, integrations, px_sources - migrations: core.0003_referencesequence, appreciation.0006, feedback.0008, observations.0012 - unit tests (format, sanitization, monthly reset, 40-thread concurrency) QA audit: - isolated E2E hospital sandbox mirroring HH-N + 10 role users (create_e2e_isolated_env) - feedback-modules-audit.spec.ts + audit helper (headed, run-to-completion) - reports/feedback-modules-qa-report.md Also bundles accumulated in-progress work across complaints, observations, organizations, templates, and other modules.
240 lines
5.6 KiB
Python
240 lines
5.6 KiB
Python
"""
|
|
Appreciation admin - Django admin interface for appreciation models
|
|
"""
|
|
from django.contrib import admin
|
|
|
|
from apps.appreciation.models import (
|
|
Appreciation,
|
|
AppreciationBadge,
|
|
AppreciationCategory,
|
|
AppreciationStats,
|
|
UserBadge,
|
|
)
|
|
|
|
|
|
@admin.register(AppreciationCategory)
|
|
class AppreciationCategoryAdmin(admin.ModelAdmin):
|
|
"""Admin interface for AppreciationCategory"""
|
|
|
|
list_display = [
|
|
'name_en',
|
|
'code',
|
|
'hospital',
|
|
'order',
|
|
'is_active',
|
|
]
|
|
list_filter = ['hospital', 'is_active']
|
|
search_fields = ['name_en', 'name_ar', 'code']
|
|
list_editable = ['order', 'is_active']
|
|
ordering = ['hospital', 'order', 'name_en']
|
|
|
|
|
|
@admin.register(Appreciation)
|
|
class AppreciationAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Appreciation"""
|
|
|
|
list_display = [
|
|
'recipient_name_display',
|
|
'sender_display',
|
|
'category',
|
|
'status',
|
|
'visibility',
|
|
'hospital',
|
|
'sent_at',
|
|
'created_at',
|
|
]
|
|
list_filter = [
|
|
'status',
|
|
'visibility',
|
|
'hospital',
|
|
'category',
|
|
'is_anonymous',
|
|
'sent_at',
|
|
]
|
|
search_fields = [
|
|
'message_en',
|
|
'message_ar',
|
|
'sender__first_name',
|
|
'sender__last_name',
|
|
'sender__email',
|
|
]
|
|
date_hierarchy = 'sent_at'
|
|
readonly_fields = [
|
|
'created_at',
|
|
'updated_at',
|
|
'sent_at',
|
|
'acknowledged_at',
|
|
'notification_sent_at',
|
|
]
|
|
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': (
|
|
'sender',
|
|
'hospital',
|
|
'department',
|
|
'legacy_location',
|
|
'legacy_main_section',
|
|
'legacy_subsection',
|
|
'section',
|
|
'category',
|
|
)
|
|
}),
|
|
('Recipient', {
|
|
'fields': (
|
|
'recipient_content_type',
|
|
'recipient_object_id',
|
|
)
|
|
}),
|
|
('Content', {
|
|
'fields': (
|
|
'message_en',
|
|
'message_ar',
|
|
)
|
|
}),
|
|
('Settings', {
|
|
'fields': (
|
|
'visibility',
|
|
'is_anonymous',
|
|
)
|
|
}),
|
|
('Status', {
|
|
'fields': (
|
|
'status',
|
|
'sent_at',
|
|
'acknowledged_at',
|
|
'notification_sent',
|
|
'notification_sent_at',
|
|
)
|
|
}),
|
|
('Metadata', {
|
|
'fields': (
|
|
'metadata',
|
|
)
|
|
}),
|
|
('Timestamps', {
|
|
'fields': (
|
|
'created_at',
|
|
'updated_at',
|
|
),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def recipient_name_display(self, obj):
|
|
"""Display recipient name in list"""
|
|
try:
|
|
return str(obj.recipient)
|
|
except:
|
|
return "Unknown"
|
|
recipient_name_display.short_description = 'Recipient'
|
|
|
|
def sender_display(self, obj):
|
|
"""Display sender name in list"""
|
|
if obj.is_anonymous:
|
|
return "Anonymous"
|
|
if obj.sender:
|
|
return obj.sender.get_full_name()
|
|
return "System"
|
|
sender_display.short_description = 'Sender'
|
|
|
|
|
|
@admin.register(AppreciationBadge)
|
|
class AppreciationBadgeAdmin(admin.ModelAdmin):
|
|
"""Admin interface for AppreciationBadge"""
|
|
|
|
list_display = [
|
|
'name_en',
|
|
'code',
|
|
'criteria_type',
|
|
'criteria_value',
|
|
'hospital',
|
|
'order',
|
|
'is_active',
|
|
]
|
|
list_filter = [
|
|
'hospital',
|
|
'criteria_type',
|
|
'is_active',
|
|
]
|
|
search_fields = [
|
|
'name_en',
|
|
'name_ar',
|
|
'code',
|
|
'description_en',
|
|
]
|
|
list_editable = ['order', 'is_active']
|
|
ordering = ['hospital', 'order', 'name_en']
|
|
|
|
|
|
@admin.register(UserBadge)
|
|
class UserBadgeAdmin(admin.ModelAdmin):
|
|
"""Admin interface for UserBadge"""
|
|
|
|
list_display = [
|
|
'recipient_name_display',
|
|
'badge',
|
|
'earned_at',
|
|
'appreciation_count',
|
|
]
|
|
list_filter = [
|
|
'badge',
|
|
'earned_at',
|
|
]
|
|
search_fields = [
|
|
'badge__name_en',
|
|
'badge__name_ar',
|
|
]
|
|
date_hierarchy = 'earned_at'
|
|
readonly_fields = ['earned_at', 'created_at', 'updated_at']
|
|
|
|
def recipient_name_display(self, obj):
|
|
"""Display recipient name in list"""
|
|
try:
|
|
return str(obj.recipient)
|
|
except:
|
|
return "Unknown"
|
|
recipient_name_display.short_description = 'Recipient'
|
|
|
|
|
|
@admin.register(AppreciationStats)
|
|
class AppreciationStatsAdmin(admin.ModelAdmin):
|
|
"""Admin interface for AppreciationStats"""
|
|
|
|
list_display = [
|
|
'recipient_name_display',
|
|
'period_display',
|
|
'hospital',
|
|
'department',
|
|
'received_count',
|
|
'sent_count',
|
|
'acknowledged_count',
|
|
'hospital_rank',
|
|
'department_rank',
|
|
]
|
|
list_filter = [
|
|
'hospital',
|
|
'department',
|
|
'year',
|
|
'month',
|
|
]
|
|
search_fields = []
|
|
date_hierarchy = None
|
|
readonly_fields = [
|
|
'created_at',
|
|
'updated_at',
|
|
]
|
|
|
|
def recipient_name_display(self, obj):
|
|
"""Display recipient name in list"""
|
|
try:
|
|
return str(obj.recipient)
|
|
except:
|
|
return "Unknown"
|
|
recipient_name_display.short_description = 'Recipient'
|
|
|
|
def period_display(self, obj):
|
|
"""Display period in list"""
|
|
return f"{obj.year}-{obj.month:02d}"
|
|
period_display.short_description = 'Period'
|