From c5e56692b1700ef1a530448423e3b819455c9473 Mon Sep 17 00:00:00 2001 From: Stephan Porada Date: Tue, 17 Mar 2020 14:00:41 +0100 Subject: [PATCH] First analys_corpus clean up --- app/static/js/nopaque.analyse_corpus.js | 294 +++++++++++++++++++ app/templates/corpora/analyse_corpus.html.j2 | 281 +----------------- 2 files changed, 303 insertions(+), 272 deletions(-) create mode 100644 app/static/js/nopaque.analyse_corpus.js diff --git a/app/static/js/nopaque.analyse_corpus.js b/app/static/js/nopaque.analyse_corpus.js new file mode 100644 index 00000000..ad9007bc --- /dev/null +++ b/app/static/js/nopaque.analyse_corpus.js @@ -0,0 +1,294 @@ +// ### Send query functions ### + +//function to get current queryData from a given queryFormElement +function getQueryData(queryFormElement) { + // Get query form element and save its data on submit and send this data via + formData = new FormData(queryFormElement); + queryData = {"context": formData.get("context"), // global declaration + "hits_per_page": formData.get("hits_per_page"), + "query": formData.get("query")}; + console.log("Query data:", queryData); + return queryData +} + +// Function to send a query to the CQP server using queryData gatherd with getQueryData +function sendQuery(event) { + event.preventDefault(); + queryData = getQueryData(queryFormElement); + nopaque.socket.emit("corpus_analysis_query", queryData.query); + // full results object declaration, global declaration! + // will always be reset if a query is sent, so that only the chunks of the + // current query will be saved in it + result = {}; + result["matches"] = []; + result["cpos_lookup"] = {}; + result["text_lookup"] = {}; + result["loaded_match_count"] = 0; + result["match_count"] = 0; + result["query"] = ""; + // some hiding/showing for loading animation + queryLoadingElement.classList.remove("hide"); + queryResultsTableElement.classList.add("hide"); + nopaque.toast("Query has been sent!"); + + resultListOptions = {page: queryData["hits_per_page"], + pagination: [{ + name: "paginationTop", + paginationClass: "paginationTop", + innerWindow: 8, + outerWindow: 1 + }, { + paginationClass: "paginationBottom", + innerWindow: 8, + outerWindow: 1 + }], + valueNames: ["titles", "lc", "hit", "rc", {data: ["index"]}], + item: ``}; + resultList = new ResultList('result-list', resultListOptions); + resultList.clear(); // empty list for new query +} + +// ### Recieve results from sent query and display them in a list etc. ### + +// Function used when CQP server sends back the query results using socketio +function recieveResults(response) { + // ERROR code checking + if (response["code"] === 0) { + console.log("[SUCCESS] corpus_analysis_init"); + console.log("Code:" + response["code"]); + // further code execution of this code block starting in line 342 + } else if (response["code"] === 1) { + queryResultsTableElement.classList.add("hide"); + queryLoadingElement.classList.add("hide"); + nopaque.toast("Invalid query entered!", "red"); + console.log("[ERROR] corpus_analysis_init"); + console.log("Code:" + response["code"]); + return; // no further code execution of this code block + } else { + console.log("[ERROR] corpus_analysis_init"); + console.log("Code:" + response["code"]); + return; // no further code execution of this code block + } + // logs the current recieved chunk + chunk = {}; + chunk = response["result"]; + console.log("### corpus_analysis chunk ###"); + console.log(chunk); + // logs and extends/push/update the current recieved chunk to the + // result Object + console.log("### corpus analysis updated result json ###"); + result["matches"].push(...chunk["matches"]); + Object.assign(result["cpos_lookup"], chunk["cpos_lookup"]); + Object.assign(result["text_lookup"], chunk["text_lookup"]); + result["match_count"] = chunk["match_count"]; + console.log("Before Current match count", result["loaded_match_count"]); + result["query"] = queryData["query"]; + console.log(result); + // Some hiding and showing of loading animations + queryLoadingElement.classList.add("hide"); + queryResultsTableElement.classList.remove("hide"); + queryResultsElement.innerHTML = ""; + + // check if query has any results + if (chunk["matches"].length === 0) { + queryResultsTableElement.classList.add("hide"); + nopaque.toast("No results for this query!"); + return; + } + + // List building/appending the chunks when query had results + match_count = chunk["match_count"]; + let count_corpus_files = Object.keys(result["text_lookup"]).length; + + var resultItems = []; // list for holding every row item + // get infos for full match row + for (let [index, match] of chunk["matches"].entries()) { + resultItems.push({...match, ...{"index": index + result["loaded_match_count"]}}); + } + resultList.add(resultItems, items => { + for (let item of items) { + item.elm = resultList.createResultRowElement(item, chunk); + } + resultList.update(); + changeContext(); // sets lr context on first result load + }); + result["loaded_match_count"] += Object.keys(chunk["matches"]).length; + console.log("After current match count", result["loaded_match_count"]); + queryResultsMetadataElement.innerHTML = `

The query resulted in a total of ${chunk["match_count"]} matches.

${result["loaded_match_count"]} of ${result["match_count"]} matches in ${count_corpus_files} corpus files have been loaded.

