Corpus analysis version 3.0 lul

This commit is contained in:
Stephan Porada 2020-04-01 13:44:06 +02:00
parent 90cf1bf8a0
commit 46b82f5737
9 changed files with 529 additions and 62 deletions

View File

@ -67,7 +67,7 @@ def pj_corpus_analysis_query(query):
socketio.emit('pj_corpus_analysis_query', response, room=request.sid)
return
response = {'code': 200, 'desc': None, 'msg': 'OK',
'payload': {**query_status, 'num_matches': results.size}}
'payload': {**query_status, 'match_count': results.size}}
socketio.emit('pj_corpus_analysis_query', response, room=request.sid)
chunk_size = 100
chunk_start = 0

View File

@ -1,9 +1,11 @@
from flask_wtf import FlaskForm
from wtforms import BooleanField, StringField, SubmitField, SelectField
from wtforms.validators import DataRequired, Length
class PJQueryForm(FlaskForm):
query = StringField('Query')
query = StringField('Query',
validators=[DataRequired(), Length(1, 1024)])
submit = SubmitField('Send query')
@ -33,3 +35,13 @@ class PJDisplayOptionsForm(FlaskForm):
('80', '80'),
('90', '90'),
('100', '100')])
class QueryDownloadForm(FlaskForm):
file_type = SelectField('File type',
choices=[('', 'Choose file type'),
('csv', 'csv'),
('json', 'json'),
('excel', 'excel'),
('html', 'html-table')],
validators=[DataRequired()])

View File

@ -1,7 +1,7 @@
from flask import request, render_template
from flask_login import login_required
from . import corpora
from .pj_forms import PJDisplayOptionsForm, PJQueryForm
from .pj_forms import PJDisplayOptionsForm, PJQueryForm, QueryDownloadForm
from .. import db
from ..models import Corpus
@ -19,7 +19,10 @@ def pj_analyse_corpus(corpus_id):
results_per_page=request.args.get('results_per_page', 30))
query_form = PJQueryForm(prefix='query-form',
query=request.args.get('query'))
query_download_form = QueryDownloadForm()
return render_template('corpora/pj_analyse_corpus.html.j2',
corpus_id=corpus_id,
display_options_form=display_options_form,
query_form=query_form, title='Corpus analysis')
query_form=query_form,
query_download_form=query_download_form,
title='Corpus analysis')

View File

