diff --git a/api/admin.py b/api/admin.py index 8df206b..b15457b 100644 --- a/api/admin.py +++ b/api/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin -from .models import Todo +from .models import Collaborator, Todo -admin.site.register(Todo) \ No newline at end of file +admin.site.register(Todo) +admin.site.register(Collaborator) \ No newline at end of file diff --git a/api/migrations/0003_collaborator.py b/api/migrations/0003_collaborator.py new file mode 100644 index 0000000..6fda4d9 --- /dev/null +++ b/api/migrations/0003_collaborator.py @@ -0,0 +1,24 @@ +# Generated by Django 3.0.7 on 2021-08-08 08:06 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('api', '0002_todo_creator'), + ] + + operations = [ + migrations.CreateModel( + name='Collaborator', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('collab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('todo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Todo')), + ], + ), + ] diff --git a/api/models.py b/api/models.py index ab3ca0a..ad5760d 100644 --- a/api/models.py +++ b/api/models.py @@ -7,4 +7,11 @@ class Todo(models.Model): title = models.CharField(max_length=255) def __str__(self): - return self.title \ No newline at end of file + return self.title + +class Collaborator(models.Model): + todo = models.ForeignKey(Todo, on_delete=models.CASCADE) + collab = models.ForeignKey(User, on_delete=models.CASCADE) + + def __str__(self): + return self.collab.first_name +" "+ self.collab.last_name + "- " + self.todo.title \ No newline at end of file diff --git a/api/serializers.py b/api/serializers.py index ffd9d3a..c259e8c 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,23 +1,9 @@ +from django.contrib.auth.models import User from rest_framework import serializers -from .models import Todo - - -""" -TODO: -Create the appropriate Serializer class(es) for implementing -Todo GET (List and Detail), PUT, PATCH and DELETE. -""" - +from .models import Todo, Collaborator +from authentication.serializers import UserSerializer class TodoCreateSerializer(serializers.ModelSerializer): - """ - TODO: - Currently, the /todo/create/ endpoint returns only 200 status code, - after successful Todo creation. - - Modify the below code (if required), so that this endpoint would - also return the serialized Todo data (id etc.), alongwith 200 status code. - """ def save(self, **kwargs): data = self.validated_data user = self.context['request'].user @@ -27,3 +13,20 @@ def save(self, **kwargs): class Meta: model = Todo fields = ('id', 'title',) + +class TodoCollabSerializer(serializers.ModelSerializer): + collab_username = serializers.CharField(max_length=255, required=True) + def save(self, id, **kwargs): + data = self.validated_data + user = self.context['request'].user + username = data['collab_username'] + collab = User.objects.filter(username=username)[0] + todo = Todo.objects.filter(creator=user, pk=id)[0] + if todo: + collaboration = Collaborator.objects.create(todo=todo, collab=collab) + return collaboration + + class Meta: + model = Collaborator + fields = ('collab_username',) + diff --git a/api/urls.py b/api/urls.py index 72f4978..5f4d0a4 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from .views import TodoCreateView +from .views import TodoCreateView, TodoGetView, TodoOperationsView, TodoAddCollabView, TodoRemoveCollabView """ TODO: @@ -9,4 +9,8 @@ urlpatterns = [ path('todo/create/', TodoCreateView.as_view()), + path('todo/', TodoGetView.as_view()), + path('todo//', TodoOperationsView.as_view()), + path('todo//add-collaborators/', TodoAddCollabView.as_view()), + path('todo//remove-collaborators/', TodoRemoveCollabView.as_view()) ] \ No newline at end of file diff --git a/api/views.py b/api/views.py index d676c64..c215e71 100644 --- a/api/views.py +++ b/api/views.py @@ -1,35 +1,125 @@ -from rest_framework import generics +import re +from django.contrib.auth.models import User +from rest_framework import generics, serializers from rest_framework import permissions from rest_framework import status +from rest_framework import response from rest_framework.response import Response -from .serializers import TodoCreateSerializer -from .models import Todo - - -""" -TODO: -Create the appropriate View classes for implementing -Todo GET (List and Detail), PUT, PATCH and DELETE. -""" - +from .serializers import TodoCreateSerializer, TodoCollabSerializer +from api.serializers import TodoCollabSerializer +from .models import Todo, Collaborator class TodoCreateView(generics.GenericAPIView): - """ - TODO: - Currently, the /todo/create/ endpoint returns only 200 status code, - after successful Todo creation. - - Modify the below code (if required), so that this endpoint would - also return the serialized Todo data (id etc.), alongwith 200 status code. - """ + permission_classes = (permissions.IsAuthenticated, ) serializer_class = TodoCreateSerializer def post(self, request): - """ - Creates a Todo entry for the logged in user. - """ serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() - return Response(status=status.HTTP_200_OK) + instance = Todo.objects.filter(creator=request.user,title=serializer.data['title']) + getTodo = self.get_serializer(instance.last()) + + return Response(getTodo.data,status=status.HTTP_200_OK) + +class TodoGetView(generics.GenericAPIView): + + permission_classes = (permissions.IsAuthenticated, ) + serializer_class = TodoCreateSerializer + + def get(self, request): + todo=Todo.objects.filter(creator=request.user) + serializer1=self.get_serializer(todo,many=True) + todoCollab=Collaborator.objects.filter(collab=request.user) + serializer2 = [] + for collabs in todoCollab: + temp=self.get_serializer(collabs.todo) + serializer2.append(temp.data) + response_data = { + "owner": serializer1.data, + "collaborator": serializer2 + } + return Response(response_data,status=status.HTTP_200_OK) + +class TodoOperationsView(generics.GenericAPIView): + + permission_classes = (permissions.IsAuthenticated, ) + serializer_class = TodoCreateSerializer + + def get(self, request, id): + todo = Todo.objects.filter(creator=request.user, pk=id) + if todo: + ownership = "owner" + title=todo[0].title + else: + todo=Todo.objects.filter(pk=id)[0] + todoCollab=Collaborator.objects.filter(collab=request.user, todo=todo)[0] + ownership = "collaborator" + title=todoCollab.todo.title + response_data = { + "ownership": ownership, + "id": id, + "title": title + } + return Response(response_data, status=status.HTTP_200_OK) + + def put(self, request, id): + todo = Todo.objects.filter(creator=request.user, pk=id) + if todo: + todo[0].title = request.data['title'] + todo[0].save() + serializer = self.get_serializer(todo) + else: + todo=Todo.objects.filter(pk=id)[0] + todoCollab=Collaborator.objects.filter(collab=request.user, todo=todo)[0] + todoCollab.todo.title = request.data['title'] + todoCollab.todo.save() + serializer = self.get_serializer(todoCollab.todo) + return Response(serializer.data, status=status.HTTP_200_OK) + + def patch(self, request, id): + todo = Todo.objects.filter(creator=request.user, pk=id) + if todo: + todo[0].title = request.data['title'] + todo[0].save() + serializer = self.get_serializer(todo) + else: + todo=Todo.objects.filter(pk=id)[0] + todoCollab=Collaborator.objects.filter(collab=request.user, todo=todo)[0] + todoCollab.todo.title = request.data['title'] + todoCollab.todo.save() + serializer = self.get_serializer(todoCollab.todo) + return Response(serializer.data, status=status.HTTP_200_OK) + + def delete(self, request, id): + todo = Todo.objects.filter(creator=request.user, pk=id) + if todo: + todo.delete() + else: + todo=Todo.objects.filter(pk=id)[0] + todoCollab=Collaborator.objects.filter(collab=request.user, todo=todo)[0] + todoCollab.todo.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + +class TodoAddCollabView(generics.GenericAPIView): + permission_classes = (permissions.IsAuthenticated, ) + serializer_class = TodoCollabSerializer + + def post(self, request, id): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save(id=id) + return Response(status=status.HTTP_201_CREATED) + +class TodoRemoveCollabView(generics.GenericAPIView): + permission_classes = (permissions.IsAuthenticated, ) + serializer_class = TodoCollabSerializer + + def post(self, request, id): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + todo = Todo.objects.filter(creator=request.user, pk=id)[0] + collab = User.objects.filter(username=request.data['collab_username'])[0] + Collaborator.objects.filter(todo=todo, collab=collab).delete() + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/authentication/serializers.py b/authentication/serializers.py index 47264af..076fbb0 100644 --- a/authentication/serializers.py +++ b/authentication/serializers.py @@ -8,16 +8,39 @@ class TokenSerializer(serializers.Serializer): class LoginSerializer(serializers.Serializer): - # TODO: Implement login functionality - pass + username = serializers.CharField(max_length=255) + password = serializers.CharField(max_length=128, write_only=True) + def create(self, data): + data = self.validated_data + username = data['username'] + password = data['password'] + user = authenticate(username=username, password=password) + return user class RegisterSerializer(serializers.Serializer): - # TODO: Implement register functionality - pass - + first_name = serializers.CharField(max_length=128, required=True) + last_name = serializers.CharField(max_length=128) + email = serializers.EmailField(max_length=None, min_length=None, allow_blank=False) + username = serializers.CharField(max_length=255, required=True) + password = serializers.CharField(max_length=128, required=True, write_only=True) + + def create(self, data): + data = self.validated_data + first_name = data['first_name'] + last_name = data['last_name'] + email = data['email'] + username = data['username'] + password = data['password'] + user = User.objects.create_user(username=username, email=email, password=password) + user.first_name = first_name + user.last_name = last_name + user.save() + + user = authenticate(username=username, password=password) + return user class UserSerializer(serializers.ModelSerializer): - # TODO: Implement the functionality to display user details - pass - \ No newline at end of file + class Meta: + model = User + fields = ['id','first_name','last_name','username','email'] \ No newline at end of file diff --git a/authentication/views.py b/authentication/views.py index d748434..30570b5 100644 --- a/authentication/views.py +++ b/authentication/views.py @@ -3,40 +3,53 @@ from rest_framework import status from rest_framework.response import Response from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User +from django.contrib.auth import authenticate from .serializers import ( LoginSerializer, RegisterSerializer, UserSerializer, TokenSerializer) def create_auth_token(user): - """ - Returns the token required for authentication for a user. - """ token, _ = Token.objects.get_or_create(user=user) return token class LoginView(generics.GenericAPIView): - """ - TODO: - Implement login functionality, taking username and password - as input, and returning the Token. - """ - pass - + serializer_class = LoginSerializer + + def post(self, request): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + user = serializer.save() + if user: + token = create_auth_token(user) + response_data = {"token": "Token " + str(token)} + return Response(response_data, status=status.HTTP_200_OK) + else: + return Response(status=status.HTTP_404_NOT_FOUND) class RegisterView(generics.GenericAPIView): - """ - TODO: - Implement register functionality, registering the user by - taking his details, and returning the Token. - """ - pass + serializer_class = RegisterSerializer + def post(self, request): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + user = serializer.save() + token = create_auth_token(user) + response_data = {"token": "Token " + str(token)} + return Response(response_data, status=status.HTTP_200_OK) class UserProfileView(generics.RetrieveAPIView): - """ - TODO: - Implement the functionality to retrieve the details - of the logged in user. - """ - pass \ No newline at end of file + permission_classes = (permissions.IsAuthenticated, ) + serializer_class = UserSerializer + + def get(self, request): + serializer = self.get_serializer(request.user) + response_data = { + "id": serializer.data['id'], + "name": serializer.data['first_name'] + " " + serializer.data['last_name'], + "email": serializer.data['email'], + "username": serializer.data['username'] + } + + return Response(response_data, status=status.HTTP_200_OK) diff --git a/todo/settings.py b/todo/settings.py index 8359271..86b2f26 100644 --- a/todo/settings.py +++ b/todo/settings.py @@ -45,7 +45,8 @@ 'rest_framework.authtoken', 'drf_yasg', 'corsheaders', - 'api' + 'api', + 'authentication' ] MIDDLEWARE = [