Complete all major API endpoints for prototype

This commit is contained in:
Tanner Collin 2017-06-18 00:11:49 +00:00
parent 170878441e
commit 3823c887b6
6 changed files with 372 additions and 72 deletions

View File

@ -1,3 +1,11 @@
from django.contrib import admin from django.contrib import admin
from .models import UserInfo, Client, Provider, WorkType, Manage, Price, Shift
# Register your models here. # Register your models here.
admin.site.register(UserInfo)
admin.site.register(Client)
admin.site.register(Provider)
admin.site.register(WorkType)
admin.site.register(Manage)
admin.site.register(Price)
admin.site.register(Shift)

View File

@ -6,39 +6,59 @@ from django.core.validators import RegexValidator
class UserInfo(models.Model): class UserInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) user = models.OneToOneField(User, on_delete=models.CASCADE)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.") phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], max_length=16, blank=True) phone_number = models.CharField(validators=[phone_regex], max_length=16)
USER_TYPES = (('C', 'Client'), ('P', 'Provider'))
user_type = models.CharField(max_length=1, choices=USER_TYPES, blank=True) def __str__(self):
return 'UserInfo: ' + self.user.username
class Client(models.Model): class Client(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) user = models.OneToOneField(User, on_delete=models.CASCADE)
business_number = models.CharField(max_length=16, blank=True) business_number = models.CharField(max_length=16)
def __str__(self):
return 'Client: ' + self.user.username
class Provider(models.Model): class Provider(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) user = models.OneToOneField(User, on_delete=models.CASCADE)
sin = models.CharField(max_length=16, blank=True) sin = models.CharField(max_length=16)
class Work(models.Model): def __str__(self):
client = models.ForeignKey(Client, on_delete=models.CASCADE) return 'Provider: ' + self.user.username
color = models.CharField(max_length=16, blank=True)
label = models.CharField(max_length=100, blank=True) class WorkType(models.Model):
client = models.ForeignKey(Client, related_name='work_types', on_delete=models.CASCADE)
color = models.CharField(max_length=16)
label = models.CharField(max_length=100)
deleted = models.BooleanField(default=False)
def __str__(self):
return 'WorkType: ' + self.label + ' of ' + self.client.user.username
class Manage(models.Model): class Manage(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE) client = models.ForeignKey(Client, related_name='employees', on_delete=models.CASCADE)
provider = models.ForeignKey(Provider, on_delete=models.CASCADE) provider = models.ForeignKey(Provider, related_name='employers', on_delete=models.CASCADE)
note = models.CharField(max_length=500, blank=True) note = models.CharField(max_length=500, blank=True)
approved = models.NullBooleanField(blank=True)
deleted = models.BooleanField(default=False)
def __str__(self):
return self.client.user.username + ' manages ' + self.provider.user.username
class Price(models.Model): class Price(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE) management = models.ForeignKey(Manage, related_name='prices', on_delete=models.CASCADE)
provider = models.ForeignKey(Provider, on_delete=models.CASCADE) work_type = models.ForeignKey(WorkType, on_delete=models.CASCADE)
work = models.ForeignKey(Work, on_delete=models.CASCADE)
amount = models.DecimalField(max_digits=8, decimal_places=2) amount = models.DecimalField(max_digits=8, decimal_places=2)
deleted = models.BooleanField(default=False)
class Shift(models.Model): class Shift(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE) price = models.ForeignKey(Price, on_delete=models.CASCADE)
provider = models.ForeignKey(Provider, on_delete=models.CASCADE) set_price = models.DecimalField(max_digits=8, decimal_places=2, null=True)
work = models.ForeignKey(Work, on_delete=models.CASCADE) set_date = models.DateField()
set_start = models.DateTimeField() set_start = models.DateTimeField()
set_end = models.DateTimeField() set_end = models.DateTimeField()
amount = models.DecimalField(max_digits=8, decimal_places=2) actual_start = models.DateTimeField(null=True)
description = models.CharField(max_length=100, blank=True) actual_end = models.DateTimeField(null=True)
amount = models.DecimalField(max_digits=8, decimal_places=2, null=True)
description = models.CharField(max_length=100, null=True)
chart = models.TextField(max_length=1000, null=True)
deleted = models.BooleanField(default=False)

View File

