def flatten(weights):
    """
    Flatten weights into a long DataFrame.

    Parameters
    ----------
    weights: pandas.DataFrame or dict
        A DataFrame of instrument weights with a MultiIndex where the top level
        contains pandas. Timestamps and the second level is instrument names.
        The columns consist of generic names. If dict is given this should be
        a dict of pandas.DataFrame in the above format, with keys for different
        root generics, e.g. 'CL'

    Returns
    -------
    A long DataFrame of weights, where columns are "date", "contract",
    "generic" and "weight". If a dictionary is passed, DataFrame will contain
    additional colum "key" containing the key value and be sorted according to
    this key value.

    Example
    -------
    >>> import pandas as pd
    >>> import mapping.util as util
    >>> vals = [[1, 0], [0, 1], [1, 0], [0, 1]]
    >>> widx = pd.MultiIndex.from_tuples([(pd.Timestamp('2015-01-03'), 'CLF5'),
    ...                                   (pd.Timestamp('2015-01-03'), 'CLG5'),
    ...                                   (pd.Timestamp('2015-01-04'), 'CLG5'),
    ...                                   (pd.Timestamp('2015-01-04'), 'CLH5')])
    >>> weights = pd.DataFrame(vals, index=widx, columns=["CL1", "CL2"])
    >>> util.flatten(weights)
    """  # NOQA
    if isinstance(weights, pd.DataFrame):
        wts = weights.stack().reset_index()
        wts.columns = ["date", "contract", "generic", "weight"]
    elif isinstance(weights, dict):
        wts = []
        for key in sorted(weights.keys()):
            wt = weights[key].stack().reset_index()
            wt.columns = ["date", "contract", "generic", "weight"]
            wt.loc[:, "key"] = key
            wts.append(wt)
        wts = pd.concat(wts, axis=0).reset_index(drop=True)
    else:
        raise ValueError("weights must be pd.DataFrame or dict")

    return wts