Files

255 lines
9.4 KiB
Python
Raw Permalink Normal View History

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
class Review(models.Model):
STATUS_PENDING = 'pending'
STATUS_LIVE = 'live'
STATUS_REJECTED = 'rejected'
STATUS_CHOICES = [
(STATUS_PENDING, 'Pending'),
(STATUS_LIVE, 'Live'),
(STATUS_REJECTED, 'Rejected'),
]
REJECT_CHOICES = [
('spam', 'Spam'),
('inappropriate', 'Inappropriate'),
('fake', 'Fake'),
]
reviewer = models.ForeignKey(
'accounts.User', on_delete=models.CASCADE, related_name='admin_reviews'
)
event = models.ForeignKey(
'events.Event', on_delete=models.CASCADE, related_name='admin_reviews'
)
rating = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)]
)
review_text = models.TextField()
submission_date = models.DateTimeField(auto_now_add=True)
status = models.CharField(
max_length=10, choices=STATUS_CHOICES, default=STATUS_PENDING
)
reject_reason = models.CharField(
max_length=15, choices=REJECT_CHOICES, null=True, blank=True
)
display_name = models.CharField(max_length=100, blank=True, default='')
is_verified = models.BooleanField(default=False)
helpful_count = models.IntegerField(default=0)
flag_count = models.IntegerField(default=0)
class Meta:
ordering = ['-submission_date']
indexes = [
models.Index(fields=['status']),
models.Index(fields=['submission_date']),
]
def __str__(self):
return f'Review #{self.pk} by {self.reviewer_id}{self.status}'
class ReviewInteraction(models.Model):
INTERACTION_CHOICES = [('HELPFUL', 'Helpful'), ('FLAG', 'Flag')]
review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name='interactions')
username = models.CharField(max_length=255)
interaction_type = models.CharField(max_length=20, choices=INTERACTION_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ('review', 'username', 'interaction_type')
def __str__(self):
return f'{self.username} {self.interaction_type} on Review #{self.review_id}'
# ---------------------------------------------------------------------------
# RBAC Models
# ---------------------------------------------------------------------------
from accounts.models import User
class Department(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True, default='')
base_scopes = models.JSONField(default=list)
color = models.CharField(max_length=7, default='#3B82F6')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class Squad(models.Model):
name = models.CharField(max_length=100)
department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='squads')
manager = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='managed_squads')
extra_scopes = models.JSONField(default=list)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['name']
def __str__(self):
return f"{self.department.name} > {self.name}"
class StaffProfile(models.Model):
ROLE_CHOICES = [('SUPER_ADMIN', 'Super Admin'), ('MANAGER', 'Manager'), ('MEMBER', 'Member')]
STATUS_CHOICES = [('active', 'Active'), ('invited', 'Invited'), ('deactivated', 'Deactivated')]
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='staff_profile')
department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True, blank=True, related_name='staff_members')
squad = models.ForeignKey(Squad, on_delete=models.SET_NULL, null=True, blank=True, related_name='members')
staff_role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='MEMBER')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
joined_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['user__first_name']
def get_effective_scopes(self):
if self.staff_role == 'SUPER_ADMIN' or self.user.is_superuser:
return ['*']
scopes = set()
if self.department:
scopes.update(self.department.base_scopes or [])
if self.squad:
scopes.update(self.squad.extra_scopes or [])
if self.staff_role == 'MANAGER':
scopes.add('settings.staff')
return list(scopes)
def get_allowed_modules(self):
scopes = self.get_effective_scopes()
if '*' in scopes:
return ['dashboard', 'partners', 'events', 'ad-control', 'users', 'reviews', 'contributions', 'leads', 'financials', 'audit-log', 'settings']
SCOPE_TO_MODULE = {
'users': 'users',
'events': 'events',
'finance': 'financials',
'partners': 'partners',
'tickets': 'dashboard',
'settings': 'settings',
'ads': 'ad-control',
'contributions': 'contributions',
'leads': 'leads',
'audit': 'audit-log',
'reviews': 'reviews',
}
modules = {'dashboard'}
for scope in scopes:
prefix = scope.split('.')[0]
if prefix in SCOPE_TO_MODULE:
modules.add(SCOPE_TO_MODULE[prefix])
return list(modules)
def __str__(self):
return f"{self.user.username} ({self.staff_role})"
class CustomRole(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True, default='')
scopes = models.JSONField(default=list)
is_system = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class AuditLog(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='audit_logs')
action = models.CharField(max_length=100)
target_type = models.CharField(max_length=50)
target_id = models.CharField(max_length=50)
details = models.JSONField(default=dict)
ip_address = models.GenericIPAddressField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
indexes = [
# Fast filter-by-action ordered by time (audit log page default view)
models.Index(fields=['action', '-created_at'], name='auditlog_action_time_idx'),
# Fast "related entries for this target" lookups in the detail panel
models.Index(fields=['target_type', 'target_id'], name='auditlog_target_idx'),
]
def __str__(self):
return f"{self.action} by {self.user} at {self.created_at}"
# ---------------------------------------------------------------------------
# Lead Manager
# ---------------------------------------------------------------------------
class Lead(models.Model):
EVENT_TYPE_CHOICES = [
('private', 'Private Event'),
('ticketed', 'Ticketed Event'),
('corporate', 'Corporate Event'),
('wedding', 'Wedding'),
('other', 'Other'),
]
STATUS_CHOICES = [
('new', 'New'),
('contacted', 'Contacted'),
('qualified', 'Qualified'),
('converted', 'Converted'),
('closed', 'Closed'),
]
SOURCE_CHOICES = [
('schedule_call', 'Schedule a Call'),
('website', 'Website'),
('manual', 'Manual'),
]
PRIORITY_CHOICES = [
('low', 'Low'),
('medium', 'Medium'),
('high', 'High'),
]
name = models.CharField(max_length=200)
email = models.EmailField()
phone = models.CharField(max_length=20)
event_type = models.CharField(max_length=20, choices=EVENT_TYPE_CHOICES, default='private')
message = models.TextField(blank=True, default='')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='new')
source = models.CharField(max_length=20, choices=SOURCE_CHOICES, default='schedule_call')
priority = models.CharField(max_length=10, choices=PRIORITY_CHOICES, default='medium')
assigned_to = models.ForeignKey(
User, on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_leads'
)
user_account = models.ForeignKey(
User, on_delete=models.SET_NULL, null=True, blank=True, related_name='submitted_leads',
help_text='Consumer platform account that submitted this lead (auto-matched by email)'
)
notes = models.TextField(blank=True, default='')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
indexes = [
models.Index(fields=['status']),
models.Index(fields=['priority']),
models.Index(fields=['created_at']),
models.Index(fields=['email']),
]
def __str__(self):
return f'Lead #{self.pk}{self.name} ({self.status})'