Skip to content
Open

Api #12

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6f382be
Models and admin
B1Dobbs Aug 3, 2020
e70646f
Change to two foreign keys
B1Dobbs Aug 3, 2020
eda985f
Merge branch 'master' into ticket_relationships
B1Dobbs Aug 3, 2020
ffe5110
Update registration
B1Dobbs Aug 3, 2020
c3b274c
Ran migrations and added registered RelationshipType
BlaiseMahoro Aug 4, 2020
d159618
Back button to same line as title, fixed tabs
B1Dobbs Aug 5, 2020
35ffcd2
Merge branch 'master' into api
B1Dobbs Aug 5, 2020
eae7510
Revert "Merge branch 'master' into api"
B1Dobbs Aug 5, 2020
5c76a4b
Ticket Change State
B1Dobbs Aug 5, 2020
f7b8b71
Merge branch 'board2'
BlaiseMahoro Aug 5, 2020
9fc151a
Merge branch 'master' of https://github.com/B1Dobbs/Maroon-Task-Manag…
BlaiseMahoro Aug 5, 2020
6977487
Merge branch 'api'
BlaiseMahoro Aug 6, 2020
93b0489
Merged board
BlaiseMahoro Aug 6, 2020
d49aa57
Resoved conflicts
BlaiseMahoro Aug 6, 2020
a807f45
reverse changes
BlaiseMahoro Aug 6, 2020
3317c50
Merge branch 'master' of https://github.com/B1Dobbs/Maroon-Task-Manag…
BlaiseMahoro Aug 6, 2020
672f447
Merge branch 'master' of https://github.com/B1Dobbs/Maroon-Task-Manag…
BlaiseMahoro Aug 6, 2020
76e1993
Merge branch 'master' of https://github.com/B1Dobbs/Maroon-Task-Manag…
BlaiseMahoro Aug 6, 2020
d185c7c
Revert "Merge branch 'master' of https://github.com/B1Dobbs/Maroon-Ta…
dvazqueb11 Aug 6, 2020
bd97702
Revert "reverse changes"
dvazqueb11 Aug 6, 2020
ed5f20e
fix
dvazqueb11 Aug 6, 2020
3ca1dfd
Revert "Resoved conflicts"
dvazqueb11 Aug 6, 2020
661dd67
Revert "Merged board"
dvazqueb11 Aug 6, 2020
edc2f38
Revert "Merge branch 'api'"
dvazqueb11 Aug 6, 2020
8215128
Revert "Merge branch 'master' of https://github.com/B1Dobbs/Maroon-Ta…
dvazqueb11 Aug 6, 2020
78639dd
Revert "Merge branch 'board2'"
dvazqueb11 Aug 6, 2020
64be623
Revert "Merge branch 'master' into board2"
dvazqueb11 Aug 6, 2020
fd48e22
Access page fix
dvazqueb11 Aug 6, 2020
2430c23
Merge branch 'master' of https://github.com/B1Dobbs/Maroon-Task-Manag…
B1Dobbs Aug 6, 2020
1de2de1
Revert "Merge branch 'master' of https://github.com/B1Dobbs/Maroon-Ta…
B1Dobbs Aug 6, 2020
492234d
Remove maroon/static folder
B1Dobbs Aug 6, 2020
ea2792e
Manual merge of 'api' branch
B1Dobbs Aug 6, 2020
4115a3d
Merge branch 'board2' of https://github.com/B1Dobbs/Maroon-Task-Manag…
B1Dobbs Aug 6, 2020
f1999b3
Merge branch 'master' into board2
B1Dobbs Aug 6, 2020
f952231
Merge branch 'board2'
B1Dobbs Aug 6, 2020
993750a
Merge branch 'ticket_relationships' of https://github.com/B1Dobbs/Mar…
B1Dobbs Aug 6, 2020
626089c
Merge branch 'ticket_relationships'
B1Dobbs Aug 6, 2020
680d758
Merge branch 'master' into api
B1Dobbs Aug 6, 2020
81b0509
Using ticket project id instead of pk
B1Dobbs Aug 6, 2020
f4536ce
Added relationships to ticket serializer
B1Dobbs Aug 7, 2020
335c568
Link, Comment, and File crud
B1Dobbs Aug 7, 2020
1ab6ef6
Upload paths for files, only set ticket paths
B1Dobbs Aug 7, 2020
7e16f95
Check if file already exists for ticket
B1Dobbs Aug 7, 2020
033bc5a
Don't need to check uniqueness
B1Dobbs Aug 7, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions maroon/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@ class TicketTemplateSerializer(serializers.ModelSerializer):
types = CustomSlugRelatedField(many=True,queryset=models.Type.objects.all(),slug_field='type_name')#TypeSerializer(many=True)
states = CustomSlugRelatedField(many=True,queryset=models.State.objects.all(),slug_field='state_name')
attributeTypes = CustomSlugRelatedField(many=True,queryset=models.AttributeType.objects.all(),slug_field='name')
relationshipTypes = CustomSlugRelatedField(many=True,queryset=models.RelationshipType.objects.all(),slug_field='name')

