From aadb04ff286025339d26a5a90f7b58259c8644da Mon Sep 17 00:00:00 2001 From: Peter Palmreuther Date: Wed, 23 Mar 2022 23:32:29 +0100 Subject: [PATCH 1/2] Unsubscribe user from shifts on user account deletion When user requests account deletion and still subscribed to future shifts, this subscription is removed, so slot is free for reuse. Fixes coders4help/volunteer_planner#477. --- accounts/views.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/accounts/views.py b/accounts/views.py index 04169f47..dccd3d69 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -1,15 +1,17 @@ -# coding: utf-8 import random import string +from django.contrib.admin.models import DELETION, LogEntry from django.contrib.auth import logout +from django.contrib.contenttypes.models import ContentType +from django.db import transaction from django.shortcuts import render from django.contrib.auth.decorators import login_required from django.views.generic.edit import UpdateView from django.urls import reverse_lazy from django.contrib.auth import models -from datetime import date, timedelta +from datetime import date, datetime, timedelta from volunteer_planner.utils import LoginRequiredMixin from scheduler.models import ShiftHelper @@ -37,7 +39,23 @@ def random_string(length=30): return u''.join(random.choice(string.ascii_letters) for x in range(length)) +@transaction.atomic +def unsub_user_from_future_shifts(user): + subscribed_shifts = ShiftHelper.objects.filter(user_account=user, shift__starting_time__gt=datetime.now()) + for sh in subscribed_shifts: + LogEntry.objects.log_action( + user_id=user.id, + content_type_id=ContentType.objects.get_for_model(ShiftHelper).id, + object_id=sh.id, + object_repr=f"User '{user}' @ shift '{sh.shift}'", + action_flag=DELETION, + change_message=f"Initially joined: {sh.joined_shift_at.isoformat()}" + ) + sh.delete() + + @login_required() +@transaction.atomic def account_delete_final(request): """ This randomizes/anonymises the user profile. The account is set inactive. @@ -47,12 +65,19 @@ def account_delete_final(request): :return http response of user_detail_deleted-template that confirms deletion. """ user = models.User.objects.get_by_natural_key(request.user.username) + + unsub_user_from_future_shifts(user.account) + user.username = random_string() user.first_name = "Deleted" user.last_name = "User" user.email = random_string(24)+"@yy.yy" user.password = random_string(20) user.is_active = False + user.is_staff = False + user.is_superuser = False + user.user_permissions.clear() + user.groups.clear() user.save() logout(request) From 10d0594a2fb425296762b21fd3a452117837355d Mon Sep 17 00:00:00 2001 From: Peter Palmreuther Date: Wed, 23 Mar 2022 23:54:06 +0100 Subject: [PATCH 2/2] Reformat only changes Let black define format and try to make flake8 happy. --- accounts/views.py | 111 +++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 45 deletions(-) diff --git a/accounts/views.py b/accounts/views.py index dccd3d69..4a1d6f04 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -24,10 +24,11 @@ def user_account_detail(request): Just shows the user profile. :param request: http request - :return: return value can be used in urls.py: url(r'^', user_account_detail, name="account_detail") + :return: return value can be used in urls.py: + url(r'^', user_account_detail, name="account_detail") """ user = request.user - return render(request, 'user_detail.html', {'user': user}) + return render(request, "user_detail.html", {"user": user}) def random_string(length=30): @@ -36,12 +37,14 @@ def random_string(length=30): :param length: (optional, default is 30) length of the string to be created """ - return u''.join(random.choice(string.ascii_letters) for x in range(length)) + return "".join(random.choice(string.ascii_letters) for x in range(length)) @transaction.atomic def unsub_user_from_future_shifts(user): - subscribed_shifts = ShiftHelper.objects.filter(user_account=user, shift__starting_time__gt=datetime.now()) + subscribed_shifts = ShiftHelper.objects.filter( + user_account=user, shift__starting_time__gt=datetime.now() + ) for sh in subscribed_shifts: LogEntry.objects.log_action( user_id=user.id, @@ -49,7 +52,7 @@ def unsub_user_from_future_shifts(user): object_id=sh.id, object_repr=f"User '{user}' @ shift '{sh.shift}'", action_flag=DELETION, - change_message=f"Initially joined: {sh.joined_shift_at.isoformat()}" + change_message=f"Initially joined: {sh.joined_shift_at.isoformat()}", ) sh.delete() @@ -59,7 +62,8 @@ def unsub_user_from_future_shifts(user): def account_delete_final(request): """ This randomizes/anonymises the user profile. The account is set inactive. - (regarding to django documentation setting inactive is preferred to deleting an account.) + (regarding to django documentation setting inactive is preferred to deleting an + account.) :param request: http request :return http response of user_detail_deleted-template that confirms deletion. @@ -71,7 +75,7 @@ def account_delete_final(request): user.username = random_string() user.first_name = "Deleted" user.last_name = "User" - user.email = random_string(24)+"@yy.yy" + user.email = random_string(24) + "@yy.yy" user.password = random_string(20) user.is_active = False user.is_staff = False @@ -81,16 +85,17 @@ def account_delete_final(request): user.save() logout(request) - return render(request, 'user_detail_deleted.html') + return render(request, "user_detail_deleted.html") class AccountUpdateView(LoginRequiredMixin, UpdateView): """ Allows a user to update his/her profile. """ - fields = ['first_name', 'last_name', 'username'] + + fields = ["first_name", "last_name", "username"] template_name = "user_account_edit.html" - success_url = reverse_lazy('account_detail') + success_url = reverse_lazy("account_detail") def get_object(self, queryset=None): return self.request.user @@ -98,9 +103,11 @@ def get_object(self, queryset=None): class AccountDeleteView(LoginRequiredMixin, UpdateView): """ - Allows a user to confirm he/she wants to delete the profile. This offers the last warning. + Allows a user to confirm he/she wants to delete the profile. This offers the last + warning. """ - fields = ['first_name', 'last_name', 'username'] + + fields = ["first_name", "last_name", "username"] template_name = "user_account_delete.html" def get_object(self, queryset=None): @@ -118,47 +125,61 @@ def shift_list_active(request): shifts_further_future. """ user = request.user - shifthelper = ShiftHelper.objects.filter(user_account=UserAccount.objects.get(user=user)) - shifts_today = shifthelper \ - .filter(shift__starting_time__day=date.today().day, - shift__starting_time__month=date.today().month, - shift__starting_time__year=date.today().year) \ - .order_by("shift__starting_time") - shifts_tomorrow = shifthelper \ - .filter(shift__starting_time__day=date.today().day + 1, - shift__starting_time__month=date.today().month, - shift__starting_time__year=date.today().year) \ - .order_by("shift__starting_time") - shifts_day_after_tomorrow = shifthelper \ - .filter(shift__starting_time__day=date.today().day + 2, - shift__starting_time__month=date.today().month, - shift__starting_time__year=date.today().year) \ - .order_by("shift__starting_time") - shifts_further_future = shifthelper \ - .filter(shift__starting_time__gt=date.today() + timedelta(days=3)) \ - .order_by("shift__starting_time") - - return render(request, 'shift_list.html', {'user': user, - 'shifts_today': shifts_today, - 'shifts_tomorrow': shifts_tomorrow, - 'shifts_day_after_tomorrow': shifts_day_after_tomorrow, - 'shifts_further_future': shifts_further_future}) + shifthelper = ShiftHelper.objects.filter( + user_account=UserAccount.objects.get(user=user) + ) + shifts_today = shifthelper.filter( + shift__starting_time__day=date.today().day, + shift__starting_time__month=date.today().month, + shift__starting_time__year=date.today().year, + ).order_by("shift__starting_time") + shifts_tomorrow = shifthelper.filter( + shift__starting_time__day=date.today().day + 1, + shift__starting_time__month=date.today().month, + shift__starting_time__year=date.today().year, + ).order_by("shift__starting_time") + shifts_day_after_tomorrow = shifthelper.filter( + shift__starting_time__day=date.today().day + 2, + shift__starting_time__month=date.today().month, + shift__starting_time__year=date.today().year, + ).order_by("shift__starting_time") + shifts_further_future = shifthelper.filter( + shift__starting_time__gt=date.today() + timedelta(days=3) + ).order_by("shift__starting_time") + + return render( + request, + "shift_list.html", + { + "user": user, + "shifts_today": shifts_today, + "shifts_tomorrow": shifts_tomorrow, + "shifts_day_after_tomorrow": shifts_day_after_tomorrow, + "shifts_further_future": shifts_further_future, + }, + ) @login_required() def shift_list_done(request): """ - Delivers the list of shifts, a user has signed up in the past (starting from yesterday). + Delivers the list of shifts, a user has signed up in the past (starting from + yesterday). :param request: http request :return: http response of rendered shift_list_done-template and user-date, - ie.: user and shifts_past. + ie.: user and shifts_past. """ user = request.user - shifthelper = ShiftHelper.objects.filter(user_account=UserAccount.objects.get(user=user)) - shifts_past = shifthelper.filter(shift__ending_time__lt=date.today()).order_by("shift__starting_time").reverse() - - return render(request, 'shift_list_done.html', {'user': user, - 'shifts_past': shifts_past}) - + shifthelper = ShiftHelper.objects.filter( + user_account=UserAccount.objects.get(user=user) + ) + shifts_past = ( + shifthelper.filter(shift__ending_time__lt=date.today()) + .order_by("shift__starting_time") + .reverse() + ) + return render( + request, "shift_list_done.html", {"user": user, "shifts_past": shifts_past} + )