Skip to content

Commit

Permalink
Break down by delivery-type
Browse files Browse the repository at this point in the history
  • Loading branch information
sravfeyn committed Oct 5, 2024
1 parent 72bfc1a commit 86ba8b9
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 48 deletions.
24 changes: 24 additions & 0 deletions commcare_connect/reports/tables.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from django.urls import reverse
from django.utils.html import format_html
from django_tables2 import columns, tables


class AdminReportTable(tables.Table):
quarter = columns.Column(verbose_name="Quarter")
delivery_type = columns.Column(verbose_name="Delivery Type")
users = columns.Column(verbose_name="Active Users")
services = columns.Column(verbose_name="Verified Services")
approved_payments = columns.Column(verbose_name="Acknowledged Payments")
Expand All @@ -12,3 +15,24 @@ class AdminReportTable(tables.Table):
class Meta:
empty_text = "No data for this quarter."
orderable = False
row_attrs = {"id": lambda record: f"row{record['quarter'][0]}-{record['quarter'][1]}"}

def render_quarter(self, value):
return f"{value[0]} Q{value[1]}"

def render_delivery_type(self, record):
if record["delivery_type"] != "All":
return record["delivery_type"]
url = reverse("reports:delivery_stats_report")
return format_html(
"""<button type="button" class="btn btn-primary btn-sm"
hx-get='{url}?year={year}&quarter={quarter}&by_delivery_type=on&drilldown'
hx-target='#row{year}-{quarter}'
hx-swap="outerHTML"
hx-select="tbody tr">
View all types
</button>""",
url=url,
year=record["quarter"][0],
quarter=record["quarter"][1],
)
143 changes: 95 additions & 48 deletions commcare_connect/reports/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import django_filters
import django_tables2 as tables
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
Expand All @@ -10,6 +11,7 @@
from django.http import JsonResponse
from django.shortcuts import render
from django.urls import reverse
from django.utils.functional import cached_property
from django.views.decorators.http import require_GET
from django_filters.views import FilterView

Expand Down Expand Up @@ -41,22 +43,29 @@ def _get_quarters_since_start():
return quarters


def _get_table_data_for_quarter(quarter, delivery_type):
def _get_table_data_for_quarter(quarter, delivery_type, group_by_delivery_type=False):
if delivery_type:
delivery_type_filter = Q(opportunity_access__opportunity__delivery_type__slug=delivery_type)
else:
delivery_type_filter = Q()

quarter_start = date(quarter[0], (quarter[1] - 1) * 3 + 1, 1)
next_quarter = _increment(quarter)
quarter_end = date(next_quarter[0], (next_quarter[1] - 1) * 3 + 1, 1)
data = []

if group_by_delivery_type:
from collections import defaultdict

user_set = defaultdict(set)
beneficiary_set = defaultdict(set)
service_count = defaultdict(int)
else:
user_set = set()
beneficiary_set = set()
service_count = 0

user_set = set()
beneficiary_set = set()
service_count = 0
last_pk = 0
more = True

while more:
visit_data = (
CompletedWork.objects.annotate(work_date=Max("uservisit__visit_date"))
Expand All @@ -68,44 +77,77 @@ def _get_table_data_for_quarter(quarter, delivery_type):
work_date__lt=quarter_end,
id__gt=last_pk,
)
.select_related("opportunity_access")
.select_related("opportunity_access__opportunity__delivery_type")
).order_by("id")[:100]

if len(visit_data) < 100:
more = False
for v in visit_data:
user_set.add(v.opportunity_access.user_id)
beneficiary_set.add(v.entity_id)
service_count += v.approved_count
delivery_type_name = v.opportunity_access.opportunity.delivery_type.name
if group_by_delivery_type:
user_set[delivery_type_name].add(v.opportunity_access.user_id)
beneficiary_set[delivery_type_name].add(v.entity_id)
service_count[delivery_type_name] += v.approved_count
else:
user_set.add(v.opportunity_access.user_id)
beneficiary_set.add(v.entity_id)
service_count += v.approved_count

last_pk = v.id

approved_payment_amount = (
Payment.objects.filter(
delivery_type_filter,
opportunity_access__opportunity__is_test=False,
confirmed=True,
date_paid__gte=quarter_start,
date_paid__lt=quarter_end,
).aggregate(Sum("amount_usd"))
)["amount_usd__sum"]

