mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 07:45:18 -05:00
Based on the git diff provided, here's a concise and descriptive commit message:
feat: add security event taxonomy and optimize park queryset - Add comprehensive security_event_types ChoiceGroup with categories for authentication, MFA, password, account, session, and API key events - Include severity levels, icons, and CSS classes for each event type - Fix park queryset optimization by using select_related for OneToOne location relationship - Remove location property fields (latitude/longitude) from values() call as they are not actual DB columns - Add proper location fields (city, state, country) to values() for map display This change enhances security event tracking capabilities and resolves a queryset optimization issue where property decorators were incorrectly used in values() queries.
This commit is contained in:
166
backend/apps/support/choices.py
Normal file
166
backend/apps/support/choices.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
Rich Choice Objects for Support Domain
|
||||
|
||||
This module defines all choice objects for the support domain,
|
||||
using the RichChoices pattern for consistent UI rendering and validation.
|
||||
|
||||
Note: Values are kept lowercase for backward compatibility with existing data.
|
||||
"""
|
||||
|
||||
from apps.core.choices import ChoiceCategory, RichChoice
|
||||
from apps.core.choices.registry import register_choices
|
||||
|
||||
# ============================================================================
|
||||
# Ticket Status Choices
|
||||
# ============================================================================
|
||||
|
||||
TICKET_STATUSES = [
|
||||
RichChoice(
|
||||
value="open",
|
||||
label="Open",
|
||||
description="Ticket is awaiting response",
|
||||
metadata={
|
||||
"color": "yellow",
|
||||
"icon": "inbox",
|
||||
"css_class": "bg-yellow-100 text-yellow-800 border-yellow-200",
|
||||
"sort_order": 1,
|
||||
"can_transition_to": ["in_progress", "closed"],
|
||||
"is_actionable": True,
|
||||
},
|
||||
category=ChoiceCategory.STATUS,
|
||||
),
|
||||
RichChoice(
|
||||
value="in_progress",
|
||||
label="In Progress",
|
||||
description="Ticket is being worked on",
|
||||
metadata={
|
||||
"color": "blue",
|
||||
"icon": "clock",
|
||||
"css_class": "bg-blue-100 text-blue-800 border-blue-200",
|
||||
"sort_order": 2,
|
||||
"can_transition_to": ["closed"],
|
||||
"is_actionable": True,
|
||||
},
|
||||
category=ChoiceCategory.STATUS,
|
||||
),
|
||||
RichChoice(
|
||||
value="closed",
|
||||
label="Closed",
|
||||
description="Ticket has been resolved",
|
||||
metadata={
|
||||
"color": "green",
|
||||
"icon": "check-circle",
|
||||
"css_class": "bg-green-100 text-green-800 border-green-200",
|
||||
"sort_order": 3,
|
||||
"can_transition_to": ["open"],
|
||||
"is_actionable": False,
|
||||
"is_final": True,
|
||||
},
|
||||
category=ChoiceCategory.STATUS,
|
||||
),
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# Ticket Category Choices
|
||||
# ============================================================================
|
||||
|
||||
TICKET_CATEGORIES = [
|
||||
RichChoice(
|
||||
value="general",
|
||||
label="General Inquiry",
|
||||
description="General questions or feedback",
|
||||
metadata={
|
||||
"color": "gray",
|
||||
"icon": "chat-bubble-left",
|
||||
"css_class": "bg-gray-100 text-gray-800 border-gray-200",
|
||||
"sort_order": 1,
|
||||
"default_priority": "low",
|
||||
},
|
||||
category=ChoiceCategory.CLASSIFICATION,
|
||||
),
|
||||
RichChoice(
|
||||
value="bug",
|
||||
label="Bug Report",
|
||||
description="Report a bug or issue with the platform",
|
||||
metadata={
|
||||
"color": "red",
|
||||
"icon": "bug-ant",
|
||||
"css_class": "bg-red-100 text-red-800 border-red-200",
|
||||
"sort_order": 2,
|
||||
"default_priority": "high",
|
||||
},
|
||||
category=ChoiceCategory.CLASSIFICATION,
|
||||
),
|
||||
RichChoice(
|
||||
value="partnership",
|
||||
label="Partnership",
|
||||
description="Partnership or collaboration inquiries",
|
||||
metadata={
|
||||
"color": "purple",
|
||||
"icon": "handshake",
|
||||
"css_class": "bg-purple-100 text-purple-800 border-purple-200",
|
||||
"sort_order": 3,
|
||||
"default_priority": "medium",
|
||||
},
|
||||
category=ChoiceCategory.CLASSIFICATION,
|
||||
),
|
||||
RichChoice(
|
||||
value="press",
|
||||
label="Press/Media",
|
||||
description="Press inquiries and media requests",
|
||||
metadata={
|
||||
"color": "indigo",
|
||||
"icon": "newspaper",
|
||||
"css_class": "bg-indigo-100 text-indigo-800 border-indigo-200",
|
||||
"sort_order": 4,
|
||||
"default_priority": "medium",
|
||||
},
|
||||
category=ChoiceCategory.CLASSIFICATION,
|
||||
),
|
||||
RichChoice(
|
||||
value="data",
|
||||
label="Data Correction",
|
||||
description="Request corrections to park or ride data",
|
||||
metadata={
|
||||
"color": "orange",
|
||||
"icon": "pencil-square",
|
||||
"css_class": "bg-orange-100 text-orange-800 border-orange-200",
|
||||
"sort_order": 5,
|
||||
"default_priority": "medium",
|
||||
},
|
||||
category=ChoiceCategory.CLASSIFICATION,
|
||||
),
|
||||
RichChoice(
|
||||
value="account",
|
||||
label="Account Issue",
|
||||
description="Account-related problems or requests",
|
||||
metadata={
|
||||
"color": "cyan",
|
||||
"icon": "user-circle",
|
||||
"css_class": "bg-cyan-100 text-cyan-800 border-cyan-200",
|
||||
"sort_order": 6,
|
||||
"default_priority": "high",
|
||||
},
|
||||
category=ChoiceCategory.CLASSIFICATION,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def register_support_choices() -> None:
|
||||
"""Register all support domain choices with the global registry."""
|
||||
register_choices(
|
||||
"ticket_statuses",
|
||||
TICKET_STATUSES,
|
||||
domain="support",
|
||||
description="Status options for support tickets",
|
||||
)
|
||||
register_choices(
|
||||
"ticket_categories",
|
||||
TICKET_CATEGORIES,
|
||||
domain="support",
|
||||
description="Category options for support tickets",
|
||||
)
|
||||
|
||||
|
||||
# Auto-register choices when module is imported
|
||||
register_support_choices()
|
||||
@@ -0,0 +1,48 @@
|
||||
# Generated by Django 5.2.10 on 2026-01-10 19:33
|
||||
|
||||
import apps.core.choices.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("support", "0003_add_incident_and_report_models"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="ticket",
|
||||
name="category",
|
||||
field=apps.core.choices.fields.RichChoiceField(
|
||||
allow_deprecated=False,
|
||||
choice_group="ticket_categories",
|
||||
choices=[
|
||||
("general", "General Inquiry"),
|
||||
("bug", "Bug Report"),
|
||||
("partnership", "Partnership"),
|
||||
("press", "Press/Media"),
|
||||
("data", "Data Correction"),
|
||||
("account", "Account Issue"),
|
||||
],
|
||||
db_index=True,
|
||||
default="general",
|
||||
domain="support",
|
||||
help_text="Category of the ticket",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="ticket",
|
||||
name="status",
|
||||
field=apps.core.choices.fields.RichChoiceField(
|
||||
allow_deprecated=False,
|
||||
choice_group="ticket_statuses",
|
||||
choices=[("open", "Open"), ("in_progress", "In Progress"), ("closed", "Closed")],
|
||||
db_index=True,
|
||||
default="open",
|
||||
domain="support",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,36 +1,14 @@
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
from apps.core.choices.fields import RichChoiceField
|
||||
from apps.core.history import TrackedModel
|
||||
|
||||
# Import choices to ensure registration on app load
|
||||
from . import choices # noqa: F401
|
||||
|
||||
|
||||
class Ticket(TrackedModel):
|
||||
STATUS_OPEN = "open"
|
||||
STATUS_IN_PROGRESS = "in_progress"
|
||||
STATUS_CLOSED = "closed"
|
||||
|
||||
STATUS_CHOICES = [
|
||||
(STATUS_OPEN, "Open"),
|
||||
(STATUS_IN_PROGRESS, "In Progress"),
|
||||
(STATUS_CLOSED, "Closed"),
|
||||
]
|
||||
|
||||
CATEGORY_GENERAL = "general"
|
||||
CATEGORY_BUG = "bug"
|
||||
CATEGORY_PARTNERSHIP = "partnership"
|
||||
CATEGORY_PRESS = "press"
|
||||
CATEGORY_DATA = "data"
|
||||
CATEGORY_ACCOUNT = "account"
|
||||
|
||||
CATEGORY_CHOICES = [
|
||||
(CATEGORY_GENERAL, "General Inquiry"),
|
||||
(CATEGORY_BUG, "Bug Report"),
|
||||
(CATEGORY_PARTNERSHIP, "Partnership"),
|
||||
(CATEGORY_PRESS, "Press/Media"),
|
||||
(CATEGORY_DATA, "Data Correction"),
|
||||
(CATEGORY_ACCOUNT, "Account Issue"),
|
||||
]
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.SET_NULL,
|
||||
@@ -40,10 +18,11 @@ class Ticket(TrackedModel):
|
||||
help_text="User who submitted the ticket (optional)",
|
||||
)
|
||||
|
||||
category = models.CharField(
|
||||
category = RichChoiceField(
|
||||
choice_group="ticket_categories",
|
||||
domain="support",
|
||||
max_length=20,
|
||||
choices=CATEGORY_CHOICES,
|
||||
default=CATEGORY_GENERAL,
|
||||
default="general",
|
||||
db_index=True,
|
||||
help_text="Category of the ticket",
|
||||
)
|
||||
@@ -51,7 +30,13 @@ class Ticket(TrackedModel):
|
||||
message = models.TextField()
|
||||
email = models.EmailField(help_text="Contact email", blank=True)
|
||||
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_OPEN, db_index=True)
|
||||
status = RichChoiceField(
|
||||
choice_group="ticket_statuses",
|
||||
domain="support",
|
||||
max_length=20,
|
||||
default="open",
|
||||
db_index=True,
|
||||
)
|
||||
|
||||
class Meta(TrackedModel.Meta):
|
||||
verbose_name = "Ticket"
|
||||
|
||||
@@ -134,9 +134,14 @@ class ReportCreateSerializer(serializers.ModelSerializer):
|
||||
class ReportResolveSerializer(serializers.Serializer):
|
||||
"""Serializer for resolving reports."""
|
||||
|
||||
from .models import Report
|
||||
|
||||
status = serializers.ChoiceField(
|
||||
choices=[("resolved", "Resolved"), ("dismissed", "Dismissed")],
|
||||
default="resolved",
|
||||
choices=[
|
||||
(Report.Status.RESOLVED, "Resolved"),
|
||||
(Report.Status.DISMISSED, "Dismissed"),
|
||||
],
|
||||
default=Report.Status.RESOLVED,
|
||||
)
|
||||
notes = serializers.CharField(required=False, allow_blank=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user