Compare commits

..

16 commits

Author SHA1 Message Date
f00afb86d6 stash
All checks were successful
/ test (push) Successful in 57s
2024-11-21 02:16:00 +01:00
4f08a2c265 stash 2024-11-21 01:54:19 +01:00
f51c367ead stash 2024-11-21 01:52:18 +01:00
124713b98c save raw_mails as file 2024-11-21 01:52:18 +01:00
f9e8ce1178 stash 2024-11-21 01:52:18 +01:00
6a99e7420f stash 2024-11-21 01:52:18 +01:00
6a81b1f832 stash 2024-11-21 01:52:18 +01:00
b6a8e3acd4 stash 2024-11-21 01:52:14 +01:00
420d22bb43 stash 2024-11-21 01:51:39 +01:00
f785189304 stash 2024-11-21 01:47:12 +01:00
9320765bc5 stash 2024-11-21 01:47:12 +01:00
33d368b5f4 stash 2024-11-21 01:47:12 +01:00
7083f4f7b7 stash 2024-11-21 01:47:12 +01:00
75ea0f4b46 stash 2024-11-21 01:47:12 +01:00
fdf5ab8ad1 implement simple backend search for items and tickets
All checks were successful
/ test (push) Successful in 51s
/ deploy (push) Successful in 4m36s
2024-11-21 01:45:47 +01:00
f2647f0dbd add /matches endpoint ad return match information in tickets and /item endpoints
All checks were successful
/ test (push) Successful in 52s
/ deploy (push) Successful in 4m42s
2024-11-21 00:59:03 +01:00
12 changed files with 327 additions and 70 deletions

View file