`; + queryResultsMetadataElement.appendChild(exportQueryResults); + exportQueryResults.classList.remove("hide"); +} + +// Function to inspect one match, to show more details +function inspect(dataIndex) { + console.log("Inspect!"); + console.log(result["matches"][dataIndex]["hit"]); + contextModal.open(); + nopaque.socket.emit("inspect_match", {"cpos": result["matches"][dataIndex]["hit"]}); +} + +// ### Display options changing live how the matches are being displayed ### + +// Event function that changes the shown hits per page. +// Just alters the resultList.page property +function changeHitsPerPage(event) { + try { + resultList.page = event.target.value; + resultList.update(); + nopaque.toast("Updated matches per page.") + } catch (e) { + console.log("resultList has no results right now. Live update of items per page is useless for now."); + } +} + +// Event function triggered on context select change and also if pagination is clicked +function changeContext(event) { + // newValue = event.target.value; // cannot use this anymore due to reuse of this function in the above paginationElements eventListener + console.log("Event", event); + try { + if (event.type === "change") { + nopaque.toast("Updated context per match!"); + } + } catch (e) { + console.log(e); + } finally { + var contextPerItemElement = document.getElementById("context"); + newValue = contextPerItemElement.value; + console.log(newValue); + var lc = document.getElementsByClassName("left-context"); + var rc = document.getElementsByClassName("right-context"); + for (let element of lc) { + array = Array.from(element.childNodes); + for (let element of array.slice(newValue)) { + element.classList.add("hide"); + } + for (let element of array.slice(0, newValue)) { + element.classList.remove("hide"); + } + } + for (let element of rc) { + array = Array.from(element.childNodes); + for (let element of array.slice(newValue)) { + element.classList.add("hide"); + } + for (let element of array.slice(0, newValue)) { + element.classList.remove("hide"); + } + } + } +} + +// ### Expert view event functions ### + +// Event function to check if pagination is used and then look if +// expertModeSwitchElement is checked +// if checked than expertModeOn is executed +// if unchecked expertModeOff is executed +function eventHandlerCheck(event) { + console.log("pagination used!"); + console.log(expertModeSwitchElement.checked); + if (expertModeSwitchElement.checked) { + expertModeOn(event.currentTarget.tokenElements, result); + } else if (!expertModeSwitchElement.checked) { + event.preventDefault(); + console.log("prevented! Destroy"); + expertModeOff(event.currentTarget.tokenElements); + } +} + +// function to apply extra information and animation to every token +function expertModeOn(tokenElements, result_lookup) { + console.log("expertModeOn!"); + for (tokenElement of tokenElements) { + tokenElement.classList.add("chip"); + tokenElement.classList.add("hoverable"); + tokenElement.classList.add("expert-view"); + token = result_lookup["cpos_lookup"][tokenElement.dataset.cpos]; + tokenElement.addEventListener("mouseover", function(event) { + console.log("Mouseover!"); + console.log(event.target); + token = result_lookup["cpos_lookup"][event.target.dataset.cpos]; + addToolTipToTokenElement(event.target, token); + }) + } +} + +// fuction that creates Tooltip for one token and extracts the corresponding +// infos from the result JSON +function addToolTipToTokenElement(tokenElement, token) { + M.Tooltip.init(tokenElement, + {"html": ` + + + + + + + + +
Token informationSource information
+ Word: ${token["word"]}
+ Lemma: ${token["lemma"]}
+ POS: ${token["pos"]}
+ Simple POS: ${token["simple_pos"]}
+ NER: ${token["ner"]} +
+ Title: ${result["text_lookup"][token["text"]]["title"]}
+ Author: ${result["text_lookup"][token["text"]]["author"]}
+ Publishing year: ${result["text_lookup"][token["text"]]["publishing_year"]} +
`, + "inDuration": 1500, + "margin": 15, + "position": "top", + "transitionMovement": 0}); +} + +// function to remove extra informations and animations from tokens +function expertModeOff(tokenElements) { + console.log("expertModeOff!"); + for (tokenElement of tokenElements) { + tokenElement.classList.remove("chip"); + tokenElement.classList.remove("hoverable"); + tokenElement.classList.remove("expert-view"); + tokenElement.outerHTML = tokenElement.outerHTML; // this is actually a workaround, but it works pretty fast + } +} + + +// ### Download results functions ### + +// function creates a unique and safe filename for the download +function createDownloadFilename() { + // get and create metadata + console.log("Create Metadata!"); + var today = new Date(); + var currentDate = today.getUTCFullYear() + '-' + (today.getUTCMonth() +1) + '-' + today.getUTCDate(); + var currentTime = today.getUTCHours() + ":" + today.getUTCMinutes() + ":" + today.getUTCSeconds(); + var safeFilename = result['query'].replace(/[^a-z0-9_-]/gi, "_"); + var resultFilename = "UTC-" + currentDate + "_" + currentTime + "_" + safeFilename; + return resultFilename +} + +// function to download the results as JSON +function downloadJSONRessource(resultFilename) { + // stringify JSON object for json download + var dataStr = JSON.stringify(result, undefined, "\t"); // use tabs to save some space + // get downloadResultsElement + downloadElement = document.getElementById("download-results-json"); + // start actual download + download(downloadElement, dataStr, resultFilename, "text/json", ".json") +} + +// Function to download data as a Blob created from a string, should be multi purpose +function download(downloadElem, dataStr, filename, type, filenameSlug) { + console.log("Start Download!"); + filename += filenameSlug; + var file = new Blob([dataStr], {type: type}); + if (window.navigator.msSaveOrOpenBlob) // IE10+ + window.navigator.msSaveOrOpenBlob(file, filename); + else { // Others + var url = URL.createObjectURL(file); + downloadElem.href = url; + downloadElem.download = filename; + } +} \ No newline at end of file diff --git a/app/templates/corpora/analyse_corpus.html.j2 b/app/templates/corpora/analyse_corpus.html.j2 index a118f9f4..7745b637 100644 --- a/app/templates/corpora/analyse_corpus.html.j2 +++ b/app/templates/corpora/analyse_corpus.html.j2 @@ -218,7 +218,9 @@ server side --> - +