def get_fields(self, *args, **kwargs):
#Save instance to pass as primary key for creating new items
fields = super(TicketTemplateSerializer, self).get_fields(*args, **kwargs)
fields['types'].child_relation.ticket_template = self.instance
fields['states'].child_relation.ticket_template = self.instance
fields['attributeTypes'].child_relation.ticket_template = self.instance
fields['attributeTypes'].child_relation.ticket_template = self.instance
fields['relationshipTypes'].child_relation.ticket_template = self.instance
return fields

class Meta:
model = models.TicketTemplate
fields = ['types','states','attributeTypes']
fields = ['types','states','attributeTypes','relationshipTypes']

def to_internal_value(self, data):
#Delete Types and States before the new ones are added to the database
Expand All @@ -48,6 +50,7 @@ def to_internal_value(self, data):

def update(self, instance, validated_data):
self.delete_removed_attributes(instance, validated_data.get('attributeTypes'))
self.delete_removed_relationships(instance, validated_data.get('relationshipTypes'))
return instance

def delete_removed_attributes(self, instance, validated_data):
Expand All @@ -56,6 +59,12 @@ def delete_removed_attributes(self, instance, validated_data):
if attribute.name not in names:
attribute.delete()

def delete_removed_relationships(self, instance, validated_data):
names = [new_relationship.name for new_relationship in validated_data]
for relationship in models.RelationshipType.objects.filter(ticket_template=instance):
if relationship.name not in names:
relationship.delete()

class RoleSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='profile.user.username')

Expand Down Expand Up @@ -124,8 +133,8 @@ class Meta:
class TicketSerializer(serializers.ModelSerializer):
project = serializers.CharField(source='project.name', required=False)
project_pk = serializers.IntegerField(source='project.pk', required=False, write_only=True)
state = serializers.CharField(source='state.state_name')
type = serializers.CharField(source='type.type_name')
state = serializers.CharField(source='state.state_name', default="None")
type = serializers.CharField(source='type.type_name', default="None")
assignee_list = serializers.ListField(child=serializers.CharField(), required=False, write_only=True)
assignees = ProfileSerializer(many=True, read_only=True)
attributes = AttributeSerialier(many=True, required=False)
Expand Down Expand Up @@ -211,6 +220,28 @@ def update_assignees(self, validated_data, ticket):
if profile.user.username not in validated_data:
ticket.assignees.remove(profile)

class CommentSerializer(serializers.ModelSerializer):
author = serializers.CharField(source='author.username', required=False, read_only=True)
created_date = serializers.DateTimeField(required=False, read_only=True)

class Meta:
model = models.Comment
fields = ('pk','author','created_date','body')

class LinkSerializer(serializers.ModelSerializer):
relationship_type = serializers.CharField(source='relationship_type.name')
ticket_2 = serializers.CharField(source='ticket_2.title', required=False, read_only=True)

class Meta:
model = models.Comment
fields = ('pk','relationship_type','ticket_2')

class FileSerializer(serializers.ModelSerializer):

class Meta:
model = models.File
fields = ('pk','name','created_date')




Expand Down
7 changes: 7 additions & 0 deletions maroon/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,12 @@
path('project/<int:pk>/ticket/<int:ticket_pk>', views.TicketDetail.as_view(), name='ticket_detail'),
path('profiles/', views.ProfileCreate.as_view(), name='profile_create'),
path('profiles/myprofile/', views.ProfileDetail.as_view(), name='profile_update'),
path('project/<int:pk>/ticket/<int:ticket_pk>/changestate', views.TicketUpdateState.as_view(), name='update_state'),
path('project/<int:pk>/ticket/<int:ticket_pk>/comment', views.Comment.as_view(), name='comment'),
path('project/<int:pk>/ticket/<int:ticket_pk>/comment/<int:comment_pk>', views.Comment.as_view(), name='comment'),
path('project/<int:pk>/ticket/<int:ticket_pk>/link', views.Link.as_view(), name='link'),
path('project/<int:pk>/ticket/<int:ticket_pk>/link/<int:link_pk>', views.Link.as_view(), name='comment'),
path('project/<int:pk>/ticket/<int:ticket_pk>/file', views.File.as_view(), name='file'),
path('project/<int:pk>/ticket/<int:ticket_pk>/file/<int:file_pk>', views.File.as_view(), name='comment'),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
99 changes: 99 additions & 0 deletions maroon/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.shortcuts import get_object_or_404
import json
from api.permissions import ProjectAdmin, ProjectCollaborator
from datetime import datetime

