Skip to content

Commit

Permalink
uses db router for lifetime and balances-due
Browse files Browse the repository at this point in the history
  • Loading branch information
smirolo committed Apr 25, 2024
1 parent ec9caba commit c5cec0e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 91 deletions.
20 changes: 16 additions & 4 deletions saas/api/metrics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2023, DjaoDjin inc.
# Copyright (c) 2024, DjaoDjin inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -32,7 +32,7 @@
from .. import settings, humanize
from ..compat import gettext_lazy as _, reverse, six
from ..docs import extend_schema
from ..filters import DateRangeFilter
from ..filters import DateRangeFilter, OrderingFilter, SearchFilter
from ..metrics.base import (abs_balances_by_period,
aggregate_transactions_by_period, aggregate_transactions_change_by_period,
generate_periods, get_different_units)
Expand Down Expand Up @@ -652,7 +652,19 @@ class LifetimeValueMetricMixin(DateRangeContextMixin, ProviderMixin):
"""
Decorates profiles with subscriber age and lifetime value
"""
filter_backends = (DateRangeFilter,) # XXX includes `DateRangeContextMixin` and `ProviderMixin`
search_fields = (
'slug',
'full_name',
)

ordering_fields = (
('slug', 'slug'),
('full_name', 'full_name'),
('created_at', 'created_at')
)
ordering = ('-created_at',)

filter_backends = (DateRangeFilter, SearchFilter, OrderingFilter)

def get_queryset(self):
organization_model = get_organization_model()
Expand All @@ -663,7 +675,7 @@ def get_queryset(self):
queryset = organization_model.objects.all()
queryset = queryset.filter(
outgoing__orig_account=Transaction.PAYABLE).distinct()
return queryset.order_by('full_name')
return queryset

def decorate_queryset(self, queryset):
decorated_queryset = list(queryset)
Expand Down
133 changes: 53 additions & 80 deletions saas/metrics/transactions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020, DjaoDjin inc.
# Copyright (c) 2024, DjaoDjin inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
Expand All @@ -24,8 +24,9 @@

import logging

from django.db import connection, router
from django.db import router
from django.db.models import F, Sum
from django.db.models.sql.query import RawQuery

from .. import settings
from ..compat import six
Expand Down Expand Up @@ -88,22 +89,20 @@ def lifetime_value(provider=None):
'liability': Transaction.LIABILITY
}
# XXX transfers: without processor fee, payments: with processor fee.
with connection.cursor() as cursor:
cursor.execute(payments_query, params=None)
for row in cursor.fetchall():
organization_slug = row[0]
unit = row[1]
amount = row[2]
account = Transaction.LIABILITY
if organization_slug not in by_profiles:
by_profiles[organization_slug] = {unit: {account: amount}}
for row in RawQuery(payments_query, using=router.db_for_read(Transaction)):
organization_slug = row[0]
unit = row[1]
amount = row[2]
account = Transaction.LIABILITY
if organization_slug not in by_profiles:
by_profiles[organization_slug] = {unit: {account: amount}}
else:
if unit not in by_profiles[organization_slug]:
by_profiles[organization_slug].update({
unit: {account: amount}})
else:
if unit not in by_profiles[organization_slug]:
by_profiles[organization_slug].update({
unit: {account: amount}})
else:
by_profiles[organization_slug][unit].update({
account: amount})
by_profiles[organization_slug][unit].update({
account: amount})

kwargs = {'dest_organization': provider} if provider else {}
refunds = Transaction.objects.filter(
Expand Down Expand Up @@ -152,22 +151,21 @@ def lifetime_value(provider=None):
'extract_number': extract_number,
'provider_clause': ("AND saas_plan.organization_id = %d" % provider.pk
if provider else "")}
with connection.cursor() as cursor:
cursor.execute(deferred_revenues_query, params=None)
for row in cursor.fetchall():
organization_slug = row[0]
unit = row[1]
amount = row[2]
account = Transaction.BACKLOG
if organization_slug not in by_profiles:
by_profiles[organization_slug] = {unit: {account: amount}}
for row in RawQuery(deferred_revenues_query,
using=router.db_for_read(Transaction)):
organization_slug = row[0]
unit = row[1]
amount = row[2]
account = Transaction.BACKLOG
if organization_slug not in by_profiles:
by_profiles[organization_slug] = {unit: {account: amount}}
else:
if unit not in by_profiles[organization_slug]:
by_profiles[organization_slug].update({
unit: {account: amount}})
else:
if unit not in by_profiles[organization_slug]:
by_profiles[organization_slug].update({
unit: {account: amount}})
else:
by_profiles[organization_slug][unit].update({
account: amount})
by_profiles[organization_slug][unit].update({
account: amount})

