From 9904e54c51f7f47012856f71cd4f8da78b023425 Mon Sep 17 00:00:00 2001
From: Patrick Jentsch <p.jentsch@uni-bielefeld.de>
Date: Thu, 15 Dec 2022 09:53:19 +0100
Subject: [PATCH] Add dashboard2

---
 app/main/routes.py                    |   6 +
 app/models.py                         |  16 ++-
 app/templates/main/dashboard2.html.j2 | 178 ++++++++++++++++++++++++++
 3 files changed, 194 insertions(+), 6 deletions(-)
 create mode 100644 app/templates/main/dashboard2.html.j2

diff --git a/app/main/routes.py b/app/main/routes.py
index 1e7665a3..7e42d60b 100644
--- a/app/main/routes.py
+++ b/app/main/routes.py
@@ -30,6 +30,12 @@ def dashboard():
     return render_template('main/dashboard.html.j2', title='Dashboard')
 
 
+@bp.route('/dashboard2')
+@login_required
+def dashboard2():
+    return render_template('main/dashboard2.html.j2', title='Dashboard')
+
+
 @bp.route('/user_manual')
 def user_manual():
     return render_template('main/user_manual.html.j2', title='User manual')
diff --git a/app/models.py b/app/models.py
index d28331a0..aa5b2faa 100644
--- a/app/models.py
+++ b/app/models.py
@@ -226,6 +226,7 @@ class Role(HashidMixin, db.Model):
             db.session.add(role)
         db.session.commit()
 
+
 class Token(db.Model):
     __tablename__ = 'tokens'
     # Primary key
@@ -249,31 +250,33 @@ class Token(db.Model):
         yesterday = datetime.utcnow() - timedelta(days=1)
         Token.query.filter(Token.refresh_expiration < yesterday).delete()
 
+
 class Avatar(HashidMixin, FileMixin, db.Model):
     __tablename__ = 'avatars'
     # Primary key
     id = db.Column(db.Integer, primary_key=True)
     # Foreign keys
     user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
+    # Backrefs: user: User
     
     @property
     def path(self):
         return os.path.join(self.user.path, 'avatar')
-    
+
     def delete(self):
         try:
             os.remove(self.path)
         except OSError as e:
             current_app.logger.error(e)
         db.session.delete(self)
-    
+
+
 class User(HashidMixin, UserMixin, db.Model):
     __tablename__ = 'users'
     # Primary key
     id = db.Column(db.Integer, primary_key=True)
     # Foreign keys
     role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
-    # privacy_id = db.Column(db.Integer, db.ForeignKey('privacies.id'))
     # Fields
     email = db.Column(db.String(254), index=True, unique=True)
     username = db.Column(db.String(64), index=True, unique=True)
@@ -555,7 +558,6 @@ class User(HashidMixin, UserMixin, db.Model):
             'show_email': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL),
             'show_last_seen': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_LAST_SEEN),
             'show_member_since': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_MEMBER_SINCE)
-
         }
         if backrefs:
             json_serializeable['role'] = \
@@ -579,6 +581,7 @@ class User(HashidMixin, UserMixin, db.Model):
             }
         return json_serializeable
 
+
 class TesseractOCRPipelineModel(FileMixin, HashidMixin, db.Model):
     __tablename__ = 'tesseract_ocr_pipeline_models'
     # Primary key
@@ -869,7 +872,7 @@ class JobInput(FileMixin, HashidMixin, db.Model):
 
     @property
     def user_id(self):
