class_docstr: A job provides data available for a single training job.
  
  It is first obtained from a training job run on AWS Sagemaker. By storing
  it locally, it can be loaded from the file system at a later point and no
  Sagemaker connection is required. The initializer of this class should not
  be used directly.
class_docstr_tok: ["A","job","provides","data","available","for","a","single","training","job",".","It","is","first","obtained","from","a","training","job","run","on","AWS","Sagemaker",".","By","storing","it","locally",",","it","can","be","loaded","from","the","file","system","at","a","later","point","and","no","Sagemaker","connection","is","required",".","The","initializer","of","this","class","should","not","be","used","directly","."]
docstr: Stores all data associated with the training job in an auto-generated,
  unique folder within the provided directory.
  
  The job is stored under `<model>/<dataset>/<autogenerated>`. Storing
  jobs via this function allows to decouple experimental results
  completely from AWS Sagemaker.
docstr_tok: ["Stores","all","data","associated","with","the","training","job","in","an","auto-generated",",","unique","folder","within","the","provided","directory",".","The","job","is","stored","under","`","<","model",">","\/","<","dataset",">","\/","<","autogenerated",">","`",".","Storing","jobs","via","this","function","allows","to","decouple","experimental","results","completely","from","AWS","Sagemaker","."]
code: def save(self, path: Path, include_forecasts: bool = True) -> None:
      """
      Stores all data associated with the training job in an auto-generated,
      unique folder within the provided directory.
  
      The job is stored under `<model>/<dataset>/<autogenerated>`. Storing
      jobs via this function allows to decouple experimental results
      completely from AWS Sagemaker.
      """
      assert self.source_job is not None, (
          "Job cannot be saved if it was not initialized from an AWS"
          " Sagemaker job."
      )
  
      # First, we generate the folder name
      components = [f"seed-{self.config['seed']}"] + [
          f"{k}-{v}"
          for k, v in self.config.get("hyperparameters", {}).items()
      ]
      target = (
          path
          / self.model
          / self.dataset
          / "+".join(sorted(components))
      )
  
      # Make sure folder exists and is empty
      if target.exists():
          if _check_all_data_available(target):
              return
          shutil.rmtree(target)
  
      target.mkdir(parents=True, exist_ok=True)
  
      # Then, we store metadata files
      with (target / "config.json").open("w+") as f:
          json.dump(self.config, f, indent=4)
      with (target / "performance.json").open("w+") as f:
          json.dump(self.performance, f, indent=4)
  
      # As well as the forecasts (we ignore val forecasts as they are never used)
      if include_forecasts:
          num_models = len(self.performance["performances"])
          with self.source_job.artifact(cache=False) as artifact:
              (target / "forecasts").mkdir()
              for i in range(num_models):
                  (target / "forecasts" / f"model_{i:02}").mkdir()
                  shutil.copyfile(
                      artifact.path
                      / "predictions"
                      / f"model_{i}"
                      / "values.npy",
                      target
                      / "forecasts"
                      / f"model_{i:02}"
                      / "values.npy",
                  )
                  shutil.copyfile(
                      artifact.path
                      / "predictions"
                      / f"model_{i}"
                      / "metadata.npz",
                      target
                      / "forecasts"
                      / f"model_{i:02}"
                      / "metadata.npz",
                  )
  
      # Finally check that saving all data worked as expected
      assert _check_all_data_available(target)
  
imports: from __future__ import annotations
  import json
  import math
  import os
  import shutil
  from pathlib import Path
  from typing import Any, cast, Dict, List, Optional, Union
  import numpy
  from tsbench.config import MODEL_REGISTRY
  from tsbench.evaluations.aws import Analysis, TrainingJob
  from tsbench.evaluations.metrics import Metric, Performance
  from tsbench.forecasts import QuantileForecasts
label_desc: Use image node_id 9 for calling the Job obj's underlying member method code with example usage: obj.save(path, include_forecasts) without return types