def grimoire_item(node, rels): ''' grimoire item page :param node: the item node :param rels: default relationship list :return: customized data for this label ''' data = generic_item(node, rels) data['rels'] = helpers.exclude_rels(data['rels'], [ 'lists', 'has', 'includes', 'wrote', 'was_written_in']) data['details']['date'] = [{'text': helpers.grimoire_date(node['props'])}] authors = extract_rel_list_by_type(rels, 'wrote', 'start') authors = extract_details(authors) if authors: data['details']['author'] = authors languages = extract_rel_list_by_type(rels, 'was_written_in', 'end') if languages: data['details']['language'] = extract_details(languages) editions = extract_rel_list(rels, 'edition', 'end') if editions: data['sidebar'].append({ 'title': 'Editions', 'data': editions }) for entity in entities: items = extract_rel_list_by_type(rels, 'lists', 'end', label=entity) if len(items): item_json = { 'title': helpers.pluralize(entity), 'data': items, 'many': True, 'compare': get_comparison(node, entity) } data['main'].append(item_json) spells = extract_rel_list(rels, 'spell', 'end') if len(spells): data['main'].append({ 'title': 'Spells', 'data': spells, 'many': False, 'compare': get_comparison(node, 'spell') }) talisman = extract_rel_list(rels, 'talisman', 'end') if len(talisman): data['main'].append({ 'title': 'Talismans', 'data': talisman, 'many': False }) return data
def entity_item(node, rels): ''' entity item page :param node: the item node :param rels: default relationship list :return: customized data for this label ''' data = generic_item(node, rels) # removes duplication of two-way sister rels rels = [r for r in rels if r['type'] != 'is_a_sister_of' \ or r['end']['id'] != node['id']] data['rels'] = helpers.exclude_rels(data['rels'], [ 'lists', 'teaches', 'skilled_in', 'serves', 'facilitates', 'appears_like', 'can', 'for' ]) grimoires = extract_rel_list(rels, 'grimoire', 'start') + \ extract_rel_list(rels, 'book', 'start') if grimoires: data['sidebar'].append({'title': 'Grimoires', 'data': grimoires}) outcomes = extract_rel_list_by_type(rels, 'for', 'end') if outcomes: data['main'].append({'title': 'Powers', 'data': outcomes}) appearance = extract_rel_list(rels, 'creature', 'end') if appearance: data['main'].append({ 'title': 'Appearance', 'data': appearance, 'many': True }) serves = extract_rel_list_by_type(rels, 'serves', 'end') serves = [ s for s in serves if not s['props']['uid'] == node['props']['uid'] ] if serves: data['main'].append({'title': 'Serves', 'data': serves}) servants = extract_rel_list_by_type(rels, 'serves', 'start') servants = [ s for s in servants if not s['props']['uid'] == node['props']['uid'] ] if servants: data['main'].append({'title': 'Servants', 'data': servants}) if not data['content']: content = 'The %s %s appears in %s' % \ (helpers.format_filter(node['label']), node['props']['identifier'], format_list(grimoires)) data['content'] = markdown(content) return data
def grimoire_item(node, rels): ''' grimoire item page :param node: the item node :param rels: default relationship list :return: customized data for this label ''' data = generic_item(node, rels) data['rels'] = helpers.exclude_rels( data['rels'], ['lists', 'has', 'includes', 'wrote', 'was_written_in']) data['details']['date'] = [{'text': helpers.grimoire_date(node['props'])}] authors = extract_rel_list_by_type(rels, 'wrote', 'start') authors = extract_details(authors) if authors: data['details']['author'] = authors languages = extract_rel_list_by_type(rels, 'was_written_in', 'end') if languages: data['details']['language'] = extract_details(languages) editions = extract_rel_list(rels, 'edition', 'end') if editions: data['sidebar'].append({'title': 'Editions', 'data': editions}) for entity in entities: items = extract_rel_list_by_type(rels, 'lists', 'end', label=entity) if len(items): item_json = { 'title': helpers.pluralize(entity), 'data': items, 'many': True, 'compare': get_comparison(node, entity) } data['main'].append(item_json) spells = extract_rel_list(rels, 'spell', 'end') if len(spells): data['main'].append({ 'title': 'Spells', 'data': spells, 'many': False, 'compare': get_comparison(node, 'spell') }) talisman = extract_rel_list(rels, 'talisman', 'end') if len(talisman): data['main'].append({ 'title': 'Talismans', 'data': talisman, 'many': False }) return data
def entity_item(node, rels): ''' entity item page :param node: the item node :param rels: default relationship list :return: customized data for this label ''' data = generic_item(node, rels) # removes duplication of two-way sister rels rels = [r for r in rels if r['type'] != 'is_a_sister_of' \ or r['end']['id'] != node['id']] data['rels'] = helpers.exclude_rels(data['rels'], [ 'lists', 'teaches', 'skilled_in', 'serves', 'facilitates', 'appears_like', 'can', 'for']) grimoires = extract_rel_list(rels, 'grimoire', 'start') + \ extract_rel_list(rels, 'book', 'start') if grimoires: data['sidebar'].append({'title': 'Grimoires', 'data': grimoires}) outcomes = extract_rel_list_by_type(rels, 'for', 'end') if outcomes: data['main'].append({'title': 'Powers', 'data': outcomes}) appearance = extract_rel_list(rels, 'creature', 'end') if appearance: data['main'].append({ 'title': 'Appearance', 'data': appearance, 'many': True}) serves = extract_rel_list_by_type(rels, 'serves', 'end') serves = [s for s in serves if not s['props']['uid'] == node['props']['uid']] if serves: data['main'].append({'title': 'Serves', 'data': serves}) servants = extract_rel_list_by_type(rels, 'serves', 'start') servants = [s for s in servants if not s['props']['uid'] == node['props']['uid']] if servants: data['main'].append({'title': 'Servants', 'data': servants}) if not data['content']: content = 'The %s %s appears in %s' % \ (helpers.format_filter(node['label']), node['props']['identifier'], format_list(grimoires)) data['content'] = markdown(content) return data
def redirect_excerpts(uid): ''' Re-route anyone who lands on an excerpt page to the proper node :param uid: the excerpt's identifier :return: a redirect to the linked content node ''' data = graph.get_node(uid) source = extract_rel_list_by_type(data['rels'], 'event', 'start') + \ extract_rel_list_by_type(data['rels'], 'excerpt', 'start') + \ extract_rel_list_by_type(data['rels'], 'image', 'start') try: source = source[0] except IndexError: return render_template(request.url, 'label-404.html', labels=graph.get_labels()) return redirect(source['link'])
def redirect_excerpts(uid): ''' Re-route anyone who lands on an excerpt page to the proper node :param uid: the excerpt's identifier :return: a redirect to the linked content node ''' data = graph.get_node(uid) source = extract_rel_list_by_type(data['rels'], 'event', 'start') + \ extract_rel_list_by_type(data['rels'], 'excerpt', 'start') + \ extract_rel_list_by_type(data['rels'], 'image', 'start') try: source = source[0] except IndexError: return render_template(request.url, '404.html', labels=graph.get_labels()) return redirect(source['link'])
def spell_item(node, rels): ''' spell item page :param node: the item node :param rels: default relationship list :return: customized data for this label ''' data = generic_item(node, rels) data['rels'] = helpers.exclude_rels(data['rels'], ['for', 'uses']) try: del data['details']['grimoire'] except KeyError: pass grimoires = extract_rel_list(rels, 'grimoire', 'start') + \ extract_rel_list(rels, 'book', 'start') if grimoires: data['sidebar'].append({'title': 'Grimoires', 'data': grimoires}) if len(grimoires) == 1: data['parent_label'] = grimoires[0] ingredients = extract_rel_list(rels, 'parent:ingredient', 'end') if ingredients: data['main'].append({'title': 'Ingredients', 'data': ingredients}) outcomes = extract_rel_list_by_type(rels, 'for', 'end') if outcomes: data['details']['Outcome'] = extract_details(outcomes) if not data['content'] and not data['excerpts']: content = 'This %s appears in %s, ' \ 'and you can find the full text there. ' % \ (node['label'], format_list(grimoires)) if outcomes: content += 'It is used for %s' % \ format_list(outcomes, italics=False).lower() if ingredients: content += ' and calls for %s' % \ format_list(ingredients, italics=False, show_label=False).lower() content += '. ' data['content'] = markdown(content) return 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 generic_item(node, rels): ''' standard item processing :param node: the item node :param rels: default relationship list :return: customized data for this label ''' def format_field(field): ''' deal with array data ''' if isinstance(field, list): return [{'text': i} for i in field] return [{'text': field}] data = {'id': node['id']} try: data['content'] = markdown(node['props']['content']) except AttributeError: data['content'] = '' # -- build timeline events = extract_rel_list(rels, 'event', 'end') for event in events: event['props']['relevant'] = True related = graph.get_related_events(node['props']['uid']) related = zip(related['nodes'], related['rels']) for rel_item in related: event = rel_item[0] this = 'this %s' % helpers.format_filter(node['label']) rel_type = helpers.format_filter(rel_item[1]['type']) if rel_item[1]['start']['props']['uid'] == node['props']['uid']: note = '(%s %s %s)' % \ (this, rel_type, helpers.unthe(rel_item[1]['end']['props']['identifier'])) else: note = '(%s %s)' % (rel_type, this) event['note'] = note events.append(event) data['timeline'], data['start_date'], \ data['end_date'] = build_timeline(events) excerpts = extract_rel_list_by_type(rels, 'excerpt', 'end') for excerpt in excerpts: try: excerpt['props']['content'] = markdown(excerpt['props']['content']) except AttributeError: pass data['excerpts'] = excerpts data['images'] = extract_rel_list_by_type(rels, 'image', 'end', label='image') # remove special node types from relationship lists remove = ['excerpt', 'event', 'image', 'contains_item'] data['rels'] = helpers.exclude_rels(rels, remove) data['details'] = {k: format_field(node['props'][k]) for k in node['props'] if k not in ['content', 'uid', 'identifier', 'owned', 'buy', 'date_precision', 'isbn']} data['buy'] = node['props']['buy'] if 'buy' in node['props'] else None data['props'] = node['props'] data['sidebar'] = [] data['main'] = [] data['has_details'] = len([d for d in data['details'] if \ data['details'][d]]) > 0 data['link'] = node['link'] return 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 generic_item(node, rels): ''' standard item processing :param node: the item node :param rels: default relationship list :return: customized data for this label ''' def format_field(field): ''' deal with array data ''' if isinstance(field, list): return [{'text': i} for i in field] return [{'text': field}] data = {'id': node['id']} try: content = node['props']['content'].encode('ascii', 'xmlcharrefreplace') data['content'] = markdown(content) except AttributeError: data['content'] = '' # -- build timeline events = extract_rel_list(rels, 'event', 'end') for event in events: event['props']['relevant'] = True related = graph.get_related_events(node['props']['uid']) related = zip(related['nodes'], related['rels']) for rel_item in related: event = rel_item[0] this = 'this %s' % helpers.format_filter(node['label']) rel_type = helpers.format_filter(rel_item[1]['type']) if rel_item[1]['start']['props']['uid'] == node['props']['uid']: note = '(%s %s %s)' % \ (this, rel_type, helpers.unthe(rel_item[1]['end']['props']['identifier'])) else: note = '(%s %s)' % (rel_type, this) event['note'] = note events.append(event) data['timeline'], data['start_date'], \ data['end_date'] = build_timeline(events) excerpts = extract_rel_list_by_type(rels, 'excerpt', 'end') for excerpt in excerpts: try: excerpt['props']['content'] = markdown(excerpt['props']['content']) except AttributeError: pass data['excerpts'] = excerpts data['images'] = extract_rel_list_by_type(rels, 'image', 'end', label='image') # remove special node types from relationship lists remove = ['excerpt', 'event', 'image', 'contains_item'] data['rels'] = helpers.exclude_rels(rels, remove) data['details'] = { k: format_field(node['props'][k]) for k in node['props'] if k not in [ 'content', 'uid', 'identifier', 'owned', 'buy', 'date_precision', 'isbn', 'full_version' ] } if 'full_version' in node['props']: data['full_version'] = node['props']['full_version'] data['buy'] = node['props']['buy'] if 'buy' in node['props'] else None data['props'] = node['props'] data['sidebar'] = [] data['main'] = [] data['has_details'] = len([d for d in data['details'] if \ data['details'][d]]) > 0 data['link'] = node['link'] return data