@ -114,10 +114,6 @@ router.register(r'boxes', ContainerViewSet, basename='boxes')
router.register(r'box', ContainerViewSet, basename='boxes')
urlpatterns = router.urls + [
# path('<event_slug>/items/', item),
# path('<event_slug>/items/<query>/', search_items),
# path('<event_slug>/item/', item),
# path('<event_slug>/item/<id>/', item_by_id),
re_path(r'^(?P<event_slug>[\w-]+)/items/$', item, name='item'),
re_path(r'^(?P<event_slug>[\w-]+)/items/(?P<query>[-A-Za-z0-9+/]*={0,3})/$', search_items, name='search_items'),
re_path(r'^(?P<event_slug>[\w-]+)/item/$', item, name='item'),

View file

@ -43,6 +43,13 @@ class Item(SoftDeleteModel):
return
self.container_history.create(container=value)
@property
def related_issues(self):
groups = groupby(self.issue_relation_changes.all(), lambda rel: rel.issue_thread.id)
return [sorted(v, key=lambda r: r.timestamp)[0].issue_thread for k, v in groups]
objects = ItemManager()
all_objects = models.Manager()

View file

@ -4,7 +4,9 @@ from rest_framework.relations import SlugRelatedField
from files.models import File
from inventory.models import Event, Container, Item
from inventory.shared_serializers import BasicItemSerializer
from mail.models import EventAddress
from tickets.shared_serializers import BasicIssueSerializer
# class EventAdressSerializer(serializers.ModelSerializer):
@ -56,34 +58,15 @@ class ContainerSerializer(serializers.ModelSerializer):
return len(instance.items)
class ItemSerializer(serializers.ModelSerializer):
class ItemSerializer(BasicItemSerializer):
dataImage = serializers.CharField(write_only=True, required=False)
cid = serializers.SerializerMethodField()
box = serializers.SerializerMethodField()
file = serializers.SerializerMethodField()
returned = serializers.SerializerMethodField(required=False)
event = serializers.SlugRelatedField(slug_field='slug', queryset=Event.objects.all(),
allow_null=True, required=False)
related_issues = BasicIssueSerializer(many=True, read_only=True)
class Meta:
model = Item
fields = ['cid', 'box', 'id', 'description', 'file', 'dataImage', 'returned', 'event']
fields = ['cid', 'box', 'id', 'description', 'file', 'dataImage', 'returned', 'event', 'related_issues']
read_only_fields = ['id']
def get_cid(self, instance):
return instance.container.id if instance.container else None
def get_box(self, instance):
return instance.container.name if instance.container else None
def get_file(self, instance):
if len(instance.files.all()) > 0:
return instance.files.all().order_by('-created_at')[0].hash
return None
def get_returned(self, instance):
return instance.returned_at is not None
def to_internal_value(self, data):
container = None
returned = False

View file

@ -0,0 +1,31 @@
from rest_framework import serializers
from inventory.models import Event, Item
class BasicItemSerializer(serializers.ModelSerializer):
cid = serializers.SerializerMethodField()
box = serializers.SerializerMethodField()
file = serializers.SerializerMethodField()
returned = serializers.SerializerMethodField(required=False)
event = serializers.SlugRelatedField(slug_field='slug', queryset=Event.objects.all(),
allow_null=True, required=False)
class Meta:
model = Item
fields = ['cid', 'box', 'id', 'description', 'file', 'returned', 'event']
read_only_fields = ['id']
def get_cid(self, instance):
return instance.container.id if instance.container else None
def get_box(self, instance):
return instance.container.name if instance.container else None
def get_file(self, instance):
if len(instance.files.all()) > 0:
return instance.files.all().order_by('-created_at')[0].hash
return None
def get_returned(self, instance):
return instance.returned_at is not None

View file

@ -30,9 +30,15 @@ class ItemTestCase(TestCase):
item = Item.objects.create(container=self.box, event=self.event, description='1')
response = self.client.get(f'/api/2/{self.event.slug}/item/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(),
[{'id': 1, 'description': '1', 'box': 'BOX', 'cid': self.box.id, 'file': None,
'returned': False, 'event': self.event.slug}])
self.assertEqual(len(response.json()), 1)
self.assertEqual(response.json()[0]['id'], item.id)
self.assertEqual(response.json()[0]['description'], '1')
self.assertEqual(response.json()[0]['box'], 'BOX')
self.assertEqual(response.json()[0]['cid'], self.box.id)
self.assertEqual(response.json()[0]['file'], None)
self.assertEqual(response.json()[0]['returned'], False)
self.assertEqual(response.json()[0]['event'], self.event.slug)
self.assertEqual(len(response.json()[0]['related_issues']), 0)
def test_members_with_file(self):
import base64
@ -40,9 +46,15 @@ class ItemTestCase(TestCase):
file = File.objects.create(item=item, data="data:text/plain;base64," + base64.b64encode(b"foo").decode('utf-8'))
response = self.client.get(f'/api/2/{self.event.slug}/item/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(),
[{'id': 1, 'description': '1', 'box': 'BOX', 'cid': self.box.id, 'file': file.hash,
'returned': False, 'event': self.event.slug}])
self.assertEqual(len(response.json()), 1)
self.assertEqual(response.json()[0]['id'], item.id)
self.assertEqual(response.json()[0]['description'], '1')
self.assertEqual(response.json()[0]['box'], 'BOX')
self.assertEqual(response.json()[0]['cid'], self.box.id)
self.assertEqual(response.json()[0]['file'], file.hash)
self.assertEqual(response.json()[0]['returned'], False)
self.assertEqual(response.json()[0]['event'], self.event.slug)
self.assertEqual(len(response.json()[0]['related_issues']), 0)
def test_multi_members(self):
Item.objects.create(container=self.box, event=self.event, description='1')
@ -55,9 +67,14 @@ class ItemTestCase(TestCase):
def test_create_item(self):
response = self.client.post(f'/api/2/{self.event.slug}/item/', {'cid': self.box.id, 'description': '1'})
self.assertEqual(response.status_code, 201)
self.assertEqual(response.json(),
{'id': 1, 'description': '1', 'box': 'BOX', 'cid': self.box.id, 'file': None,
'returned': False, 'event': self.event.slug})
self.assertEqual(response.json()['id'], 1)
self.assertEqual(response.json()['description'], '1')
self.assertEqual(response.json()['box'], 'BOX')
self.assertEqual(response.json()['cid'], self.box.id)
self.assertEqual(response.json()['file'], None)
self.assertEqual(response.json()['returned'], False)
self.assertEqual(response.json()['event'], self.event.slug)
self.assertEqual(len(response.json()['related_issues']), 0)
self.assertEqual(len(Item.objects.all()), 1)
self.assertEqual(Item.objects.all()[0].id, 1)
self.assertEqual(Item.objects.all()[0].description, '1')
@ -94,9 +111,14 @@ class ItemTestCase(TestCase):
response = self.client.patch(f'/api/2/{self.event.slug}/item/{item.id}/', {'description': '2'},
content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(),
{'id': 1, 'description': '2', 'box': 'BOX', 'cid': self.box.id, 'file': None,
'returned': False, 'event': self.event.slug})
self.assertEqual(response.json()['id'], item.id)
self.assertEqual(response.json()['description'], '2')
self.assertEqual(response.json()['box'], 'BOX')
self.assertEqual(response.json()['cid'], self.box.id)
self.assertEqual(response.json()['file'], None)
self.assertEqual(response.json()['returned'], False)
self.assertEqual(response.json()['event'], self.event.slug)
self.assertEqual(len(response.json()['related_issues']), 0)
self.assertEqual(len(Item.objects.all()), 1)
self.assertEqual(Item.objects.all()[0].id, 1)
self.assertEqual(Item.objects.all()[0].description, '2')
@ -215,7 +237,7 @@ class ItemSearchTestCase(TestCase):
self.assertEqual(self.item1.id, response.json()[0]['id'])
self.assertEqual('abc def', response.json()[0]['description'])
self.assertEqual('BOX', response.json()[0]['box'])
self.assertEqual(self.box.id, response.json()[0]['id'])
self.assertEqual(self.box.id, response.json()[0]['cid'])
self.assertEqual(1, response.json()[0]['search_score'])
self.assertEqual(self.item2.id, response.json()[1]['id'])
self.assertEqual('def ghi', response.json()[1]['description'])
@ -242,7 +264,7 @@ class ItemSearchTestCase(TestCase):
self.assertEqual(self.item1.id, response.json()[0]['id'])
self.assertEqual('abc def', response.json()[0]['description'])
self.assertEqual('BOX', response.json()[0]['box'])
self.assertEqual(self.box.id, response.json()[0]['id'])
self.assertEqual(self.box.id, response.json()[0]['cid'])
self.assertEqual(2, response.json()[0]['search_score'])
self.assertEqual(self.item2.id, response.json()[1]['id'])
self.assertEqual('def ghi', response.json()[1]['description'])

View file

@ -1,4 +1,3 @@
import logging
from base64 import b64decode
from django.urls import re_path
@ -15,8 +14,9 @@ from inventory.models import Event
from mail.models import Email
from mail.protocol import send_smtp, make_reply, collect_references
from notify_sessions.models import SystemEvent
from tickets.models import IssueThread, Comment, STATE_CHOICES, ShippingVoucher
from tickets.models import IssueThread, Comment, STATE_CHOICES, ShippingVoucher, ItemRelation
from tickets.serializers import IssueSerializer, CommentSerializer, ShippingVoucherSerializer, SearchResultSerializer
from tickets.shared_serializers import RelationSerializer
class IssueViewSet(viewsets.ModelViewSet):
@ -24,6 +24,11 @@ class IssueViewSet(viewsets.ModelViewSet):
queryset = IssueThread.objects.all()
class RelationViewSet(viewsets.ModelViewSet):
serializer_class = RelationSerializer
queryset = ItemRelation.objects.all()
class CommentViewSet(viewsets.ModelViewSet):
serializer_class = CommentSerializer
queryset = Comment.objects.all()
@ -157,10 +162,9 @@ def search_issues(request, event_slug, query):
router = routers.SimpleRouter()
router.register(r'tickets', IssueViewSet, basename='issues')
# router.register(r'comments', CommentViewSet, basename='comments')
router.register(r'matches', RelationViewSet, basename='matches')
router.register(r'shipping_vouchers', ShippingVoucherViewSet, basename='shipping_vouchers')
# [-A-Za-z0-9+/]*={0,3}
urlpatterns = ([
re_path(r'tickets/states/$', get_available_states, name='get_available_states'),

View file

@ -0,0 +1,29 @@
# Generated by Django 4.2.7 on 2024-11-20 23:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0008_alter_item_event_alter_itemplacement_container_and_more'),
('tickets', '0012_alter_itemrelation_item'),
]
operations = [
migrations.RemoveField(
model_name='issuethread',
name='related_items',
),
migrations.AlterField(
model_name='itemrelation',
name='issue_thread',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_relation_changes', to='tickets.issuethread'),
),
migrations.AlterField(
model_name='itemrelation',
name='item',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issue_relation_changes', to='inventory.item'),
),
]

View file

@ -1,3 +1,5 @@
from itertools import groupby
from django.utils import timezone
from django.db import models
from django_softdelete.models import SoftDeleteModel
@ -43,7 +45,6 @@ class IssueThread(SoftDeleteModel):
name = models.CharField(max_length=255)
event = models.ForeignKey(Event, null=True, on_delete=models.SET_NULL, related_name='issue_threads')
manually_created = models.BooleanField(default=False)
related_items = models.ManyToManyField(Item, through='ItemRelation')
def short_uuid(self):
return self.uuid[:8]
@ -76,6 +77,11 @@ class IssueThread(SoftDeleteModel):
return
self.assignments.create(assigned_to=value)
@property
def related_items(self):
groups = groupby(self.item_relation_changes.all(), lambda rel: rel.item.id)
return [sorted(v, key=lambda r: r.timestamp)[0].item for k, v in groups]
def __str__(self):
return '[' + str(self.id) + '][' + self.short_uuid() + '] ' + self.name
@ -132,8 +138,8 @@ class Assignment(models.Model):
class ItemRelation(models.Model):
id = models.AutoField(primary_key=True)
issue_thread = models.ForeignKey(IssueThread, on_delete=models.CASCADE, related_name='item_relations')
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='issue_relations')
issue_thread = models.ForeignKey(IssueThread, on_delete=models.CASCADE, related_name='item_relation_changes')
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='issue_relation_changes')
timestamp = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=255, choices=RELATION_STATUS_CHOICES, default='possible')

