diff --git a/code_check.py b/code_check.py index 7822dea..56fb571 100755 --- a/code_check.py +++ b/code_check.py @@ -39,7 +39,7 @@ def status(line): cmd("black dash test_runner") status("Running ruff") - cmd("ruff dash") + cmd("ruff check dash") status("Running isort") cmd("isort dash") diff --git a/dash/categories/migrations/0001_squashed.py b/dash/categories/migrations/0001_squashed.py new file mode 100644 index 0000000..06ca8cd --- /dev/null +++ b/dash/categories/migrations/0001_squashed.py @@ -0,0 +1,284 @@ +# Generated by Django 5.0.8 on 2024-08-22 09:06 + +import functools + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + +import dash.utils + + +class Migration(migrations.Migration): + + replaces = [ + ("categories", "0001_initial"), + ("categories", "0002_auto_20140820_1415"), + ("categories", "0003_categoryimage"), + ("categories", "0004_auto_20140904_0927"), + ("categories", "0005_auto_20140922_1514"), + ("categories", "0006_auto_20141008_1955"), + ("categories", "0007_auto_20170301_0914"), + ("categories", "0008_alter_category_options"), + ("categories", "0009_auto_20210910_1450"), + ("categories", "0010_alter_category_created_by_alter_category_modified_by_and_more"), + ("categories", "0011_alter_category_unique_together_and_more"), + ] + + initial = True + + dependencies = [ + ("orgs", "0005_orgbackground"), + ("orgs", "0033_rename_orgs_orgbac_org_id_607508_idx_orgs_orgbac_org_slug_idx_and_more"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Category", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField(auto_now_add=True, help_text="When this item was originally created"), + ), + ("modified_on", models.DateTimeField(auto_now=True, help_text="When this item was last modified")), + ("name", models.CharField(help_text="The name of this category", max_length=64)), + ( + "image", + models.ImageField( + blank=True, + help_text="An optional image that can describe this category", + null=True, + upload_to="categories", + ), + ), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "org", + models.ForeignKey( + help_text="The organization this category applies to", + on_delete=django.db.models.deletion.PROTECT, + to="orgs.org", + ), + ), + ], + options={ + "verbose_name_plural": "Categories", + }, + ), + migrations.CreateModel( + name="CategoryImage", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField(auto_now_add=True, help_text="When this item was originally created"), + ), + ("modified_on", models.DateTimeField(auto_now=True, help_text="When this item was last modified")), + ("name", models.CharField(help_text="The name to describe this image", max_length=64)), + ("image", models.ImageField(help_text="The image file to use", upload_to="categories")), + ( + "category", + models.ForeignKey( + help_text="The category this image represents", + on_delete=django.db.models.deletion.PROTECT, + to="categories.category", + ), + ), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.AlterField( + model_name="category", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="categories_category_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="category", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="categories_category_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="categoryimage", + name="category", + field=models.ForeignKey( + help_text="The category this image represents", + on_delete=django.db.models.deletion.PROTECT, + related_name="images", + to="categories.category", + ), + ), + migrations.AlterField( + model_name="category", + name="org", + field=models.ForeignKey( + help_text="The organization this category applies to", + on_delete=django.db.models.deletion.PROTECT, + related_name="categories", + to="orgs.org", + ), + ), + migrations.AlterField( + model_name="category", + name="created_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + migrations.AlterField( + model_name="category", + name="modified_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + migrations.AlterField( + model_name="categoryimage", + name="created_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + migrations.AlterField( + model_name="categoryimage", + name="modified_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + migrations.AlterModelOptions( + name="category", + options={"ordering": ["name"], "verbose_name_plural": "Categories"}, + ), + migrations.AlterField( + model_name="category", + name="image", + field=models.ImageField( + blank=True, + help_text="An optional image that can describe this category", + null=True, + upload_to=functools.partial(dash.utils.generate_file_path, *("categories",), **{}), + ), + ), + migrations.AlterField( + model_name="categoryimage", + name="image", + field=models.ImageField( + help_text="The image file to use", + upload_to=functools.partial(dash.utils.generate_file_path, *("categories",), **{}), + ), + ), + migrations.AlterField( + model_name="category", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="category", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="categoryimage", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="categoryimage", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddConstraint( + model_name="category", + constraint=models.UniqueConstraint(fields=("name", "org"), name="categories_category_name_unique"), + ), + ] diff --git a/dash/categories/migrations/0011_alter_category_unique_together_and_more.py b/dash/categories/migrations/0011_alter_category_unique_together_and_more.py new file mode 100644 index 0000000..302439d --- /dev/null +++ b/dash/categories/migrations/0011_alter_category_unique_together_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.8 on 2024-08-21 16:03 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("categories", "0010_alter_category_created_by_alter_category_modified_by_and_more"), + ("orgs", "0033_rename_orgs_orgbac_org_id_607508_idx_orgs_orgbac_org_slug_idx_and_more"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="category", + unique_together=set(), + ), + migrations.AddConstraint( + model_name="category", + constraint=models.UniqueConstraint(fields=("name", "org"), name="categories_category_name_unique"), + ), + ] diff --git a/dash/categories/models.py b/dash/categories/models.py index 875aec0..2f079f9 100644 --- a/dash/categories/models.py +++ b/dash/categories/models.py @@ -50,7 +50,7 @@ def __str__(self): class Meta: ordering = ["name"] - unique_together = ("name", "org") + constraints = [models.UniqueConstraint(fields=["name", "org"], name="categories_category_name_unique")] verbose_name_plural = _("Categories") diff --git a/dash/dashblocks/migrations/0001_squashed.py b/dash/dashblocks/migrations/0001_squashed.py new file mode 100644 index 0000000..1986710 --- /dev/null +++ b/dash/dashblocks/migrations/0001_squashed.py @@ -0,0 +1,646 @@ +# Generated by Django 5.0.8 on 2024-08-22 09:04 + +import functools + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + +import dash.utils + + +def generate_initial_block_types(apps, schema_editor): + User = apps.get_model("auth", "User") + root = User.objects.filter(username="root").first() + + if not root: + root = User.objects.filter(username="root2").first() + + if not root: + root = User.objects.create(username="root2") + + DashBlockType = apps.get_model("dashblocks", "DashBlockType") + + DashBlockType.objects.get_or_create( + name="About", + slug="about", + description="About pages by highest priority", + has_title=True, + has_image=True, + has_rich_text=False, + has_summary=False, + has_link=False, + has_gallery=False, + has_color=False, + has_video=False, + has_tags=False, + created_by=root, + modified_by=root, + ) + + DashBlockType.objects.get_or_create( + name="Join & Engage", + slug="join_engage", + description="Join & Engage by highest priority", + has_title=True, + has_image=True, + has_rich_text=False, + has_summary=False, + has_link=False, + has_gallery=False, + has_color=False, + has_video=False, + has_tags=False, + created_by=root, + modified_by=root, + ) + + DashBlockType.objects.get_or_create( + name="YouTube Videos", + slug="youtube_videos", + description="YouTube Videos to embed on the website", + has_title=True, + has_image=False, + has_rich_text=False, + has_summary=False, + has_link=False, + has_gallery=False, + has_color=False, + has_video=True, + has_tags=False, + created_by=root, + modified_by=root, + ) + + DashBlockType.objects.get_or_create( + name="U-Reporters", + slug="ureporters", + description="U-Reporters Page", + has_title=True, + has_image=True, + has_rich_text=False, + has_summary=False, + has_link=False, + has_gallery=False, + has_color=False, + has_video=False, + has_tags=False, + created_by=root, + modified_by=root, + ) + + DashBlockType.objects.get_or_create( + name="Missions", + slug="missions", + description="U-Report missions blocks", + has_title=True, + has_image=True, + has_rich_text=False, + has_summary=False, + has_link=False, + has_gallery=False, + has_color=False, + has_video=False, + has_tags=False, + created_by=root, + modified_by=root, + ) + + DashBlockType.objects.get_or_create( + name="Contact Us", + slug="contact_us", + description="U-Report contact us blocks", + has_title=True, + has_image=False, + has_rich_text=False, + has_summary=False, + has_link=False, + has_gallery=False, + has_color=False, + has_video=False, + has_tags=False, + created_by=root, + modified_by=root, + ) + + DashBlockType.objects.get_or_create( + name="Join Steps", + slug="join_steps", + description="U-Report join steps", + has_title=True, + has_image=True, + has_rich_text=False, + has_summary=False, + has_link=False, + has_gallery=False, + has_color=False, + has_video=False, + has_tags=False, + created_by=root, + modified_by=root, + ) + + +def noop(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + replaces = [ + ("dashblocks", "0001_initial"), + ("dashblocks", "0002_auto_20140802_2112"), + ("dashblocks", "0003_auto_20140804_0236"), + ("dashblocks", "0004_auto_20140902_1200"), + ("dashblocks", "0005_auto_20140904_0957"), + ("dashblocks", "0006_auto_20140922_1514"), + ("dashblocks", "0007_auto_20170301_0914"), + ("dashblocks", "0008_auto_20210902_1125"), + ("dashblocks", "0009_auto_20210910_1450"), + ("dashblocks", "0010_alter_dashblock_created_by_and_more"), + ("dashblocks", "0011_alter_dashblock_index_together_and_more"), + ( + "dashblocks", + "0012_rename_dashblock_org_is_active_dashblock_type_priority_dashblocks__org_id_024805_idx_and_more", + ), + ("dashblocks", "0013_rename_dashblocks__org_id_024805_idx_dashblock_org_typ_prio_idx_and_more"), + ] + + initial = True + + dependencies = [ + ("orgs", "0001_initial"), + ("orgs", "0031_alter_orgbackend_index_together"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="DashBlock", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField(auto_now_add=True, help_text="When this item was originally created"), + ), + ("modified_on", models.DateTimeField(auto_now=True, help_text="When this item was last modified")), + ( + "title", + models.CharField( + blank=True, + help_text="The title for this block of content, optional", + max_length=255, + null=True, + ), + ), + ( + "summary", + models.TextField(blank=True, help_text="The summary for this item, should be short", null=True), + ), + ( + "content", + models.TextField( + blank=True, help_text="The body of text for this content block, optional", null=True + ), + ), + ( + "image", + models.ImageField( + blank=True, + help_text="Any image that should be displayed with this content block, optional", + null=True, + upload_to="dashblocks", + ), + ), + ( + "color", + models.CharField( + blank=True, + help_text="A background color to use for the image, in the format: #rrggbb", + max_length=16, + null=True, + ), + ), + ( + "link", + models.CharField( + blank=True, + help_text="Any link that should be associated with this content block, optional", + max_length=255, + null=True, + ), + ), + ( + "video_id", + models.CharField( + blank=True, + help_text="The id of the YouTube video that should be linked to this item", + max_length=255, + null=True, + ), + ), + ( + "tags", + models.CharField( + blank=True, + help_text="Any tags for this content block, separated by spaces, can be used to do more advanced filtering, optional", + max_length=255, + null=True, + ), + ), + ( + "priority", + models.IntegerField( + default=0, help_text="The priority for this block, higher priority blocks come first" + ), + ), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "org", + models.ForeignKey( + help_text="The organization this content block belongs to", + on_delete=django.db.models.deletion.PROTECT, + to="orgs.org", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="DashBlockImage", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField(auto_now_add=True, help_text="When this item was originally created"), + ), + ("modified_on", models.DateTimeField(auto_now=True, help_text="When this item was last modified")), + ( + "image", + models.ImageField(height_field="height", upload_to="dashblock_images/", width_field="width"), + ), + ("caption", models.CharField(max_length=64)), + ("priority", models.IntegerField(blank=True, default=0, null=True)), + ("width", models.IntegerField()), + ("height", models.IntegerField()), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "dashblock", + models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="dashblocks.dashblock"), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="DashBlockType", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField(auto_now_add=True, help_text="When this item was originally created"), + ), + ("modified_on", models.DateTimeField(auto_now=True, help_text="When this item was last modified")), + ( + "name", + models.CharField( + help_text="The human readable name for this content type", max_length=75, unique=True + ), + ), + ( + "slug", + models.SlugField( + help_text="The slug to idenfity this content type, used with the template tags", unique=True + ), + ), + ( + "description", + models.TextField( + blank=True, + help_text="A description of where this content type is used on the site and how it will be dsiplayed", + null=True, + ), + ), + ( + "has_title", + models.BooleanField(default=True, help_text="Whether this content should include a title"), + ), + ( + "has_image", + models.BooleanField(default=True, help_text="Whether this content should include an image"), + ), + ( + "has_rich_text", + models.BooleanField(default=True, help_text="Whether this content should use a rich HTML editor"), + ), + ( + "has_summary", + models.BooleanField(default=True, help_text="Whether this content should include a summary field"), + ), + ( + "has_link", + models.BooleanField(default=True, help_text="Whether this content should include a link"), + ), + ( + "has_gallery", + models.BooleanField( + default=False, + help_text="Whether this content should allow upload of additional images, ie a gallery", + ), + ), + ("has_color", models.BooleanField(default=False, help_text="Whether this content has a color field")), + ( + "has_video", + models.BooleanField( + default=False, help_text="Whether this content should allow setting a YouTube id" + ), + ), + ("has_tags", models.BooleanField(default=False, help_text="Whether this content should allow tags")), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.AddField( + model_name="dashblock", + name="dashblock_type", + field=models.ForeignKey( + help_text="The category, or type for this content block", + on_delete=django.db.models.deletion.PROTECT, + to="dashblocks.dashblocktype", + verbose_name="Content Type", + ), + ), + migrations.RunPython(code=generate_initial_block_types, reverse_code=noop), + migrations.AlterField( + model_name="dashblock", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="dashblocks_dashblock_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblock", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="dashblocks_dashblock_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblockimage", + name="dashblock", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name="images", to="dashblocks.dashblock" + ), + ), + migrations.AlterField( + model_name="dashblocktype", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="dashblocks_dashblocktype_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblocktype", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="dashblocks_dashblocktype_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblock", + name="created_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + migrations.AlterField( + model_name="dashblock", + name="modified_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + migrations.AlterField( + model_name="dashblockimage", + name="created_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + migrations.AlterField( + model_name="dashblockimage", + name="modified_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + migrations.AlterField( + model_name="dashblocktype", + name="created_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + migrations.AlterField( + model_name="dashblocktype", + name="modified_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + migrations.AlterModelOptions( + name="dashblock", + options={"ordering": ["dashblock_type", "title"]}, + ), + migrations.AlterModelOptions( + name="dashblocktype", + options={"ordering": ["name"]}, + ), + migrations.AlterField( + model_name="dashblock", + name="image", + field=models.ImageField( + blank=True, + help_text="Any image that should be displayed with this content block, optional", + null=True, + upload_to=functools.partial(dash.utils.generate_file_path, *("dashblocks",), **{}), + ), + ), + migrations.AlterField( + model_name="dashblockimage", + name="image", + field=models.ImageField( + height_field="height", + upload_to=functools.partial(dash.utils.generate_file_path, *("dashblock_images/",), **{}), + width_field="width", + ), + ), + migrations.AlterField( + model_name="dashblock", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblock", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblockimage", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblockimage", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblocktype", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dashblocktype", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddIndex( + model_name="dashblock", + index=models.Index( + fields=["org", "is_active", "dashblock_type", "priority"], name="dashblock_org_typ_prio_idx" + ), + ), + migrations.AddIndex( + model_name="dashblocktype", index=models.Index(fields=["slug", "name"], name="dashblocktype_slug_name_idx") + ), + ] diff --git a/dash/dashblocks/migrations/0013_rename_dashblocks__org_id_024805_idx_dashblock_org_typ_prio_idx_and_more.py b/dash/dashblocks/migrations/0013_rename_dashblocks__org_id_024805_idx_dashblock_org_typ_prio_idx_and_more.py new file mode 100644 index 0000000..3f1e55f --- /dev/null +++ b/dash/dashblocks/migrations/0013_rename_dashblocks__org_id_024805_idx_dashblock_org_typ_prio_idx_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.8 on 2024-08-21 16:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "dashblocks", + "0012_rename_dashblock_org_is_active_dashblock_type_priority_dashblocks__org_id_024805_idx_and_more", + ), + ] + + operations = [ + migrations.RenameIndex( + model_name="dashblock", + new_name="dashblock_org_typ_prio_idx", + old_name="dashblocks__org_id_024805_idx", + ), + migrations.RenameIndex( + model_name="dashblocktype", + new_name="dashblocktype_slug_name_idx", + old_name="dashblocks__slug_c0c6c6_idx", + ), + ] diff --git a/dash/dashblocks/models.py b/dash/dashblocks/models.py index 101e99e..8da9df9 100644 --- a/dash/dashblocks/models.py +++ b/dash/dashblocks/models.py @@ -49,7 +49,7 @@ def __str__(self): class Meta: ordering = ["name"] - indexes = [models.Index(fields=["slug", "name"])] + indexes = [models.Index(fields=["slug", "name"], name="dashblocktype_slug_name_idx")] class DashBlock(SmartModel): @@ -149,7 +149,9 @@ def __str__(self): class Meta: ordering = ["dashblock_type", "title"] - indexes = [models.Index(fields=["org", "is_active", "dashblock_type", "priority"])] + indexes = [ + models.Index(fields=["org", "is_active", "dashblock_type", "priority"], name="dashblock_org_typ_prio_idx") + ] class DashBlockImage(SmartModel): diff --git a/dash/orgs/migrations/0001_squashed.py b/dash/orgs/migrations/0001_squashed.py new file mode 100644 index 0000000..aca1a8d --- /dev/null +++ b/dash/orgs/migrations/0001_squashed.py @@ -0,0 +1,738 @@ +# Generated by Django 5.0.8 on 2024-08-22 09:02 + +import functools +import json + +import timezone_field.fields + +import django.contrib.postgres.fields.jsonb +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + +import dash.utils + + +# Functions from the following migrations need manual copying. +# Move them and any dependencies into this file, then update the +# RunPython operations to refer to the local versions: +# dash.orgs.migrations.0019_restructure_org_config +def migrate_api_token_and_common_org_config(apps, schema_editor): + Org = apps.get_model("orgs", "Org") + orgs = Org.objects.all() + + for org in orgs: + if not org.config: + old_config = dict() + else: + old_config = json.loads(org.config) + + if "common" in old_config and "rapidpro" in old_config: + print("Skipped org(%d), it looks like already migrated" % org.id) + continue + + new_config = {"common": old_config, "rapidpro": {"api_token": org.api_token}} + org.config = json.dumps(new_config) + org.save() + + +def noop(apps, schema_editor): + pass + + +# dash.orgs.migrations.0022_populate_rapidpro_specific_config +def populate_rapidpro_config(apps, schema_editor): + Org = apps.get_model("orgs", "Org") + orgs = Org.objects.all() + + for org in orgs: + if not org.config: + continue + + backends_config_dict = getattr(settings, "DATA_API_BACKENDS_CONFIG", {}) + for backend_slug in backends_config_dict: + config_fields = getattr(settings, "BACKENDS_ORG_CONFIG_FIELDS", []) + for config_field in config_fields: + name = config_field["name"] + existing_value = org.config["common"].get(name, None) + if backend_slug not in org.config: + org.config[backend_slug] = dict() + org.config[backend_slug][name] = existing_value + + org.save() + + +# dash.orgs.migrations.0024_populate_org_backend +def populate_org_backend(apps, schema_editor): + Org = apps.get_model("orgs", "Org") + OrgBackend = apps.get_model("orgs", "OrgBackend") + orgs = Org.objects.all() + User = apps.get_model("auth", "User") + root = User.objects.filter(username="root").first() + + if not root: + root = User.objects.filter(username="root2").first() + + if not root: + root = User.objects.create(username="root2") + + default_backend = getattr(settings, "SITE_BACKEND", None) + host = getattr(settings, "SITE_API_HOST", None) + + for org in orgs: + if not org.config: + continue + + config = org.config + rapidpro_config = config.get("rapidpro", dict()) + api_token = rapidpro_config.get("api_token", "") + OrgBackend.objects.create( + org=org, + slug="rapidpro", + api_token=api_token, + host=host, + backend_type=default_backend, + created_by=root, + modified_by=root, + ) + + del rapidpro_config["api_token"] + config["rapipro"] = rapidpro_config + org.config = config + org.save() + + +# dash.orgs.migrations.0026_fix_org_config_rapidpro +def fix_org_config_rapidpro(apps, schema_editor): + Org = apps.get_model("orgs", "Org") + orgs = Org.objects.all() + for org in orgs: + if not org.config: + continue + + config = org.config + rapidpro_config = config.get("rapidpro", dict()) + if "api_token" in rapidpro_config: + del rapidpro_config["api_token"] + + if "rapipro" in config: + del config["rapipro"] # remove the mistakenly added key by typo in 0024_populate_org_backend + + config["rapidpro"] = rapidpro_config + org.config = config + org.save() + + +class Migration(migrations.Migration): + + replaces = [ + ("orgs", "0001_initial"), + ("orgs", "0002_auto_20140802_2104"), + ("orgs", "0003_org_logo"), + ("orgs", "0004_auto_20140804_1453"), + ("orgs", "0005_orgbackground"), + ("orgs", "0006_auto_20140919_2056"), + ("orgs", "0007_auto_20140922_1514"), + ("orgs", "0008_org_timezone"), + ("orgs", "0009_auto_20150331_1452"), + ("orgs", "0010_auto_20150618_1042"), + ("orgs", "0011_auto_20150710_1612"), + ("orgs", "0012_auto_20150715_1816"), + ("orgs", "0013_auto_20150715_1831"), + ("orgs", "0014_auto_20150722_1419"), + ("orgs", "0015_auto_20160209_0926"), + ("orgs", "0016_taskstate_is_disabled"), + ("orgs", "0017_auto_20161026_1513"), + ("orgs", "0018_auto_20170301_0914"), + ("orgs", "0019_restructure_org_config"), + ("orgs", "0020_remove_org_api_token"), + ("orgs", "0021_auto_20180315_0823"), + ("orgs", "0022_populate_rapidpro_specific_config"), + ("orgs", "0023_orgbackend"), + ("orgs", "0024_populate_org_backend"), + ("orgs", "0025_auto_20180322_1415"), + ("orgs", "0026_fix_org_config_rapidpro"), + ("orgs", "0027_alter_org_options"), + ("orgs", "0028_alter_org_config"), + ("orgs", "0029_auto_20211025_1504"), + ("orgs", "0030_alter_invitation_created_by_and_more"), + ("orgs", "0031_alter_orgbackend_index_together"), + ("orgs", "0032_rename_orgbackend_org_is_active_slug_orgs_orgbac_org_id_607508_idx"), + ("orgs", "0033_rename_orgs_orgbac_org_id_607508_idx_orgs_orgbac_org_slug_idx_and_more"), + ] + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Org", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + ( + "modified_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + ( + "name", + models.CharField(help_text="The name of this organization", max_length=128, verbose_name="Name"), + ), + ( + "language", + models.CharField( + blank=True, + help_text="The main language used by this organization", + max_length=64, + null=True, + verbose_name="Language", + ), + ), + ( + "subdomain", + models.SlugField( + blank=True, + error_messages={"unique": "This subdomain is not available"}, + help_text="The subdomain for this organization", + max_length=255, + null=True, + unique=True, + verbose_name="Subdomain", + ), + ), + ( + "api_token", + models.CharField( + blank=True, + help_text="The API token for the RapidPro account this dashboard is tied to", + max_length=128, + null=True, + ), + ), + ( + "config", + models.TextField( + blank=True, + help_text="JSON blob used to store configuration information associated with this organization", + null=True, + ), + ), + ( + "administrators", + models.ManyToManyField( + help_text="The administrators in your organization", + related_name="org_admins", + to=settings.AUTH_USER_MODEL, + verbose_name="Administrators", + ), + ), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="orgs_org_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "editors", + models.ManyToManyField( + help_text="The editors in your organization", + related_name="org_editors", + to=settings.AUTH_USER_MODEL, + verbose_name="Editors", + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="orgs_org_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "viewers", + models.ManyToManyField( + help_text="The viewers in your organization", + related_name="org_viewers", + to=settings.AUTH_USER_MODEL, + verbose_name="Viewers", + ), + ), + ( + "logo", + models.ImageField( + blank=True, + help_text="The logo that should be used for this organization", + null=True, + upload_to="logos", + ), + ), + ( + "timezone", + timezone_field.fields.TimeZoneField( + default="UTC", help_text="The timezone your organization is in.", verbose_name="Timezone" + ), + ), + ( + "domain", + models.CharField( + blank=True, + error_messages={"unique": "This domain is not available"}, + help_text="The custom domain for this organization", + max_length=255, + null=True, + unique=True, + verbose_name="Domain", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="TaskState", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("task_key", models.CharField(max_length=32)), + ("started_on", models.DateTimeField(null=True)), + ("ended_on", models.DateTimeField(null=True)), + ("last_successfully_started_on", models.DateTimeField(null=True)), + ("last_results", models.TextField(null=True)), + ("is_failing", models.BooleanField(default=False)), + ( + "org", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name="task_states", to="orgs.org" + ), + ), + ("is_disabled", models.BooleanField(default=False)), + ], + options={}, + ), + migrations.CreateModel( + name="Invitation", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + ( + "modified_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + ( + "email", + models.EmailField( + help_text="The email to which we send the invitation of the viewer", + max_length=254, + verbose_name="Email", + ), + ), + ( + "secret", + models.CharField( + help_text="a unique code associated with this invitation", + max_length=64, + unique=True, + verbose_name="Secret", + ), + ), + ( + "user_group", + models.CharField( + choices=[("A", "Administrator"), ("E", "Editor"), ("V", "Viewer")], + default="V", + max_length=1, + verbose_name="User Role", + ), + ), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="orgs_invitation_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="orgs_invitation_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "org", + models.ForeignKey( + help_text="The organization to which the account is invited to view", + on_delete=django.db.models.deletion.PROTECT, + related_name="invitations", + to="orgs.org", + verbose_name="Org", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="OrgBackground", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + ( + "modified_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + ( + "name", + models.CharField( + help_text="The name to describe this background", max_length=128, verbose_name="Name" + ), + ), + ( + "background_type", + models.CharField( + choices=[("B", "Banner"), ("P", "Pattern")], + default="P", + max_length=1, + verbose_name="Background type", + ), + ), + ("image", models.ImageField(help_text="The image file", upload_to="org_bgs")), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="orgs_orgbackground_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="orgs_orgbackground_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "org", + models.ForeignKey( + help_text="The organization in which the image will be used", + on_delete=django.db.models.deletion.PROTECT, + related_name="backgrounds", + to="orgs.org", + verbose_name="Org", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.RunPython( + code=migrate_api_token_and_common_org_config, + reverse_code=noop, + ), + migrations.RemoveField( + model_name="org", + name="api_token", + ), + migrations.AlterField( + model_name="org", + name="config", + field=django.contrib.postgres.fields.jsonb.JSONField( + default=dict, + help_text="JSON blob used to store configuration information associated with this organization", + ), + ), + migrations.RunPython( + code=populate_rapidpro_config, + reverse_code=noop, + ), + migrations.CreateModel( + name="OrgBackend", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + ( + "modified_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + ( + "api_token", + models.CharField( + blank=True, help_text="The API token for this backend", max_length=128, null=True + ), + ), + ("host", models.CharField(blank=True, max_length=128, null=True)), + ("slug", models.CharField(max_length=16)), + ("backend_type", models.CharField(blank=True, max_length=256, null=True)), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="orgs_orgbackend_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="orgs_orgbackend_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "org", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, related_name="backends", to="orgs.org" + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.RunPython( + code=populate_org_backend, + reverse_code=noop, + ), + migrations.AlterField( + model_name="orgbackend", + name="api_token", + field=models.CharField(default="", help_text="The API token for this backend", max_length=128), + preserve_default=False, + ), + migrations.AlterField( + model_name="orgbackend", + name="backend_type", + field=models.CharField(default="", max_length=256), + preserve_default=False, + ), + migrations.AlterField( + model_name="orgbackend", + name="host", + field=models.CharField(default="", max_length=128), + preserve_default=False, + ), + migrations.RunPython( + code=fix_org_config_rapidpro, + reverse_code=noop, + ), + migrations.AlterModelOptions( + name="org", + options={"ordering": ["name"]}, + ), + migrations.AlterField( + model_name="org", + name="config", + field=models.JSONField( + default=dict, + help_text="JSON blob used to store configuration information associated with this organization", + ), + ), + migrations.AlterField( + model_name="org", + name="logo", + field=models.ImageField( + blank=True, + help_text="The logo that should be used for this organization", + null=True, + upload_to=functools.partial(dash.utils.generate_file_path, *("logos",), **{}), + ), + ), + migrations.AlterField( + model_name="orgbackground", + name="image", + field=models.ImageField( + help_text="The image file", + upload_to=functools.partial(dash.utils.generate_file_path, *("org_bgs",), **{}), + ), + ), + migrations.AlterField( + model_name="invitation", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="invitation", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="org", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="org", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="orgbackend", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="orgbackend", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="orgbackground", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="orgbackground", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddIndex( + model_name="orgbackend", + index=models.Index(fields=["org", "is_active", "slug"], name="orgs_orgbac_org_slug_idx"), + ), + migrations.AddConstraint( + model_name="orgbackend", + constraint=models.UniqueConstraint(fields=("org", "slug"), name="orgs_orgbackend_org_slug_unique"), + ), + migrations.AddConstraint( + model_name="taskstate", + constraint=models.UniqueConstraint(fields=("org", "task_key"), name="orgs_taskstate_org_task_key_unique"), + ), + ] diff --git a/dash/orgs/migrations/0033_rename_orgs_orgbac_org_id_607508_idx_orgs_orgbac_org_slug_idx_and_more.py b/dash/orgs/migrations/0033_rename_orgs_orgbac_org_id_607508_idx_orgs_orgbac_org_slug_idx_and_more.py new file mode 100644 index 0000000..909eb0e --- /dev/null +++ b/dash/orgs/migrations/0033_rename_orgs_orgbac_org_id_607508_idx_orgs_orgbac_org_slug_idx_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.0.8 on 2024-08-21 16:03 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("orgs", "0032_rename_orgbackend_org_is_active_slug_orgs_orgbac_org_id_607508_idx"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RenameIndex( + model_name="orgbackend", + new_name="orgs_orgbac_org_slug_idx", + old_name="orgs_orgbac_org_id_607508_idx", + ), + migrations.AlterUniqueTogether( + name="orgbackend", + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name="taskstate", + unique_together=set(), + ), + migrations.AddConstraint( + model_name="orgbackend", + constraint=models.UniqueConstraint(fields=("org", "slug"), name="orgs_orgbackend_org_slug_unique"), + ), + migrations.AddConstraint( + model_name="taskstate", + constraint=models.UniqueConstraint(fields=("org", "task_key"), name="orgs_taskstate_org_task_key_unique"), + ), + ] diff --git a/dash/orgs/models.py b/dash/orgs/models.py index 46b83b5..b48155c 100644 --- a/dash/orgs/models.py +++ b/dash/orgs/models.py @@ -397,7 +397,7 @@ def get_time_taken(self): return (until - self.started_on).total_seconds() class Meta: - unique_together = ("org", "task_key") + constraints = [models.UniqueConstraint(fields=["org", "task_key"], name="orgs_taskstate_org_task_key_unique")] class OrgBackend(SmartModel): @@ -415,5 +415,5 @@ def __str__(self): return self.slug class Meta: - unique_together = ("org", "slug") - indexes = [models.Index(fields=["org", "is_active", "slug"])] + constraints = [models.UniqueConstraint(fields=["org", "slug"], name="orgs_orgbackend_org_slug_unique")] + indexes = [models.Index(fields=["org", "is_active", "slug"], name="orgs_orgbac_org_slug_idx")] diff --git a/dash/stories/migrations/0001_squashed.py b/dash/stories/migrations/0001_squashed.py new file mode 100644 index 0000000..a5184c7 --- /dev/null +++ b/dash/stories/migrations/0001_squashed.py @@ -0,0 +1,361 @@ +# Generated by Django 5.0.8 on 2024-08-22 09:00 + +import functools + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + +import dash.stories.models +import dash.utils + + +# Functions from the following migrations need manual copying. +# Move them and any dependencies into this file, then update the +# RunPython operations to refer to the local versions: +# dash.stories.migrations.0004_auto_20140806_1540 +def populate_story_summary(apps, schema_editor): + Story = apps.get_model("stories", "Story") + for story in Story.objects.all(): + story.summary = story.content + story.save() + + +# dash.stories.migrations.0006_auto_20140821_0859 +def add_category_to_current_stories(apps, schema_editor): + Story = apps.get_model("stories", "Story") + Category = apps.get_model("categories", "Category") + + for story in Story.objects.all(): + general_category = Category.objects.get(name__icontains="general", org=story.org) + story.category = general_category + story.save() + + +# dash.stories.migrations.0008_auto_20140912_0524 +def move_images(apps, schema_editor): + # for each story we want to move any image currently on it and instead create a StoryImage for it + Story = apps.get_model("stories", "Story") + StoryImage = apps.get_model("stories", "StoryImage") + + for story in Story.objects.exclude(image=None): + StoryImage.objects.create( + story=story, image=story.image, created_by=story.created_by, modified_by=story.modified_by + ) + + +def noop(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + replaces = [ + ("stories", "0001_initial"), + ("stories", "0002_auto_20140805_1158"), + ("stories", "0003_story_summary"), + ("stories", "0004_auto_20140806_1540"), + ("stories", "0005_story_category"), + ("stories", "0006_auto_20140821_0859"), + ("stories", "0007_storyimage"), + ("stories", "0008_auto_20140912_0524"), + ("stories", "0009_remove_story_image"), + ("stories", "0010_auto_20140922_1514"), + ("stories", "0011_story_audio_link"), + ("stories", "0012_story_written_by"), + ("stories", "0013_auto_20170301_0914"), + ("stories", "0014_alter_storyimage_image"), + ("stories", "0015_story_attachment"), + ("stories", "0016_alter_story_created_by_alter_story_modified_by_and_more"), + ] + + initial = True + + dependencies = [ + ("categories", "0002_auto_20140820_1415"), + ("orgs", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Story", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField(auto_now_add=True, help_text="When this item was originally created"), + ), + ("modified_on", models.DateTimeField(auto_now=True, help_text="When this item was last modified")), + ("title", models.CharField(help_text="The title for this story", max_length=255)), + ("featured", models.BooleanField(default=False, help_text="Whether this story is featured")), + ("content", models.TextField(help_text="The body of text for the story")), + ( + "image", + models.ImageField( + blank=True, + help_text="Any image that should be displayed with this story", + null=True, + upload_to="stories", + ), + ), + ( + "video_id", + models.CharField( + blank=True, + help_text="The id of the YouTube video that should be linked to this story", + max_length=255, + null=True, + ), + ), + ( + "tags", + models.CharField( + blank=True, + help_text="Any tags for this story, separated by spaces, can be used to do more advanced filtering, optional", + max_length=255, + null=True, + ), + ), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "org", + models.ForeignKey( + help_text="The organization this story belongs to", + on_delete=django.db.models.deletion.PROTECT, + to="orgs.org", + ), + ), + ("summary", models.TextField(blank=True, help_text="The summary for the story", null=True)), + ], + options={ + "verbose_name_plural": "Stories", + }, + ), + migrations.RunPython( + code=populate_story_summary, + reverse_code=noop, + ), + migrations.AddField( + model_name="story", + name="category", + field=models.ForeignKey( + blank=True, + help_text="The category for this story", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="categories.category", + ), + ), + migrations.RunPython( + code=add_category_to_current_stories, + reverse_code=noop, + ), + migrations.CreateModel( + name="StoryImage", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField(auto_now_add=True, help_text="When this item was originally created"), + ), + ("modified_on", models.DateTimeField(auto_now=True, help_text="When this item was last modified")), + ("name", models.CharField(help_text="The name to describe this image", max_length=64)), + ("image", models.ImageField(help_text="The image file to use", upload_to="stories")), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "story", + models.ForeignKey( + help_text="The story to associate to", + on_delete=django.db.models.deletion.PROTECT, + to="stories.story", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.RunPython( + code=move_images, + reverse_code=noop, + ), + migrations.RemoveField( + model_name="story", + name="image", + ), + migrations.AlterField( + model_name="story", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="story", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="story", + name="video_id", + field=models.CharField( + blank=True, + help_text="The id of the YouTube video that should be linked to this story (this is the text that comes afer v= and before & in the YouTube URL)", + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="storyimage", + name="story", + field=models.ForeignKey( + help_text="The story to associate to", + on_delete=django.db.models.deletion.PROTECT, + related_name="images", + to="stories.story", + ), + ), + migrations.AddField( + model_name="story", + name="audio_link", + field=models.URLField( + blank=True, help_text="A link to an mp3 file to publish on this story", max_length=255, null=True + ), + ), + migrations.AddField( + model_name="story", + name="written_by", + field=models.CharField(blank=True, help_text="The writer of the story", max_length=255, null=True), + ), + migrations.AlterField( + model_name="story", + name="created_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + migrations.AlterField( + model_name="story", + name="modified_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + migrations.AlterField( + model_name="storyimage", + name="created_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + migrations.AlterField( + model_name="storyimage", + name="modified_on", + field=models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + migrations.AlterField( + model_name="storyimage", + name="image", + field=models.ImageField( + help_text="The image file to use", + upload_to=functools.partial(dash.utils.generate_file_path, *("stories",), **{}), + ), + ), + migrations.AddField( + model_name="story", + name="attachment", + field=models.FileField( + blank=True, + help_text="The PDF report to attach", + null=True, + upload_to=functools.partial(dash.utils.generate_file_path, *("story_attachments",), **{}), + validators=[dash.stories.models.validate_file_extension], + ), + ), + migrations.AlterField( + model_name="storyimage", + name="created_by", + field=models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="storyimage", + name="modified_by", + field=models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/dash/tags/migrations/0001_squashed.py b/dash/tags/migrations/0001_squashed.py new file mode 100644 index 0000000..2db93e3 --- /dev/null +++ b/dash/tags/migrations/0001_squashed.py @@ -0,0 +1,80 @@ +# Generated by Django 5.0.8 on 2024-08-22 08:59 + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [ + ("tags", "0001_initial"), + ("tags", "0002_alter_tag_created_by_alter_tag_modified_by"), + ("tags", "0003_alter_tag_unique_together_and_more"), + ] + + initial = True + + dependencies = [ + ("orgs", "0026_fix_org_config_rapidpro"), + ("orgs", "0033_rename_orgs_orgbac_org_id_607508_idx_orgs_orgbac_org_slug_idx_and_more"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Tag", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "is_active", + models.BooleanField( + default=True, help_text="Whether this item is active, use this instead of deleting" + ), + ), + ( + "created_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was originally created", + ), + ), + ( + "modified_on", + models.DateTimeField( + blank=True, + default=django.utils.timezone.now, + editable=False, + help_text="When this item was last modified", + ), + ), + ("name", models.CharField(help_text="The name of this tag", max_length=64)), + ( + "created_by", + models.ForeignKey( + help_text="The user which originally created this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_creations", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "modified_by", + models.ForeignKey( + help_text="The user which last modified this item", + on_delete=django.db.models.deletion.PROTECT, + related_name="%(app_label)s_%(class)s_modifications", + to=settings.AUTH_USER_MODEL, + ), + ), + ("org", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="orgs.org")), + ], + ), + migrations.AddConstraint( + model_name="tag", + constraint=models.UniqueConstraint(fields=("name", "org"), name="tags_tag_name_org_unique"), + ), + ] diff --git a/dash/tags/migrations/0003_alter_tag_unique_together_and_more.py b/dash/tags/migrations/0003_alter_tag_unique_together_and_more.py new file mode 100644 index 0000000..668f30e --- /dev/null +++ b/dash/tags/migrations/0003_alter_tag_unique_together_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.8 on 2024-08-21 16:03 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("orgs", "0033_rename_orgs_orgbac_org_id_607508_idx_orgs_orgbac_org_slug_idx_and_more"), + ("tags", "0002_alter_tag_created_by_alter_tag_modified_by"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="tag", + unique_together=set(), + ), + migrations.AddConstraint( + model_name="tag", + constraint=models.UniqueConstraint(fields=("name", "org"), name="tags_tag_name_org_unique"), + ), + ] diff --git a/dash/tags/models.py b/dash/tags/models.py index 9de0a30..cfab767 100644 --- a/dash/tags/models.py +++ b/dash/tags/models.py @@ -15,4 +15,4 @@ def __str__(self) -> str: return self.name class Meta: - unique_together = ("name", "org") + constraints = [models.UniqueConstraint(fields=["name", "org"], name="tags_tag_name_org_unique")] diff --git a/pyproject.toml b/pyproject.toml index a45b1e3..abf4580 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,11 +49,13 @@ line-length = 119 [tool.ruff] line-length = 120 -select = ["E", "F", "W"] -ignore = ["E501", "F405"] fix = true exclude = ["./.tox/*", "./.venv/*", "./env/*", "*/migrations/*", "./build/*"] +[tool.ruff.lint] +select = ["E", "F", "W"] +ignore = ["E501", "F405"] + [tool.isort] multi_line_output = 3 force_grid_wrap = 0