Improvements to the item page.
This commit is contained in:
parent
0b10bde447
commit
0a575b7b69
150
app.py
150
app.py
|
@ -1,7 +1,8 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from flask import Flask, render_template, url_for, redirect, request, g
|
from flask import Flask, render_template, url_for, redirect, request, g, jsonify
|
||||||
from depicts import utils, wdqs, commons, mediawiki, painting
|
from depicts import utils, wdqs, commons, mediawiki, painting, saam, database
|
||||||
|
from depicts.model import DepictsItem, DepictsItemAltLabel
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import locale
|
import locale
|
||||||
|
@ -9,9 +10,9 @@ import random
|
||||||
|
|
||||||
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
|
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
|
||||||
|
|
||||||
thumbwidth = 300
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
app.config.from_object('config.default')
|
||||||
|
database.init_db(app.config['DB_URL'])
|
||||||
|
|
||||||
find_more_props = {
|
find_more_props = {
|
||||||
'P135': 'movement',
|
'P135': 'movement',
|
||||||
|
@ -159,13 +160,7 @@ def random_painting():
|
||||||
item_id = wdqs.row_id(row)
|
item_id = wdqs.row_id(row)
|
||||||
return redirect(url_for('item_page', item_id=item_id))
|
return redirect(url_for('item_page', item_id=item_id))
|
||||||
|
|
||||||
@app.route("/item/Q<int:item_id>")
|
def image_with_cache(qid, image_filename, width):
|
||||||
def item_page(item_id):
|
|
||||||
qid = f'Q{item_id}'
|
|
||||||
item = painting.Painting(qid)
|
|
||||||
|
|
||||||
width = 800
|
|
||||||
image_filename = item.image_filename
|
|
||||||
filename = f'cache/{qid}_{width}_image.json'
|
filename = f'cache/{qid}_{width}_image.json'
|
||||||
if os.path.exists(filename):
|
if os.path.exists(filename):
|
||||||
detail = json.load(open(filename))
|
detail = json.load(open(filename))
|
||||||
|
@ -173,13 +168,46 @@ def item_page(item_id):
|
||||||
detail = commons.image_detail([image_filename], thumbwidth=width)
|
detail = commons.image_detail([image_filename], thumbwidth=width)
|
||||||
json.dump(detail, open(filename, 'w'), indent=2)
|
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<int:item_id>")
|
||||||
|
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',
|
return render_template('item.html',
|
||||||
qid=qid,
|
qid=qid,
|
||||||
item=item,
|
item=item,
|
||||||
image=detail[image_filename],
|
saam_data=saam_data,
|
||||||
hits=hits,
|
labels=find_more_props,
|
||||||
|
entity=item.entity,
|
||||||
|
label=label,
|
||||||
|
image=image,
|
||||||
|
other=other,
|
||||||
|
# hits=hits,
|
||||||
title=item.display_title)
|
title=item.display_title)
|
||||||
|
|
||||||
def get_entity_label(entity):
|
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}
|
return {entity['id']: get_entity_label(entity) for entity in labels}
|
||||||
|
|
||||||
@app.route("/next/Q<int:item_id>")
|
def get_other(entity):
|
||||||
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)
|
|
||||||
|
|
||||||
other_items = set()
|
other_items = set()
|
||||||
for key in find_more_props.keys():
|
for key in find_more_props.keys():
|
||||||
if key not in entity['claims']:
|
if key not in entity['claims']:
|
||||||
|
@ -231,21 +245,27 @@ def next_page(item_id):
|
||||||
for claim in entity['claims'][key]:
|
for claim in entity['claims'][key]:
|
||||||
other_items.add(claim['mainsnak']['datavalue']['value']['id'])
|
other_items.add(claim['mainsnak']['datavalue']['value']['id'])
|
||||||
|
|
||||||
item_labels = get_labels(other_items)
|
return get_labels(other_items)
|
||||||
|
|
||||||
if 'en' in entity['labels']:
|
@app.route("/next/Q<int:item_id>")
|
||||||
label = entity['labels']['en']['value']
|
def next_page(item_id):
|
||||||
elif len(entity['labels']) == 1:
|
qid = f'Q{item_id}'
|
||||||
label = list(entity['labels'].values())[0]['value']
|
|
||||||
else:
|
entity = mediawiki.get_entity_with_cache(qid)
|
||||||
label = 'title missing'
|
|
||||||
|
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',
|
return render_template('next.html',
|
||||||
qid=qid,
|
qid=qid,
|
||||||
label=label,
|
label=label,
|
||||||
image=detail[image_filename],
|
image=image,
|
||||||
labels=find_more_props,
|
labels=find_more_props,
|
||||||
other=item_labels,
|
other=other,
|
||||||
entity=entity)
|
entity=entity)
|
||||||
|
|
||||||
@app.route('/P<int:property_id>/Q<int:item_id>')
|
@app.route('/P<int:property_id>/Q<int:item_id>')
|
||||||
|
@ -315,6 +335,8 @@ def browse_page():
|
||||||
|
|
||||||
filenames = [cur['image_filename'] for cur in items]
|
filenames = [cur['image_filename'] for cur in items]
|
||||||
|
|
||||||
|
thumbwidth = app.config['THUMBWIDTH']
|
||||||
|
|
||||||
filename = f'cache/{flat}_{page_size}_images.json'
|
filename = f'cache/{flat}_{page_size}_images.json'
|
||||||
if os.path.exists(filename):
|
if os.path.exists(filename):
|
||||||
detail = json.load(open(filename))
|
detail = json.load(open(filename))
|
||||||
|
@ -337,6 +359,58 @@ def browse_page():
|
||||||
total=len(bindings),
|
total=len(bindings),
|
||||||
items=items)
|
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__":
|
if __name__ == "__main__":
|
||||||
app.debug = True
|
app.debug = True
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
<link rel="stylesheet" href="{{ url_for('static', filename='javascript/bootstrap4/css/bootstrap.min.css') }}">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
<title>
|
<title>
|
||||||
|
@ -15,9 +15,9 @@
|
||||||
<body>
|
<body>
|
||||||
{% block content %}{% endblock %}
|
{% 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="{{ url_for('static', filename='javascript/jquery/jquery.min.js') }}"></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="{{ url_for('static', filename='javascript/popper.js/popper.min.js') }}"></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/bootstrap4/js/bootstrap.min.js') }}"></script>
|
||||||
|
|
||||||
{% block script %}{% endblock %}
|
{% block script %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -2,17 +2,32 @@
|
||||||
|
|
||||||
{% block title %}{{ label }} ({{qid }}){% endblock %}
|
{% block title %}{{ label }} ({{qid }}){% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block style %}
|
||||||
<div class="m-3">
|
<style>
|
||||||
<h1>{{ self.title() }}</h1>
|
.description { margin-left: 2em; color: rgb(96, 96, 96); }
|
||||||
<div class="row">
|
</style>
|
||||||
<div class="col">
|
{% endblock %}
|
||||||
<img src="{{ image.thumburl }}">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col">
|
{% block content %}
|
||||||
<p><a href="https://www.wikidata.org/wiki/{{ qid }}">view on Wikidata</a></p>
|
<div>
|
||||||
<p><a href="{{ url_for('random_painting') }}">random painting</a></p>
|
<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 %}
|
{% for hit in hits %}
|
||||||
<p>
|
<p>
|
||||||
url: {{ hit.url }}<br>
|
url: {{ hit.url }}<br>
|
||||||
|
@ -22,13 +37,70 @@
|
||||||
</p>
|
</p>
|
||||||
{% endfor %}
|
{% 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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<pre>{{ item.query_variables() | pprint }}</pre>
|
<input class="form-control-lg mt-2 w-100" autofocus autocomplete="off" v-model.trim="searchTerms" @input="search" />
|
||||||
|
<div id="item-list">
|
||||||
<pre>{{ item.build_query() }}</pre>
|
<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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% 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 %}
|
||||||
|
|
Loading…
Reference in a new issue