last commit

This commit is contained in:
Sayyid Hamid Mahdavi 2025-10-14 20:49:31 +03:30
parent 491670200c
commit e30005021d
7 changed files with 106 additions and 50 deletions

View file

@ -2,11 +2,11 @@ from functools import update_wrapper
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(Promotion)
admin.site.register(Event)
admin.site.register(EventSaver)
admin.site.register(Recipient)

View file

@ -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',
),
]

View file

@ -5,14 +5,47 @@ from django.utils import timezone
from oauth2_provider.settings import oauth2_settings
from apps.promotions import handlers
from apps.promotions.handlers import PromotionTypeChoices, BasePromotionHandler
from apps.users.models import User
from utils.models import BaseModel
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):
pass
@ -63,20 +96,6 @@ class Plan(BaseModel):
def __str__(self):
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):
self.promote(event=event)
@ -155,7 +174,6 @@ class Recipient(BaseModel):
raise
class PromotionQuerySet(models.QuerySet):
def used(self, user):
return self.filter(user=user, state__in=[Promotion.StateChoices.CREATED])

View file

@ -16,7 +16,7 @@ class PromotionSerializer(serializers.ModelSerializer):
"user",
"application",
)
read_only_fields = ('user','application', "promotion_amount",)
read_only_fields = ('user', 'application', "promotion_amount",)
class EventSerializer(serializers.ModelSerializer):
@ -28,7 +28,8 @@ class EventSerializer(serializers.ModelSerializer):
"user",
"application",
)
read_only_fields = ('user','application')
read_only_fields = ('user', 'application')
class PlanSerializer(serializers.ModelSerializer):
base_amount = serializers.IntegerField(required=True)
@ -36,13 +37,13 @@ class PlanSerializer(serializers.ModelSerializer):
class Meta:
model = Plan
fields = (
"title",
"description",
"base_amount",
"application",
"promotion_amount"
)
fields = ("label"
"title",
"description",
"base_amount",
"application",
"promotion_amount"
)
read_only_fields = ('application', "promotion_amount", "title", "description")
# def get_promotion_amount(self, obj):
@ -57,5 +58,4 @@ class PlanPromotSerializer(serializers.ModelSerializer):
"user",
"application",
)
read_only_fields = ('user','application')
read_only_fields = ('user', 'application')

View file

@ -1,11 +1,12 @@
from drf_spectacular.utils import extend_schema
from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework import exceptions
from rest_framework.viewsets import GenericViewSet
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
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 .tasks import analyze_event_task
from ..users.models import User
@ -74,14 +75,20 @@ class ApplicationEventViewSet(
"POST": [["promotions.application.event:submit"]],
}
def perform_create(self, serializer):
def perform_create(self, serializer: EventSerializer):
if hasattr(self.request, 'user'):
user = self.request.user
else:
user = None
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)

View file

@ -188,7 +188,7 @@ REST_FRAMEWORK = {
# 'otp_min': '1/min',
# 'otp_day': '20/day',
},
'EXCEPTION_HANDLER': 'utils.exceptions.ExceptionHandler',
'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
}
AUTHENTICATION_BACKENDS = (

View file

@ -5,34 +5,33 @@ from rest_framework.views import exception_handler as drf_exception_handler
logger = logging.getLogger(__name__)
class ExceptionHandler:
@classmethod
def __call__(cls, exc, context):
def exception_handler(exc, context):
# پاسخ پیش‌فرض DRF را دریافت می‌کنیم
response = drf_exception_handler(exc, context)
response = drf_exception_handler(exc, context)
if response is not None:
# ساختار دلخواه خود را تعریف می‌کنیم
response_data = {
'success': False,
'status_code': response.status_code,
'message': 'An error occurred',
'details': {}
}
if response is not None:
# ساختار دلخواه خود را تعریف می‌کنیم
response_data = {
'success': False,
'status_code': response.status_code,
'message': 'An error occurred',
'details': {}
}
if isinstance(exc, APIException):
response_data['message'] = exc.detail if isinstance(exc.detail, str) else "Validation error"
response_data['details'] = exc.detail if isinstance(exc.detail, dict) else {}
if isinstance(exc, APIException):
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 = response_data
response.data = response_data
return response
return response
# در فایل middleware.py
from django.http import JsonResponse
class ErrorMiddleware:
def __init__(self, get_response):
self.get_response = get_response
@ -51,4 +50,4 @@ class ErrorMiddleware:
"details": str(exception) # فقط در حالت توسعه! در تولید بهتر است لاگ شود.
},
status=500
)
)