]> git.ipfire.org Git - pbs.git/commitdiff
Show ETA of build jobs.
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 17 Feb 2013 15:47:09 +0000 (16:47 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 17 Feb 2013 15:47:09 +0000 (16:47 +0100)
backend/builds.py
data/templates/jobs-detail.html
data/templates/modules/jobs/boxes.html
data/templates/modules/jobs/list.html
web/__init__.py

index 43a557d1034d585942ab1d284c5626c2993bee23..ef1e7c1fb9b6bf59cce6112a8a0f65cc8b282a77 100644 (file)
@@ -333,7 +333,7 @@ class Builds(base.Object):
 
                return comments
 
-       def get_build_times_summary(self, name=None, job_type=None):
+       def get_build_times_summary(self, name=None, job_type=None, arch=None):
                query = "\
                        SELECT \
                                builds_times.arch AS arch, \
@@ -355,10 +355,15 @@ class Builds(base.Object):
                        args.append(name)
 
                # Filter by job types.
-               if type:
+               if job_type:
                        conditions.append("builds_times.job_type = %s")
                        args.append(job_type)
 
+               # Filter by arch.
+               if arch:
+                       conditions.append("builds_times.arch = %s")
+                       args.append(arch)
+
                # Add conditions.
                if conditions:
                        query += " WHERE %s" % " AND ".join(conditions)
@@ -368,6 +373,15 @@ class Builds(base.Object):
 
                return self.db.query(query, *args)
 
+       def get_build_times_by_arch(self, arch, **kwargs):
+               kwargs.update({
+                       "arch" : arch,
+               })
+
+               build_times = self.get_build_times_summary(**kwargs)
+               if build_times:
+                       return build_times[0]
+
 
 class Build(base.Object):
        def __init__(self, pakfire, id, data=None):
@@ -1762,6 +1776,12 @@ class Job(base.Object):
        def size(self):
                return sum((p.size for p in self.packages))
 
+       def is_running(self):
+               """
+                       Returns True if job is in a running state.
+               """
+               return self.state in ("pending", "dispatching", "running", "uploading")
+
        def get_state(self):
                return self.data.state
 
@@ -1887,6 +1907,28 @@ class Job(base.Object):
        def time_finished(self):
                return self.data.time_finished
 
+       @property
+       def expected_runtime(self):
+               """
+                       Returns the estimated time and stddev, this job takes to finish.
+               """
+               # Get the average build time.
+               build_times = self.pakfire.builds.get_build_times_by_arch(self.arch.name,
+                       name=self.pkg.name)
+
+               # If there is no statistical data, we cannot estimate anything.
+               if not build_times:
+                       return None, None
+
+               return build_times.average, build_times.stddev
+
+       @property
+       def eta(self):
+               expected_runtime, stddev = self.expected_runtime
+
+               if expected_runtime:
+                       return expected_runtime - int(self.duration), stddev
+
        @property
        def tries(self):
                return self.data.tries
index 1db1f040b501869429f883603842b282acbc6ec7..1ce30a8899af89edf5538a40afabbe85487732a6 100644 (file)
                                                                        {{ format_date(job.time_finished, full_format=True) }}
                                                                </p>
                                                        {% end %}
+                                                       {% if job.is_running() %}
+                                                               <p>
+                                                                       <strong>{{ _("ETA") }}</strong><br>
+                                                                       {{ format_eta(job.eta) }}
+                                                               </p>
+                                                       {% end %}
                                        </div>
                                </div>
                        </div>
index 23fa23150d123933df83d3b1a3c9c2a4b2b44f8c..4f7f0cfcb7d43cf81b390a2105023def66078d84 100644 (file)
                                                {{ _("Builder") }}: <a href="/builder/{{ j.builder.name }}">{{ j.builder.name }}</a>
                                        </li>
                                {% end %}
+
+                               {% if j.is_running() %}
+                                       <li>
+                                               {{ _("ETA") }}: {{ format_eta(j.eta) }}
+                                       </li>
+                               {% end %}
                        </ul>
                </div>
        {% end %}
index 78df1cc7c2ceaa40155ee82d0575eea9dfe76423..d48b58e9b0218de6bd7aab5c6fb1f686a4f90d3a 100644 (file)
 
                                <td>
                                        {{ friendly_time(job.duration) }}
+
+                                       {% if job.is_running() %}
+                                               <br> <span class="muted">{{ _("ETA") }}: {{ format_eta(job.eta) }}</span>
+                                       {% end %}
                                </td>
                        </tr>
                {% end %}
index 1315dfe25ba5dc996a531a2d8cca7293b389b880..b40e5f27eaf67064eab760a2d65e4923a81c9b8b 100644 (file)
@@ -1,4 +1,5 @@
 #!/usr/bin/python
+# encoding: utf-8
 
 import logging
 import multiprocessing
@@ -88,6 +89,7 @@ class Application(tornado.web.Application):
                                "SelectTimezone"     : SelectTimezoneModule,
                        },
                        ui_methods = {
+                               "format_eta"         : self.format_eta,
                                "format_time"        : self.format_time,
                        },
                        xsrf_cookies = True,
@@ -298,6 +300,19 @@ class Application(tornado.web.Application):
 
        ## UI methods
 
+       def format_eta(self, handler, (s, stddev)):
+               if s is None:
+                       _ = handler.locale.translate
+                       return _("Unknown")
+
+               if s < 0:
+                       s = 0
+
+               return "%s ± %s" % (
+                       self.format_time(handler, s),
+                       self.format_time_short(handler, stddev),
+               )
+
        def format_time(self, handler, s):
                _ = handler.locale.translate
 
@@ -308,3 +323,16 @@ class Application(tornado.web.Application):
                        min += 1
 
                return _("%(hrs)d:%(min)02d hrs") % {"hrs" : hrs, "min" : min}
+
+       def format_time_short(self, handler, s):
+               _ = handler.locale.translate
+
+               hrs = s / 3600
+               if hrs >= 1:
+                       return _("%dh") % hrs
+
+               min = s / 60
+               if min >= 1:
+                       return _("%dm") % min
+
+               return _("%ds") % s