-        return self.job.user_id
+        return self.job.user.id
 
     def to_json_serializeable(self, backrefs=False, relationships=False):
         json_serializeable = {
@@ -925,7 +928,7 @@ class JobResult(FileMixin, HashidMixin, db.Model):
 
     @property
     def user_id(self):
-        return self.job.user_id
+        return self.job.user.id
 
     def to_json_serializeable(self, backrefs=False, relationships=False):
         json_serializeable = {
@@ -1166,6 +1169,7 @@ class CorpusFile(FileMixin, HashidMixin, db.Model):
                 self.corpus.to_json_serializeable(backrefs=True)
         return json_serializeable
 
+
 class Corpus(HashidMixin, db.Model):
     '''
     Class to define a corpus.
diff --git a/app/templates/main/dashboard2.html.j2 b/app/templates/main/dashboard2.html.j2
new file mode 100644
index 00000000..47a7e236
--- /dev/null
+++ b/app/templates/main/dashboard2.html.j2
@@ -0,0 +1,178 @@
+{% extends "base.html.j2" %}
+{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %}
+
+{% block page_content %}
+<div class="corpus-list no-autoinit" id="corpus-list" data-user-id="{{ current_user.hashid }}">
+  <div class="parallax-container">
+    <div class="parallax"><img src="{{ url_for('static', filename='images/parallax_hq/canvas.png') }}"></div>
+    <div style="position: absolute; bottom: 0; width: 100%;">
+      <div class="container">
+        <div class="white" style="padding: 1px 35px 0 10px; border-radius: 35px;">
+          <div class="input-field">
+            <i class="material-icons prefix">search</i>
+            <input class="search" id="corpus-list-search" type="text">
+            <label for="corpus-list-search">Search Corpus</label>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <div class="section">
+    <div class="row">
+      <div class="col s1"></div>
+      <div class="col s3">
+          <h2 id="corpora">My Corpora</h1>
+          <p>Create a corpus to interactively perform linguistic analysis.</p>
+          <p>Or browse our users public corpora.<span class="new badge"></span></p>
+      </div>
+      <div class="col s7">
+        <div class="card">
+          <div class="card-content">
+            <div>
+              <table>
+                <thead>
+                  <tr>
+                    <th></th>
+                    <th>Title and Description</th>
+                    <th>Status</th>
+                    <th></th>
+                  </tr>
+                </thead>
+                <tbody class="list"></tbody>
+              </table>
+              <ul class="pagination"></ul>
+            </div>
+          </div>
+          <div class="card-action right-align">
+            <a class="btn disabled waves-effect waves-light" href="{{ url_for('corpora.import_corpus') }}">Import Corpus<i class="material-icons right">import_export</i></a>
+            <a class="btn waves-effect waves-light" href="{{ url_for('corpora.create_corpus') }}">Create corpus<i class="material-icons right">add</i></a>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="job-list no-autoinit" id="job-list" data-user-id="{{ current_user.hashid }}">
+  <div class="parallax-container">
+    <div class="parallax"><img src="{{ url_for('static', filename='images/parallax_hq/canvas.png') }}"></div>
+    <div style="position: absolute; bottom: 0; width: 100%;">
+      <div class="container">
+        <div class="white" style="padding: 1px 35px 0 10px; border-radius: 35px;">
+          <div class="input-field">
+            <i class="material-icons prefix">search</i>
+            <input class="search" id="job-list-search" type="text">
+            <label for="job-list-search">Search Job</label>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <div class="section">
+    <div class="row">
+      <div class="col s1"></div>
+      <div class="col s3">
+        <h2 id="jobs">My Jobs</h1>
+        <p>
+          A job is the execution of a service provided by nopaque. You can
+          create any number of jobs and let them be processed simultaneously. We
+          <b>strongly recommend</b> that you create a folder on your computer where you
+          save the various files that nopaque provides you with after each
+          pre-processing step. You will need the result of each step for the
+          next step.
+        </p>
+        <p><b>Where is my Job data?</b> Don't worry, please read <a href="{{ url_for('main.news', _anchor='april-2022-update') }}">this news</a> entry</p>
+      </div>
+      <div class="col s7">
+        <div class="card">
+          <div class="card-content">
+            <div>
+              <table>
+                <thead>
+                  <tr>
+                    <th></th>
+                    <th>Title and Description</th>
+                    <th>Status</th>
+                    <th></th>
+                  </tr>
+                </thead>
+                <tbody class="list"></tbody>
+              </table>
+              <ul class="pagination"></ul>
+            </div>
+          </div>
+          <div class="card-action right-align">
+            <a class="btn modal-trigger waves-effect waves-light" data-target="create-job-modal"><i class="material-icons left">add</i>Create job</a>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock page_content %}
+
+{% block modals %}
+{{ super() }}
+<div id="create-job-modal" class="modal">
+  <div class="modal-content">
+    <h4>Select a service</h4>
+    <p>&nbsp;</p>
+    <div class="row">
+      <div class="col s12 m4">
+          <div class="card-panel center-align hoverable">
+            <br>
+            <a href="{{ url_for('services.file_setup_pipeline') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
+              <i class="nopaque-icons service-color darken service-icon" data-service="file-setup-pipeline"></i>
+            </a>
+            <br><br>
+            <p class="service-color-text darken" data-service="file-setup-pipeline"><b>File setup</b></p>
+            <p class="light">Digital copies of text based research data (books, letters, etc.) often comprise various files and formats. nopaque converts and merges those files to facilitate further processing.</p>
+            <a href="{{ url_for('services.file_setup_pipeline') }}" class="waves-effect waves-light btn service-color darken" data-service="file-setup-pipeline">Create Job</a>
+          </div>
+      </div>
+      <div class="col s12 m4">
+          <div class="card-panel center-align hoverable">
+            <br>
+            <a href="{{ url_for('services.tesseract_ocr_pipeline') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
+              <i class="nopaque-icons service-color darken service-icon" data-service="tesseract-ocr-pipeline" style="font-size: 2.5rem;"></i>
+            </a>
+            <br><br>
+            <p class="service-color-text darken" data-service="tesseract-ocr-pipeline"><b>Optical Character Recognition</b></p>
+            <p class="light">nopaque converts your image data – like photos or scans – into text data through a process called OCR. This step enables you to proceed with further computational analysis of your documents.</p>
+            <a href="{{ url_for('services.tesseract_ocr_pipeline') }}" class="waves-effect waves-light btn service-color darken" data-service="tesseract-ocr-pipeline">Create Job</a>
+          </div>
+      </div>
+      <div class="col s12 m4">
+          <div class="card-panel center-align hoverable">
+            <br>
+            <a href="{{ url_for('services.spacy_nlp_pipeline') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);">
+              <i class="nopaque-icons service-color darken service-icon" data-service="spacy-nlp-pipeline" style="font-size: 2.5rem;"></i>
+            </a>
+            <br><br>
+            <p class="service-color-text darken" data-service="spacy-nlp-pipeline"><b>Natural Language Processing</b></p>
+            <p class="light">By means of computational linguistic data processing (tokenization, lemmatization, part-of-speech tagging and named-entity recognition) nopaque extracts additional information from your text.</p>
+            <a href="{{ url_for('services.spacy_nlp_pipeline') }}" class="waves-effect waves-light btn service-color darken" data-service="spacy-nlp-pipeline">Create Job</a>
+          </div>
+      </div>
+    </div>
+  </div>
+  <div class="modal-footer">
+    <a class="btn-flat modal-close waves-effect waves-light">Close</a>
+  </div>
+</div>
+{% endblock modals %}
+
+
+{% block scripts %}
+{{ super() }}
+<script>
+  let corpusListElement = document.querySelector('#corpus-list');
+  let corpusListOptions = {initialHtmlGenerator: null};
+  let corpusList = new CorpusList(corpusListElement, corpusListOptions);
+  let jobListElement = document.querySelector('#job-list');
+  let jobListOptions = {initialHtmlGenerator: null};
+  let jobList = new JobList(jobListElement, jobListOptions);
+</script>
+{% endblock scripts %}
-- 
GitLab