diff --git a/opentech/apply/funds/tables.py b/opentech/apply/funds/tables.py index 1a83d5a405a621bcf8f832633df2ce6664717adc..7e463f84fdad19f3591951b3c740e3481cb50c4c 100644 --- a/opentech/apply/funds/tables.py +++ b/opentech/apply/funds/tables.py @@ -3,8 +3,7 @@ import textwrap from django import forms from django.contrib.auth import get_user_model -from django.db.models import CharField, F, Func, OuterRef, Q, Subquery -from django.db.models.functions import Length +from django.db.models import F, Q from django.utils.html import format_html from django.utils.text import mark_safe, slugify @@ -14,7 +13,7 @@ from django_tables2.utils import A from wagtail.core.models import Page -from opentech.apply.funds.models import ApplicationBase, ApplicationSubmission, Round +from opentech.apply.funds.models import ApplicationSubmission, Round from opentech.apply.funds.workflow import STATUSES from opentech.apply.users.groups import STAFF_GROUP_NAME from .widgets import Select2MultiCheckboxesWidget @@ -181,40 +180,12 @@ class RoundsTable(tables.Table): return qs.order_by(self._field_order('end_date', desc)), True def order_fund(self, qs, desc): - funds = ApplicationBase.objects.filter(path=OuterRef('parent_path')) - qs = qs.annotate( - parent_path=Left(F('path'), Length('path') - ApplicationBase.steplen, output_field=CharField()), - fund=Subquery(funds.values('title')[:1]), - ) return qs.order_by(self._field_order('fund', desc)), True def order_progress(self, qs, desc): return qs.order_by(self._field_order('progress', desc)), True -# TODO remove in django 2.1 where this is fixed -F.relabeled_clone = lambda self, relabels: self - - -# TODO remove in django 2.1 where this is added -class Left(Func): - function = 'LEFT' - arity = 2 - - def __init__(self, expression, length, **extra): - """ - expression: the name of a field, or an expression returning a string - length: the number of characters to return from the start of the string - """ - if not hasattr(length, 'resolve_expression'): - if length < 1: - raise ValueError("'length' must be greater than 0.") - super().__init__(expression, length, **extra) - - def get_substr(self): - return Substr(self.source_expressions[0], Value(1), self.source_expressions[1]) - - class ActiveRoundFilter(Select2MultipleChoiceFilter): def __init__(self, *args, **kwargs): super().__init__(self, *args, choices=[('active', 'Active'), ('inactive', 'Inactive')], **kwargs) @@ -248,6 +219,7 @@ class OpenRoundFilter(Select2MultipleChoiceFilter): class RoundsFilter(filters.FilterSet): + fund = Select2ModelMultipleChoiceFilter(queryset=get_used_funds, label='Funds') lead = Select2ModelMultipleChoiceFilter(queryset=get_round_leads, label='Leads') active = ActiveRoundFilter(label='Active') open_rouds = OpenRoundFilter(label='Open') diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py index 43bf67155d0ee21c453a733200d476054e97d398..1bc6e5e49dc40098efbed59b4a07607ac9d0736e 100644 --- a/opentech/apply/funds/views.py +++ b/opentech/apply/funds/views.py @@ -3,8 +3,20 @@ from copy import copy from django.contrib.auth.decorators import login_required from django.contrib import messages from django.core.exceptions import PermissionDenied -from django.db.models import Count, FloatField, IntegerField, F, OuterRef, Subquery, Q, When, Case -from django.db.models.functions import Coalesce +from django.db.models import ( + Case, + CharField, + Count, + F, + FloatField, + Func, + IntegerField, + OuterRef, + Q, + Subquery, + When, +) +from django.db.models.functions import Coalesce, Length from django.http import HttpResponseRedirect, Http404 from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy @@ -32,7 +44,7 @@ from opentech.apply.utils.views import DelegateableView, ViewDispatcher from .differ import compare from .forms import ProgressSubmissionForm, ScreeningSubmissionForm, UpdateReviewersForm, UpdateSubmissionLeadForm -from .models import ApplicationSubmission, ApplicationRevision, RoundBase, LabBase +from .models import ApplicationBase, ApplicationSubmission, ApplicationRevision, RoundBase, LabBase from .models.utils import SubmittableStreamForm from .tables import ( AdminSubmissionsTable, @@ -480,6 +492,7 @@ class RoundListView(SingleTableMixin, FilterView): def get_queryset(self): submissions = ApplicationSubmission.objects.filter(Q(round=OuterRef('pk')) | Q(page=OuterRef('pk'))).current() + funds = ApplicationBase.objects.filter(path=OuterRef('parent_path')) closed_submissions = submissions.inactive() queryset = Page.objects.type(SubmittableStreamForm).annotate( @@ -503,6 +516,8 @@ class RoundListView(SingleTableMixin, FilterView): ), start_date=F('roundbase__start_date'), end_date=F('roundbase__end_date'), + parent_path=Left(F('path'), Length('path') - ApplicationBase.steplen, output_field=CharField()), + fund=Subquery(funds.values('title')[:1]), ).annotate( progress=Case( When(total_submissions=0, then=None), @@ -513,3 +528,26 @@ class RoundListView(SingleTableMixin, FilterView): ) return queryset + + +# TODO remove in django 2.1 where this is fixed +F.relabeled_clone = lambda self, relabels: self + + +# TODO remove in django 2.1 where this is added +class Left(Func): + function = 'LEFT' + arity = 2 + + def __init__(self, expression, length, **extra): + """ + expression: the name of a field, or an expression returning a string + length: the number of characters to return from the start of the string + """ + if not hasattr(length, 'resolve_expression'): + if length < 1: + raise ValueError("'length' must be greater than 0.") + super().__init__(expression, length, **extra) + + def get_substr(self): + return Substr(self.source_expressions[0], Value(1), self.source_expressions[1])