views.py 7.69 KB
Newer Older
1
import csv
2
import json
3
from datetime import date, datetime
4

5
from django.contrib.auth.decorators import login_required, permission_required
6
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
7
from django.db.models import Q
8
from django.http import HttpResponse
9
from django.shortcuts import get_object_or_404, render
10
from django.utils.translation import gettext as _
11
from rest_framework.authtoken.models import Token
12
from rest_framework.authtoken.views import ObtainAuthToken
13
from rest_framework.response import Response
14

15
from members import services
16
from .services import member_achievements
Thom Wiggers's avatar
Thom Wiggers committed
17
from . import models
18
from .forms import ProfileForm
19

20

21
22
23
class ObtainThaliaAuthToken(ObtainAuthToken):

    def post(self, request, *args, **kwargs):
24
25
26
27
28
29
30
31
32
        serializer = self.serializer_class(data={
            'username': request.data.get('username').lower()
            if 'username' in request.data else None,
            'password': request.data.get('password')
        }, context={'request': request})
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        return Response({'token': token.key})
33
34


35
def filter_users(tab, keywords, year_range):
36
37
    memberships_query = Q(until__gt=datetime.now()) | Q(until=None)
    members_query = ~Q(id=None)
38

39
    if tab and tab.isdigit():
40
        members_query &= Q(profile__starting_year=int(tab))
41
    elif tab == 'old':
42
        members_query &= Q(profile__starting_year__lt=year_range[-1])
43
44
    elif tab == 'ex':
        # Filter out all current active memberships
45
        memberships_query &= Q(type='member') | Q(type='honorary')
46
        memberships = models.Membership.objects.filter(memberships_query)
47
        members_query &= ~Q(pk__in=memberships.values('user__pk'))
48
        # Members_query contains users that are not currently (honorary)member
49
    elif tab == 'honor':
50
51
52
        memberships_query = Q(until__gt=datetime.now().date()) | Q(until=None)
        memberships_query &= Q(type='honorary')

53
54
    if keywords:
        for key in keywords:
55
            members_query &= (
56
                (Q(profile__nickname__icontains=key) &
57
                 # Works because relevant options all have `nick` in their key
58
59
60
61
                 Q(profile__display_name_preference__contains='nick')) |
                Q(first_name__icontains=key) |
                Q(last_name__icontains=key) |
                Q(username__icontains=key))
62

63
    if tab == 'ex':
64
65
66
67
68
        memberships_query = Q(type='member') | Q(type='honorary')
        memberships = models.Membership.objects.filter(memberships_query)
        all_memberships = models.Membership.objects.all()
        # Only keep members that were once members, or are legacy users that
        #  do not have any memberships at all
69
70
        members_query &= (Q(pk__in=memberships.values('user__pk')) |
                          ~Q(pk__in=all_memberships.values('user__pk')))
71
72
    else:
        memberships = models.Membership.objects.filter(memberships_query)
73
        members_query &= Q(pk__in=memberships.values('user__pk'))
74
    return (models.Member.objects
75
76
77
                  .filter(members_query)
                  .order_by('-profile__starting_year',
                            'first_name'))
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97


@login_required
def index(request):
    query_filter = '' if request.GET.get(
        'filter') is None else request.GET.get('filter')
    keywords = '' if request.GET.get('keywords') is None else request.GET.get(
        'keywords').split()

    page = request.GET.get('page')
    page = 1 if page is None or not page.isdigit() else int(page)

    start_year = date.today().year - 4
    # If language is English show one year less
    # since the width is smaller than needed for the translations to fit
    if request.LANGUAGE_CODE == 'en':
        start_year += 1
    year_range = list(reversed(range(start_year, date.today().year + 1)))

    members = filter_users(query_filter, keywords, year_range)
98
99
100

    paginator = Paginator(members, 24)

Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
101
102
103
104
105
106
107
108
109
    try:
        members = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        members = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        members = paginator.page(paginator.num_pages)

110
111
112
    page_range = range(1, paginator.num_pages + 1)
    if paginator.num_pages > 7:
        if page > 3:
113
114
115
116
            page_range_end = paginator.num_pages
            if page + 3 <= paginator.num_pages:
                page_range_end = page + 3

Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
117
            page_range = range(page - 2, page_range_end)
118
119
120
121
            while page_range.stop - page_range.start < 5:
                page_range = range(page_range.start - 1, page_range.stop)
        else:
            page_range = range(1, 6)
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
122

Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
123
124
125
    return render(request, 'members/index.html',
                  {'members': members, 'filter': query_filter,
                   'year_range': year_range, 'page_range': page_range,
126
                   'keywords': keywords})
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
127
128


129
@login_required
130
131
def profile(request, pk=None):
    if pk:
132
        member = get_object_or_404(models.Member, pk=int(pk))
133
    else:
134
        member = request.member
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
135

136
    # Group the memberships under the committees for easier template rendering
137
    achievements = member_achievements(member)
138
139

    membership = member.current_membership
140
    membership_type = _("Unknown membership history")
141
142
    if membership:
        membership_type = membership.get_type_display()
143
144
145
146
147
148
149
    elif member.has_been_honorary_member():
        membership_type = _("Former honorary member")
    elif member.has_been_member():
        membership_type = _("Former member")
    elif member.latest_membership:
        membership_type = _("Former supporter")

150
    return render(request, 'members/profile.html',
151
152
153
154
155
                  {
                      'achievements': achievements,
                      'member': member,
                      'membership_type': membership_type,
                   })
156

157

158
159
160
161
@login_required
def account(request):
    return render(request, 'members/account.html')

162

163
164
@login_required
def edit_profile(request):
165
    profile = get_object_or_404(models.Profile, user=request.user)
166
    saved = False
167
168

    if request.POST:
169
        form = ProfileForm(request.POST, request.FILES, instance=profile)
170
        if form.is_valid():
171
            saved = True
172
173
            form.save()
    else:
174
        form = ProfileForm(instance=profile)
175
176

    return render(request, 'members/edit_profile.html',
177
                  {'form': form, 'saved': saved})
178

179

180
181
182
183
184
@permission_required('auth.change_user')
def iban_export(request):
    header_fields = ['name', 'username', 'iban']
    rows = []

185
    members = models.Member.current_members.filter(
186
            profile__direct_debit_authorized=True)
187
188

    for member in members:
189
        if member.current_membership.type != 'honorary':
190
191
            rows.append({
                'name': member.get_full_name(),
192
193
                'username': member.username,
                'iban': member.profile.bank_account
194
            })
195
196
197
198
199
200
201
202
203
204
205
206
207

    response = HttpResponse(content_type='text/csv')
    writer = csv.DictWriter(response, header_fields)
    writer.writeheader()

    for row in rows:
        writer.writerow(row)

    response['Content-Disposition'] = (
        'attachment; filename="iban-export.csv"')
    return response


208
@login_required
209
def statistics(request):
210
    member_types = ("member", "supporter", "honorary")
211
212

    # The numbers
213
    total = models.Member.current_members.count()
214
215

    context = {
Thom Wiggers's avatar
Thom Wiggers committed
216
        "total_members": total,
217
        "total_stats_year": json.dumps(services.gen_stats_year(member_types)),
218
        "total_stats_member_type": json.dumps(
219
            services.gen_stats_member_type(member_types)),
220
221
222
    }

    return render(request, 'members/statistics.html', context)