225 lines
6.6 KiB
Python
225 lines
6.6 KiB
Python
"""
|
|
Version control models for acknowledgement content.
|
|
Tracks changes to content and checklist items for audit purposes.
|
|
"""
|
|
from django.db import models
|
|
from django.conf import settings
|
|
|
|
from apps.core.models import TimeStampedModel, UUIDModel
|
|
|
|
|
|
class ContentVersion(UUIDModel, TimeStampedModel):
|
|
"""
|
|
Version history for AcknowledgementContent.
|
|
Tracks all changes made to content sections.
|
|
"""
|
|
content = models.ForeignKey(
|
|
'AcknowledgementContent',
|
|
on_delete=models.CASCADE,
|
|
related_name='versions',
|
|
help_text='The content this version belongs to'
|
|
)
|
|
|
|
# Version number (auto-incrementing per content)
|
|
version_number = models.PositiveIntegerField(
|
|
help_text='Version number (1, 2, 3, ...)'
|
|
)
|
|
|
|
# Snapshots of the content at this version
|
|
title_en = models.CharField(max_length=200)
|
|
title_ar = models.CharField(max_length=200, blank=True)
|
|
description_en = models.TextField()
|
|
description_ar = models.TextField(blank=True)
|
|
content_en = models.TextField(blank=True)
|
|
content_ar = models.TextField(blank=True)
|
|
|
|
# Metadata
|
|
role = models.CharField(max_length=50, null=True, blank=True)
|
|
order = models.IntegerField(default=0)
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
# Change tracking
|
|
changed_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='content_versions_created'
|
|
)
|
|
change_reason = models.TextField(
|
|
blank=True,
|
|
help_text='Reason for this change'
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['-version_number']
|
|
unique_together = [['content', 'version_number']]
|
|
verbose_name = 'Content Version'
|
|
verbose_name_plural = 'Content Versions'
|
|
|
|
def __str__(self):
|
|
return f"{self.content.code} v{self.version_number}"
|
|
|
|
@property
|
|
def change_summary(self):
|
|
"""Get a summary of changes from previous version"""
|
|
previous = ContentVersion.objects.filter(
|
|
content=self.content,
|
|
version_number__lt=self.version_number
|
|
).first()
|
|
|
|
if not previous:
|
|
return "Initial version"
|
|
|
|
changes = []
|
|
if self.title_en != previous.title_en:
|
|
changes.append("Title (EN) changed")
|
|
if self.title_ar != previous.title_ar:
|
|
changes.append("Title (AR) changed")
|
|
if self.content_en != previous.content_en:
|
|
changes.append("Content (EN) changed")
|
|
if self.content_ar != previous.content_ar:
|
|
changes.append("Content (AR) changed")
|
|
if self.is_active != previous.is_active:
|
|
changes.append("Active status changed")
|
|
|
|
return ", ".join(changes) if changes else "Minor changes"
|
|
|
|
|
|
class ChecklistItemVersion(UUIDModel, TimeStampedModel):
|
|
"""
|
|
Version history for AcknowledgementChecklistItem.
|
|
Tracks all changes made to checklist items.
|
|
"""
|
|
checklist_item = models.ForeignKey(
|
|
'AcknowledgementChecklistItem',
|
|
on_delete=models.CASCADE,
|
|
related_name='versions',
|
|
help_text='The checklist item this version belongs to'
|
|
)
|
|
|
|
# Version number
|
|
version_number = models.PositiveIntegerField(
|
|
help_text='Version number (1, 2, 3, ...)'
|
|
)
|
|
|
|
# Snapshots of the item at this version
|
|
text_en = models.CharField(max_length=500)
|
|
text_ar = models.CharField(max_length=500, blank=True)
|
|
description_en = models.TextField(blank=True)
|
|
description_ar = models.TextField(blank=True)
|
|
|
|
# Configuration
|
|
is_required = models.BooleanField(default=True)
|
|
is_active = models.BooleanField(default=True)
|
|
order = models.IntegerField(default=0)
|
|
|
|
# Relationships
|
|
content = models.ForeignKey(
|
|
'AcknowledgementContent',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='checklist_versions'
|
|
)
|
|
role = models.CharField(max_length=50, null=True, blank=True)
|
|
|
|
# Change tracking
|
|
changed_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='checklist_versions_created'
|
|
)
|
|
change_reason = models.TextField(
|
|
blank=True,
|
|
help_text='Reason for this change'
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['-version_number']
|
|
unique_together = [['checklist_item', 'version_number']]
|
|
verbose_name = 'Checklist Item Version'
|
|
verbose_name_plural = 'Checklist Item Versions'
|
|
|
|
def __str__(self):
|
|
return f"{self.checklist_item.code} v{self.version_number}"
|
|
|
|
|
|
class ContentChangeLog(UUIDModel, TimeStampedModel):
|
|
"""
|
|
Comprehensive change log for all content-related activities.
|
|
Captures create, update, and delete operations.
|
|
"""
|
|
ACTION_CHOICES = [
|
|
('create', 'Created'),
|
|
('update', 'Updated'),
|
|
('delete', 'Deleted'),
|
|
('activate', 'Activated'),
|
|
('deactivate', 'Deactivated'),
|
|
('reorder', 'Reordered'),
|
|
]
|
|
|
|
CONTENT_TYPE_CHOICES = [
|
|
('content', 'Acknowledgement Content'),
|
|
('checklist_item', 'Checklist Item'),
|
|
]
|
|
|
|
user = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='content_change_logs'
|
|
)
|
|
|
|
action = models.CharField(
|
|
max_length=20,
|
|
choices=ACTION_CHOICES
|
|
)
|
|
|
|
content_type = models.CharField(
|
|
max_length=20,
|
|
choices=CONTENT_TYPE_CHOICES
|
|
)
|
|
|
|
object_id = models.CharField(
|
|
max_length=50,
|
|
help_text='UUID of the content/checklist item'
|
|
)
|
|
|
|
object_code = models.CharField(
|
|
max_length=100,
|
|
help_text='Human-readable code of the object'
|
|
)
|
|
|
|
# Snapshot of the object after the change
|
|
snapshot = models.JSONField(
|
|
default=dict,
|
|
help_text='JSON snapshot of the object after change'
|
|
)
|
|
|
|
# Change details
|
|
changes_summary = models.TextField(
|
|
blank=True,
|
|
help_text='Human-readable summary of changes'
|
|
)
|
|
|
|
# IP and user agent for audit
|
|
ip_address = models.GenericIPAddressField(
|
|
null=True,
|
|
blank=True
|
|
)
|
|
user_agent = models.TextField(
|
|
blank=True
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['-created_at']
|
|
verbose_name = 'Content Change Log'
|
|
verbose_name_plural = 'Content Change Logs'
|
|
|
|
def __str__(self):
|
|
return f"{self.get_action_display()} {self.object_code} by {self.user}"
|