View file

@ -2,9 +2,10 @@ from rest_framework import serializers
from authentication.models import ExtendedUser
from inventory.models import Event
from inventory.shared_serializers import BasicItemSerializer
from mail.api_v2 import AttachmentSerializer
from tickets.models import IssueThread, Comment, STATE_CHOICES, ShippingVoucher
from inventory.serializers import ItemSerializer
from tickets.shared_serializers import BasicIssueSerializer
class CommentSerializer(serializers.ModelSerializer):
@ -37,14 +38,10 @@ class ShippingVoucherSerializer(serializers.ModelSerializer):
read_only_fields = ('id', 'timestamp', 'used_at')
class IssueSerializer(serializers.ModelSerializer):
class IssueSerializer(BasicIssueSerializer):
timeline = serializers.SerializerMethodField()
last_activity = serializers.SerializerMethodField()
assigned_to = serializers.SlugRelatedField(slug_field='username', queryset=ExtendedUser.objects.all(),
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 = BasicItemSerializer(many=True, read_only=True)
class Meta:
model = IssueThread
@ -55,8 +52,6 @@ class IssueSerializer(serializers.ModelSerializer):
ret = super().to_internal_value(data)
if 'state' in data:
ret['state'] = data['state']
# if 'assigned_to' in data:
# ret['assigned_to'] = data['assigned_to']
return ret
def validate(self, attrs):
@ -74,8 +69,8 @@ class IssueSerializer(serializers.ModelSerializer):
last_mail = self.emails.order_by('-timestamp').first().timestamp if self.emails.count() > 0 else None
last_assignment = self.assignments.order_by('-timestamp').first().timestamp if \
self.assignments.count() > 0 else None
last_relation = self.item_relations.order_by('-timestamp').first().timestamp if \
self.item_relations.count() > 0 else None
last_relation = self.item_relation_changes.order_by('-timestamp').first().timestamp if \
self.item_relation_changes.count() > 0 else None
args = [x for x in [last_state_change, last_comment, last_mail, last_assignment, last_relation] if
x is not None]
return max(args)
@ -117,13 +112,13 @@ class IssueSerializer(serializers.ModelSerializer):
'timestamp': assignment.timestamp,
'assigned_to': assignment.assigned_to.username,
})
for relation in obj.item_relations.all():
for relation in (obj.item_relation_changes.all()):
timeline.append({
'type': 'item_relation',
'id': relation.id,
'status': relation.status,
'timestamp': relation.timestamp,
'item': ItemSerializer(relation.item).data,
'item': BasicItemSerializer(relation.item).data,
})
for shipping_voucher in obj.shipping_vouchers.all():
timeline.append({
@ -135,9 +130,6 @@ class IssueSerializer(serializers.ModelSerializer):
})
return sorted(timeline, key=lambda x: x['timestamp'])
def get_queryset(self):
return IssueThread.objects.all().order_by('-last_activity')
class SearchResultSerializer(serializers.Serializer):
search_score = serializers.IntegerField()

