Projet

Général

Profil

0001-agendas-add-anonymize-delay-45288.patch

Valentin Deniaud, 15 septembre 2020 16:29

Télécharger (9,06 ko)

Voir les différences:

Subject: [PATCH] agendas: add anonymize delay (#45288)

 .../management/commands/anonymize_bookings.py | 42 ++++++++++++++++
 .../migrations/0061_auto_20200915_1611.py     | 34 +++++++++++++
 chrono/agendas/models.py                      | 11 ++++-
 chrono/manager/forms.py                       |  1 +
 debian/chrono.cron.daily                      |  3 ++
 tests/test_agendas.py                         | 48 +++++++++++++++++++
 tests/test_manager.py                         |  3 ++
 7 files changed, 141 insertions(+), 1 deletion(-)
 create mode 100644 chrono/agendas/management/commands/anonymize_bookings.py
 create mode 100644 chrono/agendas/migrations/0061_auto_20200915_1611.py
 create mode 100644 debian/chrono.cron.daily
chrono/agendas/management/commands/anonymize_bookings.py
1
# chrono - agendas system
2
# Copyright (C) 2020  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from datetime import timedelta
18

  
19
from django.core.management.base import BaseCommand
20
from django.db.models import F
21
from django.utils import timezone
22

  
23
from chrono.agendas.models import Booking
24

  
25

  
26
class Command(BaseCommand):
27
    help = 'Anonymize bookings according to agendas delays'
28

  
29
    def handle(self, **options):
30
        bookings_to_anonymize = Booking.objects.filter(
31
            anonymization_datetime__isnull=True,
32
            creation_datetime__lt=timezone.now() - timedelta(days=1) * F('event__agenda__anonymize_delay'),
33
        )
34

  
35
        bookings_to_anonymize.update(
36
            label='',
37
            user_display_label='',
38
            user_external_id='',
39
            user_name='',
40
            extra_data={},
41
            anonymization_datetime=timezone.now(),
42
        )
chrono/agendas/migrations/0061_auto_20200915_1611.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-09-15 14:11
3
from __future__ import unicode_literals
4

  
5
import django.core.validators
6
from django.db import migrations, models
7

  
8

  
9
class Migration(migrations.Migration):
10

  
11
    dependencies = [
12
        ('agendas', '0060_auto_20200903_1041'),
13
    ]
14

  
15
    operations = [
16
        migrations.AddField(
17
            model_name='agenda',
18
            name='anonymize_delay',
19
            field=models.PositiveIntegerField(
20
                blank=True,
21
                default=None,
22
                help_text='After this delay, user data contained in bookings will be pruned.',
23
                null=True,
24
                validators=[
25
                    django.core.validators.MinValueValidator(30),
26
                    django.core.validators.MaxValueValidator(1000),
27
                ],
28
                verbose_name='Anonymize delay (in days)',
29
            ),
30
        ),
31
        migrations.AddField(
32
            model_name='booking', name='anonymization_datetime', field=models.DateTimeField(null=True),
33
        ),
34
    ]
chrono/agendas/models.py
32 32
from django.contrib.postgres.fields import ArrayField
33 33
from django.core.exceptions import FieldDoesNotExist
34 34
from django.core.exceptions import ValidationError
35
from django.core.validators import MaxValueValidator
35
from django.core.validators import MaxValueValidator, MinValueValidator
36 36
from django.db import models, transaction
37 37
from django.db.models import Count, Q, Case, When
38 38
from django.urls import reverse
......
141 141
        blank=True,
142 142
        validators=[MaxValueValidator(10000)],
143 143
    )  # eight weeks
144
    anonymize_delay = models.PositiveIntegerField(
145
        _('Anonymize delay (in days)'),
146
        default=None,
147
        null=True,
148
        blank=True,
149
        validators=[MinValueValidator(30), MaxValueValidator(1000)],
150
        help_text=_('After this delay, user data contained in bookings will be pruned.'),
151
    )
144 152
    real_agendas = models.ManyToManyField(
145 153
        'self',
146 154
        related_name='virtual_agendas',
......
992 1000
class Booking(models.Model):
993 1001
    event = models.ForeignKey(Event, on_delete=models.CASCADE)
994 1002
    extra_data = JSONField(null=True)
1003
    anonymization_datetime = models.DateTimeField(null=True)
995 1004
    cancellation_datetime = models.DateTimeField(null=True)
996 1005
    in_waiting_list = models.BooleanField(default=False)
997 1006
    creation_datetime = models.DateTimeField(auto_now_add=True)
chrono/manager/forms.py
72 72
            'view_role',
73 73
            'minimal_booking_delay',
74 74
            'maximal_booking_delay',
75
            'anonymize_delay',
75 76
            'default_view',
76 77
        ]
77 78

  
debian/chrono.cron.daily
1
#! /bin/sh
2

  
3
/sbin/runuser -u chrono /usr/bin/chrono-manage -- tenant_command anonymize_bookings --all-tenants
tests/test_agendas.py
1238 1238
    # no new email on subsequent run
1239 1239
    call_command('send_email_notifications')
1240 1240
    assert len(mailoutbox) == 1
1241

  
1242

  
1243
def test_anonymize_bookings(freezer):
1244
    day = datetime.datetime(year=2020, month=1, day=1)
1245
    freezer.move_to(day)
1246
    agenda = Agenda.objects.create(label='Agenda', kind='events')
1247
    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, label='Event')
1248

  
1249
    for i in range(5):
1250
        Booking.objects.create(
1251
            event=event,
1252
            extra_data={'test': True},
1253
            label='john',
1254
            user_display_label='john',
1255
            user_external_id='john',
1256
            user_name='john',
1257
            backoffice_url='https://example.org',
1258
        )
1259

  
1260
    freezer.move_to(day + datetime.timedelta(days=50))
1261
    new_event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, label='Event')
1262
    booking = Booking.objects.create(event=new_event, label='hop')
1263

  
1264
    freezer.move_to(day + datetime.timedelta(days=101))
1265
    call_command('anonymize_bookings')
1266
    assert not Booking.objects.filter(anonymization_datetime__isnull=False).exists()
1267

  
1268
    # now ask for anonymization
1269
    agenda.anonymize_delay = 100
1270
    agenda.save()
1271

  
1272
    call_command('anonymize_bookings')
1273
    assert (
1274
        Booking.objects.filter(
1275
            label='',
1276
            user_display_label='',
1277
            user_external_id='',
1278
            user_name='',
1279
            backoffice_url='https://example.org',
1280
            extra_data={},
1281
            anonymization_datetime=now(),
1282
        ).count()
1283
        == 5
1284
    )
1285

  
1286
    booking.refresh_from_db()
1287
    assert booking.label == 'hop'
1288
    assert not booking.anonymization_datetime
tests/test_manager.py
875 875
    resp = app.get('/manage/agendas/%s/edit' % agenda_events.pk)
876 876
    assert resp.form['label'].value == 'Foo bar'
877 877
    resp.form['label'] = 'Foo baz'
878
    resp.form['anonymize_delay'] = 365
878 879
    assert 'default_view' in resp.context['form'].fields
879 880
    resp = resp.form.submit()
880 881
    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda_events.pk)
......
882 883
    assert 'has_resources' not in resp.context
883 884
    assert 'Foo baz' in resp.text
884 885
    assert '<h2>Settings' in resp.text
886
    agenda_events.refresh_from_db()
887
    assert agenda_events.anonymize_delay == 365
885 888

  
886 889
    resp = app.get('/manage/agendas/%s/edit' % agenda_meetings.pk)
887 890
    assert 'default_view' not in resp.context['form'].fields
888
-