fix off-by-one error on the progress and fix vars in ToolExistsPrecondition
[enigma2.git] / lib / python / Components / Task.py
index aebca7324903e6b6cf2b96854367eb32c42c345c..07b9d3f5db8383fb32504b7450d2678c46f663e2 100644 (file)
@@ -46,32 +46,41 @@ class Job(object):
        def start(self, callback):
                assert self.callback is None
                self.callback = callback
+               self.restart()
+
+       def restart(self):
                self.status = self.IN_PROGRESS
                self.state_changed()
                self.runNext()
                sumTaskWeightings = sum([t.weighting for t in self.tasks])
-               self.weightScale = (self.end+1) / float(sumTaskWeightings)
+               self.weightScale = self.end / float(sumTaskWeightings)
 
        def runNext(self):
                if self.current_task == len(self.tasks):
-                       self.callback(self, [])
+                       cb = self.callback
+                       self.callback = None
                        self.status = self.FINISHED
                        self.state_changed()
+                       cb(self, None, [])
                else:
-                       self.tasks[self.current_task].run(self.taskCallback,self.task_progress_changed_CB)
+                       self.tasks[self.current_task].run(self.taskCallback, self.task_progress_changed_CB)
                        self.state_changed()
 
-       def taskCallback(self, res):
+       def taskCallback(self, task, res):
                if len(res):
                        print ">>> Error:", res
                        self.status = self.FAILED
                        self.state_changed()
-                       self.callback(self, res)
+                       self.callback(self, task, res)
                else:
                        self.state_changed();
                        self.current_task += 1
                        self.runNext()
 
+       def retry(self):
+               assert self.status == self.FAILED
+               self.restart()
+
        def abort(self):
                if self.current_task < len(self.tasks):
                        self.tasks[self.current_task].abort()
@@ -80,7 +89,7 @@ class Job(object):
                # some Jobs might have a better idea of how to cancel a job
                self.abort()
 
-class Task(object)     :
+class Task(object):
        def __init__(self, job, name):
                self.name = name
                self.immediate_preconditions = [ ]
@@ -97,6 +106,7 @@ class Task(object)   :
                self.cwd = "/tmp"
                self.args = [ ]
                self.task_progress_changed = None
+               self.output_line = ""
                job.addTask(self)
 
        def setCommandline(self, cmd, args):
@@ -123,7 +133,7 @@ class Task(object)  :
        def run(self, callback, task_progress_changed):
                failed_preconditions = self.checkPreconditions(True) + self.checkPreconditions(False)
                if len(failed_preconditions):
-                       callback(failed_preconditions)
+                       callback(self, failed_preconditions)
                        return
                self.prepare()
 
@@ -151,6 +161,15 @@ class Task(object) :
                pass
 
        def processOutput(self, data):
+               self.output_line += data
+               while True:
+                       i = self.output_line.find('\n')
+                       if i == -1:
+                               break
+                       self.processOutputLine(self.output_line[:i+1])
+                       self.output_line = self.output_line[i+1:]
+
+       def processOutputLine(self, line):
                pass
 
        def processFinished(self, returncode):
@@ -171,7 +190,8 @@ class Task(object)  :
                                if not postcondition.check(self):
                                        not_met.append(postcondition)
 
-               self.callback(not_met)
+               self.cleanup(not_met)
+               self.callback(self, not_met)
 
        def afterRun(self):
                pass
@@ -183,12 +203,19 @@ class Task(object)        :
                return self.__progress
 
        def setProgress(self, progress):
+               if progress > self.end:
+                       progress = self.end
+               if progress < 0:
+                       progress = 0
                print "progress now", progress
                self.__progress = progress
                self.task_progress_changed()
 
        progress = property(getProgress, setProgress)
 
+# The jobmanager will execute multiple jobs, each after another.
+# later, it will also support suspending jobs (and continuing them after reboot etc)
+# It also supports a notification when some error occured, and possibly a retry.
 class JobManager:
        def __init__(self):
                self.active_jobs = [ ]
@@ -206,14 +233,28 @@ class JobManager:
                                self.active_job = self.active_jobs.pop(0)
                                self.active_job.start(self.jobDone)
 
-       def jobDone(self, job, problems):
-               print "job", job, "completed with", problems
+       def jobDone(self, job, task, problems):
+               print "job", job, "completed with", problems, "in", task
                if problems:
-                       self.failed_jobs.append(self.active_job)
+                       from Tools import Notifications
+                       from Screens.MessageBox import MessageBox
+                       Notifications.AddNotificationWithCallback(self.errorCB, MessageBox, _("Error: %s\nRetry?") % (problems[0].getErrorMessage(task)))
+                       return
+                       #self.failed_jobs.append(self.active_job)
 
                self.active_job = None
                self.kick()
 
+       def errorCB(self, answer):
+               if answer:
+                       print "retrying job"
+                       self.active_job.retry()
+               else:
+                       print "not retrying job."
+                       self.failed_jobs.append(self.active_job)
+                       self.active_job = None
+                       self.kick()
+
 # some examples:
 #class PartitionExistsPostcondition:
 #      def __init__(self, device):
@@ -248,28 +289,51 @@ class JobManager:
 #              if filesystem is not None:
 #                      self.args += ["-t", filesystem]
 #              self.args.append(device + "part%d" % partition)
-#
-#class DiskspacePrecondition:
-#      def __init__(self, diskspace_required):
-#              self.diskspace_required = diskspace_required
-#
-#      def check(self, task):
-#              return getFreeDiskspace(task.workspace) >= self.diskspace_required
-#
 
-class ToolExistsPrecondition:
+class Condition:
+       RECOVERABLE = False
+
+       def getErrorMessage(self, task):
+               return _("An error has occured. (%s)") % (self.__class__.__name__)
+
+class WorkspaceExistsPrecondition(Condition):
+       def check(self, task):
+               return os.access(task.job.workspace, os.W_OK)
+
+class DiskspacePrecondition(Condition):
+       def __init__(self, diskspace_required):
+               self.diskspace_required = diskspace_required
+               self.diskspace_available = None
+
+       def check(self, task):
+               import os
+               try:
+                       s = os.statvfs(task.job.workspace)
+                       self.diskspace_available = s.f_bsize * s.f_bavail 
+                       return self.diskspace_available >= self.diskspace_required
+               except OSError:
+                       return False
+
+       def getErrorMessage(self, task):
+               return _("Not enough diskspace. Please free up some diskspace and try again. (%d MB required, %d MB available)") % (self.diskspace_required / 1024 / 1024, self.diskspace_available / 1024 / 1024)
+
+class ToolExistsPrecondition(Condition):
        def check(self, task):
                import os
                if task.cmd[0]=='/':
                        realpath = task.cmd
                else:
-                       realpath = self.cwd + '/' + self.cmd
+                       realpath = task.cwd + '/' + task.cmd
+               self.realpath = realpath
                return os.access(realpath, os.X_OK)
 
-class AbortedPostcondition:
+       def getErrorMessage(self, task):
+               return _("A required tool (%s) was not found.") % (self.realpath)
+
+class AbortedPostcondition(Condition):
        pass
 
-class ReturncodePostcondition:
+class ReturncodePostcondition(Condition):
        def check(self, task):
                return task.returncode == 0