From 43f79291aad8448a72af60818613856a926b8c7c Mon Sep 17 00:00:00 2001
From: Stephan Porada <sporada@uni-bielefeld.de>
Date: Wed, 8 Jul 2020 11:35:47 +0200
Subject: [PATCH] Unify job input tables

---
 web/app/jobs/tables.py             | 28 ------------
 web/app/jobs/views.py              | 14 +++---
 web/app/static/js/nopaque.lists.js | 69 +++++++++++++++++++++++++++---
 web/app/templates/jobs/job.html.j2 | 34 +++++++--------
 4 files changed, 82 insertions(+), 63 deletions(-)
 delete mode 100644 web/app/jobs/tables.py

diff --git a/web/app/jobs/tables.py b/web/app/jobs/tables.py
deleted file mode 100644
index aeac3319..00000000
--- a/web/app/jobs/tables.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from flask_table import Table, Col, LinkCol
-
-
-class JobInputTable(Table):
-    """
-    Declares the table describing colum by column.
-    """
-    classes = ['highlight', 'responsive-table']
-    filename = Col('Filename', column_html_attrs={'class': 'filename'},
-                   th_html_attrs={'class': 'sort',
-                                  'data-sort': 'filename'})
-    url = LinkCol('Download', 'jobs.download_job_input',
-                  url_kwargs=dict(job_id='job.id',
-                                  job_input_id='input_id'),
-                  anchor_attrs={'class': 'waves-effect waves-light btn-floating',
-                                'download': ''},
-                  text_fallback='<i class="material-icons">file_download</i>')
-
-
-class JobInputItem(object):
-    """
-    Describes one item like one row per table.
-    """
-
-    def __init__(self, filename, job, input_id):
-        self.filename = filename
-        self.job = job
-        self.input_id = input_id
diff --git a/web/app/jobs/views.py b/web/app/jobs/views.py
index 6e66b211..f8f9f6a9 100644
--- a/web/app/jobs/views.py
+++ b/web/app/jobs/views.py
@@ -3,10 +3,8 @@ from flask import (abort, current_app, flash, redirect, render_template,
 from flask_login import current_user, login_required
 from . import jobs
 from . import tasks
-from . tables import JobInputItem, JobInputTable
 from ..models import Job, JobInput, JobResult
 import os
-import html
 
 
 @jobs.route('/<int:job_id>')
@@ -15,15 +13,13 @@ def job(job_id):
     job = Job.query.get_or_404(job_id)
     if not (job.creator == current_user or current_user.is_administrator()):
         abort(403)
-    items = [JobInputItem(input.filename, job, input.id)
-             for input in job.inputs]
-    # Convert table object to html string and unescape <>& for al little hack to use icons in buttons
-    job_input_table = html.unescape(JobInputTable(items).__html__())
-    # Add class "list" to tbody element. Needed for "List.js"
-    job_input_table = job_input_table.replace('tbody', 'tbody class="list"', 1)
+    job_inputs = [dict(filename=input.filename,
+                       id=input.id,
+                       job_id=job.id)
+                  for input in job.inputs]
     return render_template('jobs/job.html.j2',
                            job=job,
-                           job_input_table=job_input_table,
+                           job_inputs=job_inputs,
                            title='Job')
 
 
diff --git a/web/app/static/js/nopaque.lists.js b/web/app/static/js/nopaque.lists.js
index 2c3aeb6c..a2584e71 100644
--- a/web/app/static/js/nopaque.lists.js
+++ b/web/app/static/js/nopaque.lists.js
@@ -1,6 +1,6 @@
 class RessourceList extends List {
   constructor(idOrElement, subscriberList, type, options={}) {
-    if (!["corpus", "job", "result", "user"].includes(type)) {
+    if (!["corpus", "job", "result", "user", "job_input"].includes(type)) {
       console.error("Unknown Type!");
       return;
     }
@@ -63,6 +63,15 @@ class RessourceList extends List {
 
 
 RessourceList.dataMapper = {
+  // ### Mapping Genera Info
+  //The Mapping describes entitys rendered per row. One key value pair holds
+  // the data to be rendered in the list.js table. Key has to correspond
+  // with the ValueNames defined below in RessourceList.options ValueNames.
+  // Links are declared with double ticks(") around them. The key for links
+  // have to correspond with the class of an <a> element in the
+  // RessourceList.options item blueprint.
+
+  // Mapping for corpus entities shown in the dashboard table.
   corpus: corpus => ({creation_date: corpus.creation_date,
                       description: corpus.description,
                       id: corpus.id,
@@ -70,6 +79,7 @@ RessourceList.dataMapper = {
                       "edit-link": `/corpora/${corpus.id}`,
                       status: corpus.status,
                       title: corpus.title}),
+  // Mapping for job entities shown in the dashboard table.
   job: job => ({creation_date: job.creation_date,
                 description: job.description,
                 id: job.id,
@@ -77,6 +87,12 @@ RessourceList.dataMapper = {
                 service: job.service,
                 status: job.status,
                 title: job.title}),
+  // Mapping for job input files shown in table on every job page
+  job_input: job_input => ({filename: job_input.filename,
+                            id: job_input.job_id,
+                            "download-link": `${job_input.job_id}/inputs/${job_input.id}/download`}),
+  // Mapping for imported result entities from corpus analysis.
+  // Shown in imported results table
   result: result => ({ query: result.query,
                        match_count: result.match_count,
                        corpus_name: result.corpus_name,
@@ -86,6 +102,7 @@ RessourceList.dataMapper = {
                        "details-link": `${result.id}/details`,
                        "inspect-link": `${result.id}/inspect`,
                        "delete-modal": `delete-result-${result.id}-modal`}),
+  // Mapping for user entities shown in admin table
   user: user => ({username: user.username,
                   email: user.email,
                   role_id: user.role_id,
@@ -96,7 +113,9 @@ RessourceList.dataMapper = {
 
 
 RessourceList.options = {
+  // common list.js options for 4 rows per page etc.
   common: {page: 4, pagination: {innerWindow: 8, outerWindow: 1}},
+  // extended list.js options for 10 rows per page etc.
   extended: {page: 10,
                  pagination: [
                  {
@@ -111,6 +130,8 @@ RessourceList.options = {
                     outerWindow: 1
                   }
                 ]},
+  // Corpus entity blueprint setting html strucuture per entity per row
+  // Link classes have to correspond with Links defined in the Mapping process
   corpus: {item: `<tr>
                     <td>
                       <a class="btn-floating disabled">
@@ -134,11 +155,18 @@ RessourceList.options = {
                       </a>
                     </td>
                   </tr>`,
-           valueNames: ["creation_date", "description", "title",
+  // Corpus Value Names per column. Have to correspond with the keys from the
+  // Mapping step above.
+           valueNames: ["creation_date",
+                        "description",
+                        "title",
                         {data: ["id"]},
                         {name: "analyse-link", attr: "href"},
                         {name: "edit-link", attr: "href"},
-                        {name: "status", attr: "data-status"}]},
+                        {name: "status", attr: "data-status"}]
+           },
+  // Job entity blueprint setting html strucuture per entity per row
+  // Link classes have to correspond with Links defined in the Mapping process
   job: {item: `<tr>
                  <td>
                    <a class="btn-floating disabled">
@@ -158,11 +186,30 @@ RessourceList.options = {
                   </a>
                  </td>
                </tr>`,
-        valueNames: ["creation_date", "description", "title",
+  // Job Value Names per column. Have to correspond with the keys from the
+  // Mapping step above.
+        valueNames: ["creation_date",
+                     "description",
+                     "title",
                      {data: ["id"]},
                      {name: "link", attr: "href"},
                      {name: "service", attr: "data-service"},
-                     {name: "status", attr: "data-status"}]},
+                     {name: "status", attr: "data-status"}]
+        },
+  job_input: {item : `<tr>
+                        <td class="filename"></td>
+                        <td class="actions">
+                          <a class="btn-floating download-link waves-effect waves-light"><i class="material-icons">file_download</i>
+                          </a>
+                        </td>
+                      </tr>`,
+              valueNames: ["filename",
+                           "id",
+                           {name: "download-link", attr: "href"}]
+              },
+  // Result (imported from corpus analysis) entity blueprint setting html
+  // strucuture per entity per row
+  // Link classes have to correspond with Links defined in the Mapping process
   result: {item: `<tr>
                     <td class="query"></td>
                     <td class="match_count"></td>
@@ -179,6 +226,8 @@ RessourceList.options = {
                       </a>
                     </td>
                   </tr>`,
+  // Result Value Names per column. Have to correspond with keys from the
+  // Mapping step above.
            valueNames: ["query",
                         "match_count",
                         "corpus_name",
@@ -187,7 +236,10 @@ RessourceList.options = {
                         "corpus_type",
                         {name: "details-link", attr: "href"},
                         {name: "inspect-link", attr: "href"},
-                        {name: "delete-modal", attr: "data-target"}]},
+                        {name: "delete-modal", attr: "data-target"}]
+           },
+  // User entity blueprint setting html strucuture per entity per row
+  // Link classes have to correspond with Links defined in the Mapping process
   user: {item: `<tr>
                   <td class="username"></td>
                   <td class="email"></td>
@@ -199,12 +251,15 @@ RessourceList.options = {
                   </a>
                   </td>
                 </tr>`,
+  // User Value Names per column. Have to correspond with keys from the
+  // Mapping step above.
          valueNames: ["username",
                       "email",
                       "role_id",
                       "confirmed",
                       "id",
-                      {name: "profile-link", attr: "href"}]}
+                      {name: "profile-link", attr: "href"}]
+         }
 };
 
 
diff --git a/web/app/templates/jobs/job.html.j2 b/web/app/templates/jobs/job.html.j2
index bc146caa..b264cb52 100644
--- a/web/app/templates/jobs/job.html.j2
+++ b/web/app/templates/jobs/job.html.j2
@@ -86,9 +86,18 @@
           <p>Original input files.</p>
         </div>
         <div class="col s12 m10">
-          <div class="inputs row">
+          <div class="row">
             <ul class="pagination paginationTop"></ul>
-            {{ job_input_table }}
+            <table class="highlight responsive-table">
+              <thead>
+                <tr>
+                  <th class="sort" data-sort="filename">Filename</th>
+                  <th>{# Actions #}</th>
+                </tr>
+              </thead>
+              <tbody class="list">
+              </tbody>
+            </table>
             <ul class="pagination paginationBottom"></ul>
           </div>
         </div>
@@ -149,23 +158,10 @@
 
 <script>
 // job_input_table code
-var options = {page: 5,
-               pagination: [
-                 {
-                   name: "paginationTop",
-                   paginationClass: "paginationTop",
-                   innerWindow: 8,
-                   outerWindow: 1
-                 },
-                 {
-                    paginationClass: "paginationBottom",
-                    innerWindow: 8,
-                    outerWindow: 1
-                  }
-                ],
-               valueNames: ['filename']};
-  var jobInputList = new List('inputs', options);
-
+  var ressources = {{ job_inputs|tojson|safe }};
+  console.log(ressources);
+  var jobInputsList = new RessourceList("inputs", null, "job_input");
+  jobInputsList.addRessources(ressources);
 
   class InformationUpdater {
     constructor(jobId, foreignJobFlag) {
-- 
GitLab