@ -4,34 +4,70 @@ class CorpusAnalysisClient {
this.corpusId = corpusId;
this.displays = {};
this.socket = socket;
this.resultList = {};
// js list options and intialization
let displayOptionsData = this.getDisplayOptions(displayOptionsFormElement);
let resultListOptions = {page: displayOptionsData["hitsPerPage"],
pagination: [{
name: "paginationTop",
paginationClass: "paginationTop",
innerWindow: 8,
outerWindow: 1
}, {
paginationClass: "paginationBottom",
innerWindow: 8,
outerWindow: 1
}],
valueNames: ["titles", "lc", "c", "rc", {data: ["index"]}],
item: `<span></span>`};
this.resultList = new ResultList("result-list", resultListOptions);
// socket on event fpr corpous analysis initialization
socket.on("pj_corpus_analysis_init", (response) => {
var errorText;
if (response.code === 200) {
console.log(`pj_corpus_analysis_init: ${response.code} - ${response.msg}`);
if (this.callbacks.init != undefined) {this.callbacks.init(response.payload);}
if (this.displays.init != undefined) {this.displays.init.setVisibilityByStatus("success");}
if (this.callbacks.init != undefined) {
this.callbacks.init(response.payload);
}
if (this.displays.init != undefined) {
this.displays.init.setVisibilityByStatus("success");
}
} else {
errorText = `Error ${response.code} - ${response.msg}`;
if (this.displays.init.errorContainer != undefined) {this.displays.init.errorContainer.innerHTML = `<p class="red-text"><i class="material-icons tiny">error</i> ${errorText}</p>`;}
if (this.displays.init != undefined) {this.displays.init.setVisibilityByStatus("error");}
if (this.displays.init.errorContainer != undefined) {
this.displays.init.errorContainer.innerHTML = `<p class="red-text"><i class="material-icons tiny">error</i> ${errorText}</p>`;
}
if (this.displays.init != undefined) {
this.displays.init.setVisibilityByStatus("error");
}
console.error(`pj_corpus_analysis_init: ${errorText}`);
}
});
// socket on event for recieveing query results
socket.on("pj_corpus_analysis_query", (response) => {
var errorText;
if (response.code === 200) {
console.log(`pj_corpus_analysis_query: ${response.code} - ${response.msg}`);
if (this.callbacks.query != undefined) {this.callbacks.query(response.payload);}
if (this.displays.query != undefined) {this.displays.query.setVisibilityByStatus("success");}
if (this.callbacks.query != undefined) {
this.callbacks.query(response.payload);
}
if (this.displays.query != undefined) {
this.displays.query.setVisibilityByStatus("success");
}
} else {
errorText = `Error ${response.payload.code} - ${response.payload.msg}`;
nopaque.flash("error", errorText);
if (this.displays.query.errorContainer != undefined) {this.displays.query.errorContainer.innerHTML = `<p class="red-text"><i class="material-icons tiny">error</i> ${errorText}</p>`;}
if (this.displays.query != undefined) {this.displays.query.setVisibilityByStatus("error");}
if (this.displays.query.errorContainer != undefined) {
this.displays.query.errorContainer.innerHTML = `<p class="red-text"><i class="material-icons tiny">error</i> ${errorText}</p>`;
}
if (this.displays.query != undefined) {
this.displays.query.setVisibilityByStatus("error");
}
console.error(`pj_corpus_analysis_query: ${errorText}`);
}
});
@ -42,14 +78,26 @@ class CorpusAnalysisClient {
}
init() {
if (this.displays.init.errorContainer != undefined) {this.displays.init.errorContainer.innerHTML == "";}
if (this.displays.init != undefined) {this.displays.init.setVisibilityByStatus("waiting");}
if (this.displays.init.errorContainer != undefined) {
this.displays.init.errorContainer.innerHTML == "";
}
if (this.displays.init != undefined) {
this.displays.init.setVisibilityByStatus("waiting");
}
this.socket.emit("pj_corpus_analysis_init", this.corpusId);
}
query(queryStr) {
if (this.displays.query.errorContainer != undefined) {this.displays.query.errorContainer.innerHTML == "";}
if (this.displays.query != undefined) {this.displays.query.setVisibilityByStatus("waiting");}
sendQuery(queryStr) {
let displayOptionsData;
let resultListOptions;
if (this.displays.query.errorContainer != undefined) {
this.displays.query.errorContainer.innerHTML == "";
}
if (this.displays.query != undefined) {
this.displays.query.setVisibilityByStatus("waiting");
}
this.resultList.clear(); // empty list for new query
nopaque.socket.emit("pj_corpus_analysis_query", queryStr);
}
@ -62,6 +110,18 @@ class CorpusAnalysisClient {
return queryStr
}
getDisplayOptions(displayOptionsFormElement) {
// gets display options parameters
let displayOptionsFormData
let displayOptionsData;
displayOptionsFormData = new FormData(displayOptionsFormElement);
displayOptionsData = {"resultsPerPage": displayOptionsFormData.get("display-options-form-results_per_page"),
"resultsContex": displayOptionsFormData.get("display-options-form-result_context"),
"expertMode": displayOptionsFormData.get("display-options-form-expert_mode")};
console.log(displayOptionsData);
return displayOptionsData
}
setCallback(type, callback) {
// saves callback functions into an object. Key is function type, callback
// is the callback function
@ -81,30 +141,56 @@ class CorpusAnalysisDisplay {
this.showOnError = element.querySelectorAll(".show-on-error");
this.showOnSuccess = element.querySelectorAll(".show-on-success");
this.showWhileWaiting = element.querySelectorAll(".show-while-waiting");
this.hideOnComplete = element.querySelectorAll(".hide-on-complete")
}
setVisibilityByStatus(status) {
switch (status) {
case "error":
for (let element of this.showOnError) {element.classList.remove("hide");}
for (let element of this.showOnSuccess) {element.classList.add("hide");}
for (let element of this.showWhileWaiting) {element.classList.add("hide");}
for (let element of this.showOnError) {
element.classList.remove("hide");
}
for (let element of this.showOnSuccess) {
element.classList.add("hide");
}
for (let element of this.showWhileWaiting) {
element.classList.add("hide");
}
break;
case "success":
for (let element of this.showOnError) {element.classList.add("hide");}
for (let element of this.showOnSuccess) {element.classList.remove("hide");}
for (let element of this.showWhileWaiting) {element.classList.add("hide");}
for (let element of this.showOnError) {
element.classList.add("hide");
}
for (let element of this.showOnSuccess) {
element.classList.remove("hide");
}
for (let element of this.showWhileWaiting) {
element.classList.add("hide");
}
break;
case "waiting":
for (let element of this.showOnError) {element.classList.add("hide");}
for (let element of this.showOnSuccess) {element.classList.add("hide");}
for (let element of this.showWhileWaiting) {element.classList.remove("hide");}
for (let element of this.showOnError) {
element.classList.add("hide");
}
for (let element of this.showOnSuccess) {
element.classList.add("hide");
}
for (let element of this.showWhileWaiting) {
element.classList.remove("hide");
}
break;
default:
// Hide all
for (let element of this.showOnError) {element.classList.add("hide");}
for (let element of this.showOnSuccess) {element.classList.add("hide");}
for (let element of this.showWhileWaiting) {element.classList.add("hide");}
for (let element of this.showOnError) {
element.classList.add("hide");
}
for (let element of this.showOnSuccess) {
element.classList.add("hide");
}
for (let element of this.showWhileWaiting) {
element.classList.add("hide");
}
}
}
}

View File

@ -101,7 +101,7 @@ function helperSendQuery(queryData) {
outerWindow: 1
}],
valueNames: ["titles", "lc", "c", "rc", {data: ["index"]}],
item: `<span class="hidden"></span>`};
item: `<span></span>`};
resultList = new ResultList('result-list', resultListOptions);
resultList.clear(); // empty list for new query
}

