From b3e8976c1ca582d9dec3642c53eccf6a77082a75 Mon Sep 17 00:00:00 2001 From: Stephan Porada Date: Mon, 17 Aug 2020 16:15:34 +0200 Subject: [PATCH] Further rework --- .../modules/nopaque.CorpusAnalysisClient.js | 55 ++- .../js/modules/nopaque.listenerCallbacks.js | 130 +++++--- .../js/modules/nopaque.listenerFunctions.js | 64 +++- web/app/static/js/nopaque.Results.js | 16 +- web/app/static/js/web-components/InfoMenu.js | 83 +++++ .../templates/corpora/analyse_corpus.html.j2 | 164 ++++----- web/app/templates/corpora/corpus.html.j2 | 7 +- web/app/templates/interactions/export.html.j2 | 2 +- web/app/templates/interactions/infos.html.j2 | 20 +- web/app/templates/main/dashboard.html.j2 | 10 +- .../templates/query_results/inspect.html.j2 | 312 ++++++++---------- .../query_results/inspect.html.j2.bak | 233 +++++++++++++ .../services/corpus_analysis.html.j2 | 7 +- 13 files changed, 755 insertions(+), 348 deletions(-) create mode 100644 web/app/static/js/web-components/InfoMenu.js create mode 100644 web/app/templates/query_results/inspect.html.j2.bak diff --git a/web/app/static/js/modules/nopaque.CorpusAnalysisClient.js b/web/app/static/js/modules/nopaque.CorpusAnalysisClient.js index 7eeb6216..0fb6fcae 100644 --- a/web/app/static/js/modules/nopaque.CorpusAnalysisClient.js +++ b/web/app/static/js/modules/nopaque.CorpusAnalysisClient.js @@ -5,12 +5,38 @@ * the server and recieves them. */ class CorpusAnalysisClient { - constructor(corpusId, socket, logging=false) { + constructor({corpusId = null, + socket = null, + logging = true, + dynamicMode = true} = {}) { this.corpusId = corpusId; this.displays = {}; + this.dynamicMode = dynamicMode; this.logging = logging; + this.requestQueryProgress = 0; + this.results = undefined; // holds the results object later on this.socket = socket; this.socketEventListeners = {}; + + /** + * Set client into imported mode if SOME THIGN IS INDICATING it + * + */ + + // Disable all console logging. Credits to https://gist.github.com/kmonsoor/0244fdb4ad79a4826371e58a1a5fa984 + if (!logging) { + (() => { + let console = (window.console = window.console || {}); + [ + 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', + 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', + 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', + 'time', 'timeEnd', 'timeStamp', 'trace', 'warn' + ].forEach(method => { + console[method] = () => {}; + }); + })(); + } } // Registers one or more SocketEventListeners to the CorpusAnalysisClient. @@ -31,10 +57,32 @@ class CorpusAnalysisClient { this.displays[type] = corpusAnalysisDisplay; } + /** + * Function that takes one or more query selector + * strings in an array as an input. The function then creates a + * class field in the client object with the query selector + * string as the key. The selector will be converted to a valid JavaScript + * Field name i. e. #html-id-string -> this.htmlIdString + * The value will be the identifed element fetched with the querySelector + * method. MutlipleResults and atattchSomeCallback not yet implemented. + */ + getHTMLElements(arrayOfSelectors, multipleResults=false, atattchSomeCallback=false) { + for (let selector of arrayOfSelectors) { + let element = document.querySelector(selector); + let cleanKey = []; + selector.match(/\w+/g).forEach((word) => { + let tmp = word[0].toUpperCase() + word.slice(1); + cleanKey.push(tmp); + }); + cleanKey[0] = cleanKey[0].toLowerCase(); + cleanKey = cleanKey.join(''); + this[cleanKey] = element; + } + } + /** * Requests a corpus analysis session via socket.io. * Opens a loading modal at the start of the request - * Should be a private method if ES2020 is finalized (Maybe?) */ requestSession() { console.info('corpus_analysis_init: Client requesting session via', @@ -48,12 +96,11 @@ class CorpusAnalysisClient { /** * Sends the query string to the server. - * Should be a private method if ES2020 is finalized (Maybe?) */ requestQueryData(queryStr) { console.info('corpus_analysis_query: Client requesting query data via', 'socket.emit for the query', queryStr); - // TODO: Display stuff + // TODO: Display stuff ? this.socket.emit('corpus_analysis_query', queryStr); } } diff --git a/web/app/static/js/modules/nopaque.listenerCallbacks.js b/web/app/static/js/modules/nopaque.listenerCallbacks.js index b0369c6b..07b37c66 100644 --- a/web/app/static/js/modules/nopaque.listenerCallbacks.js +++ b/web/app/static/js/modules/nopaque.listenerCallbacks.js @@ -1,37 +1,68 @@ +// This callback is called on socket.on "query". +// Does some hiding, showing disabling etc. +/** + * This call back is called when the SocketEventListener 'recieveQueryStatus' + * has been triggered. It just gets the incoming status data of the issued + * and does some preperation work like hiding or showing elements and deleting + * the date from the last query. + */ +function querySetup(payload, client) { + // deletes old data from query issued before this new query + client.results.clearAll(); + // load necessary HTMLElements with selectory syntax and save them as fields + client.getHTMLElements(['#query-progress-bar', '#query-results-user-feedback', + '#recieved-match-count', '#total-match-count', + '#text-lookup-count', '#text-lookup-titles']); + client.requestQueryProgress = 0; + client.recievedMatchCount.textContent = 0; + client.totalMatchCount.textContent = `${payload.match_count}`; + client.queryResultsUserFeedback.classList.toggle('hide'); + client.queryProgressBar.classList.toggle('hide'); + client.queryProgressBar.lastElementChild.style.width = '0%' +} + /** * This callback is called when the SocketEventListener 'recieveQueryData' * has been triggered. It takes the incoming chunk and renders the results in a - * the results.jsList. It can eiterh hande llive incoming data or imported + * the results.jsList. It can either handle live incoming data or imported * results data. */ -function queryRenderResults(payload, client, imported=false) { - console.info("Current recieved chunk:", payload.chunk); - if (payload.chunk.cpos_ranges == true) { - client.results.data["cpos_ranges"] = true; - } else { - client.results.data["cpos_ranges"] = false; +function queryRenderResults(payload, client) { + client.getHTMLElements(['#recieved-match-count', '#match-count']) + const renderResults = (data) => { + /** + * resultItem saves the incoming chunk matches as objects to add those later + * to the client.results.jsList + */ + let resultItems = []; + // get infos for full match row + for (let [index, match] of data.matches.entries()) { + resultItems.push({...match, ...{'index': index + client.results.data.matches.length}}); + } + client.results.jsList.add(resultItems, (items) => { + for (let item of items) { + item.elm = client.results.jsList.createResultRowElement(item, data); + } + }); } - /** - * resultItem is a list where - */ - let resultItems = []; - // get infos for full match row - for (let [index, match] of payload.chunk.matches.entries()) { - resultItems.push({...match, ...{"index": index + client.results.data.matches.length}}); - } - if (!imported) { - // update progress bar - // queryResultsDeterminateElement.style.width = `${payload.progress}%`; - client.results.jsList.add(resultItems, (items) => { - for (let item of items) { - item.elm = client.results.jsList.createResultRowElement(item, payload.chunk); - } - }); + if (client.dynamicMode) { + if (payload.chunk.cpos_ranges == true) { + client.results.data['cpos_ranges'] = true; + } else { + client.results.data['cpos_ranges'] = false; + } + renderResults(payload.chunk); helperQueryRenderResults(payload, client); - // if (progress === 100) { - // resultsCreateElement.classList.remove("disabled"); - // queryResultsProgressElement.classList.add("hide"); - // queryResultsUserFeedbackElement.classList.add("hide"); + console.info('Result progress is:', client.requestQueryProgress); + if (client.requestQueryProgress === 100) { + /** + * activate, hide or show elements if all reults have been recieved + * also load some new elements taht have not ben loaded before + */ + client.getHTMLElements(['#query-results-create']); + client.queryProgressBar.classList.toggle('hide'); + client.queryResultsUserFeedback.classList.toggle('hide'); + client.queryResultsCreate.classList.toggle('disabled'); // resultsExportElement.classList.remove("disabled"); // addToSubResultsElement.removeAttribute("disabled"); // // inital expert mode check and sub results activation @@ -42,20 +73,18 @@ function queryRenderResults(payload, client, imported=false) { // if (expertModeSwitchElement.checked) { // client.results.jsList.expertModeOn("query-display"); // } - // } - } else if (imported) { - client.results.jsList.add(resultItems, (items) => { - for (let item of items) { - item.elm = client.results.jsList.createResultRowElement(item, payload.chunk, - true); - } - helperQueryRenderResults(payload, client); - progress = 100; - client.results.jsList.activateInspect(); - if (expertModeSwitchElement.checked) { - client.results.jsList.expertModeOn("query-display"); - } - }); + } + } else if (!client.dynamicMode) { + client.requestQueryProgress === 100; + client.queryResultsUserFeedback.classList.toggle('hide'); + renderResults(payload); + helperQueryRenderResults({'chunk': payload}, client); + client.queryProgressBar.classList.toggle('hide'); + client.queryResultsUserFeedback.classList.toggle('hide'); + client.results.jsList.activateInspect(); + if (expertModeSwitchElement.checked) { + client.results.jsList.expertModeOn("query-display"); + } } } @@ -65,20 +94,23 @@ function helperQueryRenderResults (payload, client) { client.results.jsList.changeContext(); // sets lr context on first result load // incorporating new chunk results into full results client.results.data.matches.push(...payload.chunk.matches); - client.results.data.addData(payload.chunk.cpos_lookup, "cpos_lookup"); - client.results.data.addData(payload.chunk.text_lookup, "text_lookup"); + client.results.data.addData(payload.chunk.cpos_lookup, 'cpos_lookup'); + client.results.data.addData(payload.chunk.text_lookup, 'text_lookup'); // complete metaData // client.results.metaData.add(); // show user current and total match count - // receivedMatchCountElement.innerText = `${client.results.data.matches.length}`; - // textLookupCountElement.innerText = `${Object.keys(client.results.data.text_lookup).length}`; + client.recievedMatchCount.textContent = `${client.results.data.matches.length}`; + client.textLookupCount.textContent = `${Object.keys(client.results.data.text_lookup).length}`; let titles = new Array(); for (let [key, value] of Object.entries(client.results.data.text_lookup)) { titles.push(`${value.title} (${value.publishing_year})`); }; - // textTitlesElement.innerText = `${titles.join(", ")}`; - // upate progress status - // progress = payload.progress; // global declaration + client.textLookupTitles.textContent = `${titles.join(", ")}`; + // update progress bar and requestQueryProgress + client.queryProgressBar.lastElementChild.style.width = `${payload.progress}%`; + client.requestQueryProgress = payload.progress; } -export { queryRenderResults } \ No newline at end of file +// TODO: Add data to data objekt using its own socket on event? + +export { querySetup, queryRenderResults } \ No newline at end of file diff --git a/web/app/static/js/modules/nopaque.listenerFunctions.js b/web/app/static/js/modules/nopaque.listenerFunctions.js index ad63d3ae..8c2d6044 100644 --- a/web/app/static/js/modules/nopaque.listenerFunctions.js +++ b/web/app/static/js/modules/nopaque.listenerFunctions.js @@ -1,4 +1,4 @@ -import {queryRenderResults} from './nopaque.listenerCallbacks.js' +import { querySetup, queryRenderResults } from './nopaque.listenerCallbacks.js' /** * Recieves a corpus analysis session via socket.io. @@ -7,13 +7,18 @@ import {queryRenderResults} from './nopaque.listenerCallbacks.js' */ function recieveSession(client) { client.socket.on('corpus_analysis_init', (response) => { + /** + * Check if request for session was ok. + * If OK execute callbacks and hide/show displays. + */ console.group('recieve session') - console.info('corpus_analysis_init: Client recieving session/or error', - 'codes via socket.on'); if (response.code === 200) { + console.info('corpus_analysis_init: Client recieving session/or error', + 'codes via socket.on'); + console.info(`corpus_analysis_init: ${response.code} - ${response.msg}`); console.info('corpus_analysis_init: Initialization succeeded'); console.info(response); - console.groupEnd(); + // Handling hide/show of displays if (client.displays.init != undefined) { client.displays.init.element.M_Modal.close(); client.displays.init.setVisibilityByStatus('success'); @@ -29,6 +34,7 @@ function recieveSession(client) { } console.error(`corpus_analysis_init: ${errorText}`); } + console.groupEnd(); }); } @@ -40,10 +46,36 @@ function recieveSession(client) { */ function recieveQueryStatus(client) { client.socket.on('corpus_analysis_query', (response) => { + /** + * Check if issued query was ok. + * If OK execute callbacks and hide/show displays. + */ console.group('corpus_analysis_query: Client recieving query process', - 'status via socket.on'); - client.results.clearAll(); - console.info(response); + 'status via socket.on'); + if (response.code === 200) { + console.info(`corpus_analysis_query: ${response.code} - ${response.msg}`); + console.info(response); + // Handling hide/show of displays + if (client.displays.query != undefined) { + client.displays.query.setVisibilityByStatus("success"); + } + // executing the callbacks + querySetup(response.payload, client); + } else { + let errorText = `Error ${response.payload.code} - ${response.payload.msg}`; + if (response.payload.code == 1281) { + errorText += ' - Invalid Query'; + } + nopaque.flash(errorText, "error"); + if (client.displays.query.errorContainer != undefined) { + client.displays.query.errorContainer.innerHTML = `

`+ + `error ${errorText}

`; + } + if (client.displays.query != undefined) { + client.displays.query.setVisibilityByStatus("error"); + } + console.error(`corpus_analysis_query: ${errorText}`); + } console.groupEnd(); }); } @@ -52,15 +84,21 @@ function recieveQueryStatus(client) { * Recieves the query data from the request and handles it. */ function recieveQueryData(client) { - client.socket.on('corpus_analysis_query_results', (response) => { - console.group('corpus_analysis_query_results: Client recieving query', - 'data via socket.on'); + console.group('corpus_analysis_query_results: Client recieving or loading', + 'query data.'); + if (client.dynamicMode) { + console.info('Client recieving query data via socket.on'); + client.socket.on('corpus_analysis_query_results', (response) => { console.info('Recieved chunk', response); queryRenderResults(response.payload, client); console.info('Added chunk data to results.data and rendered it with', - 'results.jsList') - console.groupEnd(); - }); + 'results.jsList'); + }); + } else { + console.info('Client loading imported query data from database.'); + queryRenderResults(client.results.data, client); + } + console.groupEnd(); } // export listeners from this module diff --git a/web/app/static/js/nopaque.Results.js b/web/app/static/js/nopaque.Results.js index c05af727..2f7778f1 100644 --- a/web/app/static/js/nopaque.Results.js +++ b/web/app/static/js/nopaque.Results.js @@ -23,12 +23,12 @@ class Data { // Sets empty object structure. Also usefull to delete old results. // matchCount default is 0 init(matchCount = 0, type = "results") { - this["matches"] = []; // list of all c with lc and rc - this["cpos_lookup"] = {}; // object contains all this key value pair - this["text_lookup"] = {}; // same as above for all text ids - this["match_count"] = matchCount; - this["corpus_type"] = "results"; - this["query"] = ""; + this.matches = []; // list of all c with lc and rc + this.cpos_lookup = {}; // object contains all this key value pair + this.text_lookup = {}; // same as above for all text ids + this.match_count = matchCount; + this.corpus_type = 'results'; + this.query = ''; } addData(jsonData, key=null) { @@ -45,7 +45,7 @@ class Data { let queryFormData; let queryStr; queryFormData = new FormData(queryFormElement); - queryStr = queryFormData.get("query-form-query"); + queryStr = queryFormData.get('query-form-query'); this["query"] = queryStr; } @@ -72,7 +72,7 @@ class Data { // Function to download data as Blob created from string // should be private but that is not yet a feature of javascript 08.04.2020 download(downloadElement, dataStr, filename, type, filenameSlug) { - console.log("Start Download!"); + console.log('Start Download!'); let file; filename += filenameSlug; file = new Blob([dataStr], {type: type}); diff --git a/web/app/static/js/web-components/InfoMenu.js b/web/app/static/js/web-components/InfoMenu.js new file mode 100644 index 00000000..af6f8599 --- /dev/null +++ b/web/app/static/js/web-components/InfoMenu.js @@ -0,0 +1,83 @@ +/** + * HTML for showing infos about the current query or result. Also gives + * the user the abiltiy to access the meta data for the current query or + * result. + */ +const template = document.createElement('template'); +template.innerHTML = ` + + + + + +
+
Infos
+
+
+
+ +
+
+

+ + 0 + of + (to be determined) + matches loaded. +
+ Matches occured in + (to be determined) + corpus files: +
+ (to be determined) +

+

+ help + The Server is still sending your results. + Functions like "Export Results" and "Match Inspect" will be + available after all matches have been loaded. +

+
+
+
+
+
+
+`; + +class InfoMenu extends HTMLElement { + constructor() { + super(); + + this.appendChild(template.content.cloneNode(true)); + this.attachShadow({ mode: 'open' }); + this.shadowRoot.appendChild(template.content.cloneNode(true)); + + } + + // methods that will be used in connectedCallback on eventListeners + showMetadata() { + console.log('Show metadata somehow'); + } + + connectedCallback() { + const showMetadataBtn = this.querySelector('#show-metadata'); + showMetadataBtn.addEventListener('click', () => this.showMetadata()); + } + + disconnectedCallback() { + const showMetadataBtn = this.querySelector('#show-metadata'); + showMetadataBtn.removeEventListener(); + } +} + +window.customElements.define('info-menu', InfoMenu); + +export { InfoMenu }; \ No newline at end of file diff --git a/web/app/templates/corpora/analyse_corpus.html.j2 b/web/app/templates/corpora/analyse_corpus.html.j2 index a5845fdb..b59c5171 100644 --- a/web/app/templates/corpora/analyse_corpus.html.j2 +++ b/web/app/templates/corpora/analyse_corpus.html.j2 @@ -67,89 +67,93 @@ {% endblock %} diff --git a/web/app/templates/corpora/corpus.html.j2 b/web/app/templates/corpora/corpus.html.j2 index 232a420a..53b514da 100644 --- a/web/app/templates/corpora/corpus.html.j2 +++ b/web/app/templates/corpora/corpus.html.j2 @@ -107,8 +107,8 @@ - - {% endblock %} diff --git a/web/app/templates/query_results/inspect.html.j2 b/web/app/templates/query_results/inspect.html.j2 index 85f954da..601e065e 100644 --- a/web/app/templates/query_results/inspect.html.j2 +++ b/web/app/templates/query_results/inspect.html.j2 @@ -54,180 +54,150 @@ {% include 'modals/context_modal.html.j2' %} - - - - {% endblock %} diff --git a/web/app/templates/query_results/inspect.html.j2.bak b/web/app/templates/query_results/inspect.html.j2.bak new file mode 100644 index 00000000..85f954da --- /dev/null +++ b/web/app/templates/query_results/inspect.html.j2.bak @@ -0,0 +1,233 @@ +{% extends "nopaque.html.j2" %} + +{% set headline = ' ' %} + +{% set full_width = True %} +{% set imported = True %} + +{% block page_content %} + +
+
+
+ +
+
+
+
+ search + + +
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ {% include 'interactions/infos.html.j2' %} + {% include 'interactions/display.html.j2' %} + {% include 'interactions/analysis.html.j2' %} + {% include 'interactions/cite.html.j2' %} +
+ {% include 'tables/query_results.html.j2' %} +
+
+
+ + +{% include 'interactions/scroll_to_top.html.j2' %} + + +{% include 'modals/show_metadata.html.j2' %} +{% include 'modals/show_text_details.html.j2' %} +{% include 'modals/context_modal.html.j2' %} + + + + + + +{% endblock %} diff --git a/web/app/templates/services/corpus_analysis.html.j2 b/web/app/templates/services/corpus_analysis.html.j2 index 53251cff..26e9905b 100644 --- a/web/app/templates/services/corpus_analysis.html.j2 +++ b/web/app/templates/services/corpus_analysis.html.j2 @@ -83,8 +83,9 @@ - {% endblock %}