From 12e4845083e82a33242e5b194dc67eb08d684f75 Mon Sep 17 00:00:00 2001 From: Daan van der Kallen Date: Mon, 10 Jun 2024 19:11:34 +0200 Subject: [PATCH] Add min_value option to stats --- binder/views.py | 52 ++++++++++++++++++++++++++++++++------------- tests/test_stats.py | 13 ++++++++++++ 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/binder/views.py b/binder/views.py index f7243bfd..afd4b14e 100644 --- a/binder/views.py +++ b/binder/views.py @@ -42,8 +42,8 @@ # annotations: a list of annotation names that have to be applied to the queryset for the expr to work (optional), Stat = namedtuple( 'Stat', - ['expr', 'filters', 'group_by', 'annotations'], - defaults=[{}, None, []], + ['expr', 'filters', 'group_by', 'annotations', 'min_value'], + defaults=[{}, None, [], None], ) @@ -3008,20 +3008,42 @@ def _get_stat(self, request, queryset, stat, annotations, include_annotations): } group_by = stat.group_by.replace('.', '__') + + value = { + # The jsonloads/jsondumps is to make sure we can handle different + # types as keys, an example is dates. + jsonloads(jsondumps(key)): value + for key, value in ( + queryset + .order_by() + .exclude(**{group_by: None}) + .values(group_by) + .annotate(_binder_stat=stat.expr) + .values_list(group_by, '_binder_stat') + ) + } + + other = 0 + if stat.min_value is not None: + min_value = stat.min_value * sum(value.values()) + new_value = {} + + others = 0 + for key, sub_value in value.items(): + if sub_value >= min_value: + new_value[key] = sub_value + else: + other += sub_value + others += 1 + + if others > 1: + value = new_value + else: + other = 0 + return { - 'value': { - # The jsonloads/jsondumps is to make sure we can handle different - # types as keys, an example is dates. - jsonloads(jsondumps(key)): value - for key, value in ( - queryset - .order_by() - .exclude(**{group_by: None}) - .values(group_by) - .annotate(_binder_stat=stat.expr) - .values_list(group_by, '_binder_stat') - ) - }, + 'value': value, + 'other': other, 'group_by': stat.group_by, 'filters': stat.filters, } diff --git a/tests/test_stats.py b/tests/test_stats.py index d048bcaa..4cd30e64 100644 --- a/tests/test_stats.py +++ b/tests/test_stats.py @@ -48,6 +48,7 @@ def test_animals_by_zoo(self): self.assertEqual(res, { 'by_zoo': { 'value': {'Zoo 1': 1, 'Zoo 2': 2}, + 'other': 0, 'filters': {}, 'group_by': 'zoo.name', }, @@ -71,6 +72,7 @@ def test_stats_filtered(self): }, 'by_zoo': { 'value': {'Zoo 1': 1}, + 'other': 0, 'filters': {}, 'group_by': 'zoo.name', }, @@ -83,3 +85,14 @@ def test_stat_not_found(self): 'message': 'unknown stat: does_not_exist', 'debug': ANY(), }) + + def test_animals_by_zoo(self): + res = self.get_stats('by_zoo') + self.assertEqual(res, { + 'by_zoo': { + 'value': {'Zoo 1': 1, 'Zoo 2': 2}, + 'other': 0, + 'filters': {}, + 'group_by': 'zoo.name', + }, + })