[cig-commits] r11824 - in cs/portal/trunk: . seismo/SeismoWebPortal seismo/SeismoWebPortal/designs/plone seismo/SeismoWebPortal/static/pics seismo/SeismoWebPortal/templates/SeismoWebPortal

leif at geodynamics.org leif at geodynamics.org
Thu Apr 17 12:47:51 PDT 2008


Author: leif
Date: 2008-04-17 12:47:51 -0700 (Thu, 17 Apr 2008)
New Revision: 11824

Added:
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/script.js
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/script.py
   cs/portal/trunk/seismo/SeismoWebPortal/static/pics/barbers_pole.gif
   cs/portal/trunk/seismo/SeismoWebPortal/static/pics/progress_bar.gif
   cs/portal/trunk/seismo/SeismoWebPortal/static/pics/progress_box.gif
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/job_progress_form.html
Modified:
   cs/portal/trunk/daemon.py
   cs/portal/trunk/mineos.py
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/Document.html
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/Document.py
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/__init__.py
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.css
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.py
   cs/portal/trunk/seismo/SeismoWebPortal/models.py
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/seismogram_table.html
   cs/portal/trunk/seismo/SeismoWebPortal/views.py
Log:
Finally implemented the progress bars that I've been imagining since
this project began.  They are animated using JavaScript.


Modified: cs/portal/trunk/daemon.py
===================================================================
--- cs/portal/trunk/daemon.py	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/daemon.py	2008-04-17 19:47:51 UTC (rev 11824)
@@ -198,7 +198,8 @@
 
 
     def argvForJob(self, job):
-        return [job.resSpec['executable']] + job.resSpec['arguments']
+        return ([job.resSpec['executable']] + job.resSpec['arguments'] +
+                ["--progress-url=%s" % job.progressUrl])
 
 
     def uploadOutputFilesForJob(self, job):
@@ -514,7 +515,7 @@
                          '--stations=' + self.simulation.stations,
                          "--scheduler.wait=True",
                          "--job.name=run%05d" % self.id,
-                         #"--portal-run-id=%d" % self.id,
+                         "--macros.run.id=%05d" % self.id,
                          #"--job.walltime=2*hour",
                          "--job.stdout=stdout.txt",
                          "--job.stderr=stderr.txt",
@@ -637,6 +638,7 @@
         fields = self.jobFields(job, run)
         response = self.postStatusChange(url, fields)
         portalId = eval(response)
+        job.progressUrl = self.portal.jobProgressUrl % portalId
         stackless.tasklet(self.jobWatcher)(job, portalId, run)
         
     def jobWatcher(self, job, portalId, run):
@@ -771,6 +773,7 @@
         # jobs
         self.jobCreateUrl        = self.urlRoot + 'jobs/create/'
         self.jobUpdateUrl        = self.urlRoot + 'jobs/%d/update/'
+        self.jobProgressUrl      = self.urlPrefix + 'jobs/%d/progress/'
         # output
         self.outputCreateUrl     = self.urlRoot + 'output/create/'
 

Modified: cs/portal/trunk/mineos.py
===================================================================
--- cs/portal/trunk/mineos.py	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/mineos.py	2008-04-17 19:47:51 UTC (rev 11824)
@@ -4,7 +4,7 @@
 from pyre.applications import Script
 from pyre.units.SI import milli, hertz
 from pyre.units.length import km
-import os, sys, stat
+import os, sys, stat, popen2, time
 from os.path import basename, dirname, splitext
 
 mHz = milli*hertz
@@ -34,11 +34,12 @@
 
 class Mineos(object):
 
-    def __init__(self, model, event, stations):
+    def __init__(self, model, event, stations, progressUrl):
         self.bin = prefix + "/bin"
         self.model = model
         self.event = event
         self.stations = stations
+        self.progressUrl = progressUrl
         self.eigcon_out = []
         self.green_out = "green"
         return
@@ -80,19 +81,31 @@
 
 
     def green(self, fmin, fmax, nsamples):
+        nStations = self.lineCount(self.stations + ".site")
         s = open("db_list", "w")
         for dbname in self.eigcon_out:
             print >>s, dbname
-        green_in = "green.in"
-        s = open(green_in, "w")
-        print >>s, self.stations
-        print >>s, "db_list"
-        print >>s, self.event
-        print >>s, fmin, fmax
-        print >>s, nsamples
-        print >>s, self.green_out
-        s.close()
-        self.run("green", stdin=green_in)
+        green_in = open("green.in", "w")
+        child = popen2.Popen4([os.path.join(self.bin, "green")])
+        for s in [green_in, child.tochild]:
+            print >>s, self.stations
+            print >>s, "db_list"
+            print >>s, self.event
+            print >>s, fmin, fmax
+            print >>s, nsamples
+            print >>s, self.green_out
+            s.close()
+        stationTally = 0
+        self.postProgress(0.0)
+        lastPostTime = time.time()
+        for line in child.fromchild:
+            print line,
+            if line.startswith(" green: Station: "):
+                stationTally += 1
+                now = time.time()
+                if now - lastPostTime >= 60.0:
+                    self.postProgress(float(stationTally) / float(nStations))
+                    lastPostTime = now
         return
 
 
