Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame^] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | """Migrations to set the unique constraints and drop duplicates.""" |
| 4 | # pylint: disable=invalid-name |
| 5 | import logging |
| 6 | |
| 7 | from django.db import migrations, models, connection |
| 8 | from django.db.models import Count, Min |
| 9 | |
| 10 | from crashreports.models import HeartBeat, Crashreport |
| 11 | |
| 12 | LOGGER = logging.getLogger(__name__) |
| 13 | |
| 14 | |
| 15 | def drop_heartbeat_duplicates(apps, schema_editor): |
| 16 | """Drop duplicate heartbeat entries.""" |
| 17 | # pylint: disable=unused-argument |
| 18 | find_and_drop_duplicates(HeartBeat) |
| 19 | |
| 20 | |
| 21 | def drop_crashreport_duplicates(apps, schema_editor): |
| 22 | """Drop duplicate crashreport entries.""" |
| 23 | # pylint: disable=unused-argument |
| 24 | find_and_drop_duplicates(Crashreport) |
| 25 | |
| 26 | |
| 27 | def find_and_drop_duplicates(object_type): |
| 28 | """Drop all duplicates of the given object type.""" |
| 29 | unique_fields = ("device", "date") |
| 30 | duplicates = ( |
| 31 | object_type.objects.values(*unique_fields) |
| 32 | .order_by() |
| 33 | .annotate(min_id=Min("id"), num_duplicates=Count("id")) |
| 34 | .filter(num_duplicates__gt=1) |
| 35 | ) |
| 36 | |
| 37 | LOGGER.info( |
| 38 | "Found %d %s instances that have duplicates. These will be removed.", |
| 39 | duplicates.count(), |
| 40 | object_type.__name__, |
| 41 | ) |
| 42 | for duplicate in duplicates: |
| 43 | LOGGER.debug("Removing duplicates: %s", duplicate) |
| 44 | ( |
| 45 | object_type.objects.filter( |
| 46 | device=duplicate["device"], date=duplicate["date"] |
| 47 | ) |
| 48 | .exclude(id=duplicate["min_id"]) |
| 49 | .delete() |
| 50 | ) |
| 51 | |
| 52 | # Manually commit the data migration before schema migrations are applied |
| 53 | connection.cursor().execute("COMMIT;") |
| 54 | |
| 55 | |
| 56 | class Migration(migrations.Migration): |
| 57 | """Change heartbeat date field, set unique constraints, drop duplicates.""" |
| 58 | |
| 59 | dependencies = [("crashreports", "0005_add_fp_staff_group")] |
| 60 | |
| 61 | operations = [ |
| 62 | migrations.AlterField( |
| 63 | model_name="heartbeat", |
| 64 | name="date", |
| 65 | field=models.DateField(db_index=True), |
| 66 | ), |
| 67 | migrations.RunPython( |
| 68 | drop_heartbeat_duplicates, reverse_code=migrations.RunPython.noop |
| 69 | ), |
| 70 | migrations.RunPython( |
| 71 | drop_crashreport_duplicates, reverse_code=migrations.RunPython.noop |
| 72 | ), |
| 73 | migrations.AlterUniqueTogether( |
| 74 | name="crashreport", unique_together=set([("device", "date")]) |
| 75 | ), |
| 76 | migrations.AlterUniqueTogether( |
| 77 | name="heartbeat", unique_together=set([("device", "date")]) |
| 78 | ), |
| 79 | ] |