def slicer(iterable, width):
    """
        Slices iterables into chunks. How does itertools not have this function.
    """
    if len(iterable) % width is not 0:
        raise Exception('Iterable length must be divisable by the width')
    for start in range(0, len(iterable), width):
        yield iterable[start:start + width]


def render_git_describe_long(pieces):
    """TAG-DISTANCE-gHEX[-dirty].

    Like 'git describe --tags --dirty --always -long'.
    The distance/hash is unconditional.

    Exceptions:
    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        rendered += '-%d-g%s' % (pieces['distance'], pieces['short'])
    else:
        rendered = pieces['short']
    if pieces['dirty']:
        rendered += '-dirty'
    return rendered


def get_model_field(schema, field):
    """Get the model field of a schema field

    :param Schema schema: a marshmallow schema
    :param str field: the name of the schema field
    :return str: the name of the field in the model
    """
    if schema._declared_fields.get(field) is None:
        raise Exception('{} has no attribute {}'.format(schema.__name__, field)
            )
    if schema._declared_fields[field].attribute is not None:
        return schema._declared_fields[field].attribute
    return field


@staticmethod
def of_size(exact=None, at_least=None, at_most=None):
    """Confuse pylint so it doesn't complain about fluent-endpoints."""


@classmethod
def request(cls, method, url, headers, params, data, timeout, proxies,
    verify, max_retries):
    """
        Main method to be implemented by HTTP clients.

        The returned data structure has the following fields:
        * `content`: string containing the response from the server
        * `status_code`: HTTP status code returned by the server

        Can raise the following exceptions:
        * `ClientError`: server cannot be contacted
        * `HttpTimeout`: connection timed out
        * `HTTPError`: unexpected HTTP response code
        """
    raise NotImplementedError(u'Must be implemented by HTTPClient subclasses.')


@classmethod
def command(cls, *args, **kwargs):
    """
        Creates a new command attached to the function.
        """
    return cls.add_meta_deco({'type': 'command', 'args': args, 'kwargs':
        kwargs})


def julianDay(year, month, day):
    """Convert a date to Julian

    :param int `year`: the year
    :param int `month`: the month
    :param int `day`: the day

    :returns: the julian date number

    """
    b = 0
    if month > 12:
        year = year + month // 12
        month = month % 12
    elif month < 1:
        month = -month
        year = year - month // 12 - 1
        month = 12 - month % 12
    if year > 0:
        yearCorr = 0
    else:
        yearCorr = 3
    if month < 3:
        year = year - 1
        month = month + 12
    if year * 10000 + month * 100 + day > 15821014:
        b = 2 - year // 100 + year // 400
    return (1461 * year - yearCorr) // 4 + 306001 * (month + 1
        ) // 10000 + day + 1720994 + b


@classmethod
def PopContext(cls):
    """Discard the currently active namespace context, restoring its
        predecessor.

        The discarded context is returned."""
    return cls.__ContextStack.pop()


def cryptomatte_layer_add():
    """Add a new input layer to a Cryptomatte node

    """
    pass


@staticmethod
def get_path_to_node(node):
    """
        Return the path from the root to the node.

        Parameters
        ----------
        node : OptNode

        Returns
        -------
        list of OptNode

        """
    rev_path = [node]
    while node.parent is not None:
        rev_path.append(node.parent)
        node = node.parent
    return list(reversed(rev_path))


def basename():
    """
        The tail component of this path.
        """


def _as_list(v):
    """Returns given object wrapped in list if not already"""
    if not isinstance(v, list):
        return [v]
    return v


def app_template_install(overwrite: bool=True, filepath: str='',
    filter_folder: bool=True, filter_glob: str='*.zip'):
    """Install an application-template

    :param overwrite: Overwrite, Remove existing template with the same ID
    :type overwrite: bool
    :param filepath: filepath
    :type filepath: str
    :param filter_folder: Filter folders
    :type filter_folder: bool
    :param filter_glob: filter_glob
    :type filter_glob: str
    """
    pass


def scale_num(val, in_low, in_high, out_low, out_high):
    """
    Scales a number linearly.

    Parameters
    ----------
    - val : int or float

        The value to be scaled.
    - in_low : int or float

        Minimum of input range.
    - in_high : int or float

        Maximum of input range.
    - out_low : int or float

        Minimum of output range.
    - out_high : int or float

        Maximum of output range.

    Returns
    -------
    int or float

        The scaled number.
    """
    return (val - in_low) * (out_high - out_low) / (in_high - in_low) + out_low


def dbh_dataguzzler(path, contenttype, extension, roottag, nsurl):
    """ Generic Dataguzzler Handler - Returns db_dataguzzler for all dataguzzler files """
    if extension.lower() == 'dgs' or extension.lower(
        ) == 'dgz' or extension.lower() == 'dga' or extension.lower() == 'dgd':
        return 'db_dataguzzler_data_file'
    else:
        return False


def coarse_pos_e(tags):
    """
	Coarse POS tags of Treebank corpus:
		N: Noun, V: Verb, A: Adjective, D: Adverb, Z: Pronoun, T: Determiner, E: Preposition, P: Postposition, U: Number, J: Conjunction, O: Punctuation, R: Residual, L: Classifier, I: Interjection

	>>> coarse_pos_e(['Nasp---', 'pers', 'prop'])
	'N'
	"""
    map = {'N': 'N', 'V': 'V', 'A': 'AJ', 'D': 'ADV', 'Z': 'PRO', 'T':
        'DET', 'E': 'P', 'P': 'POSTP', 'U': 'NUM', 'J': 'CONJ', 'O': 'PUNC',
        'R': 'RES', 'L': 'CL', 'I': 'INT'}
    try:
        if tags[0][0] == 'C':
            if 'pronominal' in tags:
                tags[0] = 'Z'
            elif 'verb' in tags:
                tags[0] = 'V'
            elif 'prep' in tags:
                tags[0] = 'E'
            elif 'adv' in tags:
                tags[0] = 'D'
            elif 'det' in tags:
                tags[0] = 'T'
        return map[tags[0][0]] + ('e' if 'ezafe' in tags else '')
    except Exception:
        return ''


def get_n_neighborhood_start_stop_indices_3D(volume_shape, point, n):
    """
    Compute the start and stop indices along the 3 dimensions for the n-neighborhood of the point within the 3D volume.

    Note that this returns an index range where the end is *non-inclusive*! So for a point at x,y,z with 0-neighborhood (only the point itself), you will get x,x+1,y,y+1,z,z+1 as return values.

    Parameters
    ----------
    volume_shape: 3-tuple of int
        The shape of the volume (e.g., whole 3D image)

    point: 1D array of length 3
        The x, y, and z coordinates of the query point. Must lie within the volume. This is the point around which the neighborhood will be computed.

    n: int >= 0
        The neighborhood size (in every direction, the neighborhood is always square). For 0, only the index of the point itself will be returned. For 1, the 26 neighbors in distance 1 plus the index of the point itself (so 27 indices) will be returned. If the point is close to the border of the volume, only the valid subset will be returned of course. For n=2 you get (up to) 125 indices.

    Returns
    -------
    xstart: int
            The x start index, inclusive.

    xend: int
            The x end index, exclusive.

    ystart: int
            The y start index, inclusive.

    yend: int
            The y end index, exclusive.

    zstart: int
            The z start index, inclusive.

    zend: int
            The z end index, exclusive.

    Examples
    --------
    >>> volume = np.zeros((3, 3, 3))
    >>> point = [2, 2, 2]
    >>> xstart, xend, ystart, yend, zstart, zend = st.get_n_neighborhood_start_stop_indices_3D(volume.shape, point, 1)    # 1-neighborhood
    """
    vx = volume_shape[0]
    vy = volume_shape[1]
    vz = volume_shape[2]
    xstart = max(0, point[0] - n)
    xend = min(point[0] + 1 + n, vx)
    ystart = max(0, point[1] - n)
    yend = min(point[1] + 1 + n, vy)
    zstart = max(0, point[2] - n)
    zend = min(point[2] + 1 + n, vz)
    return xstart, xend, ystart, yend, zstart, zend


def plot_confusion_matrix(conf_matrix, classes=None, normalize=False, title
    ='Confusion Matrix', cmap=None, print_raw_matrix=False, fig_size=(5, 5),
    show_help=False):
    """Check if Normalization Option is Set to True. If so, normalize the raw confusion matrix before visualizing
    #Other code should be equivalent to your previous function.
    Note: Taken from bs_ds and modified"""
    import itertools
    import numpy as np
    import matplotlib.pyplot as plt
    cm = conf_matrix
    if cmap == None:
        cmap = plt.get_cmap('Blues')
    fmt = '.2f' if normalize else 'd'
    fontDict = {'title': {'fontsize': 16, 'fontweight': 'semibold', 'ha':
        'center'}, 'xlabel': {'fontsize': 14, 'fontweight': 'normal'},
        'ylabel': {'fontsize': 14, 'fontweight': 'normal'}, 'xtick_labels':
        {'fontsize': 10, 'fontweight': 'normal', 'rotation': 45, 'ha':
        'right'}, 'ytick_labels': {'fontsize': 10, 'fontweight': 'normal',
        'rotation': 0, 'ha': 'right'}, 'data_labels': {'ha': 'center',
        'fontweight': 'semibold'}}
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, (np.newaxis)]
    fig, ax = plt.subplots(figsize=fig_size)
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title, **fontDict['title'])
    plt.colorbar()
    if classes is None:
        classes = ['negative', 'positive']
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, **fontDict['xtick_labels'])
    plt.yticks(tick_marks, classes, **fontDict['ytick_labels'])
    thresh = cm.max() / 2.0
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt), color='darkgray', **fontDict[
            'data_labels'])
    plt.tight_layout()
    plt.ylabel('True label', **fontDict['ylabel'])
    plt.xlabel('Predicted label', **fontDict['xlabel'])
    fig = plt.gcf()
    plt.show()
    if print_raw_matrix:
        print_title = 'Raw Confusion Matrix Counts:'
        print('\n', print_title)
        print(conf_matrix)
    if show_help:
        print(
            """For binary classifications:
        [[0,0(true_neg),  0,1(false_pos)]
        [1,0(false_neg), 1,1(true_pos)] ]

        to get vals as vars:
        >>  tn,fp,fn,tp=confusion_matrix(y_test,y_hat_test).ravel()
                """
            )
    return fig


def grant_permission_to_role_no_inherit(permission_id, role_id):
    """Assign the role to the principal without local inherit."""


def recursive_get(dict_, key):
    """Get nested dictionary keys.

    >>> recursive_get({"a": {"b": {"c": "d"}}}, "a.b.c")
    "d"

    Returns None if there are type conflicts.
    """
    keys = key.split('.')
    last = keys.pop(-1)
    dict_ = dict_.copy()
    for key in keys:
        value = dict_.get(key)
        if not isinstance(value, dict):
            return
        dict_ = value
    return dict_.get(last)


def update_plugin_data(dashboard_entry, request=None):
    """Update plugin data of a dashboard entry."""
    if dashboard_entry:
        plugin = dashboard_entry.get_plugin(request=request)
        if plugin:
            return plugin._update_plugin_data(dashboard_entry)


