chore: page version migrations (#5103)

* chore: rewrite page version migration to remove data back migration

* dev: rename exporter history choice field

* dev: update migration
This commit is contained in:
Nikhil
2024-07-10 19:37:04 +05:30
committed by GitHub
parent f9a3778c7f
commit c6db050443
12 changed files with 114 additions and 424 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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),

View File

@@ -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,
)
)

View File

@@ -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,
)

View File

@@ -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='<p></p>')),
('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'),
),
]

View File

@@ -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="<p></p>"),
),
(
"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),
]

View File

@@ -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(

View File

@@ -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"