changeset 615:cbb170d9c894

admin: Improve publish logs showing as alerts in the admin panel. * Don't show stuff that happened before the page was opened. * Skinning tweaks.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 04 Feb 2016 21:50:18 -0800
parents a2d9ef307a08
children ccd328d0881f
files foodtruck/pubutil.py foodtruck/templates/layouts/master.html foodtruckui/assets/js/foodtruck.js foodtruckui/assets/sass/foodtruck.scss foodtruckui/assets/sass/foodtruck/_base.scss foodtruckui/assets/sass/foodtruck/_publog.scss
diffstat 6 files changed, 118 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/foodtruck/pubutil.py	Thu Feb 04 21:49:15 2016 -0800
+++ b/foodtruck/pubutil.py	Thu Feb 04 21:50:18 2016 -0800
@@ -28,7 +28,20 @@
                   lambda *args: _shutdown_server_and_raise_sigint())
 
 
+def _read_pid_file(pid_file):
+    logger.debug("Reading PID file: %s" % pid_file)
+    try:
+        with open(pid_file, 'r') as fp:
+            pid_str = fp.read()
+
+        return int(pid_str.strip())
+    except Exception:
+        logger.error("Error reading PID file.")
+        raise
+
+
 def _pid_exists(pid):
+    logger.debug("Checking if process ID %d is running" % pid)
     try:
         os.kill(pid, 0)
     except OSError as ex:
@@ -44,61 +57,52 @@
         return True
 
 
-def _read_pid_file(pid_file):
-    logger.debug("Reading PID file: %s" % pid_file)
-    try:
-        with open(pid_file, 'r') as fp:
-            pid_str = fp.read()
-
-        return int(pid_str.strip())
-    except Exception:
-        logger.error("Error reading PID file.")
-        raise
-
-
 class PublishLogReader(object):
-    _pub_max_time = 10 * 60   # Don't bother about pubs older than 10mins.
     _poll_interval = 1        # Check the process every 1 seconds.
     _ping_interval = 30       # Send a ping message every 30 seconds.
 
     def __init__(self, pid_path, log_path):
         self.pid_path = pid_path
         self.log_path = log_path
-        self._pub_pid_mtime = 0
-        self._last_seek = -1
-        self._last_ping_time = 0
 
     def run(self):
         logger.debug("Opening publish log...")
         pid = None
+        pid_mtime = 0
         is_running = False
+        last_seek = -1
+        last_ping_time = 0
         try:
             while not server_shutdown:
                 # PING!
-                interval = time.time() - self._last_ping_time
+                interval = time.time() - last_ping_time
                 if interval > self._ping_interval:
                     logger.debug("Sending ping...")
-                    self._last_ping_time = time.time()
+                    last_ping_time = time.time()
                     yield bytes("event: ping\ndata: 1\n\n", 'utf8')
 
-                # Check if the PID file has changed.
+                # Check the PID file timestamp.
                 try:
                     new_mtime = os.path.getmtime(self.pid_path)
                 except OSError:
                     new_mtime = 0
 
-                if (new_mtime > 0 and
-                        time.time() - new_mtime > self._pub_max_time):
-                    new_mtime = 0
+                # If there's a valid PID file and we either just started
+                # streaming (pid_mtime == 0) or we remember an older version
+                # of that PID file (pid_mtime != new_mtime), let's read the
+                # PID from the file.
+                is_pid_file_prehistoric = False
+                if new_mtime > 0 and new_mtime != pid_mtime:
+                    is_pid_file_prehistoric = (pid_mtime == 0)
+                    pid_mtime = new_mtime
+                    pid = _read_pid_file(self.pid_path)
 
-                # Re-read the PID file.
-                prev_mtime = self._pub_pid_mtime
-                if new_mtime > 0 and new_mtime != prev_mtime:
-                    self._pub_pid_mtime = new_mtime
-                    pid = _read_pid_file(self.pid_path)
-                    if pid:
-                        logger.debug("Monitoring new process, PID: %d" % pid)
+                if is_pid_file_prehistoric:
+                    logger.debug("PID file is pre-historic, we will skip the "
+                                 "first parts of the log.")
 
+                # If we have a valid PID, let's check if the process is
+                # currently running.
                 was_running = is_running
                 if pid:
                     is_running = _pid_exists(pid)
@@ -106,29 +110,41 @@
                             "Process %d is %s" %
                             (pid, 'running' if is_running else 'not running'))
                     if not is_running:
+                        # Let's forget this PID file until it changes.
                         pid = None
                 else:
                     is_running = False
 
-                # Send data.
+                # Read new data from the log file.
                 new_data = None
                 if is_running or was_running:
-                    if self._last_seek < 0:
-                        outstr = 'event: message\ndata: Publish started.\n\n'
-                        yield bytes(outstr, 'utf8')
-                        self._last_seek = 0
+                    if last_seek < 0:
+                        # Only send the "publish started" message if we
+                        # actually caught the process as it was starting, not
+                        # if we started streaming after it started.
+                        # This means we saw the PID file get changed.
+                        if not is_pid_file_prehistoric:
+                            outstr = (
+                                    'event: message\n'
+                                    'data: Publish started.\n\n')
+                            yield bytes(outstr, 'utf8')
+                        last_seek = 0
 
                     try:
                         with open(self.log_path, 'r', encoding='utf8') as fp:
-                            fp.seek(self._last_seek)
+                            fp.seek(last_seek)
                             new_data = fp.read()
-                            self._last_seek = fp.tell()
+                            last_seek = fp.tell()
                     except OSError:
                         pass
                 if not is_running:
-                    self._last_seek = 0
+                    # Process is not running anymore, let's reset our seek
+                    # marker back to the beginning.
+                    last_seek = -1
 
