agendas: add anonymize delay (#45288)
This commit is contained in:
parent
2adf7e0a48
commit
43c7a6b5e4
|
@ -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(),
|
||||
)
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -74,6 +74,7 @@ class AgendaEditForm(AgendaAddForm):
|
|||
'view_role',
|
||||
'minimal_booking_delay',
|
||||
'maximal_booking_delay',
|
||||
'anonymize_delay',
|
||||
'default_view',
|
||||
]
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#! /bin/sh
|
||||
|
||||
/sbin/runuser -u chrono /usr/bin/chrono-manage -- tenant_command anonymize_bookings --all-tenants
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue