HH/apps/appreciation/admin.py
ismail 7369d08012
All checks were successful
Build and Push Docker Image / build (push) Successful in 4m14s
feat: unified reference numbers + feedback modules QA audit
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.
2026-06-14 14:29:23 +03:00

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'