Unverified Commit cee45c7c authored by Luuk Scholten's avatar Luuk Scholten Committed by Thom Wiggers

Add api for getting birthdays of users

parent 841970ff
from django.urls import reverse
from events.api.serializers import CalenderJSSerializer
from members.models import Member
class MemberBirthdaySerializer(CalenderJSSerializer):
class Meta(CalenderJSSerializer.Meta):
model = Member
def _start(self, instance):
return instance.birthday
def _end(self, instance):
pass
def _all_day(self, instance):
return True
def _is_birthday(self, instance):
return True
def _url(self, instance):
return reverse('#')
def _title(self, instance):
return instance.display_name()
def _description(self, instance):
membership = instance.current_membership
if membership and membership.type == 'honorary':
return instance.membership.get_type_display()
return ''
def _background_color(self, instance):
membership = instance.current_membership
if membership and membership.type == 'honorary':
return '#E62272'
return 'black'
def _text_color(self, instance):
return 'white'
from rest_framework import routers
from members.api import viewsets
router = routers.SimpleRouter()
router.register(r'members', viewsets.MemberViewset)
urlpatterns = router.urls
from django.utils import timezone
from rest_framework import viewsets
from rest_framework.decorators import list_route
from datetime import datetime
import copy
from rest_framework.exceptions import ParseError
from rest_framework.response import Response
from members.api.serializers import MemberBirthdaySerializer
from members.models import Member
class MemberViewset(viewsets.ViewSet):
queryset = Member.objects.all()
def _get_birthdays(self, member, start, end):
birthdays = []
start_year = max(start.year, member.birthday.year)
for year in range(start_year, end.year + 1):
bday = copy.deepcopy(member)
bday.birthday = bday.birthday.replace(year=year)
if start.date() <= bday.birthday <= end.date():
birthdays.append(bday)
return birthdays
@list_route()
def birthdays(self, request):
try:
start = timezone.make_aware(
datetime.strptime(request.query_params['start'], '%Y-%m-%d')
)
end = timezone.make_aware(
datetime.strptime(request.query_params['end'], '%Y-%m-%d')
)
except:
raise ParseError(detail='start or end query parameters invalid')
queryset = (
Member
.active_members
.with_birthdays_in_range(start, end)
.filter(show_birthday=True)
)
queryset.prefetch_related('membership_get')
all_birthdays = [
self._get_birthdays(m, start, end)
for m in queryset.all()
]
birthdays = [x for sublist in all_birthdays for x in sublist]
serializer = MemberBirthdaySerializer(birthdays, many=True)
return Response(serializer.data)
......@@ -130,5 +130,15 @@
"direct_debit_authorized": false,
"bank_account": ""
}
},
{
"model": "members.membership",
"pk": 1,
"fields": {
"type": "member",
"user": 1,
"since": "1980-01-01",
"until": null
}
}
]
......@@ -4,6 +4,9 @@ from django.db.models import Q
from django.core import validators
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from datetime import timedelta
import operator
from functools import reduce
from localflavor.generic.countries.sepa import IBAN_SEPA_COUNTRIES
from localflavor.generic.models import IBANField
......@@ -19,6 +22,25 @@ class ActiveMemberManager(models.Manager):
.filter(Q(user__membership__until__isnull=True) |
Q(user__membership__until__gt=timezone.now().date())))
def with_birthdays_in_range(self, from_date, to_date):
queryset = self.get_queryset().filter(birthday__lte=to_date)
if (to_date - from_date).days >= 366:
# 366 is important to also account for leap years
# Everyone that's born before to_date has a birthday
return queryset
delta = to_date - from_date
dates = [from_date + timedelta(days=i) for i in range(delta.days + 1)]
monthdays = [
{"birthday__month": d.month, "birthday__day": d.day}
for d in dates
]
# Don't get me started (basically, we are making a giant OR query with
# all days and months that are in the range)
query = reduce(operator.or_, [Q(**d) for d in monthdays])
return queryset.filter(query)
class Member(models.Model):
"""This class describes a member"""
......
# Create your tests here.
from datetime import datetime
from django.test import TestCase
from django.utils import timezone
from members.models import Member
class MemberBirthdayTest(TestCase):
fixtures = ['members.json']
def _make_date(self, date):
return timezone.make_aware(datetime.strptime(date, '%Y-%m-%d'))
def _get_members(self, start, end):
start_date = self._make_date(start)
end_date = self._make_date(end)
return Member.active_members.with_birthdays_in_range(
start_date, end_date
)
def _assert_none(self, start, end):
members = self._get_members(start, end)
self.assertEquals(len(members), 0)
def _assert_thom(self, start, end):
members = self._get_members(start, end)
self.assertEquals(len(members), 1)
self.assertEquals(members[0].get_full_name(), 'Thom Wiggers')
def test_one_year_contains_birthday(self):
self._assert_thom('2016-03-02', '2016-08-08')
def test_one_year_not_contains_birthday(self):
self._assert_none('2016-01-01', '2016-02-01')
def test_span_year_contains_birthday(self):
self._assert_thom('2015-08-09', '2016-08-08')
def test_span_year_not_contains_birthday(self):
self._assert_none('2015-12-25', '2016-03-01')
def test_span_multiple_years_contains_birthday(self):
self._assert_thom('2012-12-31', '2016-01-01')
def test_range_before_person_born(self):
self._assert_none('1985-12-12', '1985-12-13')
def test_person_born_in_range_in_one_year(self):
self._assert_thom('1993-01-01', '1993-04-01')
def test_person_born_in_range_spanning_one_year(self):
self._assert_thom('1992-12-31', '1993-04-01')
def test_person_born_in_range_spanning_multiple_years(self):
self._assert_thom('1992-12-31', '1995-01-01')
......@@ -65,6 +65,7 @@ urlpatterns = [
url(r'^private-thumbnails/(?P<size_fit>\d+x\d+_[01])/(?P<path>.*)', private_thumbnails, name='private-thumbnails'),
url(r'^api/', include([
url(r'^', include('events.api.urls')),
url(r'^', include('members.api.urls')),
])),
# Default login helpers
url(r'^', include('django.contrib.auth.urls')),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment