From 6a8a48686b871b0bd6ee68d0f162e726ff1b551c Mon Sep 17 00:00:00 2001
From: Todd Dembrey <todd.dembrey@torchbox.com>
Date: Tue, 12 Nov 2019 17:19:50 +0000
Subject: [PATCH] Add ability to filter and order reports on project table

---
 hypha/apply/projects/filters.py | 25 +++++++++++++++++++++++++
 hypha/apply/projects/models.py  | 30 ++++++++++++++++++++++++++----
 hypha/apply/projects/tables.py  |  7 +++++++
 3 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/hypha/apply/projects/filters.py b/hypha/apply/projects/filters.py
index 4917e4c27..a817fdf4d 100644
--- a/hypha/apply/projects/filters.py
+++ b/hypha/apply/projects/filters.py
@@ -1,6 +1,8 @@
 import django_filters as filters
 from django import forms
 from django.contrib.auth import get_user_model
+from django.db.models import Q
+from django_select2.forms import Select2Widget
 
 from hypha.apply.funds.tables import (
     Select2ModelMultipleChoiceFilter,
@@ -9,6 +11,8 @@ from hypha.apply.funds.tables import (
 )
 
 from .models import (
+    CLOSING,
+    IN_PROGRESS,
     PROJECT_STATUS_CHOICES,
     REQUEST_STATUS_CHOICES,
     PaymentRequest,
@@ -34,15 +38,36 @@ class PaymentRequestListFilter(filters.FilterSet):
 
 
 class ProjectListFilter(filters.FilterSet):
+    REPORTING_CHOICES = (
+        (0, 'Up to date'),
+        (1, 'Behind schedule'),
+    )
+
     fund = Select2ModelMultipleChoiceFilter(label='Funds', queryset=get_used_funds)
     lead = Select2ModelMultipleChoiceFilter(label='Lead', queryset=get_project_leads)
     status = Select2MultipleChoiceFilter(label='Status', choices=PROJECT_STATUS_CHOICES)
     query = filters.CharFilter(field_name='title', lookup_expr="icontains", widget=forms.HiddenInput)
+    reporting = filters.ChoiceFilter(
+        choices=REPORTING_CHOICES,
+        method="filter_reporting",
+        widget=Select2Widget(attrs={
+            'data-placeholder': 'Reporting',
+            'data-minimum-results-for-search': -1,
+        }),
+    )
 
     class Meta:
         fields = ['status', 'lead', 'fund']
         model = Project
 
+    def filter_reporting(self, queryset, name, value):
+        if value == '1':
+            return queryset.filter(outstanding_reports__gt=0)
+        return queryset.filter(
+            Q(outstanding_reports__lt=1) | Q(outstanding_reports__isnull=True),
+            status__in=(IN_PROGRESS, CLOSING),
+        )
+
 
 class DateRangeInputWidget(filters.widgets.SuffixedMultiWidget):
     template_name = 'application_projects/filters/widgets/date_range_input_widget.html'
diff --git a/hypha/apply/projects/models.py b/hypha/apply/projects/models.py
index 66d02a763..060a684a1 100644
--- a/hypha/apply/projects/models.py
+++ b/hypha/apply/projects/models.py
@@ -13,9 +13,19 @@ from django.contrib.postgres.fields import JSONField
 from django.core.exceptions import ValidationError
 from django.core.validators import MinValueValidator
 from django.db import models
-from django.db.models import Case, ExpressionWrapper, F, Max, OuterRef, Q, Subquery, Sum
-from django.db.models import Value as V
-from django.db.models import When
+from django.db.models import (
+    Count,
+    Case,
+    F,
+    ExpressionWrapper,
+    Max,
+    OuterRef,
+    Q,
+    Subquery,
+    Sum,
+    Value as V,
+    When,
+)
 from django.db.models.functions import Cast, Coalesce
 from django.db.models.signals import post_delete
 from django.dispatch.dispatcher import receiver
@@ -307,6 +317,18 @@ class ProjectQuerySet(models.QuerySet):
             last_payment_request=Max('payment_requests__requested_at'),
         )
 
+    def with_outstanding_reports(self):
+        return self.annotate(
+            outstanding_reports=Subquery(
+                Report.objects.filter(
+                    project=OuterRef('pk'),
+                ).to_do().order_by().values('project').annotate(
+                    count=Count('pk'),
+                ).values('count'),
+                output_field=models.IntegerField(),
+            )
+        )
+
     def with_start_date(self):
         return self.annotate(
             start=Cast(
@@ -322,7 +344,7 @@ class ProjectQuerySet(models.QuerySet):
         )
 
     def for_table(self):
-        return self.with_amount_paid().with_last_payment().select_related(
+        return self.with_amount_paid().with_last_payment().with_outstanding_reports().select_related(
             'report_config',
             'submission__page',
             'lead',
diff --git a/hypha/apply/projects/tables.py b/hypha/apply/projects/tables.py
index 2c86e162e..82219029c 100644
--- a/hypha/apply/projects/tables.py
+++ b/hypha/apply/projects/tables.py
@@ -77,6 +77,13 @@ class BaseProjectsTable(tables.Table):
     end_date = tables.DateColumn(verbose_name='End Date', accessor='proposed_end')
     fund_allocation = tables.Column(verbose_name='Fund Allocation', accessor='value')
 
+    def order_reporting(self, qs, is_descending):
+        direction = '-' if is_descending else ''
+
+        qs = qs.order_by(f'{direction}outstanding_reports')
+
+        return qs, True
+
     def render_fund_allocation(self, record):
         return f'${intcomma(record.amount_paid)} / ${intcomma(record.value)}'
 
-- 
GitLab