From ccfbf852b20f15fb9bc4a45830a10388a47181b9 Mon Sep 17 00:00:00 2001 From: Stephan Porada Date: Tue, 8 Sep 2020 10:42:39 +0200 Subject: [PATCH] Feature parity with old implementation --- web/app/corpora/events.py | 76 +++++++++------ .../view/InteractionElement.js | 97 ------------------- .../corpus_analysis/view/ResultsView.js | 17 ++-- .../modules/corpus_analysis/view/callbacks.js | 26 ++--- .../templates/corpora/analyse_corpus.html.j2 | 28 +++++- 5 files changed, 97 insertions(+), 147 deletions(-) delete mode 100644 web/app/static/js/modules/corpus_analysis/view/InteractionElement.js diff --git a/web/app/corpora/events.py b/web/app/corpora/events.py index cdc6bc7c..e941ba5b 100644 --- a/web/app/corpora/events.py +++ b/web/app/corpora/events.py @@ -35,41 +35,58 @@ def init_corpus_analysis(corpus_id): def corpus_analysis_get_meta_data(corpus_id): # get meta data from db db_corpus = Corpus.query.get(corpus_id) - # TODO: Check if current user is actually the creator of the corpus? metadata = {} metadata['corpus_name'] = db_corpus.title metadata['corpus_description'] = db_corpus.description metadata['corpus_creation_date'] = db_corpus.creation_date.isoformat() metadata['corpus_last_edited_date'] = db_corpus.last_edited_date.isoformat() - # get meta data from corpus in cqp server client = corpus_analysis_clients.get(request.sid) - client_corpus = client.corpora.get('CORPUS') - metadata['corpus_properties'] = client_corpus.attrs['properties'] - metadata['corpus_size_tokens'] = client_corpus.attrs['size'] + if client is None: + response = {'code': 424, 'desc': 'No client found for this session', + 'msg': 'Failed Dependency'} + socketio.emit('corpus_analysis_query', response, room=request.sid) + return + # check if client is busy or not + if client.status == 'running': + client.status = 'abort' + while client.status != 'ready': + socketio.sleep(0.1) + # get meta data from corpus in cqp server + client.status = 'running' + try: + client_corpus = client.corpora.get('CORPUS') + metadata['corpus_properties'] = client_corpus.attrs['properties'] + metadata['corpus_size_tokens'] = client_corpus.attrs['size'] - text_attr = client_corpus.structural_attributes.get('text') - struct_attrs = client_corpus.structural_attributes.list(filters={'part_of': text_attr}) - text_ids = range(0, (text_attr.attrs['size'])) - texts_metadata = {} - for text_id in text_ids: - texts_metadata[text_id] = {} - for struct_attr in struct_attrs: - texts_metadata[text_id][struct_attr.attrs['name'][(len(text_attr.attrs['name']) + 1):]] = struct_attr.values_by_ids(list(range(struct_attr.attrs['size'])))[text_id] - metadata['corpus_all_texts'] = texts_metadata - metadata['corpus_analysis_date'] = datetime.utcnow().isoformat() - metadata['corpus_cqi_py_protocol_version'] = client.api.version - metadata['corpus_cqi_py_package_version'] = cqi.__version__ - metadata['corpus_cqpserver_version'] = 'CQPserver v3.4.22' # TODO: make this dynamically + text_attr = client_corpus.structural_attributes.get('text') + struct_attrs = client_corpus.structural_attributes.list(filters={'part_of': text_attr}) + text_ids = range(0, (text_attr.attrs['size'])) + texts_metadata = {} + for text_id in text_ids: + texts_metadata[text_id] = {} + for struct_attr in struct_attrs: + texts_metadata[text_id][struct_attr.attrs['name'][(len(text_attr.attrs['name']) + 1):]] = struct_attr.values_by_ids(list(range(struct_attr.attrs['size'])))[text_id] + metadata['corpus_all_texts'] = texts_metadata + metadata['corpus_analysis_date'] = datetime.utcnow().isoformat() + metadata['corpus_cqi_py_protocol_version'] = client.api.version + metadata['corpus_cqi_py_package_version'] = cqi.__version__ + metadata['corpus_cqpserver_version'] = 'CQPserver v3.4.22' # TODO: make this dynamically - # write some metadata to the db - db_corpus.current_nr_of_tokens = metadata['corpus_size_tokens'] - db.session.commit() + # write some metadata to the db + db_corpus.current_nr_of_tokens = metadata['corpus_size_tokens'] + db.session.commit() - # emit data - payload = metadata - response = {'code': 200, 'desc': 'Corpus meta data', 'msg': 'OK', - 'payload': payload} - socketio.emit('corpus_analysis_meta_data', response, room=request.sid) + # emit data + payload = metadata + response = {'code': 200, 'desc': 'Corpus meta data', 'msg': 'OK', + 'payload': payload} + socketio.emit('corpus_analysis_meta_data', response, room=request.sid) + except cqi.errors.CQiException as e: + payload = {'code': e.code, 'desc': e.description, 'msg': e.name} + response = {'code': 500, 'desc': None, 'msg': 'Internal Server Error', + 'payload': payload} + socketio.emit('corpus_analysis_meta_data', response, room=request.sid) + client.status = 'ready' @socketio.on('corpus_analysis_query') @@ -100,7 +117,6 @@ def corpus_analysis_query(query): 'match_count': results.attrs['size']} response = {'code': 200, 'desc': None, 'msg': 'OK', 'payload': payload} socketio.emit('corpus_analysis_query', response, room=request.sid) - # TODO: Stop here and add a new method for transmission chunk_size = 100 chunk_start = 0 context = 50 @@ -142,6 +158,11 @@ def corpus_analysis_inspect_match(payload): socketio.emit('corpus_analysis_inspect_match', response, room=request.sid) return + if client.status == 'running': + client.status = 'abort' + while client.status != 'ready': + socketio.sleep(0.1) + client.status = 'running' try: corpus = client.corpora.get('CORPUS') s = corpus.structural_attributes.get('s') @@ -171,6 +192,7 @@ def corpus_analysis_inspect_match(payload): 'type': type, 'data_indexes': data_indexes} socketio.emit('corpus_analysis_inspect_match', response, room=request.sid) + client.status = 'ready' def corpus_analysis_session_handler(app, corpus_id, user_id, session_id): diff --git a/web/app/static/js/modules/corpus_analysis/view/InteractionElement.js b/web/app/static/js/modules/corpus_analysis/view/InteractionElement.js deleted file mode 100644 index 0fa3fbb7..00000000 --- a/web/app/static/js/modules/corpus_analysis/view/InteractionElement.js +++ /dev/null @@ -1,97 +0,0 @@ -// TODO: Simplify this so that things are executed on change page and on first -// load look below at function doc strings - -// one interaction can have more than one type when the associatee callback -// can be executed - -class Interaction { - constructor(htmlId="", - checkStatus=true, - disabledBefore=true, - disabledAfter=false, - hideBefore=true, - hideAfter=false) { - this.htmlId = htmlId; - this.element = ((htmlId) => { - document.getElementById(htmlId); - })() - this.checkStatus = checkStatus; - this.callbacks = {}; - this.disabledBefore = disabledBefore; - this.disabledAfter = disabledAfter; - this.hideBefore = hideBefore; - this.hideAfter = hideAfter; - } - - setCallback(trigger, callback, bindThis, args=[]) { - this.callbacks[trigger] = { - "function": callback, - "bindThis": bindThis, - "args": args - }; - } - - bindThisToCallback(trigger) { - let callback = this.callbacks[trigger]; - let boundedCallback = callback["function"].bind(callback.bindThis); - return boundedCallback; - } -} - -class Interactions { - constructor() { - this.interactions = []; - } - - addInteractions (interactions) { - this.interactions.push(...interactions); - } - - /** - * This function executes all registered interactions of the type - * onElementChange. - * Interactions and their associated callbacks will be executed every time - * a chante event occurs on the specified element. - */ - onElementChangeExecute() { - // checks if a change for every interactionElement happens and executes - // the callbacks accordingly - for (let interaction of this.interactions) { - if (interaction.checkStatus) { - interaction.element.addEventListener("change", (event) => { - if (event.target.checked) { - let f_on = interaction.bindThisToCallback("on"); - let args_on = interaction.callbacks.on.args; - f_on(...args_on); - } else if (!event.target.checked){ - let f_off = interaction.bindThisToCallback("off"); - let args_off = interaction.callbacks.off.args; - f_off(...args_off); - } - }); - } else { - continue - } - }; - } - - /** - * This function executes all registered interactions of the type onQueryLoad. - * Interactions and their associated callbacks will be executed once if when - * the first query results of a query request are being displayed. - */ - onQueryLoadExecute() { - } - - /** - * This function executes all registered interactions of the type - * onPageChange. - * Interactions and their associated callbacks will be executed everytime if - * the user used the page navigation and a new result page is being displayed. - */ - onPageChangeExecute() { - } -} - -// export Classes -export { InteractionElement, InteractionElements }; \ No newline at end of file diff --git a/web/app/static/js/modules/corpus_analysis/view/ResultsView.js b/web/app/static/js/modules/corpus_analysis/view/ResultsView.js index 46c82f02..2fe6ba1b 100644 --- a/web/app/static/js/modules/corpus_analysis/view/ResultsView.js +++ b/web/app/static/js/modules/corpus_analysis/view/ResultsView.js @@ -251,7 +251,8 @@ class ResultsList extends List { /** * Also activate/deactivate buttons in the table/jsList results accordingly * if button in inspect was activated/deactivated. - * This part only runs if tableCall is false. + * This part only runs if tableCall is set to false when this function is + * called. */ if (!tableCall) { this.getHTMLElements(['#query-results-table']); @@ -350,7 +351,7 @@ class ResultsList extends List { this.contextModal.open(); // add a button to add this match to sub results with onclick event let classes = `btn-floating btn waves-effect` + - `waves-light grey right` + ` waves-light grey right` let addToSubResultsIdsBtn = document.createElement("a"); addToSubResultsIdsBtn.setAttribute("class", classes + ` add`); addToSubResultsIdsBtn.innerHTML = 'add'; @@ -545,12 +546,10 @@ class ResultsList extends List { this.page = this.displayOptionsFormResultsPerPage.value; this.update(); } - // TODO: reimplement the followinmg stuff - // this.activateInspect(); - // this.pageChangeEventInteractionHandler(interactionElements); - // if (expertModeSwitchElement.checked) { - // this.expertModeOn("query-display"); // page holds new result rows, so add new tooltips - // } + this.activateInspect(); + if (this.displayOptionsFormExpertMode.checked) { + this.expertModeOn("query-display"); + } } // Event function triggered on context select change @@ -612,7 +611,7 @@ class ResultsList extends List { this.currentExpertTokenElements[htmlId] = []; } let container = document.getElementById(htmlId); - let tokens = container.querySelectorAll("span.token"); + let tokens = container.querySelectorAll('span.token'); this.currentExpertTokenElements[htmlId].push(...tokens); this.eventTokens[htmlId] = []; for (let tokenElement of this.currentExpertTokenElements[htmlId]) { diff --git a/web/app/static/js/modules/corpus_analysis/view/callbacks.js b/web/app/static/js/modules/corpus_analysis/view/callbacks.js index 8e0a36c3..d9c85926 100644 --- a/web/app/static/js/modules/corpus_analysis/view/callbacks.js +++ b/web/app/static/js/modules/corpus_analysis/view/callbacks.js @@ -51,17 +51,17 @@ function queryDataPreparingCallback(resultsList, detail) { resultsList.clear() // get needed HTML Elements let results = detail.results; - resultsList.getHTMLElements( - ['#interactions-menu', - '#recieved-match-count', - '#total-match-count', - '#text-lookup-count', - '#text-lookup-titles', - '#query-results-user-feedback', - '#query-progress-bar', - '#query-results-create', - '#sub-results-match-ids', - '#nr-marked-matches', + resultsList.getHTMLElements([ + '#interactions-menu', + '#recieved-match-count', + '#total-match-count', + '#text-lookup-count', + '#text-lookup-titles', + '#query-results-user-feedback', + '#query-progress-bar', + '#query-results-create', + '#sub-results-match-ids', + '#nr-marked-matches', ]); // show or enable some things for the user resultsList.interactionsMenu.classList.toggle('hide', false) @@ -108,6 +108,10 @@ function queryDataRecievingCallback(resultsList, detail) { resultsList.update(); resultsList.changeHitsPerPage(); resultsList.changeContext(); + //activate expertMode of switch is checked + if (resultsList.displayOptionsFormExpertMode.checked) { + resultsList.expertModeOn('query-display', results); + } } else if (!client.dynamicMode) { results.jsList.add(resultItems, (items) => { for (let item of items) { diff --git a/web/app/templates/corpora/analyse_corpus.html.j2 b/web/app/templates/corpora/analyse_corpus.html.j2 index 86c4978f..d940e6ff 100644 --- a/web/app/templates/corpora/analyse_corpus.html.j2 +++ b/web/app/templates/corpora/analyse_corpus.html.j2 @@ -123,8 +123,9 @@ document.addEventListener("DOMContentLoaded", () => { 'dynamicMode': true}); /** * Initializing the results object as a model holding all the data of a query. - * Also holds the metadata of one query. After that initialize the ResultsList - * object as the View handeling the represnetation of the data. + * Also holds the metadata of one query. + * After that initialize the ResultsList object as the View handeling the + * represnetation of the data for the user. */ let results = new Results(); let resultsList = new ResultsList('result-list', ResultsList.options); @@ -174,7 +175,7 @@ document.addEventListener("DOMContentLoaded", () => { listenForResults]); client.loadSocketEventListeners(); /** - * Register resultsList listeners listening to nitification events. + * Register resultsList listeners listening to notification events. */ const listenForClientNotification = new ViewEventListener('notify-view', recieveClientNotification); @@ -221,7 +222,28 @@ document.addEventListener("DOMContentLoaded", () => { '#query-results-download-modal', '#query-results-table', '#display-options-form-expert_mode', + '.pagination', ]); + + /** + * The following listener handles what functions are called when the user + * does use the page navigation to navigate to a new page. + */ + for (let element of resultsList.pagination) { + element.addEventListener("click", (event) => { + // shows match context according to the user picked value + resultsList.changeContext(); + // activates or deactivates expertMode on new page depending switch value + if (resultsList.displayOptionsFormExpertMode.checked) { + resultsList.expertModeOn('query-display', results); + } else { + resultsList.expertModeOff('query-display'); + } + // activates inspect buttons on new page if client is not busy + resultsList.activateInspect(); + }); + } + /** * The following event Listener handles the expert mode switch for the list */