def compare_grimoires(uid_1, uid_2):
    ''' compare two items of the same type '''
    try:
        data = (helpers.get_node(uid_1), helpers.get_node(uid_2))
    except NameError:
        return redirect('/compare')

    nodes = [d['node'] for d in data]
    rels = [d['rels'] for d in data]

    data = {
        'item_1': nodes[0],
        'item_2': nodes[1]
    }

    # ----- check that item 1 and item 2 are both grimoires
    if nodes[0]['parent_label'] != 'parent:book' or nodes[1]['parent_label'] != 'parent:book':
        return redirect(nodes[0]['link'])

    # ----- get all shared items to list out
    max_list_size = 0
    # keeps track of the biggest list
    data['shared_list'] = {}
    for entity in entities + ['spell']:
        entity_lists = [
            extract_rel_list_by_type(rel_list, 'lists', 'end', label=entity)
            for rel_list in rels]
        data['shared_list'][entity] = intersection(entity_lists[0],
                                                   entity_lists[1])
        max_list_size = max_list_size \
                        if len(data['shared_list'][entity]) < max_list_size \
                        else len(data['shared_list'][entity])
    data['default_collapse'] = max_list_size > 20

    data['title'] = '"%s" vs "%s"' % (nodes[0]['props']['identifier'],
                                      nodes[1]['props']['identifier'])

    # ----- Properties table
    grim_items = [grimoire_item(nodes[i], rels[i]) for i in range(2)]
    details = [g['details'] for g in grim_items]
    for d in details:
        d['author'] = d.get('author', [{'text': 'Unknown'}])
    keys = list(set(details[0].keys()) & set(details[1].keys()))
    keys = [k for k in keys if k not in ['Name', 'online_edition']]
    data['props'] = {k: [details[0][k], details[1][k]] for k in keys}

    # ----- compare remaining rels
    rels = [helpers.exclude_rels(rel_list, ['lists']) for rel_list in rels]

    # pull out rels between the two grimoires
    data['self_rels'] = []
    for rel in rels[0]:
        if rel['start']['props']['uid'] in [uid_1, uid_2] and \
           rel['end']['props']['uid'] in [uid_1, uid_2]:
            data['self_rels'].append(rel)

    exclude_ids = [r['id'] for r in data['self_rels']]
    rels[0] = [r for r in rels[0] if not r['id'] in exclude_ids]
    rels[1] = [r for r in rels[1] if not r['id'] in exclude_ids]

    # make a dictionary for each relationship type with an empty array value
    # ie: {'wrote': [[], []], 'influenced': [[], []]}
    types = {t: [[], []] for t in \
             list(set([r['type'] for r in rels[0] + rels[1]]))}

    # populate types dictionary with every relationship of that type
    for (i, rel_list) in enumerate(rels):
        for rel in rel_list:
            types[rel['type']][i] += [rel]

    # remove types that only exist for one item or the other
    types = {k: v for (k, v) in types.items() if v[0] and v[1]}

    data['same'] = {'start': [], 'end': []}
    for rel_type in types:
        relset = types[rel_type]
        direction = 'end' if \
                    relset[0][0]['start']['props']['uid'] == uid_1 \
                    else 'start'

        subjects = [[rel[direction] for rel in r] for r in relset]
        same_uids = [
            i['props']['uid'] for i in intersection(subjects[0], subjects[1])
        ]
        same_rels = [rel for rel in relset[0] if \
                     rel[direction]['props']['uid'] in same_uids]

        data['same']['start' if direction == 'end' else 'end'] += same_rels

    data['same'] = {
        'start': helpers.combine_rels(data['same']['start']),
        'end': helpers.combine_rels(data['same']['end'])
    }

    data['grimoires'] = graph.get_all('grimoire')['nodes']

    return render_template(request.url, 'compare.html', **data)
