diff --git a/apps/promotions/migrations/0007_alter_event_options_plan_banner_and_more.py b/apps/promotions/migrations/0007_alter_event_options_plan_banner_and_more.py new file mode 100644 index 0000000..c8000d8 --- /dev/null +++ b/apps/promotions/migrations/0007_alter_event_options_plan_banner_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 6.0.2 on 2026-02-22 10:12 + +import django_minio_backend.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('promotions', '0006_event_promotions_event_data_bc656abc_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='event', + options={'ordering': ('-created_at',)}, + ), + migrations.AddField( + model_name='plan', + name='banner', + field=models.ImageField(blank=True, null=True, storage=django_minio_backend.models.MinioBackend(), upload_to=django_minio_backend.models.iso_date_prefix), + ), + migrations.AddField( + model_name='plan', + name='description_details', + field=models.JSONField(blank=True, default={}, verbose_name='description details'), + preserve_default=False, + ), + ] diff --git a/apps/promotions/models.py b/apps/promotions/models.py index df5f081..5938ce0 100644 --- a/apps/promotions/models.py +++ b/apps/promotions/models.py @@ -5,6 +5,7 @@ from django.contrib.postgres.indexes import GinIndex from django.db import models, transaction from django.db.models import F from django.utils import timezone +from django_minio_backend import MinioBackend, iso_date_prefix from oauth2_provider.settings import oauth2_settings @@ -105,7 +106,9 @@ class Plan(BaseModel): null=True, blank=True) event_list = models.JSONField(default=list) title = models.CharField(max_length=255, verbose_name=_('title'), db_index=True) + banner = models.ImageField(null=True, blank=True, storage=MinioBackend(), upload_to=iso_date_prefix) description = models.TextField(verbose_name=_('description'), blank=True) + description_details = models.JSONField(verbose_name=_('description details'), blank=True) processor = models.CharField(max_length=64, verbose_name=_('processor'), db_index=True, choices=ProcessorTypeChoices.choices) # TODO: processor type data = models.JSONField(default=dict, verbose_name=_('data'), null=True, blank=True) diff --git a/apps/promotions/serializers.py b/apps/promotions/serializers.py index 91ab52e..1e1c8b9 100644 --- a/apps/promotions/serializers.py +++ b/apps/promotions/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import Promotion, Plan, Event +from .models import Promotion, Plan, Event, Recipient class PromotionSerializer(serializers.ModelSerializer): @@ -74,3 +74,27 @@ class PromoteSerializer(serializers.ModelSerializer): "label", "data", ) + + +class UserRecipientSerializer(serializers.ModelSerializer): + class Meta: + model = Recipient + fields = ['label', + "base_amount_field"] + + +class UserPlanSerializer(serializers.ModelSerializer): + recipients = UserRecipientSerializer(many=True, read_only=True) + class Meta: + model = Plan + fields = ("title", + 'banner', + "description", + "description_details", + "recipients", + ) + read_only_fields = ("title", "description", "description_details", 'banner', "recipients") + + # def get_promotion_amount(self, obj): + # user = self.context['request'].user + # return obj.calculate_promotion(user) diff --git a/apps/promotions/urls.py b/apps/promotions/urls.py index d64de68..4e1f04b 100644 --- a/apps/promotions/urls.py +++ b/apps/promotions/urls.py @@ -12,7 +12,7 @@ app_name = 'promotions' router = DefaultRouter() # router.register(r'application', views.ApplicationPromotionViewSet, basename='application_promotions') # router.register(r'events', views.ApplicationViewSet, basename='application-events') -# router.register(r'plans', views.UserViewSet, basename='user') +router.register(r'plans', views.UserPlanViewSet, basename='user-plans') # urlpatterns = @@ -20,7 +20,6 @@ urlpatterns = [ path('api/v1/plans//', ApplicationPromoteUserApiView.as_view(), name='promotion-create'), path('api/v1/plans//promotins', ApplicationPromotionListApiView.as_view(), name='promotion-list'), path('api/v1/events/submit', ApplicationEventSubmitAPIView.as_view(), name='event-create'), - path('api/v1/events/get', ApplicationEventRetrieveAPIView.as_view(), name='event-retrieve'), - path('api/v1/', include(router.urls), name='events'), - + # path('api/v1/events/get', ApplicationEventRetrieveAPIView.as_view(), name='event-retrieve'), + path('api/v2/', include(router.urls)), ] diff --git a/apps/promotions/views.py b/apps/promotions/views.py index 626845a..915e477 100644 --- a/apps/promotions/views.py +++ b/apps/promotions/views.py @@ -10,7 +10,7 @@ from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRe from apps.gooyal_oauth2.utils import get_application from utils.exceptions import UnprocessableEntity from .models import Plan, Promotion, EventSaver -from .serializers import PlanSerializer, PromotionSerializer, EventSerializer, PromoteSerializer +from .serializers import PlanSerializer, PromotionSerializer, EventSerializer, PromoteSerializer, UserPlanSerializer from .tasks import analyze_event_task from ..users.models import User @@ -100,14 +100,15 @@ class ApplicationViewSet( serializer.instance = event -class UserViewSet(mixins.RetrieveModelMixin, - mixins.ListModelMixin, +class UserPlanViewSet(mixins.RetrieveModelMixin, + # mixins.ListModelMixin, + mixins.CreateModelMixin, GenericViewSet): - serializer_class = PlanSerializer + serializer_class = UserPlanSerializer permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements] required_alternate_scopes = { - "GET": [["promotions.user.self-plans:list-retrieve"]], - "POST": [["promotions.user.self-plans:list-retrieve"]], + "GET": [["promotions.user.plans:list-retrieve"]], + "POST": [["promotions.user.plans:list-retrieve"]], } def get_queryset(self): @@ -117,39 +118,39 @@ class UserViewSet(mixins.RetrieveModelMixin, queryset = Plan.objects.all() return queryset - @extend_schema(responses=PlanSerializer(many=True)) - @action(detail=False, methods=['POST'], serializer_class=PlanSerializer) - def available(self, request): - serializer = self.get_serializer(data=self.request.data) - serializer.is_valid(raise_exception=True) - print(serializer.validated_data) - plans = self.paginate_queryset(self.filter_queryset(self.get_queryset())) - for plan in plans: - plan.calculate_promotion(**serializer.validated_data) - serializer = self.get_serializer(plans, many=True) - return self.get_paginated_response(serializer.data) + # @extend_schema(responses=PlanSerializer(many=True)) + # @action(detail=False, methods=['POST'], serializer_class=PlanSerializer) + # def available(self, request): + # serializer = self.get_serializer(data=self.request.data) + # serializer.is_valid(raise_exception=True) + # print(serializer.validated_data) + # plans = self.paginate_queryset(self.filter_queryset(self.get_queryset())) + # for plan in plans: + # plan.calculate_promotion(**serializer.validated_data) + # serializer = self.get_serializer(plans, many=True) + # return self.get_paginated_response(serializer.data) - @action(detail=True, methods=['POST'], serializer_class=EventSerializer) + @action(detail=True, methods=['POST'], serializer_class=PromoteSerializer) def promote(self, request, pk=None): - serializer = self.get_serializer(data=self.request.data) + plan = self.get_object() + serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) - - if hasattr(self.request, 'user'): - user = self.request.user - else: - user = None - + data = serializer.validated_data application = get_application(self.request) - # EventSaver.objects.first(event_label=serializer.validated_data['event_label'], valid_application__contains=application.uuid) + event_saver: EventSaver = EventSaver.objects.filter(event_label=serializer.validated_data['label']).first() - try: - event = event_saver.save_event(user=user, application=application, **serializer.validated_data) - except Exception as e: - raise exceptions.ValidationError(str(e)) - plan: Plan = self.get_object() + user = self.request.user.pk or serializer.validated_data['user'] + # try: + event = event_saver.save_event(user=user, application=application, **serializer.validated_data) + # except Exception as e: + # raise exceptions.ValidationError(str(e)) - return + result_list = plan.process_event(event) + data["promotions"] = result_list + + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) class ApplicationPromoteUserApiView(CreateAPIView): diff --git a/main/settings.py b/main/settings.py index 9c2d79f..be5a728 100644 --- a/main/settings.py +++ b/main/settings.py @@ -36,6 +36,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.contrib.postgres', # pip installed apps 'drf_spectacular',