Projet

Général

Profil

0001-dataviz-split-get_chart-into-several-methods-48865.patch

Valentin Deniaud, 07 décembre 2020 16:40

Télécharger (7,55 ko)

Voir les différences:

Subject: [PATCH 1/3] dataviz: split get_chart into several methods (#48865)

 combo/apps/dataviz/models.py | 115 +++++++++++++++++++++--------------
 1 file changed, 70 insertions(+), 45 deletions(-)
combo/apps/dataviz/models.py
229 229
            'table': pygal.Bar,
230 230
            }[self.chart_type](config=pygal.Config(style=copy.copy(style)))
231 231

  
232
        x_labels, y_labels, data = self.parse_response(response, chart)
233
        chart.x_labels = x_labels
234
        self.prepare_chart(chart, width, height)
235

  
236
        if chart.axis_count == 1:
237
            if self.hide_null_values:
238
                data = self.hide_values(chart, data)
239
            if self.sort_order != 'none':
240
                data = self.sort_values(chart, data)
241
            if chart.compute_sum and self.chart_type == 'table':
242
                data = self.add_total_to_line_table(chart, data)
243
        self.add_data_to_chart(chart, data, y_labels)
244

  
245
        return chart
246

  
247
    def parse_response(self, response, chart):
232 248
        # normalize axis to have a fake axis when there are no dimensions and
233 249
        # always a x axis when there is a single dimension.
234 250
        data = response['data']
......
261 277
        else:
262 278
            chart.axis_count = 2
263 279

  
264
        # hide/sort values
265
        if chart.axis_count == 1 and (self.sort_order != 'none' or self.hide_null_values):
266
            if self.sort_order == 'alpha':
267
                tmp_items = sorted(zip(x_labels, data), key=lambda x: x[0])
268
            elif self.sort_order == 'asc':
269
                tmp_items = sorted(zip(x_labels, data), key=lambda x: (x[1] or 0))
270
            elif self.sort_order == 'desc':
271
                tmp_items = sorted(zip(x_labels, data), key=lambda x: (x[1] or 0), reverse=True)
272
            else:
273
                tmp_items = zip(x_labels, data)
274
            tmp_x_labels = []
275
            tmp_data = []
276
            for label, value in tmp_items:
277
                if self.hide_null_values and not value:
278
                    continue
279
                tmp_x_labels.append(label)
280
                tmp_data.append(value)
281
            x_labels = tmp_x_labels
282
            data = tmp_data
280
        chart.show_legend = bool(len(response['axis']) > 1)
281
        chart.compute_sum = bool(response.get('measure') == 'integer' and chart.axis_count > 0)
283 282

  
283
        formatter = self.get_value_formatter(response.get('unit'), response.get('measure'))
284
        if formatter:
285
            chart.config.value_formatter = formatter
286

  
287
        return x_labels, y_labels, data
288

  
289
    def prepare_chart(self, chart, width, height):
284 290
        chart.config.margin = 0
285 291
        if width:
286 292
            chart.config.width = width
......
289 295
        if width or height:
290 296
            chart.config.explicit_size = True
291 297
        chart.config.js = [os.path.join(settings.STATIC_URL, 'js/pygal-tooltips.js')]
292
        chart.x_labels = x_labels
293 298

  
294
        chart.show_legend = bool(len(response['axis']) > 1)
295 299
        chart.truncate_legend = 30
296 300
        # matplotlib tab10 palette
297 301
        chart.config.style.colors = (
......
302 306
        if self.chart_type == 'dot':
303 307
            chart.show_legend = False
304 308
            # use a single colour for dots
305
            chart.config.style.colors = ('#1f77b4',) * len(x_labels)
309
            chart.config.style.colors = ('#1f77b4',) * len(chart.x_labels)
306 310

  
307
        chart.compute_sum = bool(response.get('measure') == 'integer')
308
        if chart.compute_sum and self.chart_type == 'table':
309
            if chart.axis_count < 2:  # workaround pygal
310
                chart.compute_sum = False
311
                if chart.axis_count == 1:
312
                    data.append(sum(data))
313
                    x_labels.append(gettext('Total'))
311
        if self.chart_type != 'pie':
312
            if width and width < 500:
313
                chart.legend_at_bottom = True
314
                if self.chart_type == 'horizontal-bar':
315
                    # truncate labels
316
                    chart.x_labels = [pygal.util.truncate(x, 15) for x in chart.x_labels]
317
        else:
318
            chart.show_legend = True
319
            if width and width < 500:
320
                chart.truncate_legend = 15
314 321

  
322
    @staticmethod
323
    def hide_values(chart, data):
324
        x_labels, new_data = zip(*[(label, value) for label, value in zip(chart.x_labels, data) if value])
325
        chart.x_labels = list(x_labels)
326
        return list(new_data)
327

  
328
    def sort_values(self, chart, data):
329
        if self.sort_order == 'alpha':
330
            tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: x[0])
331
        elif self.sort_order == 'asc':
332
            tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: (x[1] or 0))
333
        elif self.sort_order == 'desc':
334
            tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: (x[1] or 0), reverse=True)
335
        x_labels, sorted_data = zip(*[(label, value) for label, value in tmp_items])
336
        chart.x_labels = list(x_labels)
337
        return list(sorted_data)
338

  
339
    @staticmethod
340
    def add_total_to_line_table(chart, data):
341
        # workaround pygal
342
        chart.compute_sum = False
343
        data.append(sum(data))
344
        chart.x_labels.append(gettext('Total'))
345
        return data
346

  
347
    def add_data_to_chart(self, chart, data, y_labels):
315 348
        if self.chart_type != 'pie':
316 349
            for i, serie_label in enumerate(y_labels):
317 350
                if chart.axis_count < 2:
318 351
                    values = data
319 352
                else:
320
                    values = [data[i][j] for j in range(len(x_labels))]
353
                    values = [data[i][j] for j in range(len(chart.x_labels))]
321 354
                chart.add(serie_label, values)
322
            if width and width < 500:
323
                chart.legend_at_bottom = True
324
                if self.chart_type == 'horizontal-bar':
325
                    # truncate labels
326
                    chart.x_labels = [pygal.util.truncate(x, 15) for x in chart.x_labels]
327 355
        else:
328 356
            # pie, create a serie by data, to get different colours
329 357
            values = data
330
            for label, value in zip(x_labels, values):
358
            for label, value in zip(chart.x_labels, values):
331 359
                if not value:
332 360
                    continue
333 361
                chart.add(label, value)
334
            chart.show_legend = True
335
            if width and width < 500:
336
                chart.truncate_legend = 15
337 362

  
338
        if response.get('unit') == 'seconds' or response.get('measure') == 'duration':
363
    @staticmethod
364
    def get_value_formatter(unit, measure):
365
        if unit == 'seconds' or measure == 'duration':
339 366
            def format_duration(value):
340 367
                if value is None:
341 368
                    return '-'
......
357 384
                else:
358 385
                    value = _('Less than an hour')
359 386
                return force_text(value)
360
            chart.config.value_formatter = format_duration
361
        elif response.get('measure') == 'percent':
387
            return format_duration
388
        elif measure == 'percent':
362 389
            percent_formatter = lambda x: '{:.1f}%'.format(x)
363
            chart.config.value_formatter = percent_formatter
364

  
365
        return chart
390
            return percent_formatter
366
-