Skip to content

Commit

Permalink
Merge pull request #286 from Deep-Chill/issue272
Browse files Browse the repository at this point in the history
Updated Issue 272 pull request
  • Loading branch information
smirolo authored Nov 4, 2023
2 parents 4eedb2d + 00b3531 commit bcf8289
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 118 deletions.
10 changes: 7 additions & 3 deletions saas/api/billing.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
CartItemUploadSerializer, ChargeSerializer, CheckoutSerializer,
OrganizationCartSerializer, RedeemCouponSerializer,
ValidationErrorSerializer, CartItemUpdateSerializer,
UserCartItemCreateSerializer)
UserCartItemCreateSerializer, QueryParamCartItemSerializer)


LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -197,8 +197,12 @@ def delete(self, request, *args, **kwargs):
#pylint:disable=unused-argument
plan = None
email = None
plan = request.query_params.get('plan')
email = request.query_params.get('email')
query_serializer = QueryParamCartItemSerializer(data=request.query_params)

if query_serializer.is_valid(raise_exception=True):
plan = query_serializer.validated_data.get('plan', None)
email = query_serializer.validated_data.get('email', None)

self.destroy_in_session(request, plan=plan, email=email)
if is_authenticated(request):
# If the user is authenticated, we delete the cart items
Expand Down
88 changes: 55 additions & 33 deletions saas/api/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@

import logging

from rest_framework.generics import GenericAPIView, ListAPIView
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework.views import APIView

from .serializers import (CartItemSerializer, LifetimeSerializer,
MetricsSerializer, PeriodSerializer, BalancesDueSerializer)
MetricsSerializer, QueryParamPeriodSerializer, BalancesDueSerializer)
from .. import settings
from ..compat import gettext_lazy as _, reverse, six
from ..filters import DateRangeFilter
Expand All @@ -49,7 +50,7 @@


class BalancesAPIView(DateRangeContextMixin, ProviderMixin,
GenericAPIView):
APIView):
"""
Retrieves 12-month trailing deferred balances
Expand Down Expand Up @@ -135,10 +136,14 @@ class BalancesAPIView(DateRangeContextMixin, ProviderMixin,
"""
serializer_class = MetricsSerializer
filter_backends = (DateRangeFilter,)
queryset = Transaction.objects.all()

@swagger_auto_schema(query_serializer=PeriodSerializer)
@swagger_auto_schema(query_serializer=QueryParamPeriodSerializer)
def get(self, request, *args, **kwargs):
query_serializer = QueryParamPeriodSerializer(data=request.query_params)
query_serializer.is_valid(raise_exception=True)

num_periods = query_serializer.validated_data.get('num_periods')
period = query_serializer.validated_data.get('period')

#pylint: disable=unused-argument
result = []
Expand All @@ -153,21 +158,21 @@ def get(self, request, *args, **kwargs):
'until': self.ends_at,
'tz': self.timezone,
}
if self.num_periods:
if num_periods:
# If a num_periods argument is passed in, we use 'nb_months'
# or 'periods' depending on the period used
arg_name = 'nb_months' if self.period_func == month_periods \
arg_name = 'nb_months' if period == month_periods \
else 'num_periods'
period_func_kwargs[arg_name] = self.num_periods
period_func_kwargs[arg_name] = num_periods

# If the period is monthly, use the existing monthly
# balance function
if self.period_func == month_periods:
if period == month_periods:
values, _unit = abs_monthly_balances(**period_func_kwargs)
# If the period is not monthly, use abs_periodic_balances
else:
values, _unit = abs_periodic_balances(
period_func=self.period_func,
period_func=period,
**period_func_kwargs)

if _unit:
Expand All @@ -182,7 +187,7 @@ def get(self, request, *args, **kwargs):


class RevenueMetricAPIView(DateRangeContextMixin, ProviderMixin,
GenericAPIView):
APIView):
"""
Retrieves 12-month trailing revenue
Expand Down Expand Up @@ -305,19 +310,25 @@ class RevenueMetricAPIView(DateRangeContextMixin, ProviderMixin,
serializer_class = MetricsSerializer
filter_backends = (DateRangeFilter,)

@swagger_auto_schema(query_serializer=PeriodSerializer)
@swagger_auto_schema(query_serializer=QueryParamPeriodSerializer)
def get(self, request, *args, **kwargs):
#pylint:disable=unused-argument
query_serializer = QueryParamPeriodSerializer(data=request.query_params)
query_serializer.is_valid(raise_exception=True)

num_periods = query_serializer.validated_data.get('num_periods')
period = query_serializer.validated_data.get('period')

period_func_kwargs = {'from_date': self.ends_at,
'tz': self.timezone}

if self.num_periods:
arg_name = 'nb_months' if (self.period_func ==
month_periods) else 'periods'
period_func_kwargs[arg_name] = self.num_periods
if num_periods:
arg_name = 'nb_months' if period == month_periods \
else 'periods'
period_func_kwargs[arg_name] = num_periods

dates = convert_dates_to_utc(
self.period_func(**period_func_kwargs))
period(**period_func_kwargs))
unit = settings.DEFAULT_UNIT

account_table, _, _, table_unit = \
Expand Down Expand Up @@ -426,7 +437,7 @@ class CouponUsesAPIView(CartItemSmartListMixin, CouponUsesQuerysetMixin,


class CustomerMetricAPIView(DateRangeContextMixin, ProviderMixin,
GenericAPIView):
APIView):
"""
Retrieves 12-month trailing customer counts
Expand Down Expand Up @@ -527,21 +538,27 @@ class CustomerMetricAPIView(DateRangeContextMixin, ProviderMixin,
serializer_class = MetricsSerializer
filter_backends = (DateRangeFilter,)

@swagger_auto_schema(query_serializer=PeriodSerializer)
@swagger_auto_schema(query_serializer=QueryParamPeriodSerializer)
def get(self, request, *args, **kwargs):
#pylint:disable=unused-argument
query_serializer = QueryParamPeriodSerializer(data=request.query_params)
query_serializer.is_valid(raise_exception=True)

num_periods = query_serializer.validated_data.get('num_periods')
period = query_serializer.validated_data.get('period')

account_title = 'Payments'
account = Transaction.RECEIVABLE
# We use ``Transaction.RECEIVABLE`` which technically counts the number
# or orders, not the number of payments.
period_func_kwargs = {'from_date': self.ends_at, 'tz': self.timezone}

if self.num_periods:
arg_name = 'nb_months' if self.period_func == month_periods else 'periods'
period_func_kwargs[arg_name] = self.num_periods
if num_periods:
arg_name = 'nb_months' if period == month_periods else 'periods'
period_func_kwargs[arg_name] = num_periods

dates = convert_dates_to_utc(
self.period_func(**period_func_kwargs)
period(**period_func_kwargs)
)
_, customer_table, customer_extra, _ = \
aggregate_transactions_change_by_period(self.provider, account,
Expand Down Expand Up @@ -645,7 +662,7 @@ def paginate_queryset(self, queryset):
return self.decorate_queryset(page if page else queryset)


class PlanMetricAPIView(DateRangeContextMixin, ProviderMixin, GenericAPIView):
class PlanMetricAPIView(DateRangeContextMixin, ProviderMixin, APIView):
"""
Retrieves 12-month trailing plans performance
Expand Down Expand Up @@ -737,29 +754,34 @@ class PlanMetricAPIView(DateRangeContextMixin, ProviderMixin, GenericAPIView):
serializer_class = MetricsSerializer
filter_backends = (DateRangeFilter,)

@swagger_auto_schema(query_serializer=PeriodSerializer)
@swagger_auto_schema(query_serializer=QueryParamPeriodSerializer)
def get(self, request, *args, **kwargs):
# pylint:disable=unused-argument
query_serializer = QueryParamPeriodSerializer(data=request.query_params)
query_serializer.is_valid(raise_exception=True)

num_periods = query_serializer.validated_data.get('num_periods')
period = query_serializer.validated_data.get('period')
table = []

common_args = {
'from_date': self.ends_at,
'tz': self.timezone,
}
if self.num_periods:
arg_name = 'nb_months' if self.period_func == month_periods else 'num_periods'
common_args[arg_name] = self.num_periods
if num_periods:
arg_name = 'nb_months' if period == month_periods else 'num_periods'
common_args[arg_name] = num_periods

for plan in Plan.objects.filter(
organization=self.provider).order_by('title'):

# If we're using monthly periods, we use active_subscribers.

if self.period_func == month_periods:
if period == month_periods:
values = active_subscribers(plan, **common_args)
else:
# For other periods, we use active_subscribers_by_period
specific_args = {'period_func': self.period_func}
specific_args = {'period_func': period}
specific_args.update(common_args)
values = active_subscribers_by_period(plan, **specific_args)

Expand All @@ -773,12 +795,12 @@ def get(self, request, *args, **kwargs):
})

# Similar to above, but for churn metrics. Monthly periods use churn_subscriber.
if self.period_func == month_periods:
if period == month_periods:
extra_values = churn_subscribers(**common_args)
else:
# For other periods, add 'period_func' and get churn values using
# churn_subscribers_by_period.
specific_args = {'period_func': self.period_func}
specific_args = {'period_func': period}
specific_args.update(common_args)
extra_values = churn_subscribers_by_period(**specific_args)

Expand Down Expand Up @@ -806,7 +828,7 @@ class BalancesDueAPIView(BalancesDueMixin, ListAPIView):
.. code-block:: http
GET /api/profile/cowork/due_balances HTTP/1.1
GET /api/profile/cowork/balances-due HTTP/1.1
responds
Expand Down
19 changes: 13 additions & 6 deletions saas/api/plans.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
from ..mixins import PlanMixin, CartMixin
from ..filters import DateRangeFilter, OrderingFilter
from ..models import Coupon, Plan, Subscription
from .serializers import PlanDetailSerializer, PlanCreateSerializer
from .serializers import (PlanDetailSerializer, PlanCreateSerializer,
QueryParamActiveSerializer)
from ..utils import datetime_or_now


Expand Down Expand Up @@ -194,6 +195,10 @@ def get_serializer_class(self):
return PlanCreateSerializer
return super(PlanListCreateAPIView, self).get_serializer_class()

@swagger_auto_schema(query_serializer=QueryParamActiveSerializer)
def get(self, request, *args, **kwargs):
return super().get(self, request, *args, **kwargs)

@swagger_auto_schema(responses={
201: OpenAPIResponse("Create successful", PlanDetailSerializer)})
def post(self, request, *args, **kwargs):
Expand Down Expand Up @@ -237,11 +242,13 @@ def post(self, request, *args, **kwargs):

def get_queryset(self):
queryset = self.organization.plans.all()
is_active = self.request.query_params.get('active')
truth_values = ['true', '1']
if is_active:
value = is_active.lower() in truth_values
queryset = queryset.filter(is_active=value)
query_serializer = QueryParamActiveSerializer(data=self.request.query_params)

if query_serializer.is_valid(raise_exception=True):
is_active = query_serializer.validated_data.get('active', None)
if is_active is not None:
queryset = queryset.filter(is_active=is_active)

# `PlanDetailSerializer` will expand `organization`,
# `advance_discounts` and `use_charges`.
queryset = queryset.select_related('organization').prefetch_related(
Expand Down
Loading

0 comments on commit bcf8289

Please sign in to comment.