Skip to content

Commit

Permalink
Migrate users last_login date to avoid unwarned delete
Browse files Browse the repository at this point in the history
  • Loading branch information
Guilouf committed Dec 24, 2024
1 parent 9694bf7 commit 56289d8
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 2 deletions.
32 changes: 32 additions & 0 deletions lemarche/users/migrations/0043_update_inactive_last_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""When the anonymizing user feature will be deployed first, there is a possibility that some users are already
inactive since a long time, and could be removed before reaching the warning period.
We set their last_login date just before, so they can receive the warning email and act before their account is
anonymized
"""

from django.db import migrations
from django.conf import settings
from django.utils import timezone

from dateutil.relativedelta import relativedelta


def update_last_login(apps, schema_editor):
User = apps.get_model("users", "User")
expiry_date = timezone.now() - relativedelta(months=settings.INACTIVE_USER_TIMEOUT_IN_MONTHS)
warning_date = expiry_date + relativedelta(days=settings.INACTIVE_USER_WARNING_DELAY_IN_DAYS)

# select inactive users about to be deleted
qs = User.objects.filter(last_login__lte=expiry_date)
# Set their last login few days before the fatal date, so they can be warned by auto email
qs.update(last_login=warning_date)


class Migration(migrations.Migration):
dependencies = [
("users", "0042_email_template_anonymise_user"),
]

operations = [
migrations.RunPython(update_last_login, reverse_code=migrations.RunPython.noop),
]
23 changes: 21 additions & 2 deletions lemarche/users/tests.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from datetime import datetime
from io import StringIO
from unittest.mock import patch
from importlib import import_module

from django.test import TestCase, override_settings
from django.utils import timezone
from django.db.models import F
from django.core.management import call_command
from django.apps import apps
from dateutil.relativedelta import relativedelta

from lemarche.companies.factories import CompanyFactory
Expand Down Expand Up @@ -177,7 +179,7 @@ class UserAnonymizationTestCase(TestCase):
def setUp(self):
frozen_now = datetime(year=2024, month=1, day=1, tzinfo=timezone.utc)
self.frozen_last_year = frozen_now - relativedelta(years=1)
frozen_warning_date = self.frozen_last_year + relativedelta(days=7)
self.frozen_warning_date = self.frozen_last_year + relativedelta(days=7)

UserFactory(first_name="active_user", last_login=frozen_now)
UserFactory(
Expand All @@ -201,7 +203,7 @@ def setUp(self):
api_key_last_updated=frozen_now,
)
UserFactory(
last_login=frozen_warning_date,
last_login=self.frozen_warning_date,
first_name="about_to_be_inactive",
)
# Set email as active to check if it's really sent
Expand Down Expand Up @@ -319,3 +321,20 @@ def test_dryrun_warn_command(self, mock_timezone):
call_command("anonymize_old_users", dry_run=True, stdout=out)

self.assertFalse(TemplateTransactionalSendLog.objects.all())

@patch("django.utils.timezone.now")
def test_last_login_migration(self, mock_timezone):
"""We test the runpython function inside the migration file"""

now_dt = datetime(year=2024, month=1, day=1, tzinfo=timezone.utc)
mock_timezone.return_value = now_dt

migration = import_module("lemarche.users.migrations.0043_update_inactive_last_login")

expired_user = User.objects.filter(last_login__lte=self.frozen_last_year)
self.assertTrue(expired_user)

migration.update_last_login(apps, None)

self.assertFalse(User.objects.filter(last_login__lte=self.frozen_last_year))
self.assertEqual(User.objects.filter(last_login__lte=self.frozen_warning_date).count(), 3)

0 comments on commit 56289d8

Please sign in to comment.