from django.db import migrations
|
from copy import deepcopy
|
from django.apps import apps as django_apps
|
from django.conf import settings
|
from core.models import AsyncMigrationStatus
|
from core.redis import start_job_async_or_sync
|
from core.utils.iterators import iterate_queryset
|
import logging
|
|
migration_name = '0017_update_agreement_selected_to_nested_structure'
|
|
logger = logging.getLogger(__name__)
|
|
|
def forward_migration(db_alias):
|
"""
|
Migrates views that have agreement_selected populated to the new structure
|
|
Old structure:
|
'agreement_selected': {
|
'annotators': List[int]
|
'models': List[str]
|
'ground_truth': bool
|
}
|
|
New structure:
|
'agreement_selected': {
|
'annotators': {
|
'all': bool
|
'ids': List[int]
|
},
|
'models': {
|
'all': bool
|
'ids': List[str]
|
},
|
'ground_truth': bool
|
}
|
"""
|
migration, created = AsyncMigrationStatus.objects.using(db_alias).get_or_create(
|
name=migration_name,
|
defaults={'status': AsyncMigrationStatus.STATUS_STARTED}
|
)
|
if not created:
|
return # already in progress or done
|
|
# Look up models at runtime inside the worker process
|
View = django_apps.get_model('data_manager', 'View')
|
|
# Iterate using values() to avoid loading full model instances
|
# Fetch only the fields we need, filtering to views that have 'agreement_selected' in data
|
qs = (
|
View.objects.using(db_alias)
|
.filter(data__has_key='agreement_selected')
|
.filter(data__agreement_selected__isnull=False)
|
.values('id', 'data')
|
)
|
|
updated = 0
|
for row in qs:
|
view_id = row['id']
|
data = row.get('data') or {}
|
|
new_data = deepcopy(data)
|
# Always use the new nested structure
|
new_data['agreement_selected'] = {
|
'annotators': {'all': True, 'ids': []},
|
'models': {'all': True, 'ids': []},
|
'ground_truth': False
|
}
|
|
# Update only the JSON field via update(); do not load model instance or call save()
|
View.objects.using(db_alias).filter(id=view_id).update(data=new_data)
|
logger.info(f'Updated View {view_id} agreement selected to default all annotators + all models')
|
updated += 1
|
|
if updated:
|
logger.info(f'{migration_name} Updated {updated} View rows')
|
|
migration.status = AsyncMigrationStatus.STATUS_FINISHED
|
migration.save(update_fields=['status'], using=db_alias)
|
|
def forwards(apps, schema_editor):
|
db_alias = schema_editor.connection.alias
|
start_job_async_or_sync(forward_migration, db_alias=db_alias, queue_name=settings.SERVICE_QUEUE_NAME)
|
|
|
def backwards(apps, schema_editor):
|
# Irreversible: we cannot reconstruct the previous annotator lists safely
|
pass
|
|
|
class Migration(migrations.Migration):
|
atomic = False
|
|
dependencies = [
|
('data_manager', '0016_migrate_agreement_selected_annotators_to_unique')
|
]
|
|
operations = [
|
migrations.RunPython(forwards, backwards),
|
]
|