def set_blob_access_tier(ase, timeout=None):
    """Set blob access tier
    :param blobxfer.models.azure.StorageEntity ase: Azure StorageEntity
    :param int timeout: timeout
    """
    ase.client.set_standard_blob_tier(container_name=ase.container,
        blob_name=ase.name, standard_blob_tier=ase.access_tier, timeout=timeout
        )


@classmethod
def response_to_attribute(cls, instance, api_response):
    """
        Convert the API response into attribute of the given instance.

        Example
            If the API response gives us

            ::

                {
                    "hello": "world",
                    "world": "hello"
                }

            this method set the attribute so that they become accessible with
            :code:`instance.hello` :code:`instance.world`.

            .. note::
                The :code:`hello` index (for exampel) is always accessible
                through the following methods

                - :code:`instance.hello`
                - :code:`instance["hello"]`

        :param obj instance: The instance we have to initiate the attribute into.
        :param dict api_response: The API response.
        """
    if isinstance(api_response, dict):
        for key, value in api_response.items():
            setattr(instance, key, value)
    return instance


def translate(context, source_text):
    """ Helper function to translate a source text within a context. """
    return source_text


def add_custom_options(parser):
    """
    This method can modify the ``parser`` object (which is an
    ``optparse.OptionParser`` instance).  This can be used to add new
    options to the command.
    """
    pass


@classmethod
def request(cls, *n, **kw):
    """
        Perform a REST request and parse the response.
        """
    return cls.IMPL.request(*n, **kw)


def _bulk_update_query_part(model, conn, key_fds, upd_fds, where):
    """
    Forms bulk update query part without values, counting that all keys and values are already in vals table
    :param model: Model to update, a subclass of django.db.models.Model
    :param conn: Database connection used
    :param key_fds: Field names, by which items would be selected (tuple)
    :param upd_fds: FieldDescriptor objects to update
    :param where: A sql, params tuple to filter query data before update
    :return: A tuple of sql and it's parameters
    """
    query = """
        UPDATE %s AS t SET %s
        FROM "vals"
        WHERE %s
    """
    db_table = model._meta.db_table
    where_items = []
    for fd in key_fds:
        table_field = '"t"."%s"' % fd.get_field(model).column
        prefixed_sel_field = '"vals"."%s"' % fd.prefixed_name
        where_items.append(fd.key_operator.get_sql(table_field,
            prefixed_sel_field))
    where_sql = ' AND '.join(where_items)
    where_params = []
    if where[0]:
        where_sql = '(%s) AND (%s)' % (where_sql, where[0])
        where_params.extend(where[1])
    set_items, set_params = [], []
    for fd in upd_fds:
        func_sql, params = fd.set_function.get_sql(fd.get_field(model),
            '"vals"."%s"' % fd.prefixed_name, conn, val_as_param=False)
        set_items.append(func_sql)
        set_params.extend(params)
    set_sql = ', '.join(set_items)
    query = query % ('"%s"' % db_table, set_sql, where_sql)
    return query, set_params + where_params


def sublist_accumulator_iterator(iterable):
    """ From a list (1, 2, 3, ...) this generator yields (), (1,), (1, 2), (1, 2, 3), ... """
    for i in range(len(iterable) + 1):
        yield iterable[:i]


def to_filename(name):
    """Convert a project or version name to its filename-escaped form

    Any '-' characters are currently replaced with '_'.
    """
    return name.replace('-', '_')


def empty_mapper(response):
    """Return only 'Ok' message."""
    return 'Ok'


@classmethod
def group_user_management_change_policy(cls, val):
    """
        Create an instance of this class set to the
        ``group_user_management_change_policy`` tag with value ``val``.

        :param GroupUserManagementChangePolicyType val:
        :rtype: EventType
        """
    return cls('group_user_management_change_policy', val)


def get_projects(slug):
    """Get project information from TimeSync"""
    p_list = [{'uri': 'https://code.osuosl.org/projects/ganeti-webmgr',
        'name': 'Ganeti Web Manager', 'slugs': [slug if slug else 'gwm'],
        'uuid': 'a034806c-00db-4fe1-8de8-514575f31bfb', 'revision': 4,
        'created_at': '2014-07-17', 'deleted_at': None, 'updated_at':
        '2014-07-20', 'users': {'patcht': {'member': True, 'spectator':
        False, 'manager': False}, 'tschuy': {'member': True, 'spectator':
        True, 'manager': True}}}]
    if not slug:
        p_list.append({'uri': 'https://code.osuosl.org/projects/timesync',
            'name': 'TimeSync', 'slugs': ['timesync', 'ts'], 'uuid':
            'a034806c-rrrr-bbbb-8de8-514575f31bfb', 'revision': 2,
            'created_at': '2014-07-17', 'deleted_at': None, 'updated_at':
            '2014-07-20', 'users': {'patcht': {'member': True, 'spectator':
            False, 'manager': False}, 'mrsj': {'member': True, 'spectator':
            True, 'manager': False}, 'tschuy': {'member': True, 'spectator':
            True, 'manager': True}}})
        p_list.append({'uri': 'https://code.osuosl.org/projects/pymesync',
            'name': 'pymesync', 'slugs': ['pymesync', 'ps'], 'uuid':
            'a034806c-ssss-cccc-8de8-514575f31bfb', 'revision': 1,
            'created_at': '2014-07-17', 'deleted_at': None, 'updated_at':
            '2014-07-20', 'users': {'patcht': {'member': True, 'spectator':
            False, 'manager': False}, 'tschuy': {'member': True,
            'spectator': True, 'manager': False}, 'mrsj': {'member': True,
            'spectator': True, 'manager': True}, 'MaraJade': {'member':
            True, 'spectator': False, 'manager': False}, 'thai': {'member':
            True, 'spectator': False, 'manager': False}}})
    return p_list


def planned_worktime(ticket):
    """ Returns a ITimeAware holding the planned work time set on either
            the ticket, milestone or project. Returns None if no work time is
            planned
        """


def share_fig_ax(fig=None, ax=None, numax=1, sharex=False, sharey=False):
    """Reurns the given figure and/or axis if given one.  If they are None, creates a new fig/ax.

    Parameters
    ----------
    fig : `matplotlib.figure.Figure`, optional
        figure
    ax : `matplotlib.axes.Axis`
        axis or array of axes
    numax : `int`
        number of axes in the desired figure, 1 for most plots, 3 for plot_fourier_chain
    sharex : `bool`, optional
        whether to share the x axis
    sharey : `bool`, optional
        whether to share the y axis

    Returns
    -------
    `matplotlib.figure.Figure`
        A figure object
    `matplotlib.axes.Axis`
        An axis object

    """
    from matplotlib import pyplot as plt
    if fig is None and ax is None:
        fig, ax = plt.subplots(nrows=1, ncols=numax, sharex=sharex, sharey=
            sharey)
    elif ax is None:
        ax = fig.gca()
    return fig, ax


@classmethod
def from_string(cls, bounding_box_string):
    """
        Creates an instance of the bounding box by giving a string.
        :param bounding_box_string: string of the format "x,y,width,height"
        :return: instance of boundingbox matching the string.
        """
    bbox_elements = bounding_box_string.split(',')
    if len(bbox_elements) != 4:
        raise Exception("bounding box string doesn't contain a bounding box.")
    x = int(bbox_elements[0].strip())
    y = int(bbox_elements[1].strip())
    width = int(bbox_elements[2].strip())
    height = int(bbox_elements[3].strip())
    result = cls(x, y, width, height)
    return result


@classmethod
def cmd(cls):
    """
        return the command id for a message class
        """
    return cls.__name__.lower()


@staticmethod
def dict_factory(cursor, old_row):
    """
        Dictionary factory for return results with dictionaries.

        :param sqlite3.Cursor cursor: The cursor.
        :param list old_row: A row from the result a a query.

        :rtype: dict
        """
    new_row = {}
    for index, col in enumerate(cursor.description):
        new_row[col[0]] = old_row[index]
    return new_row


@classmethod
def usable_id(cls, id):
    """ Retrieve id from input which can be num or id."""
    try:
        qry_id = int(id)
    except Exception:
        qry_id = None
    if not qry_id:
        msg = 'unknown identifier %s' % id
        cls.error(msg)
    return qry_id


def calc_fluxes_intg_rel_from_light(light_oc, light_ref=1.0):
    """Calculate integrated fluxes of the of both binary stars relative to the total integrated flux.

    Parameters
    ----------
    light_oc : float
        Light level during occultation (primary eclipse). Light level is the average of the flat region at the bottom
        of one of the eclipse minima. The event is a total eclipse of the star with the smaller radius.
    light_ref : {1.0}, float, optional
        Reference for light levels outside of eclipse. Typically total system light is normalized to 1.0.

    Returns
    -------
    flux_intg_rel_s : float
        Integrated flux of greater-sized star as a fraction of total integrated flux.
    flux_intg_rel_g : float
        Integrated flux of greater-sized star as a fraction of total integrated flux.

    Notes
    -----
    Note: Fluxes are returned as a tuple: (fs, fg)
    flux_intg_rel_s = (light_ref - light_oc) / light_ref
    flux_intg_rel_g = light_oc / light_ref
    From equation 7.2 in section 7.3 of [1]_.

    References
    ----------
    .. [1] Budding, 2007, Introduction to Astronomical Photometry

    """
    flux_intg_rel_s = (light_ref - light_oc) / light_ref
    flux_intg_rel_g = light_oc / light_ref
    return flux_intg_rel_s, flux_intg_rel_g


def get_uid(vobject_component):
    """UID value of an item if defined."""
    return vobject_component.uid.value if hasattr(vobject_component, 'uid'
        ) else None


def remove():
    """Delete current cache

    """
    pass


def set_testmode(testing):
    """Sets the package testing mode.
    """
    global testmode
    testmode = testing


def urljoin(*args):
    """Join multiple url parts to construct one url"""
    if len(args) == 0:
        raise TypeError('urljoin requires at least one argument')
    trailing_slash = '/' if args[-1].endswith('/') else ''
    return '/'.join(map(lambda x: str(x).strip('/'), args)) + trailing_slash


def process_conf_options(confgrp, kwargs):
    """Set Adapter constructor kwargs based on conf options.

    :param oslo_config.cfg.GroupAttr confgrp: Config object group containing
            options to inspect.
    :param dict kwargs: Keyword arguments suitable for the constructor of
            keystoneauth1.adapter.Adapter. Will be modified by this method.
            Values already set remain unaffected.
    :raise TypeError: If invalid conf option values or combinations are found.
    """
    if confgrp.valid_interfaces and getattr(confgrp, 'interface', None):
        raise TypeError(
            'interface and valid_interfaces are mutually exclusive. Please use valid_interfaces.'
            )
    if confgrp.valid_interfaces:
        for iface in confgrp.valid_interfaces:
            if iface not in ('public', 'internal', 'admin'):
                raise TypeError(
                    "'{iface}' is not a valid value for valid_interfaces. Valid valies are public, internal or admin"
                    .format(iface=iface))
        kwargs.setdefault('interface', confgrp.valid_interfaces)
    elif hasattr(confgrp, 'interface'):
        kwargs.setdefault('interface', confgrp.interface)
    kwargs.setdefault('service_type', confgrp.service_type)
    kwargs.setdefault('service_name', confgrp.service_name)
    kwargs.setdefault('region_name', confgrp.region_name)
    kwargs.setdefault('endpoint_override', confgrp.endpoint_override)
    kwargs.setdefault('version', confgrp.version)
    kwargs.setdefault('min_version', confgrp.min_version)
    kwargs.setdefault('max_version', confgrp.max_version)
    if kwargs['version'] and (kwargs['max_version'] or kwargs['min_version']):
        raise TypeError(
            'version is mutually exclusive with min_version and max_version')
    kwargs.setdefault('connect_retries', confgrp.connect_retries)
    kwargs.setdefault('connect_retry_delay', confgrp.connect_retry_delay)
    kwargs.setdefault('status_code_retries', confgrp.status_code_retries)
    kwargs.setdefault('status_code_retry_delay', confgrp.
        status_code_retry_delay)


