<commit_before>from __future__ import print_function
import os
import sys
import glob
from git import Repo
import subprocess as sp
import requests
import locale

LOCAL_REPO_DIR = os.getenv("GGD_LOCAL", os.path.expanduser("~/.config/"))
RECIPE_REPO_DIR = os.path.join(LOCAL_REPO_DIR, "ggd-recipes")
GITHUB_URL = "https://github.com/gogetdata/ggd-recipes.git"
METADATA_REPO_DIR = os.path.join(LOCAL_REPO_DIR, "ggd-metadata")
METADATA_GITHUB_URL = "https://github.com/gogetdata/ggd-metadata"
GGD_CLI_REQUIREMENTS = "https://raw.githubusercontent.com/gogetdata/ggd-cli/master/requirements.txt"

def get_species(update_repo=True):
    """ Method to get available annotated species in the ggd repo

    get_species
    ===========
    This method is used to get a list of all available/annotated species in the
    ggd repo. It returns a list of species
    """

    if update_repo:
        update_local_repo()
    elif not os.path.isdir(LOCAL_REPO_DIR): ## If There is no local repo directory
        update_local_repo()

    genomes_dir = os.path.join(RECIPE_REPO_DIR, "genomes")
    return os.listdir(genomes_dir)

def get_ggd_channels():
    """Method used to get avaiable ggd channels

    get_ggd_channels
    ================
    This method is used to get all avaiable/created ggd conaa channels.
    This method will return a list of ggd conda channels.
    """

    recipe_dir = os.path.join(RECIPE_REPO_DIR, "recipes")
    return os.listdir(recipe_dir)

def get_channel_data(ggd_channel):
    """Method used to get channel meta data for a specific ggd channel

    get_channel_data
    ================
    This method is used to get the ggd channel's metadata json file.

    Parameters:
    -----------
    1) ggd_channel: The ggd channel to get metadata for

    Returns:
    +++++++
    1) The file path to the metadata file for the specifc channel
    """

    update_metadata_local_repo()
    channeldata_path = os.path.join(METADATA_REPO_DIR, "channeldata", ggd_channel, "channeldata.json")
    return (channeldata_path)

def get_channeldata_url(ggd_channel):
    """Method used to get a url for the metadata json file for a specific ggd channel

    get_channeldata_url
    ===================
    This method is used to get a url for a specific metadata json file for a specific ggd conda channel

    Parameters:
    -----------
    1) ggd_channel: The ggd channel to get the metadata json file url for

    Returns:
    ++++++++
    1) The url for the metadata file
    """

    return(os.path.join("https://raw.githubusercontent.com/gogetdata/ggd-metadata/master/channeldata", ggd_channel,
            "channeldata.json"))

def get_required_conda_version():
    """Method to get the conda version from the ggd-cli requirements file

    get_required_conda_version
    ==========================
    This method is used to get the required version for conda based on the version set in the
     requiremetns file in ggd-cli. This version can be used to mantain the correct version while
     using ggd

    Return:
    +++++++
    1) The required version if found, else -1
    """

    req = requests.get(GGD_CLI_REQUIREMENTS, stream=True)

    conda_version = -1
    for line in req.iter_lines():
        if "conda=" in str(line.decode()):
            conda_version = str(line.decode()).strip().split("=")[1]
    return(conda_version)

def check_output(args, **kwargs):
    """Method to get a byte converted string from a subprocess command """

    return _to_str(sp.check_output(args, **kwargs).strip())

def _to_str(s, enc=locale.getpreferredencoding()):
    """Method to convert a bytes into a string based on a local prefered encoding

    _to_str
    =======
    This method is used to decode a bytes stream into a string based on the location/regional
    preference. It returns the converted string.
    """

    if isinstance(s, bytes):
        return s.decode(enc)
    return s

def get_builds(species):
    """
    Method to get the annotated/available genome builds for a species within ggd
    """

    update_local_repo()
    species_dir = os.path.join(RECIPE_REPO_DIR, "genomes", species)

    if species == "*":
        paths = glob.glob(species_dir)
        builds = []
        for path in paths:
            builds.extend(os.listdir(path))
        return builds
    else:
        if os.path.isdir(species_dir):
            return os.listdir(species_dir)

def update_metadata_local_repo():
    """Method to update the ggd-metadata local repo

    update_metadata_local_repo()
    ============================
    This method is used to update the ggd-metadata local repo. This local repo is used
     to get information about the metadata for ggd recipes. This method updates the local
     repo with any changes that have occured since the last update.
    """

    if not os.path.isdir(LOCAL_REPO_DIR):
        os.makedirs(LOCAL_REPO_DIR)
    if not os.path.isdir(METADATA_REPO_DIR):
        Repo.clone_from(METADATA_GITHUB_URL, METADATA_REPO_DIR)
    Repo(METADATA_REPO_DIR).remotes.origin.pull()

def update_local_repo():
    """Method to update the local ggd-recipes repo

    update_local_repo
    =================
    This method is used to update the a local version of the ggd-recipe repo. This local
     ggd-recipe repo is used to access information about the repo. This method updates the
     local repo with any changes made to the repo since the last update.
    """

    if not os.path.isdir(LOCAL_REPO_DIR):
        os.makedirs(LOCAL_REPO_DIR)
    if not os.path.isdir(RECIPE_REPO_DIR):
        Repo.clone_from(GITHUB_URL, RECIPE_REPO_DIR)
    Repo(RECIPE_REPO_DIR).remotes.origin.pull()

def validate_build(build, species):
    """
    Method to validate that a genome-build is correclty assigned based on a species.
    """
    if build != "*":
        builds_list = get_builds(species)
        if not builds_list or build not in builds_list:
            if species != "*":
                print("Unknown build '%s' for species '%s'" % (build, species), file=sys.stderr)
            else:
                print("Unknown build '%s'" % (build), file=sys.stderr)
            if (builds_list):
                print("Available builds: '%s'" % ("', '".join(builds_list)), file=sys.stderr)
            return False
    return True

def conda_root():
    """ Method used to get the conda root

    conda_root
    ==========
    This method is used to get the conda root dir. A string representing the conda root dir path
    is returned.
    """

    croot = check_output(['conda', 'info', '--root'])
    conda_env, conda_path = get_conda_env()
    if conda_env != "base":
        croot = conda_path
    return(croot)

def get_conda_env():
    """Method used to get the current conda environment

    get_conda_env
    =============
    This method is used to get the current conda environment used to access the
     ggd environment variables created for this specific environment.

    Returns:
    ++++++++
    1) The conda environment name
    2) The path to the conda environent
    """

    env_info = check_output(["conda", "info", "--envs"])
    fields = env_info.split("\n")
    curr_env = ""
    for field in fields:
        if len(field) > 0 and field[0] != "#":
            env = field.split()
            if len(env) > 0 and "*" in env:
                return env[0],env[-1]
    print("Error in checking conda environment. Verify that conda is working and try again.", file=sys.stderr)
    exit()

def active_conda_env():
    """Method used to get the active conda environmnet

    active_conda_env
    ================
    This method is used to get the active conda environment. A string representing the active environment
    is retunred.
    """

    environment_list = sp.check_output(['conda', 'info', '--env']).decode("utf8").strip().split("\n")
    active_environment = "base"
    for environment in environment_list:
        if "*" in environment_list:
            active_environment = env.split(" ")[0]
    return(active_environment)

