From 42be32b17b0234b959dddd5958c1d3ec90a005f7 Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Mon, 8 Apr 2024 12:33:23 +0000
Subject: [PATCH 01/11] Allow to track queue change in follow-ups
---
helpdesk/templates/helpdesk/ticket.html | 3 +++
helpdesk/update_ticket.py | 14 ++++++++++++++
helpdesk/views/staff.py | 4 ++++
3 files changed, 21 insertions(+)
diff --git a/helpdesk/templates/helpdesk/ticket.html b/helpdesk/templates/helpdesk/ticket.html
index b4eb61db..6efdb8e1 100644
--- a/helpdesk/templates/helpdesk/ticket.html
+++ b/helpdesk/templates/helpdesk/ticket.html
@@ -150,6 +150,9 @@
+
+
+
{{ form.due_date }}
diff --git a/helpdesk/update_ticket.py b/helpdesk/update_ticket.py
index 711566ba..cd2542a3 100644
--- a/helpdesk/update_ticket.py
+++ b/helpdesk/update_ticket.py
@@ -12,6 +12,7 @@ from helpdesk.decorators import (
is_helpdesk_staff,
)
from helpdesk.models import (
+ Queue,
FollowUp,
Ticket,
TicketCC,
@@ -200,6 +201,7 @@ def update_ticket(
owner=-1,
ticket_title=None,
priority=-1,
+ queue=-1,
new_status=None,
time_spent=None,
due_date=None,
@@ -213,6 +215,8 @@ def update_ticket(
title = ticket.title
if priority == -1:
priority = ticket.priority
+ if queue == -1:
+ queue = ticket.queue.id
if new_status is None:
new_status = ticket.status
if new_checklists is None:
@@ -302,6 +306,16 @@ def update_ticket(
c.save()
ticket.priority = priority
+ if queue != ticket.queue.id:
+ c = TicketChange(
+ followup=f,
+ field=_('Queue'),
+ old_value=ticket.queue.id,
+ new_value=queue,
+ )
+ c.save()
+ ticket.queue.id = queue
+
if due_date != ticket.due_date:
c = TicketChange(
followup=f,
diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py
index e1cdc967..8abb9e7b 100644
--- a/helpdesk/views/staff.py
+++ b/helpdesk/views/staff.py
@@ -428,6 +428,7 @@ def view_ticket(request, ticket_id):
'form': form,
'active_users': users,
'priorities': Ticket.PRIORITY_CHOICES,
+ 'queues': queue_choices,
'preset_replies': PreSetReply.objects.filter(
Q(queues=ticket.queue) | Q(queues__isnull=True)),
'ticketcc_string': ticketcc_string,
@@ -566,6 +567,7 @@ def update_ticket_view(request, ticket_id, public=False):
title = request.POST.get('title', '')
owner = int(request.POST.get('owner', -1))
priority = int(request.POST.get('priority', ticket.priority))
+ queue = int(request.POST.get('queue', ticket.queue.id))
# Check if a change happened on checklists
new_checklists = {}
@@ -589,6 +591,7 @@ def update_ticket_view(request, ticket_id, public=False):
new_status == ticket.status,
title == ticket.title,
priority == int(ticket.priority),
+ queue == int(ticket.queue.id),
due_date == ticket.due_date,
(owner == -1) or (not owner and not ticket.assigned_to) or
(owner and User.objects.get(id=owner) == ticket.assigned_to),
@@ -605,6 +608,7 @@ def update_ticket_view(request, ticket_id, public=False):
public = request.POST.get('public', False),
owner = int(request.POST.get('owner', -1)),
priority = int(request.POST.get('priority', -1)),
+ queue = int(request.POST.get('queue', -1)),
new_status = new_status,
time_spent = get_time_spent_from_request(request),
due_date = get_due_date_from_request_or_ticket(request, ticket),
From bb4e05ba39abc533ef2de4764842e6e7280789c0 Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Mon, 8 Apr 2024 12:43:11 +0000
Subject: [PATCH 02/11] Removed unused Queue model import
---
helpdesk/update_ticket.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/helpdesk/update_ticket.py b/helpdesk/update_ticket.py
index cd2542a3..a1746398 100644
--- a/helpdesk/update_ticket.py
+++ b/helpdesk/update_ticket.py
@@ -12,7 +12,6 @@ from helpdesk.decorators import (
is_helpdesk_staff,
)
from helpdesk.models import (
- Queue,
FollowUp,
Ticket,
TicketCC,
From e526c21aeff5c6c84411af1c5ec8b40a197c81f5 Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Tue, 9 Apr 2024 12:05:32 +0000
Subject: [PATCH 03/11] Update ticket queue foreign key id
---
helpdesk/update_ticket.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/helpdesk/update_ticket.py b/helpdesk/update_ticket.py
index a1746398..5a2e02e1 100644
--- a/helpdesk/update_ticket.py
+++ b/helpdesk/update_ticket.py
@@ -313,7 +313,7 @@ def update_ticket(
new_value=queue,
)
c.save()
- ticket.queue.id = queue
+ ticket.queue_id = queue
if due_date != ticket.due_date:
c = TicketChange(
From e372f4447b19b5ebc62be0081c375d7a609bae5a Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Tue, 9 Apr 2024 12:18:43 +0000
Subject: [PATCH 04/11] Adding test_update_ticket_queue test
---
helpdesk/tests/test_ticket_actions.py | 44 +++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/helpdesk/tests/test_ticket_actions.py b/helpdesk/tests/test_ticket_actions.py
index abf95c3f..0daf1f1d 100644
--- a/helpdesk/tests/test_ticket_actions.py
+++ b/helpdesk/tests/test_ticket_actions.py
@@ -7,6 +7,7 @@ from helpdesk import settings as helpdesk_settings
from helpdesk.models import CustomField, Queue, Ticket
from helpdesk.templatetags.ticket_to_link import num_to_link
from helpdesk.user import HelpdeskUser
+from django.utils.translation import gettext_lazy as _
try: # python 3
@@ -323,3 +324,46 @@ class TicketActionsTestCase(TestCase):
ticket_1_follow_up, ticket_2_follow_up])
self.assertEqual(list(ticket_1.ticketcc_set.all()),
[ticket_1_cc, ticket_2_cc])
+
+ def test_update_ticket_queue(self):
+ """Tests whether user can change the queue in the Respond to this ticket section."""
+
+ # log user in
+ self.loginUser()
+
+ # create ticket
+ initial_data = {
+ 'title': 'Queue change ticket test',
+ 'queue': self.queue_public,
+ 'assigned_to': self.user,
+ 'status': Ticket.OPEN_STATUS,
+ }
+ ticket = Ticket.objects.create(**initial_data)
+ ticket_id = ticket.id
+
+ # initial queue
+ self.assertEqual(ticket.queue, self.queue_public)
+
+ # POST first follow-up with new queue
+ new_queue = Queue.objects.create(
+ title='New Queue',
+ slug='newqueue',
+ )
+ post_data = {
+ 'comment': 'first follow-up in new queue',
+ 'queue': str(new_queue.id),
+ }
+ response = self.client.post(reverse('helpdesk:update',
+ kwargs={'ticket_id': ticket_id}),
+ post_data)
+
+ # queue was correctly modified
+ ticket.refresh_from_db()
+ self.assertEqual(ticket.queue, new_queue)
+
+ # ticket change was saved
+ latest_fup = ticket.followup_set.latest('date')
+ latest_ticketchange = latest_fup.ticketchange_set.latest('id')
+ self.assertEqual(latest_ticketchange.field, _('Queue'))
+ self.assertEqual(int(latest_ticketchange.old_value), self.queue_public.id)
+ self.assertEqual(int(latest_ticketchange.new_value), new_queue.id)
\ No newline at end of file
From f4ab7a5226ea26495630cf249b11c6be085671eb Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Tue, 9 Apr 2024 13:25:35 +0000
Subject: [PATCH 05/11] Refactored time_spent calculation queue exclusions to
reflect queue changes over time
---
helpdesk/models.py | 67 +++++++++++++++++++++++++++++++---------------
1 file changed, 45 insertions(+), 22 deletions(-)
diff --git a/helpdesk/models.py b/helpdesk/models.py
index 1781093f..a9ce9a1b 100644
--- a/helpdesk/models.py
+++ b/helpdesk/models.py
@@ -1006,36 +1006,61 @@ class FollowUp(models.Model):
def time_spent_calculation(self):
"Returns timedelta according to rules settings."
+
+ open_hours = helpdesk_settings.FOLLOWUP_TIME_SPENT_OPENING_HOURS
+ holidays = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_HOLIDAYS
+ exclude_statuses = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_STATUSES
+ exclude_queues = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES
+
+ # queryset for this ticket previous follow-ups
+ prev_fup_qs = self.ticket.followup_set.all()
+ if self.id:
+ # if the follow-up exist in DB, only keep previous follow-ups
+ prev_fup_qs = prev_fup_qs.filter(date__lt=self.date)
+
+ # handle exclusions
+
+ # extract previous status from follow-up or ticket for exclusion check
+ if exclude_statuses:
+ try:
+ prev_fup = prev_fup_qs.latest("date")
+ prev_status = prev_fup.new_status
+ except ObjectDoesNotExist:
+ prev_status = self.ticket.status
+
+ # don't calculate status exclusions
+ if prev_status in exclude_statuses:
+ return datetime.timedelta(seconds=0)
+
+ # find the previous queue for exclusion check
+ if exclude_queues:
+ try:
+ prev_fup_ids = prev_fup_qs.values_list('id', flat=True)
+ prev_queue_change = TicketChange.objects.filter(followup_id__in=prev_fup_ids,
+ field=_('Queue')).latest('id')
+ prev_queue = Queue.objects.get(pk=prev_queue_change.new_value)
+ prev_queue_slug = prev_queue.slug
+ except ObjectDoesNotExist:
+ prev_queue_slug = self.ticket.queue.slug
+
+ # don't calculate queue exclusions
+ if prev_queue_slug in exclude_queues:
+ return datetime.timedelta(seconds=0)
+
+ # no exclusion found
+
+ time_spent_seconds = 0
# extract earliest from previous follow-up or ticket
try:
- prev_fup_qs = self.ticket.followup_set.all()
- if self.id:
- prev_fup_qs = prev_fup_qs.filter(id__lt=self.id)
prev_fup = prev_fup_qs.latest("date")
earliest = prev_fup.date
except ObjectDoesNotExist:
earliest = self.ticket.created
- # extract previous status from follow-up or ticket
- try:
- prev_fup_qs = self.ticket.followup_set.exclude(new_status__isnull=True)
- if self.id:
- prev_fup_qs = prev_fup_qs.filter(id__lt=self.id)
- prev_fup = prev_fup_qs.latest("date")
- prev_status = prev_fup.new_status
- except ObjectDoesNotExist:
- prev_status = self.ticket.status
-
# latest time is current follow-up date
latest = self.date
- time_spent_seconds = 0
- open_hours = helpdesk_settings.FOLLOWUP_TIME_SPENT_OPENING_HOURS
- holidays = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_HOLIDAYS
- exclude_statuses = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_STATUSES
- exclude_queues = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES
-
# split time interval by days
days = latest.toordinal() - earliest.toordinal()
for day in range(days + 1):
@@ -1054,9 +1079,7 @@ class FollowUp(models.Model):
start_day_time = middle_day_time.replace(hour=0, minute=0, second=0)
end_day_time = middle_day_time.replace(hour=23, minute=59, second=59, microsecond=999999)
- if (start_day_time.strftime("%Y-%m-%d") not in holidays and
- prev_status not in exclude_statuses and
- self.ticket.queue.slug not in exclude_queues):
+ if start_day_time.strftime("%Y-%m-%d") not in holidays:
time_spent_seconds += daily_time_spent_calculation(start_day_time, end_day_time, open_hours)
return datetime.timedelta(seconds=time_spent_seconds)
From 6e845f6351e9adce9a07137fdb82089cc5a16496 Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Tue, 9 Apr 2024 13:27:42 +0000
Subject: [PATCH 06/11] Adding test for multiple exclusion queues through
follow-ups
---
helpdesk/tests/test_time_spent_auto.py | 70 +++++++++++++++++++++++++-
1 file changed, 69 insertions(+), 1 deletion(-)
diff --git a/helpdesk/tests/test_time_spent_auto.py b/helpdesk/tests/test_time_spent_auto.py
index 2cdaab91..ac3a5a0b 100644
--- a/helpdesk/tests/test_time_spent_auto.py
+++ b/helpdesk/tests/test_time_spent_auto.py
@@ -2,7 +2,10 @@
from datetime import datetime, timedelta
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import User
+from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
+from django.test.client import Client
+from django.urls import reverse
from helpdesk.models import FollowUp, Queue, Ticket
from helpdesk import settings as helpdesk_settings
import uuid
@@ -33,6 +36,21 @@ class TimeSpentAutoTestCase(TestCase):
is_active=True
)
+ self.client = Client()
+
+
+ def loginUser(self, is_staff=True):
+ """Create a staff user and login"""
+ User = get_user_model()
+ self.user = User.objects.create(
+ username='User_1',
+ is_staff=is_staff,
+ )
+ self.user.set_password('pass')
+ self.user.save()
+ self.client.login(username='User_1', password='pass')
+
+
def test_add_two_followups_time_spent_auto(self):
"""Tests automatic time_spent calculation."""
# activate automatic calculation
@@ -251,4 +269,54 @@ class TimeSpentAutoTestCase(TestCase):
self.assertEqual(ticket.time_spent.total_seconds(), 0.0)
# Remove queues exclusion
- helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES = ()
\ No newline at end of file
+ helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES = ()
+
+ def test_http_followup_time_spent_auto_exclude_queues(self):
+ """Tests automatic time_spent calculation queues exclusion with client"""
+
+ # activate automatic calculation
+ helpdesk_settings.FOLLOWUP_TIME_SPENT_AUTO = True
+ helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES = ('stop1', 'stop2')
+
+ # make staff user
+ self.loginUser()
+
+ # create queues
+ queues_sequence = ('new', 'stop1', 'resume1', 'stop2', 'resume2', 'end')
+ queues = dict()
+ for slug in queues_sequence:
+ queues[slug] = Queue.objects.create(
+ title=slug,
+ slug=slug,
+ )
+
+ # create ticket
+ initial_data = {
+ 'title': 'Queue change ticket test',
+ 'queue': queues['new'],
+ 'assigned_to': self.user,
+ 'status': Ticket.OPEN_STATUS,
+ 'created': datetime.strptime('2024-04-09T08:00:00+00:00', "%Y-%m-%dT%H:%M:%S%z")
+ }
+ ticket = Ticket.objects.create(**initial_data)
+
+ # create a change queue follow-up every hour
+ # first follow-up created at the same time of the ticket without queue change
+ # new --1h--> stop1 --0h--> resume1 --1h--> stop2 --0h--> --1h--> end
+ for (i, queue) in enumerate(queues_sequence):
+ # create follow-up
+ post_data = {
+ 'comment': 'ticket in queue {}'.format(queue),
+ 'queue': queues[queue].id,
+ }
+ response = self.client.post(reverse('helpdesk:update', kwargs={
+ 'ticket_id': ticket.id}), post_data)
+ latest_fup = ticket.followup_set.latest('id')
+ latest_fup.date = ticket.created + timedelta(minutes=60 * i)
+ latest_fup.time_spent = None
+ latest_fup.save()
+
+ # total ticket time for followups is 5 hours
+ self.assertEqual(latest_fup.date - ticket.created, timedelta(hours=5))
+ # calculated time spent with 2 hours exclusion is 3 hours
+ self.assertEqual(ticket.time_spent.total_seconds(), 3 * 3600.0)
\ No newline at end of file
From d1af580483b1c190df912b969fafaa5c24743bde Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Tue, 9 Apr 2024 13:32:11 +0000
Subject: [PATCH 07/11] Remove queues exclusion at the end of test
---
helpdesk/tests/test_time_spent_auto.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/helpdesk/tests/test_time_spent_auto.py b/helpdesk/tests/test_time_spent_auto.py
index ac3a5a0b..792a3843 100644
--- a/helpdesk/tests/test_time_spent_auto.py
+++ b/helpdesk/tests/test_time_spent_auto.py
@@ -319,4 +319,7 @@ class TimeSpentAutoTestCase(TestCase):
# total ticket time for followups is 5 hours
self.assertEqual(latest_fup.date - ticket.created, timedelta(hours=5))
# calculated time spent with 2 hours exclusion is 3 hours
- self.assertEqual(ticket.time_spent.total_seconds(), 3 * 3600.0)
\ No newline at end of file
+ self.assertEqual(ticket.time_spent.total_seconds(), 3 * 3600.0)
+
+ # remove queues exclusion
+ helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES = ()
\ No newline at end of file
From aa5e2d0c67aa64f9887aea945fc6d19edee0c703 Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Tue, 9 Apr 2024 13:33:20 +0000
Subject: [PATCH 08/11] Fixed test comment
---
helpdesk/tests/test_time_spent_auto.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/helpdesk/tests/test_time_spent_auto.py b/helpdesk/tests/test_time_spent_auto.py
index 792a3843..a7ea0bbf 100644
--- a/helpdesk/tests/test_time_spent_auto.py
+++ b/helpdesk/tests/test_time_spent_auto.py
@@ -302,7 +302,7 @@ class TimeSpentAutoTestCase(TestCase):
# create a change queue follow-up every hour
# first follow-up created at the same time of the ticket without queue change
- # new --1h--> stop1 --0h--> resume1 --1h--> stop2 --0h--> --1h--> end
+ # new --1h--> stop1 --0h--> resume1 --1h--> stop2 --0h--> resume2 --1h--> end
for (i, queue) in enumerate(queues_sequence):
# create follow-up
post_data = {
From 62ef86a04732f4d971d8d6ba5ec6addf8c4f0e58 Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Tue, 9 Apr 2024 13:45:41 +0000
Subject: [PATCH 09/11] Settings assertion values in hours
---
helpdesk/tests/test_time_spent_auto.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/helpdesk/tests/test_time_spent_auto.py b/helpdesk/tests/test_time_spent_auto.py
index a7ea0bbf..56b25ad0 100644
--- a/helpdesk/tests/test_time_spent_auto.py
+++ b/helpdesk/tests/test_time_spent_auto.py
@@ -312,14 +312,14 @@ class TimeSpentAutoTestCase(TestCase):
response = self.client.post(reverse('helpdesk:update', kwargs={
'ticket_id': ticket.id}), post_data)
latest_fup = ticket.followup_set.latest('id')
- latest_fup.date = ticket.created + timedelta(minutes=60 * i)
+ latest_fup.date = ticket.created + timedelta(hours=i)
latest_fup.time_spent = None
latest_fup.save()
# total ticket time for followups is 5 hours
self.assertEqual(latest_fup.date - ticket.created, timedelta(hours=5))
# calculated time spent with 2 hours exclusion is 3 hours
- self.assertEqual(ticket.time_spent.total_seconds(), 3 * 3600.0)
+ self.assertEqual(ticket.time_spent.total_seconds(), timedelta(hours=3).total_seconds())
# remove queues exclusion
helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES = ()
\ No newline at end of file
From 5b39c9aeeb0a6c1246199e499bd67f914964fcb3 Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Fri, 12 Apr 2024 10:39:58 +0200
Subject: [PATCH 10/11] Update forloop for queues with explicit variable names
in ticket template
Co-authored-by: Benbb96
---
helpdesk/templates/helpdesk/ticket.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/helpdesk/templates/helpdesk/ticket.html b/helpdesk/templates/helpdesk/ticket.html
index 6efdb8e1..8f0ede76 100644
--- a/helpdesk/templates/helpdesk/ticket.html
+++ b/helpdesk/templates/helpdesk/ticket.html
@@ -151,7 +151,7 @@
-
+
{{ form.due_date }}
From a775622521f7cda25107c03772fe74fcc13d4e68 Mon Sep 17 00:00:00 2001
From: Sam Splunks <72095718+samsplunks@users.noreply.github.com>
Date: Fri, 12 Apr 2024 10:44:56 +0200
Subject: [PATCH 11/11] Create ticket change through instance relationship
Co-authored-by: Benbb96
---
helpdesk/update_ticket.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/helpdesk/update_ticket.py b/helpdesk/update_ticket.py
index 5a2e02e1..deca0b40 100644
--- a/helpdesk/update_ticket.py
+++ b/helpdesk/update_ticket.py
@@ -306,13 +306,11 @@ def update_ticket(
ticket.priority = priority
if queue != ticket.queue.id:
- c = TicketChange(
- followup=f,
+ c = f.ticketchange_set.create(
field=_('Queue'),
old_value=ticket.queue.id,
new_value=queue,
)
- c.save()
ticket.queue_id = queue
if due_date != ticket.due_date: