From c6db05044393565ea623a99ceac158aa16ae4538 Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Wed, 10 Jul 2024 19:37:04 +0530 Subject: [PATCH] chore: page version migrations (#5103) * chore: rewrite page version migration to remove data back migration * dev: rename exporter history choice field * dev: update migration --- apiserver/plane/api/serializers/issue.py | 11 +- apiserver/plane/api/views/inbox.py | 8 - apiserver/plane/api/views/project.py | 8 - apiserver/plane/app/serializers/issue.py | 10 +- apiserver/plane/app/views/project/base.py | 9 +- apiserver/plane/bgtasks/dummy_data_task.py | 8 - apiserver/plane/db/backfills/__init__.py | 0 .../backfills/backfill_0070_page_versions.py | 63 ---- ...ervice_exporterhistory_filters_and_more.py | 106 ++++++ .../plane/db/migrations/0070_page_versions.py | 311 ------------------ apiserver/plane/db/models/exporter.py | 2 +- apiserver/plane/db/models/issue_type.py | 2 + 12 files changed, 114 insertions(+), 424 deletions(-) delete mode 100644 apiserver/plane/db/backfills/__init__.py delete mode 100644 apiserver/plane/db/backfills/backfill_0070_page_versions.py create mode 100644 apiserver/plane/db/migrations/0070_apitoken_is_service_exporterhistory_filters_and_more.py delete mode 100644 apiserver/plane/db/migrations/0070_page_versions.py diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index 280dc02084..45b7e7e06c 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -11,7 +11,6 @@ from rest_framework import serializers # Module imports from plane.db.models import ( Issue, - IssueType, IssueActivity, IssueAssignee, IssueAttachment, @@ -132,15 +131,9 @@ class IssueSerializer(BaseSerializer): workspace_id = self.context["workspace_id"] default_assignee_id = self.context["default_assignee_id"] - # Get the issue type from the project - issue_type = ( - IssueType.objects.filter(project_id=project_id) - .order_by("created_at") - .first() - ) - issue = Issue.objects.create( - **validated_data, project_id=project_id, type=issue_type + **validated_data, + project_id=project_id, ) # Issue Audit Users diff --git a/apiserver/plane/api/views/inbox.py b/apiserver/plane/api/views/inbox.py index 513dca52b9..43c9d56521 100644 --- a/apiserver/plane/api/views/inbox.py +++ b/apiserver/plane/api/views/inbox.py @@ -16,7 +16,6 @@ from plane.app.permissions import ProjectLitePermission from plane.bgtasks.issue_activites_task import issue_activity from plane.db.models import ( Inbox, - IssueType, InboxIssue, Issue, Project, @@ -142,12 +141,6 @@ class InboxIssueAPIEndpoint(BaseAPIView): color="#ff7700", is_triage=True, ) - # Get the issue type - issue_type = ( - IssueType.objects.filter(project_id=project_id) - .order_by("created_at") - .first() - ) # create an issue issue = Issue.objects.create( @@ -159,7 +152,6 @@ class InboxIssueAPIEndpoint(BaseAPIView): priority=request.data.get("issue", {}).get("priority", "none"), project_id=project_id, state=state, - type=issue_type, ) # create an inbox issue diff --git a/apiserver/plane/api/views/project.py b/apiserver/plane/api/views/project.py index 1e0234d39f..bf1109e8f6 100644 --- a/apiserver/plane/api/views/project.py +++ b/apiserver/plane/api/views/project.py @@ -26,7 +26,6 @@ from plane.db.models import ( ProjectMember, State, Workspace, - IssueType, ) from plane.bgtasks.webhook_task import model_activity from .base import BaseAPIView @@ -242,13 +241,6 @@ class ProjectAPIEndpoint(BaseAPIView): .first() ) - # Create the Issue Types - IssueType.objects.create( - name="Task", - description="A task that needs to be done", - project_id=project.id, - ) - # Model activity model_activity.delay( model_name="project", diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index c1440be514..f3988c7c34 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -33,7 +33,6 @@ from plane.db.models import ( IssueVote, IssueRelation, State, - IssueType, ) @@ -136,15 +135,10 @@ class IssueCreateSerializer(BaseSerializer): workspace_id = self.context["workspace_id"] default_assignee_id = self.context["default_assignee_id"] - # Get Issue Type - issue_type = ( - IssueType.objects.filter(project_id=project_id) - .order_by("created_at") - .first() - ) # Create Issue issue = Issue.objects.create( - **validated_data, project_id=project_id, type=issue_type + **validated_data, + project_id=project_id, ) # Issue Audit Users diff --git a/apiserver/plane/app/views/project/base.py b/apiserver/plane/app/views/project/base.py index 745805837c..3d20b175d5 100644 --- a/apiserver/plane/app/views/project/base.py +++ b/apiserver/plane/app/views/project/base.py @@ -47,7 +47,6 @@ from plane.db.models import ( ProjectMember, State, Workspace, - IssueType, ) from plane.utils.cache import cache_response from plane.bgtasks.webhook_task import model_activity @@ -343,13 +342,7 @@ class ProjectViewSet(BaseViewSet): .first() ) - # Create the issue type - IssueType.objects.create( - name="Task", - description="A task that needs to be done", - project_id=project.id, - ) - + # Create the model activity model_activity.delay( model_name="project", model_id=str(project.id), diff --git a/apiserver/plane/bgtasks/dummy_data_task.py b/apiserver/plane/bgtasks/dummy_data_task.py index d1915c3cf0..74e210de64 100644 --- a/apiserver/plane/bgtasks/dummy_data_task.py +++ b/apiserver/plane/bgtasks/dummy_data_task.py @@ -21,7 +21,6 @@ from plane.db.models import ( Cycle, Module, Issue, - IssueType, IssueSequence, IssueAssignee, IssueLabel, @@ -337,12 +336,6 @@ def create_issues(workspace, project, user_id, issue_count): 65535 if largest_sort_order is None else largest_sort_order + 10000 ) - issue_type = IssueType.objects.create( - name="Task", - description="A task that needs to be completed.", - project=project, - ) - for _ in range(0, issue_count): start_date = [None, fake.date_this_year()][random.randint(0, 1)] end_date = ( @@ -371,7 +364,6 @@ def create_issues(workspace, project, user_id, issue_count): random.randint(0, 4) ], created_by_id=creators[random.randint(0, len(creators) - 1)], - type=issue_type, ) ) diff --git a/apiserver/plane/db/backfills/__init__.py b/apiserver/plane/db/backfills/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/apiserver/plane/db/backfills/backfill_0070_page_versions.py b/apiserver/plane/db/backfills/backfill_0070_page_versions.py deleted file mode 100644 index 6afbd233d1..0000000000 --- a/apiserver/plane/db/backfills/backfill_0070_page_versions.py +++ /dev/null @@ -1,63 +0,0 @@ -# Third party imports -from celery import shared_task - -# Django imports -from django.utils import timezone - -# Module imports -from plane.db.models import PageVersion, IssueType, Issue - - -@shared_task -def backfill_issue_type_task(projects): - # Create the issue types for all projects - IssueType.objects.bulk_create( - [ - IssueType( - name="Task", - description="A task that needs to be completed.", - project_id=project["id"], - workspace_id=project["workspace_id"], - ) - for project in projects - ], - batch_size=1000, - ) - - # Update the issue type for all existing issues - issue_types = { - str(issue_type["project_id"]): str(issue_type["id"]) - for issue_type in IssueType.objects.filter( - project_id__in=[project["id"] for project in projects] - ).values("id", "project_id") - } - # Update the issue type for all existing issues - bulk_issues = [] - for issue in Issue.objects.filter( - project_id__in=[project["id"] for project in projects] - ): - issue.type_id = issue_types[str(issue.project_id)] - bulk_issues.append(issue) - - # Update the issue type for all existing issues - Issue.objects.bulk_update(bulk_issues, ["type_id"], batch_size=1000) - - -@shared_task -def backfill_page_versions_task(pages): - # Create the page versions for all pages - PageVersion.objects.bulk_create( - [ - PageVersion( - page_id=page["id"], - workspace_id=page["workspace_id"], - last_saved_at=timezone.now(), - owned_by_id=page["owned_by_id"], - description_binary=page["description_binary"], - description_html=page["description_html"], - description_stripped=page["description_stripped"], - ) - for page in pages - ], - batch_size=1000, - ) diff --git a/apiserver/plane/db/migrations/0070_apitoken_is_service_exporterhistory_filters_and_more.py b/apiserver/plane/db/migrations/0070_apitoken_is_service_exporterhistory_filters_and_more.py new file mode 100644 index 0000000000..0a81c578f3 --- /dev/null +++ b/apiserver/plane/db/migrations/0070_apitoken_is_service_exporterhistory_filters_and_more.py @@ -0,0 +1,106 @@ +# Generated by Django 4.2.11 on 2024-07-10 13:59 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0069_alter_account_provider_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='apitoken', + name='is_service', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='exporterhistory', + name='filters', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='exporterhistory', + name='name', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Exporter Name'), + ), + migrations.AddField( + model_name='exporterhistory', + name='type', + field=models.CharField(choices=[('issue_exports', 'Issue Exports'), ('issue_worklogs', 'Issue Worklogs')], default='issue_exports', max_length=50), + ), + migrations.AddField( + model_name='project', + name='is_time_tracking_enabled', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='project', + name='start_date', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='project', + name='target_date', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.CreateModel( + name='PageVersion', + fields=[ + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), + ('last_saved_at', models.DateTimeField(default=django.utils.timezone.now)), + ('description_binary', models.BinaryField(null=True)), + ('description_html', models.TextField(blank=True, default='

')), + ('description_stripped', models.TextField(blank=True, null=True)), + ('description_json', models.JSONField(blank=True, default=dict)), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), + ('owned_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_versions', to=settings.AUTH_USER_MODEL)), + ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_versions', to='db.page')), + ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_versions', to='db.workspace')), + ], + options={ + 'verbose_name': 'Page Version', + 'verbose_name_plural': 'Page Versions', + 'db_table': 'page_versions', + 'ordering': ('-created_at',), + }, + ), + migrations.CreateModel( + name='IssueType', + fields=[ + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Modified At')), + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(blank=True)), + ('logo_props', models.JSONField(default=dict)), + ('sort_order', models.FloatField(default=65535)), + ('is_default', models.BooleanField(default=True)), + ('weight', models.PositiveIntegerField(default=0)), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), + ('project', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_%(class)s', to='db.project')), + ('updated_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated_by', to=settings.AUTH_USER_MODEL, verbose_name='Last Modified By')), + ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_%(class)s', to='db.workspace')), + ], + options={ + 'verbose_name': 'Issue Type', + 'verbose_name_plural': 'Issue Types', + 'db_table': 'issue_types', + 'ordering': ('sort_order',), + 'unique_together': {('project', 'name')}, + }, + ), + migrations.AddField( + model_name='issue', + name='type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issue_type', to='db.issuetype'), + ), + ] diff --git a/apiserver/plane/db/migrations/0070_page_versions.py b/apiserver/plane/db/migrations/0070_page_versions.py deleted file mode 100644 index f303d07f2f..0000000000 --- a/apiserver/plane/db/migrations/0070_page_versions.py +++ /dev/null @@ -1,311 +0,0 @@ -# Generated by Django 4.2.11 on 2024-07-01 06:10 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import uuid -from apiserver.plane.db.backfills.backfill_0070_page_versions import ( - backfill_issue_type_task, - backfill_page_versions_task, -) - -CHUNK_SIZE = 100 # Initial Delay in seconds -INITIAL_DELAY = 30 # Initial delay in seconds -INCREMENT_DELAY = 1 # Increment delay in seconds - - -def backfill_issue_types(apps, schema_editor): - start = 0 - end = CHUNK_SIZE - - Project = apps.get_model("db", "Project") - - total = Project.objects.count() - delay_increment = INITIAL_DELAY - - while start < total: - projects = list( - Project.objects.values("id", "workspace_id")[start:end] - ) - backfill_issue_type_task.apply_async( - (projects,), countdown=delay_increment - ) - delay_increment += ( - INCREMENT_DELAY # Increment delay for the next batch - ) - start += CHUNK_SIZE - end += CHUNK_SIZE - - -def backfill_page_versions(apps, schema_editor): - start = 0 - end = CHUNK_SIZE - - Page = apps.get_model("db", "Page") - - total = Page.objects.count() - delay_increment = INITIAL_DELAY - - while start < total: - pages = list( - Page.objects.values( - "id", - "workspace_id", - "owned_by_id", - "description_binary", - "description_html", - "description_stripped", - )[start:end] - ) - backfill_page_versions_task.apply_async( - (pages,), countdown=delay_increment - ) - delay_increment += ( - INCREMENT_DELAY # Increment delay for the next batch - ) - start += CHUNK_SIZE - end += CHUNK_SIZE - - -class Migration(migrations.Migration): - - dependencies = [ - ("db", "0069_alter_account_provider_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="IssueType", - fields=[ - ( - "created_at", - models.DateTimeField( - auto_now_add=True, verbose_name="Created At" - ), - ), - ( - "updated_at", - models.DateTimeField( - auto_now=True, verbose_name="Last Modified At" - ), - ), - ( - "id", - models.UUIDField( - db_index=True, - default=uuid.uuid4, - editable=False, - primary_key=True, - serialize=False, - unique=True, - ), - ), - ("name", models.CharField(max_length=255)), - ("description", models.TextField(blank=True)), - ("logo_props", models.JSONField(default=dict)), - ("sort_order", models.FloatField(default=65535)), - ( - "created_by", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="%(class)s_created_by", - to=settings.AUTH_USER_MODEL, - verbose_name="Created By", - ), - ), - ( - "project", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="project_%(class)s", - to="db.project", - ), - ), - ( - "updated_by", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="%(class)s_updated_by", - to=settings.AUTH_USER_MODEL, - verbose_name="Last Modified By", - ), - ), - ( - "workspace", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="workspace_%(class)s", - to="db.workspace", - ), - ), - ( - "is_default", - models.BooleanField(default=True), - ), - ], - options={ - "verbose_name": "Issue Type", - "verbose_name_plural": "Issue Types", - "db_table": "issue_types", - "ordering": ("sort_order",), - }, - ), - migrations.AddField( - model_name="issue", - name="type", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="issue_type", - to="db.issuetype", - ), - ), - migrations.AddField( - model_name="apitoken", - name="is_service", - field=models.BooleanField(default=False), - ), - migrations.CreateModel( - name="PageVersion", - fields=[ - ( - "created_at", - models.DateTimeField( - auto_now_add=True, verbose_name="Created At" - ), - ), - ( - "updated_at", - models.DateTimeField( - auto_now=True, verbose_name="Last Modified At" - ), - ), - ( - "id", - models.UUIDField( - db_index=True, - default=uuid.uuid4, - editable=False, - primary_key=True, - serialize=False, - unique=True, - ), - ), - ( - "last_saved_at", - models.DateTimeField(default=django.utils.timezone.now), - ), - ("description_binary", models.BinaryField(null=True)), - ( - "description_html", - models.TextField(blank=True, default="

