Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions promo_code/business/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# === Promo ===
PROMO_MODE_COMMON = 'COMMON'
PROMO_MODE_UNIQUE = 'UNIQUE'
PROMO_MODE_CHOICES = [
(PROMO_MODE_COMMON, 'Common'),
(PROMO_MODE_UNIQUE, 'Unique'),
]


PROMO_DESC_MIN_LENGTH = 10
PROMO_DESC_MAX_LENGTH = 300
PROMO_IMAGE_URL_MAX_LENGTH = 350


# === Promo Common ===
PROMO_COMMON_CODE_MIN_LENGTH = 5
PROMO_COMMON_CODE_MAX_LENGTH = 30
PROMO_COMMON_MIN_COUNT = 1
PROMO_COMMON_MAX_COUNT = 100_000_000


# === Promo Unique ===
PROMO_UNIQUE_CODE_MIN_LENGTH = 3
PROMO_UNIQUE_CODE_MAX_LENGTH = 30
PROMO_UNIQUE_LIST_MIN_ITEMS = 1
PROMO_UNIQUE_LIST_MAX_ITEMS = 5000
PROMO_UNIQUE_MAX_COUNT = 1


# === Target ===
TARGET_AGE_MIN = 0
TARGET_AGE_MAX = 100
TARGET_COUNTRY_CODE_LENGTH = 2
TARGET_CATEGORY_MIN_LENGTH = 2
TARGET_CATEGORY_MAX_LENGTH = 20
TARGET_CATEGORY_MAX_ITEMS = 20
7 changes: 5 additions & 2 deletions promo_code/business/managers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import django.contrib.auth.models
import django.db.models

import business.constants
import business.models


Expand Down Expand Up @@ -36,10 +37,12 @@ def create_promo(
**kwargs,
)

if promo.mode == business.models.Promo.MODE_COMMON:
if promo.mode == business.constants.PROMO_MODE_COMMON:
promo.promo_common = promo_common
promo.save(update_fields=['promo_common'])
elif promo.mode == business.models.Promo.MODE_UNIQUE and promo_unique:
elif (
promo.mode == business.constants.PROMO_MODE_UNIQUE and promo_unique
):
self._create_unique_codes(promo, promo_unique)

return promo
Expand Down
25 changes: 13 additions & 12 deletions promo_code/business/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import django.contrib.auth.models
import django.db.models

import business.constants
import business.managers


Expand Down Expand Up @@ -33,13 +34,6 @@ def __str__(self):


class Promo(django.db.models.Model):
MODE_COMMON = 'COMMON'
MODE_UNIQUE = 'UNIQUE'
MODE_CHOICES = [
(MODE_COMMON, 'Common'),
(MODE_UNIQUE, 'Unique'),
]

id = django.db.models.UUIDField(
'UUID',
primary_key=True,
Expand All @@ -53,19 +47,24 @@ class Promo(django.db.models.Model):
null=True,
blank=True,
)
description = django.db.models.CharField(max_length=300)
description = django.db.models.CharField(
max_length=business.constants.PROMO_DESC_MAX_LENGTH,
)
image_url = django.db.models.URLField(
max_length=350,
max_length=business.constants.PROMO_IMAGE_URL_MAX_LENGTH,
blank=True,
null=True,
)
target = django.db.models.JSONField(default=dict)
max_count = django.db.models.IntegerField()
active_from = django.db.models.DateField(null=True, blank=True)
active_until = django.db.models.DateField(null=True, blank=True)
mode = django.db.models.CharField(max_length=10, choices=MODE_CHOICES)
mode = django.db.models.CharField(
max_length=10,
choices=business.constants.PROMO_MODE_CHOICES,
)
promo_common = django.db.models.CharField(
max_length=30,
max_length=business.constants.PROMO_COMMON_CODE_MAX_LENGTH,
blank=True,
null=True,
)
Expand All @@ -86,7 +85,9 @@ class PromoCode(django.db.models.Model):
related_name='unique_codes',
to_field='id',
)
code = django.db.models.CharField(max_length=30)
code = django.db.models.CharField(
max_length=business.constants.PROMO_UNIQUE_CODE_MAX_LENGTH,
)
is_used = django.db.models.BooleanField(default=False)
used_at = django.db.models.DateTimeField(null=True, blank=True)