def copy(obj):
    """Return a deep copy of object obj. Works on Color, Sound, and Picture
    objects."""
    return obj.copy()


def __len__():
    """Return the number of objects indexed."""


def p_main_notext(t):
    """main : main COMMENT
            | main RAW
            | main HEADING
            | main SLIDE
            | main SCISSOR
            | main MACRO
            | main YAML
            | nil"""
    if len(t) > 2:
        t[0] = t[1]
        t[0].append(t[2])
    else:
        t[0] = []


def describe_compute_environments(computeEnvironments=None, maxResults=None,
    nextToken=None):
    """
    Describes one or more of your compute environments.
    If you are using an unmanaged compute environment, you can use the DescribeComputeEnvironment operation to determine the ecsClusterArn that you should launch your Amazon ECS container instances into.
    See also: AWS API Documentation

    Examples
    This example describes the P2OnDemand compute environment.
    Expected Output:

    :example: response = client.describe_compute_environments(
        computeEnvironments=[
            'string',
        ],
        maxResults=123,
        nextToken='string'
    )

    :type computeEnvironments: list
    :param computeEnvironments: A list of up to 100 compute environment names or full Amazon Resource Name (ARN) entries.
            (string) --

    :type maxResults: integer
    :param maxResults: The maximum number of cluster results returned by DescribeComputeEnvironments in paginated output. When this parameter is used, DescribeComputeEnvironments only returns maxResults results in a single page along with a nextToken response element. The remaining results of the initial request can be seen by sending another DescribeComputeEnvironments request with the returned nextToken value. This value can be between 1 and 100. If this parameter is not used, then DescribeComputeEnvironments returns up to 100 results and a nextToken value if applicable.

    :type nextToken: string
    :param nextToken: The nextToken value returned from a previous paginated DescribeComputeEnvironments request where maxResults was used and the results exceeded the value of that parameter. Pagination continues from the end of the previous results that returned the nextToken value. This value is null when there are no more results to return.
            Note
            This token should be treated as an opaque identifier that is only used to retrieve the next items in a list and not for other programmatic purposes.

    :rtype: dict
    :return: {
        'computeEnvironments': [
            {
                'computeEnvironmentName': 'string',
                'computeEnvironmentArn': 'string',
                'ecsClusterArn': 'string',
                'type': 'MANAGED'|'UNMANAGED',
                'state': 'ENABLED'|'DISABLED',
                'status': 'CREATING'|'UPDATING'|'DELETING'|'DELETED'|'VALID'|'INVALID',
                'statusReason': 'string',
                'computeResources': {
                    'type': 'EC2'|'SPOT',
                    'minvCpus': 123,
                    'maxvCpus': 123,
                    'desiredvCpus': 123,
                    'instanceTypes': [
                        'string',
                    ],
                    'imageId': 'string',
                    'subnets': [
                        'string',
                    ],
                    'securityGroupIds': [
                        'string',
                    ],
                    'ec2KeyPair': 'string',
                    'instanceRole': 'string',
                    'tags': {
                        'string': 'string'
                    },
                    'bidPercentage': 123,
                    'spotIamFleetRole': 'string'
                },
                'serviceRole': 'string'
            },
        ],
        'nextToken': 'string'
    }

    :returns:
    (string) --

    """
    pass


@classmethod
def _screaming_snake_case(cls, text):
    """
        Transform text to SCREAMING_SNAKE_CASE

        :param text:
        :return:
        """
    if text.isupper():
        return text
    result = ''
    for pos, symbol in enumerate(text):
        if symbol.isupper() and pos > 0:
            result += '_' + symbol
        else:
            result += symbol.upper()
    return result


@staticmethod
def validateShape(image, texnvals, texndims):
    """Called by :meth:`__init__`. Makes sure that the specified texture
        settings (number of dimensions, and number of values per texture
        element) are compatible with the image.

        :arg image:    :class:`.Image`
        :arg texnvals: Number of values per texture element
        :arg texndims: Number of texture dimensions
        :raises:       :exc:`RuntimeError` if the texture properties are not
                       compatible with the image
        """
    imgnvals = image.nvals
    imgndims = len(image.shape)
    if texnvals == 1:
        return
    if imgnvals == 1:
        expShape = list(image.shape[:3])
        expShape += [1] * (imgndims - 3)
        expShape[-1] = texnvals
        if list(image.shape) != expShape:
            raise RuntimeError(
                'Data shape mismatch: texture size {} requested for image shape {}'
                .format(texnvals, image.shape))
    elif imgnvals != texnvals:
        raise RuntimeError(
            'Data shape mismatch: texture size {} requested for image with nvals {}'
            .format(texnvals, imgnvals))


def supports_encryption() ->bool:
    """Test if we support encryption."""
    try:
        import nacl
        return True
    except OSError:
        return False


def get_line_indent(token):
    """Finds the indent of the line the token starts in."""
    start = token.start_mark.buffer.rfind('\n', 0, token.start_mark.pointer
        ) + 1
    content = start
    while token.start_mark.buffer[content] == ' ':
        content += 1
    return content - start


def get_null_stats(df):
    """
    Function that will return count (absolute and relative)  of null
    values in each column in `df`.

    Parameters
    ----------

    df : pandas.DataFrame
        DataFrame on which this function will operate.

    Return
    ------

    pandas.DataFrame with `df.columns` as row index and `["cnt", "ratio"]`
    (for absolute and relative counts, respectively) in column index.
    """
    nulls = df.isnull().sum()
    nulls = nulls.rename('cnt').to_frame()
    nulls['ratio'] = nulls / len(df)
    return nulls.sort_values('ratio', ascending=False)


def andmatch(st, words):
    """returns True if st contains all the words in any order.

    The comparison is case insensitive.
    Here are some examples:
    >>> st="this is the string that contains some words"
    >>> andmatch(st,["this"])
    True
    >>> andmatch(st,["that","this"])
    True
    >>> andmatch(st,["that","this","there"])
    False
    >>> andmatch(st,["THIS", "There"])
    False
    >>> andmatch(st,["THIS", "That"])
    True
    >>> andmatch(st,["THIS", "That", "contains"])
    True
    """
    st = st.upper()
    for w in words:
        w = w.upper()
        if st.find(w) == -1:
            return False
    return True


def ppo_cli_args(parser):
    """
    Add standard PPO arguments to an ArgumentParser.

    This is useful for scripts that run training.
    """
    parser.add_argument('--ppo-lr', help='Adam learning rate', default=
        0.0003, type=float)
    parser.add_argument('--ppo-horizon', help='PPO horizon', default=128,
        type=int)
    parser.add_argument('--ppo-iters', help='PPO iterations', default=16,
        type=int)
    parser.add_argument('--ppo-epsilon', help='PPO clipping fraction',
        default=0.1, type=float)
    parser.add_argument('--ppo-discount', help='PPO discount factor',
        default=0.99, type=float)
    parser.add_argument('--ppo-lambda', help='PPO lambda', default=0.95,
        type=float)
    parser.add_argument('--ppo-entropy', help='PPO entropy bonus', default=
        0.01, type=float)
    parser.add_argument('--ppo-reward-scale', help='PPO reward scale',
        default=1.0, type=float)
    parser.add_argument('--save-path', help='path to PPO save file',
        default='ppo_agent.pkl')
    parser.add_argument('--save-interval', help='iterations per save',
        default=5, type=int)


def implements(*interfaces):
    """Declare interfaces implemented by instances of a class

        This function is called in a class definition (Python 2.x only).

        The arguments are one or more interfaces or interface
        specifications (IDeclaration objects).

        The interfaces given (including the interfaces in the
        specifications) are added to any interfaces previously
        declared.

        Previous declarations include declarations for base classes
        unless implementsOnly was used.

        This function is provided for convenience. It provides a more
        convenient way to call classImplements. For example::

          implements(I1)

        is equivalent to calling::

          classImplements(C, I1)

        after the class has been created.

        Consider the following example (Python 2.x only)::

          class C(A, B):
            implements(I1, I2)

        Instances of ``C`` implement ``I1``, ``I2``, and whatever interfaces
        instances of ``A`` and ``B`` implement.
        """


def autocomplete():
    """Show a list of used text in the open document

    """
    pass


def render_pep440_old(pieces):
    """TAG[.postDISTANCE[.dev0]] .

    The ".dev0" means dirty.

    Eexceptions:
    1: no tags. 0.postDISTANCE[.dev0]
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance'] or pieces['dirty']:
            rendered += '.post%d' % pieces['distance']
            if pieces['dirty']:
                rendered += '.dev0'
    else:
        rendered = '0.post%d' % pieces['distance']
        if pieces['dirty']:
            rendered += '.dev0'
    return rendered


def validate_tag_attribute_value(tag, value):
    """Validates whether the given value is a string or not."""
    if not value:
        return
    if not isinstance(value, str):
        raise ValueError('<{tag}>: {value} should be a string'.format(tag=
            tag, value=value))


def int_(value):
    """
    Returns int or None
    """
    if value:
        return float(value)
    else:
        return None


def _describe_timeseries(ts):
    """
    Describes a cmf timeseries with its start, end, step, min, mean and max.

    :param: ts: a cmf.timeseries
    :return: None
    """
    if ts:
        return (
            '{count} values from {start:%Y-%m-%d} to {end:%Y-%m-%d} step {step}, min/mean/max  {min:0.5g} / {mean:0.5g} / {max:0.5g}'
            .format(count=len(ts), start=ts.begin.AsPython(), end=ts.end.
            AsPython(), step=ts.step, min=ts.min(), mean=ts.mean(), max=ts.
            max()))
    else:
        return '~'


def view_selected(include_handles: bool=True):
    """Reset viewable area to show selected keyframe range

    :param include_handles: Include Handles, Include handles of keyframes when calculating extents
    :type include_handles: bool
    """
    pass


def render_git_describe(pieces):
    """TAG[-DISTANCE-gHEX][-dirty].

    Like 'git describe --tags --dirty --always'.

    Exceptions:
    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance']:
            rendered += '-%d-g%s' % (pieces['distance'], pieces['short'])
    else:
        rendered = pieces['short']
    if pieces['dirty']:
        rendered += '-dirty'
    return rendered


