|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -from dandischema import migrate |
| 3 | +import logging |
| 4 | + |
| 5 | +from dandischema.consts import DANDI_SCHEMA_VERSION |
| 6 | +from dandischema.metadata import migrate |
| 7 | +from django.db import transaction |
4 | 8 | import djclick as click |
5 | 9 |
|
6 | 10 | from dandiapi.api.models import Version |
| 11 | +from dandiapi.api.services import audit |
| 12 | +from dandiapi.api.services.metadata import validate_version_metadata |
| 13 | + |
| 14 | +logger = logging.getLogger(__name__) |
7 | 15 |
|
8 | 16 |
|
9 | 17 | @click.command() |
10 | | -@click.argument('to_version') |
11 | | -def migrate_version_metadata(*, to_version: str): |
12 | | - click.echo(f'Migrating all version metadata to version {to_version}') |
13 | | - for version in Version.objects.filter(version='draft'): |
14 | | - click.echo(f'Migrating {version.dandiset.identifier}/{version.version}') |
15 | | - |
16 | | - metadata = version.metadata |
17 | | - |
18 | | - try: |
19 | | - metanew = migrate(metadata, to_version=to_version, skip_validation=True) |
20 | | - except Exception as e: # noqa: BLE001 |
21 | | - click.echo(f'Failed to migrate {version.dandiset.identifier}/{version.version}') |
22 | | - click.echo(e) |
23 | | - continue |
24 | | - |
25 | | - if version.metadata != metanew: |
| 18 | +@click.argument( |
| 19 | + 'dandisets', |
| 20 | + type=click.INT, |
| 21 | + nargs=-1, |
| 22 | +) |
| 23 | +@click.option( |
| 24 | + '-a', |
| 25 | + '--all', |
| 26 | + 'include_all', |
| 27 | + is_flag=True, |
| 28 | + help='Run on all dandisets.', |
| 29 | +) |
| 30 | +def migrate_version_metadata(dandisets: tuple[int, ...], *, include_all: bool): |
| 31 | + if bool(dandisets) == include_all: |
| 32 | + raise click.ClickException("Must specify exactly one of 'dandisets' or --all") |
| 33 | + |
| 34 | + versions = Version.objects.filter(version='draft') |
| 35 | + if dandisets: |
| 36 | + versions = versions.filter(dandiset_id__in=dandisets) |
| 37 | + |
| 38 | + logger.info( |
| 39 | + 'Migrating %s dandiset draft versions to schema version %s', |
| 40 | + versions.count(), |
| 41 | + DANDI_SCHEMA_VERSION, |
| 42 | + ) |
| 43 | + |
| 44 | + migrated_count = 0 |
| 45 | + failed_count = 0 |
| 46 | + unchanged_count = 0 |
| 47 | + for version in versions.iterator(): |
| 48 | + logger.info('-----------------------------------------') |
| 49 | + logger.info('Migrating %s', version) |
| 50 | + |
| 51 | + with transaction.atomic(): |
| 52 | + locked_version = Version.objects.select_for_update().get(id=version.id) |
| 53 | + |
| 54 | + try: |
| 55 | + metanew = migrate(locked_version.metadata, skip_validation=True) |
| 56 | + except Exception as e: |
| 57 | + logger.exception('Failed to migrate %s', version, exc_info=e) |
| 58 | + failed_count += 1 |
| 59 | + continue |
| 60 | + |
| 61 | + if locked_version.metadata == metanew: |
| 62 | + logger.info('No change in metadata for %s. Skipping save...', version) |
| 63 | + unchanged_count += 1 |
| 64 | + continue |
| 65 | + |
26 | 66 | version.metadata = metanew |
27 | 67 | version.status = Version.Status.PENDING |
28 | 68 | version.save() |
| 69 | + |
| 70 | + audit.update_metadata( |
| 71 | + dandiset=locked_version.dandiset, |
| 72 | + metadata=locked_version.metadata, |
| 73 | + user=None, |
| 74 | + admin=True, |
| 75 | + description=f'Update schema version to {DANDI_SCHEMA_VERSION}', |
| 76 | + ) |
| 77 | + |
| 78 | + migrated_count += 1 |
| 79 | + logger.info('Metadata migrated for version %s', version) |
| 80 | + |
| 81 | + # Validate outside of transaction, since this function uses `select_for_update` itself |
| 82 | + validate_version_metadata(version=version) |
| 83 | + |
| 84 | + logger.info( |
| 85 | + '%d migrated, %d failed, %d left unchanged, out of %d total selected versions', |
| 86 | + migrated_count, |
| 87 | + failed_count, |
| 88 | + unchanged_count, |
| 89 | + versions.count(), |
| 90 | + ) |
0 commit comments