From 5d7ef98201e17bad4d3a71f66a5633e49c1ebc87 Mon Sep 17 00:00:00 2001 From: OhMaley Date: Wed, 27 Nov 2024 15:00:15 -0500 Subject: [PATCH] add soft deletions attributes in profile class + override deletion method + update email and log in mechanism --- src/apps/competitions/emails.py | 10 +++++++- .../migrations/0014_auto_20241120_1607.py | 23 +++++++++++++++++ src/apps/profiles/models.py | 25 +++++++++++++++++++ src/apps/profiles/views.py | 2 +- 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/apps/profiles/migrations/0014_auto_20241120_1607.py diff --git a/src/apps/competitions/emails.py b/src/apps/competitions/emails.py index 7c12a1fa3..5438f545b 100644 --- a/src/apps/competitions/emails.py +++ b/src/apps/competitions/emails.py @@ -2,10 +2,12 @@ def get_organizer_emails(competition): - return [user.email for user in competition.all_organizers] + return [user.email for user in competition.all_organizers if not user.is_deleted] def send_participation_requested_emails(participant): + if participant.user.is_deleted: return + context = { 'participant': participant } @@ -29,6 +31,8 @@ def send_participation_requested_emails(participant): def send_participation_accepted_emails(participant): + if participant.user.is_deleted: return + context = { 'participant': participant } @@ -50,6 +54,8 @@ def send_participation_accepted_emails(participant): def send_participation_denied_emails(participant): + if participant.user.is_deleted: return + context = { 'participant': participant } @@ -72,6 +78,8 @@ def send_participation_denied_emails(participant): def send_direct_participant_email(participant, content): + if participant.user.is_deleted: return + codalab_send_markdown_email( subject=f'A message from the admins of {participant.competition.title}', markdown_content=content, diff --git a/src/apps/profiles/migrations/0014_auto_20241120_1607.py b/src/apps/profiles/migrations/0014_auto_20241120_1607.py new file mode 100644 index 000000000..b339957c9 --- /dev/null +++ b/src/apps/profiles/migrations/0014_auto_20241120_1607.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.28 on 2024-11-20 16:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0013_auto_20240304_0616'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='user', + name='is_deleted', + field=models.BooleanField(default=False), + ), + ] diff --git a/src/apps/profiles/models.py b/src/apps/profiles/models.py index b150e54e4..99bebaa25 100644 --- a/src/apps/profiles/models.py +++ b/src/apps/profiles/models.py @@ -93,6 +93,10 @@ class User(ChaHubSaveMixin, AbstractBaseUser, PermissionsMixin): # Required for social auth and such to create users objects = ChaHubUserManager() + # Soft deletion + is_deleted = models.BooleanField(default=False) + deleted_at = models.DateTimeField(null=True, blank=True) + def save(self, *args, **kwargs): self.slug = slugify(self.username, allow_unicode=True) super().save(*args, **kwargs) @@ -194,6 +198,27 @@ def get_used_storage_space(self): return storage_used + def delete(self, *args, **kwargs): + """Soft delete the user and anonymize personal data.""" + # Mark the user as deleted + self.is_deleted = True + self.deleted_at = now() + + # Anonymize personal data + # TODO add all personal data that needs to be anonymized + self.email = f"deleted_{uuid.uuid4()}@domain.com" + self.username = f"deleted_user_{self.id}" + + # Save the changes + self.save() + + def restore(self, *args, **kwargs): + """Restore a soft-deleted user. Note that personal data remains anonymized.""" + self.is_deleted = False + self.deleted_at = None + self.save() + + class GithubUserInfo(models.Model): # Required Info uid = models.CharField(max_length=30, unique=True) diff --git a/src/apps/profiles/views.py b/src/apps/profiles/views.py index 08c697e85..6ea14a1d1 100644 --- a/src/apps/profiles/views.py +++ b/src/apps/profiles/views.py @@ -181,7 +181,7 @@ def log_in(request): # Check if the user exists try: - user = User.objects.get(Q(username=username) | Q(email=username)) + user = User.objects.get((Q(username=username) | Q(email=username)) & Q(is_deleted=False)) except User.DoesNotExist: messages.error(request, "User does not exist!") else: