Skip to content
Open
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
12 changes: 12 additions & 0 deletions apps/products/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from datetime import date

from rest_framework import serializers
from rest_framework.relations import PrimaryKeyRelatedField

Expand All @@ -15,6 +17,16 @@ class Meta:
model = PriceInterval
fields = '__all__'

def validate(self, data):
if data['start_date'] > data['end_date']:
raise serializers.ValidationError("start_date can not be bigger than end_date.")
return data

def validate_end_date(self, value):
if not value:
return date.max
return value


class ProductStatsSerializer(serializers.Serializer):
product = PrimaryKeyRelatedField(queryset=Product.objects.all(), required=True)
Expand Down
Empty file added apps/products/utils/__init__.py
Empty file.
39 changes: 39 additions & 0 deletions apps/products/utils/price_counter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from typing import Dict
from datetime import timedelta

from apps.products.models import PriceInterval


class Prices:
def __init__(self, data: dict):
self.data: dict = data
self.price = 0
self.days = 0

def query_records(self):
return PriceInterval.objects.filter(
product=self.data.get('product'),
end_date__gte=self.data.get('start_date'),
start_date__lte=self.data.get('end_date')
)

def get_average(self) -> Dict:
for record in self.query_records():
if record.start_date > self.data.get('start_date'):
start_date = record.start_date
else:
start_date = self.data.get('start_date')

if record.end_date < self.data.get('end_date'):
end_date = record.end_date
else:
end_date = self.data.get('end_date')

days = end_date - start_date + timedelta(days=1)

self.price += record.price * days.days
self.days += days.days
return {
"price": self.price / self.days,
"days": (self.data.get('end_date') - self.data.get('start_date') + timedelta(days=1)).days
}
47 changes: 47 additions & 0 deletions apps/products/utils/price_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from datetime import timedelta


class PriceValidator:
def __init__(self, serializer, queryset):
self.serializer = serializer
self.validated_data = serializer.validated_data
self.queryset = queryset

def get_existing_price_query(self, process_date):
"""Get already existing prices for submitted dates."""
prices = self.queryset.filter(
start_date__lt=process_date,
end_date__gt=process_date).first()
return prices

def update_date_intervals(self, start_date_query, end_date_query):
"""Update date intervals for dates that persist within current dates."""
if start_date_query:
start_date_query.end_date = self.validated_data.get('start_date') - timedelta(days=1)
start_date_query.save()
if end_date_query:
end_date_query.start_date = self.validated_data.get('end_date') + timedelta(days=1)
end_date_query.save()

def clear_existing_range(self):
"""Clears outdated prices within submitted dates."""
self.queryset.filter(
start_date__gte=self.validated_data.get('start_date'),
end_date__lte=self.validated_data.get('end_date')
).delete()

def validate_save_data(self):
if self.queryset:
self.update_date_intervals(
start_date_query=self.get_existing_price_query(self.validated_data.get('start_date')),
end_date_query=self.get_existing_price_query(self.validated_data.get('end_date'))
)
self.serializer.save()

def insert_price(self):
self.queryset = self.queryset.filter(product=self.validated_data.get('product')).order_by('start_date')
if not self.queryset:
self.serializer.save()
else:
self.clear_existing_range()
self.validate_save_data()
29 changes: 26 additions & 3 deletions apps/products/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
from drf_util.views import BaseViewSet, BaseCreateModelMixin, BaseListModelMixin
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny
from rest_framework.response import Response

from apps.products.models import Product, PriceInterval
from apps.products.serializers import ProductSerializer, PriceIntervalSerializer
from apps.products.serializers import ProductSerializer, PriceIntervalSerializer, ProductStatsSerializer
from apps.products.utils.price_counter import Prices
from apps.products.utils.price_validator import PriceValidator


class ProductViewSet(BaseListModelMixin, BaseCreateModelMixin, BaseViewSet):
Expand All @@ -11,7 +16,16 @@ class ProductViewSet(BaseListModelMixin, BaseCreateModelMixin, BaseViewSet):
serializer_class = ProductSerializer
queryset = Product.objects.all()

# TODO create here a new endpoint for stats
@action(detail=False, methods=['GET'])
def stats(self, request, *args, **kwargs):
serializer = ProductStatsSerializer(data=request.query_params)

if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

average_prices = Prices(serializer.validated_data)
average_prices.get_average()
return Response(average_prices, status=status.HTTP_200_OK)


class ProductPriceViewSet(BaseListModelMixin, BaseCreateModelMixin, BaseViewSet):
Expand All @@ -20,4 +34,13 @@ class ProductPriceViewSet(BaseListModelMixin, BaseCreateModelMixin, BaseViewSet)
serializer_class = PriceIntervalSerializer
queryset = PriceInterval.objects.all()

# TODO create interval here
def create(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)

if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

interval_inserter = PriceValidator(serializer, self.queryset)
interval_inserter.insert_price()
return Response(serializer.data, status=status.HTTP_201_CREATED)

3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ django-nose==1.4.7
django-extensions==3.2.1
jsonschema==3.2.0
PyJWT==1.7.1
django-filter==22.1
django-filter==22.1
psycopg2-binary==2.9.5