def get_pressure_at_height(press_0, z_0, z, temp_z, air_molar_mass=
    0.0289644, lapse_rate=0.0065, univ_gas_cst=8.3144598, g_0=9.80665):
    """Use the barometric formula to compute pressure at a
    given level from pressure at another.actuator.

    :param press_0: Pressure at given height (Pa).
    :param z_0: Height at which pressure is given (m).
    :param z: Height at which to return pressure (m).
    :param temp_z: Standard temperature at output height (K).
    :param air_molar_mass: Molar mass of air(kg/mol).
      Default is dry air molar mass 0.0289644 kg/mol.
    :param lapse_rate: Temperature lapse rate (K/m).
    :param univ_gas_cst: Universal gas constant (J/K/mol).
      Default is 8.3144598 J/K/mol.
    :param g_0: Gravitational acceleration (m/s2).
    :type press_0: float, array_like
    :type z_0: float, array_like
    :type z: float, array_like
    :type Tstd: float, array_like
    :type air_molar_mass: float, array_like
    :type lapse_rate: float, array_like
    :type univ_gas_cst: float
    :type g_0: float

    :returns: Pressure at given level.
    :rtype: float, array_like
    """
    exp = g_0 * air_molar_mass / (univ_gas_cst * lapse_rate)
    if z >= z_0:
        press_z = press_0 * (temp_z / (temp_z + lapse_rate * (z - z_0))) ** exp
    else:
        press_z = press_0 * (temp_z / (temp_z + lapse_rate * (z_0 - z))
            ) ** -exp
    return press_z


def to_ascii_hex(value: int, digits: int) ->str:
    """Converts an int value to ASCII hex, as used by LifeSOS.
       Unlike regular hex, it uses the first 6 characters that follow
       numerics on the ASCII table instead of A - F."""
    if digits < 1:
        return ''
    text = ''
    for _ in range(0, digits):
        text = chr(ord('0') + value % 16) + text
        value //= 16
    return text


def substitute_none(data):
    """Ensure that stream does not contain None"""
    for value in data:
        if value is None:
            yield 0
        else:
            yield value


def merge_dimensions(df1, df2, interp_lim=3):
    """
    Merges variables measured at different time intervals. Glider data may be
    sampled at different time intervals, as is the case for primary CTD and
    SciCon data.

    Parameters
    ----------
    df1 : pandas.DataFrame
        A dataframe indexed by datetime64 sampling times. Can have multiple
        columns. The index of this first dataframe will be preserved.
    df2 : pandas.DataFrame
        A dataframe indexed by datetime64 sampling times. Can have multiple
        columns. This second dataframe will be interpolated linearly onto the
        first dataframe.

    Returns
    -------
    merged_df : pandas.DataFrame
        The combined arrays interpolated onto the index of the first axis

    Raises
    ------
    Userwarning
        If either one of the indicies are not datetime64 dtypes

    Example
    -------
    You can use the following code and alter it if you want more control

    >>> df = pd.concat([df1, df2], sort=True, join='outer')  # doctest: +SKIP
    >>> df = (df
              .sort_index()
              .interpolate(limit=interp_lim)
              .bfill(limit=interp_lim)
              .loc[df1.index]
        )
    """
    import xarray as xr
    from .helpers import GliderToolsError
    is_xds = isinstance(df1, xr.Dataset) | isinstance(df2, xr.Dataset)
    if is_xds:
        msg = 'One of your input objects is xr.Dataset, please define '
        raise GliderToolsError(msg)
    same_type = type(df1.index) == type(df2.index)
    if same_type:
        df = df1.join(df2, sort=True, how='outer', rsuffix='_drop')
        df = df.interpolate(limit=interp_lim).bfill(limit=interp_lim)
        return df.loc[df1.index]
    else:
        raise UserWarning('Both dataframe indicies need to be same dtype')


def clean(threshold: float=0.001, channels: bool=False):
    """Simplify F-Curves by removing closely spaced keyframes

    :param threshold: Threshold
    :type threshold: float
    :param channels: Channels
    :type channels: bool
    """
    pass


@classmethod
def find_ultrahd(cls, matches, start, end, index):
    """Find Ultra HD match."""
    return matches.range(start, end, index=index, predicate=lambda m: not m
        .private and m.name == 'other' and m.value == 'Ultra HD')


def del_chars_from_string(s, chars_to_del):
    """
    Delete characters from list

    :param s: string to clean
    :param chars_to_del: characters to delete in string
    """
    if type(chars_to_del) != 'str':
        for c in chars_to_del:
            s = s.replace(c, '')
    else:
        s = s.replace(chars_to_del, '')
    return s


def describe_application(ApplicationName=None):
    """
    Returns information about a specific Amazon Kinesis Analytics application.
    If you want to retrieve a list of all applications in your account, use the  ListApplications operation.
    This operation requires permissions to perform the kinesisanalytics:DescribeApplication action. You can use DescribeApplication to get the current application versionId, which you need to call other operations such as Update .
    See also: AWS API Documentation

    :example: response = client.describe_application(
        ApplicationName='string'
    )

    :type ApplicationName: string
    :param ApplicationName: [REQUIRED]
            Name of the application.

    :rtype: dict
    :return: {
        'ApplicationDetail': {
            'ApplicationName': 'string',
            'ApplicationDescription': 'string',
            'ApplicationARN': 'string',
            'ApplicationStatus': 'DELETING'|'STARTING'|'STOPPING'|'READY'|'RUNNING'|'UPDATING',
            'CreateTimestamp': datetime(2015, 1, 1),
            'LastUpdateTimestamp': datetime(2015, 1, 1),
            'InputDescriptions': [
                {
                    'InputId': 'string',
                    'NamePrefix': 'string',
                    'InAppStreamNames': [
                        'string',
                    ],
                    'KinesisStreamsInputDescription': {
                        'ResourceARN': 'string',
                        'RoleARN': 'string'
                    },
                    'KinesisFirehoseInputDescription': {
                        'ResourceARN': 'string',
                        'RoleARN': 'string'
                    },
                    'InputSchema': {
                        'RecordFormat': {
                            'RecordFormatType': 'JSON'|'CSV',
                            'MappingParameters': {
                                'JSONMappingParameters': {
                                    'RecordRowPath': 'string'
                                },
                                'CSVMappingParameters': {
                                    'RecordRowDelimiter': 'string',
                                    'RecordColumnDelimiter': 'string'
                                }
                            }
                        },
                        'RecordEncoding': 'string',
                        'RecordColumns': [
                            {
                                'Name': 'string',
                                'Mapping': 'string',
                                'SqlType': 'string'
                            },
                        ]
                    },
                    'InputParallelism': {
                        'Count': 123
                    },
                    'InputStartingPositionConfiguration': {
                        'InputStartingPosition': 'NOW'|'TRIM_HORIZON'|'LAST_STOPPED_POINT'
                    }
                },
            ],
            'OutputDescriptions': [
                {
                    'OutputId': 'string',
                    'Name': 'string',
                    'KinesisStreamsOutputDescription': {
                        'ResourceARN': 'string',
                        'RoleARN': 'string'
                    },
                    'KinesisFirehoseOutputDescription': {
                        'ResourceARN': 'string',
                        'RoleARN': 'string'
                    },
                    'DestinationSchema': {
                        'RecordFormatType': 'JSON'|'CSV'
                    }
                },
            ],
            'ReferenceDataSourceDescriptions': [
                {
                    'ReferenceId': 'string',
                    'TableName': 'string',
                    'S3ReferenceDataSourceDescription': {
                        'BucketARN': 'string',
                        'FileKey': 'string',
                        'ReferenceRoleARN': 'string'
                    },
                    'ReferenceSchema': {
                        'RecordFormat': {
                            'RecordFormatType': 'JSON'|'CSV',
                            'MappingParameters': {
                                'JSONMappingParameters': {
                                    'RecordRowPath': 'string'
                                },
                                'CSVMappingParameters': {
                                    'RecordRowDelimiter': 'string',
                                    'RecordColumnDelimiter': 'string'
                                }
                            }
                        },
                        'RecordEncoding': 'string',
                        'RecordColumns': [
                            {
                                'Name': 'string',
                                'Mapping': 'string',
                                'SqlType': 'string'
                            },
                        ]
                    }
                },
            ],
            'ApplicationCode': 'string',
            'ApplicationVersionId': 123
        }
    }

    :returns:
    LATEST - Start reading just after the most recent record in the stream.
    TRIM_HORIZON - Start reading at the last untrimmed record in the stream, which is the oldest record available in the stream. This option is not available for an Amazon Kinesis Firehose delivery stream.
    LAST_STOPPED_POINT - Resume reading from where the application last stopped reading.

    """
    pass


@staticmethod
def _analyseOneLoop(lFields, iStart):
    """Processes one loop in the data extraction of the CIF file
        @param lFields: list of all the words contained in the cif file
        @type lFields: list
        @param iStart: the starting index corresponding to the "loop_" key
        @type iStart: integer
        @return: the list of loop dictionaries, the length of the data extracted from the lFields and the list of all the keys of the loop.
        @rtype: tuple
        """
    loop = []
    keys = []
    i = iStart + 1
    bFinished = False
    while not bFinished:
        if lFields[i][0] == '_':
            keys.append(lFields[i])
            i += 1
        else:
            bFinished = True
    data = []
    while True:
        if i >= len(lFields):
            break
        elif len(lFields[i]) == 0:
            break
        elif lFields[i][0] == '_':
            break
        elif lFields[i] in ['loop_', 'stop_', 'global_', 'data_', 'save_']:
            break
        else:
            data.append(lFields[i])
            i += 1
    k = 0
    if len(data) < len(keys):
        element = {}
        for j in keys:
            if k < len(data):
                element[j] = data[k]
            else:
                element[j] = '?'
            k += 1
        loop.append(element)
    else:
        for i in range(len(data) / len(keys)):
            element = {}
            for j in keys:
                element[j] = data[k]
                k += 1
            loop.append(element)
    return loop, 1 + len(keys) + len(data), keys


def _shaper(row):
    """
    Parallel function to create shapely points
    from latitude/longitude pair in dataframe

    Parameters
    ----------
    row : pandas or dask dataframe row
        Row containing latitude and longitude variables and data

    Returns
    -------
    shapely point
        Shapely spatial object for geoprocessing in Python.
    """
    try:
        from shapely.geometry import Point
    except:
        raise ImportError('You need to install shapely to use this feature.')
    try:
        import fiona
    except:
        raise ImportError('You need to install fiona to use this feature.')
    try:
        import geopandas
    except:
        raise ImportError('You need to install geopandas to use this feature.')
    try:
        geometry = Point(row['ActionGeo_Long'], row['ActionGeo_Lat'])
    except:
        geometry = Point(row['actiongeolong'], row['actiongeolat'])
    return geometry


def snooze(value):
    """
    time.sleep() substitute
    Keep busy for some time (very roughly and depending on machine, `value` is in ms)

    :param value: Time
    :type value: Number
    """
    for i in range(int(650.0 * value)):
        _ = 2 ** (i / value)


