diff --git a/promo_code/business/constants.py b/promo_code/business/constants.py new file mode 100644 index 0000000..b705aba --- /dev/null +++ b/promo_code/business/constants.py @@ -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 diff --git a/promo_code/business/managers.py b/promo_code/business/managers.py index a21b86b..e8851b2 100644 --- a/promo_code/business/managers.py +++ b/promo_code/business/managers.py @@ -1,6 +1,7 @@ import django.contrib.auth.models import django.db.models +import business.constants import business.models @@ -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 diff --git a/promo_code/business/models.py b/promo_code/business/models.py index 31181b5..0418dc3 100644 --- a/promo_code/business/models.py +++ b/promo_code/business/models.py @@ -3,6 +3,7 @@ import django.contrib.auth.models import django.db.models +import business.constants import business.managers @@ -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, @@ -53,9 +47,11 @@ 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, ) @@ -63,9 +59,12 @@ class Promo(django.db.models.Model): 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, ) @@ -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) diff --git a/promo_code/business/serializers.py b/promo_code/business/serializers.py index 454f240..481661e 100644 --- a/promo_code/business/serializers.py +++ b/promo_code/business/serializers.py @@ -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 @@ -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, ) @@ -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, ) @@ -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() ] @@ -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): @@ -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) @@ -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']), ], @@ -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 @@ -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 diff --git a/promo_code/business/validators.py b/promo_code/business/validators.py index c00cb4d..08be430 100644 --- a/promo_code/business/validators.py +++ b/promo_code/business/validators.py @@ -1,6 +1,6 @@ import rest_framework.exceptions -import rest_framework.permissions +import business.constants import business.models @@ -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( { @@ -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': ( @@ -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( { @@ -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.'}, )