Source code for vis_tools.Config
# ==============================================================================
# Config.py - Python wrapper for IDM config.json files
# ==============================================================================
"""Config.py
This class is a simple Python wrapper for the config.json file used to configure
the DTK.
Usage::
config = Config(path.join(my_dir, "config.json"))
print config
"""
# imports
from builtins import object
import json
import sys
# ==============================================================================
# Config - a class to hold DTK config.json
# ==============================================================================
[docs]class Config(object):
"""Class to hold DTK config.json data.
The class is constructed with the path to the config.json file. Thereafter
the public data members source_file, timestamp_count, and parameters may be
used to directly access the resultant Python object. There are also accessor
methods for the most commonly needed parameters.
Public members:
The following data members are publicly exposed.
source_file (str): A copy of the file_path used to construct the Config
object.
timestep_count (int): The Simulation_Duration parameter.
parameters (obj): The entire config.json as a Python object.
"""
def __init__(self, file_path="", verbose=False):
"""Construct a Config.
Args:
file_path (str): The path to the config.json file.
verbose (bool): True for extra messaging from methods.
Raises:
I/O or JSON exceptions.
"""
# data members, some of which are gleaned by processing the nodes
self.source_file = ""
self.timestep_count = 0
self._verbose = verbose
# the bare parts of the read config file
self.parameters = {}
# read if file path was given
if not file_path == "":
self._read_json(file_path)
# --------------------------------------------------------------------------
def __str__(self):
"""Generate a textual representation of a Config.
This method allows the Config object to report the source file
and number of timesteps when it is printed.
Returns:
str: String containing source file and timestep count.
"""
if self.parameters == {}:
return "(empty)"
else:
return self.source_file + ": " +\
repr(int(self.parameters["Simulation_Duration"])) + " timesteps"
# --------------------------------------------------------------------------
# Accessors
# --------------------------------------------------------------------------
[docs] def get_demographics_filenames(self):
"""Return the value of Parameters > Demographics_Filenames.
Returns:
list(str): List of demographics filenames.
Args:
None.
"""
return self.parameters["Demographics_Filenames"]
# --------------------------------------------------------------------------
[docs] def get_first_demographics_filename(self):
"""Returns the first (or only) demographics file from the config.
This function understands the presence of Demographics_Filename with a
single path, Demographics_Filename with multiple paths delimited by ';',
and Demographics_Filenames as an array of paths.
Returns:
str: Demographics file name from Demographics_Filenames or
Demographics_Filename, or None.
Args:
None.
"""
if "Demographics_Filenames" in self.parameters:
return self.parameters["Demographics_Filenames"][0]
elif "Demographics_Filename" in self.parameters:
parts = self.parameters["Demographics_Filename"].split(";")
if len(parts) == 1:
return self.parameters["Demographics_Filename"]
else:
return parts[0]
else:
return None
# --------------------------------------------------------------------------
[docs] def get_demographics_filename_or_list(self):
"""Returns a string or list of demographics filenames, or None.
This function understands the presence of Demographics_Filename with a
single path, Demographics_Filename with multiple paths delimited by ';',
and Demographics_Filenames as an array of paths.
Returns:
str|list: if a single demographics file is specified in the config,
returns it as a string. If an array is specified, returns an array
of filenames. Otherwise returns None.
Args:
None.
"""
if "Demographics_Filenames" in self.parameters:
if len(self.parameters["Demographics_Filenames"]) == 1:
return self.parameters["Demographics_Filenames"][0]
else:
return self.parameters["Demographics_Filenames"]
elif "Demographics_Filename" in self.parameters:
parts = self.parameters["Demographics_Filename"].split(";")
if len(parts) == 1:
return self.parameters["Demographics_Filename"]
else:
return [fp.strip() for fp in parts]
else:
return None
# --------------------------------------------------------------------------
[docs] def get_timestep_count(self):
"""Return the value of Parameters > Simulation_Duration.
Returns:
int: Timestep count.
Args:
None.
"""
return self.parameters["Simulation_Duration"]
# --------------------------------------------------------------------------
# Implementation
# --------------------------------------------------------------------------
def _read_json(self, json_file_path):
with open(json_file_path, "r") as config_file:
self.source_file = json_file_path
raw = json.load(config_file)
# distribute the parts to our members
if "parameters" in raw:
self.parameters = raw["parameters"]
# We deliberately cast the following value to an int because there
# were simulations in the wild with fractional timestep counts.
# e.g 3d9b55ef-537e-e811-80c9-f0921c167864 in staging with a 10.95
# duration, and the associated spatial binaries had 10 timesteps,
# indicating that DTK is truncating. So now we are too.
self.parameters["Simulation_Duration"] =\
int(self.parameters["Simulation_Duration"])
self.timestep_count = self.parameters["Simulation_Duration"]
if self._verbose:
print("Config._read_json: Config read, simulation duration %d "
"timesteps" % self.timestep_count)
elif self._verbose:
print("Config._read_json: Parameters key not found in config file.",
file=sys.stderr)