diff --git a/web/app/corpora/events.py b/web/app/corpora/events.py index 071e0d34..b148839e 100644 --- a/web/app/corpora/events.py +++ b/web/app/corpora/events.py @@ -57,7 +57,8 @@ def corpus_analysis_get_meta_data(corpus_id): 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_version'] = cqi.version + 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 @@ -127,7 +128,6 @@ def corpus_analysis_query(query): @socketio.on('corpus_analysis_inspect_match') @socketio_login_required def corpus_analysis_inspect_match(payload): - payload = payload["payload"] client = corpus_analysis_clients.get(request.sid) if client is None: response = {'code': 424, 'desc': 'No client found for this session', diff --git a/web/app/corpora/forms.py b/web/app/corpora/forms.py index 8ec9d17b..cab8b3bf 100644 --- a/web/app/corpora/forms.py +++ b/web/app/corpora/forms.py @@ -63,7 +63,7 @@ class AddCorpusForm(FlaskForm): class QueryForm(FlaskForm): query = StringField('Query', validators=[DataRequired(), Length(1, 1024)]) - submit = SubmitField('Send query') + submit = SubmitField('Search') class DisplayOptionsForm(FlaskForm): diff --git a/web/app/static/js/nopaque.InteractionElement.js b/web/app/static/js/nopaque.InteractionElement.js new file mode 100644 index 00000000..477fd243 --- /dev/null +++ b/web/app/static/js/nopaque.InteractionElement.js @@ -0,0 +1,33 @@ +class InteractionElement { + constructor(htmlId="", + disabledBefore=true, + disabledAfter=false, + hideBefore=true, + hideAfter=false) { + this.htmlId = htmlId; + this.callbacks = {}; + this.disabledBefore = disabledBefore; + this.disabledAfter = disabledAfter; + this.hideBefore = hideBefore; + this.hideAfter = hideAfter; + } + + getElement() { + this.interactionStatusElement = document.getElementById(this.htmlId); + return this.interactionStatusElement + } + + 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; + } +} \ No newline at end of file diff --git a/web/app/static/js/nopaque.callbacks.js b/web/app/static/js/nopaque.callbacks.js index bb02a2fc..e26c3985 100644 --- a/web/app/static/js/nopaque.callbacks.js +++ b/web/app/static/js/nopaque.callbacks.js @@ -7,6 +7,7 @@ function querySetup(payload) { // This is called when a query was successfull // some hiding and resetting queryResultsExportElement.classList.add("disabled"); + addToSubSubcorpusElement.setAttribute("disabled", ""); queryResultsDeterminateElement.style.width = "0%"; queryResultsProgressElement.classList.remove("hide"); queryResultsUserFeedbackElement.classList.remove("hide"); @@ -14,6 +15,7 @@ function querySetup(payload) { receivedMatchCountElement.innerText = "0"; textLookupCountElement.innerText = "0"; matchCountElement.innerText = payload.match_count; + textTitlesElement.innterText = ""; // always re initializes results to delete old results from it // this has to be done here again because the last chunk from old results was still being recieved results.clearAll() @@ -53,6 +55,11 @@ function queryRenderResults(payload) { // show user current and total match count receivedMatchCountElement.innerText = `${results.data.matches.length}`; textLookupCountElement.innerText = `${Object.keys(results.data.text_lookup).length}`; + let titles = new Array(); + for (let [key, value] of Object.entries(results.data.text_lookup)) { + titles.push(`${value.title} (${value.publishing_year})`); + }; + textTitlesElement.innerText = `${titles.join(", ")}`; console.log("Results recieved:", results.data); // upate progress status progress = payload.progress; // global declaration @@ -60,10 +67,14 @@ function queryRenderResults(payload) { queryResultsProgressElement.classList.add("hide"); queryResultsUserFeedbackElement.classList.add("hide"); queryResultsExportElement.classList.remove("disabled"); + addToSubSubcorpusElement.removeAttribute("disabled"); results.jsList.activateInspect(); - } - // inital expert mode check and activation - if (expertModeSwitchElement.checked) { - results.jsList.expertModeOn("query-display"); + // inital expert mode check and sub subcorpus activation + if (addToSubSubcorpusElement.checked) { + results.jsList.activateAddToSubSubcorpus(); + } + if (expertModeSwitchElement.checked) { + results.jsList.expertModeOn("query-display"); + } } } \ No newline at end of file diff --git a/web/app/static/js/nopaque.lists.js b/web/app/static/js/nopaque.lists.js index 235e2b1d..5354fc69 100644 --- a/web/app/static/js/nopaque.lists.js +++ b/web/app/static/js/nopaque.lists.js @@ -134,8 +134,27 @@ class ResultsList extends List { this.currentExpertTokenElements = {}; // all token elements which have added // classes like chip and hoverable for expert view. Collected //here to delete later on + this.addToSubSubcorpuStatus = {}; } + // handels interactionElements during a pagination navigation + pageChangeEventInteractionHandler(interactionElements) { + // get elements to check thier status + for (let interaction of interactionElements) { + let element = interaction.getElement(); + if (element.checked) { + let f_on = interaction.bindThisToCallback("on"); + let args_on = interaction.callbacks.on.args; + f_on(...args_on); + console.log("ON TRIGGERED"); + } else { + let f_off = interaction.bindThisToCallback("off"); + let args_off = interaction.callbacks.off.args; + f_off(...args_off); + console.log("OFF TRIGGERED"); + } + } + } // get display options from display options form element static getDisplayOptions(displayOptionsFormElement) { @@ -152,6 +171,40 @@ class ResultsList extends List { return displayOptionsData } + // ###### Functions to add one match to a sub-subcorpus ###### + // activate add button + activateAddToSubSubcorpus() { + if (progress === 100) { + let addToSubSubcorpusBtnElements = document.getElementsByClassName("add"); + for (let addToSubSubcorpusBtn of addToSubSubcorpusBtnElements) { + addToSubSubcorpusBtn.classList.remove("hide"); + } + } + } + // deactivate add button + deactivateAddToSubSubcorpus() { + let addToSubSubcorpusBtnElements = document.getElementsByClassName("add"); + for (let addToSubSubcorpusBtn of addToSubSubcorpusBtnElements) { + addToSubSubcorpusBtn.classList.add("hide"); + } + } + // add match on click to a SubSubcorpus + addToSubSubcorpus(dataIndex) { + if (!this.addToSubSubcorpuStatus[dataIndex] + || this.addToSubSubcorpuStatus === undefined) { + event.target.classList.remove("grey"); + event.target.classList.add("green"); + event.target.innerText = "check"; + this.addToSubSubcorpuStatus[dataIndex] = true; + console.log(dataIndex); + } else if (this.addToSubSubcorpuStatus[dataIndex]) { + event.target.classList.remove("green"); + event.target.classList.add("grey"); + event.target.innerText = "add"; + this.addToSubSubcorpuStatus[dataIndex] = false; + } + } + // ###### Functions to inspect one match, to show more details ###### // activate inspect buttons if progress is 100 activateInspect() { @@ -167,22 +220,21 @@ class ResultsList extends List { } //gets result cpos infos for one dataIndex to send back to the server - inspect(dataIndex) { + inspect(dataIndex, type) { this.contextId = dataIndex; let contextResultsElement; contextResultsElement = document.getElementById("context-results"); contextResultsElement.innerHTML = ""; // clear it from old inspects contextModal.open(); nopaque.socket.emit("corpus_analysis_inspect_match", - { - payload: { + {"type": type, first_cpos: results.data.matches[dataIndex].c[0], last_cpos: results.data.matches[dataIndex].c[1], } - } ); } + // create Element from HTML String helper function HTMLTStrToElement(htmlStr) { // https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518 let template = document.createElement("template"); @@ -432,20 +484,6 @@ class ResultsList extends List { } // ###### Expert view event functions ###### - - // Event function to check if pagination is used and then look if - // expertModeSwitchElement is checked - // if checked than expertModeOn is executed - // if unchecked expertModeOff is executed - eventHandlerCheck(event) { - if (expertModeSwitchElement.checked) { - this.expertModeOn("query-display"); - } else if (!expertModeSwitchElement.checked) { - event.preventDefault(); - this.expertModeOff("query-display"); - } - } - // function to create a tooltip for the current hovered token tooltipEventCreate(event) { // console.log("Create Tooltip on mouseover."); @@ -465,8 +503,12 @@ class ResultsList extends List { } expertModeOn(htmlId) { - // torn the expert mode on for all tokens in the DOM element identified by its htmlID - this.currentExpertTokenElements[htmlId] = document.getElementById(htmlId).getElementsByClassName("token"); + // turn the expert mode on for all tokens in the DOM element identified by its htmlID + console.log(this); + if (!Array.isArray(this.currentExpertTokenElements[htmlId])) { + this.currentExpertTokenElements[htmlId] = []; + } + this.currentExpertTokenElements[htmlId].push( ...document.getElementById(htmlId).getElementsByClassName("token")); this.tooltipEventCreateBind = this.tooltipEventCreate.bind(this); this.tooltipEventDestroyBind = this.tooltipEventDestroy.bind(this); this.eventTokens[htmlId] = []; @@ -511,6 +553,13 @@ class ResultsList extends List { // function to remove extra informations and animations from tokens expertModeOff(htmlId) { // console.log("Expert mode is off."); + console.log(this); + if (!Array.isArray(this.currentExpertTokenElements[htmlId])) { + this.currentExpertTokenElements[htmlId] = []; + } + if (!Array.isArray(this.eventTokens[htmlId])) { + this.eventTokens[htmlId] = []; + } for (let tokenElement of this.currentExpertTokenElements[htmlId]) { tokenElement.classList.remove("chip", "hoverable", "expert-view"); } @@ -524,6 +573,7 @@ class ResultsList extends List { } createResultRowElement(item, chunk) { + let addToSubSubcorpusBtn; let c; let cCellElement; let cpos; @@ -579,16 +629,29 @@ class ResultsList extends List { `${token.word} `); // get text titles of every hit cpos token textTitles.add(chunk.text_lookup[token.text].title); - // add button to trigger more context to every match td - inspectBtn = document.createElement("a"); - inspectBtn.setAttribute("class", `btn-floating btn-flat waves-effect` + - `waves-light grey right inspect disabled` - ); - inspectBtn.innerHTML = 'search'; - inspectBtn.onclick = () => {this.inspect(values.index)}; } - // add text titles at front as first td of one row + // add some interaction buttons + // # some btn css rules and classes + let css = `margin-right: 10px;` + let classes = `btn-floating btn-flat waves-effect` + + `waves-light grey right` + // # add button to trigger more context to every match td + inspectBtn = document.createElement("a"); + inspectBtn.setAttribute("class", classes + ` disabled inspect` + ); + inspectBtn.setAttribute("style", css) + inspectBtn.innerHTML = 'search'; + inspectBtn.onclick = () => {this.inspect(values.index, "inspect")}; + // # add btn to add matches to sub-subcorpus. hidden per default + addToSubSubcorpusBtn = document.createElement("a"); + addToSubSubcorpusBtn.setAttribute("class", classes + ` hide add` + ); + addToSubSubcorpusBtn.setAttribute("style", css) + addToSubSubcorpusBtn.innerHTML = 'add'; + addToSubSubcorpusBtn.onclick= (event) => {this.addToSubSubcorpus(values.index)} cCellElement.appendChild(inspectBtn); + cCellElement.appendChild(addToSubSubcorpusBtn); + // add text titles at front as first td of one row textTitlesCellElement.innerText = [...textTitles].join(", "); matchRowElement.insertAdjacentHTML("afterbegin", textTitlesCellElement.outerHTML); matchNrElement.innerText = values.index + 1; diff --git a/web/app/templates/corpora/analyse_corpus.html.j2 b/web/app/templates/corpora/analyse_corpus.html.j2 index 6caa7913..bc49037f 100644 --- a/web/app/templates/corpora/analyse_corpus.html.j2 +++ b/web/app/templates/corpora/analyse_corpus.html.j2 @@ -1,114 +1,118 @@ {% extends "nopaque.html.j2" %} +{% set headline = ' ' %} + {% set full_width = True %} {% block page_content %}
- + +
+ +
- Query Results
-
-
-
-

- - of - - matches loaded. -
- Matches occured in - - corpus files. -

-

- help - The Server is still sending your results. - Functions like "Export Results" and "Match Inspect" will be - available after all matches have been loaded. -

-
-
-
-
+
+
+
+
+
Infos
+
+
+

+ + of + + matches loaded. +
+ Matches occured in + + corpus files: +
+ +

+

+ help + The Server is still sending your results. + Functions like "Export Results" and "Match Inspect" will be + available after all matches have been loaded. +

+
+
+
+
-
-
-
- +
+
+
+
Export
+
+ +
+
+
Create
+
+
+ Sub-Subcorpus creation: + +
+
+
+
Display
+
+
+ {{ M.render_field(display_options_form.results_per_page, + material_icon='format_list_numbered') }} + {{ M.render_field(display_options_form.result_context, + material_icon='short_text') }} + {{ M.render_field(display_options_form.expert_mode) }} +
+
+
@@ -275,11 +279,15 @@ +