From f86f3f4fd5250420b8a45d2dff02c21ac3adb54b Mon Sep 17 00:00:00 2001
From: Patrick Jentsch <pjentsch@sfb1288inf-Laptop.fritz.box>
Date: Thu, 9 Jul 2020 15:07:43 +0200
Subject: [PATCH] Move file deletion to model methods. add restart job button.

---
 web/app/jobs/tasks.py              | 32 ++----------------------------
 web/app/jobs/views.py              |  2 +-
 web/app/models.py                  | 27 +++++++++++++++++++++++--
 web/app/templates/jobs/job.html.j2 |  5 ++++-
 4 files changed, 32 insertions(+), 34 deletions(-)

diff --git a/web/app/jobs/tasks.py b/web/app/jobs/tasks.py
index e17bf5b4..0daec416 100644
--- a/web/app/jobs/tasks.py
+++ b/web/app/jobs/tasks.py
@@ -1,9 +1,5 @@
-from time import sleep
-from .. import db, logger
 from ..decorators import background
 from ..models import Job
-import os
-import shutil
 
 
 @background
@@ -12,20 +8,7 @@ def delete_job(job_id, *args, **kwargs):
     with app.app_context():
         job = Job.query.get(job_id)
         if job is None:
-            return
-        if job.status not in ['complete', 'failed']:
-            job.status = 'canceling'
-            db.session.commit()
-            while job.status != 'canceled':
-                # In case the daemon handled a job in any way
-                if job.status != 'canceling':
-                    job.status = 'canceling'
-                    db.session.commit()
-                sleep(1)
-                db.session.refresh(job)
-        path = os.path.join(app.config['NOPAQUE_STORAGE'], str(job.user_id),
-                            'jobs', str(job.id))
-        shutil.rmtree(path, ignore_errors=True)
+            raise Exception('Could not find job with id {}'.format(job_id))
         job.delete()
 
 
@@ -35,16 +18,5 @@ def restart_job(job_id, *args, **kwargs):
     with app.app_context():
         job = Job.query.get(job_id)
         if job is None:
-            logger.warning('Job not found')
-            return
-        if job.status != 'failed':
-            logger.warning('Job not failed')
-            return
-        logger.warning('Restarted')
-        job_dir = os.path.join(app.config['NOPAQUE_STORAGE'],
-                               str(job.user_id),
-                               'jobs',
-                               str(job.id))
-        shutil.rmtree(os.path.join(job_dir, 'output'), ignore_errors=True)
-        shutil.rmtree(os.path.join(job_dir, 'pyflow.data'), ignore_errors=True)
+            raise Exception('Could not find job with id {}'.format(job_id))
         job.restart()
diff --git a/web/app/jobs/views.py b/web/app/jobs/views.py
index 16b6756d..ffc17dd9 100644
--- a/web/app/jobs/views.py
+++ b/web/app/jobs/views.py
@@ -56,7 +56,7 @@ def download_job_input(job_id, job_input_id):
 def restart(job_id):
     job = Job.query.get_or_404(job_id)
     if job.status != 'failed':
-        flash('Job can not be restarted!', 'job')
+        flash('Could not restart job: status is not "failed"', 'error')
     else:
         tasks.restart_job(job_id)
         flash('Job has been restarted!', 'job')
diff --git a/web/app/models.py b/web/app/models.py
index e2cf09fb..538322d9 100644
--- a/web/app/models.py
+++ b/web/app/models.py
@@ -2,9 +2,12 @@ from datetime import datetime
 from flask import current_app
 from flask_login import UserMixin, AnonymousUserMixin
 from itsdangerous import BadSignature, TimedJSONWebSignatureSerializer
+from time import sleep
 from werkzeug.security import generate_password_hash, check_password_hash
 from werkzeug.utils import secure_filename
 from . import db, login_manager
+import os
+import shutil
 
 
 class Permission:
@@ -364,7 +367,21 @@ class Job(db.Model):
         '''
         Delete the job and its inputs and results from the database.
         '''
-
+        if self.status not in ['complete', 'failed']:
+            self.status = 'canceling'
+            db.session.commit()
+            while self.status != 'canceled':
+                # In case the daemon handled a job in any way
+                if self.status != 'canceling':
+                    self.status = 'canceling'
+                    db.session.commit()
+                sleep(1)
+                db.session.refresh(self)
+        job_dir = os.path.join(current_app.config['NOPAQUE_STORAGE'],
+                               str(self.user_id),
+                               'jobs',
+                               str(self.id))
+        shutil.rmtree(job_dir, ignore_errors=True)
         db.session.delete(self)
         db.session.commit()
 
@@ -374,7 +391,13 @@ class Job(db.Model):
         '''
 
         if self.status != 'failed':
-            return
+            raise Exception('Could not restart job: status is not "failed"')
+        job_dir = os.path.join(current_app.config['NOPAQUE_STORAGE'],
+                               str(self.user_id),
+                               'jobs',
+                               str(self.id))
+        shutil.rmtree(os.path.join(job_dir, 'output'), ignore_errors=True)
+        shutil.rmtree(os.path.join(job_dir, 'pyflow.data'), ignore_errors=True)
         self.end_date = None
         self.status = 'submitted'
         db.session.commit()
diff --git a/web/app/templates/jobs/job.html.j2 b/web/app/templates/jobs/job.html.j2
index 50d87980..3c12a70f 100644
--- a/web/app/templates/jobs/job.html.j2
+++ b/web/app/templates/jobs/job.html.j2
@@ -71,7 +71,10 @@
       </div>
     </div>
     <div class="card-action right-align">
-      <a href="#" class="btn disabled waves-effect waves-light"><i class="material-icons left">settings</i>Export Parameters</a>
+      {% if current_user.is_administrator() and job.status == 'failed' %}
+      <a href="{{ url_for('jobs.restart', job_id=job.id) }}" class="btn waves-effect waves-light"><i class="material-icons left">repeat</i>Restart</a>
+      {% endif %}
+      <!-- <a href="#" class="btn disabled waves-effect waves-light"><i class="material-icons left">settings</i>Export Parameters</a> -->
       <a data-target="delete-job-modal" class="waves-effect waves-light btn red modal-trigger"><i class="material-icons left">delete</i>Delete</a>
     </div>
   </div>
-- 
GitLab