View File

@ -1,32 +1,55 @@
function querySetup(payload) {
// This is called when a query was successfull
console.log("Query setup.");
console.log(payload);
// some hiding
queryResultsExportElement.classList.add("disabled");
console.log("Query initial setup seccessfull.");
queryResultsDeterminateElement.style.width = "0%";
queryResultsProgressElement.classList.remove("hide");
receivedMatchNumElement.innerText = "0";
textLookupNumElement.innerText = "0";
matchNumElement.innerText = payload.num_matches;
queryResultsUserFeedbackElement.classList.remove("hide");
receivedMatchCountElement.innerText = "0";
textLookupCountElement.innerText = "0";
matchCountElement.innerText = payload.match_count;
// always re initializes results to delete old results from it
results = {};
results["matches"] = []; // list of all c with lc and rc
results["cpos_lookup"] = {}; // object contains all cpos as key value pair
results["text_lookup"] = {}; // same as above for all text ids
results["num_matches"] = payload.num_matches;
results["match_count"] = payload.match_count;
results["query"] = client.getQueryStr(queryFormElement);
}
function queryRenderResults(payload) {
// This is called when results are transmitted.
console.log("CHUNK:", payload.chunk);
console.log("RESULTS:", results);
// This is called when results are transmitted and being recieved
console.log("Current recieved chunk:", payload.chunk);
// upate progress status
if (payload.progress === 100) {
queryResultsProgressElement.classList.add("hide");
queryResultsUserFeedbackElement.classList.add("hide");
queryResultsExportElement.classList.remove("disabled");
activateInspect();
}
// update progress bar
queryResultsDeterminateElement.style.width = `${payload.progress}%`;
results.matches.push(...payload.chunk.matches);
receivedMatchNumElement.innerText = `${results.matches.length}`;
// building the result list js list from incoming chunk
resultItems = []; // list for holding every row item
// get infos for full match row
for (let [index, match] of payload.chunk.matches.entries()) {
resultItems.push({...match, ...{"index": index + results.matches.length}});
}
client.resultList.add(resultItems, (items) => {
for (let item of items) {
item.elm = client.resultList.createResultRowElement(item, payload.chunk);
}
client.resultList.update();
changeContext(); // sets lr context on first result load
});
// incorporating new chunk results into full results
results.matches.push(...payload.chunk.matches);
Object.assign(results.cpos_lookup, payload.chunk.cpos_lookup);
Object.assign(results.text_lookup, payload.chunk.text_lookup);
textLookupNumElement.innerText = `${Object.keys(results.text_lookup).length}`;
// show user current and total match count
receivedMatchCountElement.innerText = `${results.matches.length}`;
textLookupCountElement.innerText = `${Object.keys(results.text_lookup).length}`;
console.log("Results recieved:", results);
}

