409 lines
12 KiB
Python
409 lines
12 KiB
Python
"""
|
|
Appreciation models
|
|
"""
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from apps.core.models import BaseModel, PriorityChoices
|
|
|
|
|
|
class AppreciationType(models.TextChoices):
|
|
"""Types of appreciations"""
|
|
STAFF = 'staff', _('Staff Appreciation')
|
|
PATIENT = 'patient', _('Patient Appreciation')
|
|
DEPARTMENT = 'department', _('Department Appreciation')
|
|
TEAM = 'team', _('Team Appreciation')
|
|
INDIVIDUAL = 'individual', _('Individual Appreciation')
|
|
GROUP = 'group', _('Group Appreciation')
|
|
|
|
|
|
class AppreciationStatus(models.TextChoices):
|
|
"""Statuses for appreciations"""
|
|
DRAFT = 'draft', _('Draft')
|
|
SUBMITTED = 'submitted', _('Submitted')
|
|
ACKNOWLEDGED = 'acknowledged', _('Acknowledged')
|
|
PUBLISHED = 'published', _('Published')
|
|
ARCHIVED = 'archived', _('Archived')
|
|
|
|
|
|
class AppreciationCategory(models.Model):
|
|
"""Categories for appreciations"""
|
|
name = models.CharField(
|
|
max_length=100,
|
|
verbose_name=_('Name'),
|
|
help_text=_('Category name')
|
|
)
|
|
description = models.TextField(
|
|
blank=True,
|
|
verbose_name=_('Description'),
|
|
help_text=_('Category description')
|
|
)
|
|
is_active = models.BooleanField(
|
|
default=True,
|
|
verbose_name=_('Is Active'),
|
|
help_text=_('Whether this category is active')
|
|
)
|
|
display_order = models.PositiveIntegerField(
|
|
default=0,
|
|
verbose_name=_('Display Order'),
|
|
help_text=_('Order for displaying categories')
|
|
)
|
|
created_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('Created At')
|
|
)
|
|
updated_at = models.DateTimeField(
|
|
auto_now=True,
|
|
verbose_name=_('Updated At')
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = _('Appreciation Category')
|
|
verbose_name_plural = _('Appreciation Categories')
|
|
ordering = ['display_order', 'name']
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Appreciation(BaseModel):
|
|
"""Appreciation model for recognizing and rewarding excellence"""
|
|
uuid = models.UUIDField(
|
|
unique=True,
|
|
editable=False,
|
|
db_index=True,
|
|
verbose_name=_('UUID')
|
|
)
|
|
|
|
# Basic Information
|
|
title = models.CharField(
|
|
max_length=255,
|
|
verbose_name=_('Title'),
|
|
help_text=_('Title of the appreciation')
|
|
)
|
|
description = models.TextField(
|
|
verbose_name=_('Description'),
|
|
help_text=_('Detailed description of the appreciation')
|
|
)
|
|
story = models.TextField(
|
|
blank=True,
|
|
verbose_name=_('Story'),
|
|
help_text=_('The story behind this appreciation')
|
|
)
|
|
|
|
# Classification
|
|
appreciation_type = models.CharField(
|
|
max_length=20,
|
|
choices=AppreciationType.choices,
|
|
verbose_name=_('Appreciation Type'),
|
|
help_text=_('Type of appreciation')
|
|
)
|
|
category = models.ForeignKey(
|
|
AppreciationCategory,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='appreciations',
|
|
verbose_name=_('Category'),
|
|
help_text=_('Category of appreciation')
|
|
)
|
|
status = models.CharField(
|
|
max_length=20,
|
|
choices=AppreciationStatus.choices,
|
|
default=AppreciationStatus.DRAFT,
|
|
verbose_name=_('Status'),
|
|
help_text=_('Status of appreciation')
|
|
)
|
|
|
|
# Organization
|
|
hospital = models.ForeignKey(
|
|
'organizations.Hospital',
|
|
on_delete=models.CASCADE,
|
|
related_name='appreciations',
|
|
verbose_name=_('Hospital'),
|
|
help_text=_('Hospital where this appreciation occurred')
|
|
)
|
|
department = models.ForeignKey(
|
|
'organizations.Department',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='appreciations',
|
|
verbose_name=_('Department'),
|
|
help_text=_('Department where this appreciation occurred')
|
|
)
|
|
|
|
# Recipient Information
|
|
recipient_name = models.CharField(
|
|
max_length=255,
|
|
verbose_name=_('Recipient Name'),
|
|
help_text=_('Name of the person or team being appreciated')
|
|
)
|
|
recipient_type = models.CharField(
|
|
max_length=50,
|
|
verbose_name=_('Recipient Type'),
|
|
help_text=_('Type of recipient (e.g., Staff, Patient, Department)')
|
|
)
|
|
recipient_id = models.CharField(
|
|
max_length=100,
|
|
blank=True,
|
|
verbose_name=_('Recipient ID'),
|
|
help_text=_('ID of the recipient in the system')
|
|
)
|
|
|
|
# Submitter Information
|
|
submitted_by = models.ForeignKey(
|
|
'accounts.User',
|
|
on_delete=models.CASCADE,
|
|
related_name='submitted_appreciations',
|
|
verbose_name=_('Submitted By'),
|
|
help_text=_('User who submitted this appreciation')
|
|
)
|
|
submitter_role = models.CharField(
|
|
max_length=100,
|
|
blank=True,
|
|
verbose_name=_('Submitter Role'),
|
|
help_text=_('Role of the submitter')
|
|
)
|
|
|
|
# Acknowledgment
|
|
acknowledged_by = models.ForeignKey(
|
|
'accounts.User',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='acknowledged_appreciations',
|
|
verbose_name=_('Acknowledged By'),
|
|
help_text=_('User who acknowledged this appreciation')
|
|
)
|
|
acknowledged_at = models.DateTimeField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_('Acknowledged At'),
|
|
help_text=_('When this appreciation was acknowledged')
|
|
)
|
|
acknowledgment_notes = models.TextField(
|
|
blank=True,
|
|
verbose_name=_('Acknowledgment Notes'),
|
|
help_text=_('Notes added during acknowledgment')
|
|
)
|
|
|
|
# Timeline
|
|
submitted_at = models.DateTimeField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_('Submitted At'),
|
|
help_text=_('When this appreciation was submitted')
|
|
)
|
|
published_at = models.DateTimeField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_('Published At'),
|
|
help_text=_('When this appreciation was published')
|
|
)
|
|
|
|
# Visibility and Sharing
|
|
is_public = models.BooleanField(
|
|
default=False,
|
|
verbose_name=_('Is Public'),
|
|
help_text=_('Whether this appreciation is publicly visible')
|
|
)
|
|
share_on_dashboard = models.BooleanField(
|
|
default=True,
|
|
verbose_name=_('Share on Dashboard'),
|
|
help_text=_('Whether to display on the dashboard')
|
|
)
|
|
share_in_newsletter = models.BooleanField(
|
|
default=False,
|
|
verbose_name=_('Share in Newsletter'),
|
|
help_text=_('Whether to include in newsletter')
|
|
)
|
|
|
|
# Additional Details
|
|
tags = models.JSONField(
|
|
default=list,
|
|
blank=True,
|
|
verbose_name=_('Tags'),
|
|
help_text=_('Tags for categorization and search')
|
|
)
|
|
impact_score = models.PositiveIntegerField(
|
|
default=0,
|
|
verbose_name=_('Impact Score'),
|
|
help_text=_('Score indicating the impact of this appreciation')
|
|
)
|
|
metadata = models.JSONField(
|
|
default=dict,
|
|
blank=True,
|
|
verbose_name=_('Metadata'),
|
|
help_text=_('Additional metadata as key-value pairs'
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = _('Appreciation')
|
|
verbose_name_plural = _('Appreciations')
|
|
ordering = ['-created_at']
|
|
indexes = [
|
|
models.Index(fields=['status']),
|
|
models.Index(fields=['appreciation_type']),
|
|
models.Index(fields=['hospital']),
|
|
models.Index(fields=['created_at']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.title} - {self.recipient_name}"
|
|
|
|
def save(self, *args, **kwargs):
|
|
# Auto-set submitted_at when status changes to submitted
|
|
if self.status == AppreciationStatus.SUBMITTED and not self.submitted_at:
|
|
from django.utils import timezone
|
|
self.submitted_at = timezone.now()
|
|
|
|
# Auto-set published_at when status changes to published
|
|
if self.status == AppreciationStatus.PUBLISHED and not self.published_at:
|
|
from django.utils import timezone
|
|
self.published_at = timezone.now()
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
class AppreciationAttachment(models.Model):
|
|
"""Attachments for appreciations"""
|
|
appreciation = models.ForeignKey(
|
|
Appreciation,
|
|
on_delete=models.CASCADE,
|
|
related_name='attachments',
|
|
verbose_name=_('Appreciation'),
|
|
help_text=_('Associated appreciation')
|
|
)
|
|
file = models.FileField(
|
|
upload_to='appreciations/attachments/%Y/%m/',
|
|
verbose_name=_('File'),
|
|
help_text=_('Attachment file')
|
|
)
|
|
filename = models.CharField(
|
|
max_length=255,
|
|
verbose_name=_('Filename'),
|
|
help_text=_('Original filename')
|
|
)
|
|
file_type = models.CharField(
|
|
max_length=100,
|
|
blank=True,
|
|
verbose_name=_('File Type'),
|
|
help_text=_('MIME type of file')
|
|
)
|
|
file_size = models.PositiveIntegerField(
|
|
verbose_name=_('File Size'),
|
|
help_text=_('Size of file in bytes')
|
|
)
|
|
description = models.TextField(
|
|
blank=True,
|
|
verbose_name=_('Description'),
|
|
help_text=_('Description of the attachment')
|
|
)
|
|
uploaded_by = models.ForeignKey(
|
|
'accounts.User',
|
|
on_delete=models.CASCADE,
|
|
related_name='appreciation_uploads',
|
|
verbose_name=_('Uploaded By'),
|
|
help_text=_('User who uploaded this attachment')
|
|
)
|
|
created_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('Created At')
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = _('Appreciation Attachment')
|
|
verbose_name_plural = _('Appreciation Attachments')
|
|
ordering = ['-created_at']
|
|
|
|
def __str__(self):
|
|
return f"{self.filename} - {self.appreciation.title}"
|
|
|
|
|
|
class AppreciationReaction(models.Model):
|
|
"""Reactions to appreciations (likes, emojis, etc.)"""
|
|
appreciation = models.ForeignKey(
|
|
Appreciation,
|
|
on_delete=models.CASCADE,
|
|
related_name='reactions',
|
|
verbose_name=_('Appreciation'),
|
|
help_text=_('Associated appreciation')
|
|
)
|
|
user = models.ForeignKey(
|
|
'accounts.User',
|
|
on_delete=models.CASCADE,
|
|
related_name='appreciation_reactions',
|
|
verbose_name=_('User'),
|
|
help_text=_('User who reacted')
|
|
)
|
|
reaction_type = models.CharField(
|
|
max_length=50,
|
|
verbose_name=_('Reaction Type'),
|
|
help_text=_('Type of reaction (e.g., like, heart, star)'
|
|
)
|
|
created_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('Created At')
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = _('Appreciation Reaction')
|
|
verbose_name_plural = _('Appreciation Reactions')
|
|
unique_together = ['appreciation', 'user']
|
|
ordering = ['-created_at']
|
|
|
|
def __str__(self):
|
|
return f"{self.user.email} - {self.reaction_type}"
|
|
|
|
|
|
class AppreciationComment(models.Model):
|
|
"""Comments on appreciations"""
|
|
appreciation = models.ForeignKey(
|
|
Appreciation,
|
|
on_delete=models.CASCADE,
|
|
related_name='comments',
|
|
verbose_name=_('Appreciation'),
|
|
help_text=_('Associated appreciation')
|
|
)
|
|
user = models.ForeignKey(
|
|
'accounts.User',
|
|
on_delete=models.CASCADE,
|
|
related_name='appreciation_comments',
|
|
verbose_name=_('User'),
|
|
help_text=_('User who commented')
|
|
)
|
|
comment = models.TextField(
|
|
verbose_name=_('Comment'),
|
|
help_text=_('Comment text')
|
|
)
|
|
parent = models.ForeignKey(
|
|
'self',
|
|
on_delete=models.CASCADE,
|
|
null=True,
|
|
blank=True,
|
|
related_name='replies',
|
|
verbose_name=_('Parent Comment'),
|
|
help_text=_('Parent comment for nested replies')
|
|
)
|
|
is_internal = models.BooleanField(
|
|
default=False,
|
|
verbose_name=_('Is Internal'),
|
|
help_text=_('Whether this is an internal comment')
|
|
)
|
|
created_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('Created At')
|
|
)
|
|
updated_at = models.DateTimeField(
|
|
auto_now=True,
|
|
verbose_name=_('Updated At')
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = _('Appreciation Comment')
|
|
verbose_name_plural = _('Appreciation Comments')
|
|
ordering = ['-created_at']
|
|
|
|
def __str__(self):
|
|
return f"{self.user.email}: {self.comment[:50]}" |