From b58561375f62748ba371b9ba2cc3cc1e45c35231 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sun, 13 Oct 2019 15:39:45 +0200
Subject: [PATCH] dataviz: format durations in graphs (#36874)

---
 combo/apps/dataviz/models.py | 27 ++++++++++++++++++++++-
 tests/test_dataviz.py        | 42 +++++++++++++++++++++++++++++++++++-
 2 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/combo/apps/dataviz/models.py b/combo/apps/dataviz/models.py
index 4f8dcdc7..085edc0d 100644
--- a/combo/apps/dataviz/models.py
+++ b/combo/apps/dataviz/models.py
@@ -19,7 +19,8 @@ import os
 
 from django.core.urlresolvers import reverse
 from django.db import models
-from django.utils.translation import ugettext_lazy as _
+from django.utils.encoding import force_text
+from django.utils.translation import ugettext_lazy as _, ungettext
 from django.conf import settings
 
 from jsonfield import JSONField
@@ -259,4 +260,28 @@ class ChartNgCell(CellBase):
                 chart.add(label, value)
             chart.show_legend = True
 
+        if response.get('unit') == 'seconds':
+            def format_duration(value):
+                if value is None:
+                    return '-'
+                days = value // 86400
+                hours = (value % 86400) // 3600
+                if days:
+                    days_string = ungettext('%d day', '%d days', days) % days
+                if hours:
+                    hours_string = ungettext('%d hour', '%d hours', hours) % hours
+                if days and hours:
+                    value = _('%(days_string)s and %(hours_string)s') % {
+                            'days_string': days_string,
+                            'hours_string': hours_string,
+                    }
+                elif days:
+                    value = days_string
+                elif hours:
+                    value = hours_string
+                else:
+                    value = _('Less than an hour')
+                return force_text(value)
+            chart.config.value_formatter = format_duration
+
         return chart
diff --git a/tests/test_dataviz.py b/tests/test_dataviz.py
index 8fcb349d..79ce83bf 100644
--- a/tests/test_dataviz.py
+++ b/tests/test_dataviz.py
@@ -88,7 +88,12 @@ VISUALIZATION_JSON = [
         'name': 'seventh visualization (loop/X/Y)',
         'slug': 'seventh',
     },
-
+    {
+        'data-url': 'https://bijoe.example.com/visualization/8/json/',
+        'path': 'https://bijoe.example.com/visualization/8/iframe/?signature=123',
+        'name': 'eighth visualization (duration)',
+        'slug': 'eighth',
+    },
 ]
 
 
@@ -175,6 +180,16 @@ def bijoe_mock(url, request):
             }
         }
         return {'content': json.dumps(response), 'request': request, 'status_code': 200}
+    if url.path == '/visualization/8/json/':
+        response = {
+            'format': '1',
+            'data': [1000, 123000, 8600, 86400],
+            'axis': {
+                'y_labels': ['web', 'mail', 'email', 'fax']
+            },
+            'unit': 'seconds',
+        }
+        return {'content': json.dumps(response), 'request': request, 'status_code': 200}
 
 
 def test_chartng_cell(app):
@@ -273,6 +288,11 @@ def test_chartng_cell(app):
             with pytest.raises(UnsupportedDataSet):
                 chart = cell.get_chart()
 
+            # duration
+            cell.data_reference = 'plop:eighth'
+            cell.save()
+            chart = cell.get_chart()
+
 
 def test_chartng_cell_view(app, normal_user):
     page = Page(title='One', slug='index')
@@ -331,6 +351,25 @@ def test_chartng_cell_view(app, normal_user):
             resp = app.get('/api/dataviz/graph/1/?width=400', status=200)
             assert 'Unsupported dataset' in resp.text
 
+            # durations
+            cell.data_reference = 'plop:eighth'
+            cell.chart_type = 'table'
+            cell.save()
+            resp = app.get('/api/dataviz/graph/1/')  # get data in cache
+            resp = app.get('/')
+            assert '<td>Less than an hour</td>' in resp.text
+            assert '<td>1 day and 10 hours</td>' in resp.text
+            assert '<td>2 hours</td>' in resp.text
+            assert '<td>1 day</td>' in resp.text
+
+            cell.chart_type = 'bar'
+            cell.save()
+            resp = app.get('/api/dataviz/graph/1/?width=400', status=200)
+            assert '>Less than an hour<' in resp.text
+            assert '>1 day and 10 hours<' in resp.text
+            assert '>2 hours<' in resp.text
+            assert '>1 day<' in resp.text
+
 
 def test_chartng_cell_manager(app, admin_user):
     page = Page(title='One', slug='index')
@@ -347,6 +386,7 @@ def test_chartng_cell_manager(app, admin_user):
             cell.save()
             resp = app.get('/manage/pages/%s/' % page.id)
             assert resp.form['cdataviz_chartngcell-%s-data_reference' % cell.id].options == [
+                (u'plop:eighth', False, u'eighth visualization (duration)'),
                 (u'plop:example', True, u'example visualization (X)'),
                 (u'plop:fifth', False, u'fifth visualization (loop/X)'),
                 (u'plop:fourth', False, u'fourth visualization (no axis)'),
-- 
2.23.0

