code: def plot_bars(
      data,
      labels: list[str],
      bounds: tuple,
      ylim: bool,
      primary_color: str,
      tl_colors: dict[str, str],
      metric: str,
  ) -> str:
      """Plotting histogram data.
  
      :param numpy.ndarray data: bin values of a histogram
      :param labels: common bin labels for all histograms. default is None.
      :param bounds: traffic light bounds (y-coordinates). default is None.
      :param ylim: place y-axis limits for zooming into the data. default is False.
      :return: JSON plot image
      :rtype: str
      """
      # basic checks first
      n = data.size  # number of bins
      if labels is not None and len(labels) != n:
          raise ValueError(
              "shape mismatch: x-axis labels do not match the data shape"
          )
  
      # skip plot generation for empty datasets
      n_data = len(data)
      n_nan = pd.isnull(data).sum()
      n_inf = np.sum(
          [np.isinf(x) for x in data if isinstance(x, float)]
      )
      if n_nan + n_inf == n_data:
          logger.debug("skipping plot with empty data.")
          return ""
  
      # plot bar
      fig = go.Figure(
          [
              go.Bar(
                  x=labels,
                  y=data,
                  hovertemplate="%{y:.4f}",
                  name=metric,
                  marker_color=primary_color,
              )
          ]
      )
  
      # set label granularity
      if len(labels) > 0:
          granularity = math.ceil(len(labels) / 50)
          labels = labels[::granularity]
  
      fig.update_layout(
          xaxis_tickangle=-90,
          xaxis={"type": "category"},
          margin={"l": 40, "r": 10, "t": 30},
      )
      fig.update_xaxes(
          tickvals=labels,
          ticktext=labels,
          showgrid=True,
          ticks="outside",
          minor_ticks="outside",
          showline=True,
          linecolor="black",
          mirror=True,
      )
      fig.update_yaxes(
          ticks="outside",
          minor_ticks="outside",
          showline=True,
          linecolor="black",
          mirror=True,
      )
      fig.update_traces(
          marker_line_color=primary_color,
          marker_line_width=1,
      )
      # plot boundaries
      try:
          all_nan = (np.isnan(data)).all()
          max_value = np.nanmax(data) if not all_nan else np.nan
          min_value = np.nanmin(data) if not all_nan else np.nan
  
          if len(bounds) > 0:
              max_r, max_y, min_y, min_r = bounds
              y_max = max(
                  max(max_r)
                  if isinstance(max_r, (list, tuple))
                  else max_r,
                  max_value,
              )
              y_min = min(
                  max(min_r)
                  if isinstance(min_r, (list, tuple))
                  else min_r,
                  min_value,
              )
              spread = (y_max - y_min) / 20
              y_max += spread
              y_min -= spread
              if not isinstance(max_r, (list, tuple)):
                  fig.add_hline(y=max_r, line_color=tl_colors["red"])
                  fig.add_hline(y=max_y, line_color=tl_colors["yellow"])
                  fig.add_hline(y=min_y, line_color=tl_colors["yellow"])
                  fig.add_hline(y=min_r, line_color=tl_colors["red"])
              else:
                  fig.add_hline(y=max_r[0], line_color=tl_colors["red"])
                  fig.add_hline(
                      y=max_y[0], line_color=tl_colors["yellow"]
                  )
                  fig.add_hline(
                      y=min_y[0], line_color=tl_colors["yellow"]
                  )
                  fig.add_hline(y=min_r[0], line_color=tl_colors["red"])
  
              if y_max > y_min:
                  fig.update_yaxes(range=[y_min, y_max])
  
          elif ylim:
              spread = (max_value - min_value) / 20
              y_min = min_value - spread
              y_max = max_value + spread
              if y_max > y_min:
                  fig.update_yaxes(range=[y_min, y_max])
      except Exception:
          logger.debug("unable to plot boundaries")
  
      plot = json.loads(fig.to_json())
      return plot
  
imports: from __future__ import annotations
  import datetime
  import json
  import logging
  import math
  import warnings
  from collections import defaultdict
  import numpy
  import pandas
  import plotly.express
  import plotly.graph_objects
  from popmon.resources import templates_env
label_desc: Use image node_id 5 for calling a global function with example usage: plot_bars(data, labels, bounds, ylim, primary_color, tl_colors, metric) and returns: plot, str