Skip to content
11 changes: 8 additions & 3 deletions users/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class UserAdmin(admin.ModelAdmin):
actions = [
"mark_selected_as_spam",
"soft_delete_selected",
"hard_delete_selected",
"clean_user_data_deletion",
"run_profile_spam_detection_on_selected",
]
search_fields = ["username", "email", "pk"]
Expand Down Expand Up @@ -220,8 +220,13 @@ def soft_delete_selected(self, request, queryset: QuerySet[User]):
for user in queryset:
user.soft_delete()

def hard_delete_selected(self, request, queryset: QuerySet[User]):
queryset.delete()
def clean_user_data_deletion(self, request, queryset: QuerySet[User]):
for user in queryset:
user.clean_user_data_delete()

clean_user_data_deletion.short_description = (
"One click Personal Data deletion (GDPR compliant)"
)

def run_profile_spam_detection_on_selected(self, request, queryset: QuerySet[User]):
for user in queryset:
Expand Down
66 changes: 65 additions & 1 deletion users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
from django.conf import settings
from django.contrib.auth.models import AbstractUser, UserManager
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.db import models, transaction
from django.db.models import QuerySet
from django.utils import timezone
from rest_framework.authtoken.models import Token
from social_django.models import UserSocialAuth

from utils.models import TimeStampedModel

Expand Down Expand Up @@ -154,6 +156,68 @@ def soft_delete(self: "User") -> None:

self.save()

@transaction.atomic
def clean_user_data_delete(self: "User") -> None:
# Update User object
self.is_active = False
self.bio = ""
self.old_usernames = []
self.website = None
self.twitter = None
self.linkedin = None
self.facebook = None
self.github = None
self.good_judgement_open = None
self.kalshi = None
self.manifold = None
self.infer = None
self.hypermind = None
self.occupation = None
self.location = None
self.profile_picture = None
self.unsubscribed_mailing_tags = []
self.language = None
self.username = "deleted_user-" + str(self.id)
self.first_name = ""
self.last_name = ""
self.email = ""
self.set_password(None)
self.save()

# Comments
self.comment_set.filter(is_private=True).delete()
# don't touch public comments

# Token
Token.objects.filter(user=self).delete()

# Social Auth login credentials
UserSocialAuth.objects.filter(user=self).delete()

# Posts (Notebooks/Questions)
from posts.models import Post

def hard_delete_post(post: Post):
if question := post.question:
question.delete()
if group_of_questions := post.group_of_questions:
group_of_questions.delete()
if conditional := post.conditional:
conditional.delete()
if notebook := post.notebook:
notebook.delete()
post.delete()

posts = self.posts.all()
for post in posts:
# keep if there is at least one non-author comment
if post.comments.exclude(author=self).exists():
continue
# keep if there is at least one non-author forecast
if post.forecasts.exclude(author=self).exists():
continue
hard_delete_post(post)


class UserCampaignRegistration(TimeStampedModel):
"""
Expand Down