class TokenAuthView(APIView):
authentication_classes = (authentication.TokenAuthentication,)
Expand Down Expand Up @@ -138,6 +139,104 @@ def delete(self, request, *args, **kwargs):
super().delete(request, *args, **kwargs)
return Response("Ticket successfully deleted", status=status.HTTP_202_ACCEPTED)

class TicketUpdateState(TokenAuthView, APIView):
permission_classes = [ProjectCollaborator]

def post(self, request, **kwargs):

#Get state out of request body
body = json.loads(request.body)
state = body['state']

#Get project from url
project = get_object_or_404(models.Project, pk=self.kwargs['pk'])

#Get ticket by project id and confirm state is valid
try:
ticket = project.tickets.get(id_in_project=self.kwargs['ticket_pk'])
except:
return Response("Ticket not found", status=status.HTTP_404_NOT_FOUND)
#print(project.ticket_template.states.all().values('state_name'))
if project.ticket_template.states.all().filter(state_name=state).exists():
#Update state
ticket.state = project.ticket_template.states.all().get(state_name=state)
ticket.save()
return Response("Ticket state updated", status=status.HTTP_202_ACCEPTED)

#State not valid
return Response("The state is not valid", status=status.HTTP_406_NOT_ACCEPTABLE)

class TicketObjectView(generics.ListCreateAPIView, generics.DestroyAPIView,):
def get_ticket(self, **kwargs):
project = get_object_or_404(models.Project, pk=kwargs['pk'])
return project.tickets.get(id_in_project=kwargs['ticket_pk'])

def delete(self, request, *args, **kwargs):
obj = getattr(self.get_ticket(**kwargs), self.ticket_attribute).get(pk=kwargs[self.url_pk])
obj.delete()
return Response("Deleted successfully.", status=status.HTTP_202_ACCEPTED)

def get_queryset(self):
kwargs = self.kwargs
return getattr(self.get_ticket(**kwargs), self.ticket_attribute).all()

class Comment(TokenAuthView, TicketObjectView):
queryset = models.Comment.objects.all()
serializer_class = serializers.CommentSerializer
permission_classes = [ProjectCollaborator]
ticket_attribute = "comments"
url_pk = "comment_pk"

def post(self, request, *args, **kwargs):
body = json.loads(request.body)
comment = models.Comment(ticket=self.get_ticket(**kwargs), author=request.user, body=body['body'], created_date=datetime.now())
comment.save()
serializer = self.serializer_class(comment)
return Response(serializer.data, status=status.HTTP_201_CREATED)

class Link(TokenAuthView, TicketObjectView):
queryset = models.Relationship.objects.all()
serializer_class = serializers.LinkSerializer
permission_classes = [ProjectCollaborator]
ticket_attribute = "relationships"
url_pk = "link_pk"

def post(self, request, *args, **kwargs):
body = json.loads(request.body)
project = get_object_or_404(models.Project, pk=kwargs['pk'])
ticket_2 = project.tickets.get(id_in_project=body['related_ticket_project_pk'])

#Check if relationship eists and create the link
if project.ticket_template.relationshipTypes.filter(name=body['link_type']).exists():
relationship_type = project.ticket_template.relationshipTypes.get(name=body['link_type'])

link = models.Relationship(ticket_1=self.get_ticket(**kwargs), ticket_2=ticket_2, relationship_type=relationship_type)
link.save()
serializer = self.serializer_class(link)
return Response(serializer.data, status=status.HTTP_201_CREATED)

#Relationship not valid
return Response("The relationship is not valid", status=status.HTTP_406_NOT_ACCEPTABLE)

class File(TokenAuthView, TicketObjectView):
queryset = models.File.objects.all()
serializer_class = serializers.FileSerializer
permission_classes = [ProjectCollaborator]
ticket_attribute = "files"
url_pk = "file_pk"

def post(self, request, *args, **kwargs):
if 'file' not in request.data:
raise ParseError("Empty content")
file = request.data['file']

ticket = self.get_ticket(**kwargs)

file_obj = models.File(ticket=ticket, file=file, name=file.name, created_date=datetime.now())
file_obj.save()
serializer = self.serializer_class(file_obj)
return Response(serializer.data, status=status.HTTP_201_CREATED)


'''For printing post requests if needed.'''
def pretty_request(request):
Expand Down
1 change: 1 addition & 0 deletions maroon/maroon/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
'api.apps.ApiConfig',
'bootstrap_modal_forms',
'colorful',
'django_cleanup.apps.CleanupConfig',
]

MIDDLEWARE = [
Expand Down
Loading