Add rudimentary rework of corpus analysis

This commit is contained in:
Stephan Porada 2020-08-10 14:48:45 +02:00
parent 25ea9ba583
commit f94d21fa96
7 changed files with 163 additions and 92 deletions

View File

@ -20,7 +20,7 @@ class CorpusAnalysisClient {
} }
} }
loadListeners() { loadSocketEventListeners() {
for (let [type, listener] of Object.entries(this.socketEventListeners)) { for (let [type, listener] of Object.entries(this.socketEventListeners)) {
listener(this); listener(this);
} }
@ -31,16 +31,6 @@ class CorpusAnalysisClient {
this.displays[type] = corpusAnalysisDisplay; this.displays[type] = corpusAnalysisDisplay;
} }
// /**
// * Initializes the interactive corpus analysis session via socket.io.
// * This function uses helper functions.
// */
// initSession() {
// let request = this.requestSession();
// let recvieveSession = this.recvieveSession();
// console.info('corpus_analysis_init: Client waiting for response'); // this happens inbetween the two functions above
// }
/** /**
* Requests a corpus analysis session via socket.io. * Requests a corpus analysis session via socket.io.
* Opens a loading modal at the start of the request * Opens a loading modal at the start of the request
@ -56,17 +46,6 @@ class CorpusAnalysisClient {
this.socket.emit('corpus_analysis_init', this.corpusId); this.socket.emit('corpus_analysis_init', this.corpusId);
} }
// /**
// * Sends a query to the server and handles the response to that query.
// * This function uses helper functions.
// */
// query(queryStr) {
// let requestQueryData = this.requestQueryData(queryStr);
// let recieveQueryProcessStatus = this.recieveQueryProcessStatus();
// let recieveQueryData = this.recieveQueryData();
// console.info('corpus_analysis_query: Client waiting for query data'); // this happens inbetween the two functions above
// }
/** /**
* Sends the query string to the server. * Sends the query string to the server.
* Should be a private method if ES2020 is finalized (Maybe?) * Should be a private method if ES2020 is finalized (Maybe?)
@ -165,5 +144,5 @@ class SocketEventListener {
} }
} }
// export both Classes from this module // export Classes from this module
export {CorpusAnalysisClient, CorpusAnalysisDisplay, SocketEventListener}; export {CorpusAnalysisClient, CorpusAnalysisDisplay, SocketEventListener};

View File

@ -0,0 +1,84 @@
/**
* This callback is called when the SocketEventListener 'recieveQueryData'
* has been triggered. It takes the incoming chunk and renders the results in a
* the results.jsList. It can eiterh hande llive incoming data or imported
* results data.
*/
function queryRenderResults(payload, client, imported=false) {
console.info("Current recieved chunk:", payload.chunk);
if (payload.chunk.cpos_ranges == true) {
client.results.data["cpos_ranges"] = true;
} else {
client.results.data["cpos_ranges"] = false;
}
/**
* resultItem is a list where
*/
let resultItems = [];
// get infos for full match row
for (let [index, match] of payload.chunk.matches.entries()) {
resultItems.push({...match, ...{"index": index + client.results.data.matches.length}});
}
if (!imported) {
// update progress bar
// queryResultsDeterminateElement.style.width = `${payload.progress}%`;
client.results.jsList.add(resultItems, (items) => {
for (let item of items) {
item.elm = client.results.jsList.createResultRowElement(item, payload.chunk);
}
});
helperQueryRenderResults(payload, client);
// if (progress === 100) {
// resultsCreateElement.classList.remove("disabled");
// queryResultsProgressElement.classList.add("hide");
// queryResultsUserFeedbackElement.classList.add("hide");
// resultsExportElement.classList.remove("disabled");
// addToSubResultsElement.removeAttribute("disabled");
// // inital expert mode check and sub results activation
// client.results.jsList.activateInspect();
// if (addToSubResultsElement.checked) {
// client.results.jsList.activateAddToSubResults();
// }
// if (expertModeSwitchElement.checked) {
// client.results.jsList.expertModeOn("query-display");
// }
// }
} else if (imported) {
client.results.jsList.add(resultItems, (items) => {
for (let item of items) {
item.elm = client.results.jsList.createResultRowElement(item, payload.chunk,
true);
}
helperQueryRenderResults(payload, client);
progress = 100;
client.results.jsList.activateInspect();
if (expertModeSwitchElement.checked) {
client.results.jsList.expertModeOn("query-display");
}
});
}
}
function helperQueryRenderResults (payload, client) {
// updating table on finished item creation callback via createResultRowElement
client.results.jsList.update();
client.results.jsList.changeContext(); // sets lr context on first result load
// incorporating new chunk results into full results
client.results.data.matches.push(...payload.chunk.matches);
client.results.data.addData(payload.chunk.cpos_lookup, "cpos_lookup");
client.results.data.addData(payload.chunk.text_lookup, "text_lookup");
// complete metaData
// client.results.metaData.add();
// show user current and total match count
// receivedMatchCountElement.innerText = `${client.results.data.matches.length}`;
// textLookupCountElement.innerText = `${Object.keys(client.results.data.text_lookup).length}`;
let titles = new Array();
for (let [key, value] of Object.entries(client.results.data.text_lookup)) {
titles.push(`${value.title} (${value.publishing_year})`);
};
// textTitlesElement.innerText = `${titles.join(", ")}`;
// upate progress status
// progress = payload.progress; // global declaration
}
export { queryRenderResults }

View File

@ -1,3 +1,5 @@
import {queryRenderResults} from './nopaque.listenerCallbacks.js'
/** /**
* Recieves a corpus analysis session via socket.io. * Recieves a corpus analysis session via socket.io.
* Closes the loading modal that has been opend with requestSession at the * Closes the loading modal that has been opend with requestSession at the
@ -34,11 +36,13 @@ function recieveSession(client) {
* Recieves the query process status before any actual results are being * Recieves the query process status before any actual results are being
* transmitted. So it recieves error codes if a query failed or * transmitted. So it recieves error codes if a query failed or
* was invalid etc. * was invalid etc.
* Also prepares the result.jsList for the incoming data.
*/ */
function recieveQueryStatus(client) { function recieveQueryStatus(client) {
client.socket.on('corpus_analysis_query', (response) => { client.socket.on('corpus_analysis_query', (response) => {
console.group('corpus_analysis_query: Client recieving query process', console.group('corpus_analysis_query: Client recieving query process',
'status via socket.on'); 'status via socket.on');
client.results.clearAll();
console.info(response); console.info(response);
console.groupEnd(); console.groupEnd();
}); });
@ -51,10 +55,13 @@ function recieveQueryData(client) {
client.socket.on('corpus_analysis_query_results', (response) => { client.socket.on('corpus_analysis_query_results', (response) => {
console.group('corpus_analysis_query_results: Client recieving query', console.group('corpus_analysis_query_results: Client recieving query',
'data via socket.on'); 'data via socket.on');
console.info(response); console.info('Recieved chunk', response);
queryRenderResults(response.payload, client);
console.info('Added chunk data to results.data and rendered it with',
'results.jsList')
console.groupEnd(); console.groupEnd();
}); });
} }
// export listeners from this module // export listeners from this module
export {recieveSession, recieveQueryStatus, recieveQueryData}; export { recieveSession, recieveQueryStatus, recieveQueryData };

View File

@ -22,7 +22,7 @@ class Results {
class Data { class Data {
// Sets empty object structure. Also usefull to delete old results. // Sets empty object structure. Also usefull to delete old results.
// matchCount default is 0 // matchCount default is 0
init(matchCount = 0) { init(matchCount = 0, type = "results") {
this["matches"] = []; // list of all c with lc and rc this["matches"] = []; // list of all c with lc and rc
this["cpos_lookup"] = {}; // object contains all this key value pair this["cpos_lookup"] = {}; // object contains all this key value pair
this["text_lookup"] = {}; // same as above for all text ids this["text_lookup"] = {}; // same as above for all text ids
@ -122,4 +122,6 @@ class MetaData {
init(json = {}) { init(json = {}) {
Object.assign(this, json); Object.assign(this, json);
} }
} }
export {Results, Data, MetaData};

View File

@ -425,15 +425,31 @@ RessourceList.options = {
class ResultsList extends List { class ResultsList extends List {
constructor(idOrElement, options={}) { static options = {
super(idOrElement, options); page: 10,
this.eventTokens = {}; // all span tokens which are holdeing events if expert pagination: [{
// mode is on. Collected here to delete later on name: "paginationTop",
this.currentExpertTokenElements = {}; // all token elements which have added paginationClass: "paginationTop",
// classes like chip and hoverable for expert view. Collected innerWindow: 8,
//here to delete later on outerWindow: 1
this.addToSubResultsStatus = {}; // holds True/false for check buttons used to add matches tu sub-results. If checked, it is True. If unchecked, it is false. Buttons for this have the class add. Those little round check buttons. }, {
this.addToSubResultsIdsToShow = new Set(); // If check button is pressed its corresponding data_index is saved in this set. The set is shown to the user. paginationClass: "paginationBottom",
innerWindow: 8,
outerWindow: 1
}],
valueNames: ["titles", "lc", "c", "rc", {data: ["index"]}],
item: `<span></span>`
};
constructor(idOrElement, options) {
super(idOrElement, options);
this.options = options;
this.eventTokens = {}; // all span tokens which are holdeing events if expert
// mode is on. Collected here to delete later on
this.currentExpertTokenElements = {}; // all token elements which have added
// classes like chip and hoverable for expert view. Collected
//here to delete later on
this.addToSubResultsStatus = {}; // holds True/false for check buttons used to add matches tu sub-results. If checked, it is True. If unchecked, it is false. Buttons for this have the class add. Those little round check buttons.
this.addToSubResultsIdsToShow = new Set(); // If check button is pressed its corresponding data_index is saved in this set. The set is shown to the user.
} }
helperCreateCpos(cpos_ranges, cpos_values) { helperCreateCpos(cpos_ranges, cpos_values) {
@ -479,12 +495,11 @@ class ResultsList extends List {
} }
// get display options from display options form element // get display options from display options form element
static getDisplayOptions(displayOptionsFormElement) { static getDisplayOptions(htmlId) {
// gets display options parameters // gets display options parameters
let displayOptionsFormData let displayOptionsFormElement = document.getElementById(htmlId);
let displayOptionsData; let displayOptionsFormData = new FormData(displayOptionsFormElement);
displayOptionsFormData = new FormData(displayOptionsFormElement); let displayOptionsData =
displayOptionsData =
{ {
"resultsPerPage": displayOptionsFormData.get("display-options-form-results_per_page"), "resultsPerPage": displayOptionsFormData.get("display-options-form-results_per_page"),
"resultsContex": displayOptionsFormData.get("display-options-form-result_context"), "resultsContex": displayOptionsFormData.get("display-options-form-result_context"),
@ -1182,5 +1197,6 @@ class ResultsList extends List {
`); `);
} }
} }
};
} export {RessourceList, ResultsList};

View File

@ -75,25 +75,43 @@
CorpusAnalysisDisplay, CorpusAnalysisDisplay,
SocketEventListener} from '../../static/js/modules/nopaque.CorpusAnalysisClient.js'; SocketEventListener} from '../../static/js/modules/nopaque.CorpusAnalysisClient.js';
import {recieveSession, recieveQueryStatus, import {recieveSession, recieveQueryStatus,
recieveQueryData} from '../../static/js/modules/nopaque.listenerFunctions.js' recieveQueryData} from '../../static/js/modules/nopaque.listenerFunctions.js';
import {Results, Data, MetaData} from '../../static/js/nopaque.Results.js';
import {ResultsList} from '../../static/js/nopaque.lists.js';
/** /**
* Second Phase: * Second Phase:
* Asynchronus and event driven code * Asynchronus and event driven code
*/ */
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
// init some modals // Initialize the CorpusAnalysisClient
const client = new CorpusAnalysisClient({{ corpus_id }}, nopaque.socket);
console.info("CorpusAnalysisClient created as client:", client);
// Initialize modals which are shown depending on events or client status
const initLoadingElement = document.getElementById("init-display"); const initLoadingElement = document.getElementById("init-display");
const initLoadingModal = M.Modal.init(initLoadingElement, const initLoadingModal = M.Modal.init(initLoadingElement,
{"dismissible": false}); {"dismissible": false});
// set up display elements // Set up display elements which hare show depending on the client status
const initLoadingDisplay = new CorpusAnalysisDisplay(initLoadingModal); const initLoadingDisplay = new CorpusAnalysisDisplay(initLoadingModal);
// set up CorpusAnalysisClient // Register those display elements to client
const client = new CorpusAnalysisClient({{ corpus_id }}, nopaque.socket);
console.info("CorpusAnalysisClient created as client:", client);
// register display elements to client
client.setDisplay("init", initLoadingDisplay); client.setDisplay("init", initLoadingDisplay);
// register listeners and load them /**
* Initializing the results object holding all the data of a query.
* Also holds the metadata of one query.
* resultsListOptions is set to determine how many results per apge are
* shown etc.
* Lastly it also contains the object ResultsList which is a list.js
* subclass which handles the visual reprensetnation of the query data.
*/
let displayOptionsData = ResultsList.getDisplayOptions('display-options-form');
ResultsList.options.page = displayOptionsData["resultsPerPage"];
let resultsList = new ResultsList("result-list", ResultsList.options);
let resultsMetaData = new MetaData();
let results = new Results(new Data(), resultsList, resultsMetaData);
// make results part of the client
client.results = results;
console.info('Initialized the Results object.')
// register listeners listening to socket.io events and load them
const listenForSession = new SocketEventListener('corpus_analysis_init', const listenForSession = new SocketEventListener('corpus_analysis_init',
recieveSession); recieveSession);
const listenForQueryStatus = new SocketEventListener('corpus_analysis_query', const listenForQueryStatus = new SocketEventListener('corpus_analysis_query',
@ -102,10 +120,10 @@
recieveQueryData); recieveQueryData);
client.setSocketEventListeners([listenForSession, listenForQueryStatus, client.setSocketEventListeners([listenForSession, listenForQueryStatus,
listenForQueryData]); listenForQueryData]);
client.loadListeners(); client.loadSocketEventListeners();
// Session initialization // Session initialization
client.requestSession(); client.requestSession();
// send a query and recieve its answer data // Send a query and recieve its answer data
let queryFormElement = document.getElementById("query-form"); let queryFormElement = document.getElementById("query-form");
queryFormElement.addEventListener("submit", (event) => { queryFormElement.addEventListener("submit", (event) => {
try { try {
@ -129,8 +147,8 @@
// Prevent page from reloading on submit // Prevent page from reloading on submit
event.preventDefault(); event.preventDefault();
// Get query string and send query to server // Get query string and send query to server
// results.data.getQueryStr(queryFormElement); results.data.getQueryStr(queryFormElement);
client.requestQueryData('"this" []* "that" within 10 words;'); client.requestQueryData(results.data.query);
}); });
}); });
</script> </script>

View File

@ -47,7 +47,9 @@
<script src="{{ url_for('static', filename='js/List.js/list.min.js') }}"></script> <script src="{{ url_for('static', filename='js/List.js/list.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/Socket.IO/socket.io.slim.js') }}"></script> <script src="{{ url_for('static', filename='js/Socket.IO/socket.io.slim.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque.lists.js') }}"></script> <script type="module">
import {RessourceList} from '../../static/js/nopaque.lists.js'
</script>
<script> <script>
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{% if current_user.setting_dark_mode %} {% if current_user.setting_dark_mode %}
@ -240,43 +242,6 @@
</div> </div>
</div> </div>
</footer> </footer>
<!--
<footer class="page-footer">
<div class="container">
<div class="row">
<div class="col s12 m3">
<a href="https://www.dfg.de/">
<img class="responsive-img" src="{{ url_for('static', filename='images/logo_-_dfg.gif') }}">
</a>
</div>
<div class="col s12 m6">
<h5 class="white-text" style="margin-left: -15px;">Contact</h5>
<div class="row">
<p>Contact our interdisciplinary team via email: <b>inf_sfb1288@lists.uni-bielefeld.de</b></p>
<p>Silke Schwandt (Digital History), Johanna Vompras (Research Data Management), Julia Becker (Cultural Studies), Patrick Jentsch (Cognitive Informatics), Anna Neubert (Digital Humanities), Stephan Porada (Interdisciplinary Media Studies), Helene Schlicht (History, Philosophy)
</div>
</div>
<div class="col s12 m3">
<div class="row">
<div class="col s8">
<h5 class="white-text">Reminder</h5>
<p>
Check out our website for nopaque's upcomming release:<br>
<small>www.uni-bielefeld.de/sfb1288/projekte/inf.html</small>
</p>
</div>
<div class="col s4">
<p>&nbsp;</p>
<img class="responsive-img" src="{{ url_for('static', filename='images/qr_-_inf.svg') }}">
</div>
</div>
</div>
</div>
</div>
</footer>
-->
<script src="{{ url_for('static', filename='js/Materialize/materialize.min.js') }}"></script> <script src="{{ url_for('static', filename='js/Materialize/materialize.min.js') }}"></script>
</body> </body>
</html> </html>