From 48617b0425bb6b4c7f253f9b44363432658c2f1c Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Sun, 3 Nov 2019 21:08:33 +0100 Subject: Resolve migration merge conflicts Since other pull requests were merged that also included migrations for the API app, this PR needed to be updated to avoid conflicts in the migration history. In addition, the test files contained names of specific migration files that needed to be updated to the correct names after the merge resolution. --- .../0046_active_infractions_migration.py | 105 --------------------- .../0047_active_infractions_migration.py | 105 +++++++++++++++++++++ ...47_add_infractions_unique_constraints_active.py | 17 ---- ...48_add_infractions_unique_constraints_active.py | 17 ++++ .../migrations/test_active_infraction_migration.py | 8 +- pydis_site/apps/api/tests/migrations/test_base.py | 4 +- 6 files changed, 128 insertions(+), 128 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0046_active_infractions_migration.py create mode 100644 pydis_site/apps/api/migrations/0047_active_infractions_migration.py delete mode 100644 pydis_site/apps/api/migrations/0047_add_infractions_unique_constraints_active.py create mode 100644 pydis_site/apps/api/migrations/0048_add_infractions_unique_constraints_active.py (limited to 'pydis_site') diff --git a/pydis_site/apps/api/migrations/0046_active_infractions_migration.py b/pydis_site/apps/api/migrations/0046_active_infractions_migration.py deleted file mode 100644 index cc53f58f..00000000 --- a/pydis_site/apps/api/migrations/0046_active_infractions_migration.py +++ /dev/null @@ -1,105 +0,0 @@ -# Generated by Django 2.2.6 on 2019-10-07 15:59 - -from django.db import migrations -from django.db.models import Count, Prefetch, QuerySet - - -class ExpirationWrapper: - """Wraps an expiration date to properly compare permanent and temporary infractions.""" - - def __init__(self, infraction): - self.expiration_date = infraction.expires_at - - def __lt__(self, other): - """An `expiration_date` is considered smaller when it comes earlier than the `other`.""" - if self.expiration_date is None: - # A permanent infraction can never end sooner than another infraction - return False - elif other.expiration_date is None: - # If `self` is temporary, but `other` is permanent, `self` is smaller - return True - else: - return self.expiration_date < other.expiration_date - - def __eq__(self, other): - """If both expiration dates are permanent they're equal, otherwise compare dates.""" - if self.expiration_date is None and other.expiration_date is None: - return True - elif self.expiration_date is None or other.expiration_date is None: - return False - else: - return self.expiration_date == other.expiration_date - - -def migrate_inactive_types_to_inactive(apps, schema_editor): - """Migrates infractions of non-active types to inactive.""" - infraction_model = apps.get_model('api', 'Infraction') - infraction_model.objects.filter(type__in=('note', 'warning', 'kick')).update(active=False) - - -def get_query(user_model, infraction_model, infr_type: str) -> QuerySet: - """ - Creates QuerySet to fetch users with multiple active infractions of the given `type`. - - The QuerySet will prefetch the infractions and attach them as an `.infractions` attribute to the - `User` instances. - """ - active_infractions = infraction_model.objects.filter(type=infr_type, active=True) - - # Build an SQL query by chaining methods together - - # Get users with active infraction(s) of the provided `infr_type` - query = user_model.objects.filter( - infractions_received__type=infr_type, infractions_received__active=True - ) - - # Prefetch their active received infractions of `infr_type` and attach `.infractions` attribute - query = query.prefetch_related( - Prefetch('infractions_received', queryset=active_infractions, to_attr='infractions') - ) - - # Count and only include them if they have at least 2 active infractions of the `type` - query = query.annotate(num_infractions=Count('infractions_received')) - query = query.filter(num_infractions__gte=2) - - # Make sure we return each individual only once - query = query.distinct() - - return query - - -def migrate_multiple_active_infractions_per_user_to_one(apps, schema_editor): - """ - Make sure a user only has one active infraction of a given "active" infraction type. - - If a user has multiple active infraction, we keep the one with longest expiration date active - and migrate the others to inactive. - """ - infraction_model = apps.get_model('api', 'Infraction') - user_model = apps.get_model('api', 'User') - - for infraction_type in ('ban', 'mute', 'superstar', 'watch'): - query = get_query(user_model, infraction_model, infraction_type) - for user in query: - infractions = sorted(user.infractions, key=ExpirationWrapper, reverse=True) - for infraction in infractions[1:]: - infraction.active = False - infraction.save() - - -def reverse_migration(apps, schema_editor): - """There's no need to do anything special to reverse these migrations.""" - return - - -class Migration(migrations.Migration): - """Data migration to get the database consistent with the new infraction validation rules.""" - - dependencies = [ - ('api', '0045_add_plural_name_for_log_entry'), - ] - - operations = [ - migrations.RunPython(migrate_inactive_types_to_inactive, reverse_migration), - migrations.RunPython(migrate_multiple_active_infractions_per_user_to_one, reverse_migration) - ] diff --git a/pydis_site/apps/api/migrations/0047_active_infractions_migration.py b/pydis_site/apps/api/migrations/0047_active_infractions_migration.py new file mode 100644 index 00000000..9ac791dc --- /dev/null +++ b/pydis_site/apps/api/migrations/0047_active_infractions_migration.py @@ -0,0 +1,105 @@ +# Generated by Django 2.2.6 on 2019-10-07 15:59 + +from django.db import migrations +from django.db.models import Count, Prefetch, QuerySet + + +class ExpirationWrapper: + """Wraps an expiration date to properly compare permanent and temporary infractions.""" + + def __init__(self, infraction): + self.expiration_date = infraction.expires_at + + def __lt__(self, other): + """An `expiration_date` is considered smaller when it comes earlier than the `other`.""" + if self.expiration_date is None: + # A permanent infraction can never end sooner than another infraction + return False + elif other.expiration_date is None: + # If `self` is temporary, but `other` is permanent, `self` is smaller + return True + else: + return self.expiration_date < other.expiration_date + + def __eq__(self, other): + """If both expiration dates are permanent they're equal, otherwise compare dates.""" + if self.expiration_date is None and other.expiration_date is None: + return True + elif self.expiration_date is None or other.expiration_date is None: + return False + else: + return self.expiration_date == other.expiration_date + + +def migrate_inactive_types_to_inactive(apps, schema_editor): + """Migrates infractions of non-active types to inactive.""" + infraction_model = apps.get_model('api', 'Infraction') + infraction_model.objects.filter(type__in=('note', 'warning', 'kick')).update(active=False) + + +def get_query(user_model, infraction_model, infr_type: str) -> QuerySet: + """ + Creates QuerySet to fetch users with multiple active infractions of the given `type`. + + The QuerySet will prefetch the infractions and attach them as an `.infractions` attribute to the + `User` instances. + """ + active_infractions = infraction_model.objects.filter(type=infr_type, active=True) + + # Build an SQL query by chaining methods together + + # Get users with active infraction(s) of the provided `infr_type` + query = user_model.objects.filter( + infractions_received__type=infr_type, infractions_received__active=True + ) + + # Prefetch their active received infractions of `infr_type` and attach `.infractions` attribute + query = query.prefetch_related( + Prefetch('infractions_received', queryset=active_infractions, to_attr='infractions') + ) + + # Count and only include them if they have at least 2 active infractions of the `type` + query = query.annotate(num_infractions=Count('infractions_received')) + query = query.filter(num_infractions__gte=2) + + # Make sure we return each individual only once + query = query.distinct() + + return query + + +def migrate_multiple_active_infractions_per_user_to_one(apps, schema_editor): + """ + Make sure a user only has one active infraction of a given "active" infraction type. + + If a user has multiple active infraction, we keep the one with longest expiration date active + and migrate the others to inactive. + """ + infraction_model = apps.get_model('api', 'Infraction') + user_model = apps.get_model('api', 'User') + + for infraction_type in ('ban', 'mute', 'superstar', 'watch'): + query = get_query(user_model, infraction_model, infraction_type) + for user in query: + infractions = sorted(user.infractions, key=ExpirationWrapper, reverse=True) + for infraction in infractions[1:]: + infraction.active = False + infraction.save() + + +def reverse_migration(apps, schema_editor): + """There's no need to do anything special to reverse these migrations.""" + return + + +class Migration(migrations.Migration): + """Data migration to get the database consistent with the new infraction validation rules.""" + + dependencies = [ + ('api', '0046_reminder_jump_url'), + ] + + operations = [ + migrations.RunPython(migrate_inactive_types_to_inactive, reverse_migration), + migrations.RunPython(migrate_multiple_active_infractions_per_user_to_one, reverse_migration) + ] diff --git a/pydis_site/apps/api/migrations/0047_add_infractions_unique_constraints_active.py b/pydis_site/apps/api/migrations/0047_add_infractions_unique_constraints_active.py deleted file mode 100644 index dc266474..00000000 --- a/pydis_site/apps/api/migrations/0047_add_infractions_unique_constraints_active.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.6 on 2019-10-07 18:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0046_active_infractions_migration'), - ] - - operations = [ - migrations.AddConstraint( - model_name='infraction', - constraint=models.UniqueConstraint(condition=models.Q(active=True), fields=('user', 'type'), name='unique_active_infraction_per_type_per_user'), - ), - ] diff --git a/pydis_site/apps/api/migrations/0048_add_infractions_unique_constraints_active.py b/pydis_site/apps/api/migrations/0048_add_infractions_unique_constraints_active.py new file mode 100644 index 00000000..4ea1fb90 --- /dev/null +++ b/pydis_site/apps/api/migrations/0048_add_infractions_unique_constraints_active.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.6 on 2019-10-07 18:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0047_active_infractions_migration'), + ] + + operations = [ + migrations.AddConstraint( + model_name='infraction', + constraint=models.UniqueConstraint(condition=models.Q(active=True), fields=('user', 'type'), name='unique_active_infraction_per_type_per_user'), + ), + ] diff --git a/pydis_site/apps/api/tests/migrations/test_active_infraction_migration.py b/pydis_site/apps/api/tests/migrations/test_active_infraction_migration.py index 2d5fd94c..8dc29b34 100644 --- a/pydis_site/apps/api/tests/migrations/test_active_infraction_migration.py +++ b/pydis_site/apps/api/tests/migrations/test_active_infraction_migration.py @@ -81,8 +81,8 @@ class InfractionFactoryTests(MigrationsTestCase): """Tests for the InfractionFactory.""" app = "api" - migration_prior = "0045_add_plural_name_for_log_entry" - migration_target = "0045_add_plural_name_for_log_entry" + migration_prior = "0046_reminder_jump_url" + migration_target = "0046_reminder_jump_url" @classmethod def setUpPostMigrationData(cls, apps): @@ -178,8 +178,8 @@ class ActiveInfractionMigrationTests(MigrationsTestCase): """ app = "api" - migration_prior = "0045_add_plural_name_for_log_entry" - migration_target = "0046_active_infractions_migration" + migration_prior = "0046_reminder_jump_url" + migration_target = "0047_active_infractions_migration" @classmethod def setUpMigrationData(cls, apps): diff --git a/pydis_site/apps/api/tests/migrations/test_base.py b/pydis_site/apps/api/tests/migrations/test_base.py index 5d38e2dd..f69bc92c 100644 --- a/pydis_site/apps/api/tests/migrations/test_base.py +++ b/pydis_site/apps/api/tests/migrations/test_base.py @@ -93,8 +93,8 @@ class MigrationsTestCaseNoSideEffectsTests(TestCase): class LifeOfBrian(MigrationsTestCase): app = "api" - migration_prior = "0045_add_plural_name_for_log_entry" - migration_target = "0047_add_infractions_unique_constraints_active" + migration_prior = "0046_reminder_jump_url" + migration_target = "0048_add_infractions_unique_constraints_active" @classmethod def log_last_migration(cls): -- cgit v1.2.3