@ -1,20 +1,3 @@
from pprint import pprint from pprint import pprint
from rest_framework import permissions from rest_framework import permissions
from caremyway.api.models import Client, Provider from caremyway.api.models import Client, Provider
class UserTypePermission(permissions.BasePermission):
"""
Disallow creation of client or provider if one already exists
"""
message = "User is already either a client or a provider."
def has_permission(self, request, view):
user = request.data.get('user', None)
if request.method != 'POST' or user is None:
return True
elif not Client.objects.filter(user=user).exists() \
and not Provider.objects.filter(user=user).exists():
return True
else:
return False

View File

@ -1,32 +1,255 @@
from datetime import datetime, timedelta, timezone
from decimal import Decimal
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.shortcuts import get_object_or_404
from rest_framework import serializers from rest_framework import serializers
from caremyway.api.models import UserInfo, Client, Provider from caremyway.api.models import UserInfo, Client, Provider, WorkType, Manage, Price, Shift
class UserInfoSerializer(serializers.ModelSerializer): class UserInfoSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = UserInfo model = UserInfo
fields = ('user', 'user_type', 'phone_number') fields = ('phone_number',)
class ChosenUserInfoSerializer(UserInfoSerializer): def create(self, validated_data):
class Meta(UserInfoSerializer.Meta): validated_data['user'] = self.context['request'].user
read_only_fields = ('user_type',) return serializers.ModelSerializer.create(self, validated_data)
class WorkTypeSerializer(serializers.ModelSerializer):
class Meta:
model = WorkType
fields = ('id', 'color', 'label', 'deleted')
read_only_fields =('deleted',)
def create(self, validated_data):
validated_data['client'] = self.context['request'].user.client
return serializers.ModelSerializer.create(self, validated_data)
class PriceSerializer(serializers.ModelSerializer):
get_employee_id = serializers.IntegerField(write_only=True)
get_work_type_id = serializers.IntegerField(write_only=True)
work_type = WorkTypeSerializer(read_only=True)
class Meta:
model = Price
fields = ('id', 'get_employee_id', 'get_work_type_id', 'work_type', 'amount', 'deleted')
read_only_fields =('deleted',)
depth = 1
def create(self, validated_data):
user = self.context['request'].user
if not Client.objects.filter(user=user).exists():
raise serializers.ValidationError("User is not a client.")
client = user.client
manage_objs = Manage.objects.filter(client=client).filter(deleted=False)
employee_id = validated_data.pop('get_employee_id')
employee = get_object_or_404(manage_objs, id=employee_id)
worktype_objs = WorkType.objects.filter(client=client).filter(deleted=False)
work_type_id = validated_data.pop('get_work_type_id')
work_type = get_object_or_404(worktype_objs, id=work_type_id)
if not employee.approved:
raise serializers.ValidationError("Employee hasn't approved employment yet.")
if Price.objects.filter(management=employee) \
.filter(work_type=work_type) \
.filter(deleted=False).exists():
raise serializers.ValidationError("Price for this work already defined.")
validated_data['management'] = employee
validated_data['work_type'] = work_type
return serializers.ModelSerializer.create(self, validated_data)
class EmployeeUserSerializer(serializers.ModelSerializer):
userinfo = UserInfoSerializer(read_only=True)
class Meta:
model = User
fields = ('is_active', 'first_name', 'last_name', 'email', 'userinfo')
depth = 1
class EmployeeSerializer(serializers.ModelSerializer):
provider_email = serializers.EmailField(write_only=True)
provider = serializers.SerializerMethodField()
prices = PriceSerializer(many=True, read_only=True)
class Meta:
model = Manage
fields = ('id', 'provider', 'provider_email', 'note', 'approved', 'prices', 'deleted')
read_only_fields = ('approved', 'deleted')
def get_provider(self, obj):
if obj.approved == True:
user = User.objects.get(provider=obj.provider)
return EmployeeUserSerializer(user).data
else:
return obj.provider.user.email
def create(self, validated_data):
user = self.context['request'].user
if not Client.objects.filter(user=user).exists():
raise serializers.ValidationError("User is not a client.")
client = user.client
provider_email = validated_data.pop('provider_email')
provider = get_object_or_404(Provider, user__email=provider_email)
if Manage.objects.filter(client=client) \
.filter(provider=provider) \
.filter(deleted=False).exists():
raise serializers.ValidationError("Employee already added.")
validated_data['client'] = client
validated_data['provider'] = provider
return serializers.ModelSerializer.create(self, validated_data)
class ClientSerializer(serializers.ModelSerializer): class ClientSerializer(serializers.ModelSerializer):
work_types = WorkTypeSerializer(many=True, read_only=True)
employees = EmployeeSerializer(many=True, read_only=True)
class Meta: class Meta:
model = Client model = Client
fields = ('user', 'business_number') fields = ('business_number', 'work_types', 'employees')
def create(self, validated_data):
user = self.context['request'].user
if Provider.objects.filter(user=user).exists():
raise serializers.ValidationError("User is already a Provider.")
validated_data['user'] = user
return serializers.ModelSerializer.create(self, validated_data)
class EmployerUserSerializer(serializers.ModelSerializer):
userinfo = UserInfoSerializer(read_only=True)
class Meta:
model = User
fields = ('is_active', 'first_name', 'last_name', 'email', 'userinfo')
depth = 1
class EmployerSerializer(serializers.ModelSerializer):
client = serializers.SerializerMethodField()
prices = PriceSerializer(many=True, read_only=True)
class Meta:
model = Manage
fields = ('id', 'client', 'note', 'approved', 'prices')
read_only_fields = ('note',)
def get_client(self, obj):
if obj.approved == True:
user = User.objects.get(client=obj.client)
return EmployerUserSerializer(user).data
else:
return obj.client.user.first_name + ' ' + obj.client.user.last_name
class ProviderSerializer(serializers.ModelSerializer): class ProviderSerializer(serializers.ModelSerializer):
employers = EmployerSerializer(many=True, read_only=True)
class Meta: class Meta:
model = Provider model = Provider
fields = ('user', 'sin') fields = ('sin', 'employers')
def create(self, validated_data):
user = self.context['request'].user
if Client.objects.filter(user=user).exists():
raise serializers.ValidationError("User is already a Client.")
validated_data['user'] = user
return serializers.ModelSerializer.create(self, validated_data)
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):
userinfo = UserInfoSerializer(read_only=True, allow_null=True) userinfo = UserInfoSerializer(read_only=True)
client = ClientSerializer(allow_null=True) client = ClientSerializer(read_only=True)
provider = ProviderSerializer(allow_null=True) provider = ProviderSerializer(read_only=True)
class Meta: class Meta:
model = User model = User
fields = ('username', 'is_active', 'first_name', 'last_name', 'email', 'userinfo', 'client', 'provider') fields = ('username', 'is_active', 'first_name', 'last_name', 'email', 'userinfo', 'client', 'provider')
depth = 1 depth = 1
class CShiftSerializer(serializers.ModelSerializer):
get_price_id = serializers.IntegerField(write_only=True)
class Meta:
model = Shift
fields = ('id', 'chart', 'get_price_id', 'price', 'set_price', 'set_date', 'set_start', 'set_end', 'actual_start', 'actual_end', 'amount', 'description', 'deleted')
read_only_fields = ('chart', 'price', 'set_price', 'set_date', 'actual_start', 'actual_end', 'amount', 'deleted')
def create(self, validated_data):
user = self.context['request'].user
if not Client.objects.filter(user=user).exists():
raise serializers.ValidationError("User is not a client.")
client = user.client
price_objs = Price.objects.filter(management__client=client).filter(deleted=False)
price_id = validated_data.pop('get_price_id')
price = get_object_or_404(price_objs, id=price_id)
if price.management.deleted:
raise serializers.ValidationError("Employment relationship was deleted.")
if price.work_type.deleted:
raise serializers.ValidationError("Type of work was deleted.")
if not price.management.approved:
raise serializers.ValidationError("Employee hasn't approved employment yet.")
now = datetime.now(timezone.utc)
set_start = validated_data['set_start']
set_end = validated_data['set_end']
if set_start > set_end:
raise serializers.ValidationError("Shift ends before it starts.")
if set_start > set_end - timedelta(hours=1):
raise serializers.ValidationError("Shift is less than one hour long.")
if set_start < now or set_end < now:
raise serializers.ValidationError("Shift is in the past.")
# TODO: Make sure provider isn't double-booked?????
validated_data['set_date'] = set_start.date()
validated_data['price'] = price
return serializers.ModelSerializer.create(self, validated_data)
class PShiftSerializer(serializers.ModelSerializer):
action = serializers.ChoiceField(write_only=True, choices=['checkin', 'checkout'])
class Meta:
model = Shift
fields = ('id', 'action', 'chart', 'price', 'set_price', 'set_date', 'set_start', 'set_end', 'actual_start', 'actual_end', 'amount', 'description', 'deleted')
read_only_fields = ('id', 'price', 'set_price', 'set_date', 'set_start', 'set_end', 'actual_start', 'actual_end', 'amount', 'description', 'deleted')
def update(self, instance, validated_data):
action = validated_data['action']
chart = validated_data['chart']
if action == 'checkin':
if instance.deleted:
raise serializers.ValidationError("Shift was deleted.")
if instance.actual_start:
raise serializers.ValidationError("Already checked in.")
if chart:
raise serializers.ValidationError("Can only provide chart on checkout.")
instance.actual_start = datetime.now(timezone.utc)
instance.set_price = instance.price.amount
elif action == 'checkout':
if not instance.actual_start:
raise serializers.ValidationError("Need to check in first.")
if instance.actual_end:
raise serializers.ValidationError("Already checked out.")
if not chart:
raise serializers.ValidationError("No chart info provided.")
instance.actual_end = datetime.now(timezone.utc)
shift_length = instance.actual_end - instance.actual_start
instance.amount = Decimal(shift_length.total_seconds()) / 3600 * instance.set_price
instance.chart = chart
instance.save()
return instance

View File

@ -1,12 +1,11 @@
from django.shortcuts import render from django.shortcuts import render
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from caremyway.api.models import UserInfo, Client, Provider from caremyway.api.models import UserInfo, Client, Provider, WorkType, Manage, Price, Shift
from rest_framework import viewsets, permissions, status from rest_framework import viewsets, permissions, status
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from rest_framework.response import Response from rest_framework.response import Response
from caremyway.api.serializers import UserSerializer, UserInfoSerializer, ClientSerializer, ProviderSerializer from rest_framework.views import APIView
from caremyway.api.permissions import UserTypePermission from caremyway.api.serializers import UserSerializer, UserInfoSerializer, ClientSerializer, ProviderSerializer, WorkTypeSerializer, EmployeeSerializer, EmployerSerializer, PriceSerializer, CShiftSerializer, PShiftSerializer
class UserViewSet(viewsets.ModelViewSet): class UserViewSet(viewsets.ModelViewSet):
lookup_field = 'username' lookup_field = 'username'
@ -16,52 +15,111 @@ class UserViewSet(viewsets.ModelViewSet):
http_method_names = ['get', 'head', 'put', 'options'] http_method_names = ['get', 'head', 'put', 'options']
def get_queryset(self): def get_queryset(self):
user = self.request.user return User.objects.filter(username=self.request.user)
if user.is_staff:
return User.objects.all().order_by('-date_joined')
else:
return User.objects.filter(username=user)
class UserInfoViewSet(viewsets.ModelViewSet): class UserInfoViewSet(viewsets.ModelViewSet):
lookup_field = "user__username" lookup_field = "user__username"
serializer_class = UserInfoSerializer serializer_class = UserInfoSerializer
# Disallow DELETE
http_method_names = ['get', 'post', 'head', 'put', 'options'] http_method_names = ['get', 'post', 'head', 'put', 'options']
def get_queryset(self): def get_queryset(self):
user = self.request.user return UserInfo.objects.filter(user__username=self.request.user)
if user.is_staff:
return UserInfo.objects.all().order_by('-user__date_joined')
else:
return UserInfo.objects.filter(user__username=user)
class ClientViewSet(viewsets.ModelViewSet): class ClientViewSet(viewsets.ModelViewSet):
lookup_field = "user__username" lookup_field = "user__username"
serializer_class = ClientSerializer serializer_class = ClientSerializer
# Disallow DELETE
http_method_names = ['get', 'post', 'head', 'put', 'options'] http_method_names = ['get', 'post', 'head', 'put', 'options']
permission_classes = (UserTypePermission,)
def get_queryset(self): def get_queryset(self):
user = self.request.user return Client.objects.filter(user__username=self.request.user)
if user.is_staff:
return Client.objects.all().order_by('-user__date_joined')
else:
return Client.objects.filter(user__username=user)
class ProviderViewSet(viewsets.ModelViewSet): class ProviderViewSet(viewsets.ModelViewSet):
lookup_field = "user__username" lookup_field = "user__username"
serializer_class = ProviderSerializer serializer_class = ProviderSerializer
# Disallow DELETE
http_method_names = ['get', 'post', 'head', 'put', 'options'] http_method_names = ['get', 'post', 'head', 'put', 'options']
permission_classes = (UserTypePermission,)
def get_queryset(self): def get_queryset(self):
user = self.request.user return Provider.objects.filter(user__username=self.request.user)
if user.is_staff:
return Provider.objects.all().order_by('-user__date_joined') class WorkTypeViewSet(viewsets.ModelViewSet):
else: serializer_class = WorkTypeSerializer
return Provider.objects.filter(user__username=user)
def get_queryset(self):
return WorkType.objects.filter(client__user__username=self.request.user)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.deleted = True
instance.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class EmployeeViewSet(viewsets.ModelViewSet):
serializer_class = EmployeeSerializer
def get_queryset(self):
return Manage.objects.filter(client__user__username=self.request.user)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.deleted = True
instance.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class EmployerViewSet(viewsets.ModelViewSet):
serializer_class = EmployerSerializer
# Disallow creation and deletions of relationships
http_method_names = ['get', 'head', 'put', 'options']
def get_queryset(self):
return Manage.objects.filter(provider__user__username=self.request.user)
class PriceViewSet(viewsets.ModelViewSet):
serializer_class = PriceSerializer
def get_queryset(self):
return Price.objects.filter(management__client__user__username=self.request.user)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.deleted = True
instance.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class CShiftViewSet(viewsets.ModelViewSet):
serializer_class = CShiftSerializer
def get_queryset(self):
return Shift.objects.filter(price__management__client__user__username=self.request.user) \
.filter(deleted=False) \
.order_by('-set_start')
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if instance.actual_start:
return Response("Shift already started.", status=status.HTTP_400_BAD_REQUEST)
instance.deleted = True
instance.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class PShiftViewSet(viewsets.ModelViewSet):
serializer_class = PShiftSerializer
# Disallow creation and deletions of relationships
http_method_names = ['get', 'head', 'put', 'options']
def get_queryset(self):
return Shift.objects.filter(price__management__provider__user__username=self.request.user) \
.filter(deleted=False) \
.order_by('-set_start')
@api_view() @api_view()
def null_view(request): def null_view(request):