Expand Down
59 changes: 32 additions & 27 deletions promo_code/business/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import rest_framework_simplejwt.tokens
import rest_framework_simplejwt.views

import business.constants
import business.models as business_models
import business.validators

Expand Down Expand Up @@ -151,26 +152,26 @@ def validate(self, attrs):

class TargetSerializer(rest_framework.serializers.Serializer):
age_from = rest_framework.serializers.IntegerField(
min_value=0,
max_value=100,
min_value=business.constants.TARGET_AGE_MIN,
max_value=business.constants.TARGET_AGE_MAX,
required=False,
)
age_until = rest_framework.serializers.IntegerField(
min_value=0,
max_value=100,
min_value=business.constants.TARGET_AGE_MIN,
max_value=business.constants.TARGET_AGE_MAX,
required=False,
)
country = rest_framework.serializers.CharField(
max_length=2,
min_length=2,
max_length=business.constants.TARGET_COUNTRY_CODE_LENGTH,
min_length=business.constants.TARGET_COUNTRY_CODE_LENGTH,
required=False,
)
categories = rest_framework.serializers.ListField(
child=rest_framework.serializers.CharField(
min_length=2,
max_length=20,
min_length=business.constants.TARGET_CATEGORY_MIN_LENGTH,
max_length=business.constants.TARGET_CATEGORY_MAX_LENGTH,
),
max_length=20,
max_length=business.constants.TARGET_CATEGORY_MAX_ITEMS,
required=False,
allow_empty=True,
)
Expand Down Expand Up @@ -202,31 +203,31 @@ def validate(self, data):

class PromoCreateSerializer(rest_framework.serializers.ModelSerializer):
description = rest_framework.serializers.CharField(
min_length=10,
max_length=300,
min_length=business.constants.PROMO_DESC_MIN_LENGTH,
max_length=business.constants.PROMO_DESC_MAX_LENGTH,
required=True,
)
image_url = rest_framework.serializers.CharField(
required=False,
max_length=350,
max_length=business.constants.PROMO_IMAGE_URL_MAX_LENGTH,
validators=[
django.core.validators.URLValidator(schemes=['http', 'https']),
],
)
target = TargetSerializer(required=True, allow_null=True)
promo_common = rest_framework.serializers.CharField(
min_length=5,
max_length=30,
min_length=business.constants.PROMO_COMMON_CODE_MIN_LENGTH,
max_length=business.constants.PROMO_COMMON_CODE_MAX_LENGTH,
required=False,
allow_null=True,
)
promo_unique = rest_framework.serializers.ListField(
child=rest_framework.serializers.CharField(
min_length=3,
max_length=30,
min_length=business.constants.PROMO_UNIQUE_CODE_MIN_LENGTH,
max_length=business.constants.PROMO_UNIQUE_CODE_MAX_LENGTH,
),
min_length=1,
max_length=5000,
min_length=business.constants.PROMO_UNIQUE_LIST_MIN_ITEMS,
max_length=business.constants.PROMO_UNIQUE_LIST_MAX_ITEMS,
required=False,
allow_null=True,
)
Expand Down Expand Up @@ -267,7 +268,7 @@ def to_representation(self, instance):
data = super().to_representation(instance)
data['target'] = instance.target

if instance.mode == business_models.Promo.MODE_UNIQUE:
if instance.mode == business.constants.PROMO_MODE_UNIQUE:
data['promo_unique'] = [
code.code for code in instance.unique_codes.all()
]
Expand Down Expand Up @@ -318,18 +319,20 @@ class Meta:
)

