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'])