From 05dfde59cb7b395623ab34a932303752943315d5 Mon Sep 17 00:00:00 2001 From: Sam Splunks <72095718+samsplunks@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:16:20 +0000 Subject: [PATCH] Include opening hours for follow-up time_spent calculation --- docs/settings.rst | 20 ++++++++++++++++++++ helpdesk/lib.py | 37 +++++++++++++++++++++++++++++++++++++ helpdesk/models.py | 30 ++++++++++++++++++++++++++++-- helpdesk/settings.py | 5 +++++ 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 9dd96f40..a78fd2af 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -298,6 +298,26 @@ Time Tracking Options **Default:** ``HELPDESK_FOLLOWUP_TIME_SPENT_AUTO = False`` +- **HELPDESK_FOLLOWUP_TIME_SPENT_OPENING_HOURS** If defined, calculates follow-up 'time_spent' according to open hours. + + **Default:** ``HELPDESK_FOLLOWUP_TIME_SPENT_OPENING_HOURS = {}`` + + If HELPDESK_FOLLOWUP_TIME_SPENT_AUTO is ``True``, you may set open hours to remove off hours from 'time_spent':: + + HELPDESK_FOLLOWUP_TIME_SPENT_OPENING_HOURS = { + "monday": (8.5, 19), + "tuesday": (8.5, 19), + "wednesday": (8.5, 19), + "thursday": (8.5, 19), + "friday": (8.5, 19), + "saturday": (0, 0), + "sunday": (0, 0), + } + + Valid hour values must be set between 0 and 23.9999. + In this example 8.5 is interpreted as 8:30AM, saturdays and sundays don't count. + + Staff Ticket Creation Settings ------------------------------ diff --git a/helpdesk/lib.py b/helpdesk/lib.py index ac0d46bc..10d53736 100644 --- a/helpdesk/lib.py +++ b/helpdesk/lib.py @@ -194,3 +194,40 @@ def convert_value(value): return value.strftime(CUSTOMFIELD_TIME_FORMAT) else: return value + + +def daily_time_spent_calculation(earliest, latest, open_hours): + """Returns timedelta for a single day time interval according to open hours.""" + + # avoid rendering day in different locale + weekday = ('monday', 'tuesday', 'wednesday', 'thursday', + 'friday', 'saturday', 'sunday')[earliest.weekday()] + + # enforce correct settings + MIDNIGHT = 23.9999 + start, end = open_hours.get(weekday, (0, MIDNIGHT)) + if not 0 <= start <= end <= MIDNIGHT: + start, end = 0, MIDNIGHT + + # transform decimals to minutes + start_hour, start_minute, start_second = int(start), int(start % 1 * 60), int(start * 60 % 1 * 60) + end_hour, end_minute, end_second = int(end), int(end % 1 * 60), int(end * 60 % 1 * 60) + + # translate time for delta calculation + earliest_f = earliest.hour + earliest.minute / 60 + latest_f = latest.hour + latest.minute / 60 + + if earliest_f < start: + earliest = earliest.replace(hour=start_hour, minute=start_minute, second=start_second) + elif earliest_f >= end: + earliest = earliest.replace(hour=end_hour, minute=end_minute, second=end_second) + + if latest_f < start: + latest = latest.replace(hour=start_hour, minute=start_minute, second=start_second) + elif latest_f >= end: + latest = latest.replace(hour=end_hour, minute=end_minute, second=end_second) + + day_delta = latest - earliest + + # returns up to 86399 seconds + return day_delta.seconds \ No newline at end of file diff --git a/helpdesk/models.py b/helpdesk/models.py index a258a315..9cd4de81 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -8,7 +8,7 @@ models.py - Model (and hence database) definitions. This is the core of the """ -from .lib import convert_value +from .lib import convert_value, daily_time_spent_calculation from .templated_email import send_templated_mail from .validators import validate_file_extension from .webhooks import send_new_ticket_webhook @@ -1008,7 +1008,7 @@ class FollowUp(models.Model): latest_time = latest_fup.date except ObjectDoesNotExist: latest_time = t.created - self.time_spent = now - latest_time + self.time_spent = self.time_spent_calculation(latest_time, now) t.modified = now t.save() super(FollowUp, self).save(*args, **kwargs) @@ -1020,6 +1020,32 @@ class FollowUp(models.Model): def time_spent_formated(self): return format_time_spent(self.time_spent) + def time_spent_calculation(self, earliest, latest): + "Returns timedelta according to rules settings." + + time_spent_seconds = 0 + open_hours = helpdesk_settings.FOLLOWUP_TIME_SPENT_OPENING_HOURS + + # split time interval by days + days = latest.toordinal() - earliest.toordinal() + if days: + for day in range(days + 1): + if day == 0: + start_day_time = earliest + end_day_time = earliest.replace(hour=23, minute=59, second=59) + elif day == days: + start_day_time = latest.replace(hour=0, minute=0, second=0) + end_day_time = latest + else: + middle_day_time = earliest + timedelta(days=day) + 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) + time_spent_seconds += daily_time_spent_calculation(start_day_time, end_day_time, open_hours) + # handle same day + else: + time_spent_seconds += daily_time_spent_calculation(earliest, latest, open_hours) + + return datetime.timedelta(seconds=time_spent_seconds) class TicketChange(models.Model): """ diff --git a/helpdesk/settings.py b/helpdesk/settings.py index 220bc424..96eac1ee 100644 --- a/helpdesk/settings.py +++ b/helpdesk/settings.py @@ -161,6 +161,11 @@ FOLLOWUP_TIME_SPENT_AUTO = getattr(settings, 'HELPDESK_FOLLOWUP_TIME_SPENT_AUTO', False) +# Calculate time_spent according to open hours +FOLLOWUP_TIME_SPENT_OPENING_HOURS = getattr(settings, + 'HELPDESK_FOLLOWUP_TIME_SPENT_OPENING_HOURS', + {}) + ############################ # options for public pages # ############################