From cc60ad374ee9ccca98533ce8ab44ee74c7f0fb2e Mon Sep 17 00:00:00 2001
From: osmala <ossi.laine@utu.fi>
Date: Wed, 10 Jun 2020 10:37:00 +0300
Subject: [PATCH] Error handling

---
 .../templates/experiment_statistics.html      |  3 +-
 app/experiment/views.py                       | 51 ++++++++++---------
 app/static/css/main.css                       |  5 ++
 app/static/js/getCSV.js                       | 10 +++-
 app/utils.py                                  | 12 +++--
 5 files changed, 50 insertions(+), 31 deletions(-)

diff --git a/app/experiment/templates/experiment_statistics.html b/app/experiment/templates/experiment_statistics.html
index 2dd5a34..2054314 100644
--- a/app/experiment/templates/experiment_statistics.html
+++ b/app/experiment/templates/experiment_statistics.html
@@ -45,7 +45,8 @@
         </button>
 
         <div id="export-link-container" class="hidden">
-          <a id="export-link" class="float-right" href="{{ url_for('experiment.download_csv', exp_id=exp.idexperiment) }}" role="button">Download</a>
+          <a id="export-link" class="float-right" href="{{ url_for('experiment.download_csv', exp_id=exp.idexperiment) }}" role="button"></a>
+          <p id="export-error"></p>
         </div>
 
 
diff --git a/app/experiment/views.py b/app/experiment/views.py
index 462db81..623b1fc 100644
--- a/app/experiment/views.py
+++ b/app/experiment/views.py
@@ -1015,8 +1015,27 @@ def statistics():
                            finished_ratings=finished_ratings,
                            question_headers=question_headers,
                            stimulus_headers=stimulus_headers,
-                           embody_questions=embody_questions
-                           )
+                           embody_questions=embody_questions)
+
+
+@experiment_blueprint.route('/download_csv')
+def download_csv():
+    exp_id = request.args.get('exp_id', None)
+    path = request.args.get('path', None)
+
+    filename = "experiment_{}_{}.csv".format(
+        exp_id, date.today().strftime("%Y-%m-%d"))
+
+    path = '/tmp/' + path
+
+    try:
+        return send_file(path,
+                         mimetype='text/csv',
+                         as_attachment=True,
+                         attachment_filename=filename)
+
+    finally:
+        os.remove(path)
 
 
 def remove_rows(rows):
@@ -1061,10 +1080,11 @@ def create_embody(meta):
 
     exp_id = meta["exp_id"]
 
-    csv = generate_csv(exp_id)
+    data = generate_csv(exp_id)
 
-    if not csv:
-        emit('timeout')
+    if isinstance(data, Exception):
+        emit('timeout', {'exc': str(data)})
+        return
 
     filename = "experiment_{}_{}".format(
         exp_id, date.today().strftime("%Y-%m-%d"))
@@ -1075,7 +1095,7 @@ def create_embody(meta):
     print(path)
 
     with os.fdopen(fd, 'w') as tmp:
-        tmp.write(csv)
+        tmp.write(data)
         tmp.flush()
 
     path = path.split('/')[-1]    
@@ -1084,22 +1104,3 @@ def create_embody(meta):
 
     # return saved_data_as_file(filename, csv)
 
-
-@experiment_blueprint.route('/download_csv')
-def download_csv():
-    exp_id = request.args.get('exp_id', None)
-    path = request.args.get('path', None)
-
-    filename = "experiment_{}_{}.csv".format(
-        exp_id, date.today().strftime("%Y-%m-%d"))
-
-    path = '/tmp/' + path
-
-    try:
-        return send_file(path,
-                         mimetype='text/csv',
-                         as_attachment=True,
-                         attachment_filename=filename)
-
-    finally:
-        os.remove(path)
diff --git a/app/static/css/main.css b/app/static/css/main.css
index 7267759..f0c53e7 100644
--- a/app/static/css/main.css
+++ b/app/static/css/main.css
@@ -82,3 +82,8 @@ body {
     margin-top: 20px;
     padding: 10px;
 }
+
+#export-error {
+    float:right;
+    color:red;
+}
diff --git a/app/static/js/getCSV.js b/app/static/js/getCSV.js
index 81243a3..a2d3463 100644
--- a/app/static/js/getCSV.js
+++ b/app/static/js/getCSV.js
@@ -10,6 +10,7 @@ $(document).ready(function()  {
 
     var exportLinkContainer = $("#export-link-container");
     var exportLink = $("#export-link");
+    var exportError = $("#export-error");
 
     // With sockets 
     function initConnection(socket) {
@@ -20,12 +21,19 @@ $(document).ready(function()  {
         });
 
         socket.on('progress', function(data) {
+            console.log(data)
             progressBar.width(100*(data.done/data.from) + '%')
         });
 
         socket.on('timeout', function(data) {
-            console.log("timeout error", data)
+            console.log("timeout error", data.exc)
             socket.disconnect()            
+            exportButton.text('Export results')
+            exportButton.removeClass('disabled')
+            progressBarContainer.addClass("hidden")
+
+            exportLinkContainer.removeClass("hidden")
+            exportError.text('Error: ' + data.exc)
         });
 
         socket.on('file_ready', function(file) {
diff --git a/app/utils.py b/app/utils.py
index a36b220..ebf43e4 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -1,9 +1,11 @@
 import os
 import tempfile
 import time
+import json
 from itertools import zip_longest
 import concurrent.futures
 from flask import send_file
+from flask_socketio import emit
 from app import app
 from app.models import background_question, background_question_answer, \
     page, question, answer_set, answer, embody_answer, embody_question
@@ -99,7 +101,7 @@ def map_answers_to_questions(answers, questions):
             break
 
         if question_matches_answer(question, current_answer):
-            results[nth_question] = current_answer.result
+            results[nth_question] = current_answer.result()
             nth_answer += 1
 
     return results
@@ -184,6 +186,8 @@ def generate_csv(exp_id):
     participants = list(filter(lambda participant: True if int(
         participant.answer_counter) > 0 else False, participants))
 
+    len_participants = len(participants)
+
     # We can use a with statement to ensure threads are cleaned up promptly
     with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
         # Start the load operations and mark each future with its URL
@@ -191,16 +195,16 @@ def generate_csv(exp_id):
             executor.submit(generate_answer_row, participant, pages, questions, embody_questions): participant
             for participant in participants}
 
-        for future in concurrent.futures.as_completed(future_to_answer):
+        for nth, future in enumerate(concurrent.futures.as_completed(future_to_answer)):
             # for testing purpose
             # answer_row = future_to_answer[future]
             try:
+                emit('progress', {'done': nth, 'from': len_participants})
                 data = future.result()
                 csv += data + '\r\n'
-            except TimeoutError:
-                return None
             except Exception as exc:
                 print('generated an exception: {}'.format(exc))
+                return exc
 
     return csv
 
-- 
GitLab