@staticmethod
def build_output_parameters(parsed_stdout, parsed_xml):
    """Build the dictionary of output parameters from the raw parsed data.

        The output parameters are based on the union of raw parsed data from the XML and stdout output files.
        Currently, if both raw parsed data dictionaries contain the same key, the stdout version takes precedence, but
        this should not occur as the `parse_stdout` method should already have solved these conflicts.

        :param parsed_stdout: the raw parsed data dictionary from the stdout output file
        :param parsed_xml: the raw parsed data dictionary from the XML output file
        :return: the union of the two parsed raw and information about the parser
        """
    from aiida_quantumespresso.parsers import get_parser_info
    parsed_info = get_parser_info(parser_info_template=
        'aiida-quantumespresso parser pw.x v{}')
    for key in list(parsed_stdout.keys()):
        if key in list(parsed_xml.keys()):
            if parsed_stdout[key] != parsed_xml[key]:
                raise AssertionError(
                    '{} found in both dictionaries with different values: {} vs. {}'
                    .format(key, parsed_stdout[key], parsed_xml[key]))
    parameters = dict(list(parsed_xml.items()) + list(parsed_stdout.items()
        ) + list(parsed_info.items()))
    return parameters


def unique_index(df):
    """Assert that the index is unique"""
    assert df.index.is_unique
    return df


def object_fullname(obj):
    """Returns the full absolute name of the object provided"""
    fullname = obj.__module__ + '.' + obj.__class__.__name__
    return fullname


def fmt_row_data(raw_data, fmt_str):
    """ Formats the values in the dicts in the given list of raw data using
    the given format string.

    *This may not be needed at all*
    Now that I'm using csv.QUOTE_NONNUMERIC, generally don't want to format floats to strings

    @param raw_data: The list of dicts to format.
    @param fmt_str: The format string to use when formatting.
    @return: The formatted list of dicts.
    """
    fmt_rows = []
    for row in raw_data:
        fmt_row = {}
        for key, raw_val in row.items():
            fmt_row[key] = fmt_str.format(raw_val)
        fmt_rows.append(fmt_row)
    return fmt_rows


def to_networkx(g, node_attrs=None, edge_attrs=None):
    """Convert to networkx graph.

    The edge id will be saved as the 'id' edge attribute.

    Parameters
    ----------
    g : DGLGraph or DGLHeteroGraph
        For DGLHeteroGraphs, we currently only support the
        case of one node type and one edge type.
    node_attrs : iterable of str, optional
        The node attributes to be copied. (Default: None)
    edge_attrs : iterable of str, optional
        The edge attributes to be copied. (Default: None)

    Returns
    -------
    networkx.DiGraph
        The nx graph
    """
    return g.to_networkx(node_attrs, edge_attrs)


@staticmethod
def is_full_formula(full_formula):
    """
        проверяем что строка является полной формулой
        - т.е обернута в []
        """
    return full_formula[0] == '[' and full_formula[-1] == ']'


def _float_gcd(a, b, atol=1e-08):
    """Greatest common divisor (=groesster gemeinsamer teiler)"""
    while abs(b) > atol:
        a, b = b, a % b
    return a


def ann_viz(model, view=True, filename='network.gv', title='My Neural Network'
    ):
    """Vizualizez a Sequential model.
    # Arguments
        model: A Keras model instance.
        view: whether to display the model after generation.
        filename: where to save the vizualization. (a .gv file)
        title: A title for the graph
    """
    from graphviz import Digraph
    import keras
    from keras.models import Sequential
    from keras.layers import Dense, Conv2D, MaxPooling2D, Dropout, Flatten
    import json
    input_layer = 0
    hidden_layers_nr = 0
    layer_types = []
    hidden_layers = []
    output_layer = 0
    for layer in model.layers:
        if layer == model.layers[0]:
            input_layer = int(str(layer.input_shape).split(',')[1][1:-1])
            hidden_layers_nr += 1
            if type(layer) == keras.layers.core.Dense:
                hidden_layers.append(int(str(layer.output_shape).split(',')
                    [1][1:-1]))
                layer_types.append('Dense')
            else:
                hidden_layers.append(1)
                if type(layer) == keras.layers.convolutional.Conv2D:
                    layer_types.append('Conv2D')
                elif type(layer) == keras.layers.pooling.MaxPooling2D:
                    layer_types.append('MaxPooling2D')
                elif type(layer) == keras.layers.core.Dropout:
                    layer_types.append('Dropout')
                elif type(layer) == keras.layers.core.Flatten:
                    layer_types.append('Flatten')
                elif type(layer) == keras.layers.core.Activation:
                    layer_types.append('Activation')
        elif layer == model.layers[-1]:
            output_layer = int(str(layer.output_shape).split(',')[1][1:-1])
        else:
            hidden_layers_nr += 1
            if type(layer) == keras.layers.core.Dense:
                hidden_layers.append(int(str(layer.output_shape).split(',')
                    [1][1:-1]))
                layer_types.append('Dense')
            else:
                hidden_layers.append(1)
                if type(layer) == keras.layers.convolutional.Conv2D:
                    layer_types.append('Conv2D')
                elif type(layer) == keras.layers.pooling.MaxPooling2D:
                    layer_types.append('MaxPooling2D')
                elif type(layer) == keras.layers.core.Dropout:
                    layer_types.append('Dropout')
                elif type(layer) == keras.layers.core.Flatten:
                    layer_types.append('Flatten')
                elif type(layer) == keras.layers.core.Activation:
                    layer_types.append('Activation')
        last_layer_nodes = input_layer
        nodes_up = input_layer
        if type(model.layers[0]) != keras.layers.core.Dense:
            last_layer_nodes = 1
            nodes_up = 1
            input_layer = 1
        g = Digraph('g', filename=filename)
        n = 0
        g.graph_attr.update(splines='false', nodesep='1', ranksep='2')
        with g.subgraph(name='cluster_input') as c:
            if type(model.layers[0]) == keras.layers.core.Dense:
                the_label = title + '\n\n\n\nInput Layer'
                if int(str(model.layers[0].input_shape).split(',')[1][1:-1]
                    ) > 10:
                    the_label += ' (+' + str(int(str(model.layers[0].
                        input_shape).split(',')[1][1:-1]) - 10) + ')'
                    input_layer = 10
                c.attr(color='white')
                for i in range(0, input_layer):
                    n += 1
                    c.node(str(n))
                    c.attr(label=the_label)
                    c.attr(rank='same')
                    c.node_attr.update(color='#2ecc71', style='filled',
                        fontcolor='#2ecc71', shape='circle')
            elif type(model.layers[0]) == keras.layers.convolutional.Conv2D:
                the_label = title + '\n\n\n\nInput Layer'
                c.attr(color='white', label=the_label)
                c.node_attr.update(shape='square')
                pxls = str(model.layers[0].input_shape).split(',')
                clr = int(pxls[3][1:-1])
                if clr == 1:
                    clrmap = 'Grayscale'
                    the_color = 'black:white'
                elif clr == 3:
                    clrmap = 'RGB'
                    the_color = '#e74c3c:#3498db'
                else:
                    clrmap = ''
                c.node_attr.update(fontcolor='white', fillcolor=the_color,
                    style='filled')
                n += 1
                c.node(str(n), label='Image\n' + pxls[1] + ' x' + pxls[2] +
                    ' pixels\n' + clrmap, fontcolor='white')
            else:
                raise ValueError(
                    'ANN Visualizer: Layer not supported for visualizing')
        for i in range(0, hidden_layers_nr):
            with g.subgraph(name='cluster_' + str(i + 1)) as c:
                if layer_types[i] == 'Dense':
                    c.attr(color='white')
                    c.attr(rank='same')
                    the_label = ''
                    if int(str(model.layers[i].output_shape).split(',')[1][
                        1:-1]) > 10:
                        the_label += ' (+' + str(int(str(model.layers[i].
                            output_shape).split(',')[1][1:-1]) - 10) + ')'
                        hidden_layers[i] = 10
                    c.attr(labeljust='right', labelloc='b', label=the_label)
                    for j in range(0, hidden_layers[i]):
                        n += 1
                        c.node(str(n), shape='circle', style='filled',
                            color='#3498db', fontcolor='#3498db')
                        for h in range(nodes_up - last_layer_nodes + 1,
                            nodes_up + 1):
                            g.edge(str(h), str(n))
                    last_layer_nodes = hidden_layers[i]
                    nodes_up += hidden_layers[i]
                elif layer_types[i] == 'Conv2D':
                    c.attr(style='filled', color='#5faad0')
                    n += 1
                    kernel_size = str(model.layers[i].get_config()[
                        'kernel_size']).split(',')[0][1] + 'x' + str(model.
                        layers[i].get_config()['kernel_size']).split(',')[1][
                        1:-1]
                    filters = str(model.layers[i].get_config()['filters'])
                    c.node('conv_' + str(n), label=
                        'Convolutional Layer\nKernel Size: ' + kernel_size +
                        '\nFilters: ' + filters, shape='square')
                    c.node(str(n), label=filters + '\nFeature Maps', shape=
                        'square')
                    g.edge('conv_' + str(n), str(n))
                    for h in range(nodes_up - last_layer_nodes + 1,
                        nodes_up + 1):
                        g.edge(str(h), 'conv_' + str(n))
                    last_layer_nodes = 1
                    nodes_up += 1
                elif layer_types[i] == 'MaxPooling2D':
                    c.attr(color='white')
                    n += 1
                    pool_size = str(model.layers[i].get_config()['pool_size']
                        ).split(',')[0][1] + 'x' + str(model.layers[i].
                        get_config()['pool_size']).split(',')[1][1:-1]
                    c.node(str(n), label='Max Pooling\nPool Size: ' +
                        pool_size, style='filled', fillcolor='#8e44ad',
                        fontcolor='white')
                    for h in range(nodes_up - last_layer_nodes + 1,
                        nodes_up + 1):
                        g.edge(str(h), str(n))
                    last_layer_nodes = 1
                    nodes_up += 1
                elif layer_types[i] == 'Flatten':
                    n += 1
                    c.attr(color='white')
                    c.node(str(n), label='Flattening', shape='invtriangle',
                        style='filled', fillcolor='#2c3e50', fontcolor='white')
                    for h in range(nodes_up - last_layer_nodes + 1,
                        nodes_up + 1):
                        g.edge(str(h), str(n))
                    last_layer_nodes = 1
                    nodes_up += 1
                elif layer_types[i] == 'Dropout':
                    n += 1
                    c.attr(color='white')
                    c.node(str(n), label='Dropout Layer', style='filled',
                        fontcolor='white', fillcolor='#f39c12')
                    for h in range(nodes_up - last_layer_nodes + 1,
                        nodes_up + 1):
                        g.edge(str(h), str(n))
                    last_layer_nodes = 1
                    nodes_up += 1
                elif layer_types[i] == 'Activation':
                    n += 1
                    c.attr(color='white')
                    fnc = model.layers[i].get_config()['activation']
                    c.node(str(n), shape='octagon', label=
                        'Activation Layer\nFunction: ' + fnc, style=
                        'filled', fontcolor='white', fillcolor='#00b894')
                    for h in range(nodes_up - last_layer_nodes + 1,
                        nodes_up + 1):
                        g.edge(str(h), str(n))
                    last_layer_nodes = 1
                    nodes_up += 1
        with g.subgraph(name='cluster_output') as c:
            if type(model.layers[-1]) == keras.layers.core.Dense:
                c.attr(color='white')
                c.attr(rank='same')
                c.attr(labeljust='1')
                for i in range(1, output_layer + 1):
                    n += 1
                    c.node(str(n), shape='circle', style='filled', color=
                        '#e74c3c', fontcolor='#e74c3c')
                    for h in range(nodes_up - last_layer_nodes + 1,
                        nodes_up + 1):
                        g.edge(str(h), str(n))
                c.attr(label='Output Layer', labelloc='bottom')
                c.node_attr.update(color='#2ecc71', style='filled',
                    fontcolor='#2ecc71', shape='circle')
        g.attr(arrowShape='none')
        g.edge_attr.update(arrowhead='none', color='#707070')
        if view == True:
            g.view()


