From ee1b6de807bd3b260c512b770073dd7eb997b0e6 Mon Sep 17 00:00:00 2001 From: "Henrik.Askjer" <henrik.askjer@uib.no> Date: Thu, 2 Sep 2021 15:31:38 +0200 Subject: [PATCH] Replace suggestion api --- package-lock.json | 84 ++++++++++++++++++- package.json | 4 +- src/components/Autocomplete.vue | 70 +++++++--------- src/components/DictionaryView.vue | 131 +++++++++++++++++++++--------- 4 files changed, 207 insertions(+), 82 deletions(-) diff --git a/package-lock.json b/package-lock.json index 859b1397..4710a532 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/vue-fontawesome": "^2.0.2", "axios": "^0.21.1", + "axios-cache-adapter": "^2.7.3", "core-js": "~3.6.5", "debounce": "^1.2.1", "inflection-table": "https://git.app.uib.no/api/v4/projects/16442/jobs/artifacts/0.2.25/raw/module.tar.gz?job=publish", @@ -3389,6 +3390,18 @@ "follow-redirects": "^1.10.0" } }, + "node_modules/axios-cache-adapter": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/axios-cache-adapter/-/axios-cache-adapter-2.7.3.tgz", + "integrity": "sha512-A+ZKJ9lhpjthOEp4Z3QR/a9xC4du1ALaAsejgRGrH9ef6kSDxdFrhRpulqsh9khsEnwXxGfgpUuDp1YXMNMEiQ==", + "dependencies": { + "cache-control-esm": "1.0.0", + "md5": "^2.2.1" + }, + "peerDependencies": { + "axios": "~0.21.1" + } + }, "node_modules/axios/node_modules/follow-redirects": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", @@ -3987,6 +4000,11 @@ "node": ">=0.10.0" } }, + "node_modules/cache-control-esm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cache-control-esm/-/cache-control-esm-1.0.0.tgz", + "integrity": "sha512-Fa3UV4+eIk4EOih8FTV6EEsVKO0W5XWtNs6FC3InTfVz+EjurjPfDXY5wZDo/lxjDxg5RjNcurLyxEJBcEUx9g==" + }, "node_modules/cache-loader": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-4.1.0.tgz", @@ -4147,6 +4165,14 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", + "engines": { + "node": "*" + } + }, "node_modules/check-types": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", @@ -5119,6 +5145,14 @@ "semver": "bin/semver" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", + "engines": { + "node": "*" + } + }, "node_modules/crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -8568,8 +8602,7 @@ "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "node_modules/is-callable": { "version": "1.2.3", @@ -9416,6 +9449,16 @@ "integrity": "sha512-wRJtOo1v1ch+gN8PRsj0IGJznk+kQ8mz13ds/nuhLI+Qyf/931ZlRpd92oq0IRPpZIb+bhX8pRjzIVdcPDKmiQ==", "dev": true }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -18220,6 +18263,15 @@ } } }, + "axios-cache-adapter": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/axios-cache-adapter/-/axios-cache-adapter-2.7.3.tgz", + "integrity": "sha512-A+ZKJ9lhpjthOEp4Z3QR/a9xC4du1ALaAsejgRGrH9ef6kSDxdFrhRpulqsh9khsEnwXxGfgpUuDp1YXMNMEiQ==", + "requires": { + "cache-control-esm": "1.0.0", + "md5": "^2.2.1" + } + }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -18703,6 +18755,11 @@ "unset-value": "^1.0.0" } }, + "cache-control-esm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cache-control-esm/-/cache-control-esm-1.0.0.tgz", + "integrity": "sha512-Fa3UV4+eIk4EOih8FTV6EEsVKO0W5XWtNs6FC3InTfVz+EjurjPfDXY5wZDo/lxjDxg5RjNcurLyxEJBcEUx9g==" + }, "cache-loader": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-4.1.0.tgz", @@ -18826,6 +18883,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, "check-types": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", @@ -19621,6 +19683,11 @@ } } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -22356,8 +22423,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { "version": "1.2.3", @@ -23012,6 +23078,16 @@ "integrity": "sha512-wRJtOo1v1ch+gN8PRsj0IGJznk+kQ8mz13ds/nuhLI+Qyf/931ZlRpd92oq0IRPpZIb+bhX8pRjzIVdcPDKmiQ==", "dev": true }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", diff --git a/package.json b/package.json index fbf246c2..9b7689c3 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/vue-fontawesome": "^2.0.2", "axios": "^0.21.1", + "axios-cache-adapter": "^2.7.3", "core-js": "~3.6.5", "debounce": "^1.2.1", "inflection-table": "https://git.app.uib.no/api/v4/projects/16442/jobs/artifacts/0.2.25/raw/module.tar.gz?job=publish", @@ -50,7 +51,8 @@ ], "rules": { "no-irregular-whitespace": "off", - "no-unused-vars": "off" + "no-unused-vars": "off", + "no-console": "off" }, "parserOptions": { "parser": "babel-eslint" diff --git a/src/components/Autocomplete.vue b/src/components/Autocomplete.vue index 725f1827..a042536d 100644 --- a/src/components/Autocomplete.vue +++ b/src/components/Autocomplete.vue @@ -36,11 +36,10 @@ </template> <script> - import axios from "axios" export default { props: { - endpoint: String, + api: Function, }, data: function() { return { @@ -62,37 +61,17 @@ select(item) { if (item) { if (typeof item != 'string') { - let self = this - /* - if (item.articles) { - axios.get(self.endpoint + 'articles?', {params: {lord: item.match, - dict: self.$parent.lang}}) - .then( - function(response) { - ['bob', 'nob'].forEach((dict_tag) => { - response.data[dict_tag].forEach((article_id) => { - let article = {} - article.article_id = article_id - article.dictionary = dict_tag - article.match = item.match - item.articles.push(article) - }) - }) - } - ) + this.items = [] + this.suggesting = false + if (item.article_promise) { + item.article_promise.then((response) => { + item.article_ids = response.data + this.submit(item) + }) + } + else { + this.submit(item) } - */ - this.items = [] - this.suggesting = false - - self.$emit('submit', item) - - setTimeout(() => { - self.$refs.autocomplete.$refs.input.select() - this.items = [] - this.suggesting = false - }, 1) - } // If blurred else { @@ -104,7 +83,7 @@ methods: { run_query(q) { this.suggesting = true - // Put full text search in the list while processing suggestions + // Keep full text search in the list while requesting suggestions if (this.items[0]) { if (this.items[0].lang) { this.items.unshift({q: q, label: q}) @@ -114,16 +93,17 @@ } } let self = this - axios.get(self.endpoint + 'suggest?', { params: {q: q, - dict: self.$parent.lang, - n: 9}} ) - .then( - function(response) { + self.api.get('suggest?', {params: {q: q, dict: self.$parent.lang, n: 9}}) + .then(async (response) => { if (self.$refs.autocomplete.searchInput == q & self.suggesting) { let hits = [] response.data.forEach((item, i) => { - let hit = {q: q, match: item[0], label: item[0], articles: []} + let match = item[0] + let hit = {q: q, match: match, label: match} + + hit.article_promise = self.api.get('articles?', {params: {lord: match, dict: self.$parent.lang}}) + hit.lang = item[1] hits.push(hit) @@ -132,12 +112,22 @@ hits.push({q: q, label: q + ' '}) self.items = hits } - self.loading = false }) }, + submit(item) { + + this.$emit('submit', item) + let self = this + setTimeout(() => { + self.$refs.autocomplete.$refs.input.select() + this.items = [] + this.suggesting = false + }, 1) + + } }, } </script> diff --git a/src/components/DictionaryView.vue b/src/components/DictionaryView.vue index 408b3004..359d5132 100644 --- a/src/components/DictionaryView.vue +++ b/src/components/DictionaryView.vue @@ -29,7 +29,7 @@ </v-radio> </v-radio-group> </div> - <Autocomplete @submit="select_result" :endpoint="oda_endpoint"> + <Autocomplete @submit="select_result" :api="get_oda_api"> </Autocomplete> </div> <div id="spinner" v-if="waiting"> @@ -73,8 +73,20 @@ import Article from './Article.vue' import SearchResults from './SearchResults.vue' import Autocomplete from './Autocomplete.vue' +import { setup } from 'axios-cache-adapter' + +const oda_api = setup({ + baseURL: 'https://oda.uib.no/opal-api/', + cache: { + maxAge: 15 * 60 * 1000, + exclude: { + query: false + } + } +}) + + var api_endpoint = process.env.VUE_APP_API_PREFIX + '/api/dict' -const oda_endpoint = 'https://oda.uib.no/opal-api/' function compare_by_hgno(lemma_text) { return function(art1, art2) { @@ -190,9 +202,9 @@ export default { api_pref: function() { return api_endpoint + '/' + this.lang + '/article/' }, - oda_endpoint: function() { - return oda_endpoint - } + get_oda_api: function() { + return oda_api + }, }, components: { Article, @@ -200,38 +212,83 @@ export default { SearchResults }, methods: { - select_result: function(event) { - this.event = event - if(event.articles){ - let source = '/' + this.lang + '/w/' + event.match - this.$router.push(source) - /* - this.search_results = event.articles.map(a => Object.assign(a, {source: source})) - this.article = null - this.error = null - - history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang, error: this.error}, '') - */ - this.waiting_for_articles = true - navigate_to_word(this, event.match) - this.$plausible.trackEvent('dropdown selection', {props: {query: event.q, match: event.match}}) - }else{ - this.waiting_for_articles = true - this.article = null - this.$router.push(`/${this.lang}/search/${event.q}`) - navigate_to_search(this, event.q) - this.$plausible.trackEvent('dropdown selection', {props: {query: event.q, match: '<fritekstsøk>'}}) - } - }, - update_lang_form: function(event) { - if (this.event){ - this.event.articles = null - this.select_result(this.event) - } - else{ - navigate_to_search(this, this.$router.history.current.params.word || this.$router.history.current.params.query) - } - }, + select_result: function (event) { + this.event = event + if (event.article_ids) { + let source = '/' + this.lang + '/w/' + event.match + this.$router.push(source) + let unwrapped = [] + for (const d in event.article_ids) { + event.article_ids[d].forEach(i => unwrapped.push({ + dictionary: d, + id: i + })) + } + + let self = this + Promise.all(unwrapped.map((article) => { + return axios.get(`${api_endpoint}/${article.dictionary}/article/${article.id}`) + + })) + .then((response) => { + self.search_results = response.map((element, index) => { + return Object.assign(element.data, { + source: source, + match: event.match, + dictionary: unwrapped[index].dictionary + }) + }) + self.article = null + self.error = null + + }) + .catch(error => { + self.search_results = [] + if (error.response) { + self.error = "Noe gikk galt på serversiden" + } else { + self.error = "Nettverksproblemer, prøv igjen" + } + }) + .then(() => { + self.$plausible.trackEvent('dropdown selection', { + props: { + query: event.q, + match: event.match + } + }) + self.waiting_for_articles = false + history.replaceState({ + article: self.article, + search_results: self.search_results, + lang: self.lang, + error: self.error + }, '') + }) + + + + } else { + this.waiting_for_articles = true + this.article = null + this.$router.push(`/${this.lang}/search/${event.q}`) + navigate_to_search(this, event.q) + this.$plausible.trackEvent('dropdown selection', { + props: { + query: event.q, + match: '<fritekstsøk>' + } + }) + } + }, + update_lang_form: function (event) { + if (this.event) { + this.event.articles = null + this.select_result(this.event) + } else { + navigate_to_search(this, this.$router.history.current.params.word || this.$router.history.current.params.query) + } + }, article_link_click: function(item) { if (this.article && this.article.article_id == item.article_id){ this.article_key++ -- GitLab