def bypass_satsolver_on_install(pkg_name, conda_channel="ggd-genomics",debug=False):
    """Method to bypass the sat solver used by conda when a cached recipe is being installed

    bypass_satsolver_on_install
    ============================
    This method is used to run the conda install steps to install a ggd aws cahced reicpe. The
        intsallation will skip the sat solver step, ignore packages that may be additionaly installed
        or uninstalled, and performs other steps in order to install the data package without using
        the sat solver.
    The majority of the work is still done by conda through the use of the conda module. This method
        should only be used when a cached recipe is being installed.

    Parameters:
    -----------
    1) pkg_name: The name of the ggd package to install. (Example: hg19-gaps)
    2) conda_channel: The ggd conda channel that package is being installed from. (Example: ggd-genomics)
    """

    #-------------------------------------------------------------------------
    # import statments
    #-------------------------------------------------------------------------
    from conda.base.context import context
    from conda.cli import common
    from conda.cli import install
    from conda.core.solve import Solver
    from conda.core.solve import SolverStateContainer
    from conda.common.io import Spinner
    from conda.core.link import PrefixSetup
    from conda.core.link import UnlinkLinkTransaction
    from argparse import Namespace
    from conda._vendor.boltons.setutils import IndexedSet
    from conda.models.prefix_graph import PrefixGraph
    from conda.core.solve import diff_for_unlink_link_precs
    from conda.common.compat import iteritems, itervalues, odict, text_type
    from conda._vendor.toolz import concat, concatv
    from conda.resolve import Resolve
    from conda.models.match_spec import MatchSpec
    from conda.common.io import ProgressBar
    from conda.gateways.logging import set_all_logger_level, set_conda_log_level
    from conda.gateways.logging import VERBOSITY_LEVELS
    from conda.gateways.logging import log
    from logging import DEBUG, ERROR, Filter, Formatter, INFO, StreamHandler, WARN, getLogger
    import sys

    print("\n\t-> Installing %s from the %s conda channel\n" %(pkg_name, conda_channel))

    #-------------------------------------------------------------------------
    # Nested functions
    #-------------------------------------------------------------------------
    def bypass_sat(package_name,ssc_object): ## Package_name will be used as a key
        """Method used to extract information during sat solving, but to bypass the sat solving step

        bypass_sat
        ==========
        This method is used to extract and process information that would have been done during the sat
        solvering step, (Solving Enviroment), bypass the sat solver, and return a filtered set of packages
        to install.

        Parameters:
        -----------
        1) package_name: The name of the package to extract. (This is the package that will be installed)
        2) ssc_object: A processed conda SolverStateContainer object.

        Returns:
        +++++++
        1) The updated ssc object based off the sat bypass and package filtering.

        """

        ## From Solver.run_sat
        specs_map_set = set(itervalues(ssc_object.specs_map))

        ## Get the specs from ssc filtered by the package name
        final_environment_specs = IndexedSet(concatv(itervalues(odict([(package_name,ssc_object.specs_map[package_name])])), ssc_object.track_features_specs, ssc_object.pinned_specs))

        ## Run the resolve process and get info for desired package
        ssc_object.solution_precs = ssc_object.r.solve(tuple(final_environment_specs))

        ## Filter ssc.solution_precs
        wanted_indices = []
        for i, info in enumerate(ssc_object.solution_precs):
            if package_name in ssc_object.solution_precs[i].namekey:
                wanted_indices.append(i)

        filtered_ssc_solution_precs = [ssc_object.solution_precs[x] for x in wanted_indices]
        ssc_object.solution_precs = filtered_ssc_solution_precs

        ## Add the final environment specs to ssc
        ssc_object.final_environment_specs = final_environment_specs

        return(ssc_object)

    #-------------------------------------------------------------------------
    # Run install
    #-------------------------------------------------------------------------

    ## Set the context.always_yes to True to bypass user input
    context.always_yes = True

    # Setup solver object
    solve = Solver(context.target_prefix, (conda_channel,u'default'), context.subdirs, [pkg_name])

    ## Create a solver state container
    ssc = SolverStateContainer(prefix=context.target_prefix, update_modifier=context.update_modifier,
                               deps_modifier=context.deps_modifier, prune=True, ignore_pinned=context.ignore_pinned,
                               force_remove=context.force_remove, should_retry_solve=False)

    ## Get channel metadata
    with Spinner("Collecting package metadata", not context.verbosity and not context.quiet, context.json):
        ssc = solve._collect_all_metadata(ssc)

    ## Set specs map to an empty map. (No need to check other specs)
    add_spec = []
    for p_name, spec  in iteritems(ssc.specs_map):
        if str(p_name) == pkg_name:
            add_spec.append((pkg_name, MatchSpec(pkg_name)))

    ssc.specs_map = odict(add_spec)

    ## Process the data in the solver state container
    with Spinner("Processing data", not context.verbosity and not context.quiet, context.json):
        ssc = solve._add_specs(ssc)
        ssc = bypass_sat(pkg_name, ssc)
        ssc = solve._post_sat_handling(ssc)

    ## create an IndexedSet from ssc.solution_precs
    ssc.solution_precs = IndexedSet(PrefixGraph(ssc.solution_precs).graph)

    ## Get linked and unlinked
    unlink_precs, link_precs = diff_for_unlink_link_precs(context.target_prefix, ssc.solution_precs, solve.specs_to_add)

    #set unlinked to empty indexed set so we do not unlink/remove any pacakges
    unlink_precs = IndexedSet()

    ## Create a PrefixSetup
    stp = PrefixSetup(solve.prefix, unlink_precs, link_precs, solve.specs_to_remove, solve.specs_to_add)

    ## Create an UnlinkLinkTransaction with stp
    unlink_link_transaction = UnlinkLinkTransaction(stp)

    #create Namespace
    args = Namespace(channel=None, cmd="install", deps_modifier=context.deps_modifier, json=False, packages=[pkg_name])

    ## Set logger level
    if debug:
        WARN, INFO, DEBUG, TRACE = VERBOSITY_LEVELS
        set_all_logger_level(DEBUG)

    ## Install package
    install.handle_txn(unlink_link_transaction, solve.prefix, args, False)

    ## Retrun True if finished
    return(True)

if __name__ == "__main__":
    import doctest
    doctest.testmod()

<commit_msg>Update PrefixSetup with new positional argument 'neutered_specs'<commit_after>from __future__ import print_function
import os
import sys
import glob
from git import Repo
import subprocess as sp
import requests
import locale

LOCAL_REPO_DIR = os.getenv("GGD_LOCAL", os.path.expanduser("~/.config/"))
RECIPE_REPO_DIR = os.path.join(LOCAL_REPO_DIR, "ggd-recipes")
GITHUB_URL = "https://github.com/gogetdata/ggd-recipes.git"
METADATA_REPO_DIR = os.path.join(LOCAL_REPO_DIR, "ggd-metadata")
METADATA_GITHUB_URL = "https://github.com/gogetdata/ggd-metadata"
GGD_CLI_REQUIREMENTS = "https://raw.githubusercontent.com/gogetdata/ggd-cli/master/requirements.txt"

def get_species(update_repo=True):
    """ Method to get available annotated species in the ggd repo

    get_species
    ===========
    This method is used to get a list of all available/annotated species in the
    ggd repo. It returns a list of species
    """

    if update_repo:
        update_local_repo()
    elif not os.path.isdir(LOCAL_REPO_DIR): ## If There is no local repo directory
        update_local_repo()

    genomes_dir = os.path.join(RECIPE_REPO_DIR, "genomes")
    return os.listdir(genomes_dir)

def get_ggd_channels():
    """Method used to get avaiable ggd channels

    get_ggd_channels
    ================
    This method is used to get all avaiable/created ggd conaa channels.
    This method will return a list of ggd conda channels.
    """

    recipe_dir = os.path.join(RECIPE_REPO_DIR, "recipes")
    return os.listdir(recipe_dir)

def get_channel_data(ggd_channel):
    """Method used to get channel meta data for a specific ggd channel

    get_channel_data
    ================
    This method is used to get the ggd channel's metadata json file.

    Parameters:
    -----------
    1) ggd_channel: The ggd channel to get metadata for

    Returns:
    +++++++
    1) The file path to the metadata file for the specifc channel
    """

    update_metadata_local_repo()
    channeldata_path = os.path.join(METADATA_REPO_DIR, "channeldata", ggd_channel, "channeldata.json")
    return (channeldata_path)

def get_channeldata_url(ggd_channel):
    """Method used to get a url for the metadata json file for a specific ggd channel

    get_channeldata_url
    ===================
    This method is used to get a url for a specific metadata json file for a specific ggd conda channel

    Parameters:
    -----------
    1) ggd_channel: The ggd channel to get the metadata json file url for

    Returns:
    ++++++++
    1) The url for the metadata file
    """

    return(os.path.join("https://raw.githubusercontent.com/gogetdata/ggd-metadata/master/channeldata", ggd_channel,
            "channeldata.json"))

def get_required_conda_version():
    """Method to get the conda version from the ggd-cli requirements file

    get_required_conda_version
    ==========================
    This method is used to get the required version for conda based on the version set in the
     requiremetns file in ggd-cli. This version can be used to mantain the correct version while
     using ggd

    Return:
    +++++++
    1) The required version if found, else -1
    """

    req = requests.get(GGD_CLI_REQUIREMENTS, stream=True)

    conda_version = -1
    for line in req.iter_lines():
        if "conda=" in str(line.decode()):
            conda_version = str(line.decode()).strip().split("=")[1]
    return(conda_version)

def check_output(args, **kwargs):
    """Method to get a byte converted string from a subprocess command """

    return _to_str(sp.check_output(args, **kwargs).strip())

def _to_str(s, enc=locale.getpreferredencoding()):
    """Method to convert a bytes into a string based on a local prefered encoding

    _to_str
    =======
    This method is used to decode a bytes stream into a string based on the location/regional
    preference. It returns the converted string.
    """

    if isinstance(s, bytes):
        return s.decode(enc)
    return s

def get_builds(species):
    """
    Method to get the annotated/available genome builds for a species within ggd
    """

    update_local_repo()
    species_dir = os.path.join(RECIPE_REPO_DIR, "genomes", species)

    if species == "*":
        paths = glob.glob(species_dir)
        builds = []
        for path in paths:
            builds.extend(os.listdir(path))
        return builds
    else:
        if os.path.isdir(species_dir):
            return os.listdir(species_dir)

def update_metadata_local_repo():
    """Method to update the ggd-metadata local repo

    update_metadata_local_repo()
    ============================
    This method is used to update the ggd-metadata local repo. This local repo is used
     to get information about the metadata for ggd recipes. This method updates the local
     repo with any changes that have occured since the last update.
    """

    if not os.path.isdir(LOCAL_REPO_DIR):
        os.makedirs(LOCAL_REPO_DIR)
    if not os.path.isdir(METADATA_REPO_DIR):
        Repo.clone_from(METADATA_GITHUB_URL, METADATA_REPO_DIR)
    Repo(METADATA_REPO_DIR).remotes.origin.pull()

def update_local_repo():
    """Method to update the local ggd-recipes repo

    update_local_repo
    =================
    This method is used to update the a local version of the ggd-recipe repo. This local
     ggd-recipe repo is used to access information about the repo. This method updates the
     local repo with any changes made to the repo since the last update.
    """

    if not os.path.isdir(LOCAL_REPO_DIR):
        os.makedirs(LOCAL_REPO_DIR)
    if not os.path.isdir(RECIPE_REPO_DIR):
        Repo.clone_from(GITHUB_URL, RECIPE_REPO_DIR)
    Repo(RECIPE_REPO_DIR).remotes.origin.pull()

def validate_build(build, species):
    """
    Method to validate that a genome-build is correclty assigned based on a species.
    """
    if build != "*":
        builds_list = get_builds(species)
        if not builds_list or build not in builds_list:
            if species != "*":
                print("Unknown build '%s' for species '%s'" % (build, species), file=sys.stderr)
            else:
                print("Unknown build '%s'" % (build), file=sys.stderr)
            if (builds_list):
                print("Available builds: '%s'" % ("', '".join(builds_list)), file=sys.stderr)
            return False
    return True

def conda_root():
    """ Method used to get the conda root

    conda_root
    ==========
    This method is used to get the conda root dir. A string representing the conda root dir path
    is returned.
    """

    croot = check_output(['conda', 'info', '--root'])
    conda_env, conda_path = get_conda_env()
    if conda_env != "base":
        croot = conda_path
    return(croot)

