class_docstr: Pipeline wrapper with 'export_pipeline'/'import_pipeline' methods
  allowing user to upload a pipeline to JSON format and import it from JSON.
  
  :params pipeline: Pipeline object to export or empty Pipeline to import
class_docstr_tok: ["Pipeline","wrapper","with","'export_pipeline'\/'import_pipeline","'","methods","allowing","user","to","upload","a","pipeline","to","JSON","format","and","import","it","from","JSON",".",":","params","pipeline",":","Pipeline","object","to","export","or","empty","Pipeline","to","import"]
docstr: Recursively go through the Pipeline from 'root_node' to PipelineNode's,
  creating a OperationTemplate with unique id for each Node. In addition,
  checking whether this Node has been visited yet.
docstr_tok: ["Recursively","go","through","the","Pipeline","from","'root_node","'","to","PipelineNode","'s",",","creating","a","OperationTemplate","with","unique","id","for","each","Node",".","In","addition",",","checking","whether","this","Node","has","been","visited","yet","."]
code: def _extract_pipeline_structure(
      self,
      node: PipelineNode,
      operation_id: int,
      visited_nodes: List[PipelineNode],
  ):
      """
      Recursively go through the Pipeline from 'root_node' to PipelineNode's,
      creating a OperationTemplate with unique id for each Node. In addition,
      checking whether this Node has been visited yet.
      """
      if node and node.nodes_from:
          nodes_from = []
          for node_parent in node.nodes_from:
              if node_parent in visited_nodes:
                  nodes_from.append(
                      visited_nodes.index(node_parent) + 1
                  )
              else:
                  visited_nodes.append(node_parent)
                  nodes_from.append(len(visited_nodes))
                  self._extract_pipeline_structure(
                      node_parent, len(visited_nodes), visited_nodes
                  )
      else:
          nodes_from = []
  
      # TODO resolve as to GraphTemplate
      if hasattr(node, "operation"):
          if (
              not isinstance(node.operation, str)
              and node.operation.operation_type == atomized_model_type()
          ):
              operation_template = AtomizedModelTemplate(
                  node, operation_id, sorted(nodes_from)
              )
          else:
              operation_template = OperationTemplate(
                  node, operation_id, sorted(nodes_from)
              )
  
          self.operation_templates.append(operation_template)
          self.total_pipeline_operations[
              operation_template.operation_type
          ] += 1
  
imports: import json
  import os
  from collections import Counter
  from datetime import datetime
  from io import BytesIO
  from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union
  from uuid import uuid4
  import joblib
  import numpy
  from golem.core.log import default_log
  from fedot.core.operations.atomized_template import AtomizedModelTemplate
  from fedot.core.operations.operation_template import OperationTemplate, check_existing_path
  from fedot.core.pipelines.node import PipelineNode
  from fedot.core.repository.operation_types_repository import atomized_model_type
  from fedot.preprocessing.dummy_preprocessing import DummyPreprocessor
  from fedot.preprocessing.preprocessing import DataPreprocessor
label_desc: Use image node_id 3 for calling the PipelineTemplate obj's underlying member method code with example usage: obj._extract_pipeline_structure(node, operation_id, visited_nodes) without return types