Source code for dragonfly_energy.config

"""dragonfly_energy configurations.

Import this into every module where access configurations are needed.

Usage:

.. code-block:: python

    from dragonfly_energy.config import folders
    print(folders.mapper_path)
    folders.mapper_path = "C:/Urbanopt_test/Honeybee.rb"
"""
from ladybug.futil import write_to_file
import honeybee_energy.config as hb_energy_config

import os
import platform
import json
import subprocess


[docs] class Folders(object): """Dragonfly_energy folders. Args: config_file: The path to the config.json file from which folders are loaded. If None, the config.json module included in this package will be used. Default: None. mute: If False, the paths to the various folders will be printed as they are found. If True, no printing will occur upon initialization of this class. Default: True. Properties: * mapper_path * urbanopt_gemfile_path * urbanopt_cli_path * urbanopt_env_path * urbanopt_version * urbanopt_version_str * reopt_assumptions_path * docker_version * docker_version_str * config_file * mute """ URBANOPT_VERSION = (0, 14, 0) COMPATIBILITY_URL = 'https://github.com/ladybug-tools/lbt-grasshopper/wiki/' \ '1.4-Compatibility-Matrix' def __init__(self, config_file=None, mute=True): self.mute = bool(mute) # set the mute value self.config_file = config_file # load paths from the config JSON file @property def mapper_path(self): """Get or set the path to the Ruby mapper used in URBANopt workflows. This is the Ruby file that is used to map URBANopt geoJSON features to honeybee model JSONs. """ return self._mapper_path @mapper_path.setter def mapper_path(self, path): if not path: # check the default installation location path = self._find_mapper_path() if path: # check that the mapper file exists in the path assert os.path.isfile(path) and path.endswith('.rb'), \ '{} is not a valid path to a Ruby mapper file.'.format(path) self._mapper_path = path # set the mapper_path if path and not self.mute: print("Path to Mapper is set to: %s" % path) @property def urbanopt_gemfile_path(self): """Get or set the path to the Gemfile used in URBANopt workflows. Setting this can be used to test newer versions of URBANopt with upgraded dependencies in the Gemfile. """ return self._urbanopt_gemfile_path @urbanopt_gemfile_path.setter def urbanopt_gemfile_path(self, path): if not path: # check the default installation location path = self._find_urbanopt_gemfile_path() if path: # check that the Gemfile exists at the path assert os.path.isfile(path), \ '{} is not a valid path to an URBANopt Gemfile.'.format(path) self._urbanopt_gemfile_path = path # set the urbanopt_gemfile_path if path and not self.mute: print("Path to URBANopt Gemfile is set to: %s" % path) @property def urbanopt_cli_path(self): """Get or set the path to the path where URBANopt is installed. Setting this can be used to test newer versions of URBANopt. """ return self._urbanopt_cli_path @urbanopt_cli_path.setter def urbanopt_cli_path(self, path): if not path: # check the default installation location path = self._find_urbanopt_cli_path() if path: # check that the installation exists at the path assert os.path.isdir(path), \ '{} is not a valid path to an URBANopt installation.'.format(path) self._urbanopt_cli_path = path # set the urbanopt_cli_path self._urbanopt_env_path = None self._urbanopt_version = None self._urbanopt_version_str = None self._docker_version = None self._docker_version_str = None if path and not self.mute: print("Path to URBANopt CLI is set to: %s" % path) @property def urbanopt_env_path(self): """Get or set the path to the executable used to set the URBANopt environment. """ return self._urbanopt_env_path @urbanopt_env_path.setter def urbanopt_env_path(self, path): if path: # check that the file exists at the path assert os.path.isfile(path), \ '{} is not a valid path to an URBANopt env executable.'.format(path) self._urbanopt_env_path = path # set the urbanopt_env_path if path and not self.mute: print("Path to URBANopt Environment executable is set to: %s" % path) @property def urbanopt_version(self): """Get a tuple for the version of URBANopt (eg. (0, 7, 1)). This will be None if the version could not be sensed or if no URBANopt installation was found. """ if self._urbanopt_cli_path and self._urbanopt_version_str is None: self._urbanopt_version_from_cli() return self._urbanopt_version @property def urbanopt_version_str(self): """Get text for the full version of URBANopt (eg. "0.7.1"). This will be None if the version could not be sensed or if no URBANopt installation was found. """ if self._urbanopt_cli_path and self._urbanopt_version_str is None: self._urbanopt_version_from_cli() return self._urbanopt_version_str @property def reopt_assumptions_path(self): """Get or set the path to the JSON file that contains base REopt assumptions. """ return self._reopt_assumptions_path @reopt_assumptions_path.setter def reopt_assumptions_path(self, path): if not path: # check the default installation location path = self._find_reopt_assumptions_path() if path: # check that the file exists at the path assert os.path.isfile(path), \ '{} is not a valid path to a REopt assumptions JSON.'.format(path) self._reopt_assumptions_path = path # set the reopt_assumptions_path if path and not self.mute: print("Path to REopt assumptions is set to: %s" % path) @property def docker_version(self): """Get a tuple for the version of Docker installed (eg. (24, 0, 7)). This will be None if the version could not be sensed or if no Docker installation was found. """ if self._docker_version_str is None: self._docker_version_from_cli() return self._docker_version @property def docker_version_str(self): """Get text for the full version of Docker (eg. "24.0.7"). This will be None if the version could not be sensed or if no Docker installation was found. """ if self._docker_version_str is None: self._docker_version_from_cli() return self._docker_version_str @property def config_file(self): """Get or set the path to the config.json file from which folders are loaded. Setting this to None will result in using the config.json module included in this package. """ return self._config_file @config_file.setter def config_file(self, cfg): if cfg is None: cfg = os.path.join(os.path.dirname(__file__), 'config.json') self._load_from_file(cfg) self._config_file = cfg
[docs] def generate_urbanopt_env_path(self): """Run the URBANopt setup-env file to set this object's urbanopt_env_path.""" # search for the file in its default location home_folder = os.getenv('HOME') or os.path.expanduser('~') env_file = os.path.join(home_folder, '.env_uo.bat') if os.name == 'nt' else \ os.path.join(home_folder, '.env_uo.sh') if self.urbanopt_cli_path: # try to generate the env file if os.name == 'nt': env_setup = os.path.join(self.urbanopt_cli_path, 'setup-env.bat') if not os.path.isfile(env_setup): env_setup = os.path.join(self.urbanopt_cli_path, 'setup-env.ps1') else: env_setup = os.path.join(self.urbanopt_cli_path, 'setup-env.sh') if os.path.isfile(env_setup): if os.name == 'nt': # run the batch file on Windows if env_setup.endswith('.bat'): os.system(env_setup) else: cmds = ['powershell.exe', env_setup] p = subprocess.Popen(cmds) p.communicate() else: # run the sell file on Mac or Linux subprocess.check_call(['chmod', 'u+x', env_setup]) subprocess.call(env_setup) if os.path.isfile(env_file): if os.name == 'nt': # remove the utf-8 encoding powershell does with open(env_file, 'r') as inf: env_data = inf.read().splitlines(True) if not env_data[0] == '\n' and not env_data[0].startswith('SET'): env_data = env_data[1:] with open(env_file, 'w') as otf: otf.write(''.join(env_data)) self._urbanopt_env_path = env_file # the file was successfully generated
[docs] def check_urbanopt_version(self): """Check if the installed version of URBANopt is the acceptable one.""" in_msg = 'Get a compatible version of URBANopt by downloading and installing\n' \ 'the version of URBANopt listed in the Ladybug Tools compatibility ' \ 'matrix\n{}.'.format(self.COMPATIBILITY_URL) assert self.urbanopt_cli_path is not None, \ 'No URBANopt installation was found on this machine.\n{}'.format(in_msg) uo_version = self.urbanopt_version if uo_version is None: if self.urbanopt_env_path is not None: ext = '.bat' if os.name == 'nt' else '.sh' ver_file = os.path.join( os.path.dirname(self.urbanopt_env_path), '.check_uo_version{}'.format(ext)) process = subprocess.Popen(ver_file, stderr=subprocess.PIPE, shell=True) _, stderr = process.communicate() else: stderr = 'Unable to set up the URBANopt environment.' msg = 'An URBANopt installation was found at {}\n' \ 'but the URBANopt executable is not accessible.\n{}'.format( self.urbanopt_cli_path, stderr) raise ValueError(msg) assert uo_version[0] == self.URBANOPT_VERSION[0] and \ uo_version[1] == self.URBANOPT_VERSION[1], \ 'The installed URBANopt is version {}.\nMust be version {} to work ' \ 'with dragonfly.\n{}'.format( '.'.join(str(v) for v in uo_version), '.'.join(str(v) for v in self.URBANOPT_VERSION), in_msg)
def _load_from_file(self, file_path): """Set all of the the properties of this object from a config JSON file. Args: file_path: Path to a JSON file containing the file paths. A sample of this JSON is the config.json file within this package. """ # check the default file path assert os.path.isfile(str(file_path)), \ ValueError('No file found at {}'.format(file_path)) # set the default paths to be all blank default_path = { "mapper_path": r'', "urbanopt_gemfile_path": r'', "urbanopt_cli_path": r'', "urbanopt_env_path": r'', "reopt_assumptions_path": r'' } with open(file_path, 'r') as cfg: try: paths = json.load(cfg) except Exception as e: print('Failed to load paths from {}.\n{}'.format(file_path, e)) else: for key, p in paths.items(): if isinstance(key, list) or not key.startswith('__'): try: default_path[key] = p.strip() except AttributeError: default_path[key] = p # set paths for the configuration self.mapper_path = default_path["mapper_path"] self.urbanopt_gemfile_path = default_path["urbanopt_gemfile_path"] self.urbanopt_cli_path = default_path["urbanopt_cli_path"] self.urbanopt_env_path = default_path["urbanopt_env_path"] self.reopt_assumptions_path = default_path["reopt_assumptions_path"] def _urbanopt_version_from_cli(self): """Set this object's URBANopt version by making a call to URBANopt CLI.""" if not self.urbanopt_env_path: self.generate_urbanopt_env_path() assert self.urbanopt_env_path is not None, 'Unable to set up the URBANopt ' \ 'environment. Make sure it is installed correctly.' if os.name == 'nt': working_drive = self.urbanopt_env_path[:2] batch = '{}\ncd {}\ncall {}\nuo --version'.format( working_drive, working_drive, self.urbanopt_env_path) batch_file = os.path.join( os.path.dirname(self.urbanopt_env_path), '.check_uo_version.bat') write_to_file(batch_file, batch, True) process = subprocess.Popen(batch_file, stdout=subprocess.PIPE, shell=True) stdout = process.communicate() else: shell = '#!/usr/bin/env bash\nsource {}\nuo --version'.format( self.urbanopt_env_path) shell_file = os.path.join( os.path.dirname(self.urbanopt_env_path), '.check_uo_version.sh') write_to_file(shell_file, shell, True) # make the shell script executable using subprocess.check_call subprocess.check_call(['chmod', 'u+x', shell_file]) # run the shell script process = subprocess.Popen(shell_file, stdout=subprocess.PIPE, shell=True) stdout = process.communicate() base_str = str(stdout[0]).split('--version')[-1] base_str = base_str.replace(r"\r", '').replace(r"\n", '').replace(r"'", '') base_str = base_str.strip() try: ver_nums = base_str.split('.') self._urbanopt_version = tuple(int(i) for i in ver_nums) self._urbanopt_version_str = base_str except Exception: pass # failed to parse the version into integers def _docker_version_from_cli(self): """Set this object's Docker version by making a call to Docker CLI.""" cmds = ['docker', '--version'] use_shell = True if os.name == 'nt' else False process = subprocess.Popen(cmds, stdout=subprocess.PIPE, shell=use_shell) stdout = process.communicate() base_str = str(stdout[0]).replace("b'", '').replace(r"\n'", '') try: self._docker_version_str = base_str.split(',')[0].split(' ')[-1] ver_nums = self._docker_version_str.split('.') self._docker_version = tuple(int(i) for i in ver_nums) except Exception: pass # failed to parse the version into integers @staticmethod def _find_mapper_path(): """Find the mapper that is distributed with the honeybee-openstudio-gem.""" measure_install = hb_energy_config.folders.honeybee_openstudio_gem_path if measure_install: mapper_file = os.path.join(measure_install, 'files', 'Honeybee.rb') if os.path.isfile(mapper_file): return mapper_file return None @staticmethod def _find_urbanopt_gemfile_path(): """Find the URBANopt Gemfile that's distributed with honeybee-openstudio-gem.""" measure_install = hb_energy_config.folders.honeybee_openstudio_gem_path if measure_install: gem_file = os.path.join(measure_install, 'files', 'urbanopt_Gemfile') if os.path.isfile(gem_file): return gem_file return None @staticmethod def _find_reopt_assumptions_path(): """Find the REopt assumptions that's distributed with honeybee-openstudio-gem.""" measure_install = hb_energy_config.folders.honeybee_openstudio_gem_path if measure_install: reopt_file = os.path.join(measure_install, 'files', 'reopt_assumptions.json') if os.path.isfile(reopt_file): return reopt_file return None @staticmethod def _find_urbanopt_cli_path(): """Find the most recent URBANopt CLI in its default location.""" def getversion(urbanopt_path): """Get digits for the version of OpenStudio.""" try: ver = ''.join(s for s in urbanopt_path if (s.isdigit() or s == '.')) return sum(int(d) * (10 ** i) for i, d in enumerate(reversed(ver.split('.')))) except ValueError: # folder starting with 'openstudio' and no version return 0 if os.name == 'nt': # search the C:/ drive on Windows uo_folders = ['C:\\{}'.format(f) for f in os.listdir('C:\\') if (f.lower().startswith('urbanopt') and os.path.isdir('C:\\{}'.format(f)))] elif platform.system() == 'Darwin': # search the Applications folder on Mac uo_folders = \ ['/Applications/{}'.format(f) for f in os.listdir('/Applications/') if (f.lower().startswith('urbanopt') and os.path.isdir('/Applications/{}'.format(f)))] elif platform.system() == 'Linux': # search the usr/local folder uo_folders = ['/usr/local/{}'.format(f) for f in os.listdir('/usr/local/') if (f.lower().startswith('urbanopt') and os.path.isdir('/usr/local/{}'.format(f)))] else: # unknown operating system uo_folders = None if not uo_folders: # No Openstudio installations were found return None # get the most recent version of OpenStudio that was found uo_path = sorted(uo_folders, key=getversion, reverse=True)[0] return uo_path
"""Object possesing all key folders within the configuration.""" folders = Folders(mute=True)