Merge branch 'development' of gitlab.ub.uni-bielefeld.de:sfb1288inf/opaque into development

This commit is contained in:
Patrick Jentsch 2020-04-08 17:21:49 +02:00
commit 6bbe0129b4
4 changed files with 193 additions and 142 deletions

View File

@ -14,6 +14,7 @@ class Results {
class ResultsJSON {
// Sets empty object structure. Also usefull to delete old results.
// matchCount default is 0
init(matchCount = 0) {
this["matches"] = []; // list of all c with lc and rc
this["cpos_lookup"] = {}; // object contains all this key value pair
@ -40,14 +41,19 @@ class ResultsJSON {
let resultFilename;
// get and create metadata
today = new Date();
currentDate = today.getUTCFullYear() + '-' + (today.getUTCMonth() +1) + '-' + today.getUTCDate();
currentTime = today.getUTCHours() + ":" + today.getUTCMinutes() + ":" + today.getUTCSeconds();
currentDate = `${today.getUTCFullYear()}` +
`-${(today.getUTCMonth() + 1)}` +
`-${today.getUTCDate()}`;
currentTime = `${today.getUTCHours()}h` +
`${today.getUTCMinutes()}m` +
`${today.getUTCSeconds()}s`;
safeFilename = this.query.replace(/[^a-z0-9_-]/gi, "_");
resultFilename = "UTC-" + currentDate + "_" + currentTime + "_" + safeFilename;
resultFilename = `UTC-${currentDate}_${currentTime}_${safeFilename}`;
return resultFilename
}
// Function to download data as a Blob created from a string, should be multi purpose
// 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(downloadElem, dataStr, filename, type, filenameSlug) {
let file;
console.log("Start Download!");
@ -68,7 +74,8 @@ class ResultsJSON {
let dataStr;
let downloadElement;
// stringify JSON object for json download
dataStr = JSON.stringify(results.resultsJSON, undefined, "\t"); // use tabs to save some space
// use tabs to save some space
dataStr = JSON.stringify(results.resultsJSON, undefined, "\t");
// get downloadResultsElement
downloadElement = document.getElementById("download-results-json");
// start actual download

View File

@ -2,22 +2,22 @@ function querySetup(payload) {
// This is called when a query was successfull
// some hiding
queryResultsExportElement.classList.add("disabled");
console.log("Query initial setup seccessfull.");
queryResultsDeterminateElement.style.width = "0%";
queryResultsProgressElement.classList.remove("hide");
queryResultsUserFeedbackElement.classList.remove("hide");
// some initial values
receivedMatchCountElement.innerText = "0";
textLookupCountElement.innerText = "0";
matchCountElement.innerText = payload.match_count;
// always re initializes results to delete old results from it
// this has to be done here again because teh last chunk from old results was still being recieved
// this has to be done here again because the last chunk from old results was still being recieved
results.clear_all()
// Get query string again
results.resultsJSON.getQueryStr(queryFormElement);
}
function queryRenderResults(payload) {
let resultItems; // array of built html result items row element
// This is called when results are transmitted and being recieved
console.log("Current recieved chunk:", payload.chunk);
if (payload.chunk.cpos_ranges == true) {
@ -54,7 +54,7 @@ function queryRenderResults(payload) {
queryResultsProgressElement.classList.add("hide");
queryResultsUserFeedbackElement.classList.add("hide");
queryResultsExportElement.classList.remove("disabled");
ResultsList.activateInspect();
results.resultsList.activateInspect();
}
// inital expert mode check and activation
if (expertModeSwitchElement.checked) {

View File

@ -81,11 +81,16 @@ RessourceList.options = {
<i class="description"></i>
</td>
<td>
<span class="badge new status" data-badge-caption=""></span>
<span class="badge new status" data-badge-caption="">
</span>
</td>
<td class="right-align">
<a class="btn-small edit-link waves-effect waves-light"><i class="material-icons">edit</i></a>
<a class="btn-small analyse-link waves-effect waves-light">Analyse<i class="material-icons right">search</i></a>
<a class="btn-small edit-link waves-effect waves-light">
<i class="material-icons">edit</i>
</a>
<a class="btn-small analyse-link waves-effect waves-light">
Analyse<i class="material-icons right">search</i>
</a>
</td>
</tr>`,
valueNames: ["creation_date", "description", "title",
@ -107,7 +112,9 @@ RessourceList.options = {
<span class="badge new status" data-badge-caption=""></span>
</td>
<td class="right-align">
<a class="btn-small link waves-effect waves-light">View<i class="material-icons right">send</i></a>
<a class="btn-small link waves-effect waves-light">
View<i class="material-icons right">send</i>
</a>
</td>
</tr>`,
valueNames: ["creation_date", "description", "title",
@ -126,17 +133,20 @@ class ResultsList extends List {
let displayOptionsFormData
let displayOptionsData;
displayOptionsFormData = new FormData(displayOptionsFormElement);
displayOptionsData = {"resultsPerPage": displayOptionsFormData.get("display-options-form-results_per_page"),
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")};
"expertMode": displayOptionsFormData.get("display-options-form-expert_mode")
};
return displayOptionsData
}
// ###### Functions to inspect one match, to show more details ######
// activate inspect buttons if queryFinished is true
static activateInspect() {
if (progress === 100) {
// activate inspect buttons if progress is 100
activateInspect() {
let inspectBtnElements;
if (progress === 100) {
inspectBtnElements = document.getElementsByClassName("inspect");
for (let inspectBtn of inspectBtnElements) {
inspectBtn.classList.remove("disabled");
@ -148,10 +158,8 @@ class ResultsList extends List {
//gets result cpos infos for one dataIndex to send back to the server
inspect(dataIndex) {
// This function should be in the AnalysisClient class as a method.
console.log("Inspect!");
console.log(results.resultsJSON.matches[dataIndex].c);
let contextResultsElement = document.getElementById("context-results");
let contextResultsElement;
contextResultsElement = document.getElementById("context-results");
contextResultsElement.innerHTML = ""; // clear it from old inspects
contextModal.open();
nopaque.socket.emit("corpus_analysis_inspect_match",
@ -159,29 +167,30 @@ class ResultsList extends List {
first_cpos: results.resultsJSON.matches[dataIndex].c[0],
last_cpos: results.resultsJSON.matches[dataIndex].c[1]
}
});
}
);
}
showMatchContext(response) {
let contextData = response.payload
let contextResultsElement;
let c;
let contextData;
let contextModalLoading;
let contextModalReady;
let expertModeSwitchElement
let partElement
let contextResultsElement;
let expertModeSwitchElement;
let lc;
let partElement;
let rc;
let token;
let tokenElement;
let tokenElements;
let lc;
let c;
let rc;
console.log("###### match_context ######");
console.log("Incoming data:", contextData);
expertModeSwitchElement = document.getElementById("display-options-form-expert_mode");
contextData = response.payload;
contextResultsElement = document.getElementById("context-results");
expertModeSwitchElement = document.getElementById("display-options-form-expert_mode");
// check if cpos ranges are used or not
if (contextData.cpos_ranges == true) {
// python range like function from MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Sequence_generator_(range)
// python range like function from MDN
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Sequence_generator_(range)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
lc = range(contextData.match.lc[0], contextData.match.lc[1], 1)
c = range(contextData.match.c[0], contextData.match.c[1], 1)
@ -191,26 +200,29 @@ class ResultsList extends List {
c = contextData.match.c;
rc = contextData.match.rc;
}
// create sentence strings as tokens
partElement = document.createElement("p");
for (let cpos of lc) {
token = contextData["cpos_lookup"][cpos];
partElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
partElement.insertAdjacentHTML("beforeend",
`<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
contextResultsElement.append(partElement);
}
for (let cpos of c) {
token = contextData["cpos_lookup"][cpos];
partElement.insertAdjacentHTML("beforeend", `<span class="token bold light-green" data-cpos="${cpos}" style="text-decoration-line: underline;">${token["word"]} </span>`);
partElement.insertAdjacentHTML("beforeend",
`<span class="token bold light-green" data-cpos="${cpos}"` +
`style="text-decoration-line: underline;">${token["word"]} </span>`);
contextResultsElement.append(partElement);
}
for (let cpos of rc) {
token = contextData["cpos_lookup"][cpos];
partElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
partElement.insertAdjacentHTML("beforeend",
`<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
contextResultsElement.append(partElement);
}
if (expertModeSwitchElement.checked) {
tokenElements = partElement.getElementsByClassName("token");
console.log(this);
this.expertModeOn(tokenElements, contextData);
}
}
@ -225,31 +237,29 @@ class ResultsList extends List {
resultsList.update();
nopaque.flash("Updated matches per page.")
} catch (e) {
console.log("resultsList has no results right now. Live update of items per page is useless for now.");
console.log("resultsList has no results right now.");
}
}
// Event function triggered on context select change and also if pagination is clicked
// Event function triggered on context select change
// also if pagination is clicked
changeContext(event) {
let newContextValue;
let lc;
let rc;
let array;
let lc;
let newContextValue;
let rc;
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)) {
for (let element of array.reverse().slice(newContextValue)) {
element.classList.add("hide");
}
for (let element of array.slice(0, newContextValue)) {
@ -275,13 +285,10 @@ class ResultsList extends List {
// if checked than expertModeOn is executed
// if unchecked expertModeOff is executed
eventHandlerCheck(event) {
console.log("pagination used!");
console.log(expertModeSwitchElement.checked);
if (expertModeSwitchElement.checked) {
this.expertModeOn(event.currentTarget.tokenElements, resultsJSON);
} else if (!expertModeSwitchElement.checked) {
event.preventDefault();
console.log("prevented! Destroy");
this.expertModeOff(event.currentTarget.tokenElements);
}
}
@ -289,17 +296,13 @@ class ResultsList extends List {
// function to apply extra information and animation to every token
expertModeOn(tokenElements, results) {
let token;
console.log("expertModeOn!");
for (let tokenElement of tokenElements) {
tokenElement.classList.add("chip");
tokenElement.classList.add("hoverable");
tokenElement.classList.add("expert-view");
token = results["cpos_lookup"][tokenElement.dataset.cpos];
token = results.cpos_lookup[tokenElement.dataset.cpos];
tokenElement.addEventListener("mouseover", (event) => {
console.log("Mouseover!");
console.log(event.target);
token = results["cpos_lookup"][event.target.dataset.cpos];
token = results.cpos_lookup[event.target.dataset.cpos];
this.addToolTipToTokenElement(event.target, token);
});
}
@ -316,19 +319,22 @@ class ResultsList extends List {
</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"]}
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: ${resultsJSON["text_lookup"][token["text"]]["title"]}<br>
Author: ${resultsJSON["text_lookup"][token["text"]]["author"]}<br>
Publishing year: ${resultsJSON["text_lookup"][token["text"]]["publishing_year"]}
Title: ${resultsJSON.text_lookup[token.text].title}
<br>
Author: ${resultsJSON.text_lookup[token.text].author}
<br>
Publishing year: ${resultsJSON.text_lookup[token.text].publishing_year}
</td>
</tr>
</table>`});
</table>`}
);
}
// function to remove extra informations and animations from tokens
@ -343,11 +349,25 @@ class ResultsList extends List {
}
createResultRowElement(item, chunk) {
let values, cpos, token, matchRowElement, lcCellElement, hitCellElement, rcCellElement, textTitlesCellElement, matchNrElement, lc, c, rc;
let c;
let cCellElement;
let cpos;
let inspectBtn
let lc;
let lcCellElement;
let matchNrElement;
let matchRowElement;
let rc;
let rcCellElement;
let textTitles;
let textTitlesCellElement;
let token;
let values;
// gather values from item
values = item.values();
if (chunk.cpos_ranges == true) {
// python range like function from MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Sequence_generator_(range)
// python range like function from MDN
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Sequence_generator_(range)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
lc = range(values.lc[0], values.lc[1], 1)
c = range(values.c[0], values.c[1], 1)
@ -364,35 +384,39 @@ class ResultsList extends List {
lcCellElement.classList.add("left-context");
matchRowElement.appendChild(lcCellElement);
for (cpos of lc) {
token = chunk["cpos_lookup"][cpos];
lcCellElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
token = chunk.cpos_lookup[cpos];
lcCellElement.insertAdjacentHTML("beforeend",
`<span class="token" data-cpos="${cpos}">${token.word} </span>`);
}
// get infos for hit of match
let textTitles = new Set();
hitCellElement = document.createElement("td");
hitCellElement.classList.add("match-hit");
textTitles = new Set();
cCellElement = document.createElement("td");
cCellElement.classList.add("match-hit");
textTitlesCellElement = document.createElement("td");
textTitlesCellElement.classList.add("titles");
matchNrElement = document.createElement("td");
matchNrElement.classList.add("match-nr");
matchRowElement.appendChild(hitCellElement);
matchRowElement.appendChild(cCellElement);
for (cpos of c) {
token = chunk["cpos_lookup"][cpos];
hitCellElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
token = chunk.cpos_lookup[cpos];
cCellElement.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].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");
inspectBtn = document.createElement("a");
inspectBtn.setAttribute("class", `btn-floating btn-flat waves-effect` +
`waves-light grey right inspect disabled`
);
inspectBtn.innerHTML = '<i class="material-icons">search</i>';
inspectBtn.onclick = () => {this.inspect(values["index"])};
inspectBtn.onclick = () => {this.inspect(values.index)};
}
// add text titles at front as first td of one row
hitCellElement.appendChild(inspectBtn);
cCellElement.appendChild(inspectBtn);
textTitlesCellElement.innerText = [...textTitles].join(", ");
matchRowElement.insertAdjacentHTML("afterbegin", textTitlesCellElement.outerHTML);
matchNrElement.innerText = values["index"] + 1;
matchNrElement.innerText = values.index + 1;
matchRowElement.insertAdjacentHTML("afterbegin", matchNrElement.outerHTML);
// get infos for right context of match
@ -401,7 +425,8 @@ class ResultsList extends List {
matchRowElement.appendChild(rcCellElement);
for (cpos of rc) {
token = chunk["cpos_lookup"][cpos];
rcCellElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
rcCellElement.insertAdjacentHTML("beforeend",
`<span class="token" data-cpos="${cpos}">${token.word} </span>`);
}
return matchRowElement
}

View File

@ -218,35 +218,59 @@
</script>
<script>
// ###### Defining global variables used in other functions ######
var client; // CorpusAnalysisClient first undefined on DOMContentLoaded defined
var collapsibleElements; // All collapsibleElements on this page
var collapsibleElements; // all collapsibles on site
var contextModal; // Modal to open on inspect for further match context
var displayOptionsData; // Getting form data from display options
var displayOptionsFormElement; // Form holding the display informations
var downloadResultsJSONElement; // button for downloading results as JSON
var expertModeSwitchElement; // Expert mode switch Element
var exportModal; // Download options modal
var firstPageElement; // first page element of resultsList pagination
var initDisplay; // CorpusAnalysisDisplay object first undfined on DOMContentLoaded defined
var initDisplayElement; // Element for initialization using initDisplay
var initModal;
var matchCountElement; // Total nr. of matches will be displayed in this element
var progress; // global progress value
var queryDisplay; // CorpusAnalysisDisplay object first undfined on DOMContentLoaded defined
var queryDisplayElement; // Element for initialization using queryDisplay
var queryFormElement; // the query form
var queryResultsDeterminateElement; // The progress bar for recieved results
var queryResultsExportElement; // Download button opens download modal
var queryResultsProgressElement; // Div element holding the progress bar
var queryResultsUserFeedbackElement; // Element showing match count|total etc
var receivedMatchCountElement; // Nr. of loaded matches will be displayed in this element
var results; // results object
var resultsJSON; // full JSON object holding match results
var resultsList; // resultsList object
var queryFormElement; // the query form
var collapsibleElements; // all collapsibles on site
var progress; // global progress value
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; // Download options modal
var contextModal; // Modal to open on inspect for further match context
var initModal;
var downloadResultsJSONElement; // button for downloading results as JSON
var displayOptionsFormElement;
var resultsListOptions; // specifies ResultsList options
var textLookupCountElement // Nr of texts the matches occured in will be shown in this element
var xpath; // xpath to grab first resultsList page pagination element
// ###### Initialize variables ######
client = undefined;
collapsibleElements = document.querySelector('.collapsible.expandable');
contextModal = document.getElementById("context-modal");
exportModal = document.getElementById("query-results-download-modal");
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");
expertModeSwitchElement = document.getElementById("display-options-form-expert_mode");
exportModal = document.getElementById("query-results-download-modal");
initDisplay = undefined;
initDisplayElement = document.getElementById("init-display");
matchCountElement = document.getElementById("match-count");
queryDisplay = undefined;
queryDisplayElement = document.getElementById("query-display");
queryFormElement = document.getElementById("query-form");
queryResultsDeterminateElement = document.getElementById("query-results-determinate");
queryResultsExportElement = document.getElementById("query-results-export");
queryResultsProgressElement = document.getElementById("query-results-progress");
queryResultsUserFeedbackElement = document.getElementById("query-results-user-feedback");
receivedMatchCountElement = document.getElementById("received-match-count");
textLookupCountElement = document.getElementById("text-lookup-count");
// js list options and intialization
let displayOptionsData = ResultsList.getDisplayOptions(displayOptionsFormElement);
let resultsListOptions = {page: displayOptionsData["resultsPerPage"],
// ###### js list options and intialization ######
displayOptionsData = ResultsList.getDisplayOptions(displayOptionsFormElement);
resultsListOptions = {page: displayOptionsData["resultsPerPage"],
pagination: [{
name: "paginationTop",
paginationClass: "paginationTop",
@ -260,23 +284,8 @@
valueNames: ["titles", "lc", "c", "rc", {data: ["index"]}],
item: `<span></span>`};
// ###### 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");
receivedMatchCountElement = document.getElementById("received-match-count");
matchCountElement = document.getElementById("match-count");
textLookupCountElement = document.getElementById("text-lookup-count");
var expertModeSwitchElement = document.getElementById("display-options-form-expert_mode");
// ###### event on DOMContentLoaded ######
document.addEventListener("DOMContentLoaded", () => {
//set accordion of collapsibles to false
M.Collapsible.init(collapsibleElements, {accordion: false});
@ -311,6 +320,19 @@
client.init();
// start a query request on submit
queryFormElement.addEventListener("submit", (event) => {
try {
// Selects first page of result list if pagination is already available
// from an query submitted before.
// This avoids confusion for the user eg: The user was on page 24
// reviewing the results and issues a new query. He would not see any
// results until the new results reach page 24 or he clicks on another
// valid result page element from the new pagination.
firstPageElement;
xpath = '//a[@class="page" and text()=1]';
firstPageElement = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
firstPageElement.click();
} catch (e) {
}
// Prevent page from reloading on submit
event.preventDefault();
// empty ResultsList and ResultsJSON for new query
@ -336,7 +358,7 @@
paginationElements = document.getElementsByClassName("pagination");
for (element of paginationElements) {
element.addEventListener("click", results.resultsList.changeContext);
element.addEventListener("click", ResultsList.activateInspect);
element.addEventListener("click", results.resultsList.activateInspect);
}
// epxert mode table view
@ -344,16 +366,13 @@
let currentTokenElements = document.getElementsByClassName("token");
let paginationElements = document.getElementsByClassName("pagination");
if (event.target.checked) {
console.log("Checked!");
results.resultsList.expertModeOn(currentTokenElements, resultsJSON);
for (element of paginationElements) {
element.tokenElements = currentTokenElements;
element.addEventListener("click", (event) => { results.resultsList.eventHandlerCheck(event)});
}
} else {
console.log("Unchecked!");
results.resultsList.expertModeOff(currentTokenElements);
console.log("unchecked! Destroy");
}
});
});