284 lines
9.1 KiB
Python

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