def remove_words(txt, words_to_remove):
    """Return a copy of the txt string with the specified words (not Words)
    removed.
    """
    output = ''
    for wrd in txt.split(' '):
        if wrd not in words_to_remove:
            output += wrd + ' '
    return output.strip()


def int2bool(val):
    """
    Funkcja zamienia int na bool

    0 -> false
    >=1 -> true
    """
    return False if int(val) == 0 else True


def raise_if_invalid_object_id(object_id: int) ->None:
    """Checks if `object_id` is a valid id.

    A valid id is an integer, higher than 0.

    Args:
        object_id (int): Unique identifier, referencing to data-entry
                      inside the database.

    Raises:
        ValueError: If the `object_id` is not a valid id.
    """
    if object_id < 1:
        raise ValueError('The `object_id` must be 1 or higher.')


def get_screen_dimensions():
    """
    Returns primary screen width and height in pixels
    """
    import mss
    with mss.mss() as sct:
        for monitor in sct.monitors:
            if monitor['left'] == 0 and monitor['top'] == 0:
                break
    return monitor['width'], monitor['height']


@classmethod
def __new_real_class__(mcs, name, bases, class_dict):
    """Create the real twin"""
    class_dict['__is_twin_proxy__'] = False
    return type.__new__(mcs, name, bases, class_dict)


def mercator_lat(lat):
    """Helper coerce lat range"""
    return lat - 180 if lat > 90 else lat


def _translate_slice(exp, length):
    """ Given a slice object, return a 3-tuple
        (start, count, step)
        for use with the hyperslab selection routines
    """
    start, stop, step = exp.indices(length)
    if step < 1:
        raise ValueError('Step must be >= 1 (got %d)' % step)
    if stop < start:
        raise ValueError('Reverse-order selections are not allowed')
    count = 1 + (stop - start - 1) // step
    return start, count, step


def safe_cast(value, to_type, default=None):
    """Cast a value to another type safely.

    :param value: The original value.
    :param type to_type: The destination type.
    :param default: The default value.
    """
    try:
        return to_type(value)
    except (ValueError, TypeError):
        return default


def rewriteFasta(sequence, sequence_name, fasta_in, fasta_out):
    """
    Rewrites a specific sequence in a multifasta file while keeping the sequence header.

    :param sequence: a string with the sequence to be written
    :param sequence_name: the name of the sequence to be retrieved eg. for '>2 dna:chromosome chromosome:GRCm38:2:1:182113224:1 REF' use: sequence_name=str(2)
    :param fasta_in: /path/to/original.fa
    :param fasta_out: /path/to/destination.fa

    :returns: nothing
    """
    f = open(fasta_in, 'r+')
    f2 = open(fasta_out, 'w')
    lines = f.readlines()
    i = 0
    while i < len(lines):
        line = lines[i]
        if line[0] == '>':
            f2.write(line)
            fChr = line.split(' ')[0]
            fChr = fChr[1:]
            if fChr == sequence_name:
                code = ['N', 'A', 'C', 'T', 'G']
                firstbase = lines[i + 1][0]
                while firstbase in code:
                    i = i + 1
                    firstbase = lines[i][0]
                s = 0
                while s <= len(sequence):
                    f2.write(sequence[s:s + 60] + '\n')
                    s = s + 60
            else:
                i = i + 1
        else:
            f2.write(line)
            i = i + 1
    f2.close
    f.close


def stop_gradient(data=None, name=None, attr=None, out=None, **kwargs):
    """Stops gradient computation.

    Stops the accumulated gradient of the inputs from flowing through this operator
    in the backward direction. In other words, this operator prevents the contribution
    of its inputs to be taken into account for computing gradients.

    Example::

      v1 = [1, 2]
      v2 = [0, 1]
      a = Variable('a')
      b = Variable('b')
      b_stop_grad = stop_gradient(3 * b)
      loss = MakeLoss(b_stop_grad + a)

      executor = loss.simple_bind(ctx=cpu(), a=(1,2), b=(1,2))
      executor.forward(is_train=True, a=v1, b=v2)
      executor.outputs
      [ 1.  5.]

      executor.backward()
      executor.grad_arrays
      [ 0.  0.]
      [ 1.  1.]

    Defined in src/operator/tensor/elemwise_unary_op_basic.cc:L327

    Parameters
    ----------
    data : Symbol
        The input array.

    name : string, optional.
        Name of the resulting symbol.

    Returns
    -------
    Symbol
        The result symbol.
    """
    return 0,


@classmethod
def properties_error(cls, val):
    """
        Create an instance of this class set to the ``properties_error`` tag
        with value ``val``.

        :param file_properties.InvalidPropertyGroupError val:
        :rtype: UploadSessionFinishError
        """
    return cls('properties_error', val)


def items(sobject):
    """
    Extract the I{items} from a suds object.

    Much like the items() method works on I{dict}.

    @param sobject: A suds object
    @type sobject: L{Object}
    @return: A list of items contained in I{sobject}.
    @rtype: [(key, value),...]

    """
    for item in sobject:
        yield item


def get_nodes(node_label):
    """Generate query returning ids of all nodes of the graph.

    Parameters
    ----------
    label
        Label of the nodes to match, default is 'node'
    """
    query = 'MATCH (n:{}) RETURN n.id\n'.format(node_label)
    return query


def to_dict(wsgi_headers, include=lambda _: True):
    """
    Convert WSGIHeaders to a dict so that it can be JSON-encoded
    """
    ret = {}
    for k, v in wsgi_headers.items():
        if include(k):
            ret[k] = v
    return ret


@classmethod
def create_batch(cls, size, **kwargs):
    """Create a batch of instances of the given class, with overriden attrs.

        Args:
            size (int): the number of instances to create

        Returns:
            object list: the created instances
        """
    return [cls.create(**kwargs) for _ in range(size)]


def dummy():
    """
    Do nothing because you're just a dummy.

    This dummy can be used by any module that imports internal to get rid of PEP8 errors about
    an import not being used. This internal module takes care of loading C# references, so most
    times it will only be imported but not actually used.
    """
    pass


@staticmethod
def get_elastic_mappings(es_major):
    """Get Elasticsearch mapping.

        :param es_major: major version of Elasticsearch, as string
        :returns:        dictionary with a key, 'items', with the mapping
        """
    mapping = """
         {
            "dynamic":true,
            "properties": {
                "data": {
                    "properties": {
                        "date": {
                            "type": "keyword"
                        }
                    }
                }
            }
        }
        """
    return {'items': mapping}


@classmethod
def extra_key_files(cls):
    """
        Return a list of extra key file paths (or path glob patterns) beyond
        standard, well known key files for this Package. List items are strings
        that are either paths or glob patterns and are relative to the package
        root.

        Knowing if a file is a "key-file" file is important for classification
        and summarization. For instance, a JAR can have key files that are not
        top level under the META-INF directory. Or a .gem archive contains a
        metadata.gz file.

        Sub-classes can implement as needed.
        """
    return []


def bytes_2_str(bytes_str):
    """
    用于将bytes 类型转换为 str 类型
    :param bytes_str:
    :return:
    """
    return str(bytes_str, encoding='GBK')


def assignment_id(df):
    """Resets and drops the current dataframe's
    index and sets it to the assignment_hash

    assignment_id is treated as a string identifier to account
    for hits in details with no assignment or assignment_id
    """
    df = df.reset_index(drop=True)
    df.index.name = 'assignment_id'
    df.index = df.index.astype(str)
    return df


def safe_get(d, key):
    """Helper function.

    :param d: dictionary
    :type d: dict
    :param key: key to retrieve from dict
    :type key: str
    :return: value of specified key if not None, otherwise empty dict
    """
    if key in d and d[key] is not None:
        return d[key]
    return {}


def view_all(use_all_regions: bool=False, center: bool=False):
    """View all objects in scene

    :param use_all_regions: All Regions, View selected for all regions
    :type use_all_regions: bool
    :param center: Center
    :type center: bool
    """
    pass


def get_session(id, password):
    """Get session. If the session is not exist for given id or
        password is wrong - return None. Else return ISession object.
        """


def get_parameter(store, name, klass=None):
    """Returns the parameter value given its name

    :param klass: if this parameter is a reference to another table, an instance
      of this klass will be returned.
    """
    res = store.execute(
        """SELECT field_value FROM parameter_data WHERE
                                    field_name = ?"""
        , (name,)).get_one()
    if not res:
        return None
    field_value = res[0]
    if klass:
        return store.find(klass, id=int(field_value)).one()
    return field_value


def _map_latest_release_options(release_option: str) ->str:
    """Overwrite Iris release options to be compatible with mozdownload."""
    if release_option == 'beta':
        return 'latest-beta'
    elif release_option == 'release':
        return 'latest'
    elif release_option == 'esr':
        return 'latest-esr'
    else:
        return 'nightly'


def call_args(call, args=None):
    """If args are none, don't pass them"""
    return [call] if args is None else [call, args]


def merge_fields(base, attach={}):
    """
    >>> merge_fields({'a': 1})
    {'a': 1}
    >>> merge_fields({'a': 1}, {'b': 2})
    {'a': 1, 'b': 2}
    """
    base.update(attach)
    return base


def is_real_pole(s):
    """Apply a threshold to determine if a pole should be treated as purely
    real, in which case its conjugate should not be used in models"""
    return abs(s.imag) < 0.001 * abs(s.real)


def get_references_from(content, name=None, depth=None):
    """Get all references where the given content is a source,
        optionaly tagged with name.

        If depth is mentioned, search all reacheable content from the
        given content in less than depth steps.
        """