total_payment_amount = (
Payment.objects.filter(
delivery_type_filter,
opportunity_access__opportunity__is_test=False,
date_paid__gte=quarter_start,
date_paid__lt=quarter_end,
).aggregate(Sum("amount_usd"))
)["amount_usd__sum"]

return {
"quarter": f"{quarter[0]} Q{quarter[1]}",
"users": len(user_set),
"services": service_count,
"approved_payments": approved_payment_amount,
"total_payments": total_payment_amount,
"beneficiaries": len(beneficiary_set),
}
payment_query = Payment.objects.filter(
delivery_type_filter,
opportunity_access__opportunity__is_test=False,
date_paid__gte=quarter_start,
date_paid__lt=quarter_end,
)

if group_by_delivery_type:
approved_payment_data = (
payment_query.filter(confirmed=True)
.values("opportunity_access__opportunity__delivery_type__name")
.annotate(approved_sum=Sum("amount_usd"))
)
total_payment_data = payment_query.values("opportunity_access__opportunity__delivery_type__name").annotate(
total_sum=Sum("amount_usd")
)

approved_payment_dict = {
item["opportunity_access__opportunity__delivery_type__name"]: item["approved_sum"]
for item in approved_payment_data
}
total_payment_dict = {
item["opportunity_access__opportunity__delivery_type__name"]: item["total_sum"]
for item in total_payment_data
}
for delivery_type_name in user_set.keys():
data.append(
{
"delivery_type": delivery_type_name,
"quarter": quarter,
"users": len(user_set[delivery_type_name]),
"services": service_count[delivery_type_name],
"approved_payments": approved_payment_dict.get(delivery_type_name, 0),
"total_payments": total_payment_dict.get(delivery_type_name, 0),
"beneficiaries": len(beneficiary_set[delivery_type_name]),
}
)
else:
approved_payment_amount = (
payment_query.filter(confirmed=True).aggregate(Sum("amount_usd"))["amount_usd__sum"] or 0
)
total_payment_amount = payment_query.aggregate(Sum("amount_usd"))["amount_usd__sum"] or 0
data.append(
{
"delivery_type": "All",
"quarter": quarter,
"users": len(user_set),
"services": service_count,
"approved_payments": approved_payment_amount,
"total_payments": total_payment_amount,
"beneficiaries": len(beneficiary_set),
}
)
return data


@login_required
Expand Down Expand Up @@ -178,9 +220,7 @@ class DeliveryReportFilters(django_filters.FilterSet):
choices=[(1, "Q1"), (2, "Q2"), (3, "Q3"), (4, "Q4")], label="Quarter", method="filter_by_ignore"
)
by_delivery_type = django_filters.BooleanFilter(
widget=forms.CheckboxInput(),
label='Break up by delivery type',
method='filter_by_ignore'
widget=forms.CheckboxInput(), label="Break up by delivery type", method="filter_by_ignore"
)

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -237,16 +277,23 @@ def get_context_data(self, *args, **kwargs):
context["report_url"] = reverse("reports:delivery_stats_report")
return context

@cached_property
def filter_values(self):
if not self.filterset.form.is_valid():
return None
else:
return self.filterset.form.cleaned_data

@property
def object_list(self):
table_data = []
if not self.filterset.form.is_valid():
if not self.filter_values:
return []

filter_values = self.filterset.form.cleaned_data
delivery_type = filter_values["delivery_type"]
year = int(filter_values["year"])
quarter = filter_values["quarter"]
delivery_type = self.filter_values["delivery_type"]
year = int(self.filter_values["year"])
quarter = self.filter_values["quarter"]
group_by_delivery_type = self.filter_values["by_delivery_type"]

if not year:
quarters = _get_quarters_since_start()
Expand All @@ -257,6 +304,6 @@ def object_list(self):
quarters = [(year, q) for q in range(1, 5)]

for q in quarters:
data = _get_table_data_for_quarter(q, delivery_type)
table_data.append(data)
data = _get_table_data_for_quarter(q, delivery_type, group_by_delivery_type)
table_data += data
return table_data

0 comments on commit 86ba8b9

Please sign in to comment.