code: def gen_tutorials(
      repo_dir: str,
      exec_tutorials: bool,
      name: Optional[str] = None,
      smoke_test: bool = False,
  ) -> None:
      """Generate HTML tutorials for Docusaurus Ax site from Jupyter notebooks.
  
      Also create ipynb and py versions of tutorial in Docusaurus site for
      download.
      """
      has_errors = False
  
      with open(
          os.path.join(repo_dir, "website", "tutorials.json"), "r"
      ) as infile:
          tutorial_config = json.loads(infile.read())
      # flatten config dict
      tutorial_configs = [
          config
          for category in tutorial_config.values()
          for config in category
      ]
      # Running only the tutorial described by "name"
      if name is not None:
          tutorial_configs = [
              d for d in tutorial_configs if d["id"] == name
          ]
          if len(tutorial_configs) == 0:
              raise RuntimeError(f"No tutorial found with name {name}.")
      # prepare paths for converted tutorials & files
      os.makedirs(
          os.path.join(repo_dir, "website", "_tutorials"), exist_ok=True
      )
      os.makedirs(
          os.path.join(repo_dir, "website", "static", "files"),
          exist_ok=True,
      )
      env = {"SMOKE_TEST": "True"} if smoke_test else None
  
      for config in tutorial_configs:
          tid = config["id"]
          t_dir = config.get("dir")
          exec_on_build = config.get("exec_on_build", True)
          print("Generating {} tutorial".format(tid))
          paths = _get_paths(repo_dir=repo_dir, t_dir=t_dir, tid=tid)
  
          total_time = None
  
          if tid in TUTORIALS_TO_SKIP:
              print(f"Skipping execution of {tid}")
              continue
          elif exec_tutorials and exec_on_build:
              tutorial_path = Path(paths["tutorial_path"])
              print("Executing tutorial {}".format(tid))
              start_time = time.monotonic()
  
              # Try / catch failures for now. We will re-raise at the end.
              timeout_minutes = 15 if smoke_test else 150
              try:
                  # Execute notebook.
                  run_script(
                      tutorial=tutorial_path,
                      timeout_minutes=timeout_minutes,
                      env=env,
                  )
                  total_time = time.monotonic() - start_time
                  print(
                      f"Finished executing tutorial {tid} in {total_time:.2f} seconds. "
                  )
              except CellTimeoutError:
                  has_errors = True
                  print(
                      f"Tutorial {tid} exceeded the maximum runtime of "
                      f"{timeout_minutes} minutes."
                  )
              except Exception as e:
                  has_errors = True
                  print(
                      f"Encountered error running tutorial {tid}: \n {e}"
                  )
  
          # load notebook
          with open(paths["tutorial_path"], "r") as infile:
              nb_str = infile.read()
              nb = nbformat.reads(nb_str, nbformat.NO_CONVERT)
          # convert notebook to HTML
          exporter = HTMLExporter(template_name="classic")
          html, _ = exporter.from_notebook_node(nb)
  
          # pull out html div for notebook
          soup = BeautifulSoup(html, "html.parser")
          nb_meat = soup.find("div", {"id": "notebook-container"})
          del nb_meat.attrs["id"]
          nb_meat.attrs["class"] = ["notebook"]
  
          # when output html, iframe it (useful for Ax reports)
          for html_div in nb_meat.findAll(
              "div", {"class": "output_html"}
          ):
              if html_div.html is not None:
                  iframe = soup.new_tag("iframe")
                  iframe.attrs[
                      "src"
                  ] = "data:text/html;charset=utf-8," + str(
                      html_div.html
                  )
                  # replace `#` in CSS
                  iframe.attrs["src"] = iframe.attrs["src"].replace(
                      "#", "%23"
                  )
                  html_div.contents = [iframe]
  
          html_out = MOCK_JS_REQUIRES + str(nb_meat)
  
          # generate HTML file
          with open(paths["html_path"], "w") as html_outfile:
              html_outfile.write(html_out)
  
          # generate JS file
          t_dir_js = t_dir if t_dir else ""
          script = TEMPLATE.format(
              t_dir=t_dir_js,
              tid=tid,
              total_time=total_time
              if total_time is not None
              else "null",
          )
          with open(paths["js_path"], "w") as js_outfile:
              js_outfile.write(script)
  
          # output tutorial in both ipynb & py form
          nbformat.write(nb, paths["ipynb_path"])
          exporter = ScriptExporter()
          script, _ = exporter.from_notebook_node(nb)
          with open(paths["py_path"], "w") as py_outfile:
              py_outfile.write(script)
  
          # create .tar archive (if necessary)
          if t_dir is not None:
              with tarfile.open(paths["tar_path"], "w:gz") as tar:
                  tar.add(
                      paths["tutorial_dir"],
                      arcname=os.path.basename(paths["tutorial_dir"]),
                  )
  
      if has_errors:
          raise Exception(
              "There are errors in tutorials, will not continue to publish"
          )
  
file_path: Ax/scripts/make_tutorials.py
imports: import argparse
  import json
  import os
  import tarfile
  import time
  from pathlib import Path
  from typing import Dict, Optional
  import nbformat
  import papermill
  from bs4 import BeautifulSoup
  from nbclient.exceptions import CellTimeoutError
  from nbconvert import HTMLExporter, ScriptExporter
label_desc: Use image node_id 3 for calling a global function with example usage: gen_tutorials(repo_dir, exec_tutorials, name, smoke_test) without return types