90 lines
3.4 KiB
Python
90 lines
3.4 KiB
Python
# Generated by Django 2.2.17 on 2021-01-24 07:24
|
|
|
|
from django.db import migrations, models
|
|
|
|
|
|
|
|
def forward(apps, schema_editor):
|
|
"""Forward migration
|
|
|
|
Attempts to reconcile and remove duplicate ReviewerSettings
|
|
"""
|
|
ReviewerSettings = apps.get_model('review', 'ReviewerSettings')
|
|
HistoricalReviewerSettings = apps.get_model('review', 'HistoricalReviewerSettings')
|
|
|
|
def reconcile_duplicate_settings(duplicate_settings, histories):
|
|
"""Helper to decide how to handle duplicate settings"""
|
|
team = duplicate_settings[0].team
|
|
person = duplicate_settings[0].person
|
|
assert(all((s.person == person and s.team == team for s in duplicate_settings)))
|
|
|
|
print('\n>> Found duplicate settings for {}'.format(duplicate_settings[0]))
|
|
# In the DB as of Dec 2020, the only duplicate settings sets were pairs where the
|
|
# earlier PK had a change history and the latter PK did not. Based on this, assuming
|
|
# a change history indicates that the settings are important. If only one has history,
|
|
# use that one. If multiple have a history, throw an error.
|
|
settings_with_history = [s for s in duplicate_settings if histories[s.pk].count() > 0]
|
|
if len(settings_with_history) == 0:
|
|
duplicate_settings.sort(key=lambda s: s.pk)
|
|
keep = duplicate_settings[-1]
|
|
reason = 'chosen by pk'
|
|
elif len(settings_with_history) == 1:
|
|
keep = settings_with_history[0]
|
|
reason = 'chosen because has change history'
|
|
else:
|
|
# Don't try to guess what to do if multiple settings have change histories
|
|
raise RuntimeError(
|
|
'Multiple ReviewerSettings with change history for {}. Please resolve manually.'.format(
|
|
settings_with_history[0]
|
|
)
|
|
)
|
|
|
|
print('>> Keeping pk={} ({})'.format(keep.pk, reason))
|
|
for settings in duplicate_settings:
|
|
if settings.pk != keep.pk:
|
|
print('>> Deleting pk={}'.format(settings.pk))
|
|
settings.delete()
|
|
|
|
# forward migration starts here
|
|
if ReviewerSettings.objects.count() == 0:
|
|
return # nothing to do
|
|
|
|
records = dict() # list of records, keyed by (person_id, team_id)
|
|
for rs in ReviewerSettings.objects.all().order_by('pk'):
|
|
key = (rs.person_id, rs.team_id)
|
|
if key in records:
|
|
records[key].append(rs)
|
|
else:
|
|
records[key] = [rs]
|
|
|
|
for duplicate_settings in records.values():
|
|
if len(duplicate_settings) > 1:
|
|
histories = dict()
|
|
for ds in duplicate_settings:
|
|
histories[ds.pk] = HistoricalReviewerSettings.objects.filter(
|
|
id=ds.pk
|
|
)
|
|
reconcile_duplicate_settings(duplicate_settings, histories)
|
|
|
|
def reverse(apps, schema_editor):
|
|
"""Reverse migration
|
|
|
|
Does nothing, but no harm in reverse migration.
|
|
"""
|
|
pass
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
dependencies = [
|
|
('review', '0026_repair_more_assignments'),
|
|
]
|
|
|
|
operations = [
|
|
migrations.RunPython(forward, reverse),
|
|
migrations.AddConstraint(
|
|
model_name='reviewersettings',
|
|
constraint=models.UniqueConstraint(fields=('team', 'person'), name='unique_reviewer_settings_per_team_person'),
|
|
),
|
|
]
|