user.py 17.4 KB
Newer Older
Skia's avatar
Skia committed
1
2
# This file contains all the views that concern the user model
from django.shortcuts import render, redirect, get_object_or_404
Skia's avatar
Skia committed
3
from django.contrib.auth import logout as auth_logout, views
4
from django.utils.translation import ugettext as _
5
from django.core.urlresolvers import reverse
6
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ValidationError
Skia's avatar
Skia committed
7
from django.http import Http404
Skia's avatar
Skia committed
8
from django.views.generic.edit import UpdateView
Sli's avatar
Sli committed
9
from django.views.generic import ListView, DetailView, TemplateView, DeleteView
10
11
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple
Skia's avatar
Skia committed
12
from django.template.response import TemplateResponse
Skia's avatar
Skia committed
13
from django.conf import settings
Sli's avatar
Sli committed
14
from django.views.generic.dates import YearMixin, MonthMixin
15

Sli's avatar
Sli committed
16
17
from django.utils import timezone
from datetime import timedelta, datetime, date
Skia's avatar
Skia committed
18
19
import logging

20
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin
Skia's avatar
Skia committed
21
from core.views.forms import RegisteringForm, UserPropForm, UserProfileForm, LoginForm, UserGodfathersForm
22
from core.models import User, SithFile
Skia's avatar
Skia committed
23
from subscription.models import Subscription
Skia's avatar
Skia committed
24

Skia's avatar
Skia committed
25
26
27
28
29
30
def login(request):
    """
    The login view

    Needs to be improve with correct handling of form exceptions
    """
Skia's avatar
Skia committed
31
    return views.login(request, template_name="core/login.jinja", authentication_form=LoginForm)
Skia's avatar
Skia committed
32
33
34
35
36
37
38
39
40
41
42

def logout(request):
    """
    The logout view
    """
    return views.logout_then_login(request)

def password_change(request):
    """
    Allows a user to change its password
    """
Skia's avatar
Skia committed
43
    return views.password_change(request, template_name="core/password_change.jinja", post_change_redirect=reverse("core:password_change_done"))
Skia's avatar
Skia committed
44
45
46
47
48

def password_change_done(request):
    """
    Allows a user to change its password
    """
Skia's avatar
Skia committed
49
    return views.password_change_done(request, template_name="core/password_change_done.jinja")
Skia's avatar
Skia committed
50

Skia's avatar
Skia committed
51
52
53
54
def password_root_change(request, user_id):
    """
    Allows a root user to change someone's password
    """
Skia's avatar
Skia committed
55
    if not request.user.is_root:
Skia's avatar
Skia committed
56
57
58
59
60
61
62
63
64
65
66
67
68
        raise PermissionDenied
    user = User.objects.filter(id=user_id).first()
    if not user:
        raise Http404("User not found")
    if request.method == "POST":
        form = views.SetPasswordForm(user=user, data=request.POST)
        if form.is_valid():
            form.save()
            return redirect("core:password_change_done")
    else:
        form = views.SetPasswordForm(user=user)
    return TemplateResponse(request, "core/password_change.jinja", {'form': form, 'target': user})

Skia's avatar
Skia committed
69
def password_reset(request):
Skia's avatar
Skia committed
70
71
72
    """
    Allows someone to enter an email adresse for resetting password
    """
Skia's avatar
Skia committed
73
    return views.password_reset(request,
Skia's avatar
Skia committed
74
75
                                template_name="core/password_reset.jinja",
                                email_template_name="core/password_reset_email.jinja",
Skia's avatar
Skia committed
76
                                post_reset_redirect="core:password_reset_done",
Skia's avatar
Skia committed
77
                               )
Skia's avatar
Skia committed
78
79

def password_reset_done(request):
Skia's avatar
Skia committed
80
81
82
    """
    Confirm that the reset email has been sent
    """
Skia's avatar
Skia committed
83
    return views.password_reset_done(request, template_name="core/password_reset_done.jinja")
Skia's avatar
Skia committed
84

