mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-11-15 01:05:42 +00:00
Corpus analysis version 3.0 lul
This commit is contained in:
parent
90cf1bf8a0
commit
46b82f5737
@ -67,7 +67,7 @@ def pj_corpus_analysis_query(query):
|
|||||||
socketio.emit('pj_corpus_analysis_query', response, room=request.sid)
|
socketio.emit('pj_corpus_analysis_query', response, room=request.sid)
|
||||||
return
|
return
|
||||||
response = {'code': 200, 'desc': None, 'msg': 'OK',
|
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)
|
socketio.emit('pj_corpus_analysis_query', response, room=request.sid)
|
||||||
chunk_size = 100
|
chunk_size = 100
|
||||||
chunk_start = 0
|
chunk_start = 0
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import BooleanField, StringField, SubmitField, SelectField
|
from wtforms import BooleanField, StringField, SubmitField, SelectField
|
||||||
|
from wtforms.validators import DataRequired, Length
|
||||||
|
|
||||||
|
|
||||||
class PJQueryForm(FlaskForm):
|
class PJQueryForm(FlaskForm):
|
||||||
query = StringField('Query')
|
query = StringField('Query',
|
||||||
|
validators=[DataRequired(), Length(1, 1024)])
|
||||||
submit = SubmitField('Send query')
|
submit = SubmitField('Send query')
|
||||||
|
|
||||||
|
|
||||||
@ -33,3 +35,13 @@ class PJDisplayOptionsForm(FlaskForm):
|
|||||||
('80', '80'),
|
('80', '80'),
|
||||||
('90', '90'),
|
('90', '90'),
|
||||||
('100', '100')])
|
('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()])
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from flask import request, render_template
|
from flask import request, render_template
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
from . import corpora
|
from . import corpora
|
||||||
from .pj_forms import PJDisplayOptionsForm, PJQueryForm
|
from .pj_forms import PJDisplayOptionsForm, PJQueryForm, QueryDownloadForm
|
||||||
from .. import db
|
from .. import db
|
||||||
from ..models import Corpus
|
from ..models import Corpus
|
||||||
|
|
||||||
@ -19,7 +19,10 @@ def pj_analyse_corpus(corpus_id):
|
|||||||
results_per_page=request.args.get('results_per_page', 30))
|
results_per_page=request.args.get('results_per_page', 30))
|
||||||
query_form = PJQueryForm(prefix='query-form',
|
query_form = PJQueryForm(prefix='query-form',
|
||||||
query=request.args.get('query'))
|
query=request.args.get('query'))
|
||||||
|
query_download_form = QueryDownloadForm()
|
||||||
return render_template('corpora/pj_analyse_corpus.html.j2',
|
return render_template('corpora/pj_analyse_corpus.html.j2',
|
||||||
corpus_id=corpus_id,
|
corpus_id=corpus_id,
|
||||||
display_options_form=display_options_form,
|
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')
|
||||||
|
@ -4,34 +4,70 @@ class CorpusAnalysisClient {
|
|||||||
this.corpusId = corpusId;
|
this.corpusId = corpusId;
|
||||||
this.displays = {};
|
this.displays = {};
|
||||||
this.socket = socket;
|
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) => {
|
socket.on("pj_corpus_analysis_init", (response) => {
|
||||||
var errorText;
|
var errorText;
|
||||||
|
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
console.log(`pj_corpus_analysis_init: ${response.code} - ${response.msg}`);
|
console.log(`pj_corpus_analysis_init: ${response.code} - ${response.msg}`);
|
||||||
if (this.callbacks.init != undefined) {this.callbacks.init(response.payload);}
|
if (this.callbacks.init != undefined) {
|
||||||
if (this.displays.init != undefined) {this.displays.init.setVisibilityByStatus("success");}
|
this.callbacks.init(response.payload);
|
||||||
|
}
|
||||||
|
if (this.displays.init != undefined) {
|
||||||
|
this.displays.init.setVisibilityByStatus("success");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errorText = `Error ${response.code} - ${response.msg}`;
|
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.errorContainer != undefined) {
|
||||||
if (this.displays.init != undefined) {this.displays.init.setVisibilityByStatus("error");}
|
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}`);
|
console.error(`pj_corpus_analysis_init: ${errorText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// socket on event for recieveing query results
|
||||||
socket.on("pj_corpus_analysis_query", (response) => {
|
socket.on("pj_corpus_analysis_query", (response) => {
|
||||||
var errorText;
|
var errorText;
|
||||||
|
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
console.log(`pj_corpus_analysis_query: ${response.code} - ${response.msg}`);
|
console.log(`pj_corpus_analysis_query: ${response.code} - ${response.msg}`);
|
||||||
if (this.callbacks.query != undefined) {this.callbacks.query(response.payload);}
|
if (this.callbacks.query != undefined) {
|
||||||
if (this.displays.query != undefined) {this.displays.query.setVisibilityByStatus("success");}
|
this.callbacks.query(response.payload);
|
||||||
|
}
|
||||||
|
if (this.displays.query != undefined) {
|
||||||
|
this.displays.query.setVisibilityByStatus("success");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errorText = `Error ${response.payload.code} - ${response.payload.msg}`;
|
errorText = `Error ${response.payload.code} - ${response.payload.msg}`;
|
||||||
nopaque.flash("error", errorText);
|
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.errorContainer != undefined) {
|
||||||
if (this.displays.query != undefined) {this.displays.query.setVisibilityByStatus("error");}
|
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}`);
|
console.error(`pj_corpus_analysis_query: ${errorText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -42,14 +78,26 @@ class CorpusAnalysisClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
if (this.displays.init.errorContainer != undefined) {this.displays.init.errorContainer.innerHTML == "";}
|
if (this.displays.init.errorContainer != undefined) {
|
||||||
if (this.displays.init != undefined) {this.displays.init.setVisibilityByStatus("waiting");}
|
this.displays.init.errorContainer.innerHTML == "";
|
||||||
|
}
|
||||||
|
if (this.displays.init != undefined) {
|
||||||
|
this.displays.init.setVisibilityByStatus("waiting");
|
||||||
|
}
|
||||||
this.socket.emit("pj_corpus_analysis_init", this.corpusId);
|
this.socket.emit("pj_corpus_analysis_init", this.corpusId);
|
||||||
}
|
}
|
||||||
|
|
||||||
query(queryStr) {
|
sendQuery(queryStr) {
|
||||||
if (this.displays.query.errorContainer != undefined) {this.displays.query.errorContainer.innerHTML == "";}
|
let displayOptionsData;
|
||||||
if (this.displays.query != undefined) {this.displays.query.setVisibilityByStatus("waiting");}
|
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);
|
nopaque.socket.emit("pj_corpus_analysis_query", queryStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +110,18 @@ class CorpusAnalysisClient {
|
|||||||
return queryStr
|
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) {
|
setCallback(type, callback) {
|
||||||
// saves callback functions into an object. Key is function type, callback
|
// saves callback functions into an object. Key is function type, callback
|
||||||
// is the callback function
|
// is the callback function
|
||||||
@ -81,30 +141,56 @@ class CorpusAnalysisDisplay {
|
|||||||
this.showOnError = element.querySelectorAll(".show-on-error");
|
this.showOnError = element.querySelectorAll(".show-on-error");
|
||||||
this.showOnSuccess = element.querySelectorAll(".show-on-success");
|
this.showOnSuccess = element.querySelectorAll(".show-on-success");
|
||||||
this.showWhileWaiting = element.querySelectorAll(".show-while-waiting");
|
this.showWhileWaiting = element.querySelectorAll(".show-while-waiting");
|
||||||
|
this.hideOnComplete = element.querySelectorAll(".hide-on-complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisibilityByStatus(status) {
|
setVisibilityByStatus(status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "error":
|
case "error":
|
||||||
for (let element of this.showOnError) {element.classList.remove("hide");}
|
for (let element of this.showOnError) {
|
||||||
for (let element of this.showOnSuccess) {element.classList.add("hide");}
|
element.classList.remove("hide");
|
||||||
for (let element of this.showWhileWaiting) {element.classList.add("hide");}
|
}
|
||||||
|
for (let element of this.showOnSuccess) {
|
||||||
|
element.classList.add("hide");
|
||||||
|
}
|
||||||
|
for (let element of this.showWhileWaiting) {
|
||||||
|
element.classList.add("hide");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "success":
|
case "success":
|
||||||
for (let element of this.showOnError) {element.classList.add("hide");}
|
for (let element of this.showOnError) {
|
||||||
for (let element of this.showOnSuccess) {element.classList.remove("hide");}
|
element.classList.add("hide");
|
||||||
for (let element of this.showWhileWaiting) {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;
|
break;
|
||||||
case "waiting":
|
case "waiting":
|
||||||
for (let element of this.showOnError) {element.classList.add("hide");}
|
for (let element of this.showOnError) {
|
||||||
for (let element of this.showOnSuccess) {element.classList.add("hide");}
|
element.classList.add("hide");
|
||||||
for (let element of this.showWhileWaiting) {element.classList.remove("hide");}
|
}
|
||||||
|
for (let element of this.showOnSuccess) {
|
||||||
|
element.classList.add("hide");
|
||||||
|
}
|
||||||
|
for (let element of this.showWhileWaiting) {
|
||||||
|
element.classList.remove("hide");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Hide all
|
// Hide all
|
||||||
for (let element of this.showOnError) {element.classList.add("hide");}
|
for (let element of this.showOnError) {
|
||||||
for (let element of this.showOnSuccess) {element.classList.add("hide");}
|
element.classList.add("hide");
|
||||||
for (let element of this.showWhileWaiting) {element.classList.add("hide");}
|
}
|
||||||
|
for (let element of this.showOnSuccess) {
|
||||||
|
element.classList.add("hide");
|
||||||
|
}
|
||||||
|
for (let element of this.showWhileWaiting) {
|
||||||
|
element.classList.add("hide");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ function helperSendQuery(queryData) {
|
|||||||
outerWindow: 1
|
outerWindow: 1
|
||||||
}],
|
}],
|
||||||
valueNames: ["titles", "lc", "c", "rc", {data: ["index"]}],
|
valueNames: ["titles", "lc", "c", "rc", {data: ["index"]}],
|
||||||
item: `<span class="hidden"></span>`};
|
item: `<span></span>`};
|
||||||
resultList = new ResultList('result-list', resultListOptions);
|
resultList = new ResultList('result-list', resultListOptions);
|
||||||
resultList.clear(); // empty list for new query
|
resultList.clear(); // empty list for new query
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,55 @@
|
|||||||
function querySetup(payload) {
|
function querySetup(payload) {
|
||||||
// This is called when a query was successfull
|
// This is called when a query was successfull
|
||||||
console.log("Query setup.");
|
// some hiding
|
||||||
console.log(payload);
|
queryResultsExportElement.classList.add("disabled");
|
||||||
|
|
||||||
|
console.log("Query initial setup seccessfull.");
|
||||||
queryResultsDeterminateElement.style.width = "0%";
|
queryResultsDeterminateElement.style.width = "0%";
|
||||||
queryResultsProgressElement.classList.remove("hide");
|
queryResultsProgressElement.classList.remove("hide");
|
||||||
receivedMatchNumElement.innerText = "0";
|
queryResultsUserFeedbackElement.classList.remove("hide");
|
||||||
textLookupNumElement.innerText = "0";
|
receivedMatchCountElement.innerText = "0";
|
||||||
matchNumElement.innerText = payload.num_matches;
|
textLookupCountElement.innerText = "0";
|
||||||
|
matchCountElement.innerText = payload.match_count;
|
||||||
|
// always re initializes results to delete old results from it
|
||||||
results = {};
|
results = {};
|
||||||
results["matches"] = []; // list of all c with lc and rc
|
results["matches"] = []; // list of all c with lc and rc
|
||||||
results["cpos_lookup"] = {}; // object contains all cpos as key value pair
|
results["cpos_lookup"] = {}; // object contains all cpos as key value pair
|
||||||
results["text_lookup"] = {}; // same as above for all text ids
|
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) {
|
function queryRenderResults(payload) {
|
||||||
// This is called when results are transmitted.
|
// This is called when results are transmitted and being recieved
|
||||||
console.log("CHUNK:", payload.chunk);
|
console.log("Current recieved chunk:", payload.chunk);
|
||||||
console.log("RESULTS:", results);
|
// upate progress status
|
||||||
if (payload.progress === 100) {
|
if (payload.progress === 100) {
|
||||||
queryResultsProgressElement.classList.add("hide");
|
queryResultsProgressElement.classList.add("hide");
|
||||||
|
queryResultsUserFeedbackElement.classList.add("hide");
|
||||||
|
queryResultsExportElement.classList.remove("disabled");
|
||||||
|
activateInspect();
|
||||||
}
|
}
|
||||||
|
// update progress bar
|
||||||
queryResultsDeterminateElement.style.width = `${payload.progress}%`;
|
queryResultsDeterminateElement.style.width = `${payload.progress}%`;
|
||||||
results.matches.push(...payload.chunk.matches);
|
// building the result list js list from incoming chunk
|
||||||
receivedMatchNumElement.innerText = `${results.matches.length}`;
|
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
|
// 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.cpos_lookup, payload.chunk.cpos_lookup);
|
||||||
Object.assign(results.text_lookup, payload.chunk.text_lookup);
|
Object.assign(results.text_lookup, payload.chunk.text_lookup);
|
||||||
|
// show user current and total match count
|
||||||
textLookupNumElement.innerText = `${Object.keys(results.text_lookup).length}`;
|
receivedMatchCountElement.innerText = `${results.matches.length}`;
|
||||||
|
textLookupCountElement.innerText = `${Object.keys(results.text_lookup).length}`;
|
||||||
|
console.log("Results recieved:", results);
|
||||||
}
|
}
|
@ -149,7 +149,7 @@ class ResultList extends List {
|
|||||||
token = chunk["cpos_lookup"][cpos];
|
token = chunk["cpos_lookup"][cpos];
|
||||||
hitCellElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
|
hitCellElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
|
||||||
// get text titles of every hit cpos token
|
// 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
|
// add button to trigger more context to every match td
|
||||||
var inspectBtn = document.createElement("a");
|
var inspectBtn = document.createElement("a");
|
||||||
inspectBtn.setAttribute("class", "btn-floating btn-flat waves-effect waves-light grey right inspect disabled");
|
inspectBtn.setAttribute("class", "btn-floating btn-flat waves-effect waves-light grey right inspect disabled");
|
||||||
|
231
app/static/js/nopaque.pj_analyse_corpus.js
Normal file
231
app/static/js/nopaque.pj_analyse_corpus.js
Normal 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> </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
|
||||||
|
}
|
||||||
|
}
|
@ -78,20 +78,58 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- entire results div/card -->
|
||||||
<div class="col s12" id="query-display">
|
<div class="col s12" id="query-display">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content" id="result-list" style="overflow: hidden;">
|
||||||
<span class="card-title">Query Results</span>
|
<span class="card-title">Query Results</span>
|
||||||
<div class="error-container hide show-on-error"></div>
|
<div class="error-container hide show-on-error"></div>
|
||||||
<div class="hide show-on-success">
|
<div class="hide show-on-success">
|
||||||
|
<div class="col s12 m6 l6">
|
||||||
|
<div class="row">
|
||||||
<p>
|
<p>
|
||||||
<span id="received-match-num"></span> of <span id="match-num"></span> matches loaded.<br>
|
<span id="received-match-count">
|
||||||
Matches occured in <span id="text-lookup-num"></span> corpus files.
|
</span> of
|
||||||
|
<span id="match-count"></span>
|
||||||
|
matches loaded.
|
||||||
|
<br>
|
||||||
|
Matches occured in <span id="text-lookup-count"></span> corpus files.
|
||||||
</p>
|
</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="progress" id="query-results-progress">
|
||||||
<div class="determinate" id="query-results-determinate"></div>
|
<div class="determinate" id="query-results-determinate"></div>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -99,6 +137,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- Modals -->
|
<!-- Modals -->
|
||||||
|
<!-- Analysis init modal -->
|
||||||
<div class="modal no-autoinit" id="init-display">
|
<div class="modal no-autoinit" id="init-display">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<h4>Initializing your corpus analysis session...</h4>
|
<h4>Initializing your corpus analysis session...</h4>
|
||||||
@ -109,11 +148,60 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 src="{{ url_for('static', filename='js/nopaque.CorpusAnalysisClient.js') }}">
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ url_for('static', filename='js/nopaque.callbacks.js') }}">
|
<script src="{{ url_for('static', filename='js/nopaque.callbacks.js') }}">
|
||||||
</script>
|
</script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque.pj_analyse_corpus.js') }}">
|
||||||
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// ###### Defining global variables used in other functions ######
|
// ###### Defining global variables used in other functions ######
|
||||||
var results; // full JSON object holding match results
|
var results; // full JSON object holding match results
|
||||||
@ -121,32 +209,44 @@
|
|||||||
var collapsibleElements; // all collapsibles on site
|
var collapsibleElements; // all collapsibles on site
|
||||||
var queryResultsProgressElement; // Div element holding the progress bar
|
var queryResultsProgressElement; // Div element holding the progress bar
|
||||||
var queryResultsDeterminateElement; // The progress bar for recieved results
|
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 ######
|
// ###### 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
|
// get collapsibles to alter options of those
|
||||||
var collapsibleElements = document.querySelector('.collapsible.expandable');
|
var collapsibleElements = document.querySelector('.collapsible.expandable');
|
||||||
|
|
||||||
|
|
||||||
var client = undefined;
|
var client = undefined;
|
||||||
var initDisplay = undefined;
|
var initDisplay = undefined;
|
||||||
var queryDisplay = undefined;
|
var queryDisplay = undefined;
|
||||||
|
|
||||||
var initDisplayElement = document.getElementById("init-display");
|
var initDisplayElement = document.getElementById("init-display");
|
||||||
var queryDisplayElement = document.getElementById("query-display");
|
var queryDisplayElement = document.getElementById("query-display");
|
||||||
var queryFormElement = document.getElementById("query-form");
|
receivedMatchCountElement = document.getElementById("received-match-count");
|
||||||
var queryResultsDeterminateElement = document.getElementById("query-results-determinate");
|
matchCountElement = document.getElementById("match-count");
|
||||||
var queryResultsProgressElement = document.getElementById("query-results-progress");
|
textLookupCountElement = document.getElementById("text-lookup-count");
|
||||||
receivedMatchNumElement = document.getElementById("received-match-num");
|
|
||||||
matchNumElement = document.getElementById("match-num");
|
|
||||||
textLookupNumElement = document.getElementById("text-lookup-num");
|
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
//set accordion of collapsibles to false
|
//set accordion of collapsibles to false
|
||||||
M.Collapsible.init(collapsibleElements, {accordion: false});
|
M.Collapsible.init(collapsibleElements, {accordion: false});
|
||||||
|
// creates some models on DOMContentLoaded
|
||||||
var initModal = M.Modal.init(initDisplayElement, {dismissible: false});
|
exportModal = M.Modal.init(document.getElementById("query-results-download-modal"),
|
||||||
|
{"dismissible": true});
|
||||||
|
initModal = M.Modal.init(initDisplayElement, {dismissible: false});
|
||||||
// Init corpus analysis components
|
// Init corpus analysis components
|
||||||
initDisplay = new CorpusAnalysisDisplay(initDisplayElement);
|
initDisplay = new CorpusAnalysisDisplay(initDisplayElement);
|
||||||
queryDisplay = new CorpusAnalysisDisplay(queryDisplayElement);
|
queryDisplay = new CorpusAnalysisDisplay(queryDisplayElement);
|
||||||
@ -157,6 +257,7 @@
|
|||||||
client.setCallback("init", () => {
|
client.setCallback("init", () => {
|
||||||
initModal.close();
|
initModal.close();
|
||||||
});
|
});
|
||||||
|
client.setDisplay("query", queryResultsUserFeedbackElement);
|
||||||
client.setDisplay("query", queryDisplay);
|
client.setDisplay("query", queryDisplay);
|
||||||
client.setCallback("query", (payload) => {
|
client.setCallback("query", (payload) => {
|
||||||
querySetup(payload);
|
querySetup(payload);
|
||||||
@ -173,8 +274,19 @@
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// Get query string and send query to server
|
// Get query string and send query to server
|
||||||
let queryStr = client.getQueryStr(queryFormElement);
|
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>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user