-                if new_data:
+                # Stream the new data to the client, but don't send old stuff
+                # that happened before we started this stream.
+                if new_data and not is_pid_file_prehistoric:
                     logger.debug("SSE: %s" % new_data)
                     for line in new_data.split('\n'):
                         outstr = 'event: message\ndata: %s\n\n' % line
--- a/foodtruck/templates/layouts/master.html	Thu Feb 04 21:49:15 2016 -0800
+++ b/foodtruck/templates/layouts/master.html	Thu Feb 04 21:50:18 2016 -0800
@@ -21,7 +21,12 @@
                 {% block content %}{% endblock %}
             </section>
             {% block after_content %}{% endblock %}
-            <div id="ft-publog"></div>
+            <div id="ft-publog" class="ft-publog" role="alert" style="display: none;">
+                <button type="button" class="close" aria-label="close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+                <div id="ft-publog-container"></div>
+            </div>
             <footer>
                 <p>Prepared by <a href="http://bolt80.com">BOLT80</a>.</p>
                 <p>Much <span class="icon ion-heart"></span> to
--- a/foodtruckui/assets/js/foodtruck.js	Thu Feb 04 21:49:15 2016 -0800
+++ b/foodtruckui/assets/js/foodtruck.js	Thu Feb 04 21:50:18 2016 -0800
@@ -5,35 +5,47 @@
     $('#ft-commit-modal').on('shown.bs.modal', function () {
         $('#ft-commit-msg').focus();
     });
+
+    var publogEl = $('#ft-publog');
+    publogEl.mouseenter(function() {
+        publogEl.attr('data-autohide', 'false');
+    });
+    publogEl.on('hide', function() {
+        var containerEl = $('#ft-publog-container', publogEl);
+        containerEl.empty();
+    });
+
+    var closePublogBtn = $('button', publogEl);
+    closePublogBtn.on('click', function() {
+        publogEl.fadeOut(200);
+    });
 });
 
 var onPublishEvent = function(e) {
-    var msgEl = $('<div></div>');
 
+    var publogEl = $('#ft-publog');
+    var containerEl = $('#ft-publog-container', publogEl);
+
+    var msgEl = $('<div>' + e.data + '</div>');
     var removeMsgEl = function() {
         msgEl.remove();
-        var publogEl = $('#ft-publog');
-        if (publogEl.children().length == 0) {
-            publogEl.hide();
+        if (containerEl.children().length == 0) {
+            // Last message, hide the log window.
+            publogEl.fadeOut(200);
         }
     };
-
-    msgEl.addClass('alert-dismissible');
-    msgEl.attr('role', 'alert');
-    msgEl.append('<button type="button" class="close" data-dismiss="alert" aria-label="close">' +
-                 '<span aria-hidden="true">&times;</span></button>');
-    msgEl.append('<div>' + e.data + '</div>');
     var timeoutId = window.setTimeout(function() {
-        msgEl.fadeOut(400, removeMsgEl);
+        if (publogEl.attr('data-autohide') == 'true') {
+            msgEl.fadeOut(400, removeMsgEl);
+        }
     }, 4000);
-    msgEl.mouseenter(function() {
-        window.clearTimeout(timeoutId);
-    });
-    $('button', msgEl).click(removeMsgEl);
 
-    var logEl = $('#ft-publog');
-    logEl.append(msgEl);
-    logEl.show();
+    if (containerEl.children().length == 0) {
+        // First message, show the log window, reset the mouseover marker.
+        publogEl.attr('data-autohide', 'true');
+        publogEl.fadeIn(200);
+    }
+    containerEl.append(msgEl);
 };
 
 if (!!window.EventSource) {
@@ -45,3 +57,4 @@
     source.addEventListener('message', onPublishEvent);
 }
 
+
--- a/foodtruckui/assets/sass/foodtruck.scss	Thu Feb 04 21:49:15 2016 -0800
+++ b/foodtruckui/assets/sass/foodtruck.scss	Thu Feb 04 21:50:18 2016 -0800
@@ -40,4 +40,5 @@
 @import "foodtruck/base";
 @import "foodtruck/sidebar";
 @import "foodtruck/editing";
+@import "foodtruck/publog";
 
--- a/foodtruckui/assets/sass/foodtruck/_base.scss	Thu Feb 04 21:49:15 2016 -0800
+++ b/foodtruckui/assets/sass/foodtruck/_base.scss	Thu Feb 04 21:50:18 2016 -0800
@@ -45,21 +45,3 @@
     padding: 0 0.2em;
 }
 
-#ft-publog {
-    position: fixed;
-    right: 0;
-    bottom: 0;
-    margin: 0.5em;
-    width: 50%;
-    max-width: 30em;
-    display: none;
-}
-#ft-publog>div {
-    padding: 1em 35px 1em 1em;
-    margin: 0.3em;
-    color: $ft-color-gray-light;
-    background: $ft-color-gray-darker;
-    border-radius: 0.5em;
-    box-shadow: 0 0 10px $ft-color-gray-dark;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foodtruckui/assets/sass/foodtruck/_publog.scss	Thu Feb 04 21:50:18 2016 -0800
@@ -0,0 +1,26 @@
+
+#ft-publog {
+    position: fixed;
+    right: 0;
+    bottom: 0;
+    width: 42%;
+    min-width: 20em;
+    margin: 0.5em;
+    color: $ft-color-white;
+    background: $ft-color-blue;
+    border-radius: 0.5em;
+    box-shadow: 0 0 10px darken($ft-color-blue, 50%);
+
+    button {
+        padding: 0.2em 0.4em;
+    }
+}
+
+#ft-publog-container {
+    margin: 1em;
+
+    div {
+        margin: 0.1em;
+    }
+}
+