Writing a data shredder

If your plugin adds the ability to store personal data within eventyay, you should also implement a “data shredder” to anonymize or pseudonymize the data later.

Shredder registration

The data shredder API does not make a lot of usage from signals, however, it does use a signal to get a list of all available data shredders. Your plugin should listen for this signal and return the subclass of eventyay.base.shredder.BaseDataShredder that we’ll provide in this plugin:

 1from django.dispatch import receiver
 2
 3from eventyay.base.signals import register_data_shredders
 4
 5
 6@receiver(register_data_shredders, dispatch_uid="custom_data_shredders")
 7def register_shredder(sender, **kwargs):
 8    return [
 9        PluginDataShredder,
10    ]

The shredder class

class eventyay.base.shredder.BaseDataShredder

The central object of each data shredder is the subclass of BaseDataShredder.

BaseDataShredder.event

The default constructor sets this property to the event we are currently working for.

identifier

A short and unique identifier for this data shredder.

This is an abstract attribute, you must override this!

verbose_name

A human-readable name for this data shredder.

This is an abstract attribute, you must override this!

description

A description of what data this shredder will remove or anonymize.

This is an abstract attribute, you must override this!

generate_files()

Generate export files containing the data that will be shredded. This is called before shred_data().

Returns:

A generator yielding tuples of (filename, content_type, file_content).

shred_data()

Actually shred (anonymize/delete) the data. This should be wrapped in a database transaction.

Example

For example, the core data shredder responsible for removing invoice address information including their history looks like this:

 1class InvoiceAddressShredder(BaseDataShredder):
 2    verbose_name = _('Invoice addresses')
 3    identifier = 'invoice_addresses'
 4    description = _('This will remove all invoice addresses from orders, '
 5                    'as well as logged changes to them.')
 6
 7    def generate_files(self) -> List[Tuple[str, str, str]]:
 8        yield 'invoice-addresses.json', 'application/json', json.dumps({
 9            ia.order.code: InvoiceAddressSerializer(ia).data
10            for ia in InvoiceAddress.objects.filter(order__event=self.event)
11        }, indent=4)
12
13    @transaction.atomic
14    def shred_data(self):
15        InvoiceAddress.objects.filter(order__event=self.event).delete()
16
17        for le in self.event.logentry_set.filter(action_type="eventyay.event.order.modified"):
18            d = le.parsed_data
19            if 'invoice_data' in d and not isinstance(d['invoice_data'], bool):
20                for field in d['invoice_data']:
21                    if d['invoice_data'][field]:
22                        d['invoice_data'][field] = '█'
23                le.data = json.dumps(d)
24                le.shredded = True
25                le.save(update_fields=['data', 'shredded'])