from reportlab.graphics.charts.barcharts import HorizontalBarChart, VerticalBarChart from reportlab.graphics.charts.doughnut import Doughnut from reportlab.graphics.charts.linecharts import HorizontalLineChart from reportlab.graphics.charts.piecharts import LegendedPie, Pie from reportlab.graphics.widgets.markers import makeMarker from xhtml2pdf.util import getColor def set_properties(obj, data, prop_map): for key, fnc in prop_map: if key in data: try: value = fnc(data[key]) if value is not None: obj.__setattr__(key, value) except Exception: continue class Props: def __init__(self, instance) -> None: self.prop_map = [ ("x", int), ("y", int), ("width", int), ("height", int), ("data", lambda x: x), ("labels", lambda x: instance.assign_labels(x)), ] self.prop_map_title = [("x", int), ("y", int), ("_text", str)] self.prop_map_legend = [ ("x", int), ("y", int), ("deltax", int), ("alignment", str), ("boxAnchor", str), ("fontSize", int), ("strokeWidth", int), ("dy", int), ("dx", int), ("dxTextSpace", int), ("deltay", int), ("columnMaximum", int), ("variColumn", int), ("deltax", int), ("fontName", str), ("colorNamePairs", list), ] self.prop_map_legend1 = [("x", int), ("y", int)] self.prop_map_bars = [("strokeWidth", int)] self.prop_map_barLabels = [("nudge", int), ("fontSize", int), ("fontName", str)] self.prop_map_categoryAxis = [ ("visibleTicks", int), ("strokeWidth", int), ("tickShift", int), ("labelAxisMode", str), ] self.prop_map_categoryAxis_labels = [ ("angle", int), ("dy", int), ("fontSize", int), ("boxAnchor", str), ("fontName", str), ("textAnchor", str), ] self.prop_map_slices = [ ("strokeWidth", int), ("labelRadius", float), ("poput", int), ("fontName", str), ("fontSize", int), ("strokeDashArray", str), ] @staticmethod def add_prop(prop_map, data): prop_map += data class BaseChart: def set_legend(self, data, legend, props=None): if props is None: props = Props(self) set_properties(legend, data, props.prop_map_legend) return legend def load_data_legend(self, data, legend): legend.colorNamePairs = [] color = self.get_colors() for x, obj in enumerate(data["data"]): if isinstance(obj, list): for y, value in enumerate(obj): if color: if data["type"] == "doughnut": legend.colorNamePairs.append( (color[x], (data["labels"][y], " ", str(value))) ) else: legend.colorNamePairs.append( (color[y], (data["labels"][y], " ", str(value))) ) elif color: legend.colorNamePairs.append( (color[x], (data["labels"][x], " ", str(obj))) ) def set_title_properties(self, data, title, props=None): if props is None: props = Props(self) set_properties(title, data, props.prop_map_title) return title def set_properties(self, data, props=None): if props is None: props = Props(self) set_properties(self, data, props.prop_map) @staticmethod def get_colors(): return [] class BaseBarChart(BaseChart): def __init__(self) -> None: super().__init__() def set_properties(self, data, props=None): props = Props(self) props.add_prop(props.prop_map, [("barWidth", str)]) props.add_prop(props.prop_map, [("barSpacing", str)]) props.add_prop(props.prop_map, [("barLabelFormat", str)]) props.add_prop(props.prop_map, [("strokeColor", getColor)]) props.add_prop(props.prop_map, [("groupSpacing", int)]) super().set_properties(data, props=props) if "bars" in data: self.set_bars(data["bars"], props=props) if "barLabels" in data: self.set_barLabels(data["barLabels"], props=props) if "categoryAxis" in data: self.set_categoryAxis(data["categoryAxis"], props=props) if "labels" in data["categoryAxis"]: self.set_categoryAxis_labels( data["categoryAxis"]["labels"], props=props ) def assign_labels(self, labels): self.categoryAxis.categoryNames = labels def set_bars(self, data, props=None): if props is None: props = Props(self) props.add_prop(props.prop_map_bars, [("strokeColor", getColor)]) set_properties(self.bars, data, props.prop_map_bars) def set_barLabels(self, data, props=None): if props is None: props = Props(self) set_properties(self.barLabels, data, props.prop_map_barLabels) def set_categoryAxis(self, data, props=None): if props is None: props = Props(self) props.add_prop(props.prop_map_categoryAxis, [("strokeColor", getColor)]) set_properties(self.categoryAxis, data, props.prop_map_categoryAxis) def set_categoryAxis_labels(self, data, props=None): if props is None: props = Props(self) props.add_prop(props.prop_map_categoryAxis_labels, [("fillColor", getColor)]) set_properties( self.categoryAxis.labels, data, props.prop_map_categoryAxis_labels ) class HorizontalBar(HorizontalBarChart, BaseBarChart): pass class VerticalBar(VerticalBarChart, BaseBarChart): pass class HorizontalLine(HorizontalLineChart, BaseChart): def __init__(self) -> None: super().__init__() def assign_labels(self, labels): self.categoryAxis.categoryNames = labels def set_properties(self, data, props=None): props = Props(self) props.add_prop(props.prop_map, [("fillColor", getColor)]) props.add_prop(props.prop_map, [("lineLabelFormat", str)]) props.add_prop(props.prop_map, [("strokeColor", int)]) props.add_prop(props.prop_map, [("joinedLines", int)]) props.add_prop(props.prop_map, [("marker", self.fill_marker)]) super().set_properties(data, props=props) def fill_marker(self, fill_type): for x in range(len(self.data)): self.lines[x].symbol = makeMarker(fill_type) @staticmethod def get_colors(): return [] class PieChart(Pie, BaseChart): def __init__(self) -> None: super().__init__() def set_properties(self, data, props=None): props = Props(self) props.add_prop(props.prop_map, [("sideLabels", int)]) props.add_prop(props.prop_map, [("simpleLabels", int)]) props.add_prop(props.prop_map, [("sideLabelsOffset", int)]) props.add_prop(props.prop_map, [("startAngle", int)]) props.add_prop(props.prop_map, [("orderMode", str)]) props.add_prop(props.prop_map, [("direction", str)]) super().set_properties(data, props=props) if "slices" in data: self.set_slices(data["slices"], props=props) def assign_labels(self, labels): self.labels = labels def set_slices(self, data, props=None): if props is None: props = Props(self) props.add_prop(props.prop_map_slices, [("strokeColor", getColor)]) props.add_prop(props.prop_map_slices, [("fillColor", getColor)]) set_properties(self.slices, data, props.prop_map_slices) def get_colors(self): colors_list = [] for x, _obj in enumerate(self.data): colors_list.append(self.slices[x].fillColor) return colors_list class LegendedPieChart(LegendedPie, BaseChart): def __init__(self) -> None: super().__init__() self.legend1.x = 350 self.legend1.y = 150 def set_properties(self, data, props=None): props = Props(self) props.add_prop(props.prop_map, [("legend_data", list)]) super().set_properties(data, props=props) if "legend1" in data: self.set_legend1(self.legend1, data["legend1"], props=props) def set_legend1(self, obj, data, props=None): if props is None: props = Props(self) set_properties(obj, data, props.prop_map_legend1) def assign_labels(self, labels): self.legend_names = labels class DoughnutChart(Doughnut, BaseChart): def __init__(self) -> None: super().__init__() def assign_labels(self, labels): self.labels = labels def get_colors(self): colors = [] for x, _obj in enumerate(self.data): colors.append(self.slices[x].fillColor) return colors