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
37 changes: 37 additions & 0 deletions promo_code/business/pagination.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import rest_framework.exceptions
import rest_framework.pagination
import rest_framework.response


class CustomLimitOffsetPagination(
rest_framework.pagination.LimitOffsetPagination,
):
default_limit = 10
max_limit = 100

def get_limit(self, request):
param_limit = request.query_params.get(self.limit_query_param)
if param_limit is not None:
try:
limit = int(param_limit)
if limit < 0:
raise rest_framework.exceptions.ValidationError(
'Limit cannot be negative.',
)

if limit == 0:
return 0

if self.max_limit:
return min(limit, self.max_limit)

return limit
except (TypeError, ValueError):
pass

return self.default_limit

def get_paginated_response(self, data):
response = rest_framework.response.Response(data)
response.headers['X-Total-Count'] = str(self.count)
return response
85 changes: 85 additions & 0 deletions promo_code/business/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import django.contrib.auth.password_validation
import django.core.exceptions
import django.core.validators
import django.utils.timezone
import pycountry
import rest_framework.exceptions
import rest_framework.serializers
Expand Down Expand Up @@ -361,3 +362,87 @@ def to_representation(self, instance):
data.pop('promo_unique', None)

return data


class PromoReadOnlySerializer(rest_framework.serializers.ModelSerializer):
promo_id = rest_framework.serializers.UUIDField(
source='id',
read_only=True,
)
company_id = rest_framework.serializers.UUIDField(
source='company.id',
read_only=True,
)
company_name = rest_framework.serializers.CharField(
source='company.name',
read_only=True,
)
target = TargetSerializer()
promo_unique = rest_framework.serializers.SerializerMethodField()
like_count = rest_framework.serializers.SerializerMethodField()
used_count = rest_framework.serializers.SerializerMethodField()
active = rest_framework.serializers.SerializerMethodField()

class Meta:
model = business_models.Promo
fields = (
'promo_id',
'company_id',
'company_name',
'description',
'image_url',
'target',
'max_count',
'active_from',
'active_until',
'mode',
'promo_common',
'promo_unique',
'like_count',
'used_count',
'active',
)

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

return None

def get_like_count(self, obj):
return 0

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

return 0

def get_active(self, obj):
now = django.utils.timezone.now().date()
active_from = obj.active_from
active_until = obj.active_until

date_active = True

if active_from and active_from > now:
date_active = False

if active_until and active_until < now:
date_active = False

else:
max_count_condition = obj.unique_codes.filter(
is_used=False,
).exists()

return date_active and max_count_condition

def to_representation(self, instance):
data = super().to_representation(instance)
if instance.mode == business_models.Promo.MODE_COMMON:
data.pop('promo_unique', None)
else:
data.pop('promo_common', None)

return data
5 changes: 5 additions & 0 deletions promo_code/business/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,9 @@
business.views.PromoCreateView.as_view(),
name='promo-create',
),
django.urls.path(
'promo/list',
business.views.CompanyPromoListView.as_view(),
name='company-promo-list',
),
]
85 changes: 85 additions & 0 deletions promo_code/business/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import re

import django.db.models
import pycountry
import rest_framework.exceptions
import rest_framework.generics
import rest_framework.permissions
Expand All @@ -9,6 +13,7 @@
import rest_framework_simplejwt.views

import business.models
import business.pagination
import business.permissions
import business.serializers
import core.views
Expand Down Expand Up @@ -128,3 +133,83 @@ def post(self, request, *args, **kwargs):
serializer.errors,
status=rest_framework.status.HTTP_400_BAD_REQUEST,
)


class CompanyPromoListView(rest_framework.generics.ListAPIView):
permission_classes = [
rest_framework.permissions.IsAuthenticated,
business.permissions.IsCompanyUser,
]
serializer_class = business.serializers.PromoReadOnlySerializer
pagination_class = business.pagination.CustomLimitOffsetPagination

def get_queryset(self):
queryset = business.models.Promo.objects.filter(
company=self.request.user,
)

countries = self.request.query_params.getlist('country', [])
country_list = []

for country_group in countries:
country_list.extend(country_group.split(','))

country_list = [c.strip() for c in country_list if c.strip()]

if country_list:
regex_pattern = r'(' + '|'.join(map(re.escape, country_list)) + ')'
queryset = queryset.filter(
django.db.models.Q(target__country__iregex=regex_pattern)
| django.db.models.Q(target__country__isnull=True),
)

sort_by = self.request.query_params.get('sort_by')
if sort_by in ['active_from', 'active_until']:
queryset = queryset.order_by(f'-{sort_by}')
else:
queryset = queryset.order_by('-created_at') # noqa: R504

return queryset # noqa: R504

def validate_query_params(self):
errors = {}
countries = self.request.query_params.getlist('country', [])
country_list = []

for country_group in countries:
country_list.extend(country_group.split(','))

country_list = [c.strip().upper() for c in country_list if c.strip()]
invalid_countries = []

for code in country_list:
try:
pycountry.countries.lookup(code)
except LookupError:
invalid_countries.append(code)

if invalid_countries:
errors['country'] = (
f'Invalid country codes: {", ".join(invalid_countries)}'
)

sort_by = self.request.query_params.get('sort_by')
if sort_by and sort_by not in ['active_from', 'active_until']:
errors['sort_by'] = (
'Invalid sort_by parameter. '
'Available values: active_from, active_until'
)

if errors:
raise rest_framework.exceptions.ValidationError(errors)

def list(self, request, *args, **kwargs):
try:
self.validate_query_params()
except rest_framework.exceptions.ValidationError as e:
return rest_framework.response.Response(
e.detail,
status=rest_framework.status.HTTP_400_BAD_REQUEST,
)

return super().list(request, *args, **kwargs)