django-orchestra/orchestra/contrib/orders/filters.py

132 lines
4.8 KiB
Python

from datetime import timedelta
from django.apps import apps
from django.contrib.admin import SimpleListFilter
from django.db.models import Q, Prefetch, F
from django.utils import timezone
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from . import settings
from .models import MetricStorage
class ActiveOrderListFilter(SimpleListFilter):
""" Filter tickets by created_by according to request.user """
title = _("is active")
parameter_name = 'is_active'
def lookups(self, request, model_admin):
return (
('True', _("Active")),
('False', _("Inactive")),
)
def queryset(self, request, queryset):
if self.value() == 'True':
return queryset.active()
elif self.value() == 'False':
return queryset.inactive()
return queryset
class BilledOrderListFilter(SimpleListFilter):
""" Filter tickets by created_by according to request.user """
title = _("billed")
parameter_name = 'billed'
# apply_last = True
def lookups(self, request, model_admin):
return (
('yes', _("Billed")),
('no', _("Not billed")),
('pending', _("Pending (re-evaluate metric)")),
('not_pending', _("Not pending (re-evaluate metric)")),
)
def get_pending_metric_pks(self, queryset):
mindelta = timedelta(days=2) # TODO
metric_pks = []
prefetch_valid_metrics = Prefetch('metrics', to_attr='valid_metrics',
queryset=MetricStorage.objects.filter(created_on__gt=F('order__billed_on'),
created_on__lte=(F('updated_on')-mindelta)).exclude(value=0)
)
metric_queryset = queryset.exclude(service__metric='').exclude(billed_on__isnull=True)
for order in metric_queryset.prefetch_related(prefetch_valid_metrics):
for metric in order.valid_metrics:
if metric.created_on <= order.billed_on:
raise ValueError("This value should already be filtered on the prefetch query.")
if metric.value > order.billed_metric:
metric_pks.append(order.pk)
break
return metric_pks
def filter_pending(self, queryset, reverse=False):
now = timezone.now()
Service = apps.get_model(settings.ORDERS_SERVICE_MODEL)
ignore_qs = Q()
for order in queryset.distinct('service_id').only('service'):
service = order.service
delta = service.handler.get_ignore_delta()
if delta is not None:
ignore_qs = ignore_qs | Q(service_id=service.id, registered_on__gt=now-delta)
ignore_qs = queryset.exclude(ignore_qs)
pending_qs = Q(
Q(pk__in=self.get_pending_metric_pks(ignore_qs)) |
Q(billed_until__isnull=True) | Q(~Q(service__billing_period=Service.NEVER) &
Q(billed_until__lte=now))
)
if reverse:
return queryset.exclude(pending_qs)
else:
return ignore_qs.filter(pending_qs)
def queryset(self, request, queryset):
now = timezone.now()
if self.value() == 'yes':
return queryset.filter(billed_until__isnull=False, billed_until__gte=now)
elif self.value() == 'no':
return queryset.exclude(billed_until__isnull=False, billed_until__gte=now)
elif self.value() == 'pending':
return self.filter_pending(queryset)
elif self.value() == 'not_pending':
return self.filter_pending(queryset, reverse=True)
return queryset
class IgnoreOrderListFilter(SimpleListFilter):
""" Filter Nodes by group according to request.user """
title = _("Ignore")
parameter_name = 'ignore'
def lookups(self, request, model_admin):
return (
('0', _("Not ignored")),
('1', _("Ignored")),
('2', _("All")),
)
def queryset(self, request, queryset):
if self.value() == '0':
return queryset.filter(ignore=False)
elif self.value() == '1':
return queryset.filter(ignore=True)
return queryset
def choices(self, cl):
""" Enable default selection different than All """
for lookup, title in self.lookup_choices:
title = title._proxy____args[0]
selected = self.value() == force_str(lookup)
if not selected and title == "Not ignored" and self.value() is None:
selected = True
# end of workaround
yield {
'selected': selected,
'query_string': cl.get_query_string({
self.parameter_name: lookup,
}, []),
'display': title,
}