"""Chart rendering utilities.""" from pptx.chart.data import CategoryChartData from pptx.shapes.graphfrm import GraphicFrame from ....typings import Numeric def render_chart( chart: GraphicFrame, categories: tuple[str, ...], series_data: list[tuple[str, tuple[Numeric, ...]]], ) -> None: """Render a chart with the given data. The resulting chart will have the same style as the original chart, but with the data replaced. It is not possible to merely replace the data in the original chart, so the original chart is overriden in place with a new chart with the same style but with the data replaced. Args: chart: Chart object to render. categories: Chart categories. series_data: Chart series data. """ chart_data = CategoryChartData() chart_data.categories = categories for series_name, series_values in series_data: chart_data.add_series(series_name, series_values) chart.chart.replace_data(chart_data) for series in chart.chart.series: _handle_chart_series(series) def _handle_chart_series(series) -> None: """Apply the necessary formatting to a chart series.""" series.has_data_labels = True series.data_labels.show_value = True series.data_labels.number_format = "0.0%" series.in_legend = True # If any of the values is 0, hide the data label. It might sound counterintuitive # to set has_text_frame to True when we want to hide the data label, but it's # actually the way to do it, because the data label will be replaced with # a blank string. # See https://stackoverflow.com/a/75657081/18197137 for idx, point in enumerate(series.points): value = series.values[idx] if value is not None: point.data_label.has_text_frame = series.values[idx] <= 0