Projet

Général

Profil

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

Valentin Deniaud, 07 septembre 2020 14:06

Télécharger (8,31 ko)

Voir les différences:

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

 .../management/commands/anonymize_bookings.py | 41 +++++++++++++++++
 .../migrations/0062_agenda_anonymize_delay.py | 31 +++++++++++++
 chrono/agendas/models.py                      | 10 ++++-
 chrono/manager/forms.py                       |  1 +
 debian/chrono.cron.daily                      |  3 ++
 tests/test_agendas.py                         | 45 +++++++++++++++++++
 tests/test_manager.py                         |  3 ++
 7 files changed, 133 insertions(+), 1 deletion(-)
 create mode 100644 chrono/agendas/management/commands/anonymize_bookings.py
 create mode 100644 chrono/agendas/migrations/0062_agenda_anonymize_delay.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 = Booking.objects.exclude(extra_data={'anonymized': True})
31
        bookings_to_anonymize = bookings.filter(
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={'anonymized': True},
41
        )
chrono/agendas/migrations/0062_agenda_anonymize_delay.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-09-07 12:02
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', '0061_auto_20200907_1401'),
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
    ]
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
......
126 126
        blank=True,
127 127
        validators=[MaxValueValidator(10000)],
128 128
    )  # eight weeks
129
    anonymize_delay = models.PositiveIntegerField(
130
        _('Anonymize delay (in days)'),
131
        default=None,
132
        null=True,
133
        blank=True,
134
        validators=[MinValueValidator(30), MaxValueValidator(1000)],
135
        help_text=_('After this delay, user data contained in bookings will be pruned.'),
136
    )
129 137
    real_agendas = models.ManyToManyField(
130 138
        'self',
131 139
        related_name='virtual_agendas',
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
1236 1236
    # no new email on subsequent run
1237 1237
    call_command('send_email_notifications')
1238 1238
    assert len(mailoutbox) == 1
1239

  
1240

  
1241
def test_anonymize_bookings(freezer):
1242
    freezer.move_to('2020-01-01')
1243
    agenda = Agenda.objects.create(label='Agenda', kind='events')
1244
    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, label='Event')
1245

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

  
1257
    freezer.move_to('2020-04-01')
1258
    booking = Booking.objects.create(event=event, label='hop')
1259

  
1260
    freezer.move_to('2020-04-15')  # about 100 days after first bookings
1261
    call_command('anonymize_bookings')
1262
    assert not Booking.objects.filter(extra_data={'anonymized': True}).exists()
1263

  
1264
    # now ask for anonymization
1265
    agenda.anonymize_delay = 100
1266
    agenda.save()
1267

  
1268
    call_command('anonymize_bookings')
1269
    assert (
1270
        Booking.objects.filter(
1271
            label='',
1272
            user_display_label='',
1273
            user_external_id='',
1274
            user_name='',
1275
            backoffice_url='https://example.org',
1276
            extra_data={'anonymized': True},
1277
        ).count()
1278
        == 5
1279
    )
1280

  
1281
    booking.refresh_from_db()
1282
    assert booking.label == 'hop'
1283
    assert not booking.extra_data
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
-