View File

@ -14,6 +14,7 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
""" """
from django.conf.urls import url, include from django.conf.urls import url, include
from django.contrib import admin
from rest_framework import routers from rest_framework import routers
from caremyway.api import views from caremyway.api import views
@ -22,11 +23,18 @@ router.register(r'user', views.UserViewSet, 'user')
router.register(r'userinfo', views.UserInfoViewSet, 'userinfo') router.register(r'userinfo', views.UserInfoViewSet, 'userinfo')
router.register(r'client', views.ClientViewSet, 'client') router.register(r'client', views.ClientViewSet, 'client')
router.register(r'provider', views.ProviderViewSet, 'provider') router.register(r'provider', views.ProviderViewSet, 'provider')
router.register(r'worktype', views.WorkTypeViewSet, 'worktype')
router.register(r'employee', views.EmployeeViewSet, 'employee')
router.register(r'employer', views.EmployerViewSet, 'employer')
router.register(r'price', views.PriceViewSet, 'price')
router.register(r'cshift', views.CShiftViewSet, 'cshift')
router.register(r'pshift', views.PShiftViewSet, 'pshift')
# Wire up our API using automatic URL routing. # Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API. # Additionally, we include login URLs for the browsable API.
urlpatterns = [ urlpatterns = [
url(r'^', include(router.urls)), url(r'^', include(router.urls)),
url(r'^admin/', include(admin.site.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^rest-auth/registration/account-email-verification-sent/', views.null_view, name='account_email_verification_sent'), url(r'^rest-auth/registration/account-email-verification-sent/', views.null_view, name='account_email_verification_sent'),
url(r'^rest-auth/registration/account-confirm-email/', views.null_view, name='account_confirm_email'), url(r'^rest-auth/registration/account-confirm-email/', views.null_view, name='account_confirm_email'),