def group_permissions(permissions):
    """
    Groups a permissions list

    Returns a dictionary, with permission types as keys and sets of entities
    with access to the resource as values, e.g.:

        {
            'organisation_id': {
                'org1': set(['rw', 'r', 'w']),
                'org2': set(['-']),
                'org3': set(['r', 'w']),
            },
            'all': set(['r'])
        }

    'org1' has 'rw' access to the resource, 'org2' is denied access and 'org3'
    has 'r' & 'w' access (the same as 'rw'). Note that 'rw' will always result
    in 'rw', 'r' & 'w' in the set to make checks easier.

    If present in the resource's permissions, the 'all' permission type is an
    exception in that it's value is just a set instead of a dictionary.

    :param permissions: a list of permissions
    :returns: defaultdict
    """
    groups = defaultdict(lambda: defaultdict(set))

    for p in sorted(permissions, key=itemgetter('type')):
        permission_set = groups[p['type']][p.get('value')]
        permission_set.add(p['permission'])

        if p['permission'] == 'rw':
            permission_set.update({'r', 'w'})

    # the 'all' permission type always has None as the value
    groups['all'] = groups['all'][None]

    return groups
Groups a permissions list

    Returns a dictionary, with permission types as keys and sets of entities
    with access to the resource as values, e.g.:

        {
            'organisation_id': {
                'org1': set(['rw', 'r', 'w']),
                'org2': set(['-']),
                'org3': set(['r', 'w']),
            },
            'all': set(['r'])
        }

    'org1' has 'rw' access to the resource, 'org2' is denied access and 'org3'
    has 'r' & 'w' access (the same as 'rw'). Note that 'rw' will always result
    in 'rw', 'r' & 'w' in the set to make checks easier.

    If present in the resource's permissions, the 'all' permission type is an
    exception in that it's value is just a set instead of a dictionary.

    :param permissions: a list of permissions
    :returns: defaultdict