def get_conda_env():
    """Method used to get the current conda environment

    get_conda_env
    =============
    This method is used to get the current conda environment used to access the
     ggd environment variables created for this specific environment.

    Returns:
    ++++++++
    1) The conda environment name
    2) The path to the conda environent
    """

    env_info = check_output(["conda", "info", "--envs"])
    fields = env_info.split("\n")
    curr_env = ""
    for field in fields:
        if len(field) > 0 and field[0] != "#":
            env = field.split()
            if len(env) > 0 and "*" in env:
                return env[0],env[-1]
    print("Error in checking conda environment. Verify that conda is working and try again.", file=sys.stderr)
    exit()

def active_conda_env():
    """Method used to get the active conda environmnet

    active_conda_env
    ================
    This method is used to get the active conda environment. A string representing the active environment
    is retunred.
    """

    environment_list = sp.check_output(['conda', 'info', '--env']).decode("utf8").strip().split("\n")
    active_environment = "base"
    for environment in environment_list:
        if "*" in environment_list:
            active_environment = env.split(" ")[0]
    return(active_environment)

def bypass_satsolver_on_install(pkg_name, conda_channel="ggd-genomics",debug=False):
    """Method to bypass the sat solver used by conda when a cached recipe is being installed

    bypass_satsolver_on_install
    ============================
    This method is used to run the conda install steps to install a ggd aws cahced reicpe. The
        intsallation will skip the sat solver step, ignore packages that may be additionaly installed
        or uninstalled, and performs other steps in order to install the data package without using
        the sat solver.
    The majority of the work is still done by conda through the use of the conda module. This method
        should only be used when a cached recipe is being installed.

    Parameters:
    -----------
    1) pkg_name: The name of the ggd package to install. (Example: hg19-gaps)
    2) conda_channel: The ggd conda channel that package is being installed from. (Example: ggd-genomics)
    """

    #-------------------------------------------------------------------------
    # import statments
    #-------------------------------------------------------------------------
    from conda.base.context import context
    from conda.cli import common
    from conda.cli import install
    from conda.core.solve import Solver
    from conda.core.solve import SolverStateContainer
    from conda.common.io import Spinner
    from conda.core.link import PrefixSetup
    from conda.core.link import UnlinkLinkTransaction
    from argparse import Namespace
    from conda._vendor.boltons.setutils import IndexedSet
    from conda.models.prefix_graph import PrefixGraph
    from conda.core.solve import diff_for_unlink_link_precs
    from conda.common.compat import iteritems, itervalues, odict, text_type
    from conda._vendor.toolz import concat, concatv
    from conda.resolve import Resolve
    from conda.models.match_spec import MatchSpec
    from conda.common.io import ProgressBar
    from conda.gateways.logging import set_all_logger_level, set_conda_log_level
    from conda.gateways.logging import VERBOSITY_LEVELS
    from conda.gateways.logging import log
    from logging import DEBUG, ERROR, Filter, Formatter, INFO, StreamHandler, WARN, getLogger
    import sys

    print("\n\t-> Installing %s from the %s conda channel\n" %(pkg_name, conda_channel))

    #-------------------------------------------------------------------------
    # Nested functions
    #-------------------------------------------------------------------------
    def bypass_sat(package_name,ssc_object): ## Package_name will be used as a key
        """Method used to extract information during sat solving, but to bypass the sat solving step

        bypass_sat
        ==========
        This method is used to extract and process information that would have been done during the sat
        solvering step, (Solving Enviroment), bypass the sat solver, and return a filtered set of packages
        to install.

        Parameters:
        -----------
        1) package_name: The name of the package to extract. (This is the package that will be installed)
        2) ssc_object: A processed conda SolverStateContainer object.

        Returns:
        +++++++
        1) The updated ssc object based off the sat bypass and package filtering.

        """

        ## From Solver.run_sat
        specs_map_set = set(itervalues(ssc_object.specs_map))

        ## Get the specs from ssc filtered by the package name
        final_environment_specs = IndexedSet(concatv(itervalues(odict([(package_name,ssc_object.specs_map[package_name])])), ssc_object.track_features_specs, ssc_object.pinned_specs))

        ## Run the resolve process and get info for desired package
        ssc_object.solution_precs = ssc_object.r.solve(tuple(final_environment_specs))

        ## Filter ssc.solution_precs
        wanted_indices = []
        for i, info in enumerate(ssc_object.solution_precs):
            if package_name in ssc_object.solution_precs[i].namekey:
                wanted_indices.append(i)

        filtered_ssc_solution_precs = [ssc_object.solution_precs[x] for x in wanted_indices]
        ssc_object.solution_precs = filtered_ssc_solution_precs

        ## Add the final environment specs to ssc
        ssc_object.final_environment_specs = final_environment_specs

        return(ssc_object)

    #-------------------------------------------------------------------------
    # Run install
    #-------------------------------------------------------------------------

    ## Set the context.always_yes to True to bypass user input
    context.always_yes = True

    # Setup solver object
    solve = Solver(context.target_prefix, (conda_channel,u'default'), context.subdirs, [pkg_name])

    ## Create a solver state container
    ssc = SolverStateContainer(prefix=context.target_prefix, update_modifier=context.update_modifier,
                               deps_modifier=context.deps_modifier, prune=True, ignore_pinned=context.ignore_pinned,
                               force_remove=context.force_remove, should_retry_solve=False)

    ## Get channel metadata
    with Spinner("Collecting package metadata", not context.verbosity and not context.quiet, context.json):
        ssc = solve._collect_all_metadata(ssc)

    ## Set specs map to an empty map. (No need to check other specs)
    add_spec = []
    for p_name, spec  in iteritems(ssc.specs_map):
        if str(p_name) == pkg_name:
            add_spec.append((pkg_name, MatchSpec(pkg_name)))

    ssc.specs_map = odict(add_spec)

    ## Process the data in the solver state container
    with Spinner("Processing data", not context.verbosity and not context.quiet, context.json):
        ssc = solve._add_specs(ssc)
        ssc = bypass_sat(pkg_name, ssc)
        ssc = solve._post_sat_handling(ssc)

    ## create an IndexedSet from ssc.solution_precs
    ssc.solution_precs = IndexedSet(PrefixGraph(ssc.solution_precs).graph)

    ## Get linked and unlinked
    unlink_precs, link_precs = diff_for_unlink_link_precs(context.target_prefix, ssc.solution_precs, solve.specs_to_add)

    #set unlinked to empty indexed set so we do not unlink/remove any pacakges
    unlink_precs = IndexedSet()

    ## Create a PrefixSetup
    stp = PrefixSetup(solve.prefix, unlink_precs, link_precs, solve.specs_to_remove, solve.specs_to_add, solve.neutered_specs)

    ## Create an UnlinkLinkTransaction with stp
    unlink_link_transaction = UnlinkLinkTransaction(stp)

    #create Namespace
    args = Namespace(channel=None, cmd="install", deps_modifier=context.deps_modifier, json=False, packages=[pkg_name])

    ## Set logger level
    if debug:
        WARN, INFO, DEBUG, TRACE = VERBOSITY_LEVELS
        set_all_logger_level(DEBUG)

    ## Install package
    install.handle_txn(unlink_link_transaction, solve.prefix, args, False)

    ## Retrun True if finished
    return(True)

if __name__ == "__main__":
    import doctest
    doctest.testmod()

<|endoftext|>


<commit_before>'''
- länge =< 10
- gross geschrieben =< 1
- keine sonderzeichen
'''

def checkio(data):
    lower = 0
    upper = 0
    spezial = 0
    if len(data) < 10:
        return False
    for c in data:
        if c.islower():
            lower += 1
        elif c.isupper():
            upper += 1
        try:
            c.encode('ascii');
        except UnicodeEncodeError:
            print("False")
        else:
            print("True")

    print(lower + upper + spezial)

    #replace this for solution
    return True or False

#Some hints
#Just check all conditions

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert checkio('A1213pokl') == False, "1st example"
    assert checkio('bAse730onE4') == True, "2nd example"
    assert checkio('asasasasasasasaas') == False, "3rd example"
    assert checkio('QWERTYqwerty') == False, "4th example"
    assert checkio('123456123456') == False, "5th example"
    assert checkio('QwErTy911poqqqq') == True, "6th example"
    print("Coding complete? Click 'Check' to review your tests and earn cool rewards!")
<commit_msg>Solved the puzzel<commit_after>'''
- länge =< 10
- gross geschrieben
- klein geschrieben
- enthält nummern
- keine sonderzeichen

'''

def checkio(data):
    lower = 0
    upper = 0
    digit = 0

    if len(data) < 10: # Check the length
        return False
    for c in data:
        if c.islower(): # Check lower case
            lower += 1
        elif c.isupper(): # Check upper case
            upper += 1
        elif c.isdigit(): # Check digit
            digit += 1
        try: # Check if it is ascii
            c.encode('ascii');
        except UnicodeEncodeError:
            return False

    if (lower >= 1 and upper >= 1 and digit >= 1): # test the condition
        return True
    else:
        return False

#Some hints
#Just check all conditions

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert checkio('A1213pokl') == False, "1st example"
    assert checkio('bAse730onE4') == True, "2nd example"
    assert checkio('asasasasasasasaas') == False, "3rd example"
    assert checkio('QWERTYqwerty') == False, "4th example"
    assert checkio('123456123456') == False, "5th example"
    assert checkio('QwErTy911poqqqq') == True, "6th example"
    print("Coding complete? Click 'Check' to review your tests and earn cool rewards!")
<|endoftext|>


<commit_before>import thread
import time

