Source code for watts.results

# SPDX-FileCopyrightText: 2022-2023 UChicago Argonne, LLC
# SPDX-License-Identifier: MIT

from collections import namedtuple
from datetime import datetime
from pathlib import Path
import shutil
from typing import List

import dill

from .fileutils import PathLike, open_file
from .parameters import Parameters


ExecInfo = namedtuple('ExecInfo', ['job_id', 'plugin', 'name', 'timestamp'])


[docs] class Results: """Results from running a workflow Parameters ---------- params Parameters used to generate inputs exec_info Execution information (job ID, plugin name, time, etc.) inputs List of input files outputs List of output files Attributes ---------- base_path Path to directory storing results inputs List of input files job_id Integer ID of job outputs List of output files parameters Parameters used to generate inputs plugin Name of plugin stdout Standard output from execution time Time at which plugin was executed """ def __init__(self, params: Parameters, exec_info: ExecInfo, inputs: List[PathLike], outputs: List[PathLike]): self.base_path = Path.cwd() self.exec_info = exec_info self.parameters = Parameters(params) self.inputs = [Path(p) for p in inputs] self.outputs = [Path(p) for p in outputs] @property def plugin(self) -> str: return self.exec_info.plugin @property def time(self) -> datetime: return datetime.fromtimestamp(self.exec_info.timestamp * 1e-9) @property def job_id(self) -> int: return self.exec_info.job_id @property def name(self) -> str: return self.exec_info.name @property def stdout(self) -> str: return (self.base_path / f"{self.plugin}_log.txt").read_text()
[docs] def move_files(self, dst: PathLike): """Move input/output files to different directory Parameters ---------- dst Destination path where files should be moved """ dst_path = Path(dst) # Move input/output files and change base -- note that trying to use the # Path.replace method doesn't work across filesystems, so instead we use # shutil.move for i, input in enumerate(self.inputs): shutil.move(str(input), str(dst_path / input.name)) self.inputs[i] = dst_path / input.name for i, output in enumerate(self.outputs): shutil.move(str(output), str(dst_path / output.name)) self.outputs[i] = dst_path / output.name self.base_path = dst_path
[docs] def save(self, filename: PathLike): """Save results to a pickle file Parameters ---------- filename File to save results to """ with open(filename, 'wb') as fh: fh.write(dill.dumps(self))
[docs] @classmethod def from_pickle(cls, filename: PathLike): """Load results from a pickle file Parameters ---------- filename Path to load results from """ with open(filename, 'rb') as fh: result = dill.loads(fh.read()) # For older results objects, add in execution info tuple if not hasattr(result, 'exec_info'): job_id = None if type(result) is Results: plugin = 'Generic' else: plugin = type(result).__name__[7:] name = result.__dict__['name'] dt = result.__dict__['time'] timestamp = int(dt.timestamp() * 1e6) * 1000 result.exec_info = ExecInfo(job_id, plugin, name, timestamp) return result
[docs] def open_folder(self): """Open folder containing results""" open_file(self.base_path)
def __repr__(self): if self.name: return f"<Results: {self.plugin}, {self.name}, {self.time})>" else: return f"<Results: {self.plugin}, {self.time})>"