From 190894725935d5a818e134855e71fd7c0e8eb365 Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Fri, 8 Dec 2023 17:51:44 -0500 Subject: [PATCH] Create squashed migrations for "api", "analytics", and "zarr" apps --- .../migrations/0001_initial_squashed.py | 48 + .../api/migrations/0001_initial_squashed.py | 823 ++++++++++++++++++ .../api/migrations/0001_stagingapplication.py | 103 +++ dandiapi/api/migrations/0002_asset_zarr.py | 53 ++ .../0003_default_oauth_application.py | 43 + .../zarr/migrations/0001_initial_squashed.py | 172 ++++ 6 files changed, 1242 insertions(+) create mode 100644 dandiapi/analytics/migrations/0001_initial_squashed.py create mode 100644 dandiapi/api/migrations/0001_initial_squashed.py create mode 100644 dandiapi/api/migrations/0001_stagingapplication.py create mode 100644 dandiapi/api/migrations/0002_asset_zarr.py create mode 100644 dandiapi/api/migrations/0003_default_oauth_application.py create mode 100644 dandiapi/zarr/migrations/0001_initial_squashed.py diff --git a/dandiapi/analytics/migrations/0001_initial_squashed.py b/dandiapi/analytics/migrations/0001_initial_squashed.py new file mode 100644 index 000000000..0e3669337 --- /dev/null +++ b/dandiapi/analytics/migrations/0001_initial_squashed.py @@ -0,0 +1,48 @@ +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + replaces = [ + ('analytics', '0001_initial'), + ('analytics', '0002_alter_processeds3log_unique_together_and_more'), + ] + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name='ProcessedS3Log', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'name', + models.CharField( + max_length=36, + validators=[ + django.core.validators.RegexValidator( + '^\\d{4}-(\\d{2}-){5}[A-F0-9]{16}$' + ) + ], + ), + ), + ('embargoed', models.BooleanField()), + ], + options={ + 'unique_together': set(), + }, + ), + migrations.AddConstraint( + model_name='processeds3log', + constraint=models.UniqueConstraint( + fields=('name', 'embargoed'), name='analytics_processeds3log_unique_name_embargoed' + ), + ), + ] diff --git a/dandiapi/api/migrations/0001_initial_squashed.py b/dandiapi/api/migrations/0001_initial_squashed.py new file mode 100644 index 000000000..8df164904 --- /dev/null +++ b/dandiapi/api/migrations/0001_initial_squashed.py @@ -0,0 +1,823 @@ +import uuid + +from django.conf import settings +import django.contrib.postgres.indexes +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django_extensions.db.fields +import oauth2_provider.generators + +import dandiapi.api.models.asset +import dandiapi.api.models.metadata +import dandiapi.api.storage + + +class Migration(migrations.Migration): + replaces = [ + ('api', '0001_initial'), + ('api', '0002_unique_asset_blob_etag_size'), + ('api', '0003_rename_uuids'), + ('api', '0004_generic_multipart_upload_id'), + ('api', '0005_version_doi'), + ('api', '0006_default_oauth_application'), + ('api', '0007_validation_status'), + ('api', '0008_version_validation_status'), + ('api', '0009_asset_published'), + ('api', '0010_blank_validation_errors'), + # 0011, 0014 omitted here, since it needs to run early + ('api', '0012_merge_20210630_1354'), + ('api', '0013_add_published_status'), + ('api', '0015_validation_fields'), + ('api', '0016_remove_metadata_tables'), + ('api', '0017_alter_version_version'), + ('api', '0018_alter_validation_errors'), + ('api', '0019_usermetadata'), + ('api', '0020_zarr'), + ('api', '0021_embargo_models'), + ('api', '0022_upload_dandiset_alter_stagingapplication_user'), + ('api', '0023_embargoedzarrarchive'), + ('api', '0024_biginteger_zarrarchives'), + ('api', '0025_zarrarchive_dandiset'), + ('api', '0026_alter_asset_id_alter_assetblob_id_alter_dandiset_id_and_more'), + ('api', '0027_embargoedzarrarchive_status_zarrarchive_status'), + ('api', '0028_alter_asset_previous'), + ('api', '0029_alter_usermetadata_user'), + ('api', '0030_alter_embargoedzarruploadfile_blob_and_more'), + ('api', '0031_asset_asset_metadata_has_schema_version_and_more'), + ('api', '0032_embargoedzarrarchive_checksum_zarrarchive_checksum_and_more'), + ('api', '0033_remove_embargoedzarrarchive_unique-embargo-dandiset-name_and_more'), + ('api', '0034_remove_embargoedzarrarchive_dandiset_and_more'), + ('api', '0035_alter_asset_zarr_delete_embargoedzarrarchive_and_more'), + ('api', '0036_dandisetuserobjectpermission_and_more'), + ('api', '0037_alter_version_status'), + ('api', '0038_assetpath_assetpathrelation_alter_asset_path_and_more'), + ('api', '0039_assetpath_consistent-leaf-paths'), + ('api', '0040_remove_assetpath_consistent-slash_and_more'), + ('api', '0041_assetblob_download_count_and_more'), + ('api', '0042_asset_remove_computed_fields'), + ('api', '0043_asset_asset_metadata_no_computed_keys_or_published'), + ('api', '0044_remove_embargoedupload_modified_and_more'), + ] + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='Asset', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ('asset_id', models.UUIDField(default=uuid.uuid4, unique=True)), + ( + 'path', + models.CharField( + max_length=512, validators=[dandiapi.api.models.asset.validate_asset_path] + ), + ), + ('metadata', models.JSONField(blank=True, default=dict)), + ( + 'status', + models.CharField( + choices=[ + ('Pending', 'Pending'), + ('Validating', 'Validating'), + ('Valid', 'Valid'), + ('Invalid', 'Invalid'), + ], + default='Pending', + max_length=10, + ), + ), + ('validation_errors', models.JSONField(blank=True, default=list, null=True)), + ('published', models.BooleanField(default=False)), + ], + bases=(dandiapi.api.models.metadata.PublishableMetadataMixin, models.Model), + ), + migrations.CreateModel( + name='AssetBlob', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ('blob_id', models.UUIDField(unique=True)), + ( + 'sha256', + models.CharField( + blank=True, + max_length=64, + null=True, + validators=[django.core.validators.RegexValidator('^[0-9a-f]{64}$')], + ), + ), + ( + 'etag', + models.CharField( + max_length=40, + validators=[ + django.core.validators.RegexValidator('^[0-9a-f]{32}(-[1-9][0-9]*)?$') + ], + ), + ), + ('size', models.PositiveBigIntegerField()), + ('download_count', models.PositiveBigIntegerField(default=0)), + ( + 'blob', + models.FileField( + blank=True, + storage=dandiapi.api.storage.get_storage, + upload_to=dandiapi.api.storage.get_storage_prefix, + ), + ), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='AssetPath', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ('path', models.CharField(max_length=512)), + ('aggregate_files', models.PositiveBigIntegerField(default=0)), + ('aggregate_size', models.PositiveBigIntegerField(default=0)), + ( + 'asset', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='leaf_paths', + to='api.asset', + ), + ), + ], + ), + migrations.CreateModel( + name='Dandiset', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ( + 'embargo_status', + models.CharField( + choices=[ + ('EMBARGOED', 'Embargoed'), + ('UNEMBARGOING', 'Unembargoing'), + ('OPEN', 'Open'), + ], + default='OPEN', + max_length=12, + ), + ), + ], + options={ + 'ordering': ['id'], + 'permissions': [('owner', 'Owns the dandiset')], + }, + ), + migrations.CreateModel( + name='Version', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ('name', models.CharField(max_length=300)), + ('metadata', models.JSONField(blank=True, default=dict)), + ( + 'version', + models.CharField( + max_length=13, + validators=[ + django.core.validators.RegexValidator('^(0\\.\\d{6}\\.\\d{4})|draft$') + ], + ), + ), + ('doi', models.CharField(blank=True, max_length=64, null=True)), + ( + 'status', + models.CharField( + choices=[ + ('Pending', 'Pending'), + ('Validating', 'Validating'), + ('Valid', 'Valid'), + ('Invalid', 'Invalid'), + ('Publishing', 'Publishing'), + ('Published', 'Published'), + ], + default='Pending', + max_length=10, + ), + ), + ('validation_errors', models.JSONField(blank=True, default=list, null=True)), + ( + 'dandiset', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='versions', + to='api.dandiset', + ), + ), + ], + bases=(dandiapi.api.models.metadata.PublishableMetadataMixin, models.Model), + ), + migrations.CreateModel( + name='UserMetadata', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ( + 'status', + models.CharField( + choices=[ + ('INCOMPLETE', 'Incomplete'), + ('PENDING', 'Pending'), + ('APPROVED', 'Approved'), + ('REJECTED', 'Rejected'), + ], + default='INCOMPLETE', + max_length=10, + ), + ), + ('questionnaire_form', models.JSONField(blank=True, null=True)), + ('rejection_reason', models.TextField(blank=True, default='', max_length=1000)), + ( + 'user', + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name='metadata', + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + 'get_latest_by': 'modified', + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Upload', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True)), + ('upload_id', models.UUIDField(db_index=True, default=uuid.uuid4, unique=True)), + ( + 'etag', + models.CharField( + blank=True, + db_index=True, + max_length=40, + null=True, + validators=[ + django.core.validators.RegexValidator('^[0-9a-f]{32}(-[1-9][0-9]*)?$') + ], + ), + ), + ( + 'multipart_upload_id', + models.CharField(db_index=True, max_length=128, unique=True), + ), + ('size', models.PositiveBigIntegerField()), + ( + 'blob', + models.FileField( + blank=True, + storage=dandiapi.api.storage.get_storage, + upload_to=dandiapi.api.storage.get_storage_prefix, + ), + ), + ( + 'dandiset', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='uploads', + to='api.dandiset', + ), + ), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='EmbargoedUpload', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True)), + ('upload_id', models.UUIDField(db_index=True, default=uuid.uuid4, unique=True)), + ( + 'etag', + models.CharField( + blank=True, + db_index=True, + max_length=40, + null=True, + validators=[ + django.core.validators.RegexValidator('^[0-9a-f]{32}(-[1-9][0-9]*)?$') + ], + ), + ), + ( + 'multipart_upload_id', + models.CharField(db_index=True, max_length=128, unique=True), + ), + ('size', models.PositiveBigIntegerField()), + ( + 'blob', + models.FileField( + blank=True, + storage=dandiapi.api.storage.get_embargo_storage, + upload_to=dandiapi.api.storage.get_embargo_storage_prefix, + ), + ), + ( + 'dandiset', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='embargoed_uploads', + to='api.dandiset', + ), + ), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='EmbargoedAssetBlob', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ('blob_id', models.UUIDField(unique=True)), + ( + 'sha256', + models.CharField( + blank=True, + max_length=64, + null=True, + validators=[django.core.validators.RegexValidator('^[0-9a-f]{64}$')], + ), + ), + ( + 'etag', + models.CharField( + max_length=40, + validators=[ + django.core.validators.RegexValidator('^[0-9a-f]{32}(-[1-9][0-9]*)?$') + ], + ), + ), + ('size', models.PositiveBigIntegerField()), + ('download_count', models.PositiveBigIntegerField(default=0)), + ( + 'blob', + models.FileField( + blank=True, + storage=dandiapi.api.storage.get_embargo_storage, + upload_to=dandiapi.api.storage.get_embargo_storage_prefix, + ), + ), + ( + 'dandiset', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='embargoed_asset_blobs', + to='api.dandiset', + ), + ), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='DandisetUserObjectPermission', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'content_object', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to='api.dandiset' + ), + ), + ( + 'permission', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to='auth.permission' + ), + ), + ( + 'user', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='DandisetGroupObjectPermission', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'content_object', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to='api.dandiset' + ), + ), + ( + 'group', + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.group'), + ), + ( + 'permission', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to='auth.permission' + ), + ), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='AssetPathRelation', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ('depth', models.PositiveIntegerField()), + ( + 'child', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='parent_links', + to='api.assetpath', + ), + ), + ( + 'parent', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='child_links', + to='api.assetpath', + ), + ), + ], + ), + migrations.AddField( + model_name='assetpath', + name='version', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='asset_paths', + to='api.version', + ), + ), + migrations.AddIndex( + model_name='assetblob', + index=django.contrib.postgres.indexes.HashIndex( + fields=['etag'], name='api_assetbl_etag_cf8377_hash' + ), + ), + migrations.AddConstraint( + model_name='assetblob', + constraint=models.UniqueConstraint(fields=('etag', 'size'), name='unique-etag-size'), + ), + migrations.AddField( + model_name='asset', + name='blob', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='assets', + to='api.assetblob', + ), + ), + migrations.AddField( + model_name='asset', + name='embargoed_blob', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='assets', + to='api.embargoedassetblob', + ), + ), + migrations.AddField( + model_name='asset', + name='previous', + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to='api.asset', + ), + ), + migrations.AddField( + model_name='asset', + name='versions', + field=models.ManyToManyField(related_name='assets', to='api.version'), + ), + migrations.AddIndex( + model_name='version', + index=django.contrib.postgres.indexes.HashIndex( + fields=['metadata'], name='api_version_metadat_f0b8f8_hash' + ), + ), + migrations.AddIndex( + model_name='version', + index=django.contrib.postgres.indexes.HashIndex( + fields=['name'], name='api_version_name_5d8af6_hash' + ), + ), + migrations.AddConstraint( + model_name='version', + constraint=models.CheckConstraint( + check=models.Q(('metadata__schemaVersion__isnull', False)), + name='version_metadata_has_schema_version', + ), + ), + migrations.AlterUniqueTogether( + name='version', + unique_together={('dandiset', 'version')}, + ), + migrations.AddIndex( + model_name='upload', + index=models.Index(fields=['etag'], name='api_upload_etag_a467fd_idx'), + ), + migrations.AddIndex( + model_name='embargoedupload', + index=models.Index(fields=['etag'], name='api_embargo_etag_064c86_idx'), + ), + migrations.AddIndex( + model_name='embargoedassetblob', + index=django.contrib.postgres.indexes.HashIndex( + fields=['etag'], name='api_embargo_etag_06a255_hash' + ), + ), + migrations.AddConstraint( + model_name='embargoedassetblob', + constraint=models.UniqueConstraint( + fields=('dandiset', 'etag', 'size'), name='unique-embargo-etag-size' + ), + ), + migrations.AlterUniqueTogether( + name='dandisetuserobjectpermission', + unique_together={('user', 'permission', 'content_object')}, + ), + migrations.AlterUniqueTogether( + name='dandisetgroupobjectpermission', + unique_together={('group', 'permission', 'content_object')}, + ), + migrations.AddConstraint( + model_name='assetpathrelation', + constraint=models.UniqueConstraint( + fields=('parent', 'child'), name='unique-relationship' + ), + ), + migrations.AddConstraint( + model_name='assetpath', + constraint=models.CheckConstraint( + check=models.Q( + models.Q(('path__endswith', '/'), ('path__startswith', '/'), _connector='OR'), + _negated=True, + ), + name='consistent-slash', + ), + ), + migrations.AddConstraint( + model_name='assetpath', + constraint=models.UniqueConstraint( + fields=('asset', 'version'), name='unique-asset-version' + ), + ), + migrations.AddConstraint( + model_name='assetpath', + constraint=models.UniqueConstraint( + fields=('version', 'path'), name='unique-version-path' + ), + ), + migrations.AddConstraint( + model_name='assetpath', + constraint=models.CheckConstraint( + check=models.Q( + ('asset__isnull', True), + models.Q(('aggregate_files__lte', 1), ('asset__isnull', False)), + _connector='OR', + ), + name='consistent-leaf-paths', + ), + ), + migrations.AddConstraint( + model_name='asset', + constraint=models.CheckConstraint( + check=models.Q( + models.Q(('blob__isnull', True), ('embargoed_blob__isnull', True)), + models.Q(('blob__isnull', True), ('embargoed_blob__isnull', False)), + models.Q(('blob__isnull', False), ('embargoed_blob__isnull', True)), + _connector='OR', + ), + name='exactly-one-blob', + ), + ), + migrations.AddConstraint( + model_name='asset', + constraint=models.CheckConstraint( + check=models.Q(('metadata__schemaVersion__isnull', False)), + name='asset_metadata_has_schema_version', + ), + ), + migrations.AddConstraint( + model_name='asset', + constraint=models.CheckConstraint( + check=models.Q( + ('path__regex', '^([A-z0-9(),&\\s#+~_=-]?\\/?\\.?[A-z0-9(),&\\s#+~_=-])+$') + ), + name='asset_path_regex', + ), + ), + migrations.AddConstraint( + model_name='asset', + constraint=models.CheckConstraint( + check=models.Q(('path__startswith', '/'), _negated=True), + name='asset_path_no_leading_slash', + ), + ), + migrations.AddConstraint( + model_name='asset', + constraint=models.CheckConstraint( + check=models.Q( + models.Q( + ('published', False), + models.Q( + ( + 'metadata__has_any_keys', + [ + 'id', + 'path', + 'identifier', + 'contentUrl', + 'contentSize', + 'digest', + 'datePublished', + 'publishedBy', + ], + ), + _negated=True, + ), + ), + models.Q( + ('published', True), + ( + 'metadata__has_keys', + [ + 'id', + 'path', + 'identifier', + 'contentUrl', + 'contentSize', + 'digest', + 'datePublished', + 'publishedBy', + ], + ), + ), + _connector='OR', + ), + name='asset_metadata_no_computed_keys_or_published', + ), + ), + ] diff --git a/dandiapi/api/migrations/0001_stagingapplication.py b/dandiapi/api/migrations/0001_stagingapplication.py new file mode 100644 index 000000000..f08a20e04 --- /dev/null +++ b/dandiapi/api/migrations/0001_stagingapplication.py @@ -0,0 +1,103 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import oauth2_provider.generators + +# This migration is needed to support wildcards in OAuth redirect URIs in staging. +# The model created here is only actually used in staging. + + +class Migration(migrations.Migration): + replaces = [ + ('api', '0011_stagingapplication'), + ('api', '0014_alter_stagingapplication_skip_authorization'), + ] + + # This has no dependencies, but isn't considered "inital" by Django's definition + initial = False + + # This is creating a swappable model, so it needs to exist before the OAuth2 models are created + run_before = [ + ('oauth2_provider', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='StagingApplication', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ( + 'client_id', + models.CharField( + db_index=True, + default=oauth2_provider.generators.generate_client_id, + max_length=100, + unique=True, + ), + ), + ( + 'redirect_uris', + models.TextField(blank=True, help_text='Allowed URIs list, space separated'), + ), + ( + 'client_type', + models.CharField( + choices=[('confidential', 'Confidential'), ('public', 'Public')], + max_length=32, + ), + ), + ( + 'authorization_grant_type', + models.CharField( + choices=[ + ('authorization-code', 'Authorization code'), + ('implicit', 'Implicit'), + ('password', 'Resource owner password-based'), + ('client-credentials', 'Client credentials'), + ('openid-hybrid', 'OpenID connect hybrid'), + ], + max_length=32, + ), + ), + ( + 'client_secret', + models.CharField( + blank=True, + db_index=True, + default=oauth2_provider.generators.generate_client_secret, + max_length=255, + ), + ), + ('name', models.CharField(blank=True, max_length=255)), + ('skip_authorization', models.BooleanField(default=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ( + 'algorithm', + models.CharField( + blank=True, + choices=[ + ('', 'No OIDC support'), + ('RS256', 'RSA with SHA-2 256'), + ('HS256', 'HMAC with SHA-2 256'), + ], + default='', + max_length=5, + ), + ), + ( + 'user', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='%(app_label)s_%(class)s', + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/dandiapi/api/migrations/0002_asset_zarr.py b/dandiapi/api/migrations/0002_asset_zarr.py new file mode 100644 index 000000000..b17d0fe9a --- /dev/null +++ b/dandiapi/api/migrations/0002_asset_zarr.py @@ -0,0 +1,53 @@ +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0001_initial_squashed'), + ('zarr', '0001_initial_squashed'), + # Merge the orphan api.0001_stagingapplication + ('api', '0001_stagingapplication'), + ] + + operations = [ + migrations.RemoveConstraint( + model_name='asset', + name='exactly-one-blob', + ), + migrations.AddField( + model_name='asset', + name='zarr', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='assets', + to='zarr.zarrarchive', + ), + ), + migrations.AddConstraint( + model_name='asset', + constraint=models.CheckConstraint( + check=models.Q( + models.Q( + ('blob__isnull', True), + ('embargoed_blob__isnull', True), + ('zarr__isnull', False), + ), + models.Q( + ('blob__isnull', True), + ('embargoed_blob__isnull', False), + ('zarr__isnull', True), + ), + models.Q( + ('blob__isnull', False), + ('embargoed_blob__isnull', True), + ('zarr__isnull', True), + ), + _connector='OR', + ), + name='exactly-one-blob', + ), + ), + ] diff --git a/dandiapi/api/migrations/0003_default_oauth_application.py b/dandiapi/api/migrations/0003_default_oauth_application.py new file mode 100644 index 000000000..be80ff17f --- /dev/null +++ b/dandiapi/api/migrations/0003_default_oauth_application.py @@ -0,0 +1,43 @@ +from django.db import migrations +from django.db.models import Q + + +def create_application(apps, schema_editor): + Application = apps.get_model('oauth2_provider', 'Application') + name = 'DANDI GUI' + # This is specified so it matches the default value used in the frontend. + client_id = 'Dk0zosgt1GAAKfN8LT4STJmLJXwMDPbYWYzfNtAl' + if not Application.objects.filter(Q(name=name) | Q(client_id=client_id)).exists(): + application = Application( + # Production instances should change this. + client_id='Dk0zosgt1GAAKfN8LT4STJmLJXwMDPbYWYzfNtAl', + # Production instances must change this. + redirect_uris='http://localhost:8085/', + # These values should not be modified. + client_type='public', + authorization_grant_type='authorization-code', + client_secret='', + name=name, + # This can be turned off in production if appropriate. + skip_authorization=True, + ) + application.save() + + +def reverse_create_application(apps, schema_editor): + # Deleting the application would destroy any customization applied to it. + # Explicitly do nothing so the migration is still nominally reversible. + pass + + +class Migration(migrations.Migration): + replaces = [ + # ('api', '0006_default_oauth_application'), + ] + + dependencies = [ + ('api', '0002_asset_zarr'), + ('oauth2_provider', '0003_auto_20201211_1314'), + ] + + operations = [migrations.RunPython(create_application, reverse_create_application)] diff --git a/dandiapi/zarr/migrations/0001_initial_squashed.py b/dandiapi/zarr/migrations/0001_initial_squashed.py new file mode 100644 index 000000000..d39a65a4e --- /dev/null +++ b/dandiapi/zarr/migrations/0001_initial_squashed.py @@ -0,0 +1,172 @@ +import uuid + +from django.db import migrations, models +import django.db.models.deletion +import django_extensions.db.fields + + +class Migration(migrations.Migration): + replaces = [ + ('zarr', '0001_initial'), + ('zarr', '0002_remove_zarruploadfile_zarr_archive_and_more'), + ('zarr', '0003_zarr_rename_tables_indexes_constraints'), + ( + 'zarr', + '0004_remove_embargoedzarrarchive_zarr-embargoedzarrarchive-consistent-checksum-status_and_more', + ), + ] + + initial = True + + dependencies = [ + ('api', '0001_initial_squashed'), + ] + + operations = [ + migrations.CreateModel( + name='ZarrArchive', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ('zarr_id', models.UUIDField(db_index=True, default=uuid.uuid4, unique=True)), + ('name', models.CharField(max_length=512)), + ('file_count', models.BigIntegerField(default=0)), + ('size', models.BigIntegerField(default=0)), + ('checksum', models.CharField(default=None, max_length=512, null=True)), + ( + 'status', + models.CharField( + choices=[ + ('Pending', 'Pending'), + ('Uploaded', 'Uploaded'), + ('Ingesting', 'Ingesting'), + ('Complete', 'Complete'), + ], + default='Pending', + max_length=9, + ), + ), + ( + 'dandiset', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='zarr_archives', + to='api.dandiset', + ), + ), + ], + options={ + 'get_latest_by': 'modified', + 'abstract': False, + }, + ), + migrations.CreateModel( + name='EmbargoedZarrArchive', + fields=[ + ( + 'id', + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name='ID' + ), + ), + ( + 'created', + django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created' + ), + ), + ( + 'modified', + django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified' + ), + ), + ('zarr_id', models.UUIDField(db_index=True, default=uuid.uuid4, unique=True)), + ('name', models.CharField(max_length=512)), + ('file_count', models.BigIntegerField(default=0)), + ('size', models.BigIntegerField(default=0)), + ('checksum', models.CharField(default=None, max_length=512, null=True)), + ( + 'status', + models.CharField( + choices=[ + ('Pending', 'Pending'), + ('Uploaded', 'Uploaded'), + ('Ingesting', 'Ingesting'), + ('Complete', 'Complete'), + ], + default='Pending', + max_length=9, + ), + ), + ( + 'dandiset', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='embargoed_zarr_archives', + to='api.dandiset', + ), + ), + ], + options={ + 'get_latest_by': 'modified', + 'abstract': False, + }, + ), + migrations.AddConstraint( + model_name='zarrarchive', + constraint=models.UniqueConstraint( + fields=('dandiset', 'name'), name='zarr-zarrarchive-unique-name' + ), + ), + migrations.AddConstraint( + model_name='zarrarchive', + constraint=models.CheckConstraint( + check=models.Q( + models.Q( + ('checksum__isnull', True), + ('status__in', ['Pending', 'Uploaded', 'Ingesting']), + ), + models.Q(('checksum__isnull', False), ('status', 'Complete')), + _connector='OR', + ), + name='zarr-zarrarchive-consistent-checksum-status', + ), + ), + migrations.AddConstraint( + model_name='embargoedzarrarchive', + constraint=models.UniqueConstraint( + fields=('dandiset', 'name'), name='zarr-embargoedzarrarchive-unique-name' + ), + ), + migrations.AddConstraint( + model_name='embargoedzarrarchive', + constraint=models.CheckConstraint( + check=models.Q( + models.Q( + ('checksum__isnull', True), + ('status__in', ['Pending', 'Uploaded', 'Ingesting']), + ), + models.Q(('checksum__isnull', False), ('status', 'Complete')), + _connector='OR', + ), + name='zarr-embargoedzarrarchive-consistent-checksum-status', + ), + ), + ]