class System(object):
    """
    Description of System.
    """

    def __init__(self, variables, schemas):
        self.__variables = variables
        self.__schemas = schemas

    def setUp(self, mission):
        """
        Responsible for appropriately configuring and launching the system,
        for a given mission.
        """
        raise NotImplementedError

    def tearDown(self, mission):
        """
        Responsible for safely closing the system, following the execution of
        a given mission.
        """
        raise NotImplementedError

    def execute(self, mission):
        self.setUp(mission)
        # TODO: do these need to be separated?
        self.executeActions(mission)

    def executeActions(self, mission):
        self.getInternalState().dump()
        for action in mission.getActions():
            self.getInternalState().dump()
            actionType = action.get_type()
            print actionType
            self.getInternalState().dump()
            # check for preconditions
            if self.__schemas[actionType].satisfiedPreconditions(self.__variables,action.get_values()) and \
                self.__schemas[actionType].satisfiedInvariants(self.__variables,action.get_values()):
                self.__schemas[actionType].dispatch(action.get_values())
                while not self.__schemas[actionType].satisfiedPostConditions(self.__variables,
                    action.get_values()):
                    time.sleep(1)
                    pass
            self.getInternalState().dump()
        self.tearDown(mission)

    def getInternalState(self):
        """
        Returns a description of the current internal state of the system.

        TODO: ensure that the system is actually running!
        """
        vals = {n: v.read() for (n, v) in self.__variables.items()}
        return InternalState(vals)

class State(object):
    """
    Describes the internal or external state of the system in terms of its
    internal or external variables.
    """

    def __init__(self, values):
        """
        Constructs a description of the system state.

        :param  values: a dictionary describing the values of the state
                        variables, indexed by their names.
        """
        self.__values = values

    def read(variable):
        """
        Returns the value for a given state variable
        """
        return self.__values[variable]

    def dump(self):
        """
        Prints this state to the standard output.
        """
        for variable in self.__values:
            print 'Variable: {} - State: {}'.format(variable, self.__values[variable])

class InternalState(State):
    """
    Describes the state of the system in terms of its internal state
    variables.
    """
    pass

class ExternalState(State):
    """
    Describes the state of the system in terms of its external state
    variables.
    """
    pass

class StateVariable(object):

    def __init__(self, name, getter):
        """
        Constructs a new state variable

        :param  name:   the name of this variable
        :param  type:   the type of this variable
        :param  getter: a lambda function, used to obtain the value of this variable
        """
        self.__name = name
        self.__getter = getter

    """
    Returns the name of this system variable
    """
    def name(self):
        return self.__name

    """
    Inspects the current state of this system variable
    """
    def read(self):
        return self.__getter()

class InternalStateVariable(StateVariable):
    """
    Internal variables describe the internal state of a given system.
    (i.e., they represent a system's knowledge of itself and its surroundings.)
    A user-provided lambda function is used to inspect the value of the state
    variable at any given time.
    """

    def __init__(self, name, getter):
        super(InternalStateVariable, self).__init__(name, getter)

class Environment(object):
    """
    Holds a description of an environment in which a mission should be conducted.
    """

    def __init__(self, values):
        """
        Constructs a description of a mission environment.

        :param  values: a dictionary of environment variable values, indexed
                        by the name of those variables.
        """
        self.__values = values

    def read(self, variable):
        """
        Returns the value of a given environment variable.
        """
        return self.__values[variable]

class Mission(object):
    """
    A mission is represented as a sequence of actions that are carried out in
    a given environment and initial state.
    """

    @staticmethod
    def fromJSON(jsn):
        """
        Constructs a mission object from a given JSON description.
        """
        assert('environment' in jsn)
        assert('internal' in jsn)
        assert('external' in jsn)
        assert('actions' in jsn)
        assert(isinstance(actions, list))

        env = Environment.fromJSON(jsn['environment'])
        internal = InternalState.fromJSON(jsn['internal'])
        external = ExternalState.fromJSON(jsn['external'])
        actions = [a.fromJSON() for a in actions]

        return Mission(env, internal, external, actions)

    def __init__(self, environment, internal, external, actions):
        """
        Constructs a new Mission description.

        :param  environment:    a description of the environment
        :param  internal:       a description of the initial internal state
        :param  external:       a description of the initial external state
        :param  actions:        a list of actions
        """
        assert(actions != [])
        assert(isinstance(environment, Environment) and not environment is None)
        assert(isinstance(internal, InternalState) and not internal is None)
        assert(isinstance(external, ExternalState) and not external is None)

        self.__environment = environment
        self.__internal = internal
        self.__external = external
        self.__actions = actions

    # TODO: make immutable
    def getEnvironment(self):
        return self.__environment

    # TODO: make immutable
    def getInitialInternalState(self):
        return self.__internal

    # TODO: make immutable
    def getInitialExternalState(self):
        return self.__external

    # TODO: make immutable
    def getActions(self):
        # TODO: returning the original list might be dangerous? We may want to
        #       pass a copy, instead.
        return self.__actions

    def toJSON(self):
        """
        Returns a JSON description of this mission.
        """
        return {
            'environment': self.__environment.toJSON(),
            'internal': self.__internal.toJSON(),
            'external': self.__external.toJSON(),
            'actions': [a.toJSON() for a in self.__actions]
        }

class Action(object):
    @staticmethod
    def fromJSON(jsn):
        """
        Constructs an Action object from its JSON description.
        """
        assert('kind' in jsn)
        assert('parameters' in jsn)
        return Action(jsn['kind'], jsn['parameters'])

    def __init__(self, kind, values):
        """
        Constructs an Action description.

        :param  kind    the name of the schema to which the action belongs
        :param  values  a dictionary of parameter values for the action
        """
        assert(isinstance(kind, str))
        assert(isinstance(values, dict))
        self.__kind = kind
        self.__values = values

    def getKind(self):
        return self.__kind

    def getValue(self, value):
        return self.__values[value]

    def getValues(self):
        return self.__values

    def toJSON(self):
        """
        Returns a JSON description of a given Action.
        """
        return {
            'kind': self.__kind,
            'parameters': self.__values
        }

class ActionSchema(object):
    """
    Action schemas are responsible for describing the kinds of actions that
    can be performed within a given system. Action schemas describe actions
    both syntactically, in terms of parameters, and semantically, in terms of
    preconditions, postconditions, and invariants.
    """

    def __init__(self, name, parameters, precondition, invariants, postconditions):
        self.__name           = name
        self.__parameters     = parameters
        self.__preconditions   = precondition
        self.__invariants     = invariants
        self.__postconditions = postconditions

    def dispatch(self, action):
        """
        Responsible for invoking an action belonging to this schema.

        :param  action  an Action belonging to this schema
        """
        raise UnimplementedError

    def satisfiedPostConditions(self, system_variables, parameters):
        print 'Doing postconditions params: {} '.format(parameters)
        print all(p.check(system_variables, parameters) for p in self.__postconditions)
        return all(p.check(system_variables, parameters) for p in self.__postconditions)

    def satisfiedPreconditions(self, system_variables, parameters):
        print 'Doing precondition params: {} '.format(parameters)
        print all(p.check(system_variables, parameters) for p in self.__preconditions)
        return all(p.check(system_variables, parameters) for p in self.__preconditions)

    def satisfiedInvariants(self, system_variables, parameters):
        return all(p.check(system_variables, parameters) for p in self.__invariants)

"""
Hello.
"""
class Predicate(object):

    def __init__(self, name, predicate):
        self.__name = name
        self.__predicate = predicate

    def check(self, system_variables, parameters):
        return self.__predicate(system_variables, parameters)

"""
Hello.
"""
class Invariant(Predicate):
    def __init__(self, name, description, predicate):
        super(Invariant, self).__init__(name, predicate)
        self.__name = name
        self.__description = description

"""
Hello.
"""
class Postcondition(Predicate):
    def __init__(self, name, description, predicate):
        super(Postcondition, self).__init__(name, predicate)
        self.__name = name
        self.__description = description

"""
Hello.
"""
class Precondition(Predicate):
    def __init__(self, name, description, predicate):
        super(Precondition, self).__init__(name, predicate)
        self.__name = name
        self.__description = description

"""
Hello.
"""
class Parameter(object):
    """docstring for ."""
    def __init__(self, typ, value, description):
        self.__type = typ
        self.__value = value
        self._description = description

    def get_value():
        return self.__value
<commit_msg>added Environment JSON methods<commit_after>import thread
import time

class System(object):
    """
    Description of System.
    """

    def __init__(self, variables, schemas):
        self.__variables = variables
        self.__schemas = schemas

    def setUp(self, mission):
        """
        Responsible for appropriately configuring and launching the system,
        for a given mission.
        """
        raise NotImplementedError

    def tearDown(self, mission):
        """
        Responsible for safely closing the system, following the execution of
        a given mission.
        """
        raise NotImplementedError

    def execute(self, mission):
        self.setUp(mission)
        # TODO: do these need to be separated?
        self.executeActions(mission)

    def executeActions(self, mission):
        self.getInternalState().dump()
        for action in mission.getActions():
            self.getInternalState().dump()
            actionType = action.get_type()
            print actionType
            self.getInternalState().dump()
            # check for preconditions
            if self.__schemas[actionType].satisfiedPreconditions(self.__variables,action.get_values()) and \
                self.__schemas[actionType].satisfiedInvariants(self.__variables,action.get_values()):
                self.__schemas[actionType].dispatch(action.get_values())
                while not self.__schemas[actionType].satisfiedPostConditions(self.__variables,
                    action.get_values()):
                    time.sleep(1)
                    pass
            self.getInternalState().dump()
        self.tearDown(mission)

    def getInternalState(self):
        """
        Returns a description of the current internal state of the system.

        TODO: ensure that the system is actually running!
        """
        vals = {n: v.read() for (n, v) in self.__variables.items()}
        return InternalState(vals)

class State(object):
    """
    Describes the internal or external state of the system in terms of its
    internal or external variables.
    """

    def __init__(self, values):
        """
        Constructs a description of the system state.

        :param  values: a dictionary describing the values of the state
                        variables, indexed by their names.
        """
        self.__values = values

    def read(variable):
        """
        Returns the value for a given state variable
        """
        return self.__values[variable]

    def dump(self):
        """
        Prints this state to the standard output.
        """
        for variable in self.__values:
            print 'Variable: {} - State: {}'.format(variable, self.__values[variable])