def render_pep440_pre(pieces):
    """TAG[.post.devDISTANCE] -- No -dirty.

    Exceptions:
    1: no tags. 0.post.devDISTANCE
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance']:
            rendered += '.post.dev%d' % pieces['distance']
    else:
        rendered = '0.post.dev%d' % pieces['distance']
    return rendered


def getLibPath(libName, libPath, compiler, libExt):
    """
      get the lib path with the os / compiler specific prefix and file extension
  """
    libPath += '/'
    if compiler.startswith('gcc') or compiler.startswith('clang'):
        libPath += 'lib'
    libPath += libName + libExt
    return libPath


def getQuery(prompt='\nEnter query: ', error='Invalid query.'):
    """
	Prompt user for query
	Return a string of valid query
	"""
    while True:
        query = input(prompt)
        if query != '':
            return query
        else:
            print(error)


@staticmethod
def volumes_to_bind_dict(volumes):
    """
        Convert the volumes' list to the bind-mount dictionary format.

        * volumes: list of Volume instances
        * **return:** dictionary with bind mounts
        """
    result = {}
    for v in volumes:
        result[v.source] = {'bind': v.destination, 'mode': v.mode}
    return result


def list_of_target_hosts_sc_fqdn_ip(root):
    """
    Function returns list of target hosts as dictionary of fqdn and ip. Works only if nessus file comes from Tenable.sc.
    :param root: root element of scan file tree
    :return: dictionary of fqdn and ip
    """
    target_list = []
    target_hosts = root[0].find(
        "Preferences/ServerPreferences/preference/[name='TARGET']/value")
    if target_hosts is not None:
        target_hosts = target_hosts.text
        target_hosts_splitted = target_hosts.split(',')
        for target in target_hosts_splitted:
            target_splitted = target[:-1].split('[')
            if len(target_splitted) == 2:
                target_list.append({'target_fqdn': target_splitted[0],
                    'target_ip': target_splitted[1]})
    else:
        target_list = None
    return target_list


def is_number(value):
    """
    returns true if value can be cast to a float otherwise false
    """
    try:
        float(value)
        return True
    except ValueError:
        return False


@classmethod
def paper_doc_resolve_comment_details(cls, val):
    """
        Create an instance of this class set to the
        ``paper_doc_resolve_comment_details`` tag with value ``val``.

        :param PaperDocResolveCommentDetails val:
        :rtype: EventDetails
        """
    return cls('paper_doc_resolve_comment_details', val)


def _extract_braces_expression(line, starting_braces_re, open_braces_re,
    closing_braces_re):
    """
    This function is taken from Plim package: https://pypi.python.org/pypi/Plim/

    :param line: may be empty
    :type line: str
    :param starting_braces_re:
    :param open_braces_re:
    :param closing_braces_re:
    """
    match = starting_braces_re.match(line)
    if not match:
        return None
    open_brace = match.group('start_brace')
    buf = [open_brace]
    tail = line[len(open_brace):]
    braces_counter = 1
    while tail:
        current_char = tail[0]
        if closing_braces_re.match(current_char):
            braces_counter -= 1
            buf.append(current_char)
            if braces_counter:
                tail = tail[1:]
                continue
            return u''.join(buf), tail[1:]
        if open_braces_re.match(current_char):
            braces_counter += 1
            buf.append(current_char)
            tail = tail[1:]
            continue
        buf.append(current_char)
        tail = tail[1:]
    raise Exception('Unexpected end of a route definition: {}'.format(line))


def remove_cookie_by_name(cookiejar, name, domain=None, path=None):
    """Unsets a cookie by name, by default over all domains and paths.

    Wraps CookieJar.clear(), is O(n).
    """
    clearables = []
    for cookie in cookiejar:
        if cookie.name == name:
            if domain is None or domain == cookie.domain:
                if path is None or path == cookie.path:
                    clearables.append((cookie.domain, cookie.path, cookie.name)
                        )
    for domain, path, name in clearables:
        cookiejar.clear(domain, path, name)


def multi_all_finite(*data, **kwargs):
    """Check if all the float numbers in all the arrays are finite (used for AMP)

    Defined in src/operator/contrib/all_finite.cc:L133

    Parameters
    ----------
    data : NDArray[]
        Arrays
    num_arrays : int, optional, default='1'
        Number of arrays.
    init_output : boolean, optional, default=1
        Initialize output to 1.

    out : NDArray, optional
        The output NDArray to hold the result.

    Returns
    -------
    out : NDArray or list of NDArrays
        The output of this function.
    """
    return 0,


def pascal_row(n):
    """ Returns n-th row of Pascal's triangle
    """
    result = [1]
    x, numerator = 1, n
    for denominator in range(1, n // 2 + 1):
        x *= numerator
        x /= denominator
        result.append(x)
        numerator -= 1
    if n & 1 == 0:
        result.extend(reversed(result[:-1]))
    else:
        result.extend(reversed(result))
    return result


@staticmethod
def lock_format():
    """Get the human readable format of lock."""
    return 'hourly'


def year_extract(date):
    """

    Extract year from a date.
    Assumes `date` to contain a year of the form: YYYY.

    :param date: a string containing a year
    :type date: ``str``
    :return: the first year found in string
    :rtype: ``str`` or ``None``
    """
    year = ''
    for c in str(date):
        if len(year) == 4:
            break
        if c.isdigit():
            year += c
        elif not c.isdigit() and len(year) < 4:
            year = ''
    return year if len(year) == 4 else None


def roc(df, price, roc, n):
    """
    The Rate-of-Change (ROC) indicator, which is also referred to as simply
    Momentum, is a pure momentum oscillator that measures the percent change in
    price from one period to the next.

    Parameters:
        df (pd.DataFrame): DataFrame which contain the asset information.
        price (string): the column name of the price of the asset.
        roc (string): the column name for the rate of change values.
        n (int): the total number of periods.

    Returns:
        df (pd.DataFrame): Dataframe with roc of the asset calculated.

    """
    price_shift_n = df[price].shift(n)
    df[roc] = (df[price] - price_shift_n) / price_shift_n * 100
    df = df.dropna().reset_index(drop=True)
    return df


def process(proc_data):
    """
    Final processing to conform to the schema.

    Parameters:

        proc_data:   (dictionary) raw structured data to process

    Returns:

        Dictionary. Structured data with the following schema:

        {
          "time":     string,
          "uptime":   string,
          "users":    integer,
          "load_1m":  float,
          "load_5m":  float,
          "load_15m": float
        }
    """
    int_list = ['users']
    for key in int_list:
        if key in proc_data:
            try:
                key_int = int(proc_data[key])
                proc_data[key] = key_int
            except ValueError:
                proc_data[key] = None
    float_list = ['load_1m', 'load_5m', 'load_15m']
    for key in float_list:
        if key in proc_data:
            try:
                key_float = float(proc_data[key])
                proc_data[key] = key_float
            except ValueError:
                proc_data[key] = None
    return proc_data


@classmethod
def is_usable(cls):
    """
        Check whether this class is available for use.

        Since certain plugin implementations may require additional dependencies
        that may not yet be available on the system, this method should check
        for those dependencies and return a boolean saying if the implementation
        is usable.

        NOTES:
            - This should be a class method
            - When an implementation is deemed not usable, this should emit a
                warning detailing why the implementation is not available for
                use.

        :return: Boolean determination of whether this implementation is usable.
        :rtype: bool

        """
    return True


def render_pep440_old(pieces):
    """TAG[.postDISTANCE[.dev0]] .

    The ".dev0" means dirty.

    Eexceptions:
    1: no tags. 0.postDISTANCE[.dev0]
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance'] or pieces['dirty']:
            rendered += '.post%d' % pieces['distance']
            if pieces['dirty']:
                rendered += '.dev0'
    else:
        rendered = '0.post%d' % pieces['distance']
        if pieces['dirty']:
            rendered += '.dev0'
    return rendered


def format_mac(mac):
    """Formats MAC strings as returned from the device into well-known MAC format"""
    return ':'.join(map(''.join, zip(*([iter(mac)] * 2))))


def add_true_negatives(err, cnt_gt):
    """Update errors statistics of a specific method with missing detections"""
    matched = len(err['all'])
    missed = cnt_gt - matched
    zeros = [0] * missed
    err['<0.5m'].extend(zeros)
    err['<1m'].extend(zeros)
    err['<2m'].extend(zeros)
    err['matched'] = 100 * matched / cnt_gt


@staticmethod
def get_pretty_name(title: str, artist: str, bold: bool=False) ->str:
    """ Returns a formatted name of song """
    mod = ''
    if bold:
        mod = '**'
    return f'{mod}"{title}"{mod} by {mod}{artist}{mod}'


def p_comp_if_1(p):
    """comp_if : IF old_test"""
    p[0] = [p[2]], []


def parse_yr_filter(dt_df, years, label):
    """
    Parse string year filter and apply it to datetime-indexed
    DataFrame.

    Arguments:
        dt_df (:obj:`pandas.DataFrame`): datetime-indexed DataFrame
        years (str or int): years to select, e.g. 2015 or 2000-2010
        label (str): identifier to print warning message if ``years``
            filter partially overlaps with actual date index

    Returns:

        ret (tuple of (:obj:`pandas.DataFrame`, str)): first element is
            input DataFrame ``dt_df`` indexed to ``years`` filter,
            second element is string of year range, e.g. '2001_2011'

    Example:

        >>> df = pd.DataFrame(index=pd.date_range('2000', '2015'))
        >>> df, yr_str = parse_yr_filter(df, '1998-2002', 'station1')
        WARNING: data for station1 starts in 2000 but you gave 1998
        Years used will only include 2000 to 2002

        Now df will only contain indices with dates between 2000 and
        2002 and

        >>> yr_str
        '1998_2002'

    Raises:
        ValueError: if ``years`` is invalid or not found
            in time series index of DataFrame.

    """
    err_msg = '{} is not a valid years option,\n'.format(years
        ), 'use single or range e.g. 2015 or 2000-2010'
    if years == 'all':
        year_str = 'all_yrs'
    else:
        try:
            if years and isinstance(years, str) and '-' in years:
                start, end = years.strip().split('-')
                year_str = '{}_{}'.format(start, end)
                data_start = start
                data_end = end
                dt_df = dt_df.loc[start:end]
                if not start in dt_df.index:
                    data_start = dt_df.index.year.min()
                    print('WARNING: data for {l} starts in {d}'.format(l=
                        label, d=data_start) + ' but you gave {s}'.format(s
                        =start))
                if not end in dt_df.index:
                    data_end = dt_df.index.year.max()
                    print('WARNING: data for {l} ends in {d}'.format(l=
                        label, d=data_end) + ' but you gave {e}'.format(e=end))
                if data_start != start or data_end != end:
                    print('Years used will only include {} to {}'.format(
                        data_start, data_end))
            else:
                year_str = str(int(years))
                if not len(year_str) == 4:
                    raise ValueError(err_msg)
                if not years in dt_df.index:
                    print('WARNING:', label, 'is missing data', 'for year:',
                        years)
                    data_start = dt_df.index.year.min()
                    data_end = dt_df.index.year.max()
                    print('Years used will only include {} to {}'.format(
                        data_start, data_end))
                else:
                    dt_df = dt_df.loc[years]
        except:
            raise ValueError(err_msg)
    ret = dt_df, year_str
    return ret


def gen_internal_line_trace_py(s, packed_ports):
    """Return the line trace method body that shows all CFFI ports."""
    return "    return ''"


def add_site():
    """Add a new site to the database
    """
    pass


def log(data=None, out=None, name=None, **kwargs):
    """Returns element-wise Natural logarithmic value of the input.

    The natural logarithm is logarithm in base *e*, so that ``log(exp(x)) = x``

    The storage type of ``log`` output is always dense

    Defined in src/operator/tensor/elemwise_unary_op_logexp.cc:L76

    Parameters
    ----------
    data : NDArray
        The input array.

    out : NDArray, optional
        The output NDArray to hold the result.

    Returns
    -------
    out : NDArray or list of NDArrays
        The output of this function.
    """
    return 0,


def scale_demand_twh(context, new_demand):
    """
    Scale demand to new_demand TWh.

    >>> class C: pass
    >>> c = C()
    >>> c.demand = pd.DataFrame([100]*10)
    >>> scale_demand_twh(c, 0.0002)
    >>> print(c.demand.loc[0])
    0    20.0
    Name: 0, dtype: float64
    """
    total_demand = context.demand.values.sum()
    new_demand *= 10 ** 6
    context.demand *= new_demand / total_demand