def compare_grimoires(uid_1, uid_2):
    ''' compare two items of the same type '''
    try:
        data = (helpers.get_node(uid_1), helpers.get_node(uid_2))
    except NameError:
        return redirect('/compare')

    nodes = [d['node'] for d in data]
    rels = [d['rels'] for d in data]

    data = {'item_1': nodes[0], 'item_2': nodes[1]}

    # ----- check that item 1 and item 2 are both grimoires
    if nodes[0]['parent_label'] != 'parent:book' or \
            nodes[1]['parent_label'] != 'parent:book':
        return redirect(nodes[0]['link'])

    # ----- get all shared items to list out
    max_list_size = 0
    # keeps track of the biggest list
    data['shared_list'] = {}
    for entity in entities + ['spell']:
        entity_lists = [
            extract_rel_list_by_type(rel_list, 'lists', 'end', label=entity)
            for rel_list in rels
        ]
        data['shared_list'][entity] = intersection(entity_lists[0],
                                                   entity_lists[1])
        max_list_size = max_list_size \
                        if len(data['shared_list'][entity]) < max_list_size \
                        else len(data['shared_list'][entity])
    data['default_collapse'] = max_list_size > 20

    data['title'] = '"%s" vs "%s"' % (nodes[0]['props']['identifier'],
                                      nodes[1]['props']['identifier'])

    # ----- Properties table
    grim_items = [grimoire_item(nodes[i], rels[i]) for i in range(2)]
    details = [g['details'] for g in grim_items]
    for d in details:
        d['author'] = d.get('author', [{'text': 'Unknown'}])
    keys = list(set(details[0].keys()) & set(details[1].keys()))
    keys = [k for k in keys if k not in ['Name', 'online_edition']]
    data['props'] = {k: [details[0][k], details[1][k]] for k in keys}

    # ----- compare remaining rels
    rels = [helpers.exclude_rels(rel_list, ['lists']) for rel_list in rels]

    # pull out rels between the two grimoires
    data['self_rels'] = []
    for rel in rels[0]:
        if rel['start']['props']['uid'] in [uid_1, uid_2] and \
           rel['end']['props']['uid'] in [uid_1, uid_2]:
            data['self_rels'].append(rel)

    exclude_ids = [r['id'] for r in data['self_rels']]
    rels[0] = [r for r in rels[0] if not r['id'] in exclude_ids]
    rels[1] = [r for r in rels[1] if not r['id'] in exclude_ids]

    # make a dictionary for each relationship type with an empty array value
    # ie: {'wrote': [[], []], 'influenced': [[], []]}
    types = {t: [[], []] for t in \
             list(set([r['type'] for r in rels[0] + rels[1]]))}

    # populate types dictionary with every relationship of that type
    for (i, rel_list) in enumerate(rels):
        for rel in rel_list:
            types[rel['type']][i] += [rel]

    # remove types that only exist for one item or the other
    types = {k: v for (k, v) in types.items() if v[0] and v[1]}

    data['same'] = {'start': [], 'end': []}
    for rel_type in types:
        relset = types[rel_type]
        direction = 'end' if \
                    relset[0][0]['start']['props']['uid'] == uid_1 \
                    else 'start'

        subjects = [[rel[direction] for rel in r] for r in relset]
        same_uids = [
            i['props']['uid'] for i in intersection(subjects[0], subjects[1])
        ]
        same_rels = [rel for rel in relset[0] if \
                     rel[direction]['props']['uid'] in same_uids]

        data['same']['start' if direction == 'end' else 'end'] += same_rels

    data['same'] = {
        'start': helpers.combine_rels(data['same']['start']),
        'end': helpers.combine_rels(data['same']['end'])
    }

    data['grimoires'] = graph.get_all('grimoire')['nodes']

    return render_template(request.url, 'compare.html', **data)