View file

@ -0,0 +1,23 @@
from rest_framework import serializers
from authentication.models import ExtendedUser
from inventory.models import Event
from tickets.models import IssueThread, ItemRelation
class RelationSerializer(serializers.ModelSerializer):
class Meta:
model = ItemRelation
fields = ('id', 'status', 'timestamp', 'item', 'issue_thread')
class BasicIssueSerializer(serializers.ModelSerializer):
assigned_to = serializers.SlugRelatedField(slug_field='username', queryset=ExtendedUser.objects.all(),
allow_null=True, required=False)
event = serializers.SlugRelatedField(slug_field='slug', queryset=Event.objects.all(),
allow_null=True, required=False)
class Meta:
model = IssueThread
fields = ('id', 'name', 'state', 'assigned_to', 'uuid', 'event')
read_only_fields = ('id', 'timeline', 'last_activity', 'uuid', 'related_items')

View file

@ -0,0 +1,149 @@
from datetime import datetime, timedelta
from django.test import TestCase, Client
from authentication.models import ExtendedUser
from inventory.models import Event, Container, Item
from mail.models import Email, EmailAttachment
from tickets.models import IssueThread, StateChange, Comment, ItemRelation
from django.contrib.auth.models import Permission
from knox.models import AuthToken
from base64 import b64encode
class IssueItemMatchApiTest(TestCase):
def setUp(self):
super().setUp()
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
self.user.user_permissions.add(*Permission.objects.all())
self.user.save()
self.event = Event.objects.create(slug='evt')
self.box = Container.objects.create(name='box1')
self.item = Item.objects.create(container=self.box, description="foo", event=self.event)
self.token = AuthToken.objects.create(user=self.user)
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
now = datetime.now()
self.issue = IssueThread.objects.create(
name="test issue",
event=self.event,
)
self.mail1 = Email.objects.create(
subject='test',
body='test',
sender='test',
recipient='test',
issue_thread=self.issue,
timestamp=now,
)
self.comment = Comment.objects.create(
issue_thread=self.issue,
comment="test",
timestamp=now + timedelta(seconds=3),
)
self.match = ItemRelation.objects.create(
issue_thread=self.issue,
item=self.item,
timestamp=now + timedelta(seconds=5),
)
def test_issues(self):
response = self.client.get('/api/2/tickets/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()), 1)
self.assertEqual(response.json()[0]['id'], self.issue.id)
self.assertEqual(response.json()[0]['name'], "test issue")
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]['uuid'], self.issue.uuid)
self.assertEqual(response.json()[0]['last_activity'], self.match.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
self.assertEqual(len(response.json()[0]['timeline']), 4)
self.assertEqual(response.json()[0]['timeline'][0]['type'], 'state')
self.assertEqual(response.json()[0]['timeline'][1]['type'], 'mail')
self.assertEqual(response.json()[0]['timeline'][2]['type'], 'comment')
self.assertEqual(response.json()[0]['timeline'][1]['id'], self.mail1.id)
self.assertEqual(response.json()[0]['timeline'][2]['id'], self.comment.id)
self.assertEqual(response.json()[0]['timeline'][0]['state'], 'pending_new')
self.assertEqual(response.json()[0]['timeline'][1]['sender'], 'test')
self.assertEqual(response.json()[0]['timeline'][1]['recipient'], 'test')
self.assertEqual(response.json()[0]['timeline'][1]['subject'], 'test')
self.assertEqual(response.json()[0]['timeline'][1]['body'], 'test')
self.assertEqual(response.json()[0]['timeline'][1]['timestamp'],
self.mail1.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
self.assertEqual(response.json()[0]['timeline'][2]['comment'], 'test')
self.assertEqual(response.json()[0]['timeline'][2]['timestamp'],
self.comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
self.assertEqual(response.json()[0]['timeline'][3]['status'], 'possible')
self.assertEqual(response.json()[0]['timeline'][3]['timestamp'],
self.match.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
self.assertEqual(response.json()[0]['timeline'][3]['item']['description'], "foo")
self.assertEqual(response.json()[0]['timeline'][3]['item']['event'], "evt")
self.assertEqual(response.json()[0]['timeline'][3]['item']['box'], "box1")
self.assertEqual(response.json()[0]['related_items'][0]['description'], "foo")
self.assertEqual(response.json()[0]['related_items'][0]['event'], "evt")
self.assertEqual(response.json()[0]['related_items'][0]['box'], "box1")
def test_members(self):
response = self.client.get(f'/api/2/{self.event.slug}/item/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()), 1)
self.assertEqual(response.json()[0]['id'], self.item.id)
self.assertEqual(response.json()[0]['description'], 'foo')
self.assertEqual(response.json()[0]['box'], 'box1')
self.assertEqual(response.json()[0]['cid'], self.box.id)
self.assertEqual(response.json()[0]['file'], None)
self.assertEqual(response.json()[0]['returned'], False)
self.assertEqual(response.json()[0]['event'], self.event.slug)
self.assertEqual(len(response.json()[0]['related_issues']), 1)
self.assertEqual(response.json()[0]['related_issues'][0]['id'], self.issue.id)
self.assertEqual(response.json()[0]['related_issues'][0]['name'], "test issue")
def test_add_match(self):
response = self.client.get('/api/2/matches/')
self.assertEqual(1, len(response.json()))
item = Item.objects.create(container=self.box, event=self.event, description='1')
issue = IssueThread.objects.create(name="test issue", event=self.event)
response = self.client.post(f'/api/2/matches/',
{'item': item.id, 'issue_thread': issue.id},
content_type='application/json')
self.assertEqual(response.status_code, 201)
response = self.client.get('/api/2/matches/')
self.assertEqual(2, len(response.json()))
response = self.client.get('/api/2/tickets/')
self.assertEqual(4, len(response.json()[0]['timeline']))
self.assertEqual('item_relation', response.json()[0]['timeline'][3]['type'])
self.assertEqual('possible', response.json()[0]['timeline'][3]['status'])
self.assertEqual(1, len(response.json()[0]['related_items']))
def test_change_match_state(self):
response = self.client.get('/api/2/matches/')
self.assertEqual(1, len(response.json()))
response = self.client.get('/api/2/tickets/')
self.assertEqual(4, len(response.json()[0]['timeline']))
self.assertEqual('item_relation', response.json()[0]['timeline'][3]['type'])
self.assertEqual('possible', response.json()[0]['timeline'][3]['status'])
self.assertEqual(1, len(response.json()[0]['related_items']))
response = self.client.post(f'/api/2/matches/',
{'item': self.item.id, 'issue_thread': self.issue.id, 'status': 'confirmed'},
content_type='application/json')
self.assertEqual(response.status_code, 201)
self.assertEqual(response.json()['status'], 'confirmed')
self.assertEqual(response.json()['id'], 2)
response = self.client.get('/api/2/matches/')
self.assertEqual(2, len(response.json()))
response = self.client.get('/api/2/tickets/')
self.assertEqual(5, len(response.json()[0]['timeline']))
self.assertEqual('item_relation', response.json()[0]['timeline'][3]['type'])
self.assertEqual('possible', response.json()[0]['timeline'][3]['status'])
self.assertEqual('item_relation', response.json()[0]['timeline'][4]['type'])
self.assertEqual('confirmed', response.json()[0]['timeline'][4]['status'])
self.assertEqual(1, len(response.json()[0]['related_items']))

View file

@ -3,9 +3,9 @@ from datetime import datetime, timedelta
from django.test import TestCase, Client
from authentication.models import ExtendedUser
from inventory.models import Event
from inventory.models import Event, Container, Item
from mail.models import Email, EmailAttachment
from tickets.models import IssueThread, StateChange, Comment
from tickets.models import IssueThread, StateChange, Comment, ItemRelation
from django.contrib.auth.models import Permission
from knox.models import AuthToken
@ -20,6 +20,8 @@ class IssueApiTest(TestCase):
self.user.user_permissions.add(*Permission.objects.all())
self.user.save()
self.event = Event.objects.create(slug='evt')
self.box = Container.objects.create(name='box1')
self.item = Item.objects.create(container=self.box, description="foo", event=self.event)
self.token = AuthToken.objects.create(user=self.user)
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
@ -56,6 +58,11 @@ class IssueApiTest(TestCase):
comment="test",
timestamp=now + timedelta(seconds=3),
)
match = ItemRelation.objects.create(
issue_thread=issue,
item = self.item,
timestamp=now + timedelta(seconds=5),
)
self.assertEqual('pending_new', issue.state)
self.assertEqual('test issue', issue.name)
self.assertEqual(None, issue.assigned_to)
@ -69,8 +76,8 @@ class IssueApiTest(TestCase):
self.assertEqual(response.json()[0]['event'], "evt")
self.assertEqual(response.json()[0]['assigned_to'], None)
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(len(response.json()[0]['timeline']), 4)
self.assertEqual(response.json()[0]['last_activity'], match.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
self.assertEqual(len(response.json()[0]['timeline']), 5)
self.assertEqual(response.json()[0]['timeline'][0]['type'], 'state')
self.assertEqual(response.json()[0]['timeline'][1]['type'], 'mail')
self.assertEqual(response.json()[0]['timeline'][2]['type'], 'mail')
@ -94,6 +101,15 @@ class IssueApiTest(TestCase):
self.assertEqual(response.json()[0]['timeline'][3]['comment'], 'test')
self.assertEqual(response.json()[0]['timeline'][3]['timestamp'],
comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
self.assertEqual(response.json()[0]['timeline'][4]['status'], 'possible')
self.assertEqual(response.json()[0]['timeline'][4]['timestamp'],
match.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
self.assertEqual(response.json()[0]['timeline'][4]['item']['description'], "foo")
self.assertEqual(response.json()[0]['timeline'][4]['item']['event'], "evt")
self.assertEqual(response.json()[0]['timeline'][4]['item']['box'], "box1")
self.assertEqual(response.json()[0]['related_items'][0]['description'], "foo")
self.assertEqual(response.json()[0]['related_items'][0]['event'], "evt")
self.assertEqual(response.json()[0]['related_items'][0]['box'], "box1")
def test_issues_incomplete_timeline(self):
now = datetime.now()
@ -287,8 +303,7 @@ class IssueApiTest(TestCase):
content_type='application/json')
self.assertEqual(response.status_code, 400)
#def test_post_comment(self):
# def test_post_comment(self):
# issue = IssueThread.objects.create(
# name="test issue",
# )