Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1053ff4684 |
@@ -4,7 +4,7 @@ from rest_framework import serializers
|
||||
# Module import
|
||||
from plane.db.models import Account, Profile, User, Workspace, WorkspaceMemberInvite
|
||||
from plane.utils.url import contains_url
|
||||
|
||||
from plane.db.models.user import get_default_tour_completed_features
|
||||
from .base import BaseSerializer
|
||||
|
||||
|
||||
@@ -194,6 +194,37 @@ class ResetPasswordSerializer(serializers.Serializer):
|
||||
|
||||
|
||||
class ProfileSerializer(BaseSerializer):
|
||||
|
||||
def validate_tour_completed_features(self, value):
|
||||
"""
|
||||
Clean tour_completed_features by removing invalid keys and keeping only
|
||||
the keys present in get_default_tour_completed_features function.
|
||||
"""
|
||||
if not isinstance(value, dict):
|
||||
return get_default_tour_completed_features()
|
||||
|
||||
expected_structure = get_default_tour_completed_features()
|
||||
|
||||
# Use dict comprehension for cleaner, more efficient code
|
||||
cleaned_data = {}
|
||||
|
||||
# Process valid top-level keys
|
||||
for key in expected_structure:
|
||||
if key in value:
|
||||
val = value[key]
|
||||
if isinstance(val, dict) and key in ['workspace_features', 'project_features']:
|
||||
# Clean nested structure using intersection
|
||||
expected_nested = expected_structure[key]
|
||||
cleaned_data[key] = {k: v for k, v in val.items() if k in expected_nested}
|
||||
else:
|
||||
cleaned_data[key] = val
|
||||
else:
|
||||
# Add missing key with default
|
||||
default_value = expected_structure[key]
|
||||
cleaned_data[key] = default_value.copy() if isinstance(default_value, dict) else default_value
|
||||
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
model = Profile
|
||||
fields = "__all__"
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 4.2.24 on 2025-10-08 10:46
|
||||
|
||||
from django.db import migrations, models
|
||||
import plane.db.models.user
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0107_migrate_filters_to_rich_filters'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='tour_completed_features',
|
||||
field=models.JSONField(default=plane.db.models.user.get_default_tour_completed_features),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='exporterhistory',
|
||||
name='url',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@@ -42,7 +42,7 @@ class ExporterHistory(BaseModel):
|
||||
)
|
||||
reason = models.TextField(blank=True)
|
||||
key = models.TextField(blank=True)
|
||||
url = models.URLField(max_length=800, blank=True, null=True)
|
||||
url = models.TextField(blank=True, null=True)
|
||||
token = models.CharField(max_length=255, default=generate_token, unique=True)
|
||||
initiated_by = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
|
||||
@@ -34,6 +34,20 @@ def get_mobile_default_onboarding():
|
||||
"workspace_join": False,
|
||||
}
|
||||
|
||||
def get_default_tour_completed_features():
|
||||
return {
|
||||
"workspace_features": {
|
||||
"projects": False
|
||||
},
|
||||
"project_features": {
|
||||
"workitems": False,
|
||||
"cycles": False,
|
||||
"modules": False,
|
||||
"views": False,
|
||||
"pages": False,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class User(AbstractBaseUser, PermissionsMixin):
|
||||
id = models.UUIDField(default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True)
|
||||
@@ -214,6 +228,9 @@ class Profile(TimeAuditModel):
|
||||
goals = models.JSONField(default=dict)
|
||||
background_color = models.CharField(max_length=255, default=get_random_color)
|
||||
|
||||
# product tour features
|
||||
tour_completed_features = models.JSONField(default=get_default_tour_completed_features)
|
||||
|
||||
# marketing
|
||||
has_marketing_email_consent = models.BooleanField(default=False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user