def create_graph(contacts, resi_labels):
    """
    Creates a graph from the contacts and residue labels formatted as a dict that can be trivially dumped to a json that
    can be read by flareplot. An "edge" key mapping to a list of edge specifiers with "name1", "name2", and "frames"
    attributes will always be generated and if `resi_labels` isn't `None` then the "trees" and "tracks" will be
    generated as well.

    Parameters
    ----------
    contacts : list of tuples of (str, str, tuple, tuple [[, tuple], tuple])
        Each entry specifies a frame-number, an interaction type, and 2 to 4 atom-tuples depending on the interaction
        type. Water mediated and water-water mediated interactions will have waters in the third and fourth tuples.

    resi_labels : dict of (str : dict of (str : str))
        Each key is a residue identifier and the associated value is a dictionary with the label, tree-path, and color
        that are consistent with the format of flareplots.

    Returns
    -------
    dict of str : list
        The types of the list contents varies depending on the key, but the format corresponds to the specifications of
        jsons used as input for flareplot. For example
        >>> {
        >>>   "edges": [
        >>>     {"name1": "ARG1", "name2": "ALA3", "frames": [0,4,10]},
        >>>     {"name1": "ALA3", "name2": "THR2", "frames": [1,2]}
        >>>   ],
        >>>   "trees": [
        >>>     {"treeName": "DefaultTree", "treePaths": ["Group1.ARG1", "Group1.THR2", "Group2.ALA3", "Group2.CYS4"]}
        >>>   ],
        >>>   "tracks": [
        >>>     {"trackName": "DefaultTrack", "trackProperties": [
        >>>       {"nodeName": "ARG1", "size": 1.0, "color": "red"},
        >>>       {"nodeName": "THR2", "size": 1.0, "color": "red"},
        >>>       {"nodeName": "ALA3", "size": 1.0, "color": "blue"},
        >>>       {"nodeName": "CYS4", "size": 1.0, "color": "blue"}
        >>>     ]}
        >>>   ]
        >>> }
    """
    ret = {
        "edges": []
    }

    # print(resi_labels)
    # Strip atom3, atom4, and atom names
    # unique_chains = set([c[2][0] for c in contacts] + [c[3][0] for c in contacts])
    contacts = [(c[0], c[1], c[2][0:3], c[3][0:3]) for c in contacts]

    resi_edges = {}
    for contact in contacts:
        # Compose a key for atom1 and atom2 that ignores the order of residues
        a1_key = ":".join(contact[2][0:3])
        a2_key = ":".join(contact[3][0:3])
        if a1_key == a2_key:
            continue
        if a1_key > a2_key:
            a1_key, a2_key = a2_key, a1_key
        contact_key = a1_key + a2_key

        # Look up labels
        if resi_labels:
            if a1_key not in resi_labels or a2_key not in resi_labels:
                print("Omitting contact "+str(contact)+" as it doesn't appear in flare-label file")
                continue
            a1_label = resi_labels[a1_key]["label"]
            a2_label = resi_labels[a2_key]["label"]
        else:
            a1_label = a1_key
            a2_label = a2_key

        # Create contact_key if it doesn't exist
        if contact_key not in resi_edges:
            edge = {"name1": a1_label, "name2": a2_label, "frames": []}
            resi_edges[contact_key] = edge
            ret["edges"].append(edge)

        resi_edges[contact_key]["frames"].append(int(contact[0]))

    # Sort edge frames and ensure that there are no duplicates
    for e in ret["edges"]:
        e["frames"] = sorted(set(e["frames"]))

    # Create "trees" and "tracks" sections if resi_labels specified
    if resi_labels is not None:
        tree = {"treeLabel": "DefaultTree", "treePaths": []}
        ret["trees"] = [tree]

        track = {"trackLabel": "DefaultTrack", "trackProperties": []}
        ret["tracks"] = [track]

        for rlabel in resi_labels.values():
            tree["treePaths"].append(rlabel["treepath"])
            track["trackProperties"].append({
                "nodeName": rlabel["label"],
                "color": rlabel["color"],
                "size": 1.0
            })

    return ret