code: def node_to_dict(
      df,
      tree,
      nodeid,
      categories,
      json,
      feature_names,
      metric,
      parent=None,
      side=TreeSide.UNKNOWN,
      classes=None,
  ):
      """Converts a node and children to a dictionary that can be saved as JSON.
  
      This is a method that is called on the current node and then its children
      recursively to construct the dictionary representation.
  
      :param df: The DataFrame to use for the current node.
      :type df: pandas.DataFrame
      :param tree: The tree to use for the current node.
      :type tree: dict
      :param nodeid: The id of the current node.
      :type nodeid: int
      :param categories: The list of categories for the current node.
      :type categories: list[tuple]
      :param json: The JSON to write the node to.
      :type json: dict
      :param feature_names: The set of feature names.
      :type feature_names: set[str]
      :param metric: The metric to use for the current node.
      :type metric: str
      :param parent: The parent node.
      :type parent: dict
      :param side: The side of the current node from the parent, if known.
      :type side: TreeSide
      :param classes: The list of classes.
      :type classes: list[str]
      :return: The JSON with the node and all children added.
      :rtype: dict
      """
      p_node_name = None
      condition = None
      arg = None
      method = None
      parentid = None
      if parent is not None:
          parentid = int(parent[SPLIT_INDEX])
          p_node_name_val = feature_names[parent[SPLIT_FEATURE]]
          # use number.Integral to check for any numpy or python number type
          if isinstance(p_node_name_val, numbers.Integral):
              # for numeric column names, we can use @df[numeric_colname] syntax
              p_node_query = "@df[" + str(p_node_name_val) + "]"
          else:
              # for string column names, we can just use column name directly
              # with backticks
              p_node_query = "`" + str(p_node_name_val) + "`"
          p_node_name = str(p_node_name_val)
          parent_threshold = parent["threshold"]
          parent_decision_type = parent["decision_type"]
          if side == TreeSide.LEFT_CHILD:
              if parent_decision_type == "<=":
                  method = "less and equal"
                  arg = float(parent_threshold)
                  condition = "{} <= {:.2f}".format(
                      p_node_name, parent_threshold
                  )
                  df = filter_by_threshold(
                      df, p_node_name_val, parent_threshold, side
                  )
              elif parent_decision_type == "==":
                  method = CohortFilterMethods.METHOD_INCLUDES
                  arg = create_categorical_arg(parent_threshold)
                  query, condition = create_categorical_query(
                      method,
                      arg,
                      p_node_name,
                      p_node_query,
                      parent,
                      categories,
                  )
                  df = df.query(query)
          elif side == TreeSide.RIGHT_CHILD:
              if parent_decision_type == "<=":
                  method = "greater"
                  arg = float(parent_threshold)
                  condition = "{} > {:.2f}".format(
                      p_node_name, parent_threshold
                  )
                  df = filter_by_threshold(
                      df, p_node_name_val, parent_threshold, side
                  )
              elif parent_decision_type == "==":
                  method = CohortFilterMethods.METHOD_EXCLUDES
                  arg = create_categorical_arg(parent_threshold)
                  query, condition = create_categorical_query(
                      method,
                      arg,
                      p_node_name,
                      p_node_query,
                      parent,
                      categories,
                  )
                  df = df.query(query)
      total = df.shape[0]
      if is_spark(df):
          metric_value, success, error = compute_metrics_pyspark(
              df, metric, total
          )
      else:
          metric_value, success, error = compute_metrics_local(
              df, metric, total, classes
          )
      metric_name = metric_to_display_name[metric]
      is_error_metric = metric in error_metrics
      if SPLIT_FEATURE in tree:
          node_name = str(feature_names[tree[SPLIT_FEATURE]])
      else:
          node_name = None
      is_regression_metric = metric in regression_metrics
      json.append(
          get_json_node(
              arg,
              condition,
              error,
              nodeid,
              method,
              node_name,
              parentid,
              p_node_name,
              total,
              success,
              metric_name,
              metric_value,
              is_error_metric,
              is_regression_metric,
          )
      )
      return json, df
  
imports: import logging
  import numbers
  from enum import Enum
  import numpy
  import pandas
  from lightgbm import Booster, LGBMClassifier, LGBMRegressor
  from sklearn.metrics import mean_absolute_error, mean_squared_error, median_absolute_error, r2_score
  from erroranalysis._internal.cohort_filter import filter_from_cohort
  from erroranalysis._internal.constants import DIFF, LEAF_INDEX, METHOD, PRED_Y, ROW_INDEX, SPLIT_FEATURE, SPLIT_INDEX, TRUE_Y, CohortFilterMethods, Metrics, ModelTask, TreeNode, error_metrics, f1_metrics, metric_to_display_name, precision_metrics, recall_metrics, regression_metrics
  from erroranalysis._internal.metrics import get_ordered_classes, metric_to_func
  from erroranalysis._internal.process_categoricals import process_categoricals
  from erroranalysis._internal.utils import is_spark
  from raiutils.exceptions import UserConfigValidationException
label_desc: Use image node_id 15 for calling a global function with example usage: node_to_dict(df, tree, nodeid, categories, json, feature_names, metric, parent, side, classes) and returns: json,  df