partition tickets by event

This commit is contained in:
j3d1 2024-11-13 23:51:54 +01:00
parent 2c609427ec
commit 5b18d4d32f
4 changed files with 65 additions and 10 deletions

View file

@ -1,4 +1,4 @@
from django.urls import path from django.urls import re_path
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from rest_framework import routers, viewsets from rest_framework import routers, viewsets
from rest_framework.decorators import api_view, permission_classes from rest_framework.decorators import api_view, permission_classes
@ -99,8 +99,8 @@ router.register(r'boxes', ContainerViewSet, basename='boxes')
router.register(r'box', ContainerViewSet, basename='boxes') router.register(r'box', ContainerViewSet, basename='boxes')
urlpatterns = router.urls + [ urlpatterns = router.urls + [
path('<event_slug>/items/', item), re_path(r'^(?P<event_slug>[\w-]+)/items/$', item, name='item'),
path('<event_slug>/items/<query>/', search_items), re_path(r'^(?P<event_slug>[\w-]+)/items/(?P<query>[-A-Za-z0-9+/]*={0,3})/$', search_items, name='search_items'),
path('<event_slug>/item/', item), re_path(r'^(?P<event_slug>[\w-]+)/item/$', item, name='item'),
path('<event_slug>/item/<id>/', item_by_id), re_path(r'^(?P<event_slug>[\w-]+)/item/(?P<id>\d+)/$', item_by_id, name='item_by_id'),
] ]

View file