Skia's avatar
Skia committed
85
def password_reset_confirm(request, uidb64=None, token=None):
Skia's avatar
Skia committed
86
87
88
    """
    Provide a reset password formular
    """
Skia's avatar
Skia committed
89
90
    return views.password_reset_confirm(request, uidb64=uidb64, token=token,
                                        post_reset_redirect="core:password_reset_complete",
Skia's avatar
Skia committed
91
                                        template_name="core/password_reset_confirm.jinja",
Skia's avatar
Skia committed
92
93
94
                                       )

def password_reset_complete(request):
Skia's avatar
Skia committed
95
96
97
    """
    Confirm the password has sucessfully been reset
    """
Skia's avatar
Skia committed
98
    return views.password_reset_complete(request,
Skia's avatar
Skia committed
99
                                         template_name="core/password_reset_complete.jinja",
Skia's avatar
Skia committed
100
                                        )
Skia's avatar
Skia committed
101

Skia's avatar
Skia committed
102
def register(request):
Skia's avatar
Skia committed
103
    context = {}
Skia's avatar
Skia committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    if request.method == 'POST':
        form = RegisteringForm(request.POST)
        if form.is_valid():
            logging.debug("Registering "+form.cleaned_data['first_name']+form.cleaned_data['last_name'])
            u = form.save()
            context['user_registered'] = u
            context['tests'] = 'TEST_REGISTER_USER_FORM_OK'
            form = RegisteringForm()
        else:
            context['error'] = 'Erreur'
            context['tests'] = 'TEST_REGISTER_USER_FORM_FAIL'
    else:
        form = RegisteringForm()
    context['form'] = form.as_p()
Skia's avatar
Skia committed
118
    return render(request, "core/register.jinja", context)
Skia's avatar
Skia committed
119

120
121
122
123
124
125
126
127
128
129
130
class UserTabsMixin(TabedViewMixin):
    def get_tabs_title(self):
        return self.object.get_display_name()

    def get_list_of_tabs(self):
        tab_list = []
        tab_list.append({
                    'url': reverse('core:user_profile', kwargs={'user_id': self.object.id}),
                    'slug': 'infos',
                    'name': _("Infos"),
                    })
Skia's avatar
Skia committed
131
132
        tab_list.append({
                    'url': reverse('core:user_godfathers', kwargs={'user_id': self.object.id}),
Skia's avatar
Skia committed
133
                    'slug': 'godfathers',
Skia's avatar
Skia committed
134
135
                    'name': _("Godfathers"),
                    })
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
        if self.request.user == self.object:
            tab_list.append({
                        'url': reverse('core:user_tools'),
                        'slug': 'tools',
                        'name': _("Tools"),
                        })
        tab_list.append({
                    'url': reverse('core:user_stats', kwargs={'user_id': self.object.id}),
                    'slug': 'stats',
                    'name': _("Stats"),
                    })
        if self.request.user.can_edit(self.object):
            tab_list.append({
                        'url': reverse('core:user_edit', kwargs={'user_id': self.object.id}),
                        'slug': 'edit',
                        'name': _("Edit"),
                        })
        if self.request.user.is_owner(self.object):
            tab_list.append({
                        'url': reverse('core:user_groups', kwargs={'user_id': self.object.id}),
                        'slug': 'groups',
                        'name': _("Groups"),
                        })
        try:
            if (self.object.customer and (self.object == self.request.user
                or self.request.user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name'])
162
                or self.request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
163
164
165
166
167
168
169
170
171
172
                or self.request.user.is_root)):
                tab_list.append({
                            'url': reverse('core:user_account', kwargs={'user_id': self.object.id}),
                            'slug': 'account',
                            'name': _("Account")+" (%s €)" % self.object.customer.amount,
                            })
        except: pass
        return tab_list

class UserView(UserTabsMixin, CanViewMixin, DetailView):
Skia's avatar
Skia committed
173
174
175
    """
    Display a user's profile
    """
