mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-11-21 15:33:09 +01:00
basic support for time spend of tikets and follow-ups
This commit is contained in:
parent
8c4e094705
commit
6ceb89a5cb
@ -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):
|
||||
|
18
helpdesk/migrations/0024_time_spent.py
Normal file
18
helpdesk/migrations/0024_time_spent.py
Normal file
@ -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),
|
||||
),
|
||||
]
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -46,6 +46,8 @@
|
||||
<dt><label for="id_new_status">New Status:</label></dt>
|
||||
<dd>{{ form.new_status }}</dd>
|
||||
<p>If the status was changed, what was it changed to?</p>
|
||||
<dt><label for="id_time_spent">Time spent:</label></dt>
|
||||
<dd>{{ form.time_spent }}</dd>
|
||||
</dl>
|
||||
</fieldset>
|
||||
<p><input class="btn btn-primary" type="submit" value="Submit"></p>{% csrf_token %}
|
||||
|
@ -45,6 +45,7 @@
|
||||
<th>{% trans "Open" %}</th>
|
||||
<th>{% trans "Resolved" %}</th>
|
||||
<th>{% trans "Closed" %}</th>
|
||||
<th>{% trans "Time spent" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -54,6 +55,7 @@
|
||||
<td>{% if queue.open %}<a href='{{ hdlist }}?queue={{ queue.queue }}&status=1&status=2'>{% endif %}{{ queue.open }}{% if queue.open %}</a>{% endif %}</td>
|
||||
<td>{% if queue.resolved %}<a href='{{ hdlist }}?queue={{ queue.queue }}&status=3'>{% endif %}{{ queue.resolved }}{% if queue.resolved %}</a>{% endif %}</td>
|
||||
<td>{% if queue.closed %}<a href='{{ hdlist }}?queue={{ queue.queue }}&status=4'>{% endif %}{{ queue.closed }}{% if queue.closed %}</a>{% endif %}</td>
|
||||
<td>{{ queue.time_spent }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan='6'>{% trans "There are no unassigned tickets." %}</td></tr>
|
||||
|
@ -46,6 +46,9 @@
|
||||
{% if followup.comment %}
|
||||
<p>{{ followup.comment|force_escape|urlizetrunc:50|num_to_link|linebreaksbr }}</p>
|
||||
{% endif %}
|
||||
{% if followup.time_spent %}
|
||||
<small>{% trans "Time spent" %}: {{ followup.time_spent }}</small></p>
|
||||
{% endif %}
|
||||
{% for change in followup.ticketchange_set.all %}
|
||||
{% if forloop.first %}<div class='changes'><ul>{% endif %}
|
||||
<li>{% blocktrans with change.field as field and change.old_value as old_value and change.new_value as new_value %}Changed {{ field }} from {{ old_value }} to {{ new_value }}.{% endblocktrans %}</li>
|
||||
@ -152,6 +155,11 @@
|
||||
<dd><input type='checkbox' name='public' value='1' checked='checked' /> {% trans 'Yes, make this update public.' %}</dd>
|
||||
<dd class='form_help_text'>{% trans "If this is public, the submitter will be e-mailed your comment or resolution." %}</dd>
|
||||
{% endif %}
|
||||
|
||||
<dt>
|
||||
<label for='id_time_spent'>{% trans "Time spent" %}</label> <span class='form_optional'>{% trans "(Optional)" %}</span>
|
||||
</dt>
|
||||
<dd><input name='time_spent' type="time" /></dd>
|
||||
</dl>
|
||||
|
||||
<p id='ShowFurtherOptPara'><button class="btn btn-warning btn-sm" id='ShowFurtherEditOptions'>{% trans "Change Further Details »" %}</button></p>
|
||||
|
@ -89,6 +89,10 @@
|
||||
<p><a data-toggle='tooltip' href='{% url 'helpdesk:ticket_dependency_add' ticket.id %}' title="{% trans "Click on 'Add Dependency', if you want to make this ticket dependent on another ticket. A ticket may not be closed until all tickets it depends on are closed." %}"><button type="button" class="btn btn-primary btn-sm"><i class="fas fa-child"></i> {% trans "Add Dependency" %}</button></a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans "Total time spent" %}</th>
|
||||
<td>{{ ticket.time_spent }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -222,6 +222,7 @@
|
||||
<th>{% trans "Created" %}</th>
|
||||
<th>{% trans "Due Date" %}</th>
|
||||
<th>{% trans "Owner" %}</th>
|
||||
<th>{% trans "Time Spent" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% if not server_side %}
|
||||
@ -339,6 +340,7 @@
|
||||
{"data": "created"},
|
||||
{"data": "due_date"},
|
||||
{"data": "assigned_to"},
|
||||
{"data": "time_spent"},
|
||||
]
|
||||
});
|
||||
})
|
||||
|
@ -12,6 +12,7 @@
|
||||
<td data-order='{{ ticket.created|date:"U" }}'><span title='{{ ticket.created|date:"r" }}'>{{ ticket.created|naturaltime }}</span></td>
|
||||
<td data-order='{{ ticket.due_date|date:"U" }}'><span title='{{ ticket.due_date|date:"r" }}'>{{ ticket.due_date|naturaltime }}</span></td>
|
||||
<td>{{ ticket.get_assigned_to }}</td>
|
||||
<td>{{ ticket.time_spent }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user