From 0a575b7b69ee1ab30de26859af689bf193d1283e Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Wed, 25 Sep 2019 13:40:15 +0100 Subject: [PATCH] Improvements to the item page. --- app.py | 150 +++++++++++++++++++++++++++++++++----------- templates/base.html | 8 +-- templates/item.html | 102 +++++++++++++++++++++++++----- 3 files changed, 203 insertions(+), 57 deletions(-) diff --git a/app.py b/app.py index 80ca19b..e01d159 100755 --- a/app.py +++ b/app.py @@ -1,7 +1,8 @@ #!/usr/bin/python3 -from flask import Flask, render_template, url_for, redirect, request, g -from depicts import utils, wdqs, commons, mediawiki, painting +from flask import Flask, render_template, url_for, redirect, request, g, jsonify +from depicts import utils, wdqs, commons, mediawiki, painting, saam, database +from depicts.model import DepictsItem, DepictsItemAltLabel import json import os import locale @@ -9,9 +10,9 @@ import random locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') -thumbwidth = 300 - app = Flask(__name__) +app.config.from_object('config.default') +database.init_db(app.config['DB_URL']) find_more_props = { 'P135': 'movement', @@ -159,13 +160,7 @@ def random_painting(): item_id = wdqs.row_id(row) return redirect(url_for('item_page', item_id=item_id)) -@app.route("/item/Q") -def item_page(item_id): - qid = f'Q{item_id}' - item = painting.Painting(qid) - - width = 800 - image_filename = item.image_filename +def image_with_cache(qid, image_filename, width): filename = f'cache/{qid}_{width}_image.json' if os.path.exists(filename): detail = json.load(open(filename)) @@ -173,13 +168,46 @@ def item_page(item_id): detail = commons.image_detail([image_filename], thumbwidth=width) json.dump(detail, open(filename, 'w'), indent=2) - hits = item.run_query() + return detail[image_filename] + +def first_datavalue(entity, pid): + return entity['claims'][pid][0]['mainsnak']['datavalue']['value'] + + +@app.route("/item/Q") +def item_page(item_id): + qid = f'Q{item_id}' + item = painting.Painting(qid) + entity = mediawiki.get_entity_with_cache(qid) + + width = 800 + image_filename = item.image_filename + image = image_with_cache(qid, image_filename, width) + + # hits = item.run_query() + label = get_entity_label(entity) + other = get_other(item.entity) + + if 'P4704' in entity['claims']: + saam_id = first_datavalue(entity, 'P4704') + catalog = saam.get_catalog(saam_id) + saam_data = { + 'keywords': catalog['keywords'], + 'description': catalog['ld']['description'] + } + else: + saam_data = None return render_template('item.html', qid=qid, item=item, - image=detail[image_filename], - hits=hits, + saam_data=saam_data, + labels=find_more_props, + entity=item.entity, + label=label, + image=image, + other=other, + # hits=hits, title=item.display_title) def get_entity_label(entity): @@ -209,21 +237,7 @@ def get_labels(keys, name=None): return {entity['id']: get_entity_label(entity) for entity in labels} -@app.route("/next/Q") -def next_page(item_id): - qid = f'Q{item_id}' - - entity = mediawiki.get_entity_with_cache(qid) - - width = 800 - image_filename = entity['claims']['P18'][0]['mainsnak']['datavalue']['value'] - filename = f'cache/{qid}_{width}_image.json' - if os.path.exists(filename): - detail = json.load(open(filename)) - else: - detail = commons.image_detail([image_filename], thumbwidth=width) - json.dump(detail, open(filename, 'w'), indent=2) - +def get_other(entity): other_items = set() for key in find_more_props.keys(): if key not in entity['claims']: @@ -231,21 +245,27 @@ def next_page(item_id): for claim in entity['claims'][key]: other_items.add(claim['mainsnak']['datavalue']['value']['id']) - item_labels = get_labels(other_items) + return get_labels(other_items) - if 'en' in entity['labels']: - label = entity['labels']['en']['value'] - elif len(entity['labels']) == 1: - label = list(entity['labels'].values())[0]['value'] - else: - label = 'title missing' +@app.route("/next/Q") +def next_page(item_id): + qid = f'Q{item_id}' + + entity = mediawiki.get_entity_with_cache(qid) + + width = 800 + image_filename = first_datavalue(entity, 'P18') + image = image_with_cache(qid, image_filename, width) + + label = get_entity_label(entity) + other = get_other(entity) return render_template('next.html', qid=qid, label=label, - image=detail[image_filename], + image=image, labels=find_more_props, - other=item_labels, + other=other, entity=entity) @app.route('/P/Q') @@ -315,6 +335,8 @@ def browse_page(): filenames = [cur['image_filename'] for cur in items] + thumbwidth = app.config['THUMBWIDTH'] + filename = f'cache/{flat}_{page_size}_images.json' if os.path.exists(filename): detail = json.load(open(filename)) @@ -337,6 +359,58 @@ def browse_page(): total=len(bindings), items=items) +@app.route('/lookup') +def depicts_lookup(): + terms = request.args.get('terms') + if not terms: + return jsonify(error='terms parameter is required') + + terms = terms.strip() + if len(terms) < 3: + return jsonify( + count=0, + hits=[], + notice='terms too short for lookup', + ) + + item_ids = [] + hits = [] + q1 = DepictsItem.query.filter(DepictsItem.label.ilike(terms + '%')) + for item in q1: + hit = { + 'label': item.label, + 'description': item.description, + 'qid': item.qid, + 'count': item.count, + } + item_ids.append(item.item_id) + hits.append(hit) + + cls = DepictsItemAltLabel + q2 = cls.query.filter(cls.alt_label.ilike(terms + '%'), + ~cls.item_id.in_(item_ids)) + + for alt in q2: + item = alt.item + hit = { + 'label': item.label, + 'description': item.description, + 'qid': item.qid, + 'count': item.count, + 'alt_label': alt.alt_label, + } + hits.append(hit) + + hits.sort(key=lambda hit: hit['count'], reverse=True) + + ret = { + 'count': q1.count() + q2.count(), + 'hits': hits, + 'terms': terms, + } + + return jsonify(ret) + if __name__ == "__main__": app.debug = True diff --git a/templates/base.html b/templates/base.html index 820407a..1d08b1a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -2,7 +2,7 @@ - + @@ -15,9 +15,9 @@ <body> {% block content %}{% endblock %} -<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> -<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> -<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> + <script src="{{ url_for('static', filename='javascript/jquery/jquery.min.js') }}"></script> + {# <script src="{{ url_for('static', filename='javascript/popper.js/popper.min.js') }}"></script> #} + <script src="{{ url_for('static', filename='javascript/bootstrap4/js/bootstrap.min.js') }}"></script> {% block script %}{% endblock %} </body> diff --git a/templates/item.html b/templates/item.html index 016bc8e..47e7f59 100644 --- a/templates/item.html +++ b/templates/item.html @@ -2,17 +2,32 @@ {% block title %}{{ label }} ({{qid }}){% endblock %} -{% block content %} -<div class="m-3"> - <h1>{{ self.title() }}</h1> - <div class="row"> - <div class="col"> - <img src="{{ image.thumburl }}"> - </div> +{% block style %} +<style> +.description { margin-left: 2em; color: rgb(96, 96, 96); } +</style> +{% endblock %} - <div class="col"> - <p><a href="https://www.wikidata.org/wiki/{{ qid }}">view on Wikidata</a></p> - <p><a href="{{ url_for('random_painting') }}">random painting</a></p> +{% block content %} +<div> + <div class="d-flex"> + <div class="flex-shrink-1 vh-100"> + <img src="{{ image.thumburl }}" class="h-100" /> + </div> + <div class="p-2 flex-fill"> + <h1>{{ self.title() }}</h1> + + <p> + <a href="https://www.wikidata.org/wiki/{{ qid }}">view this painting on Wikidata</a> + | + <a href="{{ url_for('random_painting') }}">open a random painting</a> + </p> + + + + + + <div> {% for hit in hits %} <p> url: {{ hit.url }}<br> @@ -22,13 +37,70 @@ </p> {% endfor %} + <div> + {% for key, prop_label in labels.items() %} + {% set claims = entity['claims'][key] %} + {% if claims %} + <div> + <strong>{{ prop_label }}</strong>: + {% for claim in claims %} + {% set claim_qid = claim.mainsnak.datavalue.value.id %} + <a href="https://www.wikidata.org/wiki/{{ claim_qid }}">{{ other[claim_qid] or '[ label missing ]' }}</a> ({{ claim_qid }}) + {% endfor %} + </div> + {% endif %} + {% endfor %} + </div> + + {% if saam_data %} + <pre>{{ saam_data | pprint }}</pre> + {% endif %} + </div> + + {% raw %} + <div id="app" class="mt-2"> + <h3>what can you see in this painting?</h3> + + <div v-for="hit in new_depicts"> + <div> + {{ hit.label }} + <span v-if="hit.alt_label">({{ hit.alt_label }})</span> + — {{ hit.count }} existing paintings + ({{ hit.qid }}) + <a :href="'https://www.wikidata.org/wiki/' + hit.qid">view on Wikidata</a> + </div> + <div v-if="hit.description"> + <div class="description">{{ hit.description }}</div> + </div> + </div> + + <input class="form-control-lg mt-2 w-100" autofocus autocomplete="off" v-model.trim="searchTerms" @input="search" /> + <div id="item-list"> + <div v-for="hit in hits"> + <div> + <a href="#" @click="add_depicts(hit)">{{ hit.label }}</a> + <span v-if="hit.alt_label">({{ hit.alt_label }})</span> + — {{ hit.count }} existing paintings + ({{ hit.qid }}) + <a :href="'https://www.wikidata.org/wiki/' + hit.qid">view on Wikidata</a> + </div> + <div v-if="hit.description"> + <div class="description">{{ hit.description }}</div> + </div> + </div> + </div> + </div> + {% endraw %} </div> </div> - <pre>{{ item.query_variables() | pprint }}</pre> - - <pre>{{ item.build_query() }}</pre> - - </div> {% endblock %} + +{% block script %} +<script> + var lookup_url = {{ url_for('depicts_lookup') | tojson }}; +</script> +<script src="{{ url_for('static', filename='vue/vue.js') }}"></script> +<script src="{{ url_for('static', filename='js/app.js') }}"></script> +{% endblock %}