@@ -162,6 +175,36 @@
         return
 
 
+    def lineCount(self, filename):
+        tally = 0
+        s = open(filename, 'r')
+        for line in s:
+            tally += 1
+        return tally
+
+
+    def postProgress(self, progress):
+        import urllib, urlparse
+        scheme, host, path = urlparse.urlparse(self.progressUrl)[0:3]
+        fields = { 'progress': "%.2f" % progress }
+        body = urllib.urlencode(fields)
+        headers = {"Content-Type": "application/x-www-form-urlencoded",
+                   "Accept": "text/plain"}
+        import httplib
+        if scheme == "http":
+            conn = httplib.HTTPConnection(host)
+        elif scheme == "https":
+            conn = httplib.HTTPSConnection(host)
+        else:
+            assert False # not scheme in ["http", "https"]
+        conn.request("POST", path, body, headers)
+        response = conn.getresponse()
+        data = response.read()
+        conn.close()
+        return data
+
+
+
 class MineosScript(Script):
 
     name = "mineos"
@@ -220,7 +263,12 @@
     plane = pyre.int("plane", default=0) # use CMT tensor components as-is
     datatype = pyre.int("datatype", default=3) # displacement in nm
 
+    #------------------------------------------------------------------------
+    # misc.
 
+    progressUrl = pyre.str("progress-url")
+
+
     def main(self, *args, **kwds):
         
         self._info.activate()
@@ -258,7 +306,7 @@
 
 
     def runMineos(self, model, enabledModes):
-        mineos = Mineos(model, self.event, self.stations)
+        mineos = Mineos(model, self.event, self.stations, self.progressUrl)
 
         # minos_bran
         for mode in enabledModes:
@@ -308,12 +356,13 @@
 
 
 def main(*args, **kwds):
-    # Redirect all output -- our journal output, and the output of
-    # our children -- to a file.
-    fd = os.open("output_mineos.txt", os.O_CREAT | os.O_WRONLY, mode)
-    os.dup2(fd, 1)
-    os.dup2(fd, 2)
-    os.close(fd)
+    if True:
+        # Redirect all output -- our journal output, and the output of
+        # our children -- to a file.
+        fd = os.open("output_mineos.txt", os.O_CREAT | os.O_WRONLY, mode)
+        os.dup2(fd, 1)
+        os.dup2(fd, 2)
+        os.close(fd)
     
     mineos = MineosScript()
     mineos.run(*args, **kwds)

Modified: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/Document.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/Document.html	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/Document.html	2008-04-17 19:47:51 UTC (rev 11824)
@@ -3,6 +3,7 @@
 <head>
     <script type="text/javascript" src="http://www.geodynamics.org/cig/portal_javascripts/Plone%20Default/ploneScripts3133.js"></script>
     <script type="text/javascript" src="http://www.geodynamics.org/cig/portal_javascripts/Plone%20Default/ploneScripts1044.js"></script>
