agendas: add anonymize delay (#45288)

This commit is contained in:
Valentin Deniaud 2020-08-13 11:48:52 +02:00
parent 2adf7e0a48
commit 43c7a6b5e4
7 changed files with 141 additions and 1 deletions

View File

@ -0,0 +1,42 @@
# chrono - agendas system
# Copyright (C) 2020 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from datetime import timedelta
from django.core.management.base import BaseCommand
from django.db.models import F
from django.utils import timezone
from chrono.agendas.models import Booking
class Command(BaseCommand):
help = 'Anonymize bookings according to agendas delays'
def handle(self, **options):
bookings_to_anonymize = Booking.objects.filter(
anonymization_datetime__isnull=True,
creation_datetime__lt=timezone.now() - timedelta(days=1) * F('event__agenda__anonymize_delay'),
)
bookings_to_anonymize.update(
label='',
user_display_label='',
user_external_id='',
user_name='',
extra_data={},
anonymization_datetime=timezone.now(),
)

View File

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2020-09-28 12:45
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('agendas', '0062_auto_20200915_1401'),
]
operations = [
migrations.AddField(
model_name='agenda',
name='anonymize_delay',
field=models.PositiveIntegerField(
blank=True,
default=None,
help_text='After this delay, user data contained in bookings will be pruned.',
null=True,
validators=[
django.core.validators.MinValueValidator(30),
django.core.validators.MaxValueValidator(1000),
],
verbose_name='Anonymize delay (in days)',
),
),
migrations.AddField(
model_name='booking', name='anonymization_datetime', field=models.DateTimeField(null=True),
),
]

View File

@ -32,7 +32,7 @@ from django.contrib.auth.models import Group
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import FieldDoesNotExist
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models, transaction
from django.db.models import Count, Q, Case, When
from django.urls import reverse
@ -141,6 +141,14 @@ class Agenda(models.Model):
blank=True,
validators=[MaxValueValidator(10000)],
) # eight weeks
anonymize_delay = models.PositiveIntegerField(
_('Anonymize delay (in days)'),
default=None,
null=True,
blank=True,
validators=[MinValueValidator(30), MaxValueValidator(1000)],
help_text=_('After this delay, user data contained in bookings will be pruned.'),
)
real_agendas = models.ManyToManyField(
'self',
related_name='virtual_agendas',
@ -989,6 +997,7 @@ class Event(models.Model):
class Booking(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE)
extra_data = JSONField(null=True)
anonymization_datetime = models.DateTimeField(null=True)
cancellation_datetime = models.DateTimeField(null=True)
reminder_datetime = models.DateTimeField(null=True)
in_waiting_list = models.BooleanField(default=False)

View File

@ -74,6 +74,7 @@ class AgendaEditForm(AgendaAddForm):
'view_role',
'minimal_booking_delay',
'maximal_booking_delay',
'anonymize_delay',
'default_view',
]

3
debian/chrono.cron.daily vendored Normal file
View File

@ -0,0 +1,3 @@
#! /bin/sh
/sbin/runuser -u chrono /usr/bin/chrono-manage -- tenant_command anonymize_bookings --all-tenants

View File

@ -1484,3 +1484,51 @@ def test_agenda_reminders_meetings(mailoutbox, freezer):
mail = mailoutbox[0]
assert mail.subject == 'Reminder for your meeting in 2 days at 11 a.m.'
assert 'Your meeting "Birth certificate" is scheduled on Monday 6 January at 11 a.m..' in mail.body
def test_anonymize_bookings(freezer):
day = datetime.datetime(year=2020, month=1, day=1)
freezer.move_to(day)
agenda = Agenda.objects.create(label='Agenda', kind='events')
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, label='Event')
for i in range(5):
Booking.objects.create(
event=event,
extra_data={'test': True},
label='john',
user_display_label='john',
user_external_id='john',
user_name='john',
backoffice_url='https://example.org',
)
freezer.move_to(day + datetime.timedelta(days=50))
new_event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, label='Event')
booking = Booking.objects.create(event=new_event, label='hop')
freezer.move_to(day + datetime.timedelta(days=101))
call_command('anonymize_bookings')
assert not Booking.objects.filter(anonymization_datetime__isnull=False).exists()
# now ask for anonymization
agenda.anonymize_delay = 100
agenda.save()
call_command('anonymize_bookings')
assert (
Booking.objects.filter(
label='',
user_display_label='',
user_external_id='',
user_name='',
backoffice_url='https://example.org',
extra_data={},
anonymization_datetime=now(),
).count()
== 5
)
booking.refresh_from_db()
assert booking.label == 'hop'
assert not booking.anonymization_datetime

View File

@ -884,6 +884,7 @@ def test_options_agenda(app, admin_user):
resp = app.get('/manage/agendas/%s/edit' % agenda_events.pk)
assert resp.form['label'].value == 'Foo bar'
resp.form['label'] = 'Foo baz'
resp.form['anonymize_delay'] = 365
assert 'default_view' in resp.context['form'].fields
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda_events.pk)
@ -891,6 +892,8 @@ def test_options_agenda(app, admin_user):
assert 'has_resources' not in resp.context
assert 'Foo baz' in resp.text
assert '<h2>Settings' in resp.text
agenda_events.refresh_from_db()
assert agenda_events.anonymize_delay == 365
resp = app.get('/manage/agendas/%s/edit' % agenda_meetings.pk)
assert 'default_view' not in resp.context['form'].fields