last commit
This commit is contained in:
parent
491670200c
commit
e30005021d
7 changed files with 106 additions and 50 deletions
|
|
@ -2,11 +2,11 @@ from functools import update_wrapper
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Promotion, Plan, Event, Recipient
|
from .models import Promotion, Plan, Event, Recipient, EventSaver
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Plan)
|
admin.site.register(Plan)
|
||||||
admin.site.register(Promotion)
|
admin.site.register(Promotion)
|
||||||
admin.site.register(Event)
|
admin.site.register(Event)
|
||||||
|
admin.site.register(EventSaver)
|
||||||
admin.site.register(Recipient)
|
admin.site.register(Recipient)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Generated by Django 5.1.7 on 2025-10-14 16:49
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('promotions', '0010_remove_plan_max_amount_remove_plan_min_amount_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='EventSaver',
|
||||||
|
fields=[
|
||||||
|
('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, db_index=True)),
|
||||||
|
('title', models.CharField(max_length=255, verbose_name='title')),
|
||||||
|
('event_label', models.CharField(db_index=True, max_length=255, unique=True, verbose_name='label')),
|
||||||
|
('save_once', models.BooleanField(default=False, verbose_name='save once')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='recipient',
|
||||||
|
name='wallet_uuid',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -5,14 +5,47 @@ from django.utils import timezone
|
||||||
|
|
||||||
from oauth2_provider.settings import oauth2_settings
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
|
||||||
|
|
||||||
from apps.promotions import handlers
|
from apps.promotions import handlers
|
||||||
|
|
||||||
from apps.promotions.handlers import PromotionTypeChoices, BasePromotionHandler
|
from apps.promotions.handlers import PromotionTypeChoices, BasePromotionHandler
|
||||||
|
|
||||||
|
|
||||||
from apps.users.models import User
|
from apps.users.models import User
|
||||||
from utils.models import BaseModel
|
from utils.models import BaseModel
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class EventSaverQuerySet(models.QuerySet):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EventSaver(BaseModel):
|
||||||
|
title = models.CharField(_('title'), max_length=255)
|
||||||
|
event_label = models.CharField(_('label'), max_length=255, unique=True, db_index=True)
|
||||||
|
save_once = models.BooleanField(_('save once'), default=False)
|
||||||
|
|
||||||
|
objects = EventSaverQuerySet.as_manager()
|
||||||
|
|
||||||
|
def save_event(self, **kwargs):
|
||||||
|
user = kwargs.get('user')
|
||||||
|
application = kwargs.get('application')
|
||||||
|
label = kwargs.get('label')
|
||||||
|
data = kwargs.get('data')
|
||||||
|
|
||||||
|
if self.save_once:
|
||||||
|
event, created = Event.objects.get_or_create(user=user, application=application, label=label, defaults={"data": data})
|
||||||
|
if created:
|
||||||
|
return event
|
||||||
|
else:
|
||||||
|
raise Exception(_('already saved'))
|
||||||
|
|
||||||
|
else:
|
||||||
|
event = Event.objects.create(user=user, application=application, label=label, defaults={"date": date})
|
||||||
|
return event
|
||||||
|
|
||||||
|
|
||||||
class EventQuerySet(models.QuerySet):
|
class EventQuerySet(models.QuerySet):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -63,20 +96,6 @@ class Plan(BaseModel):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
# @property
|
|
||||||
# def handler(self):
|
|
||||||
# if self.promotion_type == PromotionTypeChoices.PERCENTAGE.name:
|
|
||||||
# return handlers.PercentageHandler(self)
|
|
||||||
#
|
|
||||||
# elif self.promotion_type == PromotionTypeChoices.REFERRAL.name:
|
|
||||||
# return handlers.ReferralHandler(self)
|
|
||||||
#
|
|
||||||
# elif self.promotion_type == PromotionTypeChoices.OTHERS.name:
|
|
||||||
# return handlers.OtherHandler(self)
|
|
||||||
#
|
|
||||||
# else:
|
|
||||||
# raise NotImplementedError()
|
|
||||||
|
|
||||||
def process_event(self, event=None):
|
def process_event(self, event=None):
|
||||||
self.promote(event=event)
|
self.promote(event=event)
|
||||||
|
|
||||||
|
|
@ -155,7 +174,6 @@ class Recipient(BaseModel):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PromotionQuerySet(models.QuerySet):
|
class PromotionQuerySet(models.QuerySet):
|
||||||
def used(self, user):
|
def used(self, user):
|
||||||
return self.filter(user=user, state__in=[Promotion.StateChoices.CREATED])
|
return self.filter(user=user, state__in=[Promotion.StateChoices.CREATED])
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ class PromotionSerializer(serializers.ModelSerializer):
|
||||||
"user",
|
"user",
|
||||||
"application",
|
"application",
|
||||||
)
|
)
|
||||||
read_only_fields = ('user','application', "promotion_amount",)
|
read_only_fields = ('user', 'application', "promotion_amount",)
|
||||||
|
|
||||||
|
|
||||||
class EventSerializer(serializers.ModelSerializer):
|
class EventSerializer(serializers.ModelSerializer):
|
||||||
|
|
@ -28,7 +28,8 @@ class EventSerializer(serializers.ModelSerializer):
|
||||||
"user",
|
"user",
|
||||||
"application",
|
"application",
|
||||||
)
|
)
|
||||||
read_only_fields = ('user','application')
|
read_only_fields = ('user', 'application')
|
||||||
|
|
||||||
|
|
||||||
class PlanSerializer(serializers.ModelSerializer):
|
class PlanSerializer(serializers.ModelSerializer):
|
||||||
base_amount = serializers.IntegerField(required=True)
|
base_amount = serializers.IntegerField(required=True)
|
||||||
|
|
@ -36,13 +37,13 @@ class PlanSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Plan
|
model = Plan
|
||||||
fields = (
|
fields = ("label"
|
||||||
"title",
|
"title",
|
||||||
"description",
|
"description",
|
||||||
"base_amount",
|
"base_amount",
|
||||||
"application",
|
"application",
|
||||||
"promotion_amount"
|
"promotion_amount"
|
||||||
)
|
)
|
||||||
read_only_fields = ('application', "promotion_amount", "title", "description")
|
read_only_fields = ('application', "promotion_amount", "title", "description")
|
||||||
|
|
||||||
# def get_promotion_amount(self, obj):
|
# def get_promotion_amount(self, obj):
|
||||||
|
|
@ -57,5 +58,4 @@ class PlanPromotSerializer(serializers.ModelSerializer):
|
||||||
"user",
|
"user",
|
||||||
"application",
|
"application",
|
||||||
)
|
)
|
||||||
read_only_fields = ('user','application')
|
read_only_fields = ('user', 'application')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
from drf_spectacular.utils import extend_schema
|
from drf_spectacular.utils import extend_schema
|
||||||
from rest_framework import mixins
|
from rest_framework import mixins
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework import exceptions
|
||||||
from rest_framework.viewsets import GenericViewSet
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
|
||||||
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
||||||
from apps.gooyal_oauth2.utils import get_application
|
from apps.gooyal_oauth2.utils import get_application
|
||||||
from .models import Plan, Promotion
|
from .models import Plan, Promotion, EventSaver
|
||||||
from .serializers import PlanSerializer, PromotionSerializer, EventSerializer
|
from .serializers import PlanSerializer, PromotionSerializer, EventSerializer
|
||||||
from .tasks import analyze_event_task
|
from .tasks import analyze_event_task
|
||||||
from ..users.models import User
|
from ..users.models import User
|
||||||
|
|
@ -74,14 +75,20 @@ class ApplicationEventViewSet(
|
||||||
"POST": [["promotions.application.event:submit"]],
|
"POST": [["promotions.application.event:submit"]],
|
||||||
}
|
}
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer: EventSerializer):
|
||||||
if hasattr(self.request, 'user'):
|
if hasattr(self.request, 'user'):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
else:
|
else:
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
application = get_application(self.request)
|
application = get_application(self.request)
|
||||||
event = serializer.save(user=user, application=application)
|
# 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))
|
||||||
|
|
||||||
analyze_event_task.delay(event.uuid)
|
analyze_event_task.delay(event.uuid)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ REST_FRAMEWORK = {
|
||||||
# 'otp_min': '1/min',
|
# 'otp_min': '1/min',
|
||||||
# 'otp_day': '20/day',
|
# 'otp_day': '20/day',
|
||||||
},
|
},
|
||||||
'EXCEPTION_HANDLER': 'utils.exceptions.ExceptionHandler',
|
'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
|
||||||
}
|
}
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = (
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
|
|
||||||
|
|
@ -5,34 +5,33 @@ from rest_framework.views import exception_handler as drf_exception_handler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ExceptionHandler:
|
|
||||||
|
|
||||||
@classmethod
|
def exception_handler(exc, context):
|
||||||
def __call__(cls, exc, context):
|
|
||||||
# پاسخ پیشفرض DRF را دریافت میکنیم
|
# پاسخ پیشفرض DRF را دریافت میکنیم
|
||||||
response = drf_exception_handler(exc, context)
|
response = drf_exception_handler(exc, context)
|
||||||
|
|
||||||
if response is not None:
|
if response is not None:
|
||||||
# ساختار دلخواه خود را تعریف میکنیم
|
# ساختار دلخواه خود را تعریف میکنیم
|
||||||
response_data = {
|
response_data = {
|
||||||
'success': False,
|
'success': False,
|
||||||
'status_code': response.status_code,
|
'status_code': response.status_code,
|
||||||
'message': 'An error occurred',
|
'message': 'An error occurred',
|
||||||
'details': {}
|
'details': {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isinstance(exc, APIException):
|
if isinstance(exc, APIException):
|
||||||
response_data['message'] = exc.detail if isinstance(exc.detail, str) else "Validation error"
|
response_data['message'] = exc.detail if isinstance(exc.detail, str) else "Validation error"
|
||||||
response_data['details'] = exc.detail if isinstance(exc.detail, dict) else {}
|
response_data['details'] = exc.detail if isinstance(exc.detail, dict) else {}
|
||||||
|
|
||||||
response.data = response_data
|
response.data = response_data
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
# در فایل middleware.py
|
# در فایل middleware.py
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
|
|
||||||
|
|
||||||
class ErrorMiddleware:
|
class ErrorMiddleware:
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
|
|
@ -51,4 +50,4 @@ class ErrorMiddleware:
|
||||||
"details": str(exception) # فقط در حالت توسعه! در تولید بهتر است لاگ شود.
|
"details": str(exception) # فقط در حالت توسعه! در تولید بهتر است لاگ شود.
|
||||||
},
|
},
|
||||||
status=500
|
status=500
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue