From 12586bb13dbe201a3099283e845007c0d9eae21b Mon Sep 17 00:00:00 2001
From: Stephan Porada <sporada@uni-bielefeld.de>
Date: Mon, 6 Apr 2020 12:32:29 +0200
Subject: [PATCH] First feature complete rebuilt of analysis interface

---
 app/corpora/pj_events.py                      |   7 +-
 app/static/js/nopaque.pj_analyse_corpus.js    | 108 +++++++++++-------
 .../corpora/pj_analyse_corpus.html.j2         |  38 +++++-
 3 files changed, 104 insertions(+), 49 deletions(-)

diff --git a/app/corpora/pj_events.py b/app/corpora/pj_events.py
index 66e9cd56..b4b10d77 100644
--- a/app/corpora/pj_events.py
+++ b/app/corpora/pj_events.py
@@ -96,7 +96,7 @@ def pj_corpus_analysis_query(query):
 @socketio.on('pj_corpus_analysis_inspect_match')
 @socketio_login_required
 def pj_corpus_analysis_inspect_match(payload):
-    logger.warning(payload)
+    payload = payload["payload"]
     client = pj_corpus_analysis_clients.get(request.sid)
     if client is None:
         socketio.emit('query', '[424]: Failed Dependency',
@@ -106,8 +106,9 @@ def pj_corpus_analysis_inspect_match(payload):
     corpus = client.corpora.get('CORPUS')
     s = corpus.attributes.structural.get('s')
     match_context = s.export(payload['first_cpos'], payload['last_cpos'],
-                             context=3, expand_lists=True)
-    socketio.emit('pj_corpus_analysis_inspect_match',
+                             context=3, expand_lists=False)
+    match_context['cpos_ranges'] = True
+    socketio.emit('pj_match_context',
                   {'payload': match_context}, room=request.sid)
 
 
diff --git a/app/static/js/nopaque.pj_analyse_corpus.js b/app/static/js/nopaque.pj_analyse_corpus.js
index 370b4317..85517acd 100644
--- a/app/static/js/nopaque.pj_analyse_corpus.js
+++ b/app/static/js/nopaque.pj_analyse_corpus.js
@@ -86,58 +86,80 @@ function inspect(dataIndex) {
   // This function should be in the AnalysisClient class as a method.
   console.log("Inspect!");
   console.log(results.resultsJSON.matches[dataIndex].c);
+  contextResultsElement = document.getElementById("context-results");
+  contextResultsElement.innerHTML = "";  // clear it from old inspects
   contextModal.open();
   nopaque.socket.emit("pj_corpus_analysis_inspect_match",
-              {payload: {first_cpos: results.resultsJSON.matches[dataIndex].c[0],
-                         last_cpos: results.resultsJSON.matches[dataIndex].c[1]}});
+              {payload: {
+                         first_cpos: results.resultsJSON.matches[dataIndex].c[0],
+                         last_cpos: results.resultsJSON.matches[dataIndex].c[1]
+                        }
+              });
 }
 
-function showMatchContext(payload) {
+function showMatchContext(response) {
+  let contextData = response.payload
   let contextResultsElement;
-  let sentenceElement
+  let contextModalLoading;
+  let contextModalReady;
+  let expertModeSwitchElement
+  let partElement
   let token;
   let tokenElement;
   console.log("###### match_context ######");
-  console.log("Incoming data:", payload);
+  console.log("Incoming data:", contextData);
+  expertModeSwitchElement = document.getElementById("display-options-form-expert_mode");
   contextResultsElement = document.getElementById("context-results");
-  contextResultsElement.innerHTML = "<p>&nbsp;</p>";
-  document.getElementById("context-modal-loading").classList.add("hide");
-  document.getElementById("context-modal-ready").classList.remove("hide");
+  contextModalLoading = document.getElementById("context-modal-loading");
+  contextModalLoading.classList.add("hide");
+  contextModalReady = document.getElementById("context-modal-ready");
+  contextModalReady.classList.remove("hide");
 
-  for (let [key, value] of Object.entries(payload['context_s_cpos'])) {
-    sentenceElement = document.createElement("p");
-    for (let cpos of value) {
-      token = payload["cpos_lookup"][cpos];
-      tokenElement = document.createElement("span");
-      tokenElement.classList.add("token");
-      if (payload["match_cpos_list"].includes(cpos)) {
-        tokenElement.classList.add("bold");
-        tokenElement.classList.add("light-green");
-      }
-      tokenElement.dataset.cpos = cpos;
-      tokenElement.innerText = token["word"];
-      var expertModeSwitchElement = document.getElementById("expert-mode-switch");
-      if (expertModeSwitchElement.checked) {
-        expertModeOn([tokenElement], payload);
-      }
-      sentenceElement.append(tokenElement);
-      sentenceElement.append(document.createTextNode(" "));
-    }
-    contextResultsElement.append(sentenceElement);
+  if (contextData.cpos_ranges == true) {
+    // python range like function from MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Sequence_generator_(range)
+    const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
+    lc = range(contextData.match.lc[0], contextData.match.lc[1], 1)
+    c = range(contextData.match.c[0], contextData.match.c[1], 1)
+    rc = range(contextData.match.rc[0], contextData.match.rc[1], 1)
+  } else {
+    lc = contextData.match.lc;
+    c = contextData.match.c;
+    rc = contextData.match.rc;
+  }
+
+  partElement = document.createElement("p");
+  for (let cpos of lc) {
+    token = contextData["cpos_lookup"][cpos];
+    partElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
+    contextResultsElement.append(partElement);
+  }
+  for (let cpos of c) {
+    token = contextData["cpos_lookup"][cpos];
+    partElement.insertAdjacentHTML("beforeend", `<span class="token bold underline" data-cpos="${cpos}" style="text-decoration-line: underline;">${token["word"]} </span>`);
+    contextResultsElement.append(partElement);
+  }
+  for (let cpos of rc) {
+    token = contextData["cpos_lookup"][cpos];
+    partElement.insertAdjacentHTML("beforeend", `<span class="token" data-cpos="${cpos}">${token["word"]} </span>`);
+    contextResultsElement.append(partElement);
+  }
+  if (expertModeSwitchElement.checked) {
+    let tokenElements = partElement.getElementsByClassName("token");
+    expertModeOn(tokenElements, contextData);
   }
 }
 
 // ###### Display options changing live how the matches are being displayed ######
 
 // Event function that changes the shown hits per page.
-// Just alters the resultList.page property
+// Just alters the resultsList.page property
 function changeHitsPerPage(event) {
   try {
-    resultList.page = event.target.value;
-    resultList.update();
+    resultsList.page = event.target.value;
+    resultsList.update();
     nopaque.flash("Updated matches per page.")
   } catch (e) {
-    console.log("resultList has no results right now. Live update of items per page is useless for now.");
+    console.log("resultsList has no results right now. Live update of items per page is useless for now.");
   }
 }
 
@@ -190,7 +212,7 @@ function eventHandlerCheck(event) {
   console.log("pagination used!");
   console.log(expertModeSwitchElement.checked);
   if (expertModeSwitchElement.checked) {
-    expertModeOn(event.currentTarget.tokenElements, result);
+    expertModeOn(event.currentTarget.tokenElements, resultsJSON);
   } else if (!expertModeSwitchElement.checked) {
     event.preventDefault();
     console.log("prevented! Destroy");
@@ -199,19 +221,21 @@ function eventHandlerCheck(event) {
 }
 
 // function to apply extra information and animation to every token
-function expertModeOn(tokenElements, result_lookup) {
+function expertModeOn(tokenElements, results) {
   let token;
 
   console.log("expertModeOn!");
+  console.log(results);
+  console.log("hier:", tokenElements);
   for (let tokenElement of tokenElements) {
     tokenElement.classList.add("chip");
     tokenElement.classList.add("hoverable");
     tokenElement.classList.add("expert-view");
-    token = result_lookup["cpos_lookup"][tokenElement.dataset.cpos];
+    token = results["cpos_lookup"][tokenElement.dataset.cpos];
     tokenElement.addEventListener("mouseover", function(event) {
       console.log("Mouseover!");
       console.log(event.target);
-      token = result_lookup["cpos_lookup"][event.target.dataset.cpos];
+      token = results["cpos_lookup"][event.target.dataset.cpos];
       addToolTipToTokenElement(event.target, token);
     });
   }
@@ -235,16 +259,12 @@ function addToolTipToTokenElement(tokenElement, token) {
                                  NER: ${token["ner"]}
                                </td>
                                <td class="left-align">
-                                 Title: ${result["text_lookup"][token["text"]]["title"]}<br>
-                                 Author: ${result["text_lookup"][token["text"]]["author"]}<br>
-                                 Publishing year: ${result["text_lookup"][token["text"]]["publishing_year"]}
+                                 Title: ${resultsJSON["text_lookup"][token["text"]]["title"]}<br>
+                                 Author: ${resultsJSON["text_lookup"][token["text"]]["author"]}<br>
+                                 Publishing year: ${resultsJSON["text_lookup"][token["text"]]["publishing_year"]}
                                </td>
                              </tr>
-                           </table>`,
-                 "inDuration": 1500,
-                 "margin": 15,
-                 "position": "top",
-                 "transitionMovement": 0});
+                           </table>`});
 }
 
 // function to remove extra informations and animations from tokens
diff --git a/app/templates/corpora/pj_analyse_corpus.html.j2 b/app/templates/corpora/pj_analyse_corpus.html.j2
index 748e42b2..d021526c 100644
--- a/app/templates/corpora/pj_analyse_corpus.html.j2
+++ b/app/templates/corpora/pj_analyse_corpus.html.j2
@@ -327,10 +327,44 @@
 
     // get context of one match if inspected via socket.io
     nopaque.socket.on("pj_match_context", showMatchContext);
-  });
 
+    // live update of hits per page if hits per page value is changed
+    hitsPerPageInputElement = document.getElementById("display-options-form-results_per_page");
+    hitsPerPageInputElement.onchange = changeHitsPerPage;
+
+    // live update of lr context per item if context value is changed
+    contextPerItemElement = document.getElementById("display-options-form-result_context");
+    contextPerItemElement.onchange = changeContext;
+
+    // eventListener if pagination is used to apply new context size to new page
+    // and also activate inspect match if queryFinished is true
+    paginationElements = document.getElementsByClassName("pagination");
+    for (element of paginationElements) {
+      element.addEventListener("click", changeContext);
+      element.addEventListener("click", activateInspect);
+    }
+
+    // epxert mode table view
+    expertModeSwitchElement = document.getElementById("display-options-form-expert_mode");
+    expertModeSwitchElement.addEventListener("change", function(event) {
+      let currentTokenElements = document.getElementsByClassName("token");
+      let paginationElements = document.getElementsByClassName("pagination");
+      if (event.target.checked) {
+        console.log("Checked!");
+        expertModeOn(currentTokenElements, resultsJSON);
+        for (element of paginationElements) {
+          element.tokenElements = currentTokenElements;
+          element.addEventListener("click", eventHandlerCheck);
+        }
+      } else {
+        console.log("Unchecked!");
+        expertModeOff(currentTokenElements);
+        console.log("unchecked! Destroy");
+      }
+    })
+  });
 
-  // Add onclick to open download modal wen Export Results button is pressed
+  // Add onclick to open download modal when Export Results button is pressed
   queryResultsExportElement.onclick = function() {
     exportModal.open();
   }
-- 
GitLab