This commit is contained in:
Stephan Porada 2020-04-02 14:22:03 +02:00
parent bface71503
commit 2c93f02c11
9 changed files with 144 additions and 76 deletions

View File

@ -95,6 +95,24 @@ def pj_corpus_analysis_query(query):
client.status = 'ready' client.status = 'ready'
@socketio.on('pj_inspect-match')
@socketio_login_required
def pj_corpus_analysis_query(payload):
logger.warning(payload)
client = pj_corpus_analysis_clients.get(request.sid)
if client is None:
socketio.emit('query', '[424]: Failed Dependency',
room=request.sid)
return
# Get more context for given match CPOS
corpus = client.corpora.get('CORPUS')
s = corpus.attributes.structural.get('s')
match_context = s.export(payload["cpos"][0], payload["cpos"][1],
context=3,
expand_lists=True)
socketio.emit('pj_match_context', {"payload": match_context}, room=request.sid)
def pj_corpus_analysis_session_handler(app, corpus_id, user_id, session_id): def pj_corpus_analysis_session_handler(app, corpus_id, user_id, session_id):
with app.app_context(): with app.app_context():
''' Setup analysis session ''' ''' Setup analysis session '''

View File

@ -4,24 +4,6 @@ class CorpusAnalysisClient {
this.corpusId = corpusId; this.corpusId = corpusId;
this.displays = {}; this.displays = {};
this.socket = socket; this.socket = socket;
this.resultList = {}; // why do you save it here? we have a global variable results
// js list options and intialization
let displayOptionsData = this.getDisplayOptions(displayOptionsFormElement);
let resultListOptions = {page: displayOptionsData["resultsPerPage"],
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 event fpr corpous analysis initialization
socket.on("pj_corpus_analysis_init", (response) => { socket.on("pj_corpus_analysis_init", (response) => {
@ -97,31 +79,9 @@ class CorpusAnalysisClient {
if (this.displays.query != undefined) { if (this.displays.query != undefined) {
this.displays.query.setVisibilityByStatus("waiting"); 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);
} }
getQueryStr(queryFormElement) {
// gets query
let queryFormData;
let queryStr;
queryFormData = new FormData(queryFormElement);
queryStr = queryFormData.get("query-form-query");
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

View File

@ -0,0 +1,8 @@
class Results {
constructor(results, resultsList) {
this.resultsJSON = results;
this.resultsList = resultsList;
}
}

View File

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

View File

@ -11,12 +11,11 @@ function querySetup(payload) {
textLookupCountElement.innerText = "0"; textLookupCountElement.innerText = "0";
matchCountElement.innerText = payload.match_count; matchCountElement.innerText = payload.match_count;
// always re initializes results to delete old results from it // always re initializes results to delete old results from it
results = {}; results.resultsJSON["matches"] = []; // list of all c with lc and rc
results["matches"] = []; // list of all c with lc and rc results.resultsJSON["cpos_lookup"] = {}; // object contains all cpos as key value pair
results["cpos_lookup"] = {}; // object contains all cpos as key value pair results.resultsJSON["text_lookup"] = {}; // same as above for all text ids
results["text_lookup"] = {}; // same as above for all text ids results.resultsJSON["match_count"] = payload.match_count;
results["match_count"] = payload.match_count; results.resultsJSON["query"] = getQueryStr(queryFormElement);
results["query"] = client.getQueryStr(queryFormElement);
} }
function queryRenderResults(payload) { function queryRenderResults(payload) {
@ -29,27 +28,32 @@ function queryRenderResults(payload) {
queryResultsExportElement.classList.remove("disabled"); queryResultsExportElement.classList.remove("disabled");
activateInspect(); activateInspect();
} }
if (payload.chunk.cpos_ranges == true) {
results.resultsJSON["cpos_ranges"] = true;
} else {
results.resultsJSON["cpos_ranges"] = false;
}
// update progress bar // update progress bar
queryResultsDeterminateElement.style.width = `${payload.progress}%`; queryResultsDeterminateElement.style.width = `${payload.progress}%`;
// building the result list js list from incoming chunk // building the result list js list from incoming chunk
resultItems = []; // list for holding every row item resultItems = []; // list for holding every row item
// get infos for full match row // get infos for full match row
for (let [index, match] of payload.chunk.matches.entries()) { for (let [index, match] of payload.chunk.matches.entries()) {
resultItems.push({...match, ...{"index": index + results.matches.length}}); resultItems.push({...match, ...{"index": index + results.resultsJSON.matches.length}});
} }
client.resultList.add(resultItems, (items) => { resultsList.add(resultItems, (items) => {
for (let item of items) { for (let item of items) {
item.elm = client.resultList.createResultRowElement(item, payload.chunk); item.elm = resultsList.createResultRowElement(item, payload.chunk);
} }
client.resultList.update(); resultsList.update();
changeContext(); // sets lr context on first result load 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); results.resultsJSON.matches.push(...payload.chunk.matches);
Object.assign(results.cpos_lookup, payload.chunk.cpos_lookup); Object.assign(results.resultsJSON.cpos_lookup, payload.chunk.cpos_lookup);
Object.assign(results.text_lookup, payload.chunk.text_lookup); Object.assign(results.resultsJSON.text_lookup, payload.chunk.text_lookup);
// show user current and total match count // show user current and total match count
receivedMatchCountElement.innerText = `${results.matches.length}`; receivedMatchCountElement.innerText = `${results.resultsJSON.matches.length}`;
textLookupCountElement.innerText = `${Object.keys(results.text_lookup).length}`; textLookupCountElement.innerText = `${Object.keys(results.resultsJSON.text_lookup).length}`;
console.log("Results recieved:", results); console.log("Results recieved:", results.resultsJSON);
} }

View File

@ -118,7 +118,7 @@ RessourceList.options = {
}; };
class ResultList extends List { class ResultsList extends List {
createResultRowElement(item, chunk) { createResultRowElement(item, chunk) {
let values, cpos, token, matchRowElement, lcCellElement, hitCellElement, rcCellElement, textTitlesCellElement, matchNrElement, lc, c, rc; let values, cpos, token, matchRowElement, lcCellElement, hitCellElement, rcCellElement, textTitlesCellElement, matchNrElement, lc, c, rc;

View File

@ -1,3 +1,28 @@
// ###### Helper functions ######
// get query as string from form Element
function getQueryStr(queryFormElement) {
// gets query
let queryFormData;
let queryStr;
queryFormData = new FormData(queryFormElement);
queryStr = queryFormData.get("query-form-query");
return queryStr
}
// get display options from display options form element
function 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
}
// ###### Download results functions ###### // ###### Download results functions ######
// TODO: Maybe write these as class functions? For this maybe create a result class // TODO: Maybe write these as class functions? For this maybe create a result class
@ -13,7 +38,7 @@ function createDownloadFilename() {
today = new Date(); today = new Date();
currentDate = today.getUTCFullYear() + '-' + (today.getUTCMonth() +1) + '-' + today.getUTCDate(); currentDate = today.getUTCFullYear() + '-' + (today.getUTCMonth() +1) + '-' + today.getUTCDate();
currentTime = today.getUTCHours() + ":" + today.getUTCMinutes() + ":" + today.getUTCSeconds(); currentTime = today.getUTCHours() + ":" + today.getUTCMinutes() + ":" + today.getUTCSeconds();
safeFilename = results["query"].replace(/[^a-z0-9_-]/gi, "_"); safeFilename = results.resultsJSON["query"].replace(/[^a-z0-9_-]/gi, "_");
resultFilename = "UTC-" + currentDate + "_" + currentTime + "_" + safeFilename; resultFilename = "UTC-" + currentDate + "_" + currentTime + "_" + safeFilename;
return resultFilename return resultFilename
} }
@ -23,7 +48,7 @@ function downloadJSONRessource(resultFilename) {
let dataStr; let dataStr;
let downloadElement; let downloadElement;
// stringify JSON object for json download // stringify JSON object for json download
dataStr = JSON.stringify(results, undefined, "\t"); // use tabs to save some space dataStr = JSON.stringify(results.resultsJSON, undefined, "\t"); // use tabs to save some space
// get downloadResultsElement // get downloadResultsElement
downloadElement = document.getElementById("download-results-json"); downloadElement = document.getElementById("download-results-json");
// start actual download // start actual download
@ -57,32 +82,33 @@ function activateInspect(progress) {
} }
//gets result cpos infos for one dataIndex to send back to the server //gets result cpos infos for one dataIndex to send back to the server
function inspect(dataIndex) { function inspect(dataIndex, socket) {
console.log("Inspect!"); console.log("Inspect!");
console.log(result["matches"][dataIndex]["c"]); console.log(results.resultsJSON.matches[dataIndex].c);
contextModal.open(); contextModal.open();
nopaque.socket.emit("inspect_match", {"cpos": result["matches"][dataIndex]["c"]}); socket.emit("pj_inspect_match",
{"payload": {"cpos": results.resultsJSON.matches[dataIndex].c} });
} }
function showMatchContext(message) { function showMatchContext(payload) {
let contextResultsElement; let contextResultsElement;
let sentenceElement let sentenceElement
let token; let token;
let tokenElement; let tokenElement;
console.log("###### match_context ######"); console.log("###### match_context ######");
console.log("Incoming data:", message); console.log("Incoming data:", payload);
contextResultsElement = document.getElementById("context-results"); contextResultsElement = document.getElementById("context-results");
contextResultsElement.innerHTML = "<p>&nbsp;</p>"; contextResultsElement.innerHTML = "<p>&nbsp;</p>";
document.getElementById("context-modal-loading").classList.add("hide"); document.getElementById("context-modal-loading").classList.add("hide");
document.getElementById("context-modal-ready").classList.remove("hide"); document.getElementById("context-modal-ready").classList.remove("hide");
for (let [key, value] of Object.entries(message['context_s_cpos'])) { for (let [key, value] of Object.entries(payload['context_s_cpos'])) {
sentenceElement = document.createElement("p"); sentenceElement = document.createElement("p");
for (let cpos of value) { for (let cpos of value) {
token = message["cpos_lookup"][cpos]; token = payload["cpos_lookup"][cpos];
tokenElement = document.createElement("span"); tokenElement = document.createElement("span");
tokenElement.classList.add("token"); tokenElement.classList.add("token");
if (message["match_cpos_list"].includes(cpos)) { if (payload["match_cpos_list"].includes(cpos)) {
tokenElement.classList.add("bold"); tokenElement.classList.add("bold");
tokenElement.classList.add("light-green"); tokenElement.classList.add("light-green");
} }
@ -90,7 +116,7 @@ function showMatchContext(message) {
tokenElement.innerText = token["word"]; tokenElement.innerText = token["word"];
var expertModeSwitchElement = document.getElementById("expert-mode-switch"); var expertModeSwitchElement = document.getElementById("expert-mode-switch");
if (expertModeSwitchElement.checked) { if (expertModeSwitchElement.checked) {
expertModeOn([tokenElement], message); expertModeOn([tokenElement], payload);
} }
sentenceElement.append(tokenElement); sentenceElement.append(tokenElement);
sentenceElement.append(document.createTextNode(" ")); sentenceElement.append(document.createTextNode(" "));

View File

@ -238,7 +238,7 @@ server side -->
var exportModal; var exportModal;
// create some global variables // create some global variables
var result; // will gradually be filled with JSON result chunks of a query var result; // will gradually be filled with JSON result chunks of a query
var resultList; // the list.js new ResultList object displaying the results var resultsList; // the list.js new ResultList object displaying the results
var queryFinished; // bool flag that shows if query is finished or not var queryFinished; // bool flag that shows if query is finished or not
var analysisClientStatus; // Object holding: {<sid>: 'running'|'idle'} var analysisClientStatus; // Object holding: {<sid>: 'running'|'idle'}
var sessionId; // the clients current session id var sessionId; // the clients current session id

View File

@ -195,28 +195,54 @@
</div> </div>
</div> </div>
<!-- Context modal used for detailed information about one match -->
<div id="context-modal" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Match context and information</h4>
<div id="context-modal-loading">
<div class="progress">
<div class="indeterminate"></div>
</div>
</div>
<div id="context-modal-ready" class="hide">
<div id="context-results"></div>
</div>
</div>
<div class="modal-footer">
<a href="#!" class="left waves-effect waves-light btn">Export</a>
<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.Results.js') }}">
</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 src="{{ url_for('static', filename='js/nopaque.pj_analyse_corpus.js') }}">
</script> </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; // results object
var resultsJSON; // full JSON object holding match results
var resultsList; // resultsList object
var queryFormElement; // the query form var queryFormElement; // the query form
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 queryResultsUserFeedbackElement; // Element showing match count|total etc
var queryResultsExportElement; // Download button opens download modal var queryResultsExportElement; // Download button opens download modal
var exportModal; var exportModal; // Download options modal
var contextModal; // Modal to open on inspect for further match context
var initModal; var initModal;
var downloadResultsJSONElement; // button for downloading results as JSON var downloadResultsJSONElement; // button for downloading results as JSON
var displayOptionsFormElement; var displayOptionsFormElement;
// ###### Initialize variables ###### // ###### Initialize variables ######
contextModal = document.getElementById("context-modal");
exportModal = document.getElementById("query-results-download-modal");
queryFormElement = document.getElementById("query-form"); queryFormElement = document.getElementById("query-form");
queryResultsProgressElement = document.getElementById("query-results-progress"); queryResultsProgressElement = document.getElementById("query-results-progress");
queryResultsDeterminateElement = document.getElementById("query-results-determinate"); queryResultsDeterminateElement = document.getElementById("query-results-determinate");
@ -224,6 +250,22 @@
queryResultsExportElement = document.getElementById("query-results-export"); queryResultsExportElement = document.getElementById("query-results-export");
displayOptionsFormElement = document.getElementById("display-options-form"); displayOptionsFormElement = document.getElementById("display-options-form");
// js list options and intialization
let displayOptionsData = getDisplayOptions(displayOptionsFormElement);
let resultsListOptions = {page: displayOptionsData["resultsPerPage"],
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>`};
// ###### Set some css options ###### // ###### 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');
@ -244,14 +286,18 @@
//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 // creates some models on DOMContentLoaded
exportModal = M.Modal.init(document.getElementById("query-results-download-modal"), contextModal = M.Modal.init(contextModal, {"dismissible": true});
{"dismissible": true}); exportModal = M.Modal.init(exportModal, {"dismissible": true});
initModal = M.Modal.init(initDisplayElement, {dismissible: false}); initModal = M.Modal.init(initDisplayElement, {"dismissible": false});
// Init corpus analysis components // Init corpus analysis components
resultsJSON = {};
resultsList = new ResultsList("result-list", resultsListOptions);
results = new Results(resultsJSON, resultsList);
initDisplay = new CorpusAnalysisDisplay(initDisplayElement); initDisplay = new CorpusAnalysisDisplay(initDisplayElement);
queryDisplay = new CorpusAnalysisDisplay(queryDisplayElement); queryDisplay = new CorpusAnalysisDisplay(queryDisplayElement);
client = new CorpusAnalysisClient({{ corpus_id }}, nopaque.socket); client = new CorpusAnalysisClient({{ corpus_id }}, nopaque.socket);
initModal.open(); initModal.open();
// set displays visibilitys and Callback functions // set displays visibilitys and Callback functions
client.setDisplay("init", initDisplay); client.setDisplay("init", initDisplay);
client.setCallback("init", () => { client.setCallback("init", () => {
@ -272,12 +318,18 @@
queryFormElement.addEventListener("submit", (event) => { queryFormElement.addEventListener("submit", (event) => {
// Prevent page from reloading on submit // Prevent page from reloading on submit
event.preventDefault(); event.preventDefault();
// empty list for new query
resultsList.clear();
// Get query string and send query to server // Get query string and send query to server
let queryStr = client.getQueryStr(queryFormElement); let queryStr = getQueryStr(queryFormElement);
client.sendQuery(queryStr); client.sendQuery(queryStr);
}); });
// get context of one match if inspected via socket.io
nopaque.socket.on("pj_match_context", showMatchContext);
}); });
// Add onclick to open download modal wen Export Results button is pressed // Add onclick to open download modal wen Export Results button is pressed
queryResultsExportElement.onclick = function() { queryResultsExportElement.onclick = function() {
exportModal.open(); exportModal.open();