@ -56,7 +56,7 @@ def reply(request, pk):
@api_view(['POST']) @api_view(['POST'])
@permission_classes([IsAuthenticated]) @permission_classes([IsAuthenticated])
@permission_required('tickets.add_issuethread_manual', raise_exception=True) @permission_required('tickets.add_issuethread_manual', raise_exception=True)
def manual_ticket(request): def manual_ticket(request, event_slug):
if 'name' not in request.data: if 'name' not in request.data:
return Response({'status': 'error', 'message': 'missing name'}, status=status.HTTP_400_BAD_REQUEST) return Response({'status': 'error', 'message': 'missing name'}, status=status.HTTP_400_BAD_REQUEST)
if 'sender' not in request.data: if 'sender' not in request.data:
@ -66,8 +66,16 @@ def manual_ticket(request):
if 'body' not in request.data: if 'body' not in request.data:
return Response({'status': 'error', 'message': 'missing body'}, status=status.HTTP_400_BAD_REQUEST) return Response({'status': 'error', 'message': 'missing body'}, status=status.HTTP_400_BAD_REQUEST)
event = None
if event_slug != 'none':
try:
event = Event.objects.get(slug=event_slug)
except:
return Response({'status': 'error', 'message': 'invalid event'}, status=status.HTTP_400_BAD_REQUEST)
issue = IssueThread.objects.create( issue = IssueThread.objects.create(
name=request.data['name'], name=request.data['name'],
event=event,
manually_created=True, manually_created=True,
) )
email = Email.objects.create( email = Email.objects.create(
@ -122,8 +130,8 @@ router.register(r'tickets', IssueViewSet, basename='issues')
router.register(r'shipping_vouchers', ShippingVoucherViewSet, basename='shipping_vouchers') router.register(r'shipping_vouchers', ShippingVoucherViewSet, basename='shipping_vouchers')
urlpatterns = ([ urlpatterns = ([
re_path(r'tickets/states/$', get_available_states, name='get_available_states'),
re_path(r'^tickets/(?P<pk>\d+)/reply/$', reply, name='reply'), re_path(r'^tickets/(?P<pk>\d+)/reply/$', reply, name='reply'),
re_path(r'^tickets/(?P<pk>\d+)/comment/$', add_comment, name='add_comment'), re_path(r'^tickets/(?P<pk>\d+)/comment/$', add_comment, name='add_comment'),
re_path(r'^tickets/manual/$', manual_ticket, name='manual_ticket'), re_path(r'^(?P<event_slug>[\w-]+)/tickets/manual/$', manual_ticket, name='manual_ticket'),
re_path(r'^tickets/states/$', get_available_states, name='get_available_states'),
] + router.urls) ] + router.urls)

View file

@ -1,6 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from authentication.models import ExtendedUser from authentication.models import ExtendedUser
from inventory.models import Event
from mail.api_v2 import AttachmentSerializer from mail.api_v2 import AttachmentSerializer
from tickets.models import IssueThread, Comment, STATE_CHOICES, ShippingVoucher from tickets.models import IssueThread, Comment, STATE_CHOICES, ShippingVoucher
from inventory.serializers import ItemSerializer from inventory.serializers import ItemSerializer
@ -41,11 +42,13 @@ class IssueSerializer(serializers.ModelSerializer):
last_activity = serializers.SerializerMethodField() last_activity = serializers.SerializerMethodField()
assigned_to = serializers.SlugRelatedField(slug_field='username', queryset=ExtendedUser.objects.all(), assigned_to = serializers.SlugRelatedField(slug_field='username', queryset=ExtendedUser.objects.all(),
allow_null=True, required=False) allow_null=True, required=False)
event = serializers.SlugRelatedField(slug_field='slug', queryset=Event.objects.all(),
allow_null=True, required=False)
related_items = ItemSerializer(many=True, read_only=True) related_items = ItemSerializer(many=True, read_only=True)
class Meta: class Meta:
model = IssueThread model = IssueThread
fields = ('id', 'timeline', 'name', 'state', 'assigned_to', 'last_activity', 'uuid', 'related_items') fields = ('id', 'timeline', 'name', 'state', 'assigned_to', 'last_activity', 'uuid', 'related_items', 'event')
read_only_fields = ('id', 'timeline', 'last_activity', 'uuid', 'related_items') read_only_fields = ('id', 'timeline', 'last_activity', 'uuid', 'related_items')
def to_internal_value(self, data): def to_internal_value(self, data):

View file

@ -3,6 +3,7 @@ from datetime import datetime, timedelta
from django.test import TestCase, Client from django.test import TestCase, Client
from authentication.models import ExtendedUser from authentication.models import ExtendedUser
from inventory.models import Event
from mail.models import Email, EmailAttachment from mail.models import Email, EmailAttachment
from tickets.models import IssueThread, StateChange, Comment from tickets.models import IssueThread, StateChange, Comment
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
@ -16,6 +17,7 @@ class IssueApiTest(TestCase):
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test') self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
self.user.user_permissions.add(*Permission.objects.all()) self.user.user_permissions.add(*Permission.objects.all())
self.user.save() self.user.save()
self.event = Event.objects.create(slug='evt')
self.token = AuthToken.objects.create(user=self.user) self.token = AuthToken.objects.create(user=self.user)
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]}) self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
@ -28,6 +30,7 @@ class IssueApiTest(TestCase):
now = datetime.now() now = datetime.now()
issue = IssueThread.objects.create( issue = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
mail1 = Email.objects.create( mail1 = Email.objects.create(
subject='test', subject='test',
@ -61,6 +64,7 @@ class IssueApiTest(TestCase):
self.assertEqual(response.json()[0]['id'], issue.id) self.assertEqual(response.json()[0]['id'], issue.id)
self.assertEqual(response.json()[0]['name'], "test issue") self.assertEqual(response.json()[0]['name'], "test issue")
self.assertEqual(response.json()[0]['state'], "pending_new") self.assertEqual(response.json()[0]['state'], "pending_new")
self.assertEqual(response.json()[0]['event'], "evt")
self.assertEqual(response.json()[0]['assigned_to'], None) self.assertEqual(response.json()[0]['assigned_to'], None)
self.assertEqual(response.json()[0]['uuid'], issue.uuid) self.assertEqual(response.json()[0]['uuid'], issue.uuid)
self.assertEqual(response.json()[0]['last_activity'], comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ')) self.assertEqual(response.json()[0]['last_activity'], comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
@ -93,12 +97,15 @@ class IssueApiTest(TestCase):
now = datetime.now() now = datetime.now()
issue1 = IssueThread.objects.create( issue1 = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
issue2 = IssueThread.objects.create( issue2 = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
issue3 = IssueThread.objects.create( issue3 = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
mail1 = Email.objects.create( mail1 = Email.objects.create(
subject='test', subject='test',
@ -118,8 +125,11 @@ class IssueApiTest(TestCase):
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
self.assertEqual(3, len(response.json())) self.assertEqual(3, len(response.json()))
self.assertEqual(issue1.id, response.json()[0]['id']) self.assertEqual(issue1.id, response.json()[0]['id'])
self.assertEqual("evt", response.json()[0]['event'])
self.assertEqual(issue2.id, response.json()[1]['id']) self.assertEqual(issue2.id, response.json()[1]['id'])
self.assertEqual("evt", response.json()[1]['event'])
self.assertEqual(issue3.id, response.json()[2]['id']) self.assertEqual(issue3.id, response.json()[2]['id'])
self.assertEqual("evt", response.json()[2]['event'])
self.assertEqual(issue1.state_changes.first().timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'), self.assertEqual(issue1.state_changes.first().timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
response.json()[0]['last_activity']) response.json()[0]['last_activity'])
self.assertEqual(mail1.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'), self.assertEqual(mail1.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
@ -153,6 +163,7 @@ class IssueApiTest(TestCase):
now = datetime.now() now = datetime.now()
issue = IssueThread.objects.create( issue = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
mail1 = Email.objects.create( mail1 = Email.objects.create(
subject='test', subject='test',
@ -189,6 +200,7 @@ class IssueApiTest(TestCase):
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
self.assertEqual(1, len(response.json())) self.assertEqual(1, len(response.json()))
self.assertEqual(issue.id, response.json()[0]['id']) self.assertEqual(issue.id, response.json()[0]['id'])
self.assertEqual("evt", response.json()[0]['event'])
self.assertEqual('pending_new', response.json()[0]['state']) self.assertEqual('pending_new', response.json()[0]['state'])
self.assertEqual('test issue', response.json()[0]['name']) self.assertEqual('test issue', response.json()[0]['name'])
self.assertEqual(None, response.json()[0]['assigned_to']) self.assertEqual(None, response.json()[0]['assigned_to'])
@ -230,13 +242,14 @@ class IssueApiTest(TestCase):
self.assertEqual(file2.hash, response.json()[0]['timeline'][1]['attachments'][1]['hash']) self.assertEqual(file2.hash, response.json()[0]['timeline'][1]['attachments'][1]['hash'])
def test_manual_creation(self): def test_manual_creation(self):
response = self.client.post('/api/2/tickets/manual/', response = self.client.post('/api/2/evt/tickets/manual/',
{'name': 'test issue', 'sender': 'test', 'recipient': 'test', 'body': 'test'}, {'name': 'test issue', 'sender': 'test', 'recipient': 'test', 'body': 'test'},
content_type='application/json') content_type='application/json')
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
self.assertEqual(response.json()['state'], 'pending_new') self.assertEqual(response.json()['state'], 'pending_new')
self.assertEqual(response.json()['name'], 'test issue') self.assertEqual(response.json()['name'], 'test issue')
self.assertEqual(response.json()['assigned_to'], None) self.assertEqual(response.json()['assigned_to'], None)
self.assertEqual("evt", response.json()['event'])
timeline = response.json()['timeline'] timeline = response.json()['timeline']
self.assertEqual(len(timeline), 2) self.assertEqual(len(timeline), 2)
self.assertEqual(timeline[0]['type'], 'state') self.assertEqual(timeline[0]['type'], 'state')
@ -247,9 +260,35 @@ class IssueApiTest(TestCase):
self.assertEqual(timeline[1]['subject'], 'test issue') self.assertEqual(timeline[1]['subject'], 'test issue')
self.assertEqual(timeline[1]['body'], 'test') self.assertEqual(timeline[1]['body'], 'test')
def test_manual_creation_none(self):
response = self.client.post('/api/2/none/tickets/manual/',
{'name': 'test issue', 'sender': 'test', 'recipient': 'test', 'body': 'test'},
content_type='application/json')
self.assertEqual(response.status_code, 201)
self.assertEqual(response.json()['state'], 'pending_new')
self.assertEqual(response.json()['name'], 'test issue')
self.assertEqual(response.json()['assigned_to'], None)
self.assertEqual(None, response.json()['event'])
timeline = response.json()['timeline']
self.assertEqual(len(timeline), 2)
self.assertEqual(timeline[0]['type'], 'state')
self.assertEqual(timeline[0]['state'], 'pending_new')
self.assertEqual(timeline[1]['type'], 'mail')
self.assertEqual(timeline[1]['sender'], 'test')
self.assertEqual(timeline[1]['recipient'], 'test')
self.assertEqual(timeline[1]['subject'], 'test issue')
self.assertEqual(timeline[1]['body'], 'test')
def test_manual_creation_invalid(self):
response = self.client.post('/api/2/foobar/tickets/manual/',
{'name': 'test issue', 'sender': 'test', 'recipient': 'test', 'body': 'test'},
content_type='application/json')
self.assertEqual(response.status_code, 400)
def test_post_comment_altenative(self): def test_post_comment_altenative(self):
issue = IssueThread.objects.create( issue = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
response = self.client.post(f'/api/2/tickets/{issue.id}/comment/', {'comment': 'test'}) response = self.client.post(f'/api/2/tickets/{issue.id}/comment/', {'comment': 'test'})
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
@ -260,6 +299,7 @@ class IssueApiTest(TestCase):
def test_post_alt_comment_empty(self): def test_post_alt_comment_empty(self):
issue = IssueThread.objects.create( issue = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
response = self.client.post(f'/api/2/tickets/{issue.id}/comment/', {'comment': ''}) response = self.client.post(f'/api/2/tickets/{issue.id}/comment/', {'comment': ''})
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
@ -267,6 +307,7 @@ class IssueApiTest(TestCase):
def test_state_change(self): def test_state_change(self):
issue = IssueThread.objects.create( issue = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
response = self.client.patch(f'/api/2/tickets/{issue.id}/', {'state': 'pending_open'}, response = self.client.patch(f'/api/2/tickets/{issue.id}/', {'state': 'pending_open'},
content_type='application/json') content_type='application/json')
@ -284,6 +325,7 @@ class IssueApiTest(TestCase):
def test_state_change_invalid_state(self): def test_state_change_invalid_state(self):
issue = IssueThread.objects.create( issue = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
response = self.client.patch(f'/api/2/tickets/{issue.id}/', {'state': 'invalid'}, response = self.client.patch(f'/api/2/tickets/{issue.id}/', {'state': 'invalid'},
content_type='application/json') content_type='application/json')
@ -292,12 +334,14 @@ class IssueApiTest(TestCase):
def test_assign_user(self): def test_assign_user(self):
issue = IssueThread.objects.create( issue = IssueThread.objects.create(
name="test issue", name="test issue",
event=self.event,
) )
response = self.client.patch(f'/api/2/tickets/{issue.id}/', {'assigned_to': self.user.username}, response = self.client.patch(f'/api/2/tickets/{issue.id}/', {'assigned_to': self.user.username},
content_type='application/json') content_type='application/json')
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
self.assertEqual('pending_new', response.json()['state']) self.assertEqual('pending_new', response.json()['state'])
self.assertEqual('test issue', response.json()['name']) self.assertEqual('test issue', response.json()['name'])
self.assertEqual("evt", response.json()['event'])
self.assertEqual(self.user.username, response.json()['assigned_to']) self.assertEqual(self.user.username, response.json()['assigned_to'])
timeline = response.json()['timeline'] timeline = response.json()['timeline']
self.assertEqual(2, len(timeline)) self.assertEqual(2, len(timeline))