bton/prohibit_sending_empty_emails #100
4 changed files with 66 additions and 11 deletions
|
@ -1,12 +1,13 @@
|
||||||
from django.urls import re_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, status
|
||||||
from rest_framework.decorators import api_view, permission_classes
|
from rest_framework.decorators import api_view, permission_classes
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
from inventory.models import Event, Container, Item
|
from inventory.models import Event, Container, Item, Comment
|
||||||
from inventory.serializers import EventSerializer, ContainerSerializer, ItemSerializer, SearchResultSerializer
|
from inventory.serializers import EventSerializer, ContainerSerializer, CommentSerializer, ItemSerializer, \
|
||||||
|
SearchResultSerializer
|
||||||
|
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
|
||||||
|
@ -22,6 +23,17 @@ class ContainerViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Container.objects.all()
|
queryset = Container.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class ItemViewSet(viewsets.ModelViewSet):
|
||||||
|
serializer_class = ItemSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = Item.objects.all()
|
||||||
|
serializer = self.get_serializer_class()
|
||||||
|
if hasattr(serializer, 'Meta') and hasattr(serializer.Meta, 'prefetch_related_fields'):
|
||||||
|
queryset = queryset.prefetch_related(*serializer.Meta.prefetch_related_fields)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
def filter_items(items, query):
|
def filter_items(items, query):
|
||||||
query_tokens = query.split(' ')
|
query_tokens = query.split(' ')
|
||||||
for item in items:
|
for item in items:
|
||||||
|
@ -49,6 +61,7 @@ def search_items(request, event_slug, query):
|
||||||
@api_view(['GET', 'POST'])
|
@api_view(['GET', 'POST'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
def item(request, event_slug):
|
def item(request, event_slug):
|
||||||
|
vs = ItemViewSet()
|
||||||
try:
|
try:
|
||||||
event = None
|
event = None
|
||||||
if event_slug != 'none':
|
if event_slug != 'none':
|
||||||
|
@ -56,7 +69,7 @@ def item(request, event_slug):
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
if not request.user.has_event_perm(event, 'view_item'):
|
if not request.user.has_event_perm(event, 'view_item'):
|
||||||
return Response(status=403)
|
return Response(status=403)
|
||||||
return Response(ItemSerializer(Item.objects.filter(event=event), many=True).data)
|
return Response(ItemSerializer(vs.get_queryset().filter(event=event), many=True).data)
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
if not request.user.has_event_perm(event, 'add_item'):
|
if not request.user.has_event_perm(event, 'add_item'):
|
||||||
return Response(status=403)
|
return Response(status=403)
|
||||||
|
@ -71,12 +84,31 @@ def item(request, event_slug):
|
||||||
return Response(status=400)
|
return Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
|
@api_view(['POST'])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
@permission_required('tickets.add_comment', raise_exception=True)
|
||||||
|
def add_comment(request, event_slug, id):
|
||||||
|
event = None
|
||||||
|
if event_slug != 'none':
|
||||||
|
event = Event.objects.get(slug=event_slug)
|
||||||
|
item = Item.objects.get(event=event, id=id)
|
||||||
|
if not request.user.has_event_perm(event, 'view_item'):
|
||||||
|
return Response(status=403)
|
||||||
|
if 'comment' not in request.data or request.data['comment'] == '':
|
||||||
|
return Response({'status': 'error', 'message': 'missing comment'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
comment = Comment.objects.create(
|
||||||
|
item=item,
|
||||||
|
comment=request.data['comment'],
|
||||||
|
)
|
||||||
|
return Response(CommentSerializer(comment).data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
|
||||||
@api_view(['GET', 'PUT', 'DELETE', 'PATCH'])
|
@api_view(['GET', 'PUT', 'DELETE', 'PATCH'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
def item_by_id(request, event_slug, id):
|
def item_by_id(request, event_slug, id):
|
||||||
try:
|
try:
|
||||||
event = Event.objects.get(slug=event_slug)
|
event = Event.objects.get(slug=event_slug)
|
||||||
item = Item.objects.get(event=event, id=id)
|
item = ItemViewSet().get_queryset().get(event=event, id=id)
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
if not request.user.has_event_perm(event, 'view_item'):
|
if not request.user.has_event_perm(event, 'view_item'):
|
||||||
return Response(status=403)
|
return Response(status=403)
|
||||||
|
@ -117,5 +149,6 @@ urlpatterns = router.urls + [
|
||||||
re_path(r'^(?P<event_slug>[\w-]+)/items/$', item, name='item'),
|
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-]+)/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'),
|
re_path(r'^(?P<event_slug>[\w-]+)/item/$', item, name='item'),
|
||||||
|
re_path(r'^(?P<event_slug>[\w-]+)/item/(?P<id>\d+)/comment/$', add_comment, name='add_comment'),
|
||||||
re_path(r'^(?P<event_slug>[\w-]+)/item/(?P<id>\d+)/$', item_by_id, name='item_by_id'),
|
re_path(r'^(?P<event_slug>[\w-]+)/item/(?P<id>\d+)/$', item_by_id, name='item_by_id'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework import serializers
|
||||||
from rest_framework.relations import SlugRelatedField
|
from rest_framework.relations import SlugRelatedField
|
||||||
|
|
||||||
from files.models import File
|
from files.models import File
|
||||||
from inventory.models import Event, Container, Item
|
from inventory.models import Event, Container, Item, Comment
|
||||||
from inventory.shared_serializers import BasicItemSerializer
|
from inventory.shared_serializers import BasicItemSerializer
|
||||||
from mail.models import EventAddress
|
from mail.models import EventAddress
|
||||||
from tickets.shared_serializers import BasicIssueSerializer
|
from tickets.shared_serializers import BasicIssueSerializer
|
||||||
|
@ -38,6 +38,18 @@ class ContainerSerializer(serializers.ModelSerializer):
|
||||||
return len(instance.items)
|
return len(instance.items)
|
||||||
|
|
||||||
|
|
||||||
|
class CommentSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
if 'comment' not in attrs or attrs['comment'] == '':
|
||||||
|
raise serializers.ValidationError('comment cannot be empty')
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Comment
|
||||||
|
fields = ('id', 'comment', 'timestamp', 'item')
|
||||||
|
|
||||||
|
|
||||||
class ItemSerializer(BasicItemSerializer):
|
class ItemSerializer(BasicItemSerializer):
|
||||||
timeline = serializers.SerializerMethodField()
|
timeline = serializers.SerializerMethodField()
|
||||||
dataImage = serializers.CharField(write_only=True, required=False)
|
dataImage = serializers.CharField(write_only=True, required=False)
|
||||||
|
@ -48,6 +60,7 @@ class ItemSerializer(BasicItemSerializer):
|
||||||
fields = ['cid', 'box', 'id', 'description', 'file', 'dataImage', 'returned', 'event', 'related_issues',
|
fields = ['cid', 'box', 'id', 'description', 'file', 'dataImage', 'returned', 'event', 'related_issues',
|
||||||
'timeline']
|
'timeline']
|
||||||
read_only_fields = ['id']
|
read_only_fields = ['id']
|
||||||
|
prefetch_related_fields = ['comments', 'issue_relation_changes', 'container_history']
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
container = None
|
container = None
|
||||||
|
|
|
@ -21,7 +21,13 @@ from tickets.shared_serializers import RelationSerializer
|
||||||
|
|
||||||
class IssueViewSet(viewsets.ModelViewSet):
|
class IssueViewSet(viewsets.ModelViewSet):
|
||||||
serializer_class = IssueSerializer
|
serializer_class = IssueSerializer
|
||||||
queryset = IssueThread.objects.all().prefetch_related('state_changes', 'comments', 'emails', 'emails__attachments', 'assignments', 'item_relation_changes', 'shipping_vouchers')
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = IssueThread.objects.all()
|
||||||
|
serializer = self.get_serializer_class()
|
||||||
|
if hasattr(serializer, 'Meta') and hasattr(serializer.Meta, 'prefetch_related_fields'):
|
||||||
|
queryset = queryset.prefetch_related(*serializer.Meta.prefetch_related_fields)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class RelationViewSet(viewsets.ModelViewSet):
|
class RelationViewSet(viewsets.ModelViewSet):
|
||||||
|
@ -171,5 +177,5 @@ urlpatterns = ([
|
||||||
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'^(?P<event_slug>[\w-]+)/tickets/manual/$', manual_ticket, name='manual_ticket'),
|
re_path(r'^(?P<event_slug>[\w-]+)/tickets/manual/$', manual_ticket, name='manual_ticket'),
|
||||||
re_path(r'^(?P<event_slug>[\w-]+)/tickets/(?P<query>[-A-Za-z0-9+/]*={0,3})/$', search_issues,
|
re_path(r'^(?P<event_slug>[\w-]+)/tickets/(?P<query>[-A-Za-z0-9+/]*={0,3})/$', search_issues,
|
||||||
name='search_issues'),
|
name='search_issues'),
|
||||||
] + router.urls)
|
] + router.urls)
|
||||||
|
|
|
@ -47,6 +47,8 @@ class IssueSerializer(BasicIssueSerializer):
|
||||||
model = IssueThread
|
model = IssueThread
|
||||||
fields = ('id', 'timeline', 'name', 'state', 'assigned_to', 'last_activity', 'uuid', 'related_items', 'event')
|
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')
|
||||||
|
prefetch_related_fields = ['state_changes', 'comments', 'emails', 'emails__attachments', 'assignments',
|
||||||
|
'item_relation_changes', 'shipping_vouchers']
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
ret = super().to_internal_value(data)
|
ret = super().to_internal_value(data)
|
||||||
|
@ -63,12 +65,14 @@ class IssueSerializer(BasicIssueSerializer):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_last_activity(self):
|
def get_last_activity(self):
|
||||||
try:
|
try:
|
||||||
last_state_change = max([t.timestamp for t in self.state_changes.all()]) if self.state_changes.exists() else None
|
last_state_change = max(
|
||||||
|
[t.timestamp for t in self.state_changes.all()]) if self.state_changes.exists() else None
|
||||||
last_comment = max([t.timestamp for t in self.comments.all()]) if self.comments.exists() else None
|
last_comment = max([t.timestamp for t in self.comments.all()]) if self.comments.exists() else None
|
||||||
last_mail = max([t.timestamp for t in self.emails.all()]) if self.emails.exists() else None
|
last_mail = max([t.timestamp for t in self.emails.all()]) if self.emails.exists() else None
|
||||||
last_assignment = max([t.timestamp for t in self.assignments.all()]) if self.assignments.exists() else None
|
last_assignment = max([t.timestamp for t in self.assignments.all()]) if self.assignments.exists() else None
|
||||||
|
|
||||||
last_relation = max([t.timestamp for t in self.item_relation_changes.all()]) if self.item_relation_changes.exists() else None
|
last_relation = max([t.timestamp for t in
|
||||||
|
self.item_relation_changes.all()]) if self.item_relation_changes.exists() else None
|
||||||
args = [x for x in [last_state_change, last_comment, last_mail, last_assignment, last_relation] if
|
args = [x for x in [last_state_change, last_comment, last_mail, last_assignment, last_relation] if
|
||||||
x is not None]
|
x is not None]
|
||||||
return max(args)
|
return max(args)
|
||||||
|
@ -129,7 +133,6 @@ class IssueSerializer(BasicIssueSerializer):
|
||||||
return sorted(timeline, key=lambda x: x['timestamp'])
|
return sorted(timeline, key=lambda x: x['timestamp'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SearchResultSerializer(serializers.Serializer):
|
class SearchResultSerializer(serializers.Serializer):
|
||||||
search_score = serializers.IntegerField()
|
search_score = serializers.IntegerField()
|
||||||
item = IssueSerializer()
|
item = IssueSerializer()
|
||||||
|
|
Loading…
Add table
Reference in a new issue