class InternalState(State):
    """
    Describes the state of the system in terms of its internal state
    variables.
    """
    pass

class ExternalState(State):
    """
    Describes the state of the system in terms of its external state
    variables.
    """
    pass

class StateVariable(object):

    def __init__(self, name, getter):
        """
        Constructs a new state variable

        :param  name:   the name of this variable
        :param  type:   the type of this variable
        :param  getter: a lambda function, used to obtain the value of this variable
        """
        self.__name = name
        self.__getter = getter

    """
    Returns the name of this system variable
    """
    def name(self):
        return self.__name

    """
    Inspects the current state of this system variable
    """
    def read(self):
        return self.__getter()

class InternalStateVariable(StateVariable):
    """
    Internal variables describe the internal state of a given system.
    (i.e., they represent a system's knowledge of itself and its surroundings.)
    A user-provided lambda function is used to inspect the value of the state
    variable at any given time.
    """

    def __init__(self, name, getter):
        super(InternalStateVariable, self).__init__(name, getter)

class Environment(object):
    @staticmethod
    def fromJSON(jsn):
        """
        Constructs a description of an environment from its JSON description
        """
        assert('variables' in jsn)
        assert(isinstance(jsn['variables'], dict))
        return Enviroment(jsn['variables'])

    """
    Holds a description of an environment in which a mission should be conducted.
    """

    def __init__(self, values):
        """
        Constructs a description of a mission environment.

        :param  values: a dictionary of environment variable values, indexed
                        by the name of those variables.
        """
        self.__values = values

    def read(self, variable):
        """
        Returns the value of a given environment variable.
        """
        return self.__values[variable]

    def toJSON(self):
        """
        Returns this environment description as a JSON object (i.e., a dict)
        """
        return {
            'variables': self.__values
        }

class Mission(object):
    """
    A mission is represented as a sequence of actions that are carried out in
    a given environment and initial state.
    """

    @staticmethod
    def fromJSON(jsn):
        """
        Constructs a mission object from a given JSON description.
        """
        assert('environment' in jsn)
        assert('internal' in jsn)
        assert('external' in jsn)
        assert('actions' in jsn)
        assert(isinstance(actions, list))

        env = Environment.fromJSON(jsn['environment'])
        internal = InternalState.fromJSON(jsn['internal'])
        external = ExternalState.fromJSON(jsn['external'])
        actions = [a.fromJSON() for a in actions]

        return Mission(env, internal, external, actions)

    def __init__(self, environment, internal, external, actions):
        """
        Constructs a new Mission description.

        :param  environment:    a description of the environment
        :param  internal:       a description of the initial internal state
        :param  external:       a description of the initial external state
        :param  actions:        a list of actions
        """
        assert(actions != [])
        assert(isinstance(environment, Environment) and not environment is None)
        assert(isinstance(internal, InternalState) and not internal is None)
        assert(isinstance(external, ExternalState) and not external is None)

        self.__environment = environment
        self.__internal = internal
        self.__external = external
        self.__actions = actions

    # TODO: make immutable
    def getEnvironment(self):
        return self.__environment

    # TODO: make immutable
    def getInitialInternalState(self):
        return self.__internal

    # TODO: make immutable
    def getInitialExternalState(self):
        return self.__external

    # TODO: make immutable
    def getActions(self):
        # TODO: returning the original list might be dangerous? We may want to
        #       pass a copy, instead.
        return self.__actions

    def toJSON(self):
        """
        Returns a JSON description of this mission.
        """
        return {
            'environment': self.__environment.toJSON(),
            'internal': self.__internal.toJSON(),
            'external': self.__external.toJSON(),
            'actions': [a.toJSON() for a in self.__actions]
        }

class Action(object):
    @staticmethod
    def fromJSON(jsn):
        """
        Constructs an Action object from its JSON description.
        """
        assert('kind' in jsn)
        assert('parameters' in jsn)
        return Action(jsn['kind'], jsn['parameters'])

    def __init__(self, kind, values):
        """
        Constructs an Action description.

        :param  kind    the name of the schema to which the action belongs
        :param  values  a dictionary of parameter values for the action
        """
        assert(isinstance(kind, str))
        assert(isinstance(values, dict))
        self.__kind = kind
        self.__values = values

    def getKind(self):
        return self.__kind

    def getValue(self, value):
        return self.__values[value]

    def getValues(self):
        return self.__values

    def toJSON(self):
        """
        Returns a JSON description of a given Action.
        """
        return {
            'kind': self.__kind,
            'parameters': self.__values
        }

class ActionSchema(object):
    """
    Action schemas are responsible for describing the kinds of actions that
    can be performed within a given system. Action schemas describe actions
    both syntactically, in terms of parameters, and semantically, in terms of
    preconditions, postconditions, and invariants.
    """

    def __init__(self, name, parameters, precondition, invariants, postconditions):
        self.__name           = name
        self.__parameters     = parameters
        self.__preconditions   = precondition
        self.__invariants     = invariants
        self.__postconditions = postconditions

    def dispatch(self, action):
        """
        Responsible for invoking an action belonging to this schema.

        :param  action  an Action belonging to this schema
        """
        raise UnimplementedError

    def satisfiedPostConditions(self, system_variables, parameters):
        print 'Doing postconditions params: {} '.format(parameters)
        print all(p.check(system_variables, parameters) for p in self.__postconditions)
        return all(p.check(system_variables, parameters) for p in self.__postconditions)

    def satisfiedPreconditions(self, system_variables, parameters):
        print 'Doing precondition params: {} '.format(parameters)
        print all(p.check(system_variables, parameters) for p in self.__preconditions)
        return all(p.check(system_variables, parameters) for p in self.__preconditions)

    def satisfiedInvariants(self, system_variables, parameters):
        return all(p.check(system_variables, parameters) for p in self.__invariants)

"""
Hello.
"""
class Predicate(object):

    def __init__(self, name, predicate):
        self.__name = name
        self.__predicate = predicate

    def check(self, system_variables, parameters):
        return self.__predicate(system_variables, parameters)

"""
Hello.
"""
class Invariant(Predicate):
    def __init__(self, name, description, predicate):
        super(Invariant, self).__init__(name, predicate)
        self.__name = name
        self.__description = description

"""
Hello.
"""
class Postcondition(Predicate):
    def __init__(self, name, description, predicate):
        super(Postcondition, self).__init__(name, predicate)
        self.__name = name
        self.__description = description

"""
Hello.
"""
class Precondition(Predicate):
    def __init__(self, name, description, predicate):
        super(Precondition, self).__init__(name, predicate)
        self.__name = name
        self.__description = description

"""
Hello.
"""
class Parameter(object):
    """docstring for ."""
    def __init__(self, typ, value, description):
        self.__type = typ
        self.__value = value
        self._description = description

    def get_value():
        return self.__value
<|endoftext|>


<commit_before>import os
import shutil
import logging

try:
    from bcbio.install import _set_matplotlib_default_backend
    _set_matplotlib_default_backend()
except OSError:
    pass
import matplotlib
matplotlib.use('Agg', force=True)

from libs.read import load_data
from libs.report import make_profile
from libs.utils import safe_dirs
from db import make_database
import templates

logger = logging.getLogger('report')

def report(args):
    """
    Create report in html format
    """
    logger.info("reading sequeces")
    data = load_data(args.json)
    out_dir = os.path.join(args.out, "html")
    safe_dirs(out_dir)

    logger.info("create profile")
    data = make_profile(data, out_dir, args)
    logger.info("create database")
    make_database(data, "seqcluster.db", args.out)

    path_template = os.path.normpath(os.path.dirname(os.path.realpath(templates.__file__)))
    css_template = os.path.join(path_template, "info.css")
    js_template = os.path.join(path_template, "jquery.tablesorter.min.js")
    css = os.path.join(out_dir, "info.css")
    js = os.path.join(out_dir, "jquery.tablesorter.min.js")
    if not os.path.exists(css):
        shutil.copy(css_template, css)
        shutil.copy(js_template, js)
    logger.info("Done. Download https://github.com/lpantano/seqclusterViz/archive/master.zip to browse the output.")
<commit_msg>Ignore non bcbio isntallation<commit_after>import os
import shutil
import logging

try:
    from bcbio.install import _set_matplotlib_default_backend
    _set_matplotlib_default_backend()
except (ImportError, OSError):
    pass
import matplotlib
matplotlib.use('Agg', force=True)

from libs.read import load_data
from libs.report import make_profile
from libs.utils import safe_dirs
from db import make_database
import templates

logger = logging.getLogger('report')

def report(args):
    """
    Create report in html format
    """
    logger.info("reading sequeces")
    data = load_data(args.json)
    out_dir = os.path.join(args.out, "html")
    safe_dirs(out_dir)

    logger.info("create profile")
    data = make_profile(data, out_dir, args)
    logger.info("create database")
    make_database(data, "seqcluster.db", args.out)

    path_template = os.path.normpath(os.path.dirname(os.path.realpath(templates.__file__)))
    css_template = os.path.join(path_template, "info.css")
    js_template = os.path.join(path_template, "jquery.tablesorter.min.js")
    css = os.path.join(out_dir, "info.css")
    js = os.path.join(out_dir, "jquery.tablesorter.min.js")
    if not os.path.exists(css):
        shutil.copy(css_template, css)
        shutil.copy(js_template, js)
    logger.info("Done. Download https://github.com/lpantano/seqclusterViz/archive/master.zip to browse the output.")
<|endoftext|>


<commit_before># TODO: rename `OutcomeBranch` to `Branch`
import copy
import json
import state
import mission
import timeit
import signal
import math

from util import TimeoutError