def degrees_to_hours(angle):
    """Converts degrees to decimal hours

    :param angle: angle in degrees
    :return: angle in decimal hours

    """
    return angle / 15.0


def get_bounding_box(in_file):
    """ Retrieve the bounding box of a volume in millimetres."""
    from itertools import product
    import nibabel as nib
    import numpy as np
    img = nib.load(in_file)
    corners = np.array(list(product([0, 1], repeat=3)))
    corners = corners * (np.array(img.shape[:3]) - 1)
    corners = img.affine.dot(np.hstack([corners, np.ones((8, 1))]).T).T[:, :3]
    low_corner = np.min(corners, axis=0)
    high_corner = np.max(corners, axis=0)
    return [low_corner.tolist(), high_corner.tolist()]


def index_startswith_substring(the_list, substring):
    """What is this? What is it used for?"""
    for i, s in enumerate(the_list):
        if s.startswith(substring):
            return i
    return -1


def addargs(parser):
    """ Parse command line arguments for s4cmb """
    parser.add_argument('-inifile', dest='inifile', required=True, help=
        'Configuration file with parameter values.')
    parser.add_argument('-tag', dest='tag', required=True, help=
        'Tag name to identify your run. E.g. run_0_crosstalk.')
    parser.add_argument('-inifile_xpure', dest='inifile_xpure', default=
        None, help='Configuration file with xpure parameter values.')
    parser.add_argument('-type_hwp', dest='type_hwp', default=None, help=
        'The type of HWP that you want to mount on your instrument.')
    parser.add_argument('-freq_hwp', dest='freq_hwp', default=None, type=
        float, help='The frequency of rotation of the HWP in Hz.')
    parser.add_argument('-angle_hwp', dest='angle_hwp', default=None, type=
        float, help='The offset of the HWP in degree.')
    parser.add_argument('-input_filename', dest='input_filename', required=
        True, nargs='+', help='Input fits with alms.')
    parser.add_argument('-array_noise_seed', dest='array_noise_seed',
        required=True, type=int, help='Seed to generate noise.')
    parser.add_argument('-sim_number', dest='sim_number', required=True,
        type=int, help='Number of the sim.')
    parser.add_argument('-folder_out', dest='folder_out', required=True,
        help='Name of the output folder.')
    parser.add_argument('-nside_in', dest='nside_in', required=True, type=
        int, help='Name of the output folder.')
    parser.add_argument('-fwhm_in', dest='fwhm_in', required=True, type=
        float, help='Name of the output folder.')
    parser.add_argument('-gain_mean', dest='gain_mean', default=1.0, type=
        float, help='Relative gain variation (mean).')
    parser.add_argument('-gain_variation', dest='gain_variation', default=
        0.05, type=float, help='Relative gain variation (sigma).')
    parser.add_argument('-nretuning_time', dest='nretuning_time', default=1,
        type=int, help='How many times to perform a retuning.')
    parser.add_argument('-drift_model', dest='drift_model', default=
        'linear', type=str, help='How to model the variation: step or linear')
    parser.add_argument('-seed', dest='seed', default=5438765, type=int,
        help='Control the random seed used to generate gain coefficients.')


def assert_popup_refresh(response):
    """assert the colorbox response redirects to the right page"""
    if response.status_code != 200:
        raise Exception(
            "colobox Response didn't redirect as expected: Response code was {0} (expected 200)"
            .format(response.status_code))
    expected_content = (
        b'<script>$.colorbox.close(); window.location=window.location;</script>'
        )
    if response.content != expected_content:
        raise Exception("Don't refresh page")


@classmethod
def initialize_with(cls, numpy_func, shape, *args, qhape=None, charge=None,
    invar=None, dirs=None, **kwargs):
    """Use the given `numpy_func` to initialize a tensor of `shape`."""
    shape = cls.flatten_shape(shape)
    res = numpy_func(shape, *args, **kwargs).view(cls)
    return res


def convert_y_domain(mpl_plot_bounds, mpl_max_y_bounds):
    """Map y dimension of current plot to plotly's domain space.

    The bbox used to locate an axes object in mpl differs from the
    method used to locate axes in plotly. The mpl version locates each
    axes in the figure so that axes in a single-plot figure might have
    the bounds, [0.125, 0.125, 0.775, 0.775] (x0, y0, width, height),
    in mpl's figure coordinates. However, the axes all share one space in
    plotly such that the domain will always be [0, 0, 1, 1]
    (x0, y0, x1, y1). To convert between the two, the mpl figure bounds
    need to be mapped to a [0, 1] domain for x and y. The margins set
    upon opening a new figure will appropriately match the mpl margins.

    Optionally, setting margins=0 and simply copying the domains from
    mpl to plotly would place axes appropriately. However,
    this would throw off axis and title labeling.

    Positional arguments:
    mpl_plot_bounds -- the (x0, y0, width, height) params for current ax **
    mpl_max_y_bounds -- overall (y0, y1) bounds for all axes **

    ** these are all specified in mpl figure coordinates

    """
    mpl_y_dom = [mpl_plot_bounds[1], mpl_plot_bounds[1] + mpl_plot_bounds[3]]
    plotting_height = mpl_max_y_bounds[1] - mpl_max_y_bounds[0]
    y0 = (mpl_y_dom[0] - mpl_max_y_bounds[0]) / plotting_height
    y1 = (mpl_y_dom[1] - mpl_max_y_bounds[0]) / plotting_height
    return [y0, y1]


def getattritem(o, a):
    """
    Get either attribute or item `a` from a given object `o`. Supports multiple evaluations, for example
    `getattritem(o,'one.two')` would get `o.one.two`, `o['one']['two']`, etc.
    :param o: Object
    :param a: Attribute or Item index. Can contain `.`, in which case the final value is obtained.
    :return: Value
    """
    flds = a.split('.')
    for x in flds:
        if x in dir(o):
            o = getattr(o, x)
        else:
            o = o[x]
    return o


@classmethod
def from_keys(cls, iterable, value=None):
    """T.from_keys(S[,v]) -> New tree with keys from S and values equal to v."""
    tree = cls()
    for key in iterable:
        tree.insert(key, value)
    return tree


@staticmethod
def add_cmdline_args(parser):
    """Add command line args specific to this agent."""
    parser = parser.add_argument_group('IrBaseline Arguments')
    parser.add_argument('-lp', '--length_penalty', type=float, default=0.5,
        help='length penalty for responses')
    parser.add_argument('-hsz', '--history_size', type=int, default=1, help
        =
        'number of utterances from the dialogue history to take use as the query'
        )
    parser.add_argument('--label_candidates_file', type=str, default=None,
        help='file of candidate responses to choose from')


def deliver(lims_sample):
    """Figure out how to deliver results for a sample.

    Answers:
        - whether to delivery to Scout
        - where to deliver raw data
        - wether we shoud delivery extra assets
    """
    extras = []
    to_scout = 'scout' in lims_sample.udf.get('Data Analysis', 'notFound')
    payload = {'target': 'caesar'}
    customer = lims_sample.udf['customer']
    uppmax_project = lims_sample.udf.get('uppmax_project')
    if uppmax_project:
        payload = {'target': 'uppmax', 'project': uppmax_project}
    if customer == 'cust002':
        extras.append('bam')
    elif customer == 'cust009':
        payload = {}
    elif customer in ('cust008', 'cust016', 'cust019', 'cust020'):
        payload = {'target': 'sll-sthlm@medstore.sahlgrenska.gu.se'}
    elif customer == 'cust000':
        payload = {'target': 'rasta'}
    return {'scout': to_scout, 'customer': customer, 'raw_data': payload,
        'extras': extras}


def select_more():
    """Select those bones connected to the initial selection

    """
    pass


def _space_savvy_standard_page(page_counter, chars_per_page):
    """
    Standard page excluding multiple spaces.

    This dialect reduce extra spaces by counting page number as *<chars_without_spaces> +
    <word_count>*. Therefore every word boundary is treated as single space only.

    Space-savvy counting is suited for technical documentation, programming texts, markup languages
    like reStructuredText or Markdown. They typically contain a lot of space characters just as
    indentation or formatting etc.

    Properly round to integer number for humans. Always returns at least 1 or more.

    :param page_counter: ``PageCounter`` instance
    :param chars_per_page: how many characters has a page
    :return: number of pages
    """
    length = page_counter.chars_without_spaces() + page_counter.word_count()
    pages = round(length / chars_per_page)
    if pages == 0:
        return 1
    return pages


def autodiscover(blacklist=[]):
    """
    Automagically discover custom dashboards and menus for installed apps.
    Optionally you can pass a ``blacklist`` of apps that you don't want to
    provide their own app index dashboard.
    """
    import imp
    from django.conf import settings
    try:
        from importlib import import_module
    except ImportError:
        from django.utils.importlib import import_module
    blacklist.append('admin_tools.dashboard')
    blacklist.append('admin_tools.menu')
    blacklist.append('admin_tools.theming')
    for app in settings.INSTALLED_APPS:
        if app in blacklist:
            continue
        try:
            app_path = import_module(app).__path__
        except AttributeError:
            continue
        try:
            imp.find_module('dashboard', app_path)
        except ImportError:
            continue
        import_module('%s.dashboard' % app)


def _translate_attachment_ref(attachment_ref):
    """Building old style connection_info by adding the 'data' key back."""
    translated_con_info = {}
    connection_info_data = attachment_ref.pop('connection_info', None)
    if connection_info_data:
        connection_info_data.pop('attachment_id', None)
        translated_con_info['driver_volume_type'] = connection_info_data.pop(
            'driver_volume_type', None)
        translated_con_info['data'] = connection_info_data
        translated_con_info['status'] = attachment_ref.pop('status', None)
        translated_con_info['instance'] = attachment_ref.pop('instance', None)
        translated_con_info['attached_at'] = attachment_ref.pop('attached_at',
            None)
        translated_con_info['detached_at'] = attachment_ref.pop('detached_at',
            None)
        for k, v in attachment_ref.items():
            if k not in ('id', 'attach_mode'):
                translated_con_info[k] = v
    attachment_ref['connection_info'] = translated_con_info
    return attachment_ref


def dms_to_deg(dms):
    """
    Convert angles degrees, minutes, seconds to float degrees.

    Param: dms - Object of type dms representing angle.

    Returns angle in degrees (float).
    """
    degrees = dms.degrees + dms.minutes / 60.0 + dms.seconds / 3600.0
    if dms.neg:
        degrees *= -1
    return degrees


def decoded(output, mode='stdout', encoding='utf-8'):
    """Decode Result of Command."""
    return getattr(output, mode).decode(encoding)


def to_text(value, encoding='utf-8', errors='ignore'):
    """Convert value to unicode, default encoding is utf-8

    :param value: Value to be converted
    :param encoding: Desired encoding
    """
    if not value:
        return ''
    if isinstance(value, str):
        return value
    if isinstance(value, bytes):
        return value.decode(encoding, errors)
    return str(value)


def set_metrics(metrics):
    """
    Set properly metrics that we want to load
    @metrics: list
    """
    result = []
    for i in metrics:
        d = {'expression': i}
        result.append(d)
    return result