From 68c22966bc8ddef148e1e12952772c51e2cfd6df Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 26 Oct 2024 20:19:31 +0930 Subject: [PATCH] feat(core): Add Base Ticket Serializer and ViewSet ref: #248 #365 --- app/core/serializers/ticket.py | 166 +++++++++++++++++++++++++++++++ app/core/viewsets/ticket.py | 172 +++++++++++++++++++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 app/core/serializers/ticket.py create mode 100644 app/core/viewsets/ticket.py diff --git a/app/core/serializers/ticket.py b/app/core/serializers/ticket.py new file mode 100644 index 00000000..7ac063c4 --- /dev/null +++ b/app/core/serializers/ticket.py @@ -0,0 +1,166 @@ +from rest_framework.reverse import reverse +from rest_framework import serializers + +from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.teams import TeamBaseSerializer + +from app.serializers.user import UserBaseSerializer + +from core.models.ticket.ticket import Ticket + +from core.fields.badge import Badge, BadgeField + + + +class TicketBaseSerializer(serializers.ModelSerializer): + + display_name = serializers.SerializerMethodField('get_display_name') + + def get_display_name(self, item): + + return str( item ) + + + url = serializers.SerializerMethodField('my_url') + + def my_url(self, item): + + context = self.context.copy() + + return reverse( + "v2:_api_v2_ticket_" + str(item.get_ticket_type_display()).lower() + "-detail", + request=context['view'].request, + kwargs={ + 'pk': item.pk + } + ) + + + class Meta: + + model = Ticket + + fields = [ + 'id', + 'display_name', + 'title', + 'url', + ] + + read_only_fields = [ + 'id', + 'display_name', + 'title', + 'url', + ] + + +class TicketModelSerializer(TicketBaseSerializer): + + _urls = serializers.SerializerMethodField('get_url') + + def get_url(self, item): + + context = self.context.copy() + + return { + '_self': reverse( + "v2:_api_v2_ticket_" + str(item.get_ticket_type_display()).lower() + "-detail", + request=context['view'].request, + kwargs={ + 'pk': item.pk + } + ), + } + + + duration = serializers.IntegerField(source='duration_ticket', read_only=True) + + status_badge = BadgeField(label='Status') + + + class Meta: + """Ticket Model Base Meta + + This class specifically has only `id` in fields and all remaining fields + as ready only so as to prevent using this serializer directly. The intent + is that for each ticket type there is a seperate serializer for that ticket + type. + + These serializers are for items that are common for ALL tickets. + """ + + model = Ticket + + fields = [ + 'id', + '_urls', + ] + + read_only_fields = [ + 'id', + 'assigned_teams', + 'assigned_users', + 'category', + 'created', + 'modified', + 'status', + 'status_badge', + 'title', + 'description', + 'estimate', + 'duration', + 'urgency', + 'impact', + 'priority', + 'external_ref', + 'external_system', + 'ticket_type', + 'is_deleted', + 'date_closed', + 'planned_start_date', + 'planned_finish_date', + 'real_start_date', + 'real_finish_date', + 'opened_by', + 'organization', + 'project', + 'milestone', + 'subscribed_teams', + 'subscribed_users', + '_urls', + ] + + + def is_valid(self, *, raise_exception=False): + + is_valid: bool = False + + is_valid = super().is_valid(raise_exception=raise_exception) + + try: + + self.validated_data['ticket_type'] = self._context['view'].ticket_type_id + + except: + + is_valid = False + + + return is_valid + + + +class TicketViewSerializer(TicketModelSerializer): + + assigned_users = UserBaseSerializer(many=True, label='Assigned Users') + + assigned_teams = TeamBaseSerializer(many=True) + + opened_by = UserBaseSerializer() + + subscribed_users = UserBaseSerializer(many=True) + + subscribed_teams = TeamBaseSerializer(many=True) + + organization = OrganizationBaseSerializer(many=False, read_only=True) diff --git a/app/core/viewsets/ticket.py b/app/core/viewsets/ticket.py new file mode 100644 index 00000000..e17cc71f --- /dev/null +++ b/app/core/viewsets/ticket.py @@ -0,0 +1,172 @@ +from api.viewsets.common import ModelViewSet + +from core.serializers.ticket import ( + Ticket, +) + +from settings.models.user_settings import UserSettings + + +class TicketViewSet(ModelViewSet): + + filterset_fields = [ + 'category', + 'external_system', + 'impact', + 'milestone', + 'organization', + 'priority', + 'project', + 'status', + 'urgency', + ] + + search_fields = [ + 'title', + 'description', + ] + + model = Ticket + + _ticket_type_id: int = None + + _ticket_type: str = None + """Name for type of ticket + + String is Camel Cased. + """ + + + def get_queryset(self): + + self.get_ticket_type() + + queryset = super().get_queryset().filter( + ticket_type = self._ticket_type_id + ) + + self.queryset = queryset + + return self.queryset + + + def get_ticket_type(self) -> None: + + ticket_type_id: int = None + + if self._ticket_type_id is None: + + ticket_types = [e for e in Ticket.TicketType] + + for i in range( 1, len(ticket_types) ): + + if self._ticket_type.lower() == str(ticket_types[i - 1].label).lower(): + + ticket_type_id = i + + break + + self._ticket_type_id = ticket_type_id + + + def get_serializer_class(self): + + serializer_prefix:str = None + + self.get_ticket_type() + + serializer_prefix = self._ticket_type + + + if ( + self.action == 'create' + or self.action == 'list' + ): + + + if ( + self.action == 'create' + or self.action == 'list' + ): + + user_settings = UserSettings.objects.get( + user = self.request.user + ) + + organization = user_settings.default_organization.id + + + if ( + self.action == 'create' + ): + + if self.request.data is not None: + + if 'organization' in self.request.data: + + organization = int(self.request.data['organization']) + + + if ( # Must be first as the priority to pickup + self._ticket_type + and self.action != 'list' + and self.action != 'retrieve' + ): + + + if self.has_organization_permission( + organization = organization, + permissions_required = [ + 'core.import_ticket_request' + ] + ): + + serializer_prefix = self._ticket_type + 'Import' + + elif self.has_organization_permission( + organization = organization, + permissions_required = [ + 'core.triage_ticket_request' + ] + ): + + serializer_prefix = self._ticket_type + 'Triage' + + elif self.has_organization_permission( + organization = organization, + permissions_required = [ + 'core.change_ticket_request' + ] + ): + + serializer_prefix = self._ticket_type + 'Change' + + elif self.has_organization_permission( + organization = organization, + permissions_required = [ + 'core.add_ticket_request' + ] + ): + + serializer_prefix = self._ticket_type + 'Add' + + elif self.has_organization_permission( + organization = organization, + permissions_required = [ + 'core.view_ticket_request' + ] + ): + + serializer_prefix = self._ticket_type + 'View' + + + + if ( + self.action == 'list' + or self.action == 'retrieve' + ): + + return globals()[serializer_prefix + 'TicketViewSerializer'] + + + return globals()[serializer_prefix + 'TicketModelSerializer']