From 6ceb89a5cb03221c5aca2f8e2577d15ba54c881a Mon Sep 17 00:00:00 2001 From: Jachym Cepicky Date: Wed, 6 Feb 2019 14:24:43 +0100 Subject: [PATCH] basic support for time spend of tikets and follow-ups --- helpdesk/admin.py | 9 ++++-- helpdesk/migrations/0024_time_spent.py | 18 ++++++++++++ helpdesk/models.py | 28 +++++++++++++++++++ helpdesk/serializers.py | 4 ++- .../templates/helpdesk/followup_edit.html | 2 ++ helpdesk/templates/helpdesk/report_index.html | 2 ++ helpdesk/templates/helpdesk/ticket.html | 8 ++++++ .../templates/helpdesk/ticket_desc_table.html | 4 +++ helpdesk/templates/helpdesk/ticket_list.html | 2 ++ .../templates/helpdesk/ticket_list_table.html | 1 + helpdesk/views/staff.py | 16 +++++++++-- 11 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 helpdesk/migrations/0024_time_spent.py diff --git a/helpdesk/admin.py b/helpdesk/admin.py index 58d5ef09..d2e4914f 100644 --- a/helpdesk/admin.py +++ b/helpdesk/admin.py @@ -14,7 +14,8 @@ class QueueAdmin(admin.ModelAdmin): @admin.register(Ticket) class TicketAdmin(admin.ModelAdmin): - list_display = ('title', 'status', 'assigned_to', 'queue', 'hidden_submitter_email',) + list_display = ('title', 'status', 'assigned_to', 'queue', + 'hidden_submitter_email', 'time_spent') date_hierarchy = 'created' list_filter = ('queue', 'assigned_to', 'status') @@ -28,6 +29,9 @@ class TicketAdmin(admin.ModelAdmin): return ticket.submitter_email hidden_submitter_email.short_description = _('Submitter E-Mail') + def time_spent(self, ticket): + return ticket.time_spent + class TicketChangeInline(admin.StackedInline): model = TicketChange @@ -40,7 +44,8 @@ class AttachmentInline(admin.StackedInline): @admin.register(FollowUp) class FollowUpAdmin(admin.ModelAdmin): inlines = [TicketChangeInline, AttachmentInline] - list_display = ('ticket_get_ticket_for_url', 'title', 'date', 'ticket', 'user', 'new_status') + list_display = ('ticket_get_ticket_for_url', 'title', 'date', 'ticket', + 'user', 'new_status', 'time_spent') list_filter = ('user', 'date', 'new_status') def ticket_get_ticket_for_url(self, obj): diff --git a/helpdesk/migrations/0024_time_spent.py b/helpdesk/migrations/0024_time_spent.py new file mode 100644 index 00000000..bbb0f22f --- /dev/null +++ b/helpdesk/migrations/0024_time_spent.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.5 on 2019-02-06 13:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('helpdesk', '0023_add_enable_notifications_on_email_events_to_ticket'), + ] + + operations = [ + migrations.AddField( + model_name='followup', + name='time_spent', + field=models.DurationField(blank=True, help_text='Time spent on this follow up', null=True), + ), + ] diff --git a/helpdesk/models.py b/helpdesk/models.py index e05c4b7d..c772ade0 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -17,6 +17,7 @@ from django.utils import timezone from django.utils.translation import ugettext_lazy as _, ugettext from io import StringIO import re +import datetime import uuid @@ -301,6 +302,17 @@ class Queue(models.Model): return u'%s <%s>' % (self.title, self.email_address) from_address = property(_from_address) + @property + def time_spent(self): + """Return back total time spent on the ticket. This is calculated value + based on total sum from all FollowUps + """ + total = datetime.timedelta(0) + for val in self.ticket_set.all(): + if val.time_spent: + total = total + val.time_spent + return total + def prepare_permission_name(self): """Prepare internally the codename for the permission and store it in permission_name. :return: The codename that can be used to create a new Permission object. @@ -497,6 +509,17 @@ class Ticket(models.Model): default=mk_secret, ) + @property + def time_spent(self): + """Return back total time spent on the ticket. This is calculated value + based on total sum from all FollowUps + """ + total = datetime.timedelta(0) + for val in self.followup_set.all(): + if val.time_spent: + total = total + val.time_spent + return total + def send(self, roles, dont_send_to=None, **kwargs): """ Send notifications to everyone interested in this ticket. @@ -771,6 +794,11 @@ class FollowUp(models.Model): objects = FollowUpManager() + time_spent = models.DurationField( + help_text=_("Time spent on this follow up"), + blank=True, null=True + ) + class Meta: ordering = ('date',) verbose_name = _('Follow-up') diff --git a/helpdesk/serializers.py b/helpdesk/serializers.py index e5c7dc7d..046509c4 100644 --- a/helpdesk/serializers.py +++ b/helpdesk/serializers.py @@ -22,7 +22,9 @@ class TicketSerializer(serializers.ModelSerializer): class Meta: model = Ticket # fields = '__all__' - fields = ('ticket', 'id', 'priority', 'title', 'queue', 'status', 'created', 'due_date', 'assigned_to', 'row_class') + fields = ('ticket', 'id', 'priority', 'title', 'queue', 'status', + 'created', 'due_date', 'assigned_to', 'row_class', + 'time_spent') def get_ticket(self, obj): return (str(obj.id) + " " + obj.ticket) diff --git a/helpdesk/templates/helpdesk/followup_edit.html b/helpdesk/templates/helpdesk/followup_edit.html index 0afff6e8..bbd2f0ca 100644 --- a/helpdesk/templates/helpdesk/followup_edit.html +++ b/helpdesk/templates/helpdesk/followup_edit.html @@ -46,6 +46,8 @@
{{ form.new_status }}