def get_promo_unique(self, obj):
if obj.mode == business_models.Promo.MODE_UNIQUE:
if obj.mode == business.constants.PROMO_MODE_UNIQUE:
return [code.code for code in obj.unique_codes.all()]

return None

def get_like_count(self, obj):
# TODO
return 0

def get_used_count(self, obj):
if obj.mode == business_models.Promo.MODE_UNIQUE:
if obj.mode == business.constants.PROMO_MODE_UNIQUE:
return obj.unique_codes.filter(is_used=True).count()

# TODO
return 0

def get_active(self, obj):
Expand All @@ -354,7 +357,7 @@ def get_active(self, obj):

def to_representation(self, instance):
data = super().to_representation(instance)
if instance.mode == business_models.Promo.MODE_COMMON:
if instance.mode == business.constants.PROMO_MODE_COMMON:
data.pop('promo_unique', None)
else:
data.pop('promo_common', None)
Expand All @@ -368,13 +371,13 @@ class PromoDetailSerializer(rest_framework.serializers.ModelSerializer):
read_only=True,
)
description = rest_framework.serializers.CharField(
min_length=10,
max_length=300,
min_length=business.constants.PROMO_DESC_MIN_LENGTH,
max_length=business.constants.PROMO_DESC_MAX_LENGTH,
required=True,
)
image_url = rest_framework.serializers.CharField(
required=False,
max_length=350,
max_length=business.constants.PROMO_IMAGE_URL_MAX_LENGTH,
validators=[
django.core.validators.URLValidator(schemes=['http', 'https']),
],
Expand Down Expand Up @@ -407,7 +410,7 @@ class Meta:
)

def get_promo_unique(self, obj):
if obj.mode == business_models.Promo.MODE_UNIQUE:
if obj.mode == business.constants.PROMO_MODE_UNIQUE:
return [code.code for code in obj.unique_codes.all()]

return None
Expand All @@ -432,10 +435,12 @@ def validate(self, data):
return validator.validate()

def get_like_count(self, obj):
# TODO
return 0

def get_used_count(self, obj):
if obj.mode == business_models.Promo.MODE_UNIQUE:
if obj.mode == business.constants.PROMO_MODE_UNIQUE:
return obj.unique_codes.filter(is_used=True).count()

# TODO
return 0
18 changes: 11 additions & 7 deletions promo_code/business/validators.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import rest_framework.exceptions
import rest_framework.permissions

import business.constants
import business.models


Expand Down Expand Up @@ -60,14 +60,14 @@ def validate(self):
active_until = self.full_data.get('active_until')

if mode not in [
business.models.Promo.MODE_COMMON,
business.models.Promo.MODE_UNIQUE,
business.constants.PROMO_MODE_COMMON,
business.constants.PROMO_MODE_UNIQUE,
]:
raise rest_framework.exceptions.ValidationError(
{'mode': 'Invalid mode.'},
)

if mode == business.models.Promo.MODE_COMMON:
if mode == business.constants.PROMO_MODE_COMMON:
if not promo_common:
raise rest_framework.exceptions.ValidationError(
{
Expand All @@ -84,7 +84,11 @@ def validate(self):
),
},
)
if max_count is None or not (0 <= max_count <= 100_000_000):
if max_count is None or not (
business.constants.PROMO_COMMON_MIN_COUNT
< max_count
<= business.constants.PROMO_COMMON_MAX_COUNT
):
raise rest_framework.exceptions.ValidationError(
{
'max_count': (
Expand All @@ -94,7 +98,7 @@ def validate(self):
},
)

elif mode == business.models.Promo.MODE_UNIQUE:
elif mode == business.constants.PROMO_MODE_UNIQUE:
if promo_common is not None:
raise rest_framework.exceptions.ValidationError(
{
Expand All @@ -103,7 +107,7 @@ def validate(self):
),
},
)
if max_count != 1:
if max_count != business.constants.PROMO_UNIQUE_MAX_COUNT:
raise rest_framework.exceptions.ValidationError(
{'max_count': 'Must be 1 for UNIQUE mode.'},
)
Expand Down