From 8a12a24e8e0c157d0a30a082f3c6f9cf330c4908 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Sun, 22 Jul 2018 18:17:39 -0700 Subject: [PATCH] Add tag_autocomplete.js. Instead of embedding the entire tag list in the search.html template every single time, this script loads the tags from the new, cache-enabled endpoint /all_tags.json. Then we can use html5 datalists to create autocomplete forms on the search and photo pages. --- etiquette/photodb.py | 9 +++ .../endpoints/photo_endpoints.py | 2 - .../endpoints/tag_endpoints.py | 10 +++ frontends/etiquette_flask/static/js/common.js | 12 +++ .../static/js/tag_autocomplete.js | 79 +++++++++++++++++++ .../etiquette_flask/templates/photo.html | 5 +- .../etiquette_flask/templates/search.html | 19 ++--- 7 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 frontends/etiquette_flask/static/js/tag_autocomplete.js diff --git a/etiquette/photodb.py b/etiquette/photodb.py index 57ae403..20e6681 100644 --- a/etiquette/photodb.py +++ b/etiquette/photodb.py @@ -792,6 +792,15 @@ class PDBTagMixin: names = [row[0] for row in rows] return names + def get_all_synonyms(self): + ''' + Return a dict mapping {synonym: mastertag} as strings. + ''' + query = 'SELECT name, mastername FROM tag_synonyms' + rows = self.sql_select(query) + synonyms = {syn: tag for (syn, tag) in rows} + return synonyms + def get_root_tags(self): ''' Yield Tags that have no parent. diff --git a/frontends/etiquette_flask/etiquette_flask/endpoints/photo_endpoints.py b/frontends/etiquette_flask/etiquette_flask/endpoints/photo_endpoints.py index 5fece5c..cbc6aa1 100644 --- a/frontends/etiquette_flask/etiquette_flask/endpoints/photo_endpoints.py +++ b/frontends/etiquette_flask/etiquette_flask/endpoints/photo_endpoints.py @@ -371,11 +371,9 @@ def get_search_core(): def get_search_html(): search_results = get_search_core() search_kwargs = search_results['search_kwargs'] - all_tags = common.P.get_all_tag_names() session = session_manager.get(request) response = flask.render_template( 'search.html', - all_tags=json.dumps(all_tags), next_page_url=search_results['next_page_url'], prev_page_url=search_results['prev_page_url'], photos=search_results['photos'], diff --git a/frontends/etiquette_flask/etiquette_flask/endpoints/tag_endpoints.py b/frontends/etiquette_flask/etiquette_flask/endpoints/tag_endpoints.py index 921289f..164aa67 100644 --- a/frontends/etiquette_flask/etiquette_flask/endpoints/tag_endpoints.py +++ b/frontends/etiquette_flask/etiquette_flask/endpoints/tag_endpoints.py @@ -1,7 +1,9 @@ import flask; from flask import request +import json import etiquette +from .. import caching from .. import common from .. import decorators from .. import jsonify @@ -48,6 +50,14 @@ def post_tag_edit(specific_tag): # Tag listings ##################################################################################### +@site.route('/all_tags.json') +@caching.cached_endpoint(max_age=0) +def get_all_tag_names(): + all_tags = common.P.get_all_tag_names() + all_synonyms = common.P.get_all_synonyms() + response = {'tags': all_tags, 'synonyms': all_synonyms} + return json.dumps(response) + @site.route('/tag/') @site.route('/tags') @session_manager.give_token diff --git a/frontends/etiquette_flask/static/js/common.js b/frontends/etiquette_flask/static/js/common.js index c520048..467d0fc 100644 --- a/frontends/etiquette_flask/static/js/common.js +++ b/frontends/etiquette_flask/static/js/common.js @@ -121,3 +121,15 @@ function html_to_element(html) template.innerHTML = html; return template.content.firstChild; } + +function normalize_tagname(tagname) +{ + tagname = tagname.trim(); + tagname = tagname.toLocaleLowerCase(); + tagname = tagname.split("."); + tagname = tagname[tagname.length-1]; + tagname = tagname.split("+")[0]; + tagname = tagname.replace(new RegExp(" ", 'g'), "_"); + tagname = tagname.replace(new RegExp("-", 'g'), "_"); + return tagname; +} diff --git a/frontends/etiquette_flask/static/js/tag_autocomplete.js b/frontends/etiquette_flask/static/js/tag_autocomplete.js new file mode 100644 index 0000000..adc8026 --- /dev/null +++ b/frontends/etiquette_flask/static/js/tag_autocomplete.js @@ -0,0 +1,79 @@ +var tag_autocomplete = {}; + +tag_autocomplete.tagset = {"tags": [], "synonyms": {}}; + +tag_autocomplete.DATALIST_ID = "tag_autocomplete_datalist" +tag_autocomplete.init_datalist = +function init_datalist() +{ + var datalist; + datalist = document.getElementById(tag_autocomplete.DATALIST_ID); + if (!datalist) + { + var datalist = document.createElement("datalist"); + datalist.id = tag_autocomplete.DATALIST_ID; + document.body.appendChild(datalist); + } + + delete_all_children(datalist); + for (var index = 0; index < tag_autocomplete.tagset["tags"].length; index += 1) + { + var option = document.createElement("option"); + option.value = tag_autocomplete.tagset["tags"][index]; + datalist.appendChild(option); + } + for (var synonym in tag_autocomplete.tagset["synonyms"]) + { + var option = document.createElement("option"); + option.value = tag_autocomplete.tagset["synonyms"][synonym] + "+" + synonym; + datalist.appendChild(option); + } +} + +tag_autocomplete.resolve = +function resolve(tagname) +{ + tagname = normalize_tagname(tagname); + if (tag_autocomplete.tagset["tags"].indexOf(tagname) != -1) + { + return tagname; + } + if (tagname in tag_autocomplete.tagset["synonyms"]) + { + return tag_autocomplete.tagset["synonyms"][tagname]; + } + return null; +} + +tag_autocomplete.update_tagset_callback = +function update_tagset_callback(response) +{ + if (response["meta"]["status"] == 304) + { + return; + } + if (response["meta"]["status"] == 200) + { + tag_autocomplete.tagset = response["data"]; + if (document.getElementById(tag_autocomplete.DATALIST_ID)) + { + tag_autocomplete.init_datalist(); + } + return tag_autocomplete.tagset; + } + console.error(response); +} + +tag_autocomplete.update_tagset = +function update_tagset() +{ + console.log("Updating known tagset."); + var url = "/all_tags.json"; + get(url, tag_autocomplete.update_tagset_callback); +} + +function on_pageload() +{ + tag_autocomplete.update_tagset(); +} +document.addEventListener("DOMContentLoaded", on_pageload); diff --git a/frontends/etiquette_flask/templates/photo.html b/frontends/etiquette_flask/templates/photo.html index 47e44c2..b960dee 100644 --- a/frontends/etiquette_flask/templates/photo.html +++ b/frontends/etiquette_flask/templates/photo.html @@ -10,6 +10,7 @@ +