If the status was changed, what was it changed to?

+
+
{{ form.time_spent }}

{% csrf_token %} diff --git a/helpdesk/templates/helpdesk/report_index.html b/helpdesk/templates/helpdesk/report_index.html index 102dfed9..b570e071 100644 --- a/helpdesk/templates/helpdesk/report_index.html +++ b/helpdesk/templates/helpdesk/report_index.html @@ -45,6 +45,7 @@ {% trans "Open" %} {% trans "Resolved" %} {% trans "Closed" %} + {% trans "Time spent" %} @@ -54,6 +55,7 @@ {% if queue.open %}{% endif %}{{ queue.open }}{% if queue.open %}{% endif %} {% if queue.resolved %}{% endif %}{{ queue.resolved }}{% if queue.resolved %}{% endif %} {% if queue.closed %}{% endif %}{{ queue.closed }}{% if queue.closed %}{% endif %} + {{ queue.time_spent }} {% empty %} {% trans "There are no unassigned tickets." %} diff --git a/helpdesk/templates/helpdesk/ticket.html b/helpdesk/templates/helpdesk/ticket.html index da90dd61..08fe24bc 100644 --- a/helpdesk/templates/helpdesk/ticket.html +++ b/helpdesk/templates/helpdesk/ticket.html @@ -46,6 +46,9 @@ {% if followup.comment %}

{{ followup.comment|force_escape|urlizetrunc:50|num_to_link|linebreaksbr }}

{% endif %} + {% if followup.time_spent %} + {% trans "Time spent" %}: {{ followup.time_spent }}

+ {% endif %} {% for change in followup.ticketchange_set.all %} {% if forloop.first %}
diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index ea8a5d1b..46db483e 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -222,6 +222,7 @@ {% trans "Created" %} {% trans "Due Date" %} {% trans "Owner" %} + {% trans "Time Spent" %} {% if not server_side %} @@ -339,6 +340,7 @@ {"data": "created"}, {"data": "due_date"}, {"data": "assigned_to"}, + {"data": "time_spent"}, ] }); }) diff --git a/helpdesk/templates/helpdesk/ticket_list_table.html b/helpdesk/templates/helpdesk/ticket_list_table.html index 6b999cb7..fa4b9d69 100644 --- a/helpdesk/templates/helpdesk/ticket_list_table.html +++ b/helpdesk/templates/helpdesk/ticket_list_table.html @@ -12,6 +12,7 @@ {{ ticket.created|naturaltime }} {{ ticket.due_date|naturaltime }} {{ ticket.get_assigned_to }} + {{ ticket.time_spent }} {% endfor %} diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 331b5f6c..f754b10c 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -237,6 +237,7 @@ def followup_edit(request, ticket_id, followup_id): 'comment': escape(followup.comment), 'public': followup.public, 'new_status': followup.new_status, + 'time_spent': followup.time_spent, }) ticketcc_string, show_subscribe = \ @@ -256,9 +257,13 @@ def followup_edit(request, ticket_id, followup_id): comment = form.cleaned_data['comment'] public = form.cleaned_data['public'] new_status = form.cleaned_data['new_status'] + time_spent = form.cleaned_data['time_spent'] # will save previous date old_date = followup.date - new_followup = FollowUp(title=title, date=old_date, ticket=_ticket, comment=comment, public=public, new_status=new_status, ) + new_followup = FollowUp(title=title, date=old_date, ticket=_ticket, + comment=comment, public=public, + new_status=new_status, + time_spent=time_spent) # keep old user if one did exist before. if followup.user: new_followup.user = followup.user @@ -469,6 +474,11 @@ def update_ticket(request, ticket_id, public=False): due_date_year = int(request.POST.get('due_date_year', 0)) due_date_month = int(request.POST.get('due_date_month', 0)) due_date_day = int(request.POST.get('due_date_day', 0)) + if request.POST.get("time_spent"): + (hours, minutes) = [int(f) for f in request.POST.get("time_spent").split(":")] + time_spent = timedelta(hours=hours, minutes=minutes) + else: + time_spent = None # NOTE: jQuery's default for dates is mm/dd/yy # very US-centric but for now that's the only format supported # until we clean up code to internationalize a little more @@ -523,7 +533,8 @@ def update_ticket(request, ticket_id, public=False): if owner is -1 and ticket.assigned_to: owner = ticket.assigned_to.id - f = FollowUp(ticket=ticket, date=timezone.now(), comment=comment) + f = FollowUp(ticket=ticket, date=timezone.now(), comment=comment, + time_spent=time_spent) if is_helpdesk_staff(request.user): f.user = request.user @@ -1161,6 +1172,7 @@ def report_index(request): 'open': queue.ticket_set.filter(status__in=[1, 2]).count(), 'resolved': queue.ticket_set.filter(status=3).count(), 'closed': queue.ticket_set.filter(status=4).count(), + 'time_spent': queue.time_spent } dash_tickets.append(dash_ticket)