mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2025-01-18 22:00:35 +00:00
Add rudimentary rework of corpus analysis
This commit is contained in:
parent
25ea9ba583
commit
f94d21fa96
@ -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};
|
84
web/app/static/js/modules/nopaque.listenerCallbacks.js
Normal file
84
web/app/static/js/modules/nopaque.listenerCallbacks.js
Normal 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 }
|
@ -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 };
|
@ -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};
|
@ -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};
|
||||||
|
@ -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>
|
||||||
|
@ -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> </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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user