Source code for shuup.admin.dashboard.charts

# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
import abc
import json
import six
from babel.numbers import format_decimal, format_percent

from shuup.utils.i18n import format_money, get_current_babel_locale
from shuup.utils.money import Money
from shuup.utils.serialization import ExtendedJSONEncoder


[docs]class ChartType(object): """ Type of a chart """ BAR = "bar" LINE = "line"
[docs]class ChartDataType(object): """ Data type of datasets """ NUMBER = "number" CURRENCY = "currency" PERCENT = "percent"
[docs]class Chart(six.with_metaclass(abc.ABCMeta)): supported_chart_types = [] # list[ChartType] def __init__(self, title, data_type=ChartDataType.NUMBER, locale=None, currency=None, options=None): """ :param str title: the title of the chart :param ChartDataType data_type: the data type of values The chart will format the output labels according to this parameter :param str locale: the locale to render values If not set, the locale will be fetched from Babel :param str currency: the ISO-4217 code for the currency This is necessary when the data_type is CURRENCY :param dict options: a dicionaty with options for Chartjs """ self.title = title self.datasets = [] self.options = options self.data_type = data_type self.currency = currency if locale: self.locale = locale else: self.locale = get_current_babel_locale() if data_type == ChartDataType.CURRENCY and not currency: raise AttributeError("You should also set currency for this data type") @abc.abstractmethod
[docs] def get_config(self): """ Get a JSONable dictionary of configuration data for this chart. This is passed on as `CHART_CONFIGS` in the JS environment and eventually processed by `dashboard-charts.js`. :return: Dict of configuration :rtype: dict """ return {} # Implement me in a subclass, please.
[docs] def get_config_json(self): return json.dumps(self.get_config(), cls=ExtendedJSONEncoder, separators=",:")
[docs] def add_data(self, name, data, chart_type): """ Add data to this chart. :param name: the name of the dataset :type name: str :param data: the list of data :type data: list[int|float|Decimal] :param chart_type: the chart type - tells how data should be rendered. This data type must be available in the `supported_chart_type` attribute of this instance :type chart_type: ChartType """ assert chart_type in self.supported_chart_types formatted_data = [] # format value for each data point if self.data_type == ChartDataType.CURRENCY: for value in data: formatted_data.append(format_money(Money(value, currency=self.currency).as_rounded())) elif self.data_type == ChartDataType.PERCENT: for value in data: formatted_data.append(format_percent(value, locale=self.locale)) # self.data_type == ChartDataType.NUMBER else: for value in data: formatted_data.append(format_decimal(value, locale=self.locale)) self.datasets.append({"type": chart_type, "label": name, "data": data, "formatted_data": formatted_data})
[docs]class BarChart(Chart): supported_chart_types = [ChartType.BAR] def __init__(self, title, labels, data_type=ChartDataType.NUMBER, **kwargs): super(BarChart, self).__init__(title, data_type=data_type, **kwargs) self.labels = labels
[docs] def get_config(self): return { "type": ChartType.BAR, "data": {"labels": self.labels, "datasets": self.datasets}, "options": self.options, }
[docs]class MixedChart(Chart): """ This chart supports both Bars and Lines. """ supported_chart_types = [ChartType.BAR, ChartType.LINE] def __init__(self, title, labels, data_type=ChartDataType.NUMBER, **kwargs): super(MixedChart, self).__init__(title, data_type=data_type, **kwargs) self.labels = labels
[docs] def get_config(self): return {"type": "mixed", "labels": self.labels, "data": self.datasets, "options": self.options}