Sith/core/views/user.py

572 lines
22 KiB
Python
Raw Normal View History

# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
2015-11-24 15:09:46 +00:00
# This file contains all the views that concern the user model
from django.shortcuts import render, redirect, get_object_or_404
2015-11-25 16:03:18 +00:00
from django.contrib.auth import logout as auth_logout, views
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ValidationError
2016-08-13 15:15:45 +00:00
from django.http import Http404
2015-11-26 15:32:56 +00:00
from django.views.generic.edit import UpdateView
2016-09-29 13:04:43 +00:00
from django.views.generic import ListView, DetailView, TemplateView, DeleteView
from django.forms.models import modelform_factory
from django.forms import CheckboxSelectMultiple
2016-08-13 15:15:45 +00:00
from django.template.response import TemplateResponse
2016-08-05 07:52:19 +00:00
from django.conf import settings
2016-09-06 19:47:15 +00:00
from django.views.generic.dates import YearMixin, MonthMixin
2016-09-06 16:43:39 +00:00
from django.utils import timezone
from datetime import timedelta, datetime, date
2015-11-24 15:09:46 +00:00
import logging
2017-01-11 11:18:42 +00:00
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
2016-09-19 18:29:43 +00:00
from core.views.forms import RegisteringForm, UserPropForm, UserProfileForm, LoginForm, UserGodfathersForm
2017-01-11 00:34:16 +00:00
from core.models import User, SithFile, Preferences
2016-10-05 11:08:51 +00:00
from club.models import Club
2016-09-15 09:07:03 +00:00
from subscription.models import Subscription
2017-05-10 20:17:05 +00:00
from trombi.views import UserTrombiForm
2015-11-24 15:09:46 +00:00
2015-11-25 16:03:18 +00:00
def login(request):
"""
The login view
Needs to be improve with correct handling of form exceptions
"""
2016-08-31 00:43:49 +00:00
return views.login(request, template_name="core/login.jinja", authentication_form=LoginForm)
2015-11-25 16:03:18 +00:00
def logout(request):
"""
The logout view
"""
return views.logout_then_login(request)
def password_change(request):
"""
Allows a user to change its password
"""
2016-02-01 16:35:55 +00:00
return views.password_change(request, template_name="core/password_change.jinja", post_change_redirect=reverse("core:password_change_done"))
2015-11-25 16:03:18 +00:00
def password_change_done(request):
"""
Allows a user to change its password
"""
2016-02-01 16:35:55 +00:00
return views.password_change_done(request, template_name="core/password_change_done.jinja")
2015-11-25 16:03:18 +00:00
2016-08-13 15:15:45 +00:00
def password_root_change(request, user_id):
"""
Allows a root user to change someone's password
"""
2016-08-14 02:35:08 +00:00
if not request.user.is_root:
2016-08-13 15:15:45 +00:00
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})
2015-11-26 09:57:26 +00:00
def password_reset(request):
2015-11-26 10:27:52 +00:00
"""
Allows someone to enter an email adresse for resetting password
"""
2015-11-26 09:57:26 +00:00
return views.password_reset(request,
2016-02-01 16:35:55 +00:00
template_name="core/password_reset.jinja",
email_template_name="core/password_reset_email.jinja",
2015-11-26 10:27:52 +00:00
post_reset_redirect="core:password_reset_done",
2015-11-26 09:57:26 +00:00
)
2015-11-25 16:03:18 +00:00
def password_reset_done(request):
2015-11-26 10:27:52 +00:00
"""
Confirm that the reset email has been sent
"""
2016-02-01 16:35:55 +00:00
return views.password_reset_done(request, template_name="core/password_reset_done.jinja")
2015-11-25 16:03:18 +00:00
2015-11-26 09:57:26 +00:00
def password_reset_confirm(request, uidb64=None, token=None):
2015-11-26 10:27:52 +00:00
"""
Provide a reset password formular
"""
2015-11-26 09:57:26 +00:00
return views.password_reset_confirm(request, uidb64=uidb64, token=token,
post_reset_redirect="core:password_reset_complete",
2016-02-01 16:35:55 +00:00
template_name="core/password_reset_confirm.jinja",
2015-11-26 09:57:26 +00:00
)
def password_reset_complete(request):
2015-11-26 10:27:52 +00:00
"""
Confirm the password has sucessfully been reset
"""
2015-11-26 09:57:26 +00:00
return views.password_reset_complete(request,
2016-02-01 16:35:55 +00:00
template_name="core/password_reset_complete.jinja",
2015-11-26 09:57:26 +00:00
)
2015-11-25 16:03:18 +00:00
2015-11-24 15:09:46 +00:00
def register(request):
2016-07-19 17:03:16 +00:00
context = {}
2015-11-24 15:09:46 +00:00
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()
2016-02-01 16:35:55 +00:00
return render(request, "core/register.jinja", context)
2015-11-24 15:09:46 +00:00
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"),
})
2016-09-19 18:29:43 +00:00
tab_list.append({
'url': reverse('core:user_godfathers', kwargs={'user_id': self.object.id}),
2016-09-19 18:35:43 +00:00
'slug': 'godfathers',
2016-09-19 18:29:43 +00:00
'name': _("Godfathers"),
})
2016-11-20 09:40:49 +00:00
tab_list.append({
'url': reverse('core:user_pictures', kwargs={'user_id': self.object.id}),
'slug': 'pictures',
'name': _("Pictures"),
})
if self.request.user == self.object:
tab_list.append({
'url': reverse('core:user_tools'),
'slug': 'tools',
'name': _("Tools"),
})
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"),
})
2017-01-11 00:34:16 +00:00
tab_list.append({
'url': reverse('core:user_prefs', kwargs={'user_id': self.object.id}),
'slug': 'prefs',
'name': _("Preferences"),
})
2016-10-05 11:08:51 +00:00
if self.request.user.can_view(self.object):
tab_list.append({
'url': reverse('core:user_clubs', kwargs={'user_id': self.object.id}),
'slug': 'clubs',
'name': _("Clubs"),
})
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
2016-12-10 00:29:56 +00:00
or self.request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
or self.request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
or self.request.user.is_root)):
tab_list.append({
'url': reverse('core:user_stats', kwargs={'user_id': self.object.id}),
'slug': 'stats',
'name': _("Stats"),
})
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):
2015-11-24 15:09:46 +00:00
"""
Display a user's profile
"""
2015-11-26 15:32:56 +00:00
model = User
pk_url_kwarg = "user_id"
context_object_name = "profile"
2016-02-01 16:35:55 +00:00
template_name = "core/user_detail.jinja"
current_tab = 'infos'
2015-11-26 15:32:56 +00:00
2016-09-29 13:04:43 +00:00
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)
2016-11-20 09:40:49 +00:00
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
"""
Display a user's pictures
"""
model = User
pk_url_kwarg = "user_id"
context_object_name = "profile"
template_name = "core/user_pictures.jinja"
current_tab = 'pictures'
2016-09-29 13:04:43 +00:00
2016-09-19 18:29:43 +00:00
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
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
"""
Display a user's stats
"""
model = User
pk_url_kwarg = "user_id"
context_object_name = "profile"
template_name = "core/user_stats.jinja"
current_tab = 'stats'
2017-03-27 23:03:31 +00:00
def dispatch(self, request, *arg, **kwargs):
profile = self.get_object()
2017-03-28 12:39:52 +00:00
if not hasattr(profile, "customer"):
raise Http404
if not (profile == request.user
or request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
or request.user.is_root):
2017-03-27 23:03:31 +00:00
raise PermissionDenied
return super(UserStatsView, self).dispatch(request, *arg, **kwargs)
def get_context_data(self, **kwargs):
kwargs = super(UserStatsView, self).get_context_data(**kwargs)
2016-09-22 09:09:15 +00:00
from counter.models import Counter, Product, Selling
from django.db.models import Sum
2016-08-19 00:53:44 +00:00
foyer = Counter.objects.filter(name="Foyer").first()
mde = Counter.objects.filter(name="MDE").first()
gommette = Counter.objects.filter(name="La Gommette").first()
2016-09-15 09:07:03 +00:00
semester_start=Subscription.compute_start(d=date.today(), duration=3)
2016-09-21 10:19:34 +00:00
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())
2016-09-15 09:07:03 +00:00
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)])
2016-09-22 09:09:15 +00:00
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]
return kwargs
2016-08-14 17:28:14 +00:00
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"
2016-10-27 16:39:40 +00:00
class UserListView(ListView, CanEditPropMixin):
2015-11-26 15:32:56 +00:00
"""
Displays the user list
"""
model = User
2016-02-01 16:35:55 +00:00
template_name = "core/user_list.jinja"
2015-11-26 15:32:56 +00:00
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)
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
2015-11-26 15:32:56 +00:00
"""
Edit a user's profile
"""
model = User
pk_url_kwarg = "user_id"
2016-02-01 16:35:55 +00:00
template_name = "core/user_edit.jinja"
form_class = UserProfileForm
current_tab = "edit"
edit_once = ['profile_pict', 'date_of_birth', 'first_name', 'last_name']
board_only = []
2016-10-15 00:33:38 +00:00
def remove_restricted_fields(self, request):
"""
Removes edit_once and board_only fields
"""
for i in self.edit_once:
if getattr(self.form.instance, i) and not (request.user.is_board_member or request.user.is_root):
self.form.fields.pop(i, None)
2016-10-15 00:33:38 +00:00
for i in self.board_only:
if not (request.user.is_board_member or request.user.is_root):
self.form.fields.pop(i, None)
def get(self, request, *args, **kwargs):
self.object = self.get_object()
self.form = self.get_form()
2016-10-15 00:33:38 +00:00
self.remove_restricted_fields(request)
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()
2016-10-15 00:33:38 +00:00
self.remove_restricted_fields(request)
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
2015-11-26 15:32:56 +00:00
2016-10-05 11:08:51 +00:00
class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
"""
Display the user's club(s)
"""
model = User
context_object_name = "profile"
pk_url_kwarg = "user_id"
template_name = "core/user_clubs.jinja"
current_tab = "clubs"
2017-01-11 00:34:16 +00:00
class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
"""
Edit a user's preferences
"""
model = User
2017-01-11 00:34:16 +00:00
pk_url_kwarg = "user_id"
2017-05-09 21:42:01 +00:00
template_name = "core/user_preferences.jinja"
form_class = modelform_factory(Preferences, fields=['receive_weekmail'])
2017-01-11 00:34:16 +00:00
context_object_name = "profile"
current_tab = "prefs"
def get_object(self, queryset=None):
user = get_object_or_404(User, pk=self.kwargs['user_id'])
return user
def get_form_kwargs(self):
kwargs = super(UserPreferencesView, self).get_form_kwargs()
2017-01-11 00:34:16 +00:00
try:
pref = self.object.preferences
2017-01-11 00:34:16 +00:00
except:
pref = Preferences(user=self.object)
2017-01-11 00:34:16 +00:00
pref.save()
kwargs.update({'instance': pref})
return kwargs
2017-01-11 00:34:16 +00:00
2017-05-09 21:42:01 +00:00
def get_context_data(self, **kwargs):
kwargs = super(UserPreferencesView, self).get_context_data(**kwargs)
2017-05-10 20:17:05 +00:00
if not hasattr(self.object, 'trombi_user'):
kwargs['trombi_form'] = UserTrombiForm()
2017-05-09 21:42:01 +00:00
return kwargs
class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
2015-11-26 15:32:56 +00:00
"""
Edit a user's groups
"""
model = User
pk_url_kwarg = "user_id"
template_name = "core/user_group.jinja"
form_class = modelform_factory(User, fields=['groups'],
widgets={'groups':CheckboxSelectMultiple})
2016-02-01 16:35:55 +00:00
context_object_name = "profile"
current_tab = "groups"
2015-11-26 15:32:56 +00:00
2017-01-11 11:18:42 +00:00
class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
"""
Displays the logged user's tools
"""
2016-02-01 16:35:55 +00:00
template_name = "core/user_tools.jinja"
current_tab = "tools"
2016-08-05 07:52:19 +00:00
def get_context_data(self, **kwargs):
self.object = self.request.user
from launderette.models import Launderette
kwargs = super(UserToolsView, self).get_context_data(**kwargs)
kwargs['launderettes'] = Launderette.objects.all()
2016-08-31 02:04:28 +00:00
kwargs['profile'] = self.request.user
kwargs['object'] = self.request.user
return kwargs
2016-09-06 19:47:15 +00:00
class UserAccountBase(UserTabsMixin, DetailView):
2016-08-05 07:52:19 +00:00
"""
2016-09-06 19:47:15 +00:00
Base class for UserAccount
2016-08-05 07:52:19 +00:00
"""
model = User
pk_url_kwarg = "user_id"
current_tab = "account"
2016-08-05 07:52:19 +00:00
def dispatch(self, request, *arg, **kwargs): # Manually validates the rights
2016-09-06 19:47:15 +00:00
res = super(UserAccountBase, self).dispatch(request, *arg, **kwargs)
2016-08-05 07:52:19 +00:00
if (self.object == request.user
2016-12-10 00:29:56 +00:00
or request.user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
or request.user.is_in_group(settings.SITH_BAR_MANAGER['unix_name']+settings.SITH_BOARD_SUFFIX)
2016-08-14 02:35:08 +00:00
or request.user.is_root):
2016-08-05 07:52:19 +00:00
return res
raise PermissionDenied
2016-09-06 19:47:15 +00:00
class UserAccountView(UserAccountBase):
"""
Display a user's account
"""
template_name = "core/user_account.jinja"
def expense_by_month(self, obj, calc):
2016-09-06 16:43:39 +00:00
stats = []
for year in obj.datetimes('date', 'year', order='DESC'):
2016-09-06 16:43:39 +00:00
stats.append([])
2016-09-08 01:21:39 +00:00
i = 0
for month in obj.filter(date__year=year.year).datetimes(
'date', 'month', order='DESC'):
2016-09-06 19:47:15 +00:00
q = obj.filter(
2016-09-08 01:21:39 +00:00
date__year=month.year,
date__month=month.month
2016-09-06 16:43:39 +00:00
)
2016-10-18 23:57:54 +00:00
stats[i].append({
'sum':sum([calc(p) for p in q]),
'date':month
})
2016-09-08 01:21:39 +00:00
i += 1
2016-09-06 16:43:39 +00:00
return stats
2016-09-06 19:47:15 +00:00
def invoices_calc(self, query):
t = 0
for it in query.items.all():
t += it.quantity * it.product_unit_price
return t
2016-08-05 07:52:19 +00:00
def get_context_data(self, **kwargs):
kwargs = super(UserAccountView, self).get_context_data(**kwargs)
kwargs['profile'] = self.object
try:
kwargs['customer'] = self.object.customer
2016-09-06 19:47:15 +00:00
kwargs['buyings_month'] = self.expense_by_month(
self.object.customer.buyings,
2016-09-08 02:15:34 +00:00
(lambda q: q.unit_price * q.quantity)
2016-09-06 19:47:15 +00:00
)
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,
2016-09-08 02:15:34 +00:00
(lambda q: q.amount)
2016-09-06 19:47:15 +00:00
)
2016-10-03 17:30:05 +00:00
kwargs['etickets'] = self.object.customer.buyings.exclude(product__eticket=None).all()
except Exception as e:
print(repr(e))
2016-08-05 07:52:19 +00:00
return kwargs
2016-09-06 19:47:15 +00:00
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
kwargs['year'] = self.get_year()
kwargs['month'] = self.get_month()
2016-09-06 19:47:15 +00:00
try:
kwargs['customer'] = self.object.customer
except:
pass
kwargs['tab'] = "account"
return kwargs