def item(label, uid):
    ''' generic page for an item
    :param label: the desired neo4j label
    :param uid: the human-readable uid of the node
    :return: customized data for this label rendered item page template
    '''

    # load and validate url data
    label = helpers.sanitize(label)
    if not graph.validate_label(label):
        logging.error('Invalid label %s', label)
        return render_template(request.url,
                               'label-404.html',
                               labels=graph.get_labels())

    try:
        data = helpers.get_node(uid)
    except NameError:
        items = graph.get_all(label)
        return render_template(request.url, 'item-404.html',
                               items=items['nodes'],
                               search=graph.search(uid)['nodes'],
                               label=label)

    node = data['node']
    rels = data['rels']

    # ----- page header/metadata
    title = node['props']['identifier']

    # ----- formatted data
    switch = {
        'parent:book': grimoire_item,
        'parent:entity': entity_item,
        'art': art_item,
        'language': language_item,
        'edition': edition_item,
        'publisher': publisher_item,
        'editor': editor_item,
        'default': generic_item,
        'spell': spell_item,
        'talisman': spell_item,
        'parent:ingredient': ingredient_item,
        'outcome': outcome_item
    }

    key = node['parent_label'] if node['parent_label'] in switch else \
          (label if label in switch else 'default')
    item_data = switch[key](node, rels)

    item_data['rels'] = helpers.combine_rels(item_data['rels'])

    # ----- sidebar
    sidebar = []
    related = graph.related(uid, label)
    if related['nodes']:
        sidebar = [{'title': 'Similar %s' % \
                    helpers.capitalize_filter(helpers.pluralize(label)),
                    'data': related['nodes']}]
    sidebar += get_others(data['rels'], node)

    if not item_data['content'] and not item_data['excerpts']:
        item_data['content'] = 'The %s "%s."' % \
                               (helpers.format_filter(label),
                                helpers.unthe(title))

    max_main_length = max([len(i['data']) for i in item_data['main']] + [0])
    default_collapse = len(item_data['main']) > 2 or max_main_length > 30

    return render_template(request.url, 'item.html',
                           data=item_data,
                           title=title,
                           label=label,
                           sidebar=sidebar,
                           default_collapse=default_collapse)
def item(label, uid):
    ''' generic page for an item
    :param label: the desired neo4j label
    :param uid: the human-readable uid of the node
    :return: customized data for this label rendered item page template
    '''

    # load and validate url data
    label = helpers.sanitize(label)
    if not graph.validate_label(label):
        logging.error('Invalid label %s', label)
        return render_template(request.url,
                               '404.html',
                               labels=graph.get_labels())

    try:
        data = helpers.get_node(uid)
    except NameError:
        items = graph.get_all(label)
        return render_template(request.url,
                               '404.html',
                               items=items['nodes'],
                               search=graph.search(uid)['nodes'],
                               label=label)

    node = data['node']
    rels = data['rels']

    # ----- page header/metadata
    title = node['props']['identifier']

    # ----- formatted data
    switch = {
        'parent:book': grimoire_item,
        'parent:entity': entity_item,
        'art': art_item,
        'language': language_item,
        'edition': edition_item,
        'publisher': publisher_item,
        'editor': editor_item,
        'default': generic_item,
        'spell': spell_item,
        'talisman': spell_item,
        'parent:ingredient': ingredient_item,
        'outcome': outcome_item
    }

    key = node['parent_label'] if node['parent_label'] in switch else \
          (label if label in switch else 'default')
    item_data = switch[key](node, rels)

    item_data['rels'] = helpers.combine_rels(item_data['rels'])

    # ----- sidebar
    sidebar = []
    related = graph.related(uid, label)
    if related['nodes']:
        sidebar = [{'title': 'Similar %s' % \
                    helpers.capitalize_filter(helpers.pluralize(label)),
                    'data': related['nodes']}]
    sidebar += get_others(data['rels'], node)

    if not item_data['content'] and not item_data['excerpts']:
        item_data['content'] = 'The %s "%s."' % \
                               (helpers.format_filter(label),
                                helpers.unthe(title))

    max_main_length = max([len(i['data']) for i in item_data['main']] + [0])
    default_collapse = len(item_data['main']) > 2 or max_main_length > 30

    return render_template(request.url,
                           'item.html',
                           data=item_data,
                           title=title,
                           label=label,
                           sidebar=sidebar,
                           default_collapse=default_collapse)