Verified Commit 002eb3f3 authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg
Browse files

Add data minimisation management command

parent 12cd7c7d
from django.core.management.base import BaseCommand
from members import services
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
dest='dry-run',
default=False,
help='Dry run instead of saving data',
)
def handle(self, *args, **options):
processed = services.execute_data_minimisation(options['dry-run'])
for p in processed:
print('Removed data for {}'.format(p))
# Generated by Django 2.0.8 on 2018-09-07 13:28
from django.db import migrations
import localflavor.generic.models
class Migration(migrations.Migration):
dependencies = [
('members', '0023_auto_20180819_1542'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='bank_account',
field=localflavor.generic.models.IBANField(blank=True, help_text='Bank account for direct debit', include_countries=('AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GI', 'GR', 'HR', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK', 'SM'), max_length=34, null=True, use_nordea_extensions=False, verbose_name='Bank account'),
),
]
......@@ -411,6 +411,7 @@ class Profile(models.Model):
help_text=_('Bank account for direct debit'),
include_countries=IBAN_SEPA_COUNTRIES,
blank=True,
null=True,
)
def display_name(self):
......
from datetime import date
from django.db.models import Q
from django.utils import timezone
from members import emails
from members.models import Membership
from members.models import Membership, Member
from utils.snippets import datetime_to_lectureyear
......@@ -139,3 +141,40 @@ def process_email_change(change_request):
member.save()
emails.send_email_change_completion_message(change_request)
def execute_data_minimisation(dry_run=False):
"""
Clean the profiles of members/users of whom the last membership ended
at least 31 days ago
:param dry_run: does not really remove data if True
:return list of processed members
"""
members = (Member.objects
.filter(Q(membership__until__isnull=False) |
Q(membership__until__lte=timezone.now().date()))
.distinct()
.prefetch_related('membership_set', 'profile'))
deletion_period = timezone.now().date() - timezone.timedelta(days=31)
processed_members = []
for member in members:
if (member.latest_membership is None or
member.latest_membership.until <= deletion_period):
processed_members.append(member)
profile = member.profile
profile.student_number = None
profile.phone_number = None
profile.address_street = None
profile.address_street2 = None
profile.address_postal_code = None
profile.address_city = None
profile.birthday = None
profile.emergency_contact_phone_number = None
profile.emergency_contact = None
profile.website = None
profile.bank_account = None
if not dry_run:
profile.save()
return processed_members
from datetime import timedelta, date
from django.test import TestCase
from django.utils import timezone
from unittest import mock
from freezegun import freeze_time
from members import services
from members.models import Member, Membership, Profile, EmailChange
from members.services import gen_stats_year, gen_stats_member_type
......@@ -214,3 +217,63 @@ class EmailChangeTest(TestCase):
self.assertEqual(self.member.email, change_request.email)
send_message_mock.assert_called_once_with(change_request)
class DataMinimalisationTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.member = Member.objects.create(
username='test1',
first_name='Test1',
last_name='Example',
email='test1@example.org'
)
Profile.objects.create(
user=cls.member,
language='nl',
student_number='s1234567'
)
cls.membership = Membership.objects.create(
user=cls.member,
type=Membership.MEMBER,
since=timezone.now().replace(year=2017, month=9, day=1),
until=timezone.now().replace(year=2018, month=9, day=1)
)
@freeze_time('2018-10-2')
def test_removes_after_31_days(self):
processed = services.execute_data_minimisation(True)
self.assertEqual(len(processed), 1)
self.assertEqual(processed[0], self.member)
self.membership.until = timezone.now().replace(
year=2018, month=11, day=1)
self.membership.save()
processed = services.execute_data_minimisation(True)
self.assertEqual(len(processed), 0)
@freeze_time('2018-10-2')
def test_dry_run(self):
with self.subTest('With dry_run=True'):
services.execute_data_minimisation(True)
self.member.refresh_from_db()
self.assertEqual(self.member.profile.student_number, 's1234567')
with self.subTest('With dry_run=False'):
services.execute_data_minimisation(False)
self.member.refresh_from_db()
self.assertIsNone(self.member.profile.student_number)
@freeze_time('2018-10-2')
def test_does_not_affect_current_members(self):
with self.subTest('Membership ends in future'):
self.membership.until = timezone.now().replace(
year=2019, month=9, day=1)
self.membership.save()
processed = services.execute_data_minimisation(True)
self.assertEqual(len(processed), 0)
with self.subTest('Never ending membership'):
self.membership.until = None
self.membership.save()
processed = services.execute_data_minimisation(True)
self.assertEqual(len(processed), 0)
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