Free and open source ticket system written in python
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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")