Skia's avatar
Skia committed
176
177
178
    model = User
    pk_url_kwarg = "user_id"
    context_object_name = "profile"
Skia's avatar
Skia committed
179
    template_name = "core/user_detail.jinja"
180
    current_tab = 'infos'
Skia's avatar
Skia committed
181

Sli's avatar
Sli committed
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
    user = User.objects.get(id=user_id)
    if ((user == request.user) or
         request.user.is_root or
         request.user.is_board_member):
        ud = get_object_or_404(User, id=godfather_id)
        if is_father == "True":
            user.godfathers.remove(ud)
        else:
            user.godchildren.remove(ud)
    else:
        raise PermissionDenied
    return redirect('core:user_godfathers', user_id=user_id)


Skia's avatar
Skia committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
    """
    Display a user's godfathers
    """
    model = User
    pk_url_kwarg = "user_id"
    context_object_name = "profile"
    template_name = "core/user_godfathers.jinja"
    current_tab = 'godfathers'

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.form = UserGodfathersForm(request.POST)
        if self.form.is_valid() and self.form.cleaned_data['user'] != self.object:
            if self.form.cleaned_data['type'] == 'godfather':
                self.object.godfathers.add(self.form.cleaned_data['user'])
                self.object.save()
            else:
                self.object.godchildren.add(self.form.cleaned_data['user'])
                self.object.save()
            self.form = UserGodfathersForm()
        return super(UserGodfathersView, self).get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        kwargs = super(UserGodfathersView, self).get_context_data(**kwargs)
        try:
            kwargs['form'] = self.form
        except:
            kwargs['form'] = UserGodfathersForm()
        return kwargs

229
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
230
231
232
233
234
235
236
    """
    Display a user's stats
    """
    model = User
    pk_url_kwarg = "user_id"
    context_object_name = "profile"
    template_name = "core/user_stats.jinja"
237
    current_tab = 'stats'
238
239
240

    def get_context_data(self, **kwargs):
        kwargs = super(UserStatsView, self).get_context_data(**kwargs)
Skia's avatar
Skia committed
241
242
        from counter.models import Counter, Product, Selling
        from django.db.models import Sum
Skia's avatar
Skia committed
243
244
245
        foyer = Counter.objects.filter(name="Foyer").first()
        mde = Counter.objects.filter(name="MDE").first()
        gommette = Counter.objects.filter(name="La Gommette").first()
Skia's avatar
Skia committed
246
        semester_start=Subscription.compute_start(d=date.today(), duration=3)