+    <script type="text/javascript" src="$root/designs/plone/script.js"></script>
     <style type="text/css"><!-- @import url(http://www.geodynamics.org/cig/portal_css/Plone%20Default/ploneStyles8697.css); --></style>
     <style type="text/css" media="screen"><!-- @import url(http://www.geodynamics.org/cig/portal_css/Plone%20Default/ploneStyles4087.css); --></style>
     <link rel="alternate stylesheet"

Modified: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/Document.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/Document.py	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/Document.py	2008-04-17 19:47:51 UTC (rev 11824)
@@ -33,10 +33,10 @@
 currentTime=time.time
 __CHEETAH_version__ = '2.0rc7'
 __CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 7)
-__CHEETAH_genTime__ = 1207258007.506633
-__CHEETAH_genTimestamp__ = 'Thu Apr  3 14:26:47 2008'
+__CHEETAH_genTime__ = 1208308360.6628461
+__CHEETAH_genTimestamp__ = 'Tue Apr 15 18:12:40 2008'
 __CHEETAH_src__ = 'Document.html'
-__CHEETAH_srcLastModified__ = 'Thu Apr  3 14:18:05 2008'
+__CHEETAH_srcLastModified__ = 'Tue Apr 15 17:52:37 2008'
 __CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
 
 if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple:
@@ -88,6 +88,10 @@
 <head>
     <script type="text/javascript" src="http://www.geodynamics.org/cig/portal_javascripts/Plone%20Default/ploneScripts3133.js"></script>
     <script type="text/javascript" src="http://www.geodynamics.org/cig/portal_javascripts/Plone%20Default/ploneScripts1044.js"></script>
+    <script type="text/javascript" src="''')
+        _v = VFFSL(SL,"root",True) # '$root' on line 6, col 41
+        if _v is not None: write(_filter(_v, rawExpr='$root')) # from line 6, col 41.
+        write('''/designs/plone/script.js"></script>
     <style type="text/css"><!-- @import url(http://www.geodynamics.org/cig/portal_css/Plone%20Default/ploneStyles8697.css); --></style>
     <style type="text/css" media="screen"><!-- @import url(http://www.geodynamics.org/cig/portal_css/Plone%20Default/ploneStyles4087.css); --></style>
     <link rel="alternate stylesheet"
@@ -104,17 +108,17 @@
         <style type="text/css" media="all">@import url(http://www.geodynamics.org/cig/IEFixes.css);</style>
     <![endif]-->
     <link href="''')
-        _v = VFFSL(SL,"root",True) # '$root' on line 21, col 17
-        if _v is not None: write(_filter(_v, rawExpr='$root')) # from line 21, col 17.
+        _v = VFFSL(SL,"root",True) # '$root' on line 22, col 17
+        if _v is not None: write(_filter(_v, rawExpr='$root')) # from line 22, col 17.
         write('/designs/plone/style.css" type="text/css" rel="stylesheet" media="all" />\n    <title>')
-        _v = VFFSL(SL,"title",True) # '$title' on line 22, col 12
-        if _v is not None: write(_filter(_v, rawExpr='$title')) # from line 22, col 12.
+        _v = VFFSL(SL,"title",True) # '$title' on line 23, col 12
+        if _v is not None: write(_filter(_v, rawExpr='$title')) # from line 23, col 12.
         write('''</title>
 </head>
 <body>
     ''')
-        _v = VFFSL(SL,"body",True) # '$body' on line 25, col 5
-        if _v is not None: write(_filter(_v, rawExpr='$body')) # from line 25, col 5.
+        _v = VFFSL(SL,"body",True) # '$body' on line 26, col 5
+        if _v is not None: write(_filter(_v, rawExpr='$body')) # from line 26, col 5.
         write('''
 </body>
 </html>

Modified: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/__init__.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/__init__.py	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/__init__.py	2008-04-17 19:47:51 UTC (rev 11824)
@@ -3,6 +3,7 @@
 from Desktop import Desktop
 from Document import Document
 from style import style
+from script import script
 
 import SeismoWebPortal.config as config
 
@@ -79,10 +80,16 @@
         if not path: raise Http404
         name = path.pop(0)
         if path: raise Http404
-        if name != "style.css":
+        index = {
+            "script.js": (script, "text/javascript"),
+            "style.css": (style, "text/css"),
+            }
+        entry = index.get(name)
+        if entry is None:
             raise Http404
-        template = style()
+        TemplateClass, mimetype = entry
+        template = TemplateClass()
         template.root = config.root
         response = HttpResponse(content = str(template),
-                                mimetype = "text/css")
+                                mimetype = mimetype)
         return response

Added: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/script.js
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/script.js	                        (rev 0)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/script.js	2008-04-17 19:47:51 UTC (rev 11824)
@@ -0,0 +1,112 @@
+
+// progress bars
+
+var progressBars = new Array();
+var liveProgressBarTally = 0;
+var progressInterval = 0;
+
+var barberOffset = -40;
+
+
+function activateProgressBars() {
+    if (!W3CDOM) {return false;}
+
+    var query = cssQuery('span.progress');
+    for (var i = 0; i < query.length; i++) {
+        progressBar = new Object();
+        progressBar.element = query[i];
+        progressBar.isBarberPole = false;
+        progressBar.poll = -1;
+        progressBars.push(progressBar);
+        interpretProgressFromServer(progressBar);
+    }
+}
+
+registerPloneFunction(activateProgressBars);
+
+
+function interpretProgressFromServer(progressBar) {
+    var html = progressBar.element.innerHTML;
+    var oldPoll = progressBar.poll;
+
+    if (html.indexOf("barbers_pole") != -1) {
+        progressBar.isBarberPole = true;
+        progressBar.poll = 60000;
+    } else if (html.indexOf("progress_bar") != -1) {
+        progressBar.isBarberPole = false;
+        progressBar.poll = 60000;
+    } else {
+        progressBar.isBarberPole = false;
+        progressBar.poll = -1;
+    }
+
+    if (oldPoll < 0 && progressBar.poll >= 0) {
+        ++liveProgressBarTally;
+        if (liveProgressBarTally == 1) {
+            progressInterval = setInterval("animateProgressBars()", 100);
+        }
+    } else if (oldPoll >= 0 && progressBar.poll < 0) {
+        --liveProgressBarTally;
+        if (liveProgressBarTally == 0) {
+            clearInterval(progressInterval);
+        }
+    }
+}
+
+
+function makeProgressRequest(progressBar) {
+    var request = null;
+    callback = makeProgressCallback(progressBar);
+    if (typeof window.ActiveXObject != 'undefined') {
+        request = new ActiveXObject("Microsoft.XMLHTTP");
+        request.onreadystatechange = callback;
+    } else {
+        request = new XMLHttpRequest();
+        request.onload = callback;
+    }
+    var a = progressBar.element.id.split('-')
+    request.open("GET", "$root/runs/" + a[1] + "/status.html", true);
+    request.send(null);
+    progressBar.request = request;
+}
+
+
+function makeProgressCallback(progressBar) {
+    function callback() {
+        request = progressBar.request;
+        if (request.readyState != 4) return;
+        progressBar.element.innerHTML = request.responseText;
+        interpretProgressFromServer(progressBar);
+    }
+    return callback;
+}
+
+
+function animateProgressBars() { 
+    ++barberOffset;
+    if (barberOffset >= -20) {
+        barberOffset = -40;
+    }
+
+    var bar = '<img src="$root/pics/progress_box.gif" style="background: white url($root/pics/barbers_pole.gif) top left no-repeat; background-position: ' + barberOffset + 'px 0px;">';
+
+    for (var i = 0; i < progressBars.length; i++) {
+        progressBar = progressBars[i];
+
+        if (progressBar.isBarberPole) {
+            var innerHTML = progressBar.element.innerHTML;
+            var index = innerHTML.indexOf("<img");
+            if (index != -1) {
+                progressBar.element.innerHTML = innerHTML.substr(0, index) + bar;
+            }
+        }
+
+        if (progressBar.poll > 0) {
+            progressBar.poll -= 100;
+            if (progressBar.poll <= 0) {
+                progressBar.poll = 0;
+                makeProgressRequest(progressBar);
+            }
+        }
+    }
+} 

Added: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/script.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/script.py	                        (rev 0)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/script.py	2008-04-17 19:47:51 UTC (rev 11824)
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+
+
+
+
+##################################################
+## DEPENDENCIES
+import sys
+import os
+import os.path
+from os.path import getmtime, exists
+import time
+import types
+import __builtin__
+from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion
+from Cheetah.Version import MinCompatibleVersionTuple as RequiredCheetahVersionTuple
+from Cheetah.Template import Template
+from Cheetah.DummyTransaction import DummyTransaction
+from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList
+from Cheetah.CacheRegion import CacheRegion
+import Cheetah.Filters as Filters
+import Cheetah.ErrorCatchers as ErrorCatchers
+
+##################################################
+## MODULE CONSTANTS
+try:
+    True, False
+except NameError:
+    True, False = (1==1), (1==0)
+VFFSL=valueFromFrameOrSearchList
+VFSL=valueFromSearchList
+VFN=valueForName
+currentTime=time.time
+__CHEETAH_version__ = '2.0rc7'
+__CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 7)
+__CHEETAH_genTime__ = 1208377227.3609331
+__CHEETAH_genTimestamp__ = 'Wed Apr 16 13:20:27 2008'
+__CHEETAH_src__ = 'script.js'
+__CHEETAH_srcLastModified__ = 'Wed Apr 16 13:20:20 2008'
+__CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
+
+if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple:
+    raise AssertionError(
+      'This template was compiled with Cheetah version'
+      ' %s. Templates compiled before version %s must be recompiled.'%(
+         __CHEETAH_version__, RequiredCheetahVersion))
+
+##################################################
+## CLASSES
+
+class script(Template):
+
+    ##################################################
+    ## CHEETAH GENERATED METHODS
+
+
+    def __init__(self, *args, **KWs):
+
+        Template.__init__(self, *args, **KWs)
+        if not self._CHEETAH__instanceInitialized:
+            cheetahKWArgs = {}
+            allowedKWs = 'searchList namespaces filter filtersLib errorCatcher'.split()
+            for k,v in KWs.items():
+                if k in allowedKWs: cheetahKWArgs[k] = v
+            self._initCheetahInstance(**cheetahKWArgs)
+        
+
+    def respond(self, trans=None):
+
+
+
+        ## CHEETAH: main method generated for this template
+        if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)):
+            trans = self.transaction # is None unless self.awake() was called
+        if not trans:
+            trans = DummyTransaction()
+            _dummyTrans = True
+        else: _dummyTrans = False
+        write = trans.response().write
+        SL = self._CHEETAH__searchList
+        _filter = self._CHEETAH__currentFilter
+        
+        ########################################
+        ## START - generated method body
+        
+        write('''
+// progress bars
+
+var progressBars = new Array();
+var liveProgressBarTally = 0;
+var progressInterval = 0;
+
+var barberOffset = -40;
+
+
+function activateProgressBars() {
+    if (!W3CDOM) {return false;}
+
+    var query = cssQuery('span.progress');
+    for (var i = 0; i < query.length; i++) {
+        progressBar = new Object();
+        progressBar.element = query[i];
+        progressBar.isBarberPole = false;
+        progressBar.poll = -1;
+        progressBars.push(progressBar);
+        interpretProgressFromServer(progressBar);
+    }
+}
+
+registerPloneFunction(activateProgressBars);
+
+
+function interpretProgressFromServer(progressBar) {
+    var html = progressBar.element.innerHTML;
+    var oldPoll = progressBar.poll;
+
+    if (html.indexOf("barbers_pole") != -1) {
+        progressBar.isBarberPole = true;
+        progressBar.poll = 60000;
+    } else if (html.indexOf("progress_bar") != -1) {
+        progressBar.isBarberPole = false;
+        progressBar.poll = 60000;
+    } else {
+        progressBar.isBarberPole = false;
+        progressBar.poll = -1;
+    }
+
+    if (oldPoll < 0 && progressBar.poll >= 0) {
+        ++liveProgressBarTally;
+        if (liveProgressBarTally == 1) {
+            progressInterval = setInterval("animateProgressBars()", 100);
+        }
+    } else if (oldPoll >= 0 && progressBar.poll < 0) {
+        --liveProgressBarTally;
+        if (liveProgressBarTally == 0) {
+            clearInterval(progressInterval);
+        }
+    }
+}
+
+
+function makeProgressRequest(progressBar) {
+    var request = null;
+    callback = makeProgressCallback(progressBar);
+    if (typeof window.ActiveXObject != 'undefined') {
+        request = new ActiveXObject("Microsoft.XMLHTTP");
+        request.onreadystatechange = callback;
+    } else {
+        request = new XMLHttpRequest();
+        request.onload = callback;
+    }
+    var a = progressBar.element.id.split('-')
+    request.open("GET", "''')
+        _v = VFFSL(SL,"root",True) # '$root' on line 68, col 26
+        if _v is not None: write(_filter(_v, rawExpr='$root')) # from line 68, col 26.
+        write('''/runs/" + a[1] + "/status.html", true);
+    request.send(null);
+    progressBar.request = request;
+}
+
+
+function makeProgressCallback(progressBar) {
+    function callback() {
+        request = progressBar.request;
+        if (request.readyState != 4) return;
+        progressBar.element.innerHTML = request.responseText;
+        interpretProgressFromServer(progressBar);
+    }
+    return callback;
+}
+
+
+function animateProgressBars() { 
+    ++barberOffset;
+    if (barberOffset >= -20) {
+        barberOffset = -40;
+    }
+
+    var bar = '<img src="''')
+        _v = VFFSL(SL,"root",True) # '$root' on line 91, col 26
+        if _v is not None: write(_filter(_v, rawExpr='$root')) # from line 91, col 26.
+        write('/pics/progress_box.gif" style="background: white url(')
+        _v = VFFSL(SL,"root",True) # '$root' on line 91, col 84
+        if _v is not None: write(_filter(_v, rawExpr='$root')) # from line 91, col 84.
+        write('''/pics/barbers_pole.gif) top left no-repeat; background-position: ' + barberOffset + 'px 0px;">';
+
+    for (var i = 0; i < progressBars.length; i++) {
+        progressBar = progressBars[i];
+
+        if (progressBar.isBarberPole) {
+            var innerHTML = progressBar.element.innerHTML;
+            var index = innerHTML.indexOf("<img");
+            if (index != -1) {
+                progressBar.element.innerHTML = innerHTML.substr(0, index) + bar;
+            }
+        }
+
+        if (progressBar.poll > 0) {
+            progressBar.poll -= 100;
+            if (progressBar.poll <= 0) {
+                progressBar.poll = 0;
+                makeProgressRequest(progressBar);
+            }
+        }
+    }
+} 
+''')
+        
+        ########################################
+        ## END - generated method body
+        
+        return _dummyTrans and trans.response().getvalue() or ""
+        
+    ##################################################
+    ## CHEETAH GENERATED ATTRIBUTES
+
+
+    _CHEETAH__instanceInitialized = False
+
+    _CHEETAH_version = __CHEETAH_version__
+
+    _CHEETAH_versionTuple = __CHEETAH_versionTuple__
+
+    _CHEETAH_genTime = __CHEETAH_genTime__
+
+    _CHEETAH_genTimestamp = __CHEETAH_genTimestamp__
+
+    _CHEETAH_src = __CHEETAH_src__
+
+    _CHEETAH_srcLastModified = __CHEETAH_srcLastModified__
+
+    _mainCheetahMethod_for_script= 'respond'
+
+## END CLASS DEFINITION
+
+if not hasattr(script, '_initCheetahAttributes'):
+    templateAPIClass = getattr(script, '_CHEETAH_templateClass', Template)
+    templateAPIClass._addCheetahPlumbingCodeToClass(script)
+
+
+# CHEETAH was developed by Tavis Rudd and Mike Orr
+# with code, advice and input from many other volunteers.
+# For more information visit http://www.CheetahTemplate.org/
+
+##################################################
+## if run from command line:
+if __name__ == '__main__':
+    from Cheetah.TemplateCmdLineIface import CmdLineIface
+    CmdLineIface(templateObj=script()).run()
+
+

Modified: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.css
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.css	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.css	2008-04-17 19:47:51 UTC (rev 11824)
@@ -500,7 +500,7 @@
 }
 
 fieldset fieldset legend {
-    font-size: normal;
+    font-size: medium;
 }
 
 form div {

Modified: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.py	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.py	2008-04-17 19:47:51 UTC (rev 11824)
@@ -33,10 +33,10 @@
 currentTime=time.time
 __CHEETAH_version__ = '2.0rc7'
 __CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 7)
-__CHEETAH_genTime__ = 1208286939.0294361
-__CHEETAH_genTimestamp__ = 'Tue Apr 15 12:15:39 2008'
+__CHEETAH_genTime__ = 1208375419.645751
+__CHEETAH_genTimestamp__ = 'Wed Apr 16 12:50:19 2008'
 __CHEETAH_src__ = 'style.css'
-__CHEETAH_srcLastModified__ = 'Tue Apr 15 12:15:34 2008'
+__CHEETAH_srcLastModified__ = 'Wed Apr 16 12:50:11 2008'
 __CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
 
 if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple:
@@ -591,7 +591,7 @@
 }
 
 fieldset fieldset legend {
-    font-size: normal;
+    font-size: medium;
 }
 
 form div {

Modified: cs/portal/trunk/seismo/SeismoWebPortal/models.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/models.py	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/seismo/SeismoWebPortal/models.py	2008-04-17 19:47:51 UTC (rev 11824)
@@ -836,6 +836,9 @@
     code = models.IntegerField(choices=CODE_CHOICES)
     parameterData = models.TextField()
 
+    # the user who made the request
+    requestor = models.ForeignKey(User)
+
     def _getParameters(self):
         if not hasattr(self, '_parameters'):
             import base64
@@ -874,6 +877,19 @@
     started = models.DateTimeField(auto_now_add=True, editable=False)
     finished = models.DateTimeField(null=True, blank=True)
 
+    def isDead(self):
+        return self.status in ['done', 'error']
+
+    def _getActiveJob(self):
+        if self.isDead():
+            return None
+        if not hasattr(self, '_activeJob'):
+            self._activeJob = None
+            for job in self.job_set.all():
+                self._activeJob = job
+        return self._activeJob
+    activeJob = property(_getActiveJob)
+
     def outputList(self):
         l = []
         for job in self.job_set.all():
@@ -891,6 +907,8 @@
     
     task = models.CharField(maxlength=100)
     status = models.CharField(maxlength=100)
+    progress = models.FloatField(max_digits=19, decimal_places=10, default=-1.0)
+    
     created = models.DateTimeField(auto_now_add=True, editable=False)
     started = models.DateTimeField(null=True, blank=True)
     finished = models.DateTimeField(null=True, blank=True)

Added: cs/portal/trunk/seismo/SeismoWebPortal/static/pics/barbers_pole.gif
===================================================================
(Binary files differ)


Property changes on: cs/portal/trunk/seismo/SeismoWebPortal/static/pics/barbers_pole.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: cs/portal/trunk/seismo/SeismoWebPortal/static/pics/progress_bar.gif
===================================================================
(Binary files differ)


Property changes on: cs/portal/trunk/seismo/SeismoWebPortal/static/pics/progress_bar.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: cs/portal/trunk/seismo/SeismoWebPortal/static/pics/progress_box.gif
===================================================================
(Binary files differ)


Property changes on: cs/portal/trunk/seismo/SeismoWebPortal/static/pics/progress_box.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/job_progress_form.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/job_progress_form.html	                        (rev 0)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/job_progress_form.html	2008-04-17 19:47:51 UTC (rev 11824)
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <title>Job Progress</title>
+    </head>
+
+    <body>
+
+    <h1>Job Progress</h1>
+
+    {% if form.has_errors %}
+    <p>{{ form.error_dict }}
+    {% endif %}
+
+    <form method="post" action="{{ action }}">
+
+    <p><label for="id_status">progress</label> {{ form.progress }}
+
+    <p><input type="submit" value="Save">
+
+    </form>
+
+    </body>
+</html>

Modified: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/seismogram_table.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/seismogram_table.html	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/seismogram_table.html	2008-04-17 19:47:51 UTC (rev 11824)
@@ -51,22 +51,7 @@
         <td>{{ ps.fmin }}</td>
         <td>{{ ps.fmax }}</td>
         <td>{{ ps.step }}</td>
-
-        <td>
-            {% if ps.request %}
-                {% if ps.request.run.outputList %}
-                    <ul class=output>
-                        {% for file in ps.request.run.outputList %}
-                            <li><a href="{{ file.url }}">{{ file.name }}</a>
-                        {% endfor %}
-                    </ul>
-                {% else %}
-                    {{ ps.request.run.status }}
-                {% endif %}
-            {% else %}
-                {{ ps.requestForm }}
-            {% endif %}
-        </td>
+        <td>{{ ps.outputStatus }}</td>
     </tr>
     {% endfor %}
     </tbody>
@@ -110,22 +95,7 @@
         <td><img src="{{ root }}/pics/icon-{% if ps.model.ellipticity %}yes{% else %}no{% endif %}.gif" alt="{{ ps.model.ellipticity }}"></td>
         <td>{{ ps.mesh }}</td>
         <td><img src="{{ root }}/pics/icon-{% if ps.receivers_can_be_buried %}yes{% else %}no{% endif %}.gif" alt="{{ ps.receivers_can_be_buried }}"></td>
-
-        <td>
-            {% if ps.request %}
-                {% if ps.request.run.outputList %}
-                    <ul class=output>
-                        {% for file in ps.request.run.outputList %}
-                            <li><a href="{{ file.url }}">{{ file.name }}</a>
-                        {% endfor %}
-                    </ul>
-                {% else %}
-                    {{ ps.request.run.status }}
-                {% endif %}
-            {% else %}
-                {{ ps.requestForm }}
-            {% endif %}
-        </td>
+        <td>{{ ps.outputStatus }}</td>
     </tr>
     {% endfor %}
     </tbody>

Modified: cs/portal/trunk/seismo/SeismoWebPortal/views.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/views.py	2008-04-17 17:17:38 UTC (rev 11823)
+++ cs/portal/trunk/seismo/SeismoWebPortal/views.py	2008-04-17 19:47:51 UTC (rev 11824)
@@ -719,10 +719,50 @@
     objId = intOr404(name)
     if not path: raise Http404
     name = path.pop(0)
-    if path or name != "status": raise Http404
+    if path: raise Http404
+    if name == "status.html":
+        run = get_object_or_404(Run, id=objId)
+        return HttpResponse(runOutputStatus(run, innerHTML=True), mimetype='text/html')
+    if name != "status": raise Http404
     return update_run_status(request, object_id = objId)
 
 
+def runOutputList(run):
+    html = ''
+    for file in run.outputList():
+        html += '<li><a href="%s">%s</a>' % (file.url(), file.name)
+    return '<ul class=output>%s</ul>' % html
+
+
+def runOutputStatus(run, innerHTML=False):
+
+    if run.status == 'done':
+        return runOutputList(run)
+    if run.status == 'error':
+        # NYI: separate error page
+        return '<span class=error>%s</span> %s' % (run.status, runOutputList(run))
+
+    if run.activeJob:
+        progress = run.activeJob.progress
+    else:
+        progress = -1.0
+    
+    if progress >= 0.0:
+        imageWidth = 154
+        offset = imageWidth - int(float(imageWidth) * progress)
+        bar = '<img src="%s/pics/progress_box.gif" style="background: white url(%s/pics/progress_bar.gif) top left no-repeat; background-position: -%dpx 0px;">' % (
+            config.root, config.root, offset)
+    else:
+        bar = '<img src="%s/pics/progress_box.gif" style="background: white url(%s/pics/barbers_pole.gif) top left no-repeat; background-position: -40px 0px;">' % (config.root, config.root)
+    
+    html = '<small>%s</small>&nbsp;%s' % (run.status, bar)
+    if innerHTML:
+        return html
+    
+    # Wrap the status in a 'progress' span so that the JavaScript can find it.
+    return '<span id="progress-%d" class=progress>%s</span>' % (run.id, html)
+
+
 def update_run_status(request, object_id):
     from forms import RunStatusManipulator
     import datetime
@@ -752,7 +792,7 @@
                 stream.write(content)
                 stream.close()
             run.status = new_data['status']
-            if run.status in ['done', 'error']:
+            if run.isDead():
                 run.finished = datetime.datetime.now()
                 if run.status == 'error':
                     notify_admins_of_failed_run(run)
@@ -814,23 +854,50 @@
     return
 
 
+class JobProgressManipulator(forms.Manipulator):
+    def __init__(self, object_id, **kwds):
+        self.job = get_object_or_404(Job, id = object_id)
+        self.fields = [
+            forms.FloatField('progress', max_digits=19, decimal_places=10, is_required=True),
+            ]
+        return
+    def flatten_data(self):
+        return {}
+    def save(self, new_data):
+        self.job.progress = new_data['progress']
+        self.job.save()
+        return self.job
+
 def jobs(request, path, desktop):
-    return daemon_post(request, path, Job, "SeismoWebPortal/job_form.html")
+    follow = {'progress': False}
+    return daemon_post(request, path,
+                       (Job.AddManipulator, "SeismoWebPortal/job_form.html", follow),
+                       update = (Job.ChangeManipulator, "SeismoWebPortal/job_form.html", follow),
+                       progress = (JobProgressManipulator, "SeismoWebPortal/job_progress_form.html", None),
+                       )
 
 def output(request, path, desktop):
-    return daemon_post(request, path, OutputFile, "SeismoWebPortal/outputfile_form.html")
+    return daemon_post(request, path,
+                       (OutputFile.AddManipulator, "SeismoWebPortal/outputfile_form.html", None),
+                       update = (OutputFile.ChangeManipulator, "SeismoWebPortal/outputfile_form.html", None),
+                       )
 
-def daemon_post(request, path, Model, template):
+def daemon_post(request, path, (AddManipulator, add_template, add_follow), **kwds):
     if not path: raise Http404
     name = path.pop(0)
     if name == "create":
         if path: raise Http404
-        manipulator = Model.AddManipulator()
+        manipulator = AddManipulator(follow = add_follow)
+        template = add_template
         action = "create"
     else:
         object_id = intOr404(name)
-        if len(path) != 1 or path[0] != "update": raise Http404
-        manipulator = Model.ChangeManipulator(object_id)
+        if len(path) != 1: raise Http404
+        name = path[0]
+        Manipulator = kwds.get(name)
+        if Manipulator is None: raise Http404
+        Manipulator, template, follow = Manipulator
+        manipulator = Manipulator(object_id, follow = follow)
         action = "update"
     
     if request.method == 'POST':
@@ -1295,13 +1362,6 @@
     raise Http404
 
 
-def progressBar(percentage):
-    imageWidth = 154
-    offset = imageWidth - int(float(imageWidth) * percentage)
-    return '<img src="%s/pics/progress_box.gif"" style="background: white url(%s/pics/progress_bar.gif) top left no-repeat; background-position: -%dpx 0px;">' % (
-        config.root, config.root, offset)
-
-
 def seismogramTable(workspace, url, request, path, appWindow, desktop):
     from django.template import Context
 
@@ -1335,10 +1395,10 @@
         for ps in parameterSetList[ParameterClass]:
             parameters = ps.snapshot(workspace)
             ps.request = None
-            ps.requestForm = requestForm(workspace, ps)
+            ps.outputStatus = requestForm(workspace, ps)
             for request in requests[ParameterClass.code]:
                 if request.parameters == parameters:
-                    ps.request = request
+                    ps.outputStatus = runOutputStatus(request.run)
 
     t = loader.get_template('SeismoWebPortal/seismogram_table.html')
     c = Context({'root': config.root,
@@ -1357,7 +1417,7 @@
     print >>html, '<input type="hidden" name="workspace" value="%d"/>' % workspace.id
     print >>html, '<input type="hidden" name="code" value="%d"/>' % parameters.code
     print >>html, '<input type="hidden" name="parameters" value="%d"/>' % parameters.id
-    print >>html, '<input type="submit" value="Request" />'
+    print >>html, '<input type="submit" value="Request" style="font-size: small; padding: 0 0 0 0;" />'
     print >>html, '</form>'
     return html.getvalue()
 
@@ -1410,20 +1470,22 @@
     else:
         raise Http404
 
-    request = Request()
+    req = Request()
     event = workspace.event
-    request.event = requestEventId(event)
-    request.stations = workspace.stations
-    request.record_length = workspace.record_length
+    req.event = requestEventId(event)
+    req.stations = workspace.stations
+    req.record_length = workspace.record_length
 
     parameterData = parameters.snapshot(workspace)
-    request.code = code
-    request.parameterData = Request.encodeParameters(parameterData)
+    req.code = code
+    req.parameterData = Request.encodeParameters(parameterData)
 
-    request.save()
+    req.requestor = request.user
 
+    req.save()
+
     Run.objects.create(
-        request = request,
+        request = req,
         status = "new",
         )
     
@@ -2249,7 +2311,9 @@
     desktop.selectWindow(appWindow)
     navtree = appWindow.root
     url = config.root + "/admin/"
-    
+
+    userIcon = img("http://www.geodynamics.org/cig/user.gif", width=16, height=16)
+
     if not path:
         child = gui.ChildWindow(url, "Administration")
         child.content = navtree.iconView(url)
@@ -2301,7 +2365,9 @@
 
             html = StringIO()
             print >>html, '<h2>Request %04d</h2>' % objId
-            print >>html, '<p class=infobar>%s</p>' % CODE_CHOICES[obj.code-1][1]
+            print >>html, '<table class=infobar width="100%%"><tr><td><b>requestor:</b> %s%s</td><td><b>code:</b> %s</td></tr></table>' % (
+                userIcon, obj.requestor.username, CODE_CHOICES[obj.code-1][1])
+            print >>html, '<p></p>'
             print >>html, '<table rules="groups" class="cool">'
             print >>html, '<thead>'
             print >>html, '<tr><th></th><th>run</th><th>status</th><th>output</th></tr>'
@@ -2330,13 +2396,13 @@
         print >>html, '<h2>Requests</h2>'
         print >>html, '<table rules="groups" class="cool">'
         print >>html, '<thead>'
-        print >>html, '<tr><th>id</th><th>code</th><th>status</th><th>output</th></tr>'
+        print >>html, '<tr><th>id</th><th>requestor</th><th>code</th><th>status</th><th>output</th></tr>'
         print >>html, '</thead>'
         print >>html, '<tbody>'
         for i, obj in enumerate(Request.objects.all().order_by('-id')):
             id = '<a href="%s%d/">%04d</a>' % (url, obj.id, obj.id)
-            print >>html, '<tr class=%s><td>%s</td><td>%s</td><td>%s</td><td>%s</td>' % (
-                (i % 2 and 'even' or 'odd'), id, CODE_CHOICES[obj.code-1][1],
+            print >>html, '<tr class=%s><td>%s</td><td>%s%s</td><td>%s</td><td>%s</td><td>%s</td>' % (
+                (i % 2 and 'even' or 'odd'), id, userIcon, obj.requestor.username, CODE_CHOICES[obj.code-1][1],
                 obj.run.status, runOutputList(obj.run)
                 )
         print >>html, '</tbody>'
@@ -2357,7 +2423,7 @@
 
         users = User.objects.all()
 
-        icon = img("http://www.geodynamics.org/cig/user.gif", width=16, height=16)
+        na = '<span class=notApplicable>n/a</span>'
         
         html = StringIO()
         print >>html, '<h2>Users</h2>'
@@ -2369,10 +2435,14 @@
         print >>html, '</thead>'
         print >>html, '<tbody>'
         for i, user in enumerate(users):
+            try:
+                invite, approved = user.userinfo.invite, yesNo(user.userinfo.approved)
+            except UserInfo.DoesNotExist:
+                invite, approved = na, na
             print >>html, ('<tr class=%s><td>%s%s</td><td>%s</td><td>%s</td><td>%s</td>' +
                            '<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>') % (
-                (i % 2 and 'even' or 'odd'), icon, user.username, mailto(user.email), user.first_name, user.last_name,
-                yesNo(user.is_staff), user.userinfo.invite, yesNo(user.userinfo.approved), user.last_login.date())
+                (i % 2 and 'even' or 'odd'), userIcon, user.username, mailto(user.email), user.first_name, user.last_name,
+                yesNo(user.is_staff), invite, approved, user.last_login.date())
         print >>html, '</tbody>'
         print >>html, '</table>'
         



More information about the cig-commits mailing list