diff --git a/apps/assets/api/__init__.py b/apps/assets/api/__init__.py index 9eccd4977..3f49c2b62 100644 --- a/apps/assets/api/__init__.py +++ b/apps/assets/api/__init__.py @@ -7,3 +7,4 @@ from .node import * from .platform import * from .protocol import * from .tree import * +from .my_asset import * diff --git a/apps/assets/api/mixin.py b/apps/assets/api/mixin.py index af5864e4e..042c6bceb 100644 --- a/apps/assets/api/mixin.py +++ b/apps/assets/api/mixin.py @@ -2,7 +2,7 @@ from typing import List from rest_framework.request import Request -from assets.models import Node, Platform, Protocol +from assets.models import Node, Platform, Protocol, MyAsset from assets.utils import get_node_from_request, is_query_node_all_assets from common.utils import lazyproperty, timeit @@ -82,6 +82,7 @@ class SerializeToTreeNodeMixin: data = [] root_assets_count = 0 + MyAsset.set_asset_custom_value(assets, self.request.user) for asset in assets: platform = platform_map.get(asset.platform_id) if not platform: diff --git a/apps/assets/api/my_asset.py b/apps/assets/api/my_asset.py new file mode 100644 index 000000000..ee02500f4 --- /dev/null +++ b/apps/assets/api/my_asset.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# +from common.api import GenericViewSet +from rest_framework.mixins import CreateModelMixin +from common.permissions import IsValidUser +from ..serializers import MyAssetSerializer + +__all__ = ['MyAssetViewSet'] + + +class MyAssetViewSet(CreateModelMixin, GenericViewSet): + serializer_class = MyAssetSerializer + permission_classes = (IsValidUser,) diff --git a/apps/assets/migrations/0005_myasset.py b/apps/assets/migrations/0005_myasset.py new file mode 100644 index 000000000..f08772075 --- /dev/null +++ b/apps/assets/migrations/0005_myasset.py @@ -0,0 +1,35 @@ +# Generated by Django 4.1.13 on 2024-08-06 09:11 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('assets', '0004_auto_20240709_1819'), + ] + + operations = [ + migrations.CreateModel( + name='MyAsset', + fields=[ + ('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(default='', max_length=128, verbose_name='Custom Name')), + ('comment', models.CharField(default='', max_length=512, verbose_name='Custom Comment')), + ('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='my_assets', to='assets.asset')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'My asset', + 'unique_together': {('user', 'asset')}, + }, + ), + ] diff --git a/apps/assets/models/__init__.py b/apps/assets/models/__init__.py index 74bb94517..dc6ca8e97 100644 --- a/apps/assets/models/__init__.py +++ b/apps/assets/models/__init__.py @@ -7,3 +7,4 @@ from .domain import * from .node import * from .favorite_asset import * from .automations import * +from .my_asset import * diff --git a/apps/assets/models/my_asset.py b/apps/assets/models/my_asset.py new file mode 100644 index 000000000..6bbefe222 --- /dev/null +++ b/apps/assets/models/my_asset.py @@ -0,0 +1,43 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from common.db.models import JMSBaseModel + +__all__ = ['MyAsset'] + + +class MyAsset(JMSBaseModel): + user = models.ForeignKey('users.User', on_delete=models.CASCADE) + asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, related_name='my_assets') + name = models.CharField(verbose_name=_("Custom Name"), max_length=128, default='') + comment = models.CharField(verbose_name=_("Custom Comment"), max_length=512, default='') + custom_fields = ['name', 'comment'] + + class Meta: + unique_together = ('user', 'asset') + verbose_name = _("My asset") + + def custom_to_dict(self): + data = {} + for field in self.custom_fields: + value = getattr(self, field) + if value == "": + continue + data.update({field: value}) + return data + + @staticmethod + def set_asset_custom_value(assets, user): + my_assets = MyAsset.objects.filter(asset__in=assets, user=user).all() + customs = {my_asset.asset.id: my_asset.custom_to_dict() for my_asset in my_assets} + for asset in assets: + custom = customs.get(asset.id) + if not custom: + continue + for field, value in custom.items(): + if not hasattr(asset, field): + continue + setattr(asset, field, value) + + def __str__(self): + return f'{self.user}-{self.asset}' diff --git a/apps/assets/serializers/__init__.py b/apps/assets/serializers/__init__.py index e071e24c0..43dbf967c 100644 --- a/apps/assets/serializers/__init__.py +++ b/apps/assets/serializers/__init__.py @@ -9,3 +9,4 @@ from .favorite_asset import * from .gateway import * from .node import * from .platform import * +from .my_asset import * diff --git a/apps/assets/serializers/favorite_asset.py b/apps/assets/serializers/favorite_asset.py index 909c13506..69c916f05 100644 --- a/apps/assets/serializers/favorite_asset.py +++ b/apps/assets/serializers/favorite_asset.py @@ -3,7 +3,6 @@ from rest_framework import serializers -from orgs.utils import tmp_to_root_org from common.serializers import BulkSerializerMixin from ..models import FavoriteAsset diff --git a/apps/assets/serializers/my_asset.py b/apps/assets/serializers/my_asset.py new file mode 100644 index 000000000..17ff26d4d --- /dev/null +++ b/apps/assets/serializers/my_asset.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# + +from rest_framework import serializers +from django.utils.translation import gettext_lazy as _ + +from ..models import MyAsset + +__all__ = ['MyAssetSerializer'] + + +class MyAssetSerializer(serializers.ModelSerializer): + user = serializers.HiddenField( + default=serializers.CurrentUserDefault() + ) + name = serializers.CharField(label=_("Custom Name"), max_length=128, allow_blank=True, required=False) + comment = serializers.CharField(label=_("Custom Comment"), max_length=512, allow_blank=True, required=False) + + class Meta: + model = MyAsset + fields = ['user', 'asset', 'name', 'comment'] + validators = [] + + def create(self, data): + custom_fields = MyAsset.custom_fields + asset = data['asset'] + user = self.context['request'].user + defaults = {field: data.get(field, '') for field in custom_fields} + obj, created = MyAsset.objects.get_or_create(defaults=defaults, user=user, asset=asset) + if created: + return obj + for field in custom_fields: + value = data.get(field) + if value is None: + continue + setattr(obj, field, value) + obj.save() + return obj diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index b56458758..d19d761b3 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -24,6 +24,7 @@ router.register(r'gateways', api.GatewayViewSet, 'gateway') router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset') router.register(r'protocol-settings', api.PlatformProtocolViewSet, 'protocol-setting') router.register(r'labels', LabelViewSet, 'label') +router.register(r'my-asset', api.MyAssetViewSet, 'my-asset') urlpatterns = [ # path('assets//gateways/', api.AssetGatewayListApi.as_view(), name='asset-gateway-list'), diff --git a/apps/perms/api/user_permission/assets.py b/apps/perms/api/user_permission/assets.py index 22c187e3e..1e541eb8d 100644 --- a/apps/perms/api/user_permission/assets.py +++ b/apps/perms/api/user_permission/assets.py @@ -4,7 +4,7 @@ from django.conf import settings from rest_framework.generics import ListAPIView, RetrieveAPIView from assets.api.asset.asset import AssetFilterSet -from assets.models import Asset, Node +from assets.models import Asset, Node, MyAsset from common.api.mixin import ExtraFilterFieldsMixin from common.utils import get_logger, lazyproperty, is_uuid from orgs.utils import tmp_to_root_org @@ -55,6 +55,11 @@ class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ExtraFilterFieldsMixin, ListAPI assets = self.serializer_class.setup_eager_loading(assets) return assets + def get_serializer(self, *args, **kwargs): + if len(args) == 1 and kwargs.get('many', False) and self.request_user_is_self(): + MyAsset.set_asset_custom_value(args[0], self.request.user) + return super().get_serializer(*args, **kwargs) + @abc.abstractmethod def get_assets(self): return Asset.objects.none()