From 9da1a6e9875d38cd06bf8c1866379c10f9a6aef2 Mon Sep 17 00:00:00 2001
From: Patrick Jentsch <p.jentsch@uni-bielefeld.de>
Date: Mon, 17 Jul 2023 10:40:34 +0200
Subject: [PATCH] Add status text to corpus analysis app startup modal

---
 .../js/CorpusAnalysis/CorpusAnalysisApp.js    |  23 +++-
 .../CorpusAnalysisConcordance.js              | 104 +++++++++---------
 .../js/CorpusAnalysis/CorpusAnalysisReader.js |  57 +++++-----
 .../CorpusAnalysisStaticVisualization.js      |   2 +-
 app/templates/corpora/analysis.html.j2        |   1 +
 5 files changed, 102 insertions(+), 85 deletions(-)

diff --git a/app/static/js/CorpusAnalysis/CorpusAnalysisApp.js b/app/static/js/CorpusAnalysis/CorpusAnalysisApp.js
index f35d3458..b7e860bd 100644
--- a/app/static/js/CorpusAnalysis/CorpusAnalysisApp.js
+++ b/app/static/js/CorpusAnalysis/CorpusAnalysisApp.js
@@ -26,22 +26,33 @@ class CorpusAnalysisApp {
     this.disableActionElements();
     this.elements.m.initModal.open();
 
+    const statusTextElement = this.elements.initModal.querySelector('.status-text');
+
     // Setup CQi over SocketIO connection and gather data from the CQPServer
     try {
+      statusTextElement.innerText = 'Creating CQi over SocketIO client...';
       const cqiClient = new cqi.CQiClient('/cqi_over_sio');
+      statusTextElement.innerText += ' Done';
+      statusTextElement.innerHTML += '<br>Waiting for the CQP server...';
       const response = await cqiClient.api.socket.emitWithAck('init', this.corpusId);
       if (response.code !== 200) {throw new Error();}
+      statusTextElement.innerText += ' Done';
+      statusTextElement.innerHTML += '<br>Connecting to the CQP server...';
       await cqiClient.connect('anonymous', '');
+      statusTextElement.innerText += ' Done';
+      statusTextElement.innerHTML += '<br>Building and receiving corpus data cache from the server (This may take a while)...';
       const cqiCorpus = await cqiClient.corpora.get(`NOPAQUE-${this.corpusId.toUpperCase()}`);
+      statusTextElement.innerText += ' Done';
       // TODO: Don't do this hgere
       await cqiCorpus.updateDb();
       this.data.cqiClient = cqiClient;
       this.data.cqiCorpus = cqiCorpus;
       this.data.corpus = {o: cqiCorpus};  // legacy
     } catch (error) {
-      // TODO: Currently we can only handle CQiErrors here,
-      //       but we should also handle other errors.
-      const errorString = `${error.code}: ${error.constructor.name}`;
+      let errorString = '';
+      if ('code' in error) {errorString += `[${error.code}] `;}
+      errorString += `${error.constructor.name}`;
+      if ('description' in error) {errorString += `: ${error.description}`;}
       const errorsElement = this.elements.initModal.querySelector('.errors');
       const progressElement = this.elements.initModal.querySelector('.progress');
       errorsElement.innerText = errorString;
@@ -51,7 +62,11 @@ class CorpusAnalysisApp {
     }
 
     // Initialize extensions
-    for (const extension of Object.values(this.extensions)) {extension.init();}
+    for (const extension of Object.values(this.extensions)) {
+      statusTextElement.innerHTML += `<br>Initializing extension ${extension.name}...`;
+      await extension.init();
+      statusTextElement.innerText += ' Done'
+    }
     for (const extensionSelectorElement of this.elements.extensionCards.querySelectorAll('.extension-selector')) {
       extensionSelectorElement.addEventListener('click', () => {
         this.elements.m.extensionTabs.select(extensionSelectorElement.dataset.target);
diff --git a/app/static/js/CorpusAnalysis/CorpusAnalysisConcordance.js b/app/static/js/CorpusAnalysis/CorpusAnalysisConcordance.js
index e6c00e73..11ac9d2f 100644
--- a/app/static/js/CorpusAnalysis/CorpusAnalysisConcordance.js
+++ b/app/static/js/CorpusAnalysis/CorpusAnalysisConcordance.js
@@ -30,62 +30,60 @@ class CorpusAnalysisConcordance {
     this.app.registerExtension(this);
   }
 
-  init() {
+  async submitForm() {
+    this.app.disableActionElements();
+    let query = this.elements.form.query.value.trim();
+    let subcorpusName = this.elements.form['subcorpus-name'].value;
+    this.elements.error.innerText = '';
+    this.elements.error.classList.add('hide');
+    this.elements.progress.classList.remove('hide');
+    try {
+      const subcorpus = {};
+      subcorpus.q = query;
+      subcorpus.selectedItems = new Set();
+      await this.data.corpus.o.query(subcorpusName, query);
+      if (subcorpusName !== 'Last') {this.data.subcorpora.Last = subcorpus;}
+      const cqiSubcorpus = await this.data.corpus.o.subcorpora.get(subcorpusName);
+      subcorpus.o = cqiSubcorpus;
+      const paginatedSubcorpus = await cqiSubcorpus.paginate(this.settings.context, 1, this.settings.perPage);
+      subcorpus.p = paginatedSubcorpus;
+      this.data.subcorpora[subcorpusName] = subcorpus;
+      this.settings.selectedSubcorpus = subcorpusName;
+      this.renderSubcorpusList();
+      this.renderSubcorpusInfo();
+      this.renderSubcorpusActions();
+      this.renderSubcorpusItems();
+      this.renderSubcorpusPagination();
+      this.elements.progress.classList.add('hide');
+    } catch (error) {
+      let errorString = '';
+      if ('code' in error) {errorString += `[${error.code}] `;}
+      errorString += `${error.constructor.name}`;
+      this.elements.error.innerText = errorString;
+      this.elements.error.classList.remove('hide');
+      app.flash(errorString, 'error');
+      this.elements.progress.classList.add('hide');
+    }
+    this.app.enableActionElements();
+  }
+
+  async init() {
     // Init data
     this.data.corpus = this.app.data.corpus;
     this.data.subcorpora = {};
     // Add event listeners
-    this.elements.form.addEventListener('submit', event => {
+    this.elements.form.addEventListener('submit', (event) => {
       event.preventDefault();
-      this.app.disableActionElements();
-      let query = this.elements.form.query.value.trim();
-      let subcorpusName = this.elements.form['subcorpus-name'].value;
-      this.elements.error.innerText = '';
-      this.elements.error.classList.add('hide');
-      this.elements.progress.classList.remove('hide');
-      let subcorpus = {};
-      this.data.corpus.o.query(subcorpusName, query)
-        .then((cqiStatus) => {
-          subcorpus.q = query;
-          subcorpus.selectedItems = new Set();
-          if (subcorpusName !== 'Last') {this.data.subcorpora.Last = subcorpus;}
-          return this.data.corpus.o.subcorpora.get(subcorpusName);
-        })
-        .then((cqiSubcorpus) => {
-          subcorpus.o = cqiSubcorpus;
-          return cqiSubcorpus.paginate(this.settings.context, 1, this.settings.perPage);
-        })
-        .then(
-          (paginatedSubcorpus) => {
-            subcorpus.p = paginatedSubcorpus;
-            this.data.subcorpora[subcorpusName] = subcorpus;
-            this.settings.selectedSubcorpus = subcorpusName;
-            this.renderSubcorpusList();
-            this.renderSubcorpusInfo();
-            this.renderSubcorpusActions();
-            this.renderSubcorpusItems();
-            this.renderSubcorpusPagination();
-            this.elements.progress.classList.add('hide');
-            this.app.enableActionElements();
-          },
-          (cqiError) => {
-            let errorString = `${cqiError.code}: ${cqiError.constructor.name}`;
-            this.elements.error.innerText = errorString;
-            this.elements.error.classList.remove('hide');
-            app.flash(errorString, 'error');
-            this.elements.progress.classList.add('hide');
-            this.app.enableActionElements();
-          }
-        );
+      this.submitForm();
     });
-    this.elements.form.addEventListener('change', event => {
+    this.elements.form.addEventListener('change', (event) => {
       if (event.target === this.elements.form['context']) {
         this.settings.context = parseInt(this.elements.form['context'].value);
-        this.elements.form.submit.click();
+        this.submitForm();
       }
       if (event.target === this.elements.form['per-page']) {
         this.settings.perPage = parseInt(this.elements.form['per-page'].value);
-        this.elements.form.submit.click();
+        this.submitForm();
       }
       if (event.target === this.elements.form['text-style']) {
         this.settings.textStyle = parseInt(this.elements.form['text-style'].value);
@@ -161,7 +159,7 @@ class CorpusAnalysisConcordance {
       </a>
     `.trim();
     M.Tooltip.init(this.elements.subcorpusActions.querySelectorAll('.tooltipped'));
-    this.elements.subcorpusActions.querySelector('.subcorpus-export-trigger').addEventListener('click', event => {
+    this.elements.subcorpusActions.querySelector('.subcorpus-export-trigger').addEventListener('click', (event) => {
       event.preventDefault();
       let subcorpus = this.data.subcorpora[this.settings.selectedSubcorpus];
       let modalElementId = Utils.generateElementId('export-subcorpus-modal-');
@@ -218,7 +216,7 @@ class CorpusAnalysisConcordance {
           }
         }
       );
-      exportButton.addEventListener('click', event => {
+      exportButton.addEventListener('click', (event) => {
         event.preventDefault();
         this.app.disableActionElements();
         this.elements.progress.classList.remove('hide');
@@ -240,7 +238,7 @@ class CorpusAnalysisConcordance {
           promise = subcorpus.o.export(50);
         }
         promise.then(
-          data => {
+          (data) => {
             let blob;
             if (exportFormat === 'csv') {
               let csvContent = 'sep=,\r\n';
@@ -286,7 +284,7 @@ class CorpusAnalysisConcordance {
       });
       modal.open();
     });
-    this.elements.subcorpusActions.querySelector('.subcorpus-delete-trigger').addEventListener('click', event => {
+    this.elements.subcorpusActions.querySelector('.subcorpus-delete-trigger').addEventListener('click', (event) => {
       event.preventDefault();
       let subcorpus = this.data.subcorpora[this.settings.selectedSubcorpus];
       subcorpus.o.drop().then(
@@ -362,7 +360,7 @@ class CorpusAnalysisConcordance {
     this.setTextStyle();
     this.setTokenRepresentation();
     for (let gotoReaderTriggerElement of this.elements.subcorpusItems.querySelectorAll('.goto-reader-trigger')) {
-      gotoReaderTriggerElement.addEventListener('click', event => {
+      gotoReaderTriggerElement.addEventListener('click', (event) => {
         event.preventDefault();
         let corpusAnalysisReader = this.app.extensions.Reader;
         let itemId = parseInt(gotoReaderTriggerElement.closest('.item').dataset.id);
@@ -384,7 +382,7 @@ class CorpusAnalysisConcordance {
       });
     }
     for (let selectTriggerElement of this.elements.subcorpusItems.querySelectorAll('.select-trigger')) {
-      selectTriggerElement.addEventListener('click', event => {
+      selectTriggerElement.addEventListener('click', (event) => {
         event.preventDefault();
         let itemElement = selectTriggerElement.closest('.item');
         let itemId = parseInt(itemElement.dataset.id);
@@ -446,14 +444,14 @@ class CorpusAnalysisConcordance {
       </li>
     `.trim();
     for (let paginationTriggerElement of this.elements.subcorpusPagination.querySelectorAll('.pagination-trigger[data-target]')) {
-      paginationTriggerElement.addEventListener('click', event => {
+      paginationTriggerElement.addEventListener('click', (event) => {
         event.preventDefault();
         this.app.disableActionElements();
         this.elements.progress.classList.remove('hide');
         let page = parseInt(paginationTriggerElement.dataset.target);
         subcorpus.o.paginate(page, this.settings.perPage, this.settings.context)
           .then(
-            paginatedSubcorpus => {
+            (paginatedSubcorpus) => {
               subcorpus.p = paginatedSubcorpus;
               this.renderSubcorpusItems();
               this.renderSubcorpusPagination();
diff --git a/app/static/js/CorpusAnalysis/CorpusAnalysisReader.js b/app/static/js/CorpusAnalysis/CorpusAnalysisReader.js
index 3e73551d..eb63cae9 100644
--- a/app/static/js/CorpusAnalysis/CorpusAnalysisReader.js
+++ b/app/static/js/CorpusAnalysis/CorpusAnalysisReader.js
@@ -29,39 +29,42 @@ class CorpusAnalysisReader {
     this.app.registerExtension(this);
   }
 
-  init() {
+  async submitForm() {
+    this.app.disableActionElements();
+    this.elements.error.innerText = '';
+    this.elements.error.classList.add('hide');
+    this.elements.progress.classList.remove('hide');
+    try {
+      const paginatedCorpus = await this.data.corpus.o.paginate(1, this.settings.perPage);
+      this.data.corpus.p = paginatedCorpus;
+      this.renderCorpus();
+      this.renderCorpusPagination();
+      this.elements.progress.classList.add('hide');
+    } catch (error) {
+      let errorString = '';
+      if ('code' in error) {errorString += `[${error.code}] `;}
+      errorString += `${error.constructor.name}`;
+      if ('description' in error) {errorString += `: ${error.description}`;}
+      this.elements.error.innerText = errorString;
+      this.elements.error.classList.remove('hide');
+      app.flash(errorString, 'error');
+      this.elements.progress.classList.add('hide');
+    }
+    this.app.enableActionElements();
+  }
+
+  async init() {
     // Init data
     this.data.corpus = this.app.data.corpus;
     // Add event listeners
     this.elements.form.addEventListener('submit', (event) => {
       event.preventDefault();
-      this.app.disableActionElements();
-      this.elements.error.innerText = '';
-      this.elements.error.classList.add('hide');
-      this.elements.progress.classList.remove('hide');
-      this.data.corpus.o.paginate(1, this.settings.perPage)
-        .then(
-          (paginatedCorpus) => {
-            this.data.corpus.p = paginatedCorpus;
-            this.renderCorpus();
-            this.renderCorpusPagination();
-            this.elements.progress.classList.add('hide');
-            this.app.enableActionElements();
-          },
-          (cqiError) => {
-            let errorString = `${cqiError.code}: ${cqiError.constructor.name}`;
-            this.elements.error.innerText = errorString;
-            this.elements.error.classList.remove('hide');
-            app.flash(errorString, 'error');
-            this.elements.progress.classList.add('hide');
-            this.app.enableActionElements();
-          }
-        );
+      this.submitForm();
     });
-    this.elements.form.addEventListener('change', event => {
+    this.elements.form.addEventListener('change', (event) => {
       if (event.target === this.elements.form['per-page']) {
         this.settings.perPage = parseInt(this.elements.form['per-page'].value);
-        this.elements.form.submit.click();
+        this.submitForm();
       }
       if (event.target === this.elements.form['text-style']) {
         this.settings.textStyle = parseInt(this.elements.form['text-style'].value);
@@ -73,7 +76,7 @@ class CorpusAnalysisReader {
       }
     });
     // Load initial data
-    this.elements.form.submit.click();
+    await this.submitForm();
   }
 
   clearCorpus() {
@@ -205,7 +208,7 @@ class CorpusAnalysisReader {
     this.elements.corpusPagination.appendChild(pageElement);
     
     for (let paginateTriggerElement of this.elements.corpusPagination.querySelectorAll('.pagination-trigger[data-target]')) {
-      paginateTriggerElement.addEventListener('click', event => {
+      paginateTriggerElement.addEventListener('click', (event) => {
         event.preventDefault();
         let page = parseInt(paginateTriggerElement.dataset.target);
         this.page(page);
diff --git a/app/static/js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js b/app/static/js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js
index 81e1d466..0bd5b78e 100644
--- a/app/static/js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js
+++ b/app/static/js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js
@@ -13,7 +13,7 @@ class CorpusAnalysisStaticVisualization {
     this.app.registerExtension(this);
   }
 
-  init() {
+  async init() {
     // Init data
     this.data.corpus = this.app.data.corpus;
     this.renderGeneralCorpusInfo();
diff --git a/app/templates/corpora/analysis.html.j2 b/app/templates/corpora/analysis.html.j2
index 0e261d2c..bb9e4e68 100644
--- a/app/templates/corpora/analysis.html.j2
+++ b/app/templates/corpora/analysis.html.j2
@@ -53,6 +53,7 @@
     <div class="progress">
       <div class="indeterminate"></div>
     </div>
+    <p class="status-text"></p>
     <p class="errors error-color-text hide"></p>
   </div>
 </div>
-- 
GitLab