Free and open source ticket system written in python
at main 174 lines 7.1 kB view raw
1from django.shortcuts import render, redirect 2from django.contrib.auth.decorators import login_required 3from django.shortcuts import get_object_or_404 4from .models import Ticket, Template, FileAttachment 5from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 6from django.db.models import Q 7from .forms import CommentForm, TicketForm, TemplateForm, TeamAssignmentForm, CategoryAssignmentForm 8from django.http import Http404, FileResponse 9from django.conf import settings 10import os 11 12 13@login_required 14def show_tickets(request): 15 tickets = Ticket.get_open_tickets(request.user).order_by("priority", "-updated_at") 16 return render(request, "ticketing/tickets.html", {"tickets": tickets}) 17 18 19@login_required 20def show_tickets_history(request): 21 qs = Ticket.get_closed_tickets(request.user).order_by("priority", "-updated_at") 22 23 per_page = int(request.GET.get("per_page") or 20) 24 paginator = Paginator(qs, per_page) 25 26 page_number = request.GET.get("page") or 1 27 try: 28 page_obj = paginator.page(page_number) 29 except PageNotAnInteger: 30 page_obj = paginator.page(1) 31 except EmptyPage: 32 # Show last page if page is out of range 33 page_obj = paginator.page(paginator.num_pages) 34 35 context = { 36 "tickets": page_obj.object_list, 37 "page_obj": page_obj, 38 "paginator": paginator, 39 "is_paginated": page_obj.paginator.num_pages > 1, 40 "per_page": per_page, 41 } 42 print(paginator, page_obj) 43 return render(request, "ticketing/tickets_history.html", context) 44 45 46@login_required 47def show_ticket(request, ticket_id): 48 ticket = get_object_or_404(Ticket, pk=ticket_id) 49 can_edit = ticket.can_edit(request.user) 50 # comment_templates = Template.objects.filter(category=ticket.category) 51 52 if not ticket.can_open(request.user): 53 return redirect("all_tickets") 54 55 form, template_form, team_assignment_form, category_assignment_form = CommentForm( 56 ), TemplateForm(), TeamAssignmentForm(), CategoryAssignmentForm() 57 58 if request.method == "POST": 59 if 'apply_template' in request.POST and can_edit: 60 template_form = TemplateForm(request.POST) 61 if template_form.is_valid(): 62 template = template_form.cleaned_data["template_select"] 63 form = CommentForm(initial={"text": template.content}) 64 elif 'assign_to_team' in request.POST and can_edit: 65 team_assignment_form = TeamAssignmentForm(request.POST) 66 if team_assignment_form.is_valid(): 67 ticket.assign_to_team( 68 team_assignment_form.cleaned_data["team_select"]) 69 elif 'assign_to_category' in request.POST and can_edit: 70 category_assignment_form = CategoryAssignmentForm(request.POST) 71 if category_assignment_form.is_valid(): 72 ticket.category = category_assignment_form.cleaned_data["category_select"] 73 ticket.save() 74 elif 'assign_self' in request.POST and can_edit: 75 ticket.assigned_to = request.user 76 ticket.save() 77 elif 'reopen_ticket' in request.POST and can_edit: 78 ticket.status = Ticket.Status.IN_PROGRESS 79 ticket.save() 80 else: 81 form = CommentForm(request.POST, request.FILES) 82 if form.is_valid(): 83 ticket.comment_set.create( 84 user=request.user, ticket=ticket, 85 text=form.cleaned_data["text"], is_only_for_staff=form.cleaned_data["hidden_from_client"] 86 ) 87 # Add attachments 88 if form.cleaned_data["attachments"]: 89 for file in form.cleaned_data["attachments"]: 90 ticket.fileattachment_set.create(file=file) 91 92 if 'close' in request.POST and can_edit: 93 ticket.close_ticket() 94 return redirect("ticket_detail", ticket_id=ticket.id) 95 96 comments = ticket.comment_set.all() 97 context = { 98 "ticket": ticket, "comments": comments, "attachments": ticket.fileattachment_set.all(), 99 "form": form, "template_form": template_form, 100 "team_assignment_form": team_assignment_form, "category_assignment_form": category_assignment_form, 101 "can_edit": can_edit 102 } 103 return render(request, "ticketing/ticket_detail.html", context) 104 105 106@login_required 107def create_ticket(request): 108 109 has_closed_tickets = Ticket.objects.filter( 110 user=request.user, status=Ticket.Status.CLOSED).exists() 111 112 if request.method == "POST": 113 form = TicketForm(request.user, request.POST, request.FILES) 114 if form.is_valid(): 115 ticket = form.save(commit=False) 116 ticket.user = request.user 117 ticket.save() 118 119 # Add attachments 120 if form.cleaned_data["attachments"]: 121 for file in form.cleaned_data["attachments"]: 122 ticket.fileattachment_set.create(file=file) 123 124 return redirect("ticket_detail", ticket_id=ticket.id) 125 else: 126 form = TicketForm(request.user) 127 return render(request, "ticketing/create_ticket.html", {"form": form, "has_closed_tickets": has_closed_tickets}) 128 129 130@login_required 131def dashboard(request): 132 133 tickets = Ticket.objects.all().order_by("-created_at") 134 open_tickets = tickets.filter(status=Ticket.Status.OPEN) 135 in_progress_tickets = tickets.filter(status=Ticket.Status.IN_PROGRESS) 136 closed_tickets = tickets.filter(status=Ticket.Status.CLOSED) 137 return render(request, "ticketing/dashboard.html", {"tickets": tickets, "open_tickets": open_tickets, "in_progress_tickets": in_progress_tickets, "closed_tickets": closed_tickets}) 138 139 140@login_required 141def download_attachment(request, attachment_id): 142 """ 143 View that checks if the user has permission 144 to access the ticket before serving the file. 145 """ 146 attachment = get_object_or_404(FileAttachment, pk=attachment_id) 147 ticket = attachment.ticket 148 149 if not ticket.can_open(request.user): 150 raise Http404("File not found") 151 152 if not attachment.file: 153 raise Http404("File not found") 154 155 try: 156 file = attachment.file.open('rb') 157 response = FileResponse(file, content_type='application/octet-stream') 158 159 filename = os.path.basename(attachment.file.name) 160 response['Content-Disposition'] = f'inline; filename="{filename}"' 161 return response 162 except (ValueError, IOError, OSError): 163 # backwards compatibility 164 try: 165 old_path = os.path.join(settings.MEDIA_ROOT, attachment.file.name) 166 if os.path.exists(old_path): 167 file = open(old_path, 'rb') 168 response = FileResponse(file, content_type='application/octet-stream') 169 filename = os.path.basename(attachment.file.name) 170 response['Content-Disposition'] = f'inline; filename="{filename}"' 171 return response 172 except (ValueError, IOError, OSError): 173 pass 174 raise Http404("File not found")