From 66efc21f5b78637612086ca2bed59fc4e27b5a1b Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 11 Sep 2026 11:28:55 +0530 Subject: [PATCH] fix: commit 46 missing api/authorization migrations, was gitignored Blocked the production deploy's release step: `search_index --populate` crashed with `relation "publication" does not exist`. Root cause traced much further than expected. .gitignore had `api/migrations/*` and `authorization/migrations/*` -- every migration generated for either app since whenever that was added has been silently invisible to git, no matter how many times someone ran `git add`. This repo's `api/migrations/` only ever had `0001_initial.py` committed (likely force-added once, or predates the ignore rule); `authorization/migrations/` had nothing at all, not even `__init__.py`. Confirmed via git log this wasn't always broken: `30480c6 "reset migrations"` deliberately squashed history down to one clean `0001_initial.py` at some point (a legitimate, intentional cleanup, probably around a Postgres migration). But nothing was ever committed again afterward -- every migration generated since (via normal `makemigrations` + `git add`, silently swallowed by the ignore rule) only ever existed on whichever host it was generated on. dev-cds's live checkout had the full, real, working set: 46 files for `api` (0002-0047, ending in the Publication/ResourceType migration this deploy needed) and all 5 for `authorization` (0001-0004 + __init__.py, defining `authorization.User` itself -- AUTH_USER_MODEL). Pulled all 51 files directly from dev-cds (read-only), verified: - Every file compiles clean (`py_compile`). - The full api dependency chain is linear and unbroken, 0001 -> 0047, no gaps, no branches. - authorization's 4 files chain cleanly from 0001, which itself correctly depends on api/0023 (a real cross-app FK relationship) -- confirms the two apps' histories are genuinely interleaved and consistent, not independently drifted. - No overlap with the deliberately-excluded fetch_resource_data feature from the earlier dev/main reconciliation (PR #153) -- the two incidental substring matches found were unrelated migration filenames. Removed both ignore rules. Django migrations must always be committed; there is no legitimate reason to exclude them. This also means any genuinely fresh database (a new contributor's local setup, a CI job with --create-db, a disaster-recovery rebuild) has been silently unable to reach current schema this whole time -- would have stopped dead at whatever 0001_initial.py defines. That risk is now closed. --- .gitignore | 2 - .../0002_dataspace_dataset_dataspace.py | 34 + api/migrations/0003_usecase.py | 29 + api/migrations/0004_usecase_status.py | 18 + api/migrations/0005_alter_usecase_slug.py | 18 + ...usecase_description_alter_usecase_title.py | 23 + api/migrations/0007_resourcechartimage.py | 24 + ...ter_resourcechartdetails_aggregate_type.py | 18 + ...ter_resourcechartdetails_aggregate_type.py | 18 + ..._resourcechartdetails_modified_and_more.py | 29 + .../0011_alter_resourceschema_format.py | 18 + .../0012_resourcechartdetails_filters.py | 18 + ...rcechartdetails_aggregate_type_and_more.py | 61 ++ ..._options_alter_dataset_options_and_more.py | 263 ++++++ .../0015_dataset_slug_resource_slug.py | 68 ++ ..._alter_dataset_slug_alter_resource_slug.py | 23 + api/migrations/0017_resourcedatatable.py | 29 + .../0018_resource_download_count.py | 18 + ...ry_sector_alter_sector_options_and_more.py | 30 + ...eated_datasetmetadata_modified_and_more.py | 120 +++ ...me_description_usecase_summary_and_more.py | 33 + .../0022_resource_version_resourceversion.py | 49 ++ api/migrations/0023_alter_usecase_summary.py | 18 + ...4_alter_organization_organization_types.py | 42 + ...usecase_completed_on_usecase_started_on.py | 23 + api/migrations/0026_usecase_contributors.py | 24 + ...secaseorganizationrelationship_and_more.py | 66 ++ ...028_dataset_access_type_dataset_license.py | 47 ++ api/migrations/0029_dataset_user.py | 27 + ...30_organization_github_profile_and_more.py | 33 + ...r_alter_resourcechartdetails_chart_type.py | 46 ++ ...32_resourcechartdetails_status_and_more.py | 39 + api/migrations/0033_usecase_organization.py | 24 + api/migrations/0034_usecasedashboard.py | 36 + api/migrations/0035_usecase_platform_url.py | 18 + ...eographies_usecase_geographies_and_more.py | 746 ++++++++++++++++++ ...l_status_aimodelversion_versionprovider.py | 233 ++++++ .../0038_aimodelversion_lifecycle_stage.py | 31 + ...nprovider_api_auth_header_name_and_more.py | 143 ++++ ...0040_promptdataset_dataset_dataset_type.py | 145 ++++ ...041_alter_promptdataset_domain_and_more.py | 67 ++ .../0042_promptresource_and_more.py | 106 +++ api/migrations/0043_promptdataset_purpose.py | 29 + ..._schema_alter_aimodel_metadata_and_more.py | 50 ++ api/migrations/0045_aimodel_domain.py | 39 + .../0046_alter_collaborative_slug.py | 30 + ...ion_collaborative_publications_and_more.py | 226 ++++++ authorization/migrations/0001_initial.py | 261 ++++++ .../migrations/0002_alter_user_table.py | 17 + ..._profile_user_linkedin_profile_and_more.py | 43 + authorization/migrations/0004_userconsent.py | 50 ++ authorization/migrations/__init__.py | 0 52 files changed, 3600 insertions(+), 2 deletions(-) create mode 100755 api/migrations/0002_dataspace_dataset_dataspace.py create mode 100755 api/migrations/0003_usecase.py create mode 100755 api/migrations/0004_usecase_status.py create mode 100755 api/migrations/0005_alter_usecase_slug.py create mode 100755 api/migrations/0006_alter_usecase_description_alter_usecase_title.py create mode 100755 api/migrations/0007_resourcechartimage.py create mode 100755 api/migrations/0008_alter_resourcechartdetails_aggregate_type.py create mode 100755 api/migrations/0009_alter_resourcechartdetails_aggregate_type.py create mode 100755 api/migrations/0010_resourcechartdetails_modified_and_more.py create mode 100755 api/migrations/0011_alter_resourceschema_format.py create mode 100755 api/migrations/0012_resourcechartdetails_filters.py create mode 100755 api/migrations/0013_remove_resourcechartdetails_aggregate_type_and_more.py create mode 100755 api/migrations/0014_alter_category_options_alter_dataset_options_and_more.py create mode 100755 api/migrations/0015_dataset_slug_resource_slug.py create mode 100755 api/migrations/0016_alter_dataset_slug_alter_resource_slug.py create mode 100755 api/migrations/0017_resourcedatatable.py create mode 100755 api/migrations/0018_resource_download_count.py create mode 100755 api/migrations/0019_rename_category_sector_alter_sector_options_and_more.py create mode 100755 api/migrations/0020_datasetmetadata_created_datasetmetadata_modified_and_more.py create mode 100755 api/migrations/0021_rename_description_usecase_summary_and_more.py create mode 100755 api/migrations/0022_resource_version_resourceversion.py create mode 100755 api/migrations/0023_alter_usecase_summary.py create mode 100755 api/migrations/0024_alter_organization_organization_types.py create mode 100755 api/migrations/0025_usecase_completed_on_usecase_started_on.py create mode 100755 api/migrations/0026_usecase_contributors.py create mode 100755 api/migrations/0027_usecaseorganizationrelationship_and_more.py create mode 100755 api/migrations/0028_dataset_access_type_dataset_license.py create mode 100755 api/migrations/0029_dataset_user.py create mode 100755 api/migrations/0030_organization_github_profile_and_more.py create mode 100755 api/migrations/0031_usecase_user_alter_resourcechartdetails_chart_type.py create mode 100755 api/migrations/0032_resourcechartdetails_status_and_more.py create mode 100755 api/migrations/0033_usecase_organization.py create mode 100755 api/migrations/0034_usecasedashboard.py create mode 100755 api/migrations/0035_usecase_platform_url.py create mode 100755 api/migrations/0036_sdg_dataset_geographies_usecase_geographies_and_more.py create mode 100755 api/migrations/0037_alter_aimodel_status_aimodelversion_versionprovider.py create mode 100755 api/migrations/0038_aimodelversion_lifecycle_stage.py create mode 100755 api/migrations/0039_versionprovider_api_auth_header_name_and_more.py create mode 100755 api/migrations/0040_promptdataset_dataset_dataset_type.py create mode 100755 api/migrations/0041_alter_promptdataset_domain_and_more.py create mode 100755 api/migrations/0042_promptresource_and_more.py create mode 100755 api/migrations/0043_promptdataset_purpose.py create mode 100755 api/migrations/0044_alter_aimodel_input_schema_alter_aimodel_metadata_and_more.py create mode 100755 api/migrations/0045_aimodel_domain.py create mode 100755 api/migrations/0046_alter_collaborative_slug.py create mode 100644 api/migrations/0047_resourcetype_publication_collaborative_publications_and_more.py create mode 100644 authorization/migrations/0001_initial.py create mode 100644 authorization/migrations/0002_alter_user_table.py create mode 100644 authorization/migrations/0003_user_github_profile_user_linkedin_profile_and_more.py create mode 100644 authorization/migrations/0004_userconsent.py create mode 100644 authorization/migrations/__init__.py diff --git a/.gitignore b/.gitignore index 847c7a01..68a4bcee 100644 --- a/.gitignore +++ b/.gitignore @@ -155,8 +155,6 @@ cython_debug/ #local env file .env -api/migrations/* -authorization/migrations/* # AWS files diff --git a/api/migrations/0002_dataspace_dataset_dataspace.py b/api/migrations/0002_dataspace_dataset_dataspace.py new file mode 100755 index 00000000..f631925f --- /dev/null +++ b/api/migrations/0002_dataspace_dataset_dataspace.py @@ -0,0 +1,34 @@ +# Generated by Django 5.0.4 on 2024-10-17 11:47 + +import api.utils.file_paths +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='DataSpace', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('description', models.CharField(max_length=1000)), + ('logo', models.ImageField(blank=True, null=True, upload_to=api.utils.file_paths._dataspace_directory_path)), + ('created', models.DateTimeField(auto_now_add=True)), + ('modified', models.DateTimeField(auto_now=True)), + ('homepage', models.URLField(blank=True)), + ('contact_email', models.EmailField(blank=True, max_length=254, null=True)), + ('slug', models.SlugField(max_length=75, null=True, unique=True)), + ], + ), + migrations.AddField( + model_name='dataset', + name='dataspace', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.dataspace'), + ), + ] diff --git a/api/migrations/0003_usecase.py b/api/migrations/0003_usecase.py new file mode 100755 index 00000000..db9434ce --- /dev/null +++ b/api/migrations/0003_usecase.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.4 on 2024-10-17 18:09 + +import api.utils.file_paths +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0002_dataspace_dataset_dataspace'), + ] + + operations = [ + migrations.CreateModel( + name='UseCase', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200, unique=True)), + ('description', models.CharField(max_length=1000)), + ('logo', models.ImageField(blank=True, null=True, upload_to=api.utils.file_paths._use_case_directory_path)), + ('created', models.DateTimeField(auto_now_add=True)), + ('modified', models.DateTimeField(auto_now=True)), + ('website', models.URLField(blank=True)), + ('contact_email', models.EmailField(blank=True, max_length=254, null=True)), + ('slug', models.SlugField(max_length=75, null=True, unique=True)), + ('datasets', models.ManyToManyField(blank=True, to='api.dataset')), + ], + ), + ] diff --git a/api/migrations/0004_usecase_status.py b/api/migrations/0004_usecase_status.py new file mode 100755 index 00000000..7b5735d0 --- /dev/null +++ b/api/migrations/0004_usecase_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-10-17 18:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0003_usecase'), + ] + + operations = [ + migrations.AddField( + model_name='usecase', + name='status', + field=models.CharField(choices=[('DRAFT', 'Draft'), ('PUBLISHED', 'Published'), ('ARCHIVED', 'Archived')], default='DRAFT', max_length=50), + ), + ] diff --git a/api/migrations/0005_alter_usecase_slug.py b/api/migrations/0005_alter_usecase_slug.py new file mode 100755 index 00000000..7cff027b --- /dev/null +++ b/api/migrations/0005_alter_usecase_slug.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-10-18 09:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0004_usecase_status'), + ] + + operations = [ + migrations.AlterField( + model_name='usecase', + name='slug', + field=models.SlugField(blank=True, max_length=75, null=True, unique=True), + ), + ] diff --git a/api/migrations/0006_alter_usecase_description_alter_usecase_title.py b/api/migrations/0006_alter_usecase_description_alter_usecase_title.py new file mode 100755 index 00000000..5a212798 --- /dev/null +++ b/api/migrations/0006_alter_usecase_description_alter_usecase_title.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.4 on 2024-10-21 05:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0005_alter_usecase_slug'), + ] + + operations = [ + migrations.AlterField( + model_name='usecase', + name='description', + field=models.CharField(blank=True, max_length=1000, null=True), + ), + migrations.AlterField( + model_name='usecase', + name='title', + field=models.CharField(blank=True, max_length=200, null=True, unique=True), + ), + ] diff --git a/api/migrations/0007_resourcechartimage.py b/api/migrations/0007_resourcechartimage.py new file mode 100755 index 00000000..cdc31de5 --- /dev/null +++ b/api/migrations/0007_resourcechartimage.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.4 on 2024-10-22 12:41 + +import api.utils.file_paths +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0006_alter_usecase_description_alter_usecase_title'), + ] + + operations = [ + migrations.CreateModel( + name='ResourceChartImage', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(blank=True, max_length=50)), + ('description', models.CharField(blank=True, max_length=1000)), + ('image', models.ImageField(blank=True, null=True, upload_to=api.utils.file_paths._chart_image_directory_path)), + ], + ), + ] diff --git a/api/migrations/0008_alter_resourcechartdetails_aggregate_type.py b/api/migrations/0008_alter_resourcechartdetails_aggregate_type.py new file mode 100755 index 00000000..9771abdb --- /dev/null +++ b/api/migrations/0008_alter_resourcechartdetails_aggregate_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-10-23 09:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0007_resourcechartimage'), + ] + + operations = [ + migrations.AlterField( + model_name='resourcechartdetails', + name='aggregate_type', + field=models.CharField(choices=[('NONE', 'None'), ('SUM', 'Sum'), ('AVG', 'Average'), ('COUNT', 'Count')], default='NONE', max_length=50), + ), + ] diff --git a/api/migrations/0009_alter_resourcechartdetails_aggregate_type.py b/api/migrations/0009_alter_resourcechartdetails_aggregate_type.py new file mode 100755 index 00000000..9b0a2e48 --- /dev/null +++ b/api/migrations/0009_alter_resourcechartdetails_aggregate_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-10-23 10:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0008_alter_resourcechartdetails_aggregate_type'), + ] + + operations = [ + migrations.AlterField( + model_name='resourcechartdetails', + name='aggregate_type', + field=models.CharField(choices=[('NONE', 'None'), ('SUM', 'Sum'), ('MEAN', 'Average'), ('COUNT', 'Count')], default='NONE', max_length=50), + ), + ] diff --git a/api/migrations/0010_resourcechartdetails_modified_and_more.py b/api/migrations/0010_resourcechartdetails_modified_and_more.py new file mode 100755 index 00000000..f3b93536 --- /dev/null +++ b/api/migrations/0010_resourcechartdetails_modified_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.4 on 2024-10-23 11:00 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0009_alter_resourcechartdetails_aggregate_type'), + ] + + operations = [ + migrations.AddField( + model_name='resourcechartdetails', + name='modified', + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name='resourcechartimage', + name='dataset', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='chart_images', to='api.dataset'), + ), + migrations.AddField( + model_name='resourcechartimage', + name='modified', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/api/migrations/0011_alter_resourceschema_format.py b/api/migrations/0011_alter_resourceschema_format.py new file mode 100755 index 00000000..6e4f46ef --- /dev/null +++ b/api/migrations/0011_alter_resourceschema_format.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-10-24 06:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0010_resourcechartdetails_modified_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='resourceschema', + name='format', + field=models.CharField(choices=[('STRING', 'String'), ('NUMBER', 'Number'), ('INTEGER', 'Integer'), ('DATE', 'Date'), ('BOOLEAN', 'Boolean')], max_length=255), + ), + ] diff --git a/api/migrations/0012_resourcechartdetails_filters.py b/api/migrations/0012_resourcechartdetails_filters.py new file mode 100755 index 00000000..d8580d96 --- /dev/null +++ b/api/migrations/0012_resourcechartdetails_filters.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-12-14 16:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0011_alter_resourceschema_format'), + ] + + operations = [ + migrations.AddField( + model_name='resourcechartdetails', + name='filters', + field=models.JSONField(blank=True, default=list), + ), + ] diff --git a/api/migrations/0013_remove_resourcechartdetails_aggregate_type_and_more.py b/api/migrations/0013_remove_resourcechartdetails_aggregate_type_and_more.py new file mode 100755 index 00000000..50d150c2 --- /dev/null +++ b/api/migrations/0013_remove_resourcechartdetails_aggregate_type_and_more.py @@ -0,0 +1,61 @@ +# Generated by Django 5.0.4 on 2025-02-26 14:07 + +import api.models.SerializableJSONField +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0012_resourcechartdetails_filters'), + ] + + operations = [ + migrations.RemoveField( + model_name='resourcechartdetails', + name='aggregate_type', + ), + migrations.RemoveField( + model_name='resourcechartdetails', + name='region_column', + ), + migrations.RemoveField( + model_name='resourcechartdetails', + name='show_legend', + ), + migrations.RemoveField( + model_name='resourcechartdetails', + name='value_column', + ), + migrations.RemoveField( + model_name='resourcechartdetails', + name='x_axis_column', + ), + migrations.RemoveField( + model_name='resourcechartdetails', + name='x_axis_label', + ), + migrations.RemoveField( + model_name='resourcechartdetails', + name='y_axis_column', + ), + migrations.RemoveField( + model_name='resourcechartdetails', + name='y_axis_label', + ), + migrations.AddField( + model_name='resourcechartdetails', + name='options', + field=api.models.SerializableJSONField(blank=True, default=dict), + ), + migrations.AlterField( + model_name='resourcechartdetails', + name='chart_type', + field=models.CharField(choices=[('BAR_VERTICAL', 'Bar Vertical'), ('BAR_HORIZONTAL', 'Bar Horizontal'), ('GROUPED_BAR_VERTICAL', 'Grouped Bar Vertical'), ('GROUPED_BAR_HORIZONTAL', 'Grouped Bar Horizontal'), ('LINE', 'Line'), ('MULTILINE', 'Multiline'), ('ASSAM_DISTRICT', 'Assam District'), ('ASSAM_RC', 'Assam Rc')], default='BAR_HORIZONTAL', max_length=50), + ), + migrations.AlterField( + model_name='resourcechartdetails', + name='filters', + field=api.models.SerializableJSONField(blank=True, default=list, null=True), + ), + ] diff --git a/api/migrations/0014_alter_category_options_alter_dataset_options_and_more.py b/api/migrations/0014_alter_category_options_alter_dataset_options_and_more.py new file mode 100755 index 00000000..1057b48a --- /dev/null +++ b/api/migrations/0014_alter_category_options_alter_dataset_options_and_more.py @@ -0,0 +1,263 @@ +# Generated by Django 5.0.4 on 2025-03-04 10:33 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0013_remove_resourcechartdetails_aggregate_type_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='category', + options={'verbose_name_plural': 'categories'}, + ), + migrations.AlterModelOptions( + name='dataset', + options={'ordering': ['-modified'], 'verbose_name': 'Dataset', 'verbose_name_plural': 'Datasets'}, + ), + migrations.AlterModelOptions( + name='datasetmetadata', + options={'ordering': ['metadata_item__label']}, + ), + migrations.AlterModelOptions( + name='geography', + options={'ordering': ['name'], 'verbose_name_plural': 'geographies'}, + ), + migrations.AlterModelOptions( + name='metadata', + options={'ordering': ['label']}, + ), + migrations.AlterModelOptions( + name='resourcechartdetails', + options={'ordering': ['-created']}, + ), + migrations.AlterModelOptions( + name='resourcemetadata', + options={'ordering': ['metadata_item__label']}, + ), + migrations.AddField( + model_name='accessmodelresource', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='accessmodelresource', + name='modified', + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name='metadata', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='metadata', + name='modified', + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name='resource', + name='is_active', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='resource', + name='url', + field=models.URLField(default='', max_length=500), + preserve_default=False, + ), + migrations.AddField( + model_name='resourcechartdetails', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='resourcemetadata', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='resourcemetadata', + name='modified', + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name='resourceschema', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='resourceschema', + name='modified', + field=models.DateTimeField(auto_now=True), + ), + migrations.AlterField( + model_name='dataset', + name='categories', + field=models.ManyToManyField(blank=True, related_name='datasets', to='api.category'), + ), + migrations.AlterField( + model_name='dataset', + name='dataspace', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='datasets', to='api.dataspace'), + ), + migrations.AlterField( + model_name='dataset', + name='description', + field=models.CharField(blank=True, max_length=1000, null=True), + ), + migrations.AlterField( + model_name='dataset', + name='organization', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='datasets', to='api.organization'), + ), + migrations.AlterField( + model_name='datasetmetadata', + name='id', + field=models.AutoField(primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='geography', + name='id', + field=models.AutoField(primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='geography', + name='parent_id', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.geography'), + ), + migrations.AlterField( + model_name='metadata', + name='data_standard', + field=models.CharField(blank=True, choices=[('DCATV3', 'Dcatv3'), ('OCDS', 'Ocds'), ('OBDS', 'Obds'), ('NA', 'Na')], max_length=50, null=True), + ), + migrations.AlterField( + model_name='metadata', + name='id', + field=models.AutoField(primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='resource', + name='dataset', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='resources', to='api.dataset'), + preserve_default=False, + ), + migrations.AlterField( + model_name='resource', + name='description', + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name='resource', + name='name', + field=models.CharField(max_length=200), + ), + migrations.AlterField( + model_name='resourcechartdetails', + name='resource', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.resource'), + ), + migrations.AlterField( + model_name='resourcemetadata', + name='id', + field=models.AutoField(primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='resourcemetadata', + name='metadata_item', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resource_metadata', to='api.metadata'), + ), + migrations.AlterField( + model_name='resourcemetadata', + name='resource', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='metadata_items', to='api.resource'), + ), + migrations.AlterField( + model_name='resourcemetadata', + name='value', + field=models.JSONField(), + ), + migrations.AlterField( + model_name='usecase', + name='id', + field=models.AutoField(primary_key=True, serialize=False), + ), + migrations.AlterUniqueTogether( + name='datasetmetadata', + unique_together={('dataset', 'metadata_item')}, + ), + migrations.AlterUniqueTogether( + name='resourcemetadata', + unique_together={('resource', 'metadata_item')}, + ), + migrations.AlterModelTable( + name='accessmodel', + table='access_model', + ), + migrations.AlterModelTable( + name='accessmodelresource', + table='access_model_resource', + ), + migrations.AlterModelTable( + name='category', + table='category', + ), + migrations.AlterModelTable( + name='dataset', + table='dataset', + ), + migrations.AlterModelTable( + name='datasetmetadata', + table='dataset_metadata', + ), + migrations.AlterModelTable( + name='dataspace', + table='dataspace', + ), + migrations.AlterModelTable( + name='geography', + table='geography', + ), + migrations.AlterModelTable( + name='metadata', + table='metadata', + ), + migrations.AlterModelTable( + name='organization', + table='organization', + ), + migrations.AlterModelTable( + name='resourcechartdetails', + table='resource_chart_details', + ), + migrations.AlterModelTable( + name='resourcechartimage', + table='resource_chart_image', + ), + migrations.AlterModelTable( + name='resourcemetadata', + table='resource_metadata', + ), + migrations.AlterModelTable( + name='resourceschema', + table='resource_schema', + ), + migrations.AlterModelTable( + name='tag', + table='tag', + ), + migrations.AlterModelTable( + name='usecase', + table='use_case', + ), + ] diff --git a/api/migrations/0015_dataset_slug_resource_slug.py b/api/migrations/0015_dataset_slug_resource_slug.py new file mode 100755 index 00000000..ecbdcccf --- /dev/null +++ b/api/migrations/0015_dataset_slug_resource_slug.py @@ -0,0 +1,68 @@ +# Generated by Django 5.0.4 on 2025-03-04 10:37 + +from django.db import migrations, models + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models +from django.utils.text import slugify + +def populate_slugs(apps, schema_editor): + # Handle Dataset slugs + Dataset = apps.get_model('api', 'Dataset') + for dataset in Dataset.objects.all(): + base_slug = slugify(dataset.title) if dataset.title else 'dataset' + slug = base_slug + counter = 1 + while Dataset.objects.filter(slug=slug).exists(): + slug = f"{base_slug}-{counter}" + counter += 1 + dataset.slug = slug + dataset.save() + + # Handle Resource slugs + Resource = apps.get_model('api', 'Resource') + for resource in Resource.objects.all(): + base_slug = slugify(resource.name) if resource.name else 'resource' + slug = base_slug + counter = 1 + while Resource.objects.filter(slug=slug).exists(): + slug = f"{base_slug}-{counter}" + counter += 1 + resource.slug = slug + resource.save() +def reverse_populate_slugs(apps, schema_editor): + Dataset = apps.get_model('api', 'Dataset') + Resource = apps.get_model('api', 'Resource') + Dataset.objects.all().update(slug="") + Resource.objects.all().update(slug="") + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0014_alter_category_options_alter_dataset_options_and_more'), + ] + + operations = [ + migrations.RunSQL( + # Forward SQL - drop existing indexes if they exist + """ + DROP INDEX IF EXISTS api_dataset_slug_ad6fedeb_like; + DROP INDEX IF EXISTS api_resource_slug_ad6fedeb_like; + """, + # Reverse SQL - do nothing + "SELECT 1;" + ), + migrations.AddField( + model_name='dataset', + name='slug', + field=models.SlugField(default='2025-03-04 10:37:40.235409+00:00', max_length=255 ), + preserve_default=False, + ), + migrations.AddField( + model_name='resource', + name='slug', + field=models.SlugField(default='2025-03-04 10:37:50.554696+00:00', max_length=255), + preserve_default=False, + ), + migrations.RunPython(populate_slugs,reverse_populate_slugs,),] diff --git a/api/migrations/0016_alter_dataset_slug_alter_resource_slug.py b/api/migrations/0016_alter_dataset_slug_alter_resource_slug.py new file mode 100755 index 00000000..064287f0 --- /dev/null +++ b/api/migrations/0016_alter_dataset_slug_alter_resource_slug.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.4 on 2025-03-04 10:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0015_dataset_slug_resource_slug'), + ] + + operations = [ + migrations.AlterField( + model_name='dataset', + name='slug', + field=models.SlugField(max_length=255, unique=True), + ), + migrations.AlterField( + model_name='resource', + name='slug', + field=models.SlugField(max_length=255, unique=True), + ), + ] diff --git a/api/migrations/0017_resourcedatatable.py b/api/migrations/0017_resourcedatatable.py new file mode 100755 index 00000000..bbf0d59f --- /dev/null +++ b/api/migrations/0017_resourcedatatable.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.4 on 2025-03-13 11:49 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0016_alter_dataset_slug_alter_resource_slug'), + ] + + operations = [ + migrations.CreateModel( + name='ResourceDataTable', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('table_name', models.CharField(max_length=255, unique=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ('modified', models.DateTimeField(auto_now=True)), + ('resource', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.resource')), + ], + options={ + 'db_table': 'resource_data_table', + 'ordering': ['-modified'], + }, + ), + ] diff --git a/api/migrations/0018_resource_download_count.py b/api/migrations/0018_resource_download_count.py new file mode 100755 index 00000000..73b3d6db --- /dev/null +++ b/api/migrations/0018_resource_download_count.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2025-03-26 12:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0017_resourcedatatable"), + ] + + operations = [ + migrations.AddField( + model_name="resource", + name="download_count", + field=models.IntegerField(default=0), + ), + ] diff --git a/api/migrations/0019_rename_category_sector_alter_sector_options_and_more.py b/api/migrations/0019_rename_category_sector_alter_sector_options_and_more.py new file mode 100755 index 00000000..0b23bef8 --- /dev/null +++ b/api/migrations/0019_rename_category_sector_alter_sector_options_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 5.0.4 on 2025-03-27 10:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0018_resource_download_count"), + ] + + operations = [ + migrations.RenameModel( + old_name="Category", + new_name="Sector", + ), + migrations.AlterModelOptions( + name="sector", + options={"verbose_name_plural": "sectors"}, + ), + migrations.RenameField( + model_name="dataset", + old_name="categories", + new_name="sectors", + ), + migrations.AlterModelTable( + name="sector", + table="sector", + ), + ] diff --git a/api/migrations/0020_datasetmetadata_created_datasetmetadata_modified_and_more.py b/api/migrations/0020_datasetmetadata_created_datasetmetadata_modified_and_more.py new file mode 100755 index 00000000..a2a0e53e --- /dev/null +++ b/api/migrations/0020_datasetmetadata_created_datasetmetadata_modified_and_more.py @@ -0,0 +1,120 @@ +# Generated by Django 5.0.4 on 2025-04-10 07:27 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0019_rename_category_sector_alter_sector_options_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="datasetmetadata", + name="created", + field=models.DateTimeField( + auto_now_add=True, default=django.utils.timezone.now + ), + preserve_default=False, + ), + migrations.AddField( + model_name="datasetmetadata", + name="modified", + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name="usecase", + name="running_status", + field=models.CharField( + choices=[ + ("DRAFT", "Draft"), + ("PUBLISHED", "Published"), + ("ARCHIVED", "Archived"), + ], + default="DRAFT", + max_length=50, + ), + ), + migrations.AddField( + model_name="usecase", + name="sectors", + field=models.ManyToManyField( + blank=True, related_name="usecases", to="api.sector" + ), + ), + migrations.AddField( + model_name="usecase", + name="tags", + field=models.ManyToManyField(blank=True, to="api.tag"), + ), + migrations.AlterField( + model_name="metadata", + name="model", + field=models.CharField( + choices=[ + ("DATASET", "Dataset"), + ("RESOURCE", "Reseource"), + ("USECASE", "Usecase"), + ], + max_length=50, + ), + ), + migrations.AlterField( + model_name="resourcechartdetails", + name="chart_type", + field=models.CharField( + choices=[ + ("BAR_VERTICAL", "Bar Vertical"), + ("BAR_HORIZONTAL", "Bar Horizontal"), + ("GROUPED_BAR_VERTICAL", "Grouped Bar Vertical"), + ("GROUPED_BAR_HORIZONTAL", "Grouped Bar Horizontal"), + ("LINE", "Line"), + ("MULTILINE", "Multiline"), + ("ASSAM_DISTRICT", "Assam District"), + ("ASSAM_RC", "Assam Rc"), + ("TREEMAP", "Treemap"), + ], + default="BAR_HORIZONTAL", + max_length=50, + ), + ), + migrations.AlterField( + model_name="resourcemetadata", + name="metadata_item", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.metadata" + ), + ), + migrations.CreateModel( + name="UseCaseMetadata", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ("value", models.CharField(max_length=1000)), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ( + "metadata_item", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.metadata" + ), + ), + ( + "usecase", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="metadata", + to="api.usecase", + ), + ), + ], + options={ + "db_table": "usecase_metadata", + "ordering": ["metadata_item__label"], + "abstract": False, + "unique_together": {("usecase", "metadata_item")}, + }, + ), + ] diff --git a/api/migrations/0021_rename_description_usecase_summary_and_more.py b/api/migrations/0021_rename_description_usecase_summary_and_more.py new file mode 100755 index 00000000..a6a24278 --- /dev/null +++ b/api/migrations/0021_rename_description_usecase_summary_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.0.4 on 2025-04-10 10:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0020_datasetmetadata_created_datasetmetadata_modified_and_more"), + ] + + operations = [ + migrations.RenameField( + model_name="usecase", + old_name="description", + new_name="summary", + ), + migrations.AlterField( + model_name="usecase", + name="running_status", + field=models.CharField( + choices=[ + ("INITIATED", "Initiated"), + ("ON_GOING", "On Going"), + ("COMPLETED", "Completed"), + ("ON_HOLD", "On Hold"), + ("CANCELLED", "Cancelled"), + ], + default="INITIATED", + max_length=50, + ), + ), + ] diff --git a/api/migrations/0022_resource_version_resourceversion.py b/api/migrations/0022_resource_version_resourceversion.py new file mode 100755 index 00000000..bc56e732 --- /dev/null +++ b/api/migrations/0022_resource_version_resourceversion.py @@ -0,0 +1,49 @@ +# Generated by Django 5.0.4 on 2025-04-10 11:30 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0021_rename_description_usecase_summary_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="resource", + name="version", + field=models.CharField(default="v1.0", max_length=50), + ), + migrations.CreateModel( + name="ResourceVersion", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("version_number", models.CharField(max_length=50)), + ("commit_hash", models.CharField(max_length=64, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("change_description", models.TextField(blank=True)), + ( + "resource", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="versions", + to="api.resource", + ), + ), + ], + options={ + "db_table": "resource_version", + "unique_together": {("resource", "version_number")}, + }, + ), + ] diff --git a/api/migrations/0023_alter_usecase_summary.py b/api/migrations/0023_alter_usecase_summary.py new file mode 100755 index 00000000..522c3c25 --- /dev/null +++ b/api/migrations/0023_alter_usecase_summary.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2025-04-11 06:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0022_resource_version_resourceversion"), + ] + + operations = [ + migrations.AlterField( + model_name="usecase", + name="summary", + field=models.CharField(blank=True, max_length=10000, null=True), + ), + ] diff --git a/api/migrations/0024_alter_organization_organization_types.py b/api/migrations/0024_alter_organization_organization_types.py new file mode 100755 index 00000000..0d522606 --- /dev/null +++ b/api/migrations/0024_alter_organization_organization_types.py @@ -0,0 +1,42 @@ +# Generated by Django 5.0.4 on 2025-04-29 08:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0023_alter_usecase_summary"), + ] + + operations = [ + migrations.AlterField( + model_name="organization", + name="organization_types", + field=models.CharField( + choices=[ + ("STATE GOVERNMENT", "State Government"), + ("UNION TERRITORY GOVERNMENT", "Union Territory Government"), + ("URBAN LOCAL BODY", "Urban Local Body"), + ("ACADEMIC INSTITUTION", "Academic Institution"), + ("CENTRAL GOVERNMENT", "Central Government"), + ("CITIZENS GROUP", "Citizens Group"), + ("CIVIL SOCIETY ORGANISATION", "Civil Society Organisation"), + ("INDUSTRY BODY", "Industry Body"), + ("MEDIA ORGANISATION", "Media Organisation"), + ( + "OPEN DATA/TECHNOLOGY COMMUNITY", + "Open Data Technology Community", + ), + ("PRIVATE COMPANY", "Private Company"), + ("PUBLIC SECTOR COMPANY", "Public Sector Company"), + ("OTHERS", "Others"), + ("STARTUP", "Startup"), + ("GOVERNMENT", "Government"), + ("CORPORATIONS", "Corporations"), + ("NGO", "Ngo"), + ], + max_length=100, + ), + ), + ] diff --git a/api/migrations/0025_usecase_completed_on_usecase_started_on.py b/api/migrations/0025_usecase_completed_on_usecase_started_on.py new file mode 100755 index 00000000..e4c2f3c5 --- /dev/null +++ b/api/migrations/0025_usecase_completed_on_usecase_started_on.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.4 on 2025-04-30 06:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0024_alter_organization_organization_types"), + ] + + operations = [ + migrations.AddField( + model_name="usecase", + name="completed_on", + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name="usecase", + name="started_on", + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/api/migrations/0026_usecase_contributors.py b/api/migrations/0026_usecase_contributors.py new file mode 100755 index 00000000..949765b7 --- /dev/null +++ b/api/migrations/0026_usecase_contributors.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.4 on 2025-04-30 07:15 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0025_usecase_completed_on_usecase_started_on"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="usecase", + name="contributors", + field=models.ManyToManyField( + blank=True, + related_name="contributed_usecases", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/api/migrations/0027_usecaseorganizationrelationship_and_more.py b/api/migrations/0027_usecaseorganizationrelationship_and_more.py new file mode 100755 index 00000000..2dd68d39 --- /dev/null +++ b/api/migrations/0027_usecaseorganizationrelationship_and_more.py @@ -0,0 +1,66 @@ +# Generated by Django 5.0.4 on 2025-04-30 12:34 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0026_usecase_contributors"), + ] + + operations = [ + migrations.CreateModel( + name="UseCaseOrganizationRelationship", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "relationship_type", + models.CharField( + choices=[("SUPPORTER", "Supporter"), ("PARTNER", "Partner")], + max_length=50, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "organization", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="api.organization", + ), + ), + ( + "usecase", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.usecase" + ), + ), + ], + options={ + "verbose_name": "UseCase Organization Relationship", + "verbose_name_plural": "UseCase Organization Relationships", + "db_table": "usecase_organization_relationship", + "unique_together": {("usecase", "organization", "relationship_type")}, + }, + ), + migrations.AddField( + model_name="usecase", + name="organizations", + field=models.ManyToManyField( + blank=True, + related_name="related_usecases", + through="api.UseCaseOrganizationRelationship", + to="api.organization", + ), + ), + ] diff --git a/api/migrations/0028_dataset_access_type_dataset_license.py b/api/migrations/0028_dataset_access_type_dataset_license.py new file mode 100755 index 00000000..96c12c44 --- /dev/null +++ b/api/migrations/0028_dataset_access_type_dataset_license.py @@ -0,0 +1,47 @@ +# Generated by Django 5.0.4 on 2025-05-01 12:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0027_usecaseorganizationrelationship_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="dataset", + name="access_type", + field=models.CharField( + choices=[ + ("PUBLIC", "Public"), + ("PRIVATE", "Private"), + ("RESTRICTED", "Restricted"), + ], + default="PUBLIC", + max_length=50, + ), + ), + migrations.AddField( + model_name="dataset", + name="license", + field=models.CharField( + choices=[ + ("GOVERNMENT_OPEN_DATA_LICENSE", "Government Open Data License"), + ("CC_BY_4_0_ATTRIBUTION", "Cc By 4 0 Attribution"), + ( + "CC_BY_SA_4_0_ATTRIBUTION_SHARE_ALIKE", + "Cc By Sa 4 0 Attribution Share Alike", + ), + ( + "OPEN_DATA_COMMONS_BY_ATTRIBUTION", + "Open Data Commons By Attribution", + ), + ("OPEN_DATABASE_LICENSE", "Open Database License"), + ], + default="CC_BY_4_0_ATTRIBUTION", + max_length=50, + ), + ), + ] diff --git a/api/migrations/0029_dataset_user.py b/api/migrations/0029_dataset_user.py new file mode 100755 index 00000000..ea6c3a15 --- /dev/null +++ b/api/migrations/0029_dataset_user.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.4 on 2025-05-07 13:15 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0028_dataset_access_type_dataset_license"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="dataset", + name="user", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="datasets", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/api/migrations/0030_organization_github_profile_and_more.py b/api/migrations/0030_organization_github_profile_and_more.py new file mode 100755 index 00000000..985ff768 --- /dev/null +++ b/api/migrations/0030_organization_github_profile_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.0.4 on 2025-05-09 14:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0029_dataset_user"), + ] + + operations = [ + migrations.AddField( + model_name="organization", + name="github_profile", + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name="organization", + name="linkedin_profile", + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name="organization", + name="location", + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name="organization", + name="twitter_profile", + field=models.URLField(blank=True, null=True), + ), + ] diff --git a/api/migrations/0031_usecase_user_alter_resourcechartdetails_chart_type.py b/api/migrations/0031_usecase_user_alter_resourcechartdetails_chart_type.py new file mode 100755 index 00000000..210c00a9 --- /dev/null +++ b/api/migrations/0031_usecase_user_alter_resourcechartdetails_chart_type.py @@ -0,0 +1,46 @@ +# Generated by Django 5.0.4 on 2025-05-21 09:06 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0030_organization_github_profile_and_more"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="usecase", + name="user", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + preserve_default=False, + ), + migrations.AlterField( + model_name="resourcechartdetails", + name="chart_type", + field=models.CharField( + choices=[ + ("BAR", "Bar"), + ("LINE", "Line"), + ("BAR_VERTICAL", "Bar Vertical"), + ("BAR_HORIZONTAL", "Bar Horizontal"), + ("GROUPED_BAR_VERTICAL", "Grouped Bar Vertical"), + ("GROUPED_BAR_HORIZONTAL", "Grouped Bar Horizontal"), + ("MULTILINE", "Multiline"), + ("ASSAM_DISTRICT", "Assam District"), + ("ASSAM_RC", "Assam Rc"), + ("TREEMAP", "Treemap"), + ], + default="BAR_HORIZONTAL", + max_length=50, + ), + ), + ] diff --git a/api/migrations/0032_resourcechartdetails_status_and_more.py b/api/migrations/0032_resourcechartdetails_status_and_more.py new file mode 100755 index 00000000..b89f1fc8 --- /dev/null +++ b/api/migrations/0032_resourcechartdetails_status_and_more.py @@ -0,0 +1,39 @@ +# Generated by Django 5.0.4 on 2025-05-21 13:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0031_usecase_user_alter_resourcechartdetails_chart_type"), + ] + + operations = [ + migrations.AddField( + model_name="resourcechartdetails", + name="status", + field=models.CharField( + choices=[ + ("DRAFT", "Draft"), + ("PUBLISHED", "Published"), + ("ARCHIVED", "Archived"), + ], + default="DRAFT", + max_length=50, + ), + ), + migrations.AddField( + model_name="resourcechartimage", + name="status", + field=models.CharField( + choices=[ + ("DRAFT", "Draft"), + ("PUBLISHED", "Published"), + ("ARCHIVED", "Archived"), + ], + default="DRAFT", + max_length=50, + ), + ), + ] diff --git a/api/migrations/0033_usecase_organization.py b/api/migrations/0033_usecase_organization.py new file mode 100755 index 00000000..af2821d8 --- /dev/null +++ b/api/migrations/0033_usecase_organization.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.4 on 2025-05-27 08:43 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0032_resourcechartdetails_status_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="usecase", + name="organization", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="api.organization", + ), + ), + ] diff --git a/api/migrations/0034_usecasedashboard.py b/api/migrations/0034_usecasedashboard.py new file mode 100755 index 00000000..c3588181 --- /dev/null +++ b/api/migrations/0034_usecasedashboard.py @@ -0,0 +1,36 @@ +# Generated by Django 5.0.4 on 2025-06-13 06:47 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0033_usecase_organization"), + ] + + operations = [ + migrations.CreateModel( + name="UseCaseDashboard", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ("name", models.CharField(max_length=200)), + ("link", models.URLField()), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ( + "usecase", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="dashboards", + to="api.usecase", + ), + ), + ], + options={ + "db_table": "usecase_dashboard", + "ordering": ["created"], + }, + ), + ] diff --git a/api/migrations/0035_usecase_platform_url.py b/api/migrations/0035_usecase_platform_url.py new file mode 100755 index 00000000..17bbeabd --- /dev/null +++ b/api/migrations/0035_usecase_platform_url.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2025-06-26 10:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0034_usecasedashboard"), + ] + + operations = [ + migrations.AddField( + model_name="usecase", + name="platform_url", + field=models.URLField(blank=True, null=True), + ), + ] diff --git a/api/migrations/0036_sdg_dataset_geographies_usecase_geographies_and_more.py b/api/migrations/0036_sdg_dataset_geographies_usecase_geographies_and_more.py new file mode 100755 index 00000000..78d7b9bc --- /dev/null +++ b/api/migrations/0036_sdg_dataset_geographies_usecase_geographies_and_more.py @@ -0,0 +1,746 @@ +# Generated by Django 5.0.4 on 2025-12-23 08:38 + +import api.utils.file_paths +import django.core.validators +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0035_usecase_platform_url"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="SDG", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, editable=False, primary_key=True, serialize=False + ), + ), + ("name", models.CharField(max_length=100, unique=True)), + ("code", models.CharField(max_length=10, unique=True)), + ("number", models.IntegerField(blank=True, null=True)), + ("description", models.TextField(blank=True, null=True)), + ("slug", models.SlugField(max_length=100, null=True, unique=True)), + ], + options={ + "verbose_name": "SDG", + "verbose_name_plural": "SDGs", + "db_table": "sdg", + "ordering": ["number"], + }, + ), + migrations.AddField( + model_name="dataset", + name="geographies", + field=models.ManyToManyField(blank=True, related_name="datasets", to="api.geography"), + ), + migrations.AddField( + model_name="usecase", + name="geographies", + field=models.ManyToManyField(blank=True, related_name="usecases", to="api.geography"), + ), + migrations.AlterField( + model_name="dataset", + name="user", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="datasets", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="geography", + name="type", + field=models.CharField( + choices=[ + ("REGION", "Region"), + ("COUNTRY", "Country"), + ("STATE", "State"), + ("DISTRICT", "District"), + ("UT", "Ut"), + ], + max_length=20, + ), + ), + migrations.AlterField( + model_name="metadata", + name="model", + field=models.CharField( + choices=[ + ("DATASET", "Dataset"), + ("RESOURCE", "Reseource"), + ("USECASE", "Usecase"), + ("COLLABORATIVE", "Collaborative"), + ], + max_length=50, + ), + ), + migrations.AlterField( + model_name="organization", + name="logo", + field=models.ImageField( + blank=True, + max_length=300, + null=True, + upload_to=api.utils.file_paths._organization_directory_path, + ), + ), + migrations.AlterField( + model_name="resourcechartdetails", + name="chart_type", + field=models.CharField( + choices=[ + ("BAR", "Bar"), + ("LINE", "Line"), + ("BIG_NUMBER", "Big Number"), + ("ASSAM_DISTRICT", "Assam District"), + ("ASSAM_RC", "Assam Rc"), + ("TREEMAP", "Treemap"), + ("POLYGON_MAP", "Polygon Map"), + ("POINT_MAP", "Point Map"), + ("GEOSPATIAL_MAP", "Geospatial Map"), + ], + default="BAR", + max_length=50, + ), + ), + migrations.AlterField( + model_name="resourcechartimage", + name="image", + field=models.ImageField( + blank=True, + max_length=300, + null=True, + upload_to=api.utils.file_paths._chart_image_directory_path, + ), + ), + migrations.AlterField( + model_name="resourcefiledetails", + name="file", + field=models.FileField(max_length=300, upload_to="resources/"), + ), + migrations.AlterField( + model_name="usecase", + name="logo", + field=models.ImageField( + blank=True, + max_length=300, + null=True, + upload_to=api.utils.file_paths._use_case_directory_path, + ), + ), + migrations.CreateModel( + name="AIModel", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("name", models.CharField(help_text="Model name or identifier", max_length=255)), + ("display_name", models.CharField(help_text="Human-readable name", max_length=255)), + ("version", models.CharField(blank=True, help_text="Model version", max_length=50)), + ( + "description", + models.TextField(help_text="Description of the model's capabilities"), + ), + ( + "model_type", + models.CharField( + choices=[ + ("TRANSLATION", "Translation"), + ("TEXT_GENERATION", "Text Generation"), + ("SUMMARIZATION", "Summarization"), + ("QUESTION_ANSWERING", "Question Answering"), + ("SENTIMENT_ANALYSIS", "Sentiment Analysis"), + ("TEXT_CLASSIFICATION", "Text Classification"), + ("NAMED_ENTITY_RECOGNITION", "Named Entity Recognition"), + ("TEXT_TO_SPEECH", "Text To Speech"), + ("SPEECH_TO_TEXT", "Speech To Text"), + ("OTHER", "Other"), + ], + max_length=50, + ), + ), + ( + "provider", + models.CharField( + choices=[ + ("OPENAI", "Openai"), + ("LLAMA_OLLAMA", "Llama Ollama"), + ("LLAMA_TOGETHER", "Llama Together"), + ("LLAMA_REPLICATE", "Llama Replicate"), + ("LLAMA_CUSTOM", "Llama Custom"), + ("CUSTOM", "Custom"), + ("HUGGINGFACE", "Huggingface"), + ], + max_length=50, + ), + ), + ( + "provider_model_id", + models.CharField( + blank=True, + help_text="Provider's model identifier (e.g., gpt-4, claude-3-opus)", + max_length=255, + ), + ), + ( + "hf_use_pipeline", + models.BooleanField(default=False, help_text="Use Pipeline inference API"), + ), + ( + "hf_auth_token", + models.CharField( + blank=True, + help_text="Huggingface Auth Token for gated models", + max_length=255, + null=True, + ), + ), + ( + "hf_model_class", + models.CharField( + blank=True, + choices=[ + ("AutoModelForCausalLM", "Causal Language Modeling Head"), + ( + "AutoModelForSeq2SeqLM", + "Sequence-To-Sequence Language Modeling Head", + ), + ("AutoModelForSequenceClassification", "Sequence Classification Head"), + ("AutoModelForNextSentencePrediction", "Next Sentence Prediction Head"), + ("AutoModelForMultipleChoice", "Multiple Choice Head"), + ("AutoModelForTokenClassification", "Token Classification Head"), + ("AutoModelForQuestionAnswering", "Question Answering Head"), + ("AutoModelForMaskedLM", "Masked Language Modeling Head"), + ], + help_text="Specify model head to use", + max_length=100, + null=True, + ), + ), + ( + "hf_attn_implementation", + models.CharField( + blank=True, + default="flash_attention_2", + help_text="Attention Function", + max_length=255, + ), + ), + ( + "framework", + models.CharField( + blank=True, + choices=[("pt", "PyTorch"), ("tf", "TensorFlow")], + help_text="Framework (PyTorch or TensorFlow)", + max_length=10, + null=True, + ), + ), + ("supports_streaming", models.BooleanField(default=False)), + ( + "max_tokens", + models.IntegerField( + blank=True, help_text="Maximum tokens supported by the model", null=True + ), + ), + ( + "supported_languages", + models.JSONField( + default=list, + help_text="List of supported language codes (e.g., ['en', 'es', 'fr'])", + ), + ), + ( + "input_schema", + models.JSONField( + default=dict, help_text="Expected input format and parameters" + ), + ), + ( + "output_schema", + models.JSONField(default=dict, help_text="Expected output format"), + ), + ( + "metadata", + models.JSONField( + default=dict, + help_text="Additional metadata (training data info, limitations, etc.)", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("REGISTERED", "Registered"), + ("VALIDATING", "Validating"), + ("ACTIVE", "Active"), + ("AUDITING", "Auditing"), + ("APPROVED", "Approved"), + ("FLAGGED", "Flagged"), + ("DEPRECATED", "Deprecated"), + ], + default="REGISTERED", + max_length=20, + ), + ), + ( + "is_public", + models.BooleanField( + default=False, help_text="Whether this model is publicly visible" + ), + ), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this model is currently active" + ), + ), + ("average_latency_ms", models.FloatField(blank=True, null=True)), + ("success_rate", models.FloatField(blank=True, null=True)), + ("last_audit_score", models.FloatField(blank=True, null=True)), + ("audit_count", models.IntegerField(default=0)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("last_tested_at", models.DateTimeField(blank=True, null=True)), + ( + "geographies", + models.ManyToManyField( + blank=True, related_name="ai_models", to="api.geography" + ), + ), + ( + "organization", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="ai_models", + to="api.organization", + ), + ), + ( + "sectors", + models.ManyToManyField(blank=True, related_name="ai_models", to="api.sector"), + ), + ("tags", models.ManyToManyField(blank=True, to="api.tag")), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="ai_models", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "ordering": ["-created_at"], + }, + ), + migrations.CreateModel( + name="Catalog", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("name", models.CharField(max_length=255)), + ( + "logo", + models.ImageField( + blank=True, + null=True, + upload_to=api.utils.file_paths._catalog_directory_path, + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ("homepage", models.URLField(blank=True)), + ("slug", models.SlugField(max_length=75, null=True, unique=True)), + ("datasets", models.ManyToManyField(related_name="catalogs", to="api.dataset")), + ], + options={ + "verbose_name": "catalog", + "verbose_name_plural": "catalogs", + }, + ), + migrations.CreateModel( + name="Collaborative", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ("title", models.CharField(blank=True, max_length=200, null=True, unique=True)), + ("summary", models.CharField(blank=True, max_length=10000, null=True)), + ( + "logo", + models.ImageField( + blank=True, + max_length=300, + null=True, + upload_to=api.utils.file_paths._use_case_directory_path, + ), + ), + ( + "cover_image", + models.ImageField( + blank=True, + max_length=300, + null=True, + upload_to=api.utils.file_paths._use_case_directory_path, + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ("website", models.URLField(blank=True)), + ("contact_email", models.EmailField(blank=True, max_length=254, null=True)), + ("slug", models.SlugField(blank=True, max_length=75, null=True, unique=True)), + ( + "status", + models.CharField( + choices=[ + ("DRAFT", "Draft"), + ("PUBLISHED", "Published"), + ("ARCHIVED", "Archived"), + ], + default="DRAFT", + max_length=50, + ), + ), + ("started_on", models.DateField(blank=True, null=True)), + ("completed_on", models.DateField(blank=True, null=True)), + ("platform_url", models.URLField(blank=True, null=True)), + ( + "contributors", + models.ManyToManyField( + blank=True, + related_name="contributed_collaboratives", + to=settings.AUTH_USER_MODEL, + ), + ), + ("datasets", models.ManyToManyField(blank=True, to="api.dataset")), + ( + "geographies", + models.ManyToManyField( + blank=True, related_name="collaboratives", to="api.geography" + ), + ), + ( + "organization", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="api.organization", + ), + ), + ( + "sectors", + models.ManyToManyField( + blank=True, related_name="collaboratives", to="api.sector" + ), + ), + ("tags", models.ManyToManyField(blank=True, to="api.tag")), + ("use_cases", models.ManyToManyField(blank=True, to="api.usecase")), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + ], + options={ + "db_table": "collaborative", + }, + ), + migrations.CreateModel( + name="CollaborativeMetadata", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ("value", models.CharField(max_length=1000)), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ( + "collaborative", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="metadata", + to="api.collaborative", + ), + ), + ( + "metadata_item", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.metadata" + ), + ), + ], + options={ + "db_table": "collaborative_metadata", + "ordering": ["metadata_item__label"], + "abstract": False, + }, + ), + migrations.CreateModel( + name="CollaborativeOrganizationRelationship", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ( + "relationship_type", + models.CharField( + choices=[("SUPPORTER", "Supporter"), ("PARTNER", "Partner")], max_length=50 + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "collaborative", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.collaborative" + ), + ), + ( + "organization", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.organization" + ), + ), + ], + options={ + "verbose_name": "Collaborative Organization Relationship", + "verbose_name_plural": "Collaborative Organization Relationships", + "db_table": "collaborative_organization_relationship", + }, + ), + migrations.AddField( + model_name="collaborative", + name="organizations", + field=models.ManyToManyField( + blank=True, + related_name="related_collaboratives", + through="api.CollaborativeOrganizationRelationship", + to="api.organization", + ), + ), + migrations.CreateModel( + name="ModelAPIKey", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ( + "name", + models.CharField(help_text="Friendly name for this API key", max_length=100), + ), + ("encrypted_key", models.BinaryField(help_text="Encrypted API key or token")), + ( + "key_type", + models.CharField( + default="api_key", + help_text="Type of credential (api_key, bearer_token, oauth_token, etc.)", + max_length=50, + ), + ), + ("is_active", models.BooleanField(default=True)), + ( + "expires_at", + models.DateTimeField( + blank=True, help_text="Expiration date for the key", null=True + ), + ), + ("last_used_at", models.DateTimeField(blank=True, null=True)), + ("usage_count", models.IntegerField(default=0)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "model", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="api_keys", + to="api.aimodel", + ), + ), + ], + options={ + "verbose_name": "Model API Key", + "verbose_name_plural": "Model API Keys", + "ordering": ["-created_at"], + }, + ), + migrations.CreateModel( + name="ModelEndpoint", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ( + "url", + models.URLField( + help_text="API endpoint URL", + max_length=500, + validators=[django.core.validators.URLValidator()], + ), + ), + ( + "http_method", + models.CharField( + choices=[("GET", "Get"), ("POST", "Post"), ("PUT", "Put")], + default="POST", + max_length=10, + ), + ), + ( + "auth_type", + models.CharField( + choices=[ + ("BEARER", "Bearer"), + ("API_KEY", "Api Key"), + ("BASIC", "Basic"), + ("OAUTH2", "Oauth2"), + ("CUSTOM", "Custom"), + ("NONE", "None"), + ], + default="BEARER", + max_length=20, + ), + ), + ( + "auth_header_name", + models.CharField( + default="Authorization", + help_text="Header name for authentication (e.g., 'Authorization', 'X-API-Key')", + max_length=100, + ), + ), + ( + "headers", + models.JSONField( + default=dict, help_text="Additional headers to include in requests" + ), + ), + ( + "request_template", + models.JSONField( + default=dict, help_text="Template for request body with placeholders" + ), + ), + ( + "response_path", + models.CharField( + blank=True, + help_text="JSON path to extract response (e.g., 'choices[0].message.content')", + max_length=255, + ), + ), + ("timeout_seconds", models.IntegerField(default=30)), + ("max_retries", models.IntegerField(default=3)), + ( + "is_primary", + models.BooleanField(default=True, help_text="Primary endpoint to use"), + ), + ("is_active", models.BooleanField(default=True)), + ( + "rate_limit_per_minute", + models.IntegerField( + blank=True, help_text="Rate limit for this endpoint", null=True + ), + ), + ("last_success_at", models.DateTimeField(blank=True, null=True)), + ("last_failure_at", models.DateTimeField(blank=True, null=True)), + ("total_requests", models.IntegerField(default=0)), + ("failed_requests", models.IntegerField(default=0)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "model", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="endpoints", + to="api.aimodel", + ), + ), + ], + options={ + "ordering": ["-is_primary", "-created_at"], + }, + ), + migrations.AddField( + model_name="collaborative", + name="sdgs", + field=models.ManyToManyField(blank=True, related_name="collaboratives", to="api.sdg"), + ), + migrations.AddField( + model_name="usecase", + name="sdgs", + field=models.ManyToManyField(blank=True, related_name="usecases", to="api.sdg"), + ), + migrations.AddIndex( + model_name="aimodel", + index=models.Index( + fields=["user", "-created_at"], name="api_aimodel_user_id_7a7462_idx" + ), + ), + migrations.AddIndex( + model_name="aimodel", + index=models.Index( + fields=["status", "is_active"], name="api_aimodel_status_f93998_idx" + ), + ), + migrations.AddIndex( + model_name="aimodel", + index=models.Index( + fields=["model_type", "provider"], name="api_aimodel_model_t_0b746e_idx" + ), + ), + migrations.AddIndex( + model_name="aimodel", + index=models.Index( + fields=["provider", "provider_model_id"], name="api_aimodel_provide_f67293_idx" + ), + ), + migrations.AlterUniqueTogether( + name="aimodel", + unique_together={("user", "name", "version")}, + ), + migrations.AlterUniqueTogether( + name="collaborativemetadata", + unique_together={("collaborative", "metadata_item")}, + ), + migrations.AlterUniqueTogether( + name="collaborativeorganizationrelationship", + unique_together={("collaborative", "organization", "relationship_type")}, + ), + migrations.AddIndex( + model_name="modelendpoint", + index=models.Index( + fields=["model", "is_primary", "is_active"], name="api_modelen_model_i_0c0857_idx" + ), + ), + ] diff --git a/api/migrations/0037_alter_aimodel_status_aimodelversion_versionprovider.py b/api/migrations/0037_alter_aimodel_status_aimodelversion_versionprovider.py new file mode 100755 index 00000000..f2bef6c6 --- /dev/null +++ b/api/migrations/0037_alter_aimodel_status_aimodelversion_versionprovider.py @@ -0,0 +1,233 @@ +# Generated by Django 5.0.4 on 2025-12-24 10:34 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0036_sdg_dataset_geographies_usecase_geographies_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="aimodel", + name="status", + field=models.CharField( + choices=[ + ("DRAFT", "Draft"), + ("REGISTERED", "Registered"), + ("VALIDATING", "Validating"), + ("ACTIVE", "Active"), + ("AUDITING", "Auditing"), + ("APPROVED", "Approved"), + ("FLAGGED", "Flagged"), + ("DEPRECATED", "Deprecated"), + ], + default="REGISTERED", + max_length=20, + ), + ), + migrations.CreateModel( + name="AIModelVersion", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ( + "version", + models.CharField(help_text="Version number (e.g., 1.0.0)", max_length=50), + ), + ( + "version_notes", + models.TextField(blank=True, help_text="Changelog/notes for this version"), + ), + ("supports_streaming", models.BooleanField(default=False)), + ( + "max_tokens", + models.IntegerField( + blank=True, help_text="Maximum tokens supported", null=True + ), + ), + ( + "supported_languages", + models.JSONField(default=list, help_text="List of supported language codes"), + ), + ( + "input_schema", + models.JSONField( + default=dict, help_text="Expected input format and parameters" + ), + ), + ( + "output_schema", + models.JSONField(default=dict, help_text="Expected output format"), + ), + ( + "metadata", + models.JSONField( + default=dict, help_text="Additional version-specific metadata" + ), + ), + ( + "status", + models.CharField( + choices=[ + ("DRAFT", "Draft"), + ("REGISTERED", "Registered"), + ("VALIDATING", "Validating"), + ("ACTIVE", "Active"), + ("AUDITING", "Auditing"), + ("APPROVED", "Approved"), + ("FLAGGED", "Flagged"), + ("DEPRECATED", "Deprecated"), + ], + default="REGISTERED", + max_length=20, + ), + ), + ( + "is_latest", + models.BooleanField( + default=False, help_text="Whether this is the latest version" + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("published_at", models.DateTimeField(blank=True, null=True)), + ( + "ai_model", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="versions", + to="api.aimodel", + ), + ), + ], + options={ + "verbose_name": "AI Model Version", + "verbose_name_plural": "AI Model Versions", + "ordering": ["-created_at"], + "unique_together": {("ai_model", "version")}, + }, + ), + migrations.CreateModel( + name="VersionProvider", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ( + "provider", + models.CharField( + choices=[ + ("OPENAI", "Openai"), + ("LLAMA_OLLAMA", "Llama Ollama"), + ("LLAMA_TOGETHER", "Llama Together"), + ("LLAMA_REPLICATE", "Llama Replicate"), + ("LLAMA_CUSTOM", "Llama Custom"), + ("CUSTOM", "Custom"), + ("HUGGINGFACE", "Huggingface"), + ], + max_length=50, + ), + ), + ( + "provider_model_id", + models.CharField( + blank=True, + help_text="Provider's model identifier (e.g., gpt-4, claude-3-opus)", + max_length=255, + ), + ), + ( + "is_primary", + models.BooleanField( + default=False, + help_text="Whether this is the primary provider for the version", + ), + ), + ("is_active", models.BooleanField(default=True)), + ( + "hf_use_pipeline", + models.BooleanField(default=False, help_text="Use Pipeline inference API"), + ), + ( + "hf_auth_token", + models.CharField( + blank=True, + help_text="Huggingface Auth Token for gated models", + max_length=255, + null=True, + ), + ), + ( + "hf_model_class", + models.CharField( + blank=True, + choices=[ + ("AutoModelForCausalLM", "Causal Language Modeling Head"), + ( + "AutoModelForSeq2SeqLM", + "Sequence-To-Sequence Language Modeling Head", + ), + ("AutoModelForSequenceClassification", "Sequence Classification Head"), + ("AutoModelForNextSentencePrediction", "Next Sentence Prediction Head"), + ("AutoModelForMultipleChoice", "Multiple Choice Head"), + ("AutoModelForTokenClassification", "Token Classification Head"), + ("AutoModelForQuestionAnswering", "Question Answering Head"), + ("AutoModelForMaskedLM", "Masked Language Modeling Head"), + ], + help_text="Specify model head to use", + max_length=100, + null=True, + ), + ), + ( + "hf_attn_implementation", + models.CharField( + blank=True, + default="flash_attention_2", + help_text="Attention Function", + max_length=255, + ), + ), + ( + "framework", + models.CharField( + blank=True, + choices=[("pt", "PyTorch"), ("tf", "TensorFlow")], + help_text="Framework (PyTorch or TensorFlow)", + max_length=10, + null=True, + ), + ), + ( + "config", + models.JSONField(default=dict, help_text="Provider-specific configuration"), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "version", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="providers", + to="api.aimodelversion", + ), + ), + ], + options={ + "verbose_name": "Version Provider", + "verbose_name_plural": "Version Providers", + "ordering": ["-is_primary", "-created_at"], + }, + ), + ] diff --git a/api/migrations/0038_aimodelversion_lifecycle_stage.py b/api/migrations/0038_aimodelversion_lifecycle_stage.py new file mode 100755 index 00000000..414dfa33 --- /dev/null +++ b/api/migrations/0038_aimodelversion_lifecycle_stage.py @@ -0,0 +1,31 @@ +# Generated by Django 5.0.4 on 2025-12-26 08:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0037_alter_aimodel_status_aimodelversion_versionprovider"), + ] + + operations = [ + migrations.AddField( + model_name="aimodelversion", + name="lifecycle_stage", + field=models.CharField( + choices=[ + ("DEVELOPMENT", "Development"), + ("TESTING", "Testing"), + ("BETA", "Beta Testing"), + ("STAGING", "Staging"), + ("PRODUCTION", "Production"), + ("DEPRECATED", "Deprecated"), + ("RETIRED", "Retired"), + ], + default="DEVELOPMENT", + help_text="Current lifecycle stage of this version", + max_length=20, + ), + ), + ] diff --git a/api/migrations/0039_versionprovider_api_auth_header_name_and_more.py b/api/migrations/0039_versionprovider_api_auth_header_name_and_more.py new file mode 100755 index 00000000..bc12fd07 --- /dev/null +++ b/api/migrations/0039_versionprovider_api_auth_header_name_and_more.py @@ -0,0 +1,143 @@ +# Generated by Django 5.0.4 on 2025-12-30 15:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0038_aimodelversion_lifecycle_stage"), + ] + + operations = [ + migrations.AddField( + model_name="versionprovider", + name="api_auth_header_name", + field=models.CharField( + default="Authorization", + help_text="Header name for authentication (e.g., Authorization, X-API-Key)", + max_length=100, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_auth_type", + field=models.CharField( + choices=[ + ("BEARER", "Bearer"), + ("API_KEY", "Api Key"), + ("BASIC", "Basic"), + ("OAUTH2", "Oauth2"), + ("CUSTOM", "Custom"), + ("NONE", "None"), + ], + default="BEARER", + help_text="Authentication type for API calls", + max_length=20, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_endpoint_url", + field=models.URLField( + blank=True, + help_text="API endpoint URL (e.g., https://api.openai.com/v1/chat/completions)", + max_length=500, + null=True, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_headers", + field=models.JSONField( + blank=True, default=dict, help_text="Additional headers to include in requests" + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_http_method", + field=models.CharField( + choices=[("GET", "Get"), ("POST", "Post"), ("PUT", "Put")], + default="POST", + help_text="HTTP method for API calls", + max_length=10, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_key", + field=models.CharField( + blank=True, + help_text="API key or token (encrypted at rest)", + max_length=500, + null=True, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_key_prefix", + field=models.CharField( + blank=True, + default="Bearer", + help_text="Prefix for API key (e.g., Bearer, Token)", + max_length=50, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_request_template", + field=models.JSONField( + blank=True, + default=dict, + help_text="Request body template with placeholders like {input}, {prompt}", + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_response_path", + field=models.CharField( + blank=True, + help_text="JSON path to extract response (e.g., choices[0].message.content)", + max_length=255, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="api_timeout_seconds", + field=models.IntegerField(default=60, help_text="Request timeout in seconds"), + ), + migrations.AddField( + model_name="versionprovider", + name="hf_device_map", + field=models.CharField( + blank=True, + default="auto", + help_text="Device map for model loading (auto, cpu, cuda)", + max_length=50, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="hf_torch_dtype", + field=models.CharField( + blank=True, + default="auto", + help_text="Torch dtype (auto, float16, float32, bfloat16)", + max_length=50, + ), + ), + migrations.AddField( + model_name="versionprovider", + name="hf_trust_remote_code", + field=models.BooleanField( + default=True, help_text="Trust remote code when loading model" + ), + ), + migrations.AlterField( + model_name="versionprovider", + name="config", + field=models.JSONField( + default=dict, help_text="Additional provider-specific configuration" + ), + ), + ] diff --git a/api/migrations/0040_promptdataset_dataset_dataset_type.py b/api/migrations/0040_promptdataset_dataset_dataset_type.py new file mode 100755 index 00000000..a636b901 --- /dev/null +++ b/api/migrations/0040_promptdataset_dataset_dataset_type.py @@ -0,0 +1,145 @@ +# Generated by Django 5.0.4 on 2026-01-12 12:55 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0039_versionprovider_api_auth_header_name_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="PromptDataset", + fields=[ + ( + "dataset_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="api.dataset", + ), + ), + ( + "task_type", + models.CharField( + blank=True, + choices=[ + ("TEXT_GENERATION", "Text Generation"), + ("TEXT_CLASSIFICATION", "Text Classification"), + ("QUESTION_ANSWERING", "Question Answering"), + ("SUMMARIZATION", "Summarization"), + ("TRANSLATION", "Translation"), + ("SENTIMENT_ANALYSIS", "Sentiment Analysis"), + ("NAMED_ENTITY_RECOGNITION", "Named Entity Recognition"), + ("CODE_GENERATION", "Code Generation"), + ("CONVERSATION", "Conversation"), + ("INSTRUCTION_FOLLOWING", "Instruction Following"), + ("REASONING", "Reasoning"), + ("CREATIVE_WRITING", "Creative Writing"), + ("DATA_EXTRACTION", "Data Extraction"), + ("OTHER", "Other"), + ], + max_length=100, + null=True, + ), + ), + ( + "target_languages", + models.JSONField( + blank=True, + help_text="List of target languages for the prompts (e.g., ['en', 'hi', 'ta'])", + null=True, + ), + ), + ( + "domain", + models.CharField( + blank=True, + help_text="Domain or category (e.g., healthcare, education, legal)", + max_length=200, + null=True, + ), + ), + ( + "target_model_types", + models.JSONField( + blank=True, + help_text="List of AI model types these prompts are designed for", + null=True, + ), + ), + ( + "prompt_format", + models.CharField( + blank=True, + help_text="Format of prompts (e.g., instruction, chat, completion)", + max_length=100, + null=True, + ), + ), + ( + "has_system_prompt", + models.BooleanField( + default=False, + help_text="Whether the prompts include system-level instructions", + ), + ), + ( + "has_example_responses", + models.BooleanField( + default=False, + help_text="Whether the prompts include example/expected responses", + ), + ), + ( + "avg_prompt_length", + models.IntegerField( + blank=True, + help_text="Average character length of prompts in this dataset", + null=True, + ), + ), + ( + "prompt_count", + models.IntegerField( + blank=True, help_text="Total number of prompts in this dataset", null=True + ), + ), + ( + "use_case", + models.TextField( + blank=True, + help_text="Description of intended use cases for these prompts", + null=True, + ), + ), + ( + "evaluation_criteria", + models.JSONField( + blank=True, + help_text="Criteria or metrics for evaluating prompt effectiveness", + null=True, + ), + ), + ], + options={ + "verbose_name": "Prompt Dataset", + "verbose_name_plural": "Prompt Datasets", + "db_table": "prompt_dataset", + }, + bases=("api.dataset",), + ), + migrations.AddField( + model_name="dataset", + name="dataset_type", + field=models.CharField( + choices=[("DATA", "Data"), ("PROMPT", "Prompt")], default="DATA", max_length=50 + ), + ), + ] diff --git a/api/migrations/0041_alter_promptdataset_domain_and_more.py b/api/migrations/0041_alter_promptdataset_domain_and_more.py new file mode 100755 index 00000000..a6b1c973 --- /dev/null +++ b/api/migrations/0041_alter_promptdataset_domain_and_more.py @@ -0,0 +1,67 @@ +# Generated by Django 5.0.4 on 2026-01-12 14:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0040_promptdataset_dataset_dataset_type"), + ] + + operations = [ + migrations.AlterField( + model_name="promptdataset", + name="domain", + field=models.CharField( + blank=True, + choices=[ + ("HEALTHCARE", "Healthcare"), + ("EDUCATION", "Education"), + ("LEGAL", "Legal"), + ("FINANCE", "Finance"), + ("AGRICULTURE", "Agriculture"), + ("ENVIRONMENT", "Environment"), + ("GOVERNMENT", "Government"), + ("TECHNOLOGY", "Technology"), + ("SCIENCE", "Science"), + ("SOCIAL_SERVICES", "Social Services"), + ("TRANSPORTATION", "Transportation"), + ("ENERGY", "Energy"), + ("GENERAL", "General"), + ("OTHER", "Other"), + ], + help_text="Domain or category (e.g., healthcare, education, legal)", + max_length=200, + null=True, + ), + ), + migrations.AlterField( + model_name="promptdataset", + name="prompt_format", + field=models.CharField( + blank=True, + choices=[ + ("INSTRUCTION", "Instruction"), + ("CHAT", "Chat"), + ("COMPLETION", "Completion"), + ("FEW_SHOT", "Few Shot"), + ("CHAIN_OF_THOUGHT", "Chain Of Thought"), + ("ZERO_SHOT", "Zero Shot"), + ("OTHER", "Other"), + ], + help_text="Format of prompts (e.g., instruction, chat, completion)", + max_length=100, + null=True, + ), + ), + migrations.AlterField( + model_name="promptdataset", + name="target_model_types", + field=models.JSONField( + blank=True, + help_text="List of AI model types these prompts are designed for (e.g., ['GPT', 'LLAMA'])", + null=True, + ), + ), + ] diff --git a/api/migrations/0042_promptresource_and_more.py b/api/migrations/0042_promptresource_and_more.py new file mode 100755 index 00000000..722c0586 --- /dev/null +++ b/api/migrations/0042_promptresource_and_more.py @@ -0,0 +1,106 @@ +# Generated by Django 5.0.4 on 2026-01-13 07:31 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0041_alter_promptdataset_domain_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="PromptResource", + fields=[ + ( + "resource", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + primary_key=True, + related_name="prompt_details", + serialize=False, + to="api.resource", + ), + ), + ( + "prompt_format", + models.CharField( + blank=True, + choices=[ + ("INSTRUCTION", "Instruction"), + ("CHAT", "Chat"), + ("COMPLETION", "Completion"), + ("FEW_SHOT", "Few Shot"), + ("CHAIN_OF_THOUGHT", "Chain Of Thought"), + ("ZERO_SHOT", "Zero Shot"), + ("OTHER", "Other"), + ], + help_text="Format of prompts in this file (e.g., instruction, chat, completion)", + max_length=100, + null=True, + ), + ), + ( + "has_system_prompt", + models.BooleanField( + default=False, + help_text="Whether the prompts in this file include system-level instructions", + ), + ), + ( + "has_example_responses", + models.BooleanField( + default=False, + help_text="Whether the prompts in this file include example/expected responses", + ), + ), + ( + "avg_prompt_length", + models.IntegerField( + blank=True, + help_text="Average character length of prompts in this file", + null=True, + ), + ), + ( + "prompt_count", + models.IntegerField( + blank=True, help_text="Total number of prompts in this file", null=True + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ], + options={ + "verbose_name": "Prompt Resource", + "verbose_name_plural": "Prompt Resources", + "db_table": "prompt_resource", + }, + ), + migrations.RemoveField( + model_name="promptdataset", + name="avg_prompt_length", + ), + migrations.RemoveField( + model_name="promptdataset", + name="has_example_responses", + ), + migrations.RemoveField( + model_name="promptdataset", + name="has_system_prompt", + ), + migrations.RemoveField( + model_name="promptdataset", + name="prompt_count", + ), + migrations.RemoveField( + model_name="promptdataset", + name="prompt_format", + ), + migrations.RemoveField( + model_name="promptdataset", + name="use_case", + ), + ] diff --git a/api/migrations/0043_promptdataset_purpose.py b/api/migrations/0043_promptdataset_purpose.py new file mode 100755 index 00000000..f137ae83 --- /dev/null +++ b/api/migrations/0043_promptdataset_purpose.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.4 on 2026-01-14 12:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0042_promptresource_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="promptdataset", + name="purpose", + field=models.CharField( + blank=True, + choices=[ + ("RESEARCH", "Research"), + ("EDUCATION", "Education"), + ("EVALUATION", "Evaluation"), + ("OTHER", "Other"), + ], + help_text="Purpose of the prompt dataset", + max_length=200, + null=True, + ), + ), + ] diff --git a/api/migrations/0044_alter_aimodel_input_schema_alter_aimodel_metadata_and_more.py b/api/migrations/0044_alter_aimodel_input_schema_alter_aimodel_metadata_and_more.py new file mode 100755 index 00000000..00e0e8cd --- /dev/null +++ b/api/migrations/0044_alter_aimodel_input_schema_alter_aimodel_metadata_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 5.0.4 on 2026-02-06 13:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0043_promptdataset_purpose"), + ] + + operations = [ + migrations.AlterField( + model_name="aimodel", + name="input_schema", + field=models.JSONField( + blank=True, + default=dict, + help_text="Expected input format and parameters", + null=True, + ), + ), + migrations.AlterField( + model_name="aimodel", + name="metadata", + field=models.JSONField( + blank=True, + default=dict, + help_text="Additional metadata (training data info, limitations, etc.)", + null=True, + ), + ), + migrations.AlterField( + model_name="aimodel", + name="output_schema", + field=models.JSONField( + blank=True, default=dict, help_text="Expected output format", null=True + ), + ), + migrations.AlterField( + model_name="aimodel", + name="supported_languages", + field=models.JSONField( + blank=True, + default=list, + help_text="List of supported language codes (e.g., ['en', 'es', 'fr'])", + null=True, + ), + ), + ] diff --git a/api/migrations/0045_aimodel_domain.py b/api/migrations/0045_aimodel_domain.py new file mode 100755 index 00000000..c342fb67 --- /dev/null +++ b/api/migrations/0045_aimodel_domain.py @@ -0,0 +1,39 @@ +# Generated by Django 5.0.4 on 2026-02-10 13:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0044_alter_aimodel_input_schema_alter_aimodel_metadata_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="aimodel", + name="domain", + field=models.CharField( + blank=True, + choices=[ + ("HEALTHCARE", "Healthcare"), + ("EDUCATION", "Education"), + ("LEGAL", "Legal"), + ("FINANCE", "Finance"), + ("AGRICULTURE", "Agriculture"), + ("ENVIRONMENT", "Environment"), + ("GOVERNMENT", "Government"), + ("TECHNOLOGY", "Technology"), + ("SCIENCE", "Science"), + ("SOCIAL_SERVICES", "Social Services"), + ("TRANSPORTATION", "Transportation"), + ("ENERGY", "Energy"), + ("GENERAL", "General"), + ("OTHER", "Other"), + ], + help_text="Domain or category (e.g., healthcare, education, legal)", + max_length=200, + null=True, + ), + ), + ] diff --git a/api/migrations/0046_alter_collaborative_slug.py b/api/migrations/0046_alter_collaborative_slug.py new file mode 100755 index 00000000..cb735ce2 --- /dev/null +++ b/api/migrations/0046_alter_collaborative_slug.py @@ -0,0 +1,30 @@ +# Generated by Django 5.0.4 on 2026-04-30 13:31 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0045_aimodel_domain"), + ] + + operations = [ + migrations.AlterField( + model_name="collaborative", + name="slug", + field=models.SlugField( + blank=True, + max_length=75, + null=True, + unique=True, + validators=[ + django.core.validators.RegexValidator( + message="Slug must be lowercase and contain only alphanumeric characters and hyphens.", + regex="^[a-z0-9]+(?:-[a-z0-9]+)*$", + ) + ], + ), + ), + ] diff --git a/api/migrations/0047_resourcetype_publication_collaborative_publications_and_more.py b/api/migrations/0047_resourcetype_publication_collaborative_publications_and_more.py new file mode 100644 index 00000000..4b9948b2 --- /dev/null +++ b/api/migrations/0047_resourcetype_publication_collaborative_publications_and_more.py @@ -0,0 +1,226 @@ +# Generated by Django 5.0.4 on 2026-08-26 08:13 + +import api.utils.file_paths +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0046_alter_collaborative_slug"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="ResourceType", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, editable=False, primary_key=True, serialize=False + ), + ), + ("name", models.CharField(max_length=75, unique=True)), + ("description", models.CharField(blank=True, max_length=1000, null=True)), + ("slug", models.SlugField(max_length=75, null=True, unique=True)), + ("is_active", models.BooleanField(default=True)), + ], + options={ + "verbose_name": "Resource Type", + "verbose_name_plural": "Resource Types", + "db_table": "resource_type", + "ordering": ["name"], + }, + ), + migrations.CreateModel( + name="Publication", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, editable=False, primary_key=True, serialize=False + ), + ), + ("title", models.CharField(blank=True, max_length=300)), + ("description", models.TextField(blank=True, null=True)), + ("slug", models.SlugField(max_length=255, unique=True)), + ("authors", models.JSONField(blank=True, default=list)), + ("publication_date", models.DateField(blank=True, null=True)), + ( + "license", + models.CharField( + choices=[ + ("GOVERNMENT_OPEN_DATA_LICENSE", "Government Open Data License"), + ("CC_BY_4_0_ATTRIBUTION", "Cc By 4 0 Attribution"), + ( + "CC_BY_SA_4_0_ATTRIBUTION_SHARE_ALIKE", + "Cc By Sa 4 0 Attribution Share Alike", + ), + ( + "OPEN_DATA_COMMONS_BY_ATTRIBUTION", + "Open Data Commons By Attribution", + ), + ("OPEN_DATABASE_LICENSE", "Open Database License"), + ], + default="CC_BY_4_0_ATTRIBUTION", + max_length=50, + ), + ), + ("external_source_link", models.URLField(blank=True, null=True)), + ( + "status", + models.CharField( + choices=[("DRAFT", "Draft"), ("PUBLISHED", "Published")], + default="DRAFT", + max_length=50, + ), + ), + ("download_count", models.IntegerField(default=0)), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ( + "geographies", + models.ManyToManyField( + blank=True, related_name="publications", to="api.geography" + ), + ), + ( + "organization", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="publications", + to="api.organization", + ), + ), + ( + "sectors", + models.ManyToManyField( + blank=True, related_name="publications", to="api.sector" + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="publications", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "resource_type", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="publications", + to="api.resourcetype", + ), + ), + ], + options={ + "verbose_name": "Publication", + "verbose_name_plural": "Publications", + "db_table": "publication", + "ordering": ["-modified"], + }, + ), + migrations.AddField( + model_name="collaborative", + name="publications", + field=models.ManyToManyField(blank=True, to="api.publication"), + ), + migrations.AddField( + model_name="usecase", + name="publications", + field=models.ManyToManyField(blank=True, to="api.publication"), + ), + migrations.CreateModel( + name="PublicationBlock", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, editable=False, primary_key=True, serialize=False + ), + ), + ("position", models.PositiveIntegerField(default=0)), + ( + "block_type", + models.CharField( + choices=[("FILE", "File"), ("YOUTUBE", "Youtube")], max_length=20 + ), + ), + ( + "file", + models.FileField( + blank=True, + max_length=300, + upload_to=api.utils.file_paths._publication_block_directory_path, + ), + ), + ("file_name", models.CharField(blank=True, max_length=300)), + ("file_format", models.CharField(blank=True, max_length=50)), + ("file_size", models.BigIntegerField(blank=True, null=True)), + ("youtube_url", models.URLField(blank=True, null=True)), + ("youtube_video_id", models.CharField(blank=True, max_length=20)), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ( + "publication", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="blocks", + to="api.publication", + ), + ), + ], + options={ + "db_table": "publication_block", + "ordering": ["position"], + "indexes": [ + models.Index( + fields=["publication", "position"], name="publication_publica_bbb551_idx" + ) + ], + }, + ), + migrations.AddConstraint( + model_name="publicationblock", + constraint=models.CheckConstraint( + check=models.Q( + models.Q( + ("block_type", "FILE"), + ("youtube_url__isnull", True), + models.Q(("file", ""), _negated=True), + ), + models.Q( + ("block_type", "YOUTUBE"), ("file", ""), ("youtube_url__isnull", False) + ), + _connector="OR", + ), + name="publicationblock_file_xor_youtube", + ), + ), + migrations.AddIndex( + model_name="publication", + index=models.Index( + fields=["organization", "-modified"], name="publication_organiz_c16e7b_idx" + ), + ), + migrations.AddIndex( + model_name="publication", + index=models.Index(fields=["user", "-modified"], name="publication_user_id_02e48d_idx"), + ), + migrations.AddIndex( + model_name="publication", + index=models.Index(fields=["status"], name="publication_status_2449b8_idx"), + ), + ] diff --git a/authorization/migrations/0001_initial.py b/authorization/migrations/0001_initial.py new file mode 100644 index 00000000..cda857b9 --- /dev/null +++ b/authorization/migrations/0001_initial.py @@ -0,0 +1,261 @@ +# Generated by Django 5.0.4 on 2025-04-22 14:21 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("api", "0023_alter_usecase_summary"), + ("auth", "0012_alter_user_first_name_max_length"), + ] + + operations = [ + migrations.CreateModel( + name="Role", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50, unique=True)), + ("description", models.TextField(blank=True, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("can_view", models.BooleanField(default=True)), + ("can_add", models.BooleanField(default=False)), + ("can_change", models.BooleanField(default=False)), + ("can_delete", models.BooleanField(default=False)), + ], + options={ + "db_table": "role", + }, + ), + migrations.CreateModel( + name="User", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ( + "username", + models.CharField( + error_messages={ + "unique": "A user with that username already exists." + }, + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + max_length=150, + unique=True, + validators=[ + django.contrib.auth.validators.UnicodeUsernameValidator() + ], + verbose_name="username", + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=150, verbose_name="first name" + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=150, verbose_name="last name" + ), + ), + ( + "email", + models.EmailField( + blank=True, max_length=254, verbose_name="email address" + ), + ), + ( + "is_staff", + models.BooleanField( + default=False, + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", + ), + ), + ( + "is_active", + models.BooleanField( + default=True, + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", + ), + ), + ( + "date_joined", + models.DateTimeField( + default=django.utils.timezone.now, verbose_name="date joined" + ), + ), + ( + "keycloak_id", + models.CharField( + blank=True, max_length=255, null=True, unique=True + ), + ), + ("bio", models.TextField(blank=True, null=True)), + ( + "profile_picture", + models.ImageField( + blank=True, null=True, upload_to="profile_pictures/" + ), + ), + ( + "groups", + models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.group", + verbose_name="groups", + ), + ), + ( + "user_permissions", + models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.permission", + verbose_name="user permissions", + ), + ), + ], + options={ + "db_table": "user", + }, + managers=[ + ("objects", django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name="OrganizationMembership", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "organization", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="api.organization", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "role", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="authorization.role", + ), + ), + ], + options={ + "db_table": "organization_membership", + "unique_together": {("user", "organization")}, + }, + ), + migrations.AddField( + model_name="user", + name="organizations", + field=models.ManyToManyField( + related_name="members", + through="authorization.OrganizationMembership", + to="api.organization", + ), + ), + migrations.CreateModel( + name="DatasetPermission", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "dataset", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="api.dataset" + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "role", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="authorization.role", + ), + ), + ], + options={ + "db_table": "dataset_permission", + "unique_together": {("user", "dataset")}, + }, + ), + ] diff --git a/authorization/migrations/0002_alter_user_table.py b/authorization/migrations/0002_alter_user_table.py new file mode 100644 index 00000000..29987fa0 --- /dev/null +++ b/authorization/migrations/0002_alter_user_table.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.4 on 2025-04-28 08:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("authorization", "0001_initial"), + ] + + operations = [ + migrations.AlterModelTable( + name="user", + table="ds_user", + ), + ] diff --git a/authorization/migrations/0003_user_github_profile_user_linkedin_profile_and_more.py b/authorization/migrations/0003_user_github_profile_user_linkedin_profile_and_more.py new file mode 100644 index 00000000..2986fc7e --- /dev/null +++ b/authorization/migrations/0003_user_github_profile_user_linkedin_profile_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.0.4 on 2025-05-09 14:11 + +import api.utils.file_paths +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authorization", "0002_alter_user_table"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="github_profile", + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name="user", + name="linkedin_profile", + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name="user", + name="location", + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name="user", + name="twitter_profile", + field=models.URLField(blank=True, null=True), + ), + migrations.AlterField( + model_name="user", + name="profile_picture", + field=models.ImageField( + blank=True, + null=True, + upload_to=api.utils.file_paths._user_profile_image_directory_path, + ), + ), + ] diff --git a/authorization/migrations/0004_userconsent.py b/authorization/migrations/0004_userconsent.py new file mode 100644 index 00000000..6e7a1525 --- /dev/null +++ b/authorization/migrations/0004_userconsent.py @@ -0,0 +1,50 @@ +# Generated by Django 5.0.4 on 2025-05-27 11:57 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authorization", "0003_user_github_profile_user_linkedin_profile_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="UserConsent", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("activity_tracking_enabled", models.BooleanField(default=False)), + ("consent_given_at", models.DateTimeField(blank=True, null=True)), + ("consent_updated_at", models.DateTimeField(auto_now=True)), + ( + "consent_ip_address", + models.GenericIPAddressField(blank=True, null=True), + ), + ("consent_user_agent", models.TextField(blank=True, null=True)), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="activity_consent", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name": "User Consent", + "verbose_name_plural": "User Consents", + "db_table": "user_consent", + }, + ), + ] diff --git a/authorization/migrations/__init__.py b/authorization/migrations/__init__.py new file mode 100644 index 00000000..e69de29b