From 39ca6000910be2cab4c3d164a14d03ad19cfd8de Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Fri, 10 Jan 2025 19:54:48 +0530 Subject: [PATCH] fix: workspace user perferences model and migrations --- apiserver/plane/app/serializers/dashboard.py | 6 +- apiserver/plane/app/views/dashboard/base.py | 14 +-- ..._dashboard_deprecateddashboard_and_more.py | 87 +++++++++++++++++++ apiserver/plane/db/models/__init__.py | 2 +- apiserver/plane/db/models/dashboard.py | 28 +++--- apiserver/plane/db/models/workspace.py | 44 +++++++++- 6 files changed, 155 insertions(+), 26 deletions(-) create mode 100644 apiserver/plane/db/migrations/0090_rename_dashboard_deprecateddashboard_and_more.py diff --git a/apiserver/plane/app/serializers/dashboard.py b/apiserver/plane/app/serializers/dashboard.py index b0ed8841be..d09fc3e354 100644 --- a/apiserver/plane/app/serializers/dashboard.py +++ b/apiserver/plane/app/serializers/dashboard.py @@ -1,6 +1,6 @@ # Module imports from .base import BaseSerializer -from plane.db.models import Dashboard, Widget +from plane.db.models import DeprecatedDashboard, DeprecatedWidget # Third party frameworks from rest_framework import serializers @@ -8,7 +8,7 @@ from rest_framework import serializers class DashboardSerializer(BaseSerializer): class Meta: - model = Dashboard + model = DeprecatedDashboard fields = "__all__" @@ -17,5 +17,5 @@ class WidgetSerializer(BaseSerializer): widget_filters = serializers.JSONField(read_only=True) class Meta: - model = Widget + model = DeprecatedWidget fields = ["id", "key", "is_visible", "widget_filters"] diff --git a/apiserver/plane/app/views/dashboard/base.py b/apiserver/plane/app/views/dashboard/base.py index 8ccce092e4..cb78f46b3f 100644 --- a/apiserver/plane/app/views/dashboard/base.py +++ b/apiserver/plane/app/views/dashboard/base.py @@ -32,15 +32,15 @@ from plane.app.serializers import ( WidgetSerializer, ) from plane.db.models import ( - Dashboard, - DashboardWidget, + DeprecatedDashboard, + DeprecatedDashboardWidget, Issue, IssueActivity, FileAsset, IssueLink, IssueRelation, Project, - Widget, + DeprecatedWidget, WorkspaceMember, CycleIssue, ) @@ -687,7 +687,7 @@ class DashboardEndpoint(BaseAPIView): if not dashboard_id: dashboard_type = request.GET.get("dashboard_type", None) if dashboard_type == "home": - dashboard, created = Dashboard.objects.get_or_create( + dashboard, created = DeprecatedDashboard.objects.get_or_create( type_identifier=dashboard_type, owned_by=request.user, is_default=True, @@ -707,7 +707,7 @@ class DashboardEndpoint(BaseAPIView): updated_dashboard_widgets = [] for widget_key in widgets_to_fetch: - widget = Widget.objects.filter(key=widget_key).values_list( + widget = DeprecatedWidget.objects.filter(key=widget_key).values_list( "id", flat=True ) if widget: @@ -722,9 +722,9 @@ class DashboardEndpoint(BaseAPIView): ) widgets = ( - Widget.objects.annotate( + DeprecatedWidget.objects.annotate( is_visible=Exists( - DashboardWidget.objects.filter( + DeprecatedDashboardWidget.objects.filter( widget_id=OuterRef("pk"), dashboard_id=dashboard.id, is_visible=True, diff --git a/apiserver/plane/db/migrations/0090_rename_dashboard_deprecateddashboard_and_more.py b/apiserver/plane/db/migrations/0090_rename_dashboard_deprecateddashboard_and_more.py new file mode 100644 index 0000000000..e0d16d5a61 --- /dev/null +++ b/apiserver/plane/db/migrations/0090_rename_dashboard_deprecateddashboard_and_more.py @@ -0,0 +1,87 @@ + # Generated by Django 4.2.17 on 2025-01-09 14:43 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0089_workspacehomepreference_and_more'), + ] + + operations = [ + migrations.RenameModel( + old_name='Dashboard', + new_name='DeprecatedDashboard', + ), + migrations.RenameModel( + old_name='DashboardWidget', + new_name='DeprecatedDashboardWidget', + ), + migrations.RenameModel( + old_name='Widget', + new_name='DeprecatedWidget', + ), + migrations.AlterModelOptions( + name='deprecateddashboard', + options={'ordering': ('-created_at',), 'verbose_name': 'DeprecatedDashboard', 'verbose_name_plural': 'DeprecatedDashboards'}, + ), + migrations.AlterModelOptions( + name='deprecateddashboardwidget', + options={'ordering': ('-created_at',), 'verbose_name': 'Deprecated Dashboard Widget', 'verbose_name_plural': 'Deprecated Dashboard Widgets'}, + ), + migrations.AlterModelOptions( + name='deprecatedwidget', + options={'ordering': ('-created_at',), 'verbose_name': 'DeprecatedWidget', 'verbose_name_plural': 'DeprecatedWidgets'}, + ), + migrations.AlterField( + model_name='workspacehomepreference', + name='sort_order', + field=models.FloatField(default=65535), + ), + migrations.AlterModelTable( + name='deprecateddashboard', + table='deprecated_dashboards', + ), + migrations.AlterModelTable( + name='deprecateddashboardwidget', + table='deprecated_dashboard_widgets', + ), + migrations.AlterModelTable( + name='deprecatedwidget', + table='deprecated_widgets', + ), + migrations.CreateModel( + name='WorkspaceUserPreference', + 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')), + ('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='Deleted At')), + ('id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), + ('key', models.CharField(max_length=255)), + ('is_pinned', models.BooleanField(default=False)), + ('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')), + ('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')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_user_preferences', to=settings.AUTH_USER_MODEL)), + ('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_user_preferences', to='db.workspace')), + ], + options={ + 'verbose_name': 'Workspace User Preference', + 'verbose_name_plural': 'Workspace User Preferences', + 'db_table': 'workspace_user_preferences', + 'ordering': ('-created_at',), + }, + ), + migrations.AddConstraint( + model_name='workspaceuserpreference', + constraint=models.UniqueConstraint(condition=models.Q(('deleted_at__isnull', True)), fields=('workspace', 'user', 'key'), name='workspace_user_preferences_unique_workspace_user_key_when_deleted_at_null'), + ), + migrations.AlterUniqueTogether( + name='workspaceuserpreference', + unique_together={('workspace', 'user', 'key', 'deleted_at')}, + ), + ] diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index 09b372fddc..436ed5148b 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -3,7 +3,7 @@ from .api import APIActivityLog, APIToken from .asset import FileAsset from .base import BaseModel from .cycle import Cycle, CycleIssue, CycleUserProperties -from .dashboard import Dashboard, DashboardWidget, Widget +from .dashboard import DeprecatedDashboard, DeprecatedDashboardWidget, DeprecatedWidget from .deploy_board import DeployBoard from .draft import ( DraftIssue, diff --git a/apiserver/plane/db/models/dashboard.py b/apiserver/plane/db/models/dashboard.py index e0f4017536..d1101e3529 100644 --- a/apiserver/plane/db/models/dashboard.py +++ b/apiserver/plane/db/models/dashboard.py @@ -8,7 +8,7 @@ from ..mixins import TimeAuditModel from .base import BaseModel -class Dashboard(BaseModel): +class DeprecatedDashboard(BaseModel): DASHBOARD_CHOICES = ( ("workspace", "Workspace"), ("project", "Project"), @@ -36,13 +36,13 @@ class Dashboard(BaseModel): return f"{self.name}" class Meta: - verbose_name = "Dashboard" - verbose_name_plural = "Dashboards" - db_table = "dashboards" + verbose_name = "DeprecatedDashboard" + verbose_name_plural = "DeprecatedDashboards" + db_table = "deprecated_dashboards" ordering = ("-created_at",) -class Widget(TimeAuditModel): +class DeprecatedWidget(TimeAuditModel): id = models.UUIDField( default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True ) @@ -55,18 +55,18 @@ class Widget(TimeAuditModel): return f"{self.key}" class Meta: - verbose_name = "Widget" - verbose_name_plural = "Widgets" - db_table = "widgets" + verbose_name = "DeprecatedWidget" + verbose_name_plural = "DeprecatedWidgets" + db_table = "deprecated_widgets" ordering = ("-created_at",) -class DashboardWidget(BaseModel): +class DeprecatedDashboardWidget(BaseModel): widget = models.ForeignKey( - Widget, on_delete=models.CASCADE, related_name="dashboard_widgets" + DeprecatedWidget, on_delete=models.CASCADE, related_name="dashboard_widgets" ) dashboard = models.ForeignKey( - Dashboard, on_delete=models.CASCADE, related_name="dashboard_widgets" + DeprecatedDashboard, on_delete=models.CASCADE, related_name="dashboard_widgets" ) is_visible = models.BooleanField(default=True) sort_order = models.FloatField(default=65535) @@ -86,7 +86,7 @@ class DashboardWidget(BaseModel): name="dashboard_widget_unique_widget_dashboard_when_deleted_at_null", ) ] - verbose_name = "Dashboard Widget" - verbose_name_plural = "Dashboard Widgets" - db_table = "dashboard_widgets" + verbose_name = "Deprecated Dashboard Widget" + verbose_name_plural = "Deprecated Dashboard Widgets" + db_table = "deprecated_dashboard_widgets" ordering = ("-created_at",) diff --git a/apiserver/plane/db/models/workspace.py b/apiserver/plane/db/models/workspace.py index 723a2628cd..ddeb50fcf5 100644 --- a/apiserver/plane/db/models/workspace.py +++ b/apiserver/plane/db/models/workspace.py @@ -1,4 +1,5 @@ # Python imports +from django.db.models.functions import Ln import pytz # Django imports @@ -345,6 +346,8 @@ class WorkspaceUserLink(WorkspaceBaseModel): class WorkspaceHomePreference(BaseModel): + """Preference for the home page of a workspace for a user""" + class HomeWidgetKeys(models.TextChoices): QUICK_LINKS = "quick_links", "Quick Links" RECENTS = "recents", "Recents" @@ -365,7 +368,7 @@ class WorkspaceHomePreference(BaseModel): key = models.CharField(max_length=255) is_enabled = models.BooleanField(default=True) config = models.JSONField(default=dict) - sort_order = models.PositiveIntegerField(default=65535) + sort_order = models.FloatField(default=65535) class Meta: unique_together = ["workspace", "user", "key", "deleted_at"] @@ -383,3 +386,42 @@ class WorkspaceHomePreference(BaseModel): def __str__(self): return f"{self.workspace.name} {self.user.email} {self.key}" + + + +class WorkspaceUserPreference(BaseModel): + """Preference for the workspace for a user""" + + class UserPreferenceKeys(models.TextChoices): + CYCLES = "cycles", "Cycles" + VIEWS = "views", "Views" + ANALYTICS = "analytics", "Analytics" + PROJECTS = "projects", "Projects" + + workspace = models.ForeignKey( + "db.Workspace", + on_delete=models.CASCADE, + related_name="workspace_user_preferences", + ) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="workspace_user_preferences", + ) + key = models.CharField(max_length=255) + is_pinned = models.BooleanField(default=False) + sort_order = models.FloatField(default=65535) + + class Meta: + unique_together = ["workspace", "user", "key", "deleted_at"] + constraints = [ + models.UniqueConstraint( + fields=["workspace", "user", "key"], + condition=models.Q(deleted_at__isnull=True), + name="workspace_user_preferences_unique_workspace_user_key_when_deleted_at_null", + ) + ] + verbose_name = "Workspace User Preference" + verbose_name_plural = "Workspace User Preferences" + db_table = "workspace_user_preferences" + ordering = ("-created_at",)