class System(object):
    """
    Description of System.
    """

    def __init__(self, identifier, variables, schemas):
        """
        Constructs a new System.

        :param  identifier: a unique string identifier (i.e., a name) for this\
                            system
        :param  variables:  a dictionary of system variables, indexed by name
        :param  schemas:    a dictionary of action schemas, indexed by name
        """
        assert (isinstance(identifier, str) and not None)
        assert (isinstance(variables, dict) and not None)
        self.__identifier = identifier
        self.__variables = variables
        self.__schemas = schemas

    def installed(self):
        """
        Returns true if this system is installed on this machine.
        """
        raise NotImplementedError

    def getIdentifier(self):
        """
        Returns the unique identifier (i.e., the name) for this system.
        """
        return self.__identifier

    def getBranches(self):
        """
        Returns a list of the branches for this system.
        """
        return [b for s in s.__schemas for b in s.getBranches()]

    def getBranch(self, iden):
        """
        Returns an outcome branch for this sytem provided its identifier.
        """
        assert (isinstance(iden, BranchID) and iden is not None)
        schema = self.__schemas[iden.getActionName()]
        return schema.getBranch(iden)

    def setUp(self, mission):
        """
        Responsible for appropriately configuring and launching the system,
        for a given mission.
        """
        raise NotImplementedError

    def tearDown(self, mission):
        """
        Responsible for safely closing the system, following the execution of
        a given mission.
        """
        raise NotImplementedError

    def execute(self, msn):
        """
        Executes a given mission.

        :param  msn:    the mission that should be executed.

        :return A summary of the outcome of the mission, in the form of a
                MissionOutcome
        """
        assert (self.installed())
        timeBeforeSetup = timeit.default_timer()
        self.setUp(msn)
        totalSetupTime = timeit.default_timer() - timeBeforeSetup

        env = msn.getEnvironment()
        outcomes = []

        try:

            # execute each action in sequence
            for action in msn.getActions():
                schema = self.__schemas[action.getSchemaName()]

                # compute expected state
                initialState = self.getState()

                # resolve the branch and compute the expected outcome
                branch = schema.resolveBranch(action, initialState, env)
                expected = branch.computeExpectedState(action, initialState, env)

                # enforce a timeout
                timeout = schema.computeTimeout(action, initialState, env)
                signal.signal(signal.SIGALRM, lambda signum, frame: TimeoutError.produce())
                signal.alarm(int(math.ceil(timeout)))

                timeBefore = timeit.default_timer()

                try:
                    schema.dispatch(action, initialState, env)
                except TimeoutError:
                    pass
                finally:
                    signal.alarm(0) # does this reset the alarm?

                timeAfter = timeit.default_timer()
                timeElapsed = timeAfter - timeBefore

                print('Doing: {}'.format(action.getSchemaName()))

                # compare the observed and expected states
                observed = self.getState()
                passed = expected.isExpected(self.__variables, observed)
                outcome = mission.ActionOutcome(action, passed, initialState, observed, timeElapsed, branch.getID())
                outcomes.append(outcome)

                if not passed:
                    totalTime = timeit.default_timer() - timeBeforeSetup
                    return mission.MissionOutcome(False, outcomes, totalSetupTime, \
                                                                        totalTime)
            totalTime = timeit.default_timer() - timeBeforeSetup
            return mission.MissionOutcome(True, outcomes, totalSetupTime, totalTime)

        finally:
            self.tearDown(msn)

    def getState(self):
        """
        Returns a description of the current state of the system.

        TODO: ensure that the system is actually running!
        """
        assert (self.installed())
        vals = {n: v.read() for (n, v) in self.__variables.items()}
        return state.State(vals)

    def getActionSchemas(self):
        """
        Returns a copy of action schemas
        """
        return copy.deepcopy(self.__schemas)

class OutcomeBranch(object):
    def __init__(self, name, schema, estimators):
        """
        Constructs a new outcome branch.

        :param  name:           the name of this branch.
        :param  schema:         the action schema to which this outcome \
                                branch belongs, given as an ActionSchema \
                                instance.
        :param  estimators:     a list of state estimators for this branch.
        """
        assert (isinstance(estimators, list) and estimators is not None)
        assert (all(isinstance(e, state.Estimator) for e in estimators))

        self.__effects = {e.getVariableName(): e for e in estimators}
        assert (isinstance(self.__effects, dict) and self.__effects is not None)

        assert (isinstance(name, str))
        assert (name is not None)
        assert (name is not "")
        self.__name = name

        assert (isinstance(schema, ActionSchema) and schema is not None)
        self.__schema = schema

    def getSchema(self):
        """
        Returns the action schema to which this outcome branch belongs.
        """
        return self.__schema

    def getSchemaName(self):
        """
        Returns the name of the schema to which this outcome branch belongs.
        """
        return self.getSchema().getName()

    def getName(self):
        """
        Returns the name of this branch.
        """
        return self.__name

    def getID(self):
        """
        Returns the identifier for this branch.
        """
        return BranchID(self.__schema, self.__name)

    def generate(self, initialState, env):
        """
        Generates an action that would cause the system to take this branch.

        :param  initialState:   the state of the system immediately before \
                                executing the generated action.
        :param  env:            the environment in which the mission will be \
                                conducted.
        """
        raise NotImplementedError

    def isSatisfiable(self, initialState, env):
        """
        Determines whether there exists a set of parameter values that would
        satisify this precondition given a fixed initial state and
        environment.
        """
        raise NotImplementedError

    def isApplicable(self, action, initialState, env):
        """
        Determines whether the guard for this outcome branch is satisfied by
        the parameters for the action, the state of the system immediately
        prior to the execution of the action, and the state of the environment.

        :param  action:         a description of the action that is about to \
                                be performed
        :param  initialState:   the state of the system immediately prior to \
                                the execution of the action
        :param  env:            the environment in which the action is being \
                                performed

        :returns    True if the guard is satisfied by the given context, \
                    otherwise False.
        """
        raise NotImplementedError

    def computeTimeout(self, action, state, environment):
        """
        Computes the timeout for the current branch.
        """
        raise NotImplementedError

    def computeExpectedState(self, action, initialState, env):
        """
        Produces an estimate of the system state following the execution of
        this branch within a given context.

        :param  action:         a description of the action being performed
        :param  initialState:   the state of the system immediately prior to \
                                the execution of this action
        :param  env:            the environment in which the action is being \
                                performed

        :returns    An ExpectedState object, describing the state that the \
                    system is expected to be in immediately after the \
                    execution of this branch
        """
        assert (isinstance(action, mission.Action) and action is not None)
        assert (isinstance(initialState, state.State) and state is not None)
        assert (isinstance(env, state.Environment) and env is not None)

        # store the expected state values in a dictionary, indexed by the name
        # of the associated state variable
        values = {}
        for (varName, initialValue) in initialState.getValues().items():
            if varName in self.__effects:
                expected = self.__effects[varName].computeExpectedValue(action, initialState, env)
                values[varName] = expected
            else:
                values[varName] = state.ExpectedStateValue(initialValue)

        return state.ExpectedState(values)

class IdleBranch(OutcomeBranch):
    def __init__(self, schema):
        super(IdleBranch, self).__init__("idle", schema, [])

    def computeTimeout(self, action, state, environment):
        return 1.0

    def isApplicable(self, action, state, environment):
        return True

    def isSatisfiable(self, state, environment):
        return True

    def generate(self, state, environment):
        return self.getSchema.generate()

class ActionSchema(object):
    """
    Action schemas are responsible for describing the kinds of actions that
    can be performed within a given system. Action schemas describe actions
    both syntactically, in terms of parameters, and semantically, in terms of
    preconditions, postconditions, and invariants.
    """

    def __init__(self, name, parameters, branches):
        """
        Constructs an ActionSchema object.

        :param  name            name of the action schema
        :param  parameters      a list of the parameters for this action schema
        :param  branches        a list of the possible outcomes for actions \
                                belonging to this schema. If none of the \

        """
        assert (isinstance(name, str) and not name is None)
        assert (len(name) > 0)
        assert (isinstance(parameters, list) and not parameters is None)
        assert (all(isinstance(p, mission.Parameter) for p in parameters))
        assert (isinstance(branches, list) and not branches is None)
        assert (all(isinstance(b, OutcomeBranch) for b in branches))
        assert (len(branches) > 0)

        # unique branch names
        assert (len(set(b.getName() for b in branches)) == len(branches))

        self.__name = name
        self.__parameters =  parameters
        self.__branches = branches

    def getName(self):
        """
        Returns the name of this schema.
        """
        return self.__name

    def getBranches(self):
        """
        Returns a list of the branches for this action schema.
        """
        return self.__branches[:]

    def getBranch(self, iden):
        """
        Returns a branch belonging to this action schema using its identifier.
        """
        assert (isinstance(iden, BranchID) and iden is not None)
        assert (iden.getActionName() == self.__name)
        return self.__branches[iden.getBranchName()]

    def dispatch(self, action, state, environment):
        """
        Responsible for invoking an action belonging to this schema.

        :param  action  an Action belonging to this schema
        """
        raise UnimplementedError

    def computeTimeout(self, action, state, environment):
        """
        Responsible for calculating the maximum time that this action shoud take.

        :param  action          the action that is going to be performed.
        :param  state           the state in which the system is currently on.
        :param  environment     the system environment

        :returns maximum time in seconds (as a float)
        """
        branch = self.resolveBranch(action, state, environment)
        return branch.computeTimeout(action, state, environment)

    def getParameters(self):
        """
        Returns the parameters being hold for the current action schema. This is
        used to generate actions
        """
        return copy.deepcopy(self.__parameters)

    def resolveBranch(self, action, initialState, environment):
        """
        Returns the branch that is appropiate for the current action, state, and
        environment based on the current action schema.
        """
        branch = None
        for b in self.__branches:
            if b.isApplicable(action, initialState, environment):
                return b
        raise Exception

    def generate(self):
        """
        Generates an action belonging to this schema at random.
        """
        values = {p.getName(): p.generate() for p in self.__parameters}
        return mission.Action(self.__name, values)

