<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[15372] CalendarServer/trunk/calendarserver/tools/dashboard.py</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/15372">15372</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2015-12-08 13:44:13 -0800 (Tue, 08 Dec 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Support quick ways to view groups of panels,.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunkcalendarservertoolsdashboardpy">CalendarServer/trunk/calendarserver/tools/dashboard.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunkcalendarservertoolsdashboardpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/dashboard.py (15371 => 15372)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/dashboard.py        2015-12-05 16:56:31 UTC (rev 15371)
+++ CalendarServer/trunk/calendarserver/tools/dashboard.py        2015-12-08 21:44:13 UTC (rev 15372)
</span><span class="lines">@@ -19,8 +19,7 @@
</span><span class="cx"> server as exposed by the L{DashboardProtocol} stats socket.
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-from getopt import getopt, GetoptError
-
</del><ins>+import argparse
</ins><span class="cx"> import curses.panel
</span><span class="cx"> import errno
</span><span class="cx"> import fcntl
</span><span class="lines">@@ -58,45 +57,26 @@
</span><span class="cx">         sys.exit(0)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-BOX_WIDTH = 52
</del><span class="cx"> 
</span><del>-
</del><span class="cx"> def main():
</span><del>-    try:
-        (optargs, _ignore_args) = getopt(
-            sys.argv[1:], &quot;hs:t&quot;, [
-                &quot;help&quot;,
-            ],
-        )
-    except GetoptError, e:
-        usage(e)
</del><ins>+    parser = argparse.ArgumentParser(description=&quot;Dashboard service for CalendarServer.&quot;)
+    parser.add_argument(&quot;-t&quot;, action=&quot;store_false&quot;, help=&quot;text output, not curses&quot;)
+    parser.add_argument(&quot;-s&quot;, default=&quot;localhost:8100&quot;, help=&quot;server host (and optional port), or unix socket path prefixed by 'unix:'&quot;)
+    args = parser.parse_args()
</ins><span class="cx"> 
</span><span class="cx">     #
</span><span class="cx">     # Get configuration
</span><span class="cx">     #
</span><del>-    useCurses = True
-    server = (&quot;localhost&quot;, 8100)
-
-    for opt, arg in optargs:
-        if opt in (&quot;-h&quot;, &quot;--help&quot;):
-            usage()
-
-        elif opt in (&quot;-t&quot;):
-            useCurses = False
-
-        elif opt in (&quot;-s&quot;):
-            if not arg.startswith(&quot;unix:&quot;):
-                server = arg.split(&quot;:&quot;)
-                if len(server) == 1:
-                    server.append(8100)
-                else:
-                    server[1] = int(server[1])
-                server = tuple(server)
-            else:
-                server = arg
-
</del><ins>+    useCurses = args.t
+    if not args.s.startswith(&quot;unix:&quot;):
+        server = args.s.split(&quot;:&quot;)
+        if len(server) == 1:
+            server.append(8100)
</ins><span class="cx">         else:
</span><del>-            raise NotImplementedError(opt)
</del><ins>+            server[1] = int(server[1])
+        server = tuple(server)
+    else:
+        server = args.s
</ins><span class="cx"> 
</span><span class="cx">     if useCurses:
</span><span class="cx">         def _wrapped(stdscrn):
</span><span class="lines">@@ -142,6 +122,10 @@
</span><span class="cx"> 
</span><span class="cx">     screen = None
</span><span class="cx">     registered_windows = {}
</span><ins>+    registered_window_sets = {
+        &quot;H&quot;: (&quot;HTTP Panels&quot;, [],),
+        &quot;J&quot;: (&quot;Jobs Panels&quot;, [],),
+    }
</ins><span class="cx">     registered_order = []
</span><span class="cx"> 
</span><span class="cx">     def __init__(self, server, screen, usesCurses):
</span><span class="lines">@@ -166,6 +150,17 @@
</span><span class="cx">         cls.registered_order.append(keypress)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @classmethod
+    def registerWindowSet(cls, wtype, keypress):
+        &quot;&quot;&quot;
+        Register a set of window types along with a key press action. This allows the
+        controller to select the appropriate set of windows when its key is pressed,
+        and also provides help information to the L{HelpWindow} for each
+        available window set type.
+        &quot;&quot;&quot;
+        cls.registered_window_sets[keypress][1].append(wtype)
+
+
</ins><span class="cx">     def run(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Create the initial window and run the L{scheduler}.
</span><span class="lines">@@ -182,7 +177,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         # Toggle a specific window on or off
</span><del>-        if wtype is not None:
</del><ins>+        if isinstance(wtype, type):
</ins><span class="cx">             if wtype not in [type(w) for w in self.windows]:
</span><span class="cx">                 self.windows.append(wtype(self.usesCurses, self.client).makeWindow())
</span><span class="cx">                 self.windows[-1].activate()
</span><span class="lines">@@ -203,7 +198,11 @@
</span><span class="cx">                     window.deactivate()
</span><span class="cx">                 self.windows = []
</span><span class="cx">             top = 0
</span><del>-            ordered_windows = [self.registered_windows[i] for i in self.registered_order]
</del><ins>+            if wtype is None:
+                # All windows in registered order
+                ordered_windows = [self.registered_windows[i] for i in self.registered_order]
+            else:
+                ordered_windows = list(wtype)
</ins><span class="cx">             for wtype in filter(lambda x: x.all, ordered_windows):
</span><span class="cx">                 new_win = wtype(self.usesCurses, self.client).makeWindow(top=top)
</span><span class="cx">                 logging.debug('created %r at panel level %r' % (new_win, new_win.z_order))
</span><span class="lines">@@ -292,6 +291,8 @@
</span><span class="cx">                     self.displayWindow(self.registered_windows[&quot;h&quot;])
</span><span class="cx">             elif c in self.registered_windows:
</span><span class="cx">                 self.displayWindow(self.registered_windows[c])
</span><ins>+            elif c in self.registered_window_sets:
+                self.displayWindow(self.registered_window_sets[c][1])
</ins><span class="cx"> 
</span><span class="cx">         if not initialUpdate:
</span><span class="cx">             self.sched.enter(self.seconds, 0, self.updateDisplay, ())
</span><span class="lines">@@ -390,6 +391,10 @@
</span><span class="cx">     all = True
</span><span class="cx">     clientItem = None
</span><span class="cx"> 
</span><ins>+    windowTitle = &quot;&quot;
+    formatWidth = 0
+    additionalRows = 0
+
</ins><span class="cx">     def __init__(self, usesCurses, client):
</span><span class="cx">         self.usesCurses = usesCurses
</span><span class="cx">         self.client = client
</span><span class="lines">@@ -399,11 +404,25 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def makeWindow(self, top=0, left=0):
</span><ins>+        self.updateRowCount()
+        self._createWindow(
+            self.windowTitle,
+            self.rowCount + self.additionalRows,
+            self.formatWidth,
+            begin_y=top, begin_x=left
+        )
+        return self
+
+
+    def updateRowCount(self):
+        &quot;&quot;&quot;
+        Update L{self.rowCount} based on the current data
+        &quot;&quot;&quot;
</ins><span class="cx">         raise NotImplementedError()
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def _createWindow(
</span><del>-        self, title, nlines, ncols=BOX_WIDTH, begin_y=0, begin_x=0
</del><ins>+        self, title, nlines, ncols, begin_y=0, begin_x=0
</ins><span class="cx">     ):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Initialize a curses window based on the sizes required.
</span><span class="lines">@@ -482,7 +501,6 @@
</span><span class="cx">     help = &quot;dashboard help&quot;
</span><span class="cx">     all = False
</span><span class="cx">     helpItems = (
</span><del>-        &quot;&quot;,
</del><span class="cx">         &quot;a - all windows&quot;,
</span><span class="cx">         &quot;n - no windows&quot;,
</span><span class="cx">         &quot;  - (space) pause dashboard polling&quot;,
</span><span class="lines">@@ -491,19 +509,20 @@
</span><span class="cx">         &quot;q - exit the dashboard&quot;,
</span><span class="cx">     )
</span><span class="cx"> 
</span><ins>+    windowTitle = &quot;Help&quot;
+    formatWidth = 48
+    additionalRows = 3
+
</ins><span class="cx">     def makeWindow(self, top=0, left=0):
</span><span class="cx">         term_w, _ignore_term_h = terminal_size()
</span><del>-        help_x_offset = term_w - BOX_WIDTH + 4
-        self._createWindow(
-            &quot;Help&quot;,
-            len(self.helpItems) + len(Dashboard.registered_windows) + 2,
-            ncols=BOX_WIDTH - 4,
-            begin_y=0,
-            begin_x=help_x_offset,
-        )
-        return self
</del><ins>+        help_x_offset = term_w - self.formatWidth
+        return super(HelpWindow, self).makeWindow(0, help_x_offset)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def updateRowCount(self):
+        self.rowCount = len(self.helpItems) + len(filter(lambda x: len(x[1]) != 0, Dashboard.registered_window_sets.values())) + len(Dashboard.registered_windows)
+
+
</ins><span class="cx">     def requiresUpdate(self):
</span><span class="cx">         return False
</span><span class="cx"> 
</span><span class="lines">@@ -526,6 +545,13 @@
</span><span class="cx">             Dashboard.registered_windows.items(), key=lambda x: x[0]
</span><span class="cx">         ):
</span><span class="cx">             items.append(&quot;{} - {}&quot;.format(keypress, wtype.help))
</span><ins>+
+        items.append(&quot;&quot;)
+        for key, value in Dashboard.registered_window_sets.items():
+            description, panels = value
+            if panels:
+                items.append(&quot;{} - {}&quot;.format(key, description))
+
</ins><span class="cx">         items.extend(self.helpItems)
</span><span class="cx">         for item in items:
</span><span class="cx">             if self.usesCurses:
</span><span class="lines">@@ -549,15 +575,15 @@
</span><span class="cx"> 
</span><span class="cx">     help = &quot;server jobs&quot;
</span><span class="cx">     clientItem = &quot;jobs&quot;
</span><del>-    FORMAT_WIDTH = 98
</del><span class="cx"> 
</span><del>-    def makeWindow(self, top=0, left=0):
-        nlines = defaultIfNone(self.readItem(&quot;jobcount&quot;), 0)
-        self.rowCount = nlines
-        self._createWindow(&quot;Jobs&quot;, self.rowCount + 6, ncols=self.FORMAT_WIDTH, begin_y=top, begin_x=left)
-        return self
</del><ins>+    windowTitle = &quot;Jobs&quot;
+    formatWidth = 98
+    additionalRows = 6
</ins><span class="cx"> 
</span><ins>+    def updateRowCount(self):
+        self.rowCount = defaultIfNone(self.readItem(&quot;jobcount&quot;), 0)
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def update(self):
</span><span class="cx">         records = defaultIfNone(self.clientData(), {})
</span><span class="cx">         if len(records) != self.rowCount:
</span><span class="lines">@@ -640,7 +666,7 @@
</span><span class="cx">             safeDivision(total_time, total_completed, 1000.0)
</span><span class="cx">         )
</span><span class="cx">         if self.usesCurses:
</span><del>-            self.window.hline(y, x, &quot;-&quot;, self.FORMAT_WIDTH - 2)
</del><ins>+            self.window.hline(y, x, &quot;-&quot;, self.formatWidth - 2)
</ins><span class="cx">             y += 1
</span><span class="cx">             self.window.addstr(y, x, s)
</span><span class="cx">         else:
</span><span class="lines">@@ -662,18 +688,15 @@
</span><span class="cx"> 
</span><span class="cx">     help = &quot;server child job assignments&quot;
</span><span class="cx">     clientItem = &quot;job_assignments&quot;
</span><del>-    FORMAT_WIDTH = 40
</del><span class="cx"> 
</span><del>-    def makeWindow(self, top=0, left=0):
-        slots = defaultIfNone(self.readItem(self.clientItem), {&quot;workers&quot;: ()})[&quot;workers&quot;]
-        self.rowCount = len(slots)
-        self._createWindow(
-            &quot;Job Assignments&quot;, self.rowCount + 5, self.FORMAT_WIDTH,
-            begin_y=top, begin_x=left
-        )
-        return self
</del><ins>+    windowTitle = &quot;Job Assignments&quot;
+    formatWidth = 40
+    additionalRows = 5
</ins><span class="cx"> 
</span><ins>+    def updateRowCount(self):
+        self.rowCount = len(defaultIfNone(self.readItem(self.clientItem), {&quot;workers&quot;: ()})[&quot;workers&quot;])
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def update(self):
</span><span class="cx">         data = defaultIfNone(self.clientData(), {&quot;workers&quot;: {}, &quot;level&quot;: 0})
</span><span class="cx">         records = data[&quot;workers&quot;]
</span><span class="lines">@@ -736,7 +759,7 @@
</span><span class="cx">             total_completed,
</span><span class="cx">         )
</span><span class="cx">         if self.usesCurses:
</span><del>-            self.window.hline(y, x, &quot;-&quot;, self.FORMAT_WIDTH - 2)
</del><ins>+            self.window.hline(y, x, &quot;-&quot;, self.formatWidth - 2)
</ins><span class="cx">             y += 1
</span><span class="cx">             self.window.addstr(y, x, s)
</span><span class="cx">         else:
</span><span class="lines">@@ -758,18 +781,15 @@
</span><span class="cx"> 
</span><span class="cx">     help = &quot;server child slots&quot;
</span><span class="cx">     clientItem = &quot;slots&quot;
</span><del>-    FORMAT_WIDTH = 72
</del><span class="cx"> 
</span><del>-    def makeWindow(self, top=0, left=0):
-        slots = defaultIfNone(self.readItem(self.clientItem), {&quot;slots&quot;: ()})[&quot;slots&quot;]
-        self.rowCount = len(slots)
-        self._createWindow(
-            &quot;HTTP Slots&quot;, self.rowCount + 5, self.FORMAT_WIDTH,
-            begin_y=top, begin_x=left
-        )
-        return self
</del><ins>+    windowTitle = &quot;HTTP Slots&quot;
+    formatWidth = 72
+    additionalRows = 5
</ins><span class="cx"> 
</span><ins>+    def updateRowCount(self):
+        self.rowCount = len(defaultIfNone(self.readItem(self.clientItem), {&quot;slots&quot;: ()})[&quot;slots&quot;])
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def update(self):
</span><span class="cx">         data = defaultIfNone(self.clientData(), {&quot;slots&quot;: {}, &quot;overloaded&quot;: False})
</span><span class="cx">         records = data[&quot;slots&quot;]
</span><span class="lines">@@ -840,7 +860,7 @@
</span><span class="cx">             sum([record[&quot;total&quot;] for record in records]),
</span><span class="cx">         )
</span><span class="cx">         if self.usesCurses:
</span><del>-            self.window.hline(y, x, &quot;-&quot;, self.FORMAT_WIDTH - 2)
</del><ins>+            self.window.hline(y, x, &quot;-&quot;, self.formatWidth - 2)
</ins><span class="cx">             y += 1
</span><span class="cx">             self.window.addstr(y, x, s)
</span><span class="cx">             x += len(s) + 4
</span><span class="lines">@@ -871,13 +891,14 @@
</span><span class="cx">     help = &quot;system details&quot;
</span><span class="cx">     clientItem = &quot;stats_system&quot;
</span><span class="cx"> 
</span><del>-    def makeWindow(self, top=0, left=0):
-        slots = defaultIfNone(self.readItem(self.clientItem), (1, 2, 3, 4,))
-        self.rowCount = len(slots)
-        self._createWindow(&quot;System&quot;, self.rowCount + 3, begin_y=top, begin_x=left)
-        return self
</del><ins>+    windowTitle = &quot;System&quot;
+    formatWidth = 52
+    additionalRows = 3
</ins><span class="cx"> 
</span><ins>+    def updateRowCount(self):
+        self.rowCount = len(defaultIfNone(self.readItem(self.clientItem), (1, 2, 3, 4,)))
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def update(self):
</span><span class="cx">         records = defaultIfNone(self.clientData(), {
</span><span class="cx">             &quot;cpu use&quot;: 0.0,
</span><span class="lines">@@ -952,13 +973,15 @@
</span><span class="cx"> 
</span><span class="cx">     help = &quot;server request stats&quot;
</span><span class="cx">     clientItem = &quot;stats&quot;
</span><del>-    FORMAT_WIDTH = 84
</del><span class="cx"> 
</span><del>-    def makeWindow(self, top=0, left=0):
-        self._createWindow(&quot;Request Statistics&quot;, 8, self.FORMAT_WIDTH, begin_y=top, begin_x=left)
-        return self
</del><ins>+    windowTitle = &quot;Request Statistics&quot;
+    formatWidth = 84
+    additionalRows = 4
</ins><span class="cx"> 
</span><ins>+    def updateRowCount(self):
+        self.rowCount = 4
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def update(self):
</span><span class="cx">         records = defaultIfNone(self.clientData(), {})
</span><span class="cx">         self.iter += 1
</span><span class="lines">@@ -1031,18 +1054,19 @@
</span><span class="cx"> 
</span><span class="cx">     help = &quot;server methods&quot;
</span><span class="cx">     clientItem = &quot;stats&quot;
</span><del>-    FORMAT_WIDTH = 116
</del><span class="cx">     stats_keys = (&quot;current&quot;, &quot;1m&quot;, &quot;5m&quot;, &quot;1h&quot;,)
</span><span class="cx"> 
</span><del>-    def makeWindow(self, top=0, left=0):
</del><ins>+    windowTitle = &quot;Methods&quot;
+    formatWidth = 116
+    additionalRows = 7
+
+    def updateRowCount(self):
</ins><span class="cx">         stats = defaultIfNone(self.clientData(), {})
</span><span class="cx">         methods = set()
</span><span class="cx">         for key in self.stats_keys:
</span><span class="cx">             methods.update(stats.get(key, {}).get(&quot;method&quot;, {}).keys())
</span><span class="cx">         nlines = len(methods)
</span><span class="cx">         self.rowCount = nlines
</span><del>-        self._createWindow(&quot;Methods&quot;, self.rowCount + 7, ncols=self.FORMAT_WIDTH, begin_y=top, begin_x=left)
-        return self
</del><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def update(self):
</span><span class="lines">@@ -1128,7 +1152,7 @@
</span><span class="cx">             *items
</span><span class="cx">         )
</span><span class="cx">         if self.usesCurses:
</span><del>-            self.window.hline(y, x, &quot;-&quot;, self.FORMAT_WIDTH - 2)
</del><ins>+            self.window.hline(y, x, &quot;-&quot;, self.formatWidth - 2)
</ins><span class="cx">             y += 1
</span><span class="cx">             self.window.addstr(y, x, s1)
</span><span class="cx">             y += 1
</span><span class="lines">@@ -1153,17 +1177,13 @@
</span><span class="cx"> 
</span><span class="cx">     help = &quot;directory service stats&quot;
</span><span class="cx">     clientItem = &quot;directory&quot;
</span><del>-    FORMAT_WIDTH = 89
</del><span class="cx"> 
</span><ins>+    windowTitle = &quot;Directory Service&quot;
+    formatWidth = 89
+    additionalRows = 8
</ins><span class="cx"> 
</span><del>-    def makeWindow(self, top=0, left=0):
-        nlines = len(defaultIfNone(self.readItem(&quot;directory&quot;), {}))
-        self.rowCount = nlines
-        self._createWindow(
-            &quot;Directory Service&quot;, self.rowCount + 8, ncols=self.FORMAT_WIDTH,
-            begin_y=top, begin_x=left
-        )
-        return self
</del><ins>+    def updateRowCount(self):
+        self.rowCount = len(defaultIfNone(self.readItem(&quot;directory&quot;), {}))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def update(self):
</span><span class="lines">@@ -1250,7 +1270,7 @@
</span><span class="cx">             &quot;&quot;,
</span><span class="cx">         )
</span><span class="cx">         if self.usesCurses:
</span><del>-            self.window.hline(y, x, &quot;-&quot;, self.FORMAT_WIDTH - 2)
</del><ins>+            self.window.hline(y, x, &quot;-&quot;, self.formatWidth - 2)
</ins><span class="cx">             y += 1
</span><span class="cx">             self.window.addstr(y, x, s)
</span><span class="cx">             self.window.addstr(y + 1, x, s_cached)
</span><span class="lines">@@ -1269,13 +1289,21 @@
</span><span class="cx"> 
</span><span class="cx"> Dashboard.registerWindow(HelpWindow, &quot;h&quot;)
</span><span class="cx"> Dashboard.registerWindow(SystemWindow, &quot;s&quot;)
</span><del>-Dashboard.registerWindow(AssignmentsWindow, &quot;w&quot;)
</del><span class="cx"> Dashboard.registerWindow(RequestStatsWindow, &quot;r&quot;)
</span><ins>+Dashboard.registerWindow(HTTPSlotsWindow, &quot;c&quot;)
</ins><span class="cx"> Dashboard.registerWindow(MethodsWindow, &quot;m&quot;)
</span><ins>+Dashboard.registerWindow(AssignmentsWindow, &quot;w&quot;)
</ins><span class="cx"> Dashboard.registerWindow(JobsWindow, &quot;j&quot;)
</span><del>-Dashboard.registerWindow(HTTPSlotsWindow, &quot;c&quot;)
</del><span class="cx"> Dashboard.registerWindow(DirectoryStatsWindow, &quot;d&quot;)
</span><span class="cx"> 
</span><ins>+Dashboard.registerWindowSet(SystemWindow, &quot;H&quot;)
+Dashboard.registerWindowSet(RequestStatsWindow, &quot;H&quot;)
+Dashboard.registerWindowSet(HTTPSlotsWindow, &quot;H&quot;)
+Dashboard.registerWindowSet(MethodsWindow, &quot;H&quot;)
</ins><span class="cx"> 
</span><ins>+Dashboard.registerWindowSet(SystemWindow, &quot;J&quot;)
+Dashboard.registerWindowSet(AssignmentsWindow, &quot;J&quot;)
+Dashboard.registerWindowSet(JobsWindow, &quot;J&quot;)
+
</ins><span class="cx"> if __name__ == &quot;__main__&quot;:
</span><span class="cx">     main()
</span></span></pre>
</div>
</div>

</body>
</html>