mirror of
				https://github.com/fspc/workstand.git
				synced 2025-11-03 16:45:34 -05:00 
			
		
		
		
	Big refactor of views.
This commit is contained in:
		
							parent
							
								
									ec68c34701
								
							
						
					
					
						commit
						b6d2f5e88c
					
				
							
								
								
									
										83
									
								
								bikeshop_project/registration/tests/test_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								bikeshop_project/registration/tests/test_utils.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					from datetime import timedelta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					from model_mommy import mommy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from core.models import Visit
 | 
				
			||||||
 | 
					from registration.models import Member
 | 
				
			||||||
 | 
					from registration.utils import signin_member, AlreadySignedInError, member_signed_in, get_signed_in_members
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GetSignedInMembersTests(TestCase):
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        self.now = timezone.now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.member1 = mommy.make(model=Member)
 | 
				
			||||||
 | 
					        self.member2 = mommy.make(model=Member)
 | 
				
			||||||
 | 
					        self.member3 = mommy.make(model=Member)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        three_hours_ago = self.now - timedelta(hours=3)
 | 
				
			||||||
 | 
					        five_hours_ago = self.now - timedelta(hours=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.visit1 = Visit.objects.create(member=self.member1, purpose=Visit.DONATE, created_at=self.now)
 | 
				
			||||||
 | 
					        self.visit2 = Visit.objects.create(member=self.member2, purpose=Visit.DONATE, created_at=three_hours_ago)
 | 
				
			||||||
 | 
					        self.visit3 = Visit.objects.create(member=self.member3, purpose=Visit.DONATE, created_at=five_hours_ago)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_get_signed_in_members(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Only members signed-in in the window are returned
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        result1 = get_signed_in_members(end=self.now)  # default window=4
 | 
				
			||||||
 | 
					        self.assertEqual(len(result1), 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result2 = get_signed_in_members(window=2, end=self.now)
 | 
				
			||||||
 | 
					        self.assertEqual(len(result2), 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result3 = get_signed_in_members(window=5, end=self.now)
 | 
				
			||||||
 | 
					        self.assertEqual(len(result3), 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SigninMember(TestCase):
 | 
				
			||||||
 | 
					    def test_not_signed_in(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        A member who hasn't signed-in in 4 hours is signed-in.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        member = mommy.make(Member)
 | 
				
			||||||
 | 
					        purpose = Visit.FIX
 | 
				
			||||||
 | 
					        visit = signin_member(member, purpose)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertIsInstance(visit, Visit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_signed_in(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        A member who has signed-in in 4 hours is not signed-in.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        member = mommy.make(Member)
 | 
				
			||||||
 | 
					        purpose = Visit.FIX
 | 
				
			||||||
 | 
					        signin_member(member, purpose)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self.assertRaises(AlreadySignedInError):
 | 
				
			||||||
 | 
					            signin_member(member, purpose)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CheckMemberSignedIn(TestCase):
 | 
				
			||||||
 | 
					    def test_member_not_signed_in(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Returns false when member is not signed-in
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        not_signed_member = mommy.make(model=Member)
 | 
				
			||||||
 | 
					        result = member_signed_in(not_signed_member)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertFalse(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_member_signed_in(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Returns true when member is signed-in
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        member = mommy.make(model=Member)
 | 
				
			||||||
 | 
					        Visit.objects.create(member=member, purpose=Visit.DONATE)
 | 
				
			||||||
 | 
					        result = member_signed_in(member)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertTrue(result)
 | 
				
			||||||
							
								
								
									
										37
									
								
								bikeshop_project/registration/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								bikeshop_project/registration/utils.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					from datetime import datetime, timedelta
 | 
				
			||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db.models import QuerySet
 | 
				
			||||||
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from core.models import Visit
 | 
				
			||||||
 | 
					from registration.models import Member
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AlreadySignedInError(ValueError):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def signin_member(member: Member, purpose: str) -> Visit:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Signs in a member, creating a new `Visit`
 | 
				
			||||||
 | 
					    :param member: the member to be signed in
 | 
				
			||||||
 | 
					    :param purpose: The reason for visit. E.g. Fix a bike or volunteer
 | 
				
			||||||
 | 
					    :return: a new `Visit`
 | 
				
			||||||
 | 
					    :raise: `AlreadySignedInError` or `ValidationError`
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    if not member_signed_in(member):
 | 
				
			||||||
 | 
					        return Visit.objects.create(member=member, purpose=purpose)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    raise AlreadySignedInError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def member_signed_in(member: Member, window: int = 4) -> bool:
 | 
				
			||||||
 | 
					    return get_signed_in_members(window=window).filter(id__in=[member.id]).exists()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_signed_in_members(window: int = 4, end: Optional[datetime] = None) -> QuerySet:
 | 
				
			||||||
 | 
					    new_end = end if end else timezone.now()
 | 
				
			||||||
 | 
					    start = new_end - timedelta(hours=window)
 | 
				
			||||||
 | 
					    visits = Visit.objects.filter(created_at__lte=new_end, created_at__gte=start)
 | 
				
			||||||
 | 
					    return visits
 | 
				
			||||||
@ -1,27 +1,23 @@
 | 
				
			|||||||
import json
 | 
					import json
 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from datetime import timedelta
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib.auth.decorators import login_required
 | 
					from django.contrib.auth.decorators import login_required
 | 
				
			||||||
from django.core.urlresolvers import reverse
 | 
					from django.core.urlresolvers import reverse
 | 
				
			||||||
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
 | 
					from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
 | 
				
			||||||
from django.shortcuts import get_object_or_404
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
from django.template.response import TemplateResponse
 | 
					from django.template.response import TemplateResponse
 | 
				
			||||||
from django.utils import timezone
 | 
					 | 
				
			||||||
from django.utils.decorators import method_decorator
 | 
					from django.utils.decorators import method_decorator
 | 
				
			||||||
from django.views.decorators.csrf import csrf_exempt
 | 
					from django.views.decorators.csrf import csrf_exempt
 | 
				
			||||||
from django.views.generic import TemplateView, View
 | 
					from django.views.generic import TemplateView, View
 | 
				
			||||||
 | 
					from haystack.query import SearchQuerySet
 | 
				
			||||||
 | 
					from rest_framework import serializers
 | 
				
			||||||
from rest_framework.renderers import JSONRenderer
 | 
					from rest_framework.renderers import JSONRenderer
 | 
				
			||||||
from rest_framework.serializers import ModelSerializer
 | 
					from rest_framework.serializers import ModelSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from core.models import Visit
 | 
					from core.models import Visit
 | 
				
			||||||
from haystack.query import SearchQuerySet
 | 
					from registration.utils import signin_member, get_signed_in_members
 | 
				
			||||||
 | 
					 | 
				
			||||||
from .forms import MemberForm
 | 
					from .forms import MemberForm
 | 
				
			||||||
from .models import Member
 | 
					from .models import Member
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger('bikeshop')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@method_decorator(login_required, name='dispatch')
 | 
					@method_decorator(login_required, name='dispatch')
 | 
				
			||||||
class MemberFormView(View):
 | 
					class MemberFormView(View):
 | 
				
			||||||
@ -69,17 +65,23 @@ class MemberSearchView(View):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MemberSerializer(ModelSerializer):
 | 
					class MemberSerializer(ModelSerializer):
 | 
				
			||||||
 | 
					    first_name = serializers.CharField(allow_blank=True, required=False)
 | 
				
			||||||
 | 
					    last_name = serializers.CharField(allow_blank=True, required=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = Member
 | 
					        model = Member
 | 
				
			||||||
        fields = ('full_name', 'email', 'id')
 | 
					        fields = ('first_name', 'last_name', 'email', 'id')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VisitSerializer(ModelSerializer):
 | 
					class VisitSerializer(ModelSerializer):
 | 
				
			||||||
    member = MemberSerializer()
 | 
					    member = MemberSerializer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = Visit
 | 
					        model = Visit
 | 
				
			||||||
        fields = ('created_at', 'purpose', 'member')
 | 
					        fields = ('created_at', 'purpose', 'member')
 | 
				
			||||||
        depth = 1
 | 
					        depth = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MemberSignIn(View):
 | 
					class MemberSignIn(View):
 | 
				
			||||||
    @method_decorator(csrf_exempt)
 | 
					    @method_decorator(csrf_exempt)
 | 
				
			||||||
    def dispatch(self, request, *args, **kwargs):
 | 
					    def dispatch(self, request, *args, **kwargs):
 | 
				
			||||||
@ -87,21 +89,17 @@ class MemberSignIn(View):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def post(self, request):
 | 
					    def post(self, request):
 | 
				
			||||||
        member = get_object_or_404(Member, id=request.POST.get('id'))
 | 
					        member = get_object_or_404(Member, id=request.POST.get('id'))
 | 
				
			||||||
        Visit.objects.create(member=member, purpose=request.POST.get('purpose'))
 | 
					        visit = signin_member(member, request.POST.get('purpose'))
 | 
				
			||||||
        data = json.dumps(dict(results=dict(id=member.id)))
 | 
					        data = json.dumps(dict(results=dict(id=member.id, created_at=visit.created_at.isoformat())))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return JsonResponse(data=data, safe=False, status=201)
 | 
					        return JsonResponse(data=data, safe=False, status=201)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get(self, request):
 | 
					    def get(self, request):
 | 
				
			||||||
        start = timezone.now()
 | 
					        visits = get_signed_in_members().prefetch_related()
 | 
				
			||||||
        end = start + timedelta(hours=4)
 | 
					 | 
				
			||||||
        visits = Visit.objects.filter(created_at__lte=end,
 | 
					 | 
				
			||||||
                                      created_at__gte=start).prefetch_related()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        serializer = VisitSerializer(visits, many=True)
 | 
					        serializer = VisitSerializer(visits, many=True)
 | 
				
			||||||
        json = JSONRenderer().render(serializer.data)
 | 
					        results_json = JSONRenderer().render(serializer.data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return JsonResponse(data=json.decode(), safe=False, status=200)
 | 
					        return JsonResponse(data=results_json.decode(), safe=False, status=200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@method_decorator(login_required, name='dispatch')
 | 
					@method_decorator(login_required, name='dispatch')
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user