class ActionGenerator(object):

    def __init__(self, schemaName, parameters = []):
        """
        Constructs a new action generator.

        :param  schemaName: the name of the schema for which this generator \
                    produces actions.
        :params parameters: a list of the parameters to this generator.
        """
        assert (isinstance(schemaName, str) and schemaName is not None)
        assert (isinstance(parameters, list) and parameters is not None)

        self.__schemaName = schemaName
        self.__parameters = parameters

    def constructWithState(self, currentState, env, values):
        """
        Responsible for constructing a dictionary of Action arguments based
        on the current state of the robot, a description of the environment,
        and a dictionary of generator parameter values.

        :returns    a dictionary of arguments for the generated action
        """
        raise NotImplementedError

    def constructWithoutState(self, env, values):
        """
        Responsible for constructing a dictionary of Action arguments based
        on the current state of the robot, a description of the environment,
        and a dictionary of generator parameter values.

        :returns    a dictionary of arguments for the generated action
        """
        raise NotImplementedError

    def getSchemaName(self):
        """
        Returns the name of the schema to which actions generated by this
        generator belong.
        """
        return self.__schemaName

    def generateActionWithState(self, currentState, env):
        values = {p.getName(): p.generate() for p in self.__parameters}
        values = self.constructWithState(currentState, env, values)
        return mission.Action(self.__schemaName, values)

    def generateActionWithoutState(self, env):
        values = {p.getName(): p.generate() for p in self.__parameters}
        values = self.constructWithoutState(env, values)
        return mission.Action(self.__schemaName, values)

class BranchID(object):
    @staticmethod
    def fromJSON(jsn):
        # TODO: fix earlier string assertions!
        assert (isinstance(jsn, str) or isinstance(jsn, unicode))
        assert (jsn is not None)
        assert (jsn != '')

        (actionName, _, branchName) = jsn.partition(':')

        return BranchID(actionName, branchName)

    def __init__(self, actionName, branchName):
        assert (isinstance(actionName, str) or isinstance(actionName, unicode))
        assert (actionName is not None)
        assert (actionName is not '')
        # TODO: rules
        assert (isinstance(branchName, str) or isinstance(branchName, unicode))
        assert (branchName is not None)
        assert (branchName is not '')
        # TODO: rules

        self.__actionName = actionName
        self.__branchName = branchName

    def __eq__(self, other):
        return  self.__actionName == other.getActionName() and \
                self.__branchName == other.getBranchName()

    def getActionName(self):
        return self.__actionName

    def getBranchName(self):
        return self.__branchName

    def __str__(self):
        return "{}:{}".format(self.__actionName, self.__branchName)

    def __repr__(self):
        return str(self)

class BranchPath(object):

    def __init__(self, identifiers):
        assert (isinstance(identifiers, list) and identifiers is not None)
        assert (all(isinstance(i, BranchID) for i in identifiers))
        self.__identifiers = identifiers

    def length(self):
        """
        Returns the length of this path (measured by its number of branches).
        """
        return len(self.__identifiers)

    def getIdentifiers(self):
        """
        Returns an ordered list of identifiers for the branches along this path.
        """
        return copy.copy(self.__identifiers)

    def getBranches(self, systm):
        """
        Returns an ordered list of the branches along this path.
        """
        return [systm.getBranch(i) for i in self.__identifiers]

    def extended(self, b):
        """
        Returns a copy of this path with an additional branch attached to the
        end.

        :param  branchID:   the branch that should be added to this path, \
                            given as an identifier or a branch object
        """
        assert (b is not None)
        if isinstance(b, BranchID):
            return BranchPath(self.__identifiers + [b])
        elif isinstance(b, OutcomeBranch):
            return BranchPath(self.__identifiers + [b.getID()])
        else:
            raise Exception('BranchPath::extended expected a BranchID or OutcomeBranch object')

    def startswith(self, prefix):
        """
        Determines whether this path is prefixed by a given path. Returns True
        if this path is prefixed by the given path, otherwise False.
        """
        assert (isinstance(prefix, BranchPath) and prefix is not None)

        if prefix.length() > self.length():
            return False

        prefix = prefix.getIdentifiers()
        for i in range(len(prefix)):
            if prefix[i] != self.__identifiers[i]:
                return False

        return True

    def __eq__(self, other):
        assert (isinstance(other, BranchPath) and not BranchPath is None)
        if self.size() != other.size():
            return False
        for (x, y) in zip(self.__identifiers, other.getIdentifiers()):
            if x != y:
                return False
        return True

    def __str__(self):
        """
        Returns a string-based description of this path.
        """
        s = ', '.join([str(i) for i in self.__identifiers])
        s = '<{}>'.format(s)
        return s

    def __repr__(self):
        return str(self)
<commit_msg>fixed Branch::getID<commit_after># TODO: rename `OutcomeBranch` to `Branch`
import copy
import json
import state
import mission
import timeit
import signal
import math

from util import TimeoutError

class System(object):
    """
    Description of System.
    """

    def __init__(self, identifier, variables, schemas):
        """
        Constructs a new System.

        :param  identifier: a unique string identifier (i.e., a name) for this\
                            system
        :param  variables:  a dictionary of system variables, indexed by name
        :param  schemas:    a dictionary of action schemas, indexed by name
        """
        assert (isinstance(identifier, str) and not None)
        assert (isinstance(variables, dict) and not None)
        self.__identifier = identifier
        self.__variables = variables
        self.__schemas = schemas

    def installed(self):
        """
        Returns true if this system is installed on this machine.
        """
        raise NotImplementedError

    def getIdentifier(self):
        """
        Returns the unique identifier (i.e., the name) for this system.
        """
        return self.__identifier

    def getBranches(self):
        """
        Returns a list of the branches for this system.
        """
        return [b for s in s.__schemas for b in s.getBranches()]

    def getBranch(self, iden):
        """
        Returns an outcome branch for this sytem provided its identifier.
        """
        assert (isinstance(iden, BranchID) and iden is not None)
        schema = self.__schemas[iden.getActionName()]
        return schema.getBranch(iden)

    def setUp(self, mission):
        """
        Responsible for appropriately configuring and launching the system,
        for a given mission.
        """
        raise NotImplementedError

    def tearDown(self, mission):
        """
        Responsible for safely closing the system, following the execution of
        a given mission.
        """
        raise NotImplementedError

    def execute(self, msn):
        """
        Executes a given mission.

        :param  msn:    the mission that should be executed.

        :return A summary of the outcome of the mission, in the form of a
                MissionOutcome
        """
        assert (self.installed())
        timeBeforeSetup = timeit.default_timer()
        self.setUp(msn)
        totalSetupTime = timeit.default_timer() - timeBeforeSetup

        env = msn.getEnvironment()
        outcomes = []

        try:

            # execute each action in sequence
            for action in msn.getActions():
                schema = self.__schemas[action.getSchemaName()]

                # compute expected state
                initialState = self.getState()

                # resolve the branch and compute the expected outcome
                branch = schema.resolveBranch(action, initialState, env)
                expected = branch.computeExpectedState(action, initialState, env)

                # enforce a timeout
                timeout = schema.computeTimeout(action, initialState, env)
                signal.signal(signal.SIGALRM, lambda signum, frame: TimeoutError.produce())
                signal.alarm(int(math.ceil(timeout)))

                timeBefore = timeit.default_timer()

                try:
                    schema.dispatch(action, initialState, env)
                except TimeoutError:
                    pass
                finally:
                    signal.alarm(0) # does this reset the alarm?

                timeAfter = timeit.default_timer()
                timeElapsed = timeAfter - timeBefore

                print('Doing: {}'.format(action.getSchemaName()))

                # compare the observed and expected states
                observed = self.getState()
                passed = expected.isExpected(self.__variables, observed)
                outcome = mission.ActionOutcome(action, passed, initialState, observed, timeElapsed, branch.getID())
                outcomes.append(outcome)

                if not passed:
                    totalTime = timeit.default_timer() - timeBeforeSetup
                    return mission.MissionOutcome(False, outcomes, totalSetupTime, \
                                                                        totalTime)
            totalTime = timeit.default_timer() - timeBeforeSetup
            return mission.MissionOutcome(True, outcomes, totalSetupTime, totalTime)

        finally:
            self.tearDown(msn)

    def getState(self):
        """
        Returns a description of the current state of the system.

        TODO: ensure that the system is actually running!
        """
        assert (self.installed())
        vals = {n: v.read() for (n, v) in self.__variables.items()}
        return state.State(vals)

    def getActionSchemas(self):
        """
        Returns a copy of action schemas
        """
        return copy.deepcopy(self.__schemas)