Skia's avatar
Skia committed
247
248
249
250
        kwargs['total_perm_time'] = sum([p.end-p.start for p in self.object.permanencies.exclude(end=None)], timedelta())
        kwargs['total_foyer_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=foyer).exclude(end=None)], timedelta())
        kwargs['total_mde_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=mde).exclude(end=None)], timedelta())
        kwargs['total_gommette_time'] = sum([p.end-p.start for p in self.object.permanencies.filter(counter=gommette).exclude(end=None)], timedelta())
Skia's avatar
Skia committed
251
252
253
254
255
256
        kwargs['total_foyer_buyings'] = sum([b.unit_price*b.quantity for b in
            self.object.customer.buyings.filter(counter=foyer, date__gte=semester_start)])
        kwargs['total_mde_buyings'] = sum([b.unit_price*b.quantity for b in self.object.customer.buyings.filter(counter=mde,
            date__gte=semester_start)])
        kwargs['total_gommette_buyings'] = sum([b.unit_price*b.quantity for b in
            self.object.customer.buyings.filter(counter=gommette, date__gte=semester_start)])
Skia's avatar
Skia committed
257
258
        kwargs['top_product'] = self.object.customer.buyings.values('product__name').annotate(
                product_sum=Sum('quantity')).exclude(product_sum=None).order_by('-product_sum').all()[:10]
259
260
        return kwargs

Skia's avatar
Skia committed
261
262
263
264
265
266
267
268
269
class UserMiniView(CanViewMixin, DetailView):
    """
    Display a user's profile
    """
    model = User
    pk_url_kwarg = "user_id"
    context_object_name = "profile"
    template_name = "core/user_mini.jinja"

Skia's avatar
Skia committed
270
271
272
273
274
class UserListView(ListView):
    """
    Displays the user list
    """
    model = User
Skia's avatar
Skia committed
275
    template_name = "core/user_list.jinja"
Skia's avatar
Skia committed
276

277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
class UserUploadProfilePictView(CanEditMixin, DetailView):
    """
    Handle the upload of the profile picture taken with webcam in navigator
    """
    model = User
    pk_url_kwarg = "user_id"
    template_name = "core/user_edit.jinja"

    def post(self, request, *args, **kwargs):
        from core.utils import resize_image
        from io import BytesIO
        from PIL import Image
        self.object = self.get_object()
        if self.object.profile_pict:
            raise ValidationError(_("User already has a profile picture"))
        f = request.FILES['new_profile_pict']
        parent = SithFile.objects.filter(parent=None, name="profiles").first()
        name = str(self.object.id) + "_profile.jpg" # Webcamejs uploads JPGs
        im = Image.open(BytesIO(f.read()))
        new_file = SithFile(parent=parent, name=name,
                file=resize_image(im, 400, f.content_type.split('/')[-1]),
                owner=self.object, is_folder=False, mime_type=f.content_type, size=f._size)
        new_file.file.name = name
        new_file.save()
        self.object.profile_pict = new_file
        self.object.save()
        return redirect("core:user_edit", user_id=self.object.id)

305
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
Skia's avatar
Skia committed
306
307
308
309
310
    """
    Edit a user's profile
    """
    model = User
    pk_url_kwarg = "user_id"
Skia's avatar
Skia committed
311
    template_name = "core/user_edit.jinja"
312
    form_class = UserProfileForm
313
    current_tab = "edit"
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.form = self.get_form()
        if self.form.instance.profile_pict and not request.user.is_in_group(settings.SITH_MAIN_BOARD_GROUP):
            self.form.fields.pop('profile_pict', None)
        return self.render_to_response(self.get_context_data(form=self.form))

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.form = self.get_form()
        if self.form.instance.profile_pict and not request.user.is_in_group(settings.SITH_MAIN_BOARD_GROUP):
            self.form.fields.pop('profile_pict', None)
        files = request.FILES.items()
        self.form.process(files)
        if request.user.is_authenticated() and request.user.can_edit(self.object) and self.form.is_valid():
            return super(UserUpdateProfileView, self).form_valid(self.form)
        return self.form_invalid(self.form)

    def get_context_data(self, **kwargs):
        kwargs = super(UserUpdateProfileView, self).get_context_data(**kwargs)
        kwargs['profile'] = self.form.instance
        kwargs['form'] = self.form
        return kwargs
Skia's avatar
Skia committed
338

339
class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
Skia's avatar
Skia committed
340
341
342
343
344
    """
    Edit a user's groups
    """
    model = User
    pk_url_kwarg = "user_id"
345
346
347
    template_name = "core/user_group.jinja"
    form_class = modelform_factory(User, fields=['groups'],
            widgets={'groups':CheckboxSelectMultiple})
Skia's avatar
Skia committed
348
    context_object_name = "profile"
349
    current_tab = "groups"
Skia's avatar
Skia committed
350

351
class UserToolsView(UserTabsMixin, TemplateView):
352
353
354
    """
    Displays the logged user's tools
    """
Skia's avatar
Skia committed
355
    template_name = "core/user_tools.jinja"
356
    current_tab = "tools"
Skia's avatar
Skia committed
357

358
    def get_context_data(self, **kwargs):
359
        self.object = self.request.user
360
361
362
        from launderette.models import Launderette
        kwargs = super(UserToolsView, self).get_context_data(**kwargs)
        kwargs['launderettes'] = Launderette.objects.all()
Skia's avatar
Skia committed
363
        kwargs['profile'] = self.request.user
364
        kwargs['object'] = self.request.user
365
366
        return kwargs

Sli's avatar
Sli committed
367
class UserAccountBase(UserTabsMixin, DetailView):
Skia's avatar
Skia committed
368
    """
Sli's avatar
Sli committed
369
    Base class for UserAccount
Skia's avatar
Skia committed
370
    """
Sli's avatar
Sli committed
371

Skia's avatar
Skia committed
372
373
    model = User
    pk_url_kwarg = "user_id"
374
    current_tab = "account"
Skia's avatar
Skia committed
375
376

    def dispatch(self, request, *arg, **kwargs): # Manually validates the rights
Sli's avatar
Sli committed
377
        res = super(UserAccountBase, self).dispatch(request, *arg, **kwargs)
Skia's avatar
Skia committed
378
379
        if (self.object == request.user
                or request.user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name'])
380
                or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
Skia's avatar
Skia committed
381
                or request.user.is_root):
Skia's avatar
Skia committed
382
383
384
            return res
        raise PermissionDenied

Sli's avatar
Sli committed
385
386
387
388
389
390
391
392
class UserAccountView(UserAccountBase):
    """
    Display a user's account
    """

    template_name = "core/user_account.jinja"

    def expense_by_month(self, obj, calc):
Sli's avatar
Sli committed
393
394
        stats = []

395
        for year in obj.datetimes('date', 'year', order='DESC'):
Sli's avatar
Sli committed
396
            stats.append([])
Sli's avatar
Sli committed
397
398
            i = 0
            for month in obj.filter(date__year=year.year).datetimes(
399
                'date', 'month', order='DESC'):
Sli's avatar
Sli committed
400
                q = obj.filter(
Sli's avatar
Sli committed
401
402
                    date__year=month.year,
                    date__month=month.month
Sli's avatar
Sli committed
403
                )
Sli's avatar
Sli committed
404
405
406
407
408
                stats[i].append((
                    sum([calc(p) for p in q]),
                    month
                ))
            i += 1
Sli's avatar
Sli committed
409
410
411

        return stats

Sli's avatar
Sli committed
412
413
414
415
416
417
    def invoices_calc(self, query):
        t = 0
        for it in query.items.all():
            t += it.quantity * it.product_unit_price
        return t

Skia's avatar
Skia committed
418
419
420
421
422
    def get_context_data(self, **kwargs):
        kwargs = super(UserAccountView, self).get_context_data(**kwargs)
        kwargs['profile'] = self.object
        try:
            kwargs['customer'] = self.object.customer
Sli's avatar
Sli committed
423
424
            kwargs['buyings_month'] = self.expense_by_month(
                self.object.customer.buyings,
Sli's avatar
Sli committed
425
                (lambda q: q.unit_price * q.quantity)
Sli's avatar
Sli committed
426
427
428
429
430
431
432
            )
            kwargs['invoices_month'] = self.expense_by_month(
                self.object.customer.user.invoices,
                self.invoices_calc
            )
            kwargs['refilling_month'] = self.expense_by_month(
                self.object.customer.refillings,
Sli's avatar
Sli committed
433
                (lambda q: q.amount)
Sli's avatar
Sli committed
434
            )
Skia's avatar
Skia committed
435
436
437
438
439
440
        except:
            pass
        # TODO: add list of month where account has activity
        return kwargs


Sli's avatar
Sli committed
441
442
443
444
445
446
447
448
449
450
451

class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
    """
    Display a user's account for month
    """

    template_name = "core/user_account_detail.jinja"

    def get_context_data(self, **kwargs):
        kwargs = super(UserAccountDetailView, self).get_context_data(**kwargs)
        kwargs['profile'] = self.object
452
453
        kwargs['year'] = self.get_year()
        kwargs['month'] = self.get_month()
Sli's avatar
Sli committed
454
455
456
457
458
459
        try:
            kwargs['customer'] = self.object.customer
        except:
            pass
        kwargs['tab'] = "account"
        return kwargs
460