From 5b2441b9948e07e165f136a0eb3c067796f377d1 Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Fri, 17 May 2024 12:34:40 +0200 Subject: [PATCH 01/12] Add list of 'parent' tickets to ticket description table 'parent' tickets are all tickets on which the current ticket depends. The parent tickets can only be resolved if the current tickets is closed. This patch splits the current |Dependencies|Total time spent| table row in two rows. The table layout is now |Status |Total time spent| |Depends|Resolves | Status and Total time only have one line. Status was chosen as filler field, because I believe it should be moved out of the title to reduce visual distraction. Depends and Resolve will have a variable number of lines and are closely related. Depends was chosen instead of "Dependencies", to indicate directly what the listed tickets do and because it is shorter. The commit also simplifies the wording of the tooltips and harmonizes with the other tooltips in the same table: It indicates directly what happens when the button is clicked (or touched!). Notes: - The complete row could be omitted if now Depends or Resolves exists for a ticket. This would reduce visual distraction but was not implemented because it could lead to misled expectations. - No button for removing/adding parent tickets was added. If considered important this can be added any time. Currently a user has to go to the parent ticket and delete the dependency (easy) or remember the ticket id/title, search for the parent and add the ticket as dependency (difficult) --- .../templates/helpdesk/ticket_desc_table.html | 58 ++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index 8cf00829..7b9e0295 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -117,24 +117,9 @@ - {% if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET != False and helpdesk_settings.HELPDESK_ENABLE_TIME_SPENT_ON_TICKET != False %} - - {% if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET %} - {% trans "Dependencies" %} - - - {% for dep in ticket.ticketdependency.all %} - {% if forloop.first %}

{% trans "This ticket cannot be resolved until the following ticket(s) are resolved" %}

{% endif %} - {% empty %} - {% trans "This ticket has no dependencies." %} - {% endfor %} - - {% else %} - - - {% endif %} + + {% trans "Status" %} + {{ ticket.get_status }} {% if helpdesk_settings.HELPDESK_ENABLE_TIME_SPENT_ON_TICKET %} {% trans "Total time spent" %} {{ ticket.time_spent_formated }} @@ -142,8 +127,41 @@ {% endif %} - - {% endif %} + + {% if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET != False %} + + {% trans "Depends" %} + + + + {% for dep in ticket.ticketdependency.all %} + {% if forloop.first %}{% endif %} + {% empty %} + {% trans "This ticket has no dependencies." %} + {% endfor %} + + {% trans "Resolves" %} + + {% for parent in ticket.depends_on.all %} + {% if forloop.first %}{% endif %} + {% empty %} + {% trans "This ticket does not resolve any other" %} + {% endfor %} + + + {% endif %} {% if ticket.kbitem %} {% trans "Knowlegebase item" %} From d7afcb65787c38050667861d6c12d459071c2304 Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Thu, 6 Jun 2024 00:28:36 +0200 Subject: [PATCH 02/12] Reformat Resolves/Depends lists into tables. Allow adding/deleting of parent tickets --- helpdesk/forms.py | 8 ++ .../templates/helpdesk/ticket_desc_table.html | 75 ++++++++++++------- .../helpdesk/ticket_resolves_add.html | 37 +++++++++ helpdesk/urls.py | 10 +++ helpdesk/views/staff.py | 46 ++++++++++++ 5 files changed, 150 insertions(+), 26 deletions(-) create mode 100644 helpdesk/templates/helpdesk/ticket_resolves_add.html diff --git a/helpdesk/forms.py b/helpdesk/forms.py index eb77c2e6..87da3893 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -591,6 +591,14 @@ class TicketDependencyForm(forms.ModelForm): model = TicketDependency exclude = ('ticket',) +class TicketResolvesForm(forms.ModelForm): + ''' Adds this ticket as a dependency for a different ticket ''' + + class Meta: + model = TicketDependency + #exclude = ('depends_on',) + fields = ('ticket',) + class MultipleTicketSelectForm(forms.Form): tickets = forms.ModelMultipleChoiceField( diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index 7b9e0295..f5003625 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -130,37 +130,60 @@ {% if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET != False %} - {% trans "Depends" %} - - + + {% trans "Resolves" %} + - {% for dep in ticket.ticketdependency.all %} - {% if forloop.first %}{% endif %} - {% empty %} - {% trans "This ticket has no dependencies." %} - {% endfor %} - - {% trans "Resolves" %} - - {% for parent in ticket.depends_on.all %} - {% if forloop.first %}{% endif %} + + + {% for resolves in ticket.depends_on.all %} + {% if forloop.first %}{% endif %} + + + + + + {% if forloop.last %}
+ + + {{ resolves.ticket.get_status_display }} + {{ resolves.ticket.ticket }} {{ resolves.ticket.title }} +
{% endif %} {% empty %} - {% trans "This ticket does not resolve any other" %} + {% trans "This ticket does not resolve any other" %} {% endfor %} + + + {% trans "Depends" %} + + + + + + {% for dep in dependencies %} + {% if forloop.first %}{% endif %} + + + + + {% if forloop.last %}
+ + + {{ dep.depends_on.get_status_display }} + {{ dep.depends_on.ticket }} {{ dep.depends_on.title }} + +
{% endif %} + {% empty %} + {% trans "This ticket has no dependencies." %} + {% endfor %} + + {% endif %} {% if ticket.kbitem %} diff --git a/helpdesk/templates/helpdesk/ticket_resolves_add.html b/helpdesk/templates/helpdesk/ticket_resolves_add.html new file mode 100644 index 00000000..66165dd0 --- /dev/null +++ b/helpdesk/templates/helpdesk/ticket_resolves_add.html @@ -0,0 +1,37 @@ +{% extends "helpdesk/base.html" %}{% load i18n %} + +{% block helpdesk_title %}{% trans "Add Ticket as Dependency" %}{% endblock %} + +{% block helpdesk_breadcrumb %} + + + +{% endblock %} + +{% block helpdesk_body %}{% blocktrans %} +

Add Ticket as Dependency

+ +

Ticket: {{ depends_on }}

+ +

Adding this ticket as dependency will stop you resolving the selected ticket until this ticket has been resolved or closed.

{% endblocktrans %} + +
+ +
+
{% for field in form %} +
+
{{ field }}
+ {% if field.errors %}
{{ field.errors }}
{% endif %} + {% if field.help_text %}
{{ field.help_text }}
{% endif %} + {% endfor %}
+
+ + + +{% csrf_token %}
+ +{% endblock %} diff --git a/helpdesk/urls.py b/helpdesk/urls.py index 02345389..a374bce2 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -88,6 +88,16 @@ urlpatterns = [ staff.ticket_dependency_del, name="ticket_dependency_del", ), + path( + "tickets//resolves/add/", + staff.ticket_resolves_add, + name="ticket_resolves_add", + ), + path( + "tickets//resolves/delete//", + staff.ticket_resolves_del, + name="ticket_resolves_del", + ), path( "tickets//attachment_delete//", staff.attachment_del, diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 8abb9e7b..0f230583 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -54,6 +54,7 @@ from helpdesk.forms import ( TicketCCUserForm, TicketDependencyForm, TicketForm, + TicketResolvesForm, UserSettingsForm ) from helpdesk.lib import queue_template_context, safe_template_context @@ -79,6 +80,7 @@ from helpdesk.user import HelpdeskUser from helpdesk.update_ticket import update_ticket, subscribe_to_ticket_updates, return_ticketccstring_and_show_subscribe import helpdesk.views.abstract_views as abstract_views from helpdesk.views.permissions import MustBeStaffMixin +import itertools import json import re from rest_framework import status @@ -422,8 +424,16 @@ def view_ticket(request, ticket_id): return redirect('helpdesk:edit_ticket_checklist', ticket.id, checklist.id) + open_dependencies = ticket.ticketdependency.filter(depends_on__status__in=Ticket.OPEN_STATUSES) + dependencies = [d for d in itertools.chain( + open_dependencies, + ticket.ticketdependency.all().difference(open_dependencies) + )] + return render(request, 'helpdesk/ticket.html', { 'ticket': ticket, + 'dependencies': dependencies, + 'open_dependencies': open_dependencies, 'submitter_userprofile_url': submitter_userprofile_url, 'form': form, 'active_users': users, @@ -1695,6 +1705,42 @@ def ticket_dependency_del(request, ticket_id, dependency_id): ticket_dependency_del = staff_member_required(ticket_dependency_del) +@helpdesk_staff_member_required +def ticket_resolves_add(request, ticket_id): + depends_on = get_object_or_404(Ticket, id=ticket_id) + ticket_perm_check(request, depends_on) + if request.method == 'POST': + form = TicketResolvesForm(request.POST) + if form.is_valid(): + ticketdependency = form.save(commit=False) + ticketdependency.depends_on = depends_on + if ticketdependency.ticket != ticketdependency.depends_on: + ticketdependency.save() + return HttpResponseRedirect(reverse('helpdesk:view', args=[depends_on.id])) + else: + form = TicketResolvesForm() + return render(request, 'helpdesk/ticket_resolves_add.html', { + 'depends_on': depends_on, + 'form': form, + }) + + +ticket_resolves_add = staff_member_required(ticket_resolves_add) + + +@helpdesk_staff_member_required +def ticket_resolves_del(request, ticket_id, dependency_id): + dependency = get_object_or_404( + TicketDependency, ticket__id=ticket_id, id=dependency_id) + depends_on_id = dependency.depends_on.id + if request.method == 'POST': + dependency.delete() + return HttpResponseRedirect(reverse('helpdesk:view', args=[depends_on_id])) + return render(request, 'helpdesk/ticket_dependency_del.html', {'dependency': dependency}) + +ticket_resolves_del = staff_member_required(ticket_resolves_del) + + @helpdesk_staff_member_required def attachment_del(request, ticket_id, attachment_id): ticket = get_object_or_404(Ticket, id=ticket_id) From 887483a6d643a26b8969568a89446b2ef8e035b6 Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Thu, 6 Jun 2024 00:34:28 +0200 Subject: [PATCH 03/12] Remove status from ticket heading, since it is now shown in the description table --- helpdesk/templates/helpdesk/ticket_desc_table.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index f5003625..9b7da984 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -12,7 +12,7 @@ -

{{ ticket.queue.slug }}-{{ ticket.id }}. {{ ticket.title }} [{{ ticket.get_status }}]

+

{{ ticket.queue.slug }}-{{ ticket.id }}. {{ ticket.title }}

{% blocktrans with ticket.queue as queue %}Queue: {{ queue }}{% endblocktrans %} From b52ab69c42a93d35b85336e3520df3667c1f9c2f Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Sat, 8 Jun 2024 12:00:43 +0200 Subject: [PATCH 04/12] Order dependend ticket with NotIn ORM operator instead of itertools --- helpdesk/apps.py | 4 ++++ helpdesk/orm_operators.py | 7 +++++++ helpdesk/views/staff.py | 8 +++----- 3 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 helpdesk/orm_operators.py diff --git a/helpdesk/apps.py b/helpdesk/apps.py index d316d314..de9d8537 100644 --- a/helpdesk/apps.py +++ b/helpdesk/apps.py @@ -11,3 +11,7 @@ class HelpdeskConfig(AppConfig): def ready(self): from . import webhooks # noqa: F401 + + from django.db.models.fields import Field + from helpdesk.orm_operators import NotIn + Field.register_lookup(NotIn) diff --git a/helpdesk/orm_operators.py b/helpdesk/orm_operators.py new file mode 100644 index 00000000..46c26ff3 --- /dev/null +++ b/helpdesk/orm_operators.py @@ -0,0 +1,7 @@ +from django.db.models.lookups import In + +class NotIn(In): + lookup_name = "not_in" + + def get_rhs_op(self, connection, rhs): + return "NOT IN %s" % rhs diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 0f230583..b95d6569 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -424,11 +424,9 @@ def view_ticket(request, ticket_id): return redirect('helpdesk:edit_ticket_checklist', ticket.id, checklist.id) - open_dependencies = ticket.ticketdependency.filter(depends_on__status__in=Ticket.OPEN_STATUSES) - dependencies = [d for d in itertools.chain( - open_dependencies, - ticket.ticketdependency.all().difference(open_dependencies) - )] + # Open tickets on top + dependencies = list(ticket.ticketdependency.filter(depends_on__status__in=Ticket.OPEN_STATUSES)) \ + + list(ticket.ticketdependency.filter(depends_on__status__not_in=Ticket.OPEN_STATUSES)) return render(request, 'helpdesk/ticket.html', { 'ticket': ticket, From 25c36a6f5c57cc23a3d9ab7c68b68e9ff72ef2e1 Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Sat, 8 Jun 2024 12:11:01 +0200 Subject: [PATCH 05/12] Restrict ticket selection for dependencies and parents When adding a dependent or parent ticket, the choice field in the ticket selection form excludes: - All existing dependencies of the current ticket. - All existing parent tickets of the current ticket. - The current ticket itself. The first two prevent immediate circular references: A dependency cannot be a parent and vice versa. Deeper circular references are not covered by this: a ticket can still be its own grandchild. They also prevent current behavior of throwing an `IntegrityException` when selecting a dependency or parent. The third one prevents also a quirky behavior: until now, specifying the ticket itself as parent or dependency just does not save the dependency and does not issue a warning either. --- helpdesk/forms.py | 12 ++++++++++++ helpdesk/views/staff.py | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 87da3893..33501190 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -591,6 +591,12 @@ class TicketDependencyForm(forms.ModelForm): model = TicketDependency exclude = ('ticket',) + def __init__(self, ticket, *args, **kwargs): + super(TicketDependencyForm,self).__init__(*args, **kwargs) + + # Exclude duplicate tickets, myself, existing dependencies and parents + self.fields['depends_on'].queryset = Ticket.objects.exclude(status=Ticket.DUPLICATE_STATUS).exclude(id=ticket.id).exclude(depends_on__ticket=ticket).exclude(ticketdependency__depends_on=ticket) + class TicketResolvesForm(forms.ModelForm): ''' Adds this ticket as a dependency for a different ticket ''' @@ -599,6 +605,12 @@ class TicketResolvesForm(forms.ModelForm): #exclude = ('depends_on',) fields = ('ticket',) + def __init__(self, ticket, *args, **kwargs): + super(TicketResolvesForm,self).__init__(*args, **kwargs) + + # Exclude duplicate tickets, myself, existing dependencies and parents + self.fields['ticket'].queryset = Ticket.objects.exclude(status=Ticket.DUPLICATE_STATUS).exclude(id=ticket.id).exclude(depends_on__ticket=ticket).exclude(ticketdependency__depends_on=ticket) + class MultipleTicketSelectForm(forms.Form): tickets = forms.ModelMultipleChoiceField( diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index b95d6569..1d80864d 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -1672,7 +1672,7 @@ def ticket_dependency_add(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) ticket_perm_check(request, ticket) if request.method == 'POST': - form = TicketDependencyForm(request.POST) + form = TicketDependencyForm(ticket, request.POST) if form.is_valid(): ticketdependency = form.save(commit=False) ticketdependency.ticket = ticket @@ -1680,7 +1680,7 @@ def ticket_dependency_add(request, ticket_id): ticketdependency.save() return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket.id])) else: - form = TicketDependencyForm() + form = TicketDependencyForm(ticket) return render(request, 'helpdesk/ticket_dependency_add.html', { 'ticket': ticket, 'form': form, @@ -1708,7 +1708,7 @@ def ticket_resolves_add(request, ticket_id): depends_on = get_object_or_404(Ticket, id=ticket_id) ticket_perm_check(request, depends_on) if request.method == 'POST': - form = TicketResolvesForm(request.POST) + form = TicketResolvesForm(depends_on, request.POST) if form.is_valid(): ticketdependency = form.save(commit=False) ticketdependency.depends_on = depends_on @@ -1716,7 +1716,7 @@ def ticket_resolves_add(request, ticket_id): ticketdependency.save() return HttpResponseRedirect(reverse('helpdesk:view', args=[depends_on.id])) else: - form = TicketResolvesForm() + form = TicketResolvesForm(depends_on) return render(request, 'helpdesk/ticket_resolves_add.html', { 'depends_on': depends_on, 'form': form, From bc764b50c1480a8c3bf4ea64b2cfc36aae8b28ab Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Sat, 8 Jun 2024 12:12:56 +0200 Subject: [PATCH 06/12] Fix: itertools are superseded by NotIn ORM operator --- helpdesk/views/staff.py | 1 - 1 file changed, 1 deletion(-) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 1d80864d..a6cc9e32 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -80,7 +80,6 @@ from helpdesk.user import HelpdeskUser from helpdesk.update_ticket import update_ticket, subscribe_to_ticket_updates, return_ticketccstring_and_show_subscribe import helpdesk.views.abstract_views as abstract_views from helpdesk.views.permissions import MustBeStaffMixin -import itertools import json import re from rest_framework import status From 7bf21685b381d8e4b517e647a11156c617c08d54 Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Sat, 8 Jun 2024 12:52:10 +0200 Subject: [PATCH 07/12] Fix: remove unused and undefined context variable --- helpdesk/views/staff.py | 1 - 1 file changed, 1 deletion(-) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index a6cc9e32..0aaf1d40 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -430,7 +430,6 @@ def view_ticket(request, ticket_id): return render(request, 'helpdesk/ticket.html', { 'ticket': ticket, 'dependencies': dependencies, - 'open_dependencies': open_dependencies, 'submitter_userprofile_url': submitter_userprofile_url, 'form': form, 'active_users': users, From 2cb9d382cbd62ff8b37d589c83045d3dd8adc5c7 Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Sat, 8 Jun 2024 18:49:53 +0200 Subject: [PATCH 08/12] Only show open ticket as dependency/parent candidates It makes no sense to make a ticket depend on an already closed ticket, or to make a closed ticket depend on another. The only case I can think of is to create such a relationship in hindsight for documentation purposes. This can be implemented by adding an admin interface or giving the administrator more choices. --- helpdesk/forms.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 33501190..6c981a3c 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -594,8 +594,8 @@ class TicketDependencyForm(forms.ModelForm): def __init__(self, ticket, *args, **kwargs): super(TicketDependencyForm,self).__init__(*args, **kwargs) - # Exclude duplicate tickets, myself, existing dependencies and parents - self.fields['depends_on'].queryset = Ticket.objects.exclude(status=Ticket.DUPLICATE_STATUS).exclude(id=ticket.id).exclude(depends_on__ticket=ticket).exclude(ticketdependency__depends_on=ticket) + # Only open tickets except myself, existing dependencies and parents + self.fields['depends_on'].queryset = Ticket.objects.filter(status__in=Ticket.OPEN_STATUSES).exclude(id=ticket.id).exclude(depends_on__ticket=ticket).exclude(ticketdependency__depends_on=ticket) class TicketResolvesForm(forms.ModelForm): ''' Adds this ticket as a dependency for a different ticket ''' @@ -608,8 +608,8 @@ class TicketResolvesForm(forms.ModelForm): def __init__(self, ticket, *args, **kwargs): super(TicketResolvesForm,self).__init__(*args, **kwargs) - # Exclude duplicate tickets, myself, existing dependencies and parents - self.fields['ticket'].queryset = Ticket.objects.exclude(status=Ticket.DUPLICATE_STATUS).exclude(id=ticket.id).exclude(depends_on__ticket=ticket).exclude(ticketdependency__depends_on=ticket) + # Only open tickets except myself, existing dependencies and parents + self.fields['ticket'].queryset = Ticket.objects.exclude(status__in=Ticket.OPEN_STATUSES).exclude(id=ticket.id).exclude(depends_on__ticket=ticket).exclude(ticketdependency__depends_on=ticket) class MultipleTicketSelectForm(forms.Form): From ec603347c7c8bae1abd39e4247dbd65f5802bc7c Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Sat, 8 Jun 2024 22:32:05 +0200 Subject: [PATCH 09/12] Improve code quality --- helpdesk/views/staff.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 0aaf1d40..d78d342a 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -1676,7 +1676,7 @@ def ticket_dependency_add(request, ticket_id): ticketdependency.ticket = ticket if ticketdependency.ticket != ticketdependency.depends_on: ticketdependency.save() - return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket.id])) + return redirect(ticket.get_absolute_url()) else: form = TicketDependencyForm(ticket) return render(request, 'helpdesk/ticket_dependency_add.html', { @@ -1712,7 +1712,7 @@ def ticket_resolves_add(request, ticket_id): ticketdependency.depends_on = depends_on if ticketdependency.ticket != ticketdependency.depends_on: ticketdependency.save() - return HttpResponseRedirect(reverse('helpdesk:view', args=[depends_on.id])) + return redirect(depends_on.get_absolute_url()) else: form = TicketResolvesForm(depends_on) return render(request, 'helpdesk/ticket_resolves_add.html', { From 54864b2c1e0f5bdadfcd88fbb483cb1ba2d99cbf Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Mon, 10 Jun 2024 10:08:07 +0200 Subject: [PATCH 10/12] Rewrite dependency tickets ordering, using Case annotation --- helpdesk/apps.py | 4 ---- helpdesk/orm_operators.py | 7 ------- helpdesk/views/staff.py | 11 +++++++---- 3 files changed, 7 insertions(+), 15 deletions(-) delete mode 100644 helpdesk/orm_operators.py diff --git a/helpdesk/apps.py b/helpdesk/apps.py index de9d8537..d316d314 100644 --- a/helpdesk/apps.py +++ b/helpdesk/apps.py @@ -11,7 +11,3 @@ class HelpdeskConfig(AppConfig): def ready(self): from . import webhooks # noqa: F401 - - from django.db.models.fields import Field - from helpdesk.orm_operators import NotIn - Field.register_lookup(NotIn) diff --git a/helpdesk/orm_operators.py b/helpdesk/orm_operators.py deleted file mode 100644 index 46c26ff3..00000000 --- a/helpdesk/orm_operators.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.db.models.lookups import In - -class NotIn(In): - lookup_name = "not_in" - - def get_rhs_op(self, connection, rhs): - return "NOT IN %s" % rhs diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index d78d342a..fc82263a 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -20,7 +20,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.core.handlers.wsgi import WSGIRequest from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator -from django.db.models import Q +from django.db.models import Q, Case, Value, When from django.forms import HiddenInput, inlineformset_factory, TextInput from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404, redirect, render @@ -423,9 +423,12 @@ def view_ticket(request, ticket_id): return redirect('helpdesk:edit_ticket_checklist', ticket.id, checklist.id) - # Open tickets on top - dependencies = list(ticket.ticketdependency.filter(depends_on__status__in=Ticket.OPEN_STATUSES)) \ - + list(ticket.ticketdependency.filter(depends_on__status__not_in=Ticket.OPEN_STATUSES)) + # List open tickets on top + dependencies = ticket.ticketdependency.annotate( + rank=Case( + When(depends_on__status__in=Ticket.OPEN_STATUSES, then=Value('1')), + default=Value('2') + )).order_by('rank') return render(request, 'helpdesk/ticket.html', { 'ticket': ticket, From 0b38f1d5bdee9c4e069b5610025f94389e326c2f Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Mon, 10 Jun 2024 17:27:43 +0200 Subject: [PATCH 11/12] Remove superfluous use of Value() in When clause --- helpdesk/views/staff.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index fc82263a..f89b5d08 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -20,7 +20,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.core.handlers.wsgi import WSGIRequest from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator -from django.db.models import Q, Case, Value, When +from django.db.models import Q, Case, When from django.forms import HiddenInput, inlineformset_factory, TextInput from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404, redirect, render @@ -426,8 +426,8 @@ def view_ticket(request, ticket_id): # List open tickets on top dependencies = ticket.ticketdependency.annotate( rank=Case( - When(depends_on__status__in=Ticket.OPEN_STATUSES, then=Value('1')), - default=Value('2') + When(depends_on__status__in=Ticket.OPEN_STATUSES, then=1), + default=2 )).order_by('rank') return render(request, 'helpdesk/ticket.html', { From 665e4008e7c72b204c5206d64a5a96c6c3b2e962 Mon Sep 17 00:00:00 2001 From: Georg Lehner Date: Mon, 10 Jun 2024 17:33:08 +0200 Subject: [PATCH 12/12] Cleanup Dependency/Resolves form code --- helpdesk/forms.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 6c981a3c..81e9dcbe 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -589,7 +589,7 @@ class TicketDependencyForm(forms.ModelForm): class Meta: model = TicketDependency - exclude = ('ticket',) + fields = ('depends_on',) def __init__(self, ticket, *args, **kwargs): super(TicketDependencyForm,self).__init__(*args, **kwargs) @@ -602,7 +602,6 @@ class TicketResolvesForm(forms.ModelForm): class Meta: model = TicketDependency - #exclude = ('depends_on',) fields = ('ticket',) def __init__(self, ticket, *args, **kwargs):