Have companies delete your personal data

Compare changes

Choose any two refs to compare.

+62 -26
+38 -16
app/badactors/admin/deletion_request.py
··· 5 5 from django.contrib import admin, messages 6 6 from django.shortcuts import redirect 7 7 from django.urls import reverse, path 8 + from django.utils import formats 8 9 from django.utils.html import format_html 9 10 from django.utils.translation import ngettext 10 11 ··· 42 43 43 44 class PendingEmailInline(admin.TabularInline): 44 45 model = PendingEmail 45 - fields = ["subject", "status", "created_at", "updated_at"] 46 - readonly_fields = ["subject", "status", "created_at", "updated_at"] 46 + fields = ["link", "created_at", "subject", "status", "updated_at"] 47 + readonly_fields = ["link", "subject", "status", "created_at", "updated_at"] 47 48 extra = 0 48 49 can_delete = False 49 50 51 + def link(self, obj): 52 + if obj.pk: 53 + link = reverse("admin:badactors_pendingemail_change", args=[obj.pk]) 54 + return format_html('<a href="{}">PE-{}</a>', link, obj.pk) 55 + return "-" 56 + 57 + link.short_description = "PendingEmail" 58 + 59 + def get_queryset(self, request): 60 + qs = super().get_queryset(request) 61 + return qs.filter( 62 + status__in=[ 63 + PendingEmail.Status.DRAFT, 64 + PendingEmail.Status.READY, 65 + PendingEmail.Status.APPROVED, 66 + ] 67 + ) 68 + 50 69 def has_add_permission(self, request, obj=None): 51 70 return False 52 71 ··· 55 74 template = "admin/badactors/deletionrequest/conversationitem_inline.html" 56 75 model = ConversationItem 57 76 fields = [ 77 + "display_message_link", 58 78 "display_message_date", 79 + "display_message_subject", 59 80 "direction", 60 - "display_message_link", 61 81 "message_type", 62 - "requires_manual_review", 63 - "notes", 64 82 ] 65 - readonly_fields = ["display_message_date", "direction", "display_message_link"] 83 + readonly_fields = [ 84 + "display_message_date", 85 + "display_message_subject", 86 + "direction", 87 + "display_message_link", 88 + ] 66 89 can_delete = False 67 90 extra = 0 68 91 ordering = ["sequence"] 69 92 93 + def display_message_subject(self, obj): 94 + if obj.message and obj.message.subject: 95 + return obj.message.subject 96 + return "-" 97 + 98 + display_message_subject.short_description = "Subject" 99 + 70 100 def display_message_date(self, obj): 71 101 """Show message timestamp.""" 72 102 if obj.message and obj.message.processed: 73 - return obj.message.processed.strftime("%Y-%m-%d %H:%M") 103 + return formats.date_format(obj.message.processed, "DATETIME_FORMAT") 74 104 return "-" 75 105 76 106 display_message_date.short_description = "Date" ··· 84 114 85 115 display_message_link.short_description = "Message" 86 116 87 - def formfield_for_dbfield(self, db_field, request, **kwargs): 88 - """Customize form fields.""" 89 - if db_field.name == "notes": 90 - kwargs["widget"] = admin.widgets.AdminTextareaWidget( 91 - attrs={"rows": 2, "cols": 40} 92 - ) 93 - return super().formfield_for_dbfield(db_field, request, **kwargs) 94 - 95 117 96 118 class ConversationClassificationInline(admin.StackedInline): 97 119 model = ConversationClassification ··· 128 150 search_fields = ["company__name", "status", "uuid", "user_email_address"] 129 151 actions = ["send_confirmation_emails_to_users", "send_gdpr_request"] 130 152 inlines = [ 131 - LetterOfAuthorityInline, 132 153 PendingEmailInline, 133 154 ConversationItemInline, 134 155 ConversationClassificationInline, 156 + LetterOfAuthorityInline, 135 157 ] 136 158 137 159 def display_company(self, obj):
+24 -10
app/badactors/models.py
··· 347 347 def __str__(self): 348 348 return f"ConversationItem #{self.sequence} for {self.deletion_request}" 349 349 350 + 350 351 @receiver(message_received) 351 352 def add_incoming_message_to_dr(sender, message: Message, **kwargs): 352 353 to_addresses = message.to_addresses 353 354 try: 354 - uuids = [address.split("@")[0] for address in to_addresses if "@deletemeplease.eu" in address] 355 + uuids = [ 356 + address.split("@")[0] 357 + for address in to_addresses 358 + if "@deletemeplease.eu" in address 359 + ] 355 360 except (AttributeError, TypeError): 356 - logger.error(f"Couldn't parse local portion of email addresses {to_addresses}. Aborting") 361 + logger.error( 362 + f"Couldn't parse local portion of email addresses {to_addresses}. Aborting" 363 + ) 357 364 return 358 365 359 366 try: 360 367 drs = DeletionRequest.objects.filter(uuid__in=uuids) 361 - except ValidationError: 362 - logger.warning(f"Found no matching DeletionRequest for incoming email {message} given UUIDs {uuids}. Aborting") 363 - return 368 + except ValidationError: 369 + logger.warning( 370 + f"Found no matching DeletionRequest for incoming email {message} given UUIDs {uuids}. Aborting" 371 + ) 372 + return 364 373 365 374 if not drs.exists(): 366 - logger.warning(f"Found no matching DeletionRequest for incoming email {message} given UUIDs {uuids}. Aborting") 375 + logger.warning( 376 + f"Found no matching DeletionRequest for incoming email {message} given UUIDs {uuids}. Aborting" 377 + ) 367 378 return 368 379 if drs.count() > 1: 369 - logger.error(f"Found multiple matching DeletionRequests for incoming email {message} given UUIDs {uuids}. Aborting") 380 + logger.error( 381 + f"Found multiple matching DeletionRequests for incoming email {message} given UUIDs {uuids}. Aborting" 382 + ) 370 383 return 371 384 372 385 ConversationItem.objects.create( 373 - deletion_request = drs.first(), 374 - message = message, 375 - direction = MessageDirection.INCOMING.value 386 + deletion_request=drs.first(), 387 + message=message, 388 + direction=MessageDirection.INCOMING.value, 376 389 ) 377 390 391 + 378 392 class DeletionRequest(models.Model): 379 393 """ 380 394 Model to store information about deletion requests.