results = {}
for slug, by_units in six.iteritems(by_profiles):
Expand Down Expand Up @@ -250,63 +248,38 @@ def get_balances_due(provider=None):
'liability': Transaction.LIABILITY
}

with connection.cursor() as cursor:
cursor.execute(payments_query, params=None)
for row in cursor.fetchall():
organization_slug = row[0]
unit = row[1]
amount = row[2]
account = Transaction.LIABILITY
if organization_slug not in by_profiles:
by_profiles[organization_slug] = {unit: {account: amount}}
else:
if unit not in by_profiles[organization_slug]:
by_profiles[organization_slug].update({
unit: {account: amount}})
else:
by_profiles[organization_slug][unit].update({
account: amount})

kwargs = {'dest_organization': provider} if provider else {}
refunds = Transaction.objects.filter(
dest_account=Transaction.REFUND,
orig_account=Transaction.REFUNDED, **kwargs).values(
slug=F('orig_organization__slug'), unit=F('orig_unit')).annotate(
amount=Sum('orig_amount')).order_by(
'orig_organization__slug')

for val in refunds:
organization_slug = val['slug']
unit = val['unit']
amount = val['amount']
account = Transaction.REFUNDED
for row in RawQuery(payments_query, using=router.db_for_read(Transaction)):
organization_slug = row[0]
unit = row[1]
amount = row[2]
account = Transaction.LIABILITY
if organization_slug not in by_profiles:
by_profiles[organization_slug] = {unit: {account: amount}}
else:
if unit not in by_profiles[organization_slug]:
by_profiles[organization_slug].update({unit: {account: amount}})
by_profiles[organization_slug].update({
unit: {account: amount}})
else:
by_profiles[organization_slug][unit].update({account: amount})
by_profiles[organization_slug][unit].update({
account: amount})

results = {}
for slug, by_units in six.iteritems(by_profiles):
for unit, val in six.iteritems(by_units):
contract_value = val.get('contract_value', 0)
payments = (val.get(Transaction.LIABILITY, 0)
- val.get(Transaction.REFUNDED, 0))
balance = contract_value - payments if contract_value > payments else 0
if balance < 1:
continue
val.update({
'contract_value': contract_value,
'cash_payments': payments,
'balance': balance
})
if slug not in results:
results[slug] = {unit: val}
else:
if unit not in results[slug]:
results[slug].update({unit: val})
payments = val.get(Transaction.LIABILITY, 0)
balance = contract_value - payments
if balance:
val.update({
'contract_value': contract_value,
'cash_payments': payments,
'balance': balance
})
if slug not in results:
results[slug] = {unit: val}
else:
results[slug][unit].update(val)
if unit not in results[slug]:
results[slug].update({unit: val})
else:
results[slug][unit].update(val)
return results
10 changes: 5 additions & 5 deletions saas/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,11 +1234,11 @@ class BalancesDueMixin(DateRangeContextMixin, ProviderMixin):
)

ordering_fields = (
('slug', 'Slug'),
('full_name', 'Full Name'),
('created_at', 'Created At')
('slug', 'slug'),
('full_name', 'full_name'),
('created_at', 'created_at')
)
ordering = ('slug',)
ordering = ('-created_at',)

filter_backends = (DateRangeFilter, SearchFilter, OrderingFilter)

Expand All @@ -1258,7 +1258,7 @@ def get_queryset(self):
queryset = queryset.filter(
outgoing__orig_account=Transaction.PAYABLE,
slug__in=self.balances_due.keys()).distinct()
return queryset.order_by('full_name')
return queryset

def decorate_queryset(self, queryset):
decorated_queryset = list(queryset)
Expand Down
2 changes: 1 addition & 1 deletion saas/templates/saas/metrics/balances_due.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<template v-for="(item, index) in items.results">
<tr v-for="(balance, unit) in item.balances" :key="index + '-' + unit" v-show="itemsLoaded && items.results.length > 0">
<td>
<a :href="'{{urls.profile_redirect}}' + item.slug + '/'">[[item.printable_name]]</a>
<a :href="'{{urls.profile_base}}' + item.slug + '/'">[[item.printable_name]]</a>
</td>
<td>[[unit]]</td>
<td>[[balance.contract_value]]</td>
Expand Down
3 changes: 2 additions & 1 deletion saas/views/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ def get_context_data(self, **kwargs):
update_context_urls(context, {
'provider': {
'api_metrics_balances_due': reverse(
'saas_api_metrics_balances_due', args=(self.provider,))}
'saas_api_metrics_balances_due', args=(self.provider,))},
'profile_base': reverse('saas_profile'),
})
return context

0 comments on commit c5cec0e

Please sign in to comment.