class OutcomeBranch(object):
    def __init__(self, name, schema, estimators):
        """
        Constructs a new outcome branch.

        :param  name:           the name of this branch.
        :param  schema:         the action schema to which this outcome \
                                branch belongs, given as an ActionSchema \
                                instance.
        :param  estimators:     a list of state estimators for this branch.
        """
        assert (isinstance(estimators, list) and estimators is not None)
        assert (all(isinstance(e, state.Estimator) for e in estimators))

        self.__effects = {e.getVariableName(): e for e in estimators}
        assert (isinstance(self.__effects, dict) and self.__effects is not None)

        assert (isinstance(name, str))
        assert (name is not None)
        assert (name is not "")
        self.__name = name

        assert (isinstance(schema, ActionSchema) and schema is not None)
        self.__schema = schema

    def getSchema(self):
        """
        Returns the action schema to which this outcome branch belongs.
        """
        return self.__schema

    def getSchemaName(self):
        """
        Returns the name of the schema to which this outcome branch belongs.
        """
        return self.getSchema().getName()

    def getName(self):
        """
        Returns the name of this branch.
        """
        return self.__name

    def getID(self):
        """
        Returns the identifier for this branch.
        """
        return BranchID(self.getSchemaName(), self.__name)

    def generate(self, initialState, env):
        """
        Generates an action that would cause the system to take this branch.

        :param  initialState:   the state of the system immediately before \
                                executing the generated action.
        :param  env:            the environment in which the mission will be \
                                conducted.
        """
        raise NotImplementedError

    def isSatisfiable(self, initialState, env):
        """
        Determines whether there exists a set of parameter values that would
        satisify this precondition given a fixed initial state and
        environment.
        """
        raise NotImplementedError

    def isApplicable(self, action, initialState, env):
        """
        Determines whether the guard for this outcome branch is satisfied by
        the parameters for the action, the state of the system immediately
        prior to the execution of the action, and the state of the environment.

        :param  action:         a description of the action that is about to \
                                be performed
        :param  initialState:   the state of the system immediately prior to \
                                the execution of the action
        :param  env:            the environment in which the action is being \
                                performed

        :returns    True if the guard is satisfied by the given context, \
                    otherwise False.
        """
        raise NotImplementedError

    def computeTimeout(self, action, state, environment):
        """
        Computes the timeout for the current branch.
        """
        raise NotImplementedError

    def computeExpectedState(self, action, initialState, env):
        """
        Produces an estimate of the system state following the execution of
        this branch within a given context.

        :param  action:         a description of the action being performed
        :param  initialState:   the state of the system immediately prior to \
                                the execution of this action
        :param  env:            the environment in which the action is being \
                                performed

        :returns    An ExpectedState object, describing the state that the \
                    system is expected to be in immediately after the \
                    execution of this branch
        """
        assert (isinstance(action, mission.Action) and action is not None)
        assert (isinstance(initialState, state.State) and state is not None)
        assert (isinstance(env, state.Environment) and env is not None)

        # store the expected state values in a dictionary, indexed by the name
        # of the associated state variable
        values = {}
        for (varName, initialValue) in initialState.getValues().items():
            if varName in self.__effects:
                expected = self.__effects[varName].computeExpectedValue(action, initialState, env)
                values[varName] = expected
            else:
                values[varName] = state.ExpectedStateValue(initialValue)

        return state.ExpectedState(values)

class IdleBranch(OutcomeBranch):
    def __init__(self, schema):
        super(IdleBranch, self).__init__("idle", schema, [])

    def computeTimeout(self, action, state, environment):
        return 1.0

    def isApplicable(self, action, state, environment):
        return True

    def isSatisfiable(self, state, environment):
        return True

    def generate(self, state, environment):
        return self.getSchema.generate()

class ActionSchema(object):
    """
    Action schemas are responsible for describing the kinds of actions that
    can be performed within a given system. Action schemas describe actions
    both syntactically, in terms of parameters, and semantically, in terms of
    preconditions, postconditions, and invariants.
    """

    def __init__(self, name, parameters, branches):
        """
        Constructs an ActionSchema object.

        :param  name            name of the action schema
        :param  parameters      a list of the parameters for this action schema
        :param  branches        a list of the possible outcomes for actions \
                                belonging to this schema. If none of the \

        """
        assert (isinstance(name, str) and not name is None)
        assert (len(name) > 0)
        assert (isinstance(parameters, list) and not parameters is None)
        assert (all(isinstance(p, mission.Parameter) for p in parameters))
        assert (isinstance(branches, list) and not branches is None)
        assert (all(isinstance(b, OutcomeBranch) for b in branches))
        assert (len(branches) > 0)

        # unique branch names
        assert (len(set(b.getName() for b in branches)) == len(branches))

        self.__name = name
        self.__parameters =  parameters
        self.__branches = branches

    def getName(self):
        """
        Returns the name of this schema.
        """
        return self.__name

    def getBranches(self):
        """
        Returns a list of the branches for this action schema.
        """
        return self.__branches[:]

    def getBranch(self, iden):
        """
        Returns a branch belonging to this action schema using its identifier.
        """
        assert (isinstance(iden, BranchID) and iden is not None)
        assert (iden.getActionName() == self.__name)
        return self.__branches[iden.getBranchName()]

    def dispatch(self, action, state, environment):
        """
        Responsible for invoking an action belonging to this schema.

        :param  action  an Action belonging to this schema
        """
        raise UnimplementedError

    def computeTimeout(self, action, state, environment):
        """
        Responsible for calculating the maximum time that this action shoud take.

        :param  action          the action that is going to be performed.
        :param  state           the state in which the system is currently on.
        :param  environment     the system environment

        :returns maximum time in seconds (as a float)
        """
        branch = self.resolveBranch(action, state, environment)
        return branch.computeTimeout(action, state, environment)

    def getParameters(self):
        """
        Returns the parameters being hold for the current action schema. This is
        used to generate actions
        """
        return copy.deepcopy(self.__parameters)

    def resolveBranch(self, action, initialState, environment):
        """
        Returns the branch that is appropiate for the current action, state, and
        environment based on the current action schema.
        """
        branch = None
        for b in self.__branches:
            if b.isApplicable(action, initialState, environment):
                return b
        raise Exception

    def generate(self):
        """
        Generates an action belonging to this schema at random.
        """
        values = {p.getName(): p.generate() for p in self.__parameters}
        return mission.Action(self.__name, values)

class ActionGenerator(object):

    def __init__(self, schemaName, parameters = []):
        """
        Constructs a new action generator.

        :param  schemaName: the name of the schema for which this generator \
                    produces actions.
        :params parameters: a list of the parameters to this generator.
        """
        assert (isinstance(schemaName, str) and schemaName is not None)
        assert (isinstance(parameters, list) and parameters is not None)

        self.__schemaName = schemaName
        self.__parameters = parameters

    def constructWithState(self, currentState, env, values):
        """
        Responsible for constructing a dictionary of Action arguments based
        on the current state of the robot, a description of the environment,
        and a dictionary of generator parameter values.

        :returns    a dictionary of arguments for the generated action
        """
        raise NotImplementedError

    def constructWithoutState(self, env, values):
        """
        Responsible for constructing a dictionary of Action arguments based
        on the current state of the robot, a description of the environment,
        and a dictionary of generator parameter values.

        :returns    a dictionary of arguments for the generated action
        """
        raise NotImplementedError

    def getSchemaName(self):
        """
        Returns the name of the schema to which actions generated by this
        generator belong.
        """
        return self.__schemaName

    def generateActionWithState(self, currentState, env):
        values = {p.getName(): p.generate() for p in self.__parameters}
        values = self.constructWithState(currentState, env, values)
        return mission.Action(self.__schemaName, values)

    def generateActionWithoutState(self, env):
        values = {p.getName(): p.generate() for p in self.__parameters}
        values = self.constructWithoutState(env, values)
        return mission.Action(self.__schemaName, values)

class BranchID(object):
    @staticmethod
    def fromJSON(jsn):
        # TODO: fix earlier string assertions!
        assert (isinstance(jsn, str) or isinstance(jsn, unicode))
        assert (jsn is not None)
        assert (jsn != '')

        (actionName, _, branchName) = jsn.partition(':')

        return BranchID(actionName, branchName)

    def __init__(self, actionName, branchName):
        assert (isinstance(actionName, str) or isinstance(actionName, unicode))
        assert (actionName is not None)
        assert (actionName is not '')
        # TODO: rules
        assert (isinstance(branchName, str) or isinstance(branchName, unicode))
        assert (branchName is not None)
        assert (branchName is not '')
        # TODO: rules

        self.__actionName = actionName
        self.__branchName = branchName

    def __eq__(self, other):
        return  self.__actionName == other.getActionName() and \
                self.__branchName == other.getBranchName()

    def getActionName(self):
        return self.__actionName

    def getBranchName(self):
        return self.__branchName

    def __str__(self):
        return "{}:{}".format(self.__actionName, self.__branchName)

    def __repr__(self):
        return str(self)

class BranchPath(object):
    def __init__(self, identifiers):
        assert (isinstance(identifiers, list) and identifiers is not None)
        assert (all(isinstance(i, BranchID) for i in identifiers))
        self.__identifiers = identifiers

    def length(self):
        """
        Returns the length of this path (measured by its number of branches).
        """
        return len(self.__identifiers)

    def getIdentifiers(self):
        """
        Returns an ordered list of identifiers for the branches along this path.
        """
        return copy.copy(self.__identifiers)

    def getBranches(self, systm):
        """
        Returns an ordered list of the branches along this path.
        """
        return [systm.getBranch(i) for i in self.__identifiers]

    def extended(self, b):
        """
        Returns a copy of this path with an additional branch attached to the
        end.

        :param  branchID:   the branch that should be added to this path, \
                            given as an identifier or a branch object
        """
        assert (b is not None)
        if isinstance(b, BranchID):
            return BranchPath(self.__identifiers + [b])
        elif isinstance(b, OutcomeBranch):
            return BranchPath(self.__identifiers + [b.getID()])
        else:
            raise Exception('BranchPath::extended expected a BranchID or OutcomeBranch object')

    def startswith(self, prefix):
        """
        Determines whether this path is prefixed by a given path. Returns True
        if this path is prefixed by the given path, otherwise False.
        """
        assert (isinstance(prefix, BranchPath) and prefix is not None)

        if prefix.length() > self.length():
            return False

        prefix = prefix.getIdentifiers()
        for i in range(len(prefix)):
            if prefix[i] != self.__identifiers[i]:
                return False

        return True

    def __eq__(self, other):
        assert (isinstance(other, BranchPath) and not BranchPath is None)
        if self.size() != other.size():
            return False
        for (x, y) in zip(self.__identifiers, other.getIdentifiers()):
            if x != y:
                return False
        return True

    def __str__(self):
        """
        Returns a string-based description of this path.
        """
        s = ', '.join([str(i) for i in self.__identifiers])
        s = '<{}>'.format(s)
        return s

    def __repr__(self):
        return str(self)
<|endoftext|>