View File

@ -149,7 +149,7 @@ class ResultList extends List {
token = chunk["cpos_lookup"][cpos];
hitCellElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
// get text titles of every hit cpos token
textTitles.add(chunk["text_lookup"][token["text"]]["title"]);
textTitles.add(chunk["text_lookup"][token["text"]]["text_title"]);
// add button to trigger more context to every match td
var inspectBtn = document.createElement("a");
inspectBtn.setAttribute("class", "btn-floating btn-flat waves-effect waves-light grey right inspect disabled");

View File

@ -0,0 +1,231 @@
// ###### Download results functions ######
// TODO: Maybe write these as class functions? For this maybe create a result class
// function creates a unique and safe filename for the download
function createDownloadFilename() {
let today;
let currentDate;
let currentTime;
let safeFilename;
let resultFilename;
// get and create metadata
console.log("Create Metadata!");
today = new Date();
currentDate = today.getUTCFullYear() + '-' + (today.getUTCMonth() +1) + '-' + today.getUTCDate();
currentTime = today.getUTCHours() + ":" + today.getUTCMinutes() + ":" + today.getUTCSeconds();
safeFilename = results["query"].replace(/[^a-z0-9_-]/gi, "_");
resultFilename = "UTC-" + currentDate + "_" + currentTime + "_" + safeFilename;
return resultFilename
}
// function to download the results as JSON
function downloadJSONRessource(resultFilename) {
let dataStr;
let downloadElement;
// stringify JSON object for json download
dataStr = JSON.stringify(results, 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) {
let file;
console.log("Start Download!");
filename += filenameSlug;
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;
}
}
// ###### Functions to inspect one match, to show more details ######
// activate inspect buttons if queryFinished is true
function activateInspect(progress) {
let inspectBtnElements;
inspectBtnElements = document.getElementsByClassName("inspect");
for (let inspectBtn of inspectBtnElements) {
inspectBtn.classList.remove("disabled");
}
}
//gets result cpos infos for one dataIndex to send back to the server
function inspect(dataIndex) {
console.log("Inspect!");
console.log(result["matches"][dataIndex]["c"]);
contextModal.open();
nopaque.socket.emit("inspect_match", {"cpos": result["matches"][dataIndex]["c"]});
}
function showMatchContext(message) {
let contextResultsElement;
let sentenceElement
let token;
let tokenElement;
console.log("###### match_context ######");
console.log("Incoming data:", message);
contextResultsElement = document.getElementById("context-results");
contextResultsElement.innerHTML = "<p>&nbsp;</p>";
document.getElementById("context-modal-loading").classList.add("hide");
document.getElementById("context-modal-ready").classList.remove("hide");
for (let [key, value] of Object.entries(message['context_s_cpos'])) {
sentenceElement = document.createElement("p");
for (let cpos of value) {
token = message["cpos_lookup"][cpos];
tokenElement = document.createElement("span");
tokenElement.classList.add("token");
if (message["match_cpos_list"].includes(cpos)) {
tokenElement.classList.add("bold");
tokenElement.classList.add("light-green");
}
tokenElement.dataset.cpos = cpos;
tokenElement.innerText = token["word"];
var expertModeSwitchElement = document.getElementById("expert-mode-switch");
if (expertModeSwitchElement.checked) {
expertModeOn([tokenElement], message);
}
sentenceElement.append(tokenElement);
sentenceElement.append(document.createTextNode(" "));
}
contextResultsElement.append(sentenceElement);
}
}
// ###### 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.flash("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) {
let newContextValue;
let lc;
let rc;
let array;
try {
if (event.type === "change") {
nopaque.flash("Updated context per match!");
}
} catch (e) {
// console.log(e);
// console.log("This error is expected.");
} finally {
newContextValue = document.getElementById("display-options-form-result_context").value;
console.log("Context value is:", newContextValue);
lc = document.getElementsByClassName("left-context");
rc = document.getElementsByClassName("right-context");
for (let element of lc) {
array = Array.from(element.childNodes);
for (let element of array.slice(newContextValue)) {
element.classList.add("hide");
}
for (let element of array.slice(0, newContextValue)) {
element.classList.remove("hide");
}
}
for (let element of rc) {
array = Array.from(element.childNodes);
for (let element of array.slice(newContextValue)) {
element.classList.add("hide");
}
for (let element of array.slice(0, newContextValue)) {
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) {
let token;
console.log("expertModeOn!");
for (let 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": `<table>
<tr>
<th>Token information</th>
<th>Source information</th>
</tr>
<tr>
<td class="left-align">
Word: ${token["word"]}<br>
Lemma: ${token["lemma"]}<br>
POS: ${token["pos"]}<br>
Simple POS: ${token["simple_pos"]}<br>
NER: ${token["ner"]}
</td>
<td class="left-align">
Title: ${result["text_lookup"][token["text"]]["title"]}<br>
Author: ${result["text_lookup"][token["text"]]["author"]}<br>
Publishing year: ${result["text_lookup"][token["text"]]["publishing_year"]}
</td>
</tr>
</table>`,
"inDuration": 1500,
"margin": 15,
"position": "top",
"transitionMovement": 0});
}
// function to remove extra informations and animations from tokens
function expertModeOff(tokenElements) {
console.log("expertModeOff!");
for (let 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
}
}

View File

@ -78,20 +78,58 @@
</ul>
</div>
<!-- entire results div/card -->
<div class="col s12" id="query-display">
<div class="card">
<div class="card-content">
<div class="card-content" id="result-list" style="overflow: hidden;">
<span class="card-title">Query Results</span>
<div class="error-container hide show-on-error"></div>
<div class="hide show-on-success">
<div class="col s12 m6 l6">
<div class="row">
<p>
<span id="received-match-num"></span> of <span id="match-num"></span> matches loaded.<br>
Matches occured in <span id="text-lookup-num"></span> corpus files.
<span id="received-match-count">
</span> of
<span id="match-count"></span>
matches loaded.
<br>
Matches occured in <span id="text-lookup-count"></span> corpus files.
</p>
<p id="query-results-user-feedback">
<i class="material-icons">help</i>
The Server is still sending your results. Functions like "Export Results" and "Match Inspect" will be available after all matches have been loaded.
</p>
</div>
<div class="row">
<div class="progress" id="query-results-progress">
<div class="determinate" id="query-results-determinate"></div>
</div>
<div id="query-results"></div>
</div>
</div>
<div class="col s12 m6 l6">
<div class="row">
<button id="query-results-export" class="waves-effect waves-light btn-small right disabled" type="submit">Export Results<i class="material-icons right">file_download</i></button>
</div>
</div>
<!-- Table showing the query results -->
<div class="col s12">
<ul class="pagination paginationTop"></ul>
<table class="responsive-table highlight">
<thead>
<tr>
<th style="width: 2%">Nr.</th>
<th style="width: 3%">Title</th>
<th style="width: 25%">Left context</th>
<th style="width: 45%">Match</th>
<th style="width: 25%">Right Context</th>
</tr>
</thead>
<tbody class="list" id="query-results">
</tbody>
</table>
<ul class="pagination paginationBottom"></ul>
</div>
</div>
</div>
</div>
@ -99,6 +137,7 @@
<!-- Modals -->
<!-- Analysis init modal -->
<div class="modal no-autoinit" id="init-display">
<div class="modal-content">
<h4>Initializing your corpus analysis session...</h4>
@ -109,11 +148,60 @@
</div>
</div>
<!-- Export query results modal -->
<div id="query-results-download-modal"
class="modal modal-fixed-footer no-autoinit">
<div class="modal-content">
{{ query_download_form.hidden_tag() }}
<h4>Download current query Results</h4>
<p>The results of the current query can be downlaoded as several files like csv or json. Those files can be used in other software like excel. Also it is easy to publish your results as raw data like this!</p>
<table>
<tr>
<td>JSON</td>
<td>
<a class="btn waves-effect waves-light" id="download-results-json">Download
<i class="material-icons right">file_download</i>
</a>
</td>
</tr>
<tr>
<td>CSV</td>
<td>
<a class="btn waves-effect waves-light disabled" id="download-results-csv">Download
<i class="material-icons right">file_download</i>
</a>
</td>
</tr>
<tr>
<td>EXCEL</td>
<td>
<a class="btn waves-effect waves-light disabled">Download
<i class="material-icons right">file_download</i>
</a>
</td>
</tr>
<tr>
<td>HTML</td>
<td>
<a class="btn waves-effect waves-light disabled">Download
<i class="material-icons right">file_download</i>
</a>
</td>
</tr>
</table>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect waves-light red btn">Close</a>
</div>
</div>
<script src="{{ url_for('static', filename='js/nopaque.CorpusAnalysisClient.js') }}">
</script>
<script src="{{ url_for('static', filename='js/nopaque.callbacks.js') }}">
</script>
<script src="{{ url_for('static', filename='js/nopaque.pj_analyse_corpus.js') }}">
</script>
<script>
// ###### Defining global variables used in other functions ######
var results; // full JSON object holding match results
@ -121,32 +209,44 @@
var collapsibleElements; // all collapsibles on site
var queryResultsProgressElement; // Div element holding the progress bar
var queryResultsDeterminateElement; // The progress bar for recieved results
var queryResultsUserFeedbackElement; // Element showing match count|total etc
var queryResultsExportElement; // Download button opens download modal
var exportModal;
var initModal;
var downloadResultsJSONElement; // button for downloading results as JSON
var displayOptionsFormElement;
// ###### Initialize variables ######
queryFormElement = document.getElementById("query-form");
queryResultsProgressElement = document.getElementById("query-results-progress");
queryResultsDeterminateElement = document.getElementById("query-results-determinate");
queryResultsUserFeedbackElement = document.getElementById("query-results-user-feedback");
queryResultsExportElement = document.getElementById("query-results-export");
displayOptionsFormElement = document.getElementById("display-options-form");
// ###### Set some css options ######
// get collapsibles to alter options of those
var collapsibleElements = document.querySelector('.collapsible.expandable');
var client = undefined;
var initDisplay = undefined;
var queryDisplay = undefined;
var initDisplayElement = document.getElementById("init-display");
var queryDisplayElement = document.getElementById("query-display");
var queryFormElement = document.getElementById("query-form");
var queryResultsDeterminateElement = document.getElementById("query-results-determinate");
var queryResultsProgressElement = document.getElementById("query-results-progress");
receivedMatchNumElement = document.getElementById("received-match-num");
matchNumElement = document.getElementById("match-num");
textLookupNumElement = document.getElementById("text-lookup-num");
receivedMatchCountElement = document.getElementById("received-match-count");
matchCountElement = document.getElementById("match-count");
textLookupCountElement = document.getElementById("text-lookup-count");
document.addEventListener("DOMContentLoaded", () => {
//set accordion of collapsibles to false
M.Collapsible.init(collapsibleElements, {accordion: false});
var initModal = M.Modal.init(initDisplayElement, {dismissible: false});
// creates some models on DOMContentLoaded
exportModal = M.Modal.init(document.getElementById("query-results-download-modal"),
{"dismissible": true});
initModal = M.Modal.init(initDisplayElement, {dismissible: false});
// Init corpus analysis components
initDisplay = new CorpusAnalysisDisplay(initDisplayElement);
queryDisplay = new CorpusAnalysisDisplay(queryDisplayElement);
@ -157,6 +257,7 @@
client.setCallback("init", () => {
initModal.close();
});
client.setDisplay("query", queryResultsUserFeedbackElement);
client.setDisplay("query", queryDisplay);
client.setCallback("query", (payload) => {
querySetup(payload);
@ -173,8 +274,19 @@
event.preventDefault();
// Get query string and send query to server
let queryStr = client.getQueryStr(queryFormElement);
client.query(queryStr);
client.sendQuery(queryStr);
});
});
// Add onclick to open download modal wen Export Results button is pressed
queryResultsExportElement.onclick = function() {
exportModal.open();
}
// add onclick to download JSON button and download the file
downloadResultsJSONElement = document.getElementById("download-results-json")
downloadResultsJSONElement.onclick = function() {
let filename = createDownloadFilename();
downloadJSONRessource(filename)};
</script>
{% endblock %}