"), - ), - ( - "description_stripped", - models.TextField(blank=True, null=True), - ), - ( - "description_json", - models.JSONField(blank=True, default=dict), - ), - ( - "created_by", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="%(class)s_created_by", - to=settings.AUTH_USER_MODEL, - verbose_name="Created By", - ), - ), - ( - "owned_by", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="page_versions", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "page", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="page_versions", - to="db.page", - ), - ), - ( - "updated_by", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="%(class)s_updated_by", - to=settings.AUTH_USER_MODEL, - verbose_name="Last Modified By", - ), - ), - ( - "workspace", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="page_versions", - to="db.workspace", - ), - ), - ], - options={ - "verbose_name": "Page Version", - "verbose_name_plural": "Page Versions", - "db_table": "page_versions", - "ordering": ("-created_at",), - }, - ), - migrations.AddField( - model_name="exporterhistory", - name="filters", - field=models.JSONField(blank=True, null=True), - ), - migrations.AddField( - model_name="exporterhistory", - name="name", - field=models.CharField( - blank=True, - max_length=255, - null=True, - verbose_name="Exporter Name", - ), - ), - migrations.AddField( - model_name="exporterhistory", - name="type", - field=models.CharField( - choices=[ - ("issue_exports", "Issue Exports"), - ("issue_work_logs", "Issue Work Logs"), - ], - default="issue_exports", - max_length=50, - ), - ), - migrations.AddField( - model_name="project", - name="is_time_tracking_enabled", - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name="project", - name="start_date", - field=models.DateTimeField(blank=True, null=True), - ), - migrations.AddField( - model_name="project", - name="target_date", - field=models.DateTimeField(blank=True, null=True), - ), - migrations.RunPython(backfill_issue_types), - migrations.RunPython(backfill_page_versions), - ] diff --git a/apiserver/plane/db/models/exporter.py b/apiserver/plane/db/models/exporter.py index a6f5110e3d..d26f034502 100644 --- a/apiserver/plane/db/models/exporter.py +++ b/apiserver/plane/db/models/exporter.py @@ -26,7 +26,7 @@ class ExporterHistory(BaseModel): default="issue_exports", choices=( ("issue_exports", "Issue Exports"), - ("issue_work_logs", "Issue Work Logs"), + ("issue_worklogs", "Issue Worklogs"), ), ) workspace = models.ForeignKey( diff --git a/apiserver/plane/db/models/issue_type.py b/apiserver/plane/db/models/issue_type.py index 33affb660d..52a750f0fb 100644 --- a/apiserver/plane/db/models/issue_type.py +++ b/apiserver/plane/db/models/issue_type.py @@ -11,8 +11,10 @@ class IssueType(WorkspaceBaseModel): logo_props = models.JSONField(default=dict) sort_order = models.FloatField(default=65535) is_default = models.BooleanField(default=True) + weight = models.PositiveIntegerField(default=0) class Meta: + unique_together = ["project", "name"] verbose_name = "Issue Type" verbose_name_plural = "Issue Types" db_table = "issue_types"