//  [python]
def generate_report_video(
        df_traces,
        df_metrics,
        keypoint_names,
        save_dir,
        format="pdf",
        box_kwargs={},
        trace_kwargs={},
        savefig_kwargs={},
        video_kwargs={},
        video_file=None,
        make_video_per_keypoint=False,
):

    if not os.path.exists(save_dir):
        os.makedirs(save_dir)

    # parse inputs
    box_kwargs_default = {
        "plot_type": "boxen",
        "plot_scale": "log",
    }
    box_kwargs = update_kwargs_dict_with_defaults(box_kwargs, box_kwargs_default)

    trace_kwargs_default = {
        "model_names": [],
    }
    trace_kwargs = update_kwargs_dict_with_defaults(trace_kwargs, trace_kwargs_default)

    video_kwargs_default = {
        "likelihood_thresh": 0.05,
        "max_frames": 1000,
        "markersize": 6,
        "framerate": 20,
        "height": 4,
    }
    video_kwargs = update_kwargs_dict_with_defaults(video_kwargs, video_kwargs_default)

    metrics_to_plot = df_metrics.keys()

    # ---------------------------------------------------
    # plot metrics for all models (catplot)
    # ---------------------------------------------------
    for metric_to_plot in metrics_to_plot:

        x_label = "Model Name"
        y_label = get_y_label(metric_to_plot)

        # plot ALL data
        df_tmp = df_metrics[metric_to_plot].melt(id_vars="model_name")
        df_tmp = df_tmp.rename(columns={"variable": "keypoint"})
        fig_box = sns.catplot(
            data=df_tmp, x="model_name", y="value", col="keypoint", col_wrap=3,
            kind=box_kwargs["plot_type"]
        )
        fig_box.set(yscale=box_kwargs["plot_scale"])
        save_file = os.path.join(save_dir, "barplot-all_%s.%s" % (metric_to_plot, format))
        plt.savefig(save_file, dpi=300, format=format, **savefig_kwargs)

        # plot mean data
        log_y = False if box_kwargs["plot_scale"] == "linear" else True
        make_seaborn_catplot(
            x="model_name", y="mean", data=df_metrics[metric_to_plot], log_y=log_y,
            x_label=x_label, y_label=y_label, title="Average over all keypoints",
            plot_type=box_kwargs["plot_type"])
        save_file = os.path.join(save_dir, "barplot-mean_%s.%s" % (metric_to_plot, format))
        plt.savefig(save_file, dpi=300, format=format, **savefig_kwargs)

    # ---------------------------------------------------
    # plot traces
    # ---------------------------------------------------
    for keypoint in keypoint_names:

        cols = get_col_names(keypoint, "x", trace_kwargs["model_names"])
        fig_traces = plot_traces(df_metrics, df_traces, cols)
        save_file = os.path.join(save_dir, "traces-%s.%s" % (keypoint, format))
        fig_traces.write_image(save_file)

    # ---------------------------------------------------
    # make labeled video(s)
    # ---------------------------------------------------
    if video_file is not None:

        cap = cv2.VideoCapture(video_file)
        video_name = os.path.splitext(os.path.basename(video_file))[0]

        # extract x, y, likelihoods for each model
        points = []
        for model_name in trace_kwargs["model_names"]:
            points_model = {}
            for keypoint in keypoint_names:
                tmp = []
                for val in ["x", "y", "likelihood"]:
                    tmp.append(
                        df_traces[get_full_name(keypoint, val, model_name)].to_numpy()[:, None])
                points_model[keypoint] = np.concatenate(tmp, axis=1)
            points.append(points_model)

        # make single video with all keypoints
        save_file = os.path.join(save_dir, "%s_all.mp4" % video_name)
        make_labeled_video(save_file, cap, points, trace_kwargs["model_names"], **video_kwargs)

        # make individual video for each keypoint
        if make_video_per_keypoint:
            for keypoint in keypoint_names:
                save_file = os.path.join(save_dir, "%s_%s.mp4" % (video_name, keypoint))
                points_single = [{keypoint: points_tmp[keypoint]} for points_tmp in points]
                make_labeled_video(
                    save_file, cap, points_single, trace_kwargs["model_names"], **video_kwargs)