webish: make upload timings visible on the recent uploads/downloads status page
authorBrian Warner <warner@allmydata.com>
Mon, 3 Mar 2008 21:48:52 +0000 (14:48 -0700)
committerBrian Warner <warner@allmydata.com>
Mon, 3 Mar 2008 21:48:52 +0000 (14:48 -0700)
src/allmydata/client.py
src/allmydata/download.py
src/allmydata/interfaces.py
src/allmydata/test/test_web.py
src/allmydata/upload.py
src/allmydata/web/unlinked-upload.xhtml [deleted file]
src/allmydata/web/upload-results.xhtml [new file with mode: 0644]
src/allmydata/web/upload-status.xhtml
src/allmydata/webish.py

index c8c1b1832eb19fa6b318c558b94a84f535682ccc..c20d422b7a277cc783bab4374cfefff0be91ce1c 100644 (file)
@@ -267,6 +267,7 @@ class Client(node.Node, testutil.PollMixin):
         uploader = self.getServiceNamed("uploader")
         return uploader.upload(uploadable)
 
+
     def list_all_uploads(self):
         uploader = self.getServiceNamed("uploader")
         return uploader.list_all_uploads()
@@ -275,6 +276,16 @@ class Client(node.Node, testutil.PollMixin):
         downloader = self.getServiceNamed("downloader")
         return downloader.list_all_downloads()
 
+
+    def list_active_uploads(self):
+        uploader = self.getServiceNamed("uploader")
+        return uploader.list_active_uploads()
+
+    def list_active_downloads(self):
+        downloader = self.getServiceNamed("downloader")
+        return downloader.list_active_downloads()
+
+
     def list_recent_uploads(self):
         uploader = self.getServiceNamed("uploader")
         return uploader.list_recent_uploads()
index 83bebb37657416a502e9c580c2b87a00be8e2b4b..907f9387ebb42011af552feb4d3558706279e0a2 100644 (file)
@@ -949,5 +949,8 @@ class Downloader(service.MultiService):
 
     def list_all_downloads(self):
         return self._all_downloads.keys()
+    def list_active_downloads(self):
+        return [d.get_download_status() for d in self._all_downloads.keys()
+                if d.get_download_status().get_active()]
     def list_recent_downloads(self):
         return self._recent_download_status
index 35bf0a34115fce66fd42fc2d0803f4eea9967f27..2d7f71edea2fc3c43fdc8e98c47ad6cf41411223 100644 (file)
@@ -1389,19 +1389,21 @@ class IClient(Interface):
 
 class IClientStatus(Interface):
     def list_all_uploads():
-        """Return a list of IUploadStatus objects, one for each upload which
-        currently has an object available. This uses weakrefs to track the
-        objects, so it may report uploads which have already finished. Use
-        get_active() to filter these out."""
+        """Return a list of uploader objects, one for each upload which
+        currently has an object available (tracked with weakrefs). This is
+        intended for debugging purposes."""
+    def list_active_uploads():
+        """Return a list of active IUploadStatus objects."""
     def list_recent_uploads():
         """Return a list of IUploadStatus objects for the most recently
         started uploads."""
 
     def list_all_downloads():
-        """Return a list of IDownloadStatus objects, one for each download
-        which currently has an object available. This uses weakrefs to track
-        the objects, so it may report downloadswhich have already finished.
-        Use get_active() to filter these out."""
+        """Return a list of downloader objects, one for each download which
+        currently has an object available (tracked with weakrefs). This is
+        intended for debugging purposes."""
+    def list_active_downloads():
+        """Return a list of active IDownloadStatus objects."""
     def list_recent_downloads():
         """Return a list of IDownloadStatus objects for the most recently
         started downloads."""
@@ -1433,6 +1435,10 @@ class IUploadStatus(Interface):
         three numbers and report the sum to the user."""
     def get_active():
         """Return True if the upload is currently active, False if not."""
+    def get_results():
+        """Return an instance of UploadResults (which contains timing and
+        sharemap information). Might return None if the upload is not yet
+        finished."""
     def get_counter():
         """Each upload status gets a unique number: this method returns that
         number. This provides a handle to this particular upload, so a web
index 7f686d2bcaffca44a1914e61402439711fff4085..9678ae52e110f5dba1f0c47f750772a0ddfabba6 100644 (file)
@@ -32,8 +32,8 @@ class FakeClient(service.MultiService):
                 }
     introducer_furl = "None"
     introducer_client = FakeIntroducerClient()
-    _all_uploads = [upload.UploadStatus()]
-    _all_downloads = [download.DownloadStatus()]
+    _all_upload_status = [upload.UploadStatus()]
+    _all_download_status = [download.DownloadStatus()]
 
     def connected_to_introducer(self):
         return False
@@ -71,14 +71,18 @@ class FakeClient(service.MultiService):
         return d
 
     def list_all_uploads(self):
-        return self._all_uploads
+        return []
     def list_all_downloads(self):
-        return self._all_downloads
+        return []
 
+    def list_active_uploads(self):
+        return self._all_upload_status
+    def list_active_downloads(self):
+        return self._all_download_status
     def list_recent_uploads(self):
-        return self._all_uploads
+        return self._all_upload_status
     def list_recent_downloads(self):
-        return self._all_downloads
+        return self._all_download_status
 
 
 class WebMixin(object):
@@ -1331,7 +1335,7 @@ class Web(WebMixin, unittest.TestCase):
                                                   "ctime": 1002777696.7564139,
                                                   "mtime": 1002777696.7564139
                                                  }
-                                               } ], 
+                                               } ],
                      "atomic_added_2": [ "filenode", { "rw_uri": "%s",
                                                 "size": 1,
                                                 "metadata": {
index 04cc9317d67f402d800c47459503106f6b02e898..c3dd29dfa3e3440837cb75356cc2515b7cac4cfc 100644 (file)
@@ -573,6 +573,7 @@ class UploadStatus:
         self.status = "Not started"
         self.progress = [0.0, 0.0, 0.0]
         self.active = True
+        self.results = None
         self.counter = self.statusid_counter.next()
 
     def get_storage_index(self):
@@ -587,6 +588,8 @@ class UploadStatus:
         return tuple(self.progress)
     def get_active(self):
         return self.active
+    def get_results(self):
+        return self.results
     def get_counter(self):
         return self.counter
 
@@ -603,6 +606,8 @@ class UploadStatus:
         self.progress[which] = value
     def set_active(self, value):
         self.active = value
+    def set_results(self, value):
+        self.results = value
 
 class CHKUploader:
     peer_selector_class = Tahoe2PeerSelector
@@ -616,6 +621,7 @@ class CHKUploader:
         self._upload_status = UploadStatus()
         self._upload_status.set_helper(False)
         self._upload_status.set_active(True)
+        self._upload_status.set_results(self._results)
 
     def log(self, *args, **kwargs):
         if "parent" not in kwargs:
@@ -776,6 +782,7 @@ class LiteralUploader:
         s.set_helper(False)
         s.set_progress(0, 1.0)
         s.set_active(False)
+        s.set_results(self._results)
 
     def start(self, uploadable):
         uploadable = IUploadable(uploadable)
@@ -971,6 +978,7 @@ class AssistedUploader:
             return d
         self.log("helper says file is already uploaded")
         self._upload_status.set_progress(1, 1.0)
+        self._upload_status.set_results(upload_results)
         return upload_results
 
     def _build_readcap(self, upload_results):
@@ -996,6 +1004,7 @@ class AssistedUploader:
             r.timings["helper_total"] = r.timings["total"]
         r.timings["total"] = now - self._started
         self._upload_status.set_status("Done")
+        self._upload_status.set_results(r)
         return r
 
     def get_upload_status(self):
@@ -1200,5 +1209,8 @@ class Uploader(service.MultiService):
 
     def list_all_uploads(self):
         return self._all_uploads.keys()
+    def list_active_uploads(self):
+        return [u.get_upload_status() for u in self._all_uploads.keys()
+                if u.get_upload_status().get_active()]
     def list_recent_uploads(self):
         return self._recent_upload_status
diff --git a/src/allmydata/web/unlinked-upload.xhtml b/src/allmydata/web/unlinked-upload.xhtml
deleted file mode 100644 (file)
index 8c6000f..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<html xmlns:n="http://nevow.com/ns/nevow/0.1">
-  <head>
-    <title>AllMyData - Tahoe - File Uploaded</title>
-    <!-- <link href="http://www.allmydata.com/common/css/styles.css"
-          rel="stylesheet" type="text/css"/> -->
-    <link href="/webform_css" rel="stylesheet" type="text/css"/>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-  </head>
-  <body>
-
-<h1>Uploading File... <span n:render="string" n:data="done" /></h1>
-
-<h2>Upload Results:</h2>
-<ul>
-  <li>URI: <tt><span n:render="string" n:data="uri" /></tt></li>
-  <li>Download link: <span n:render="download_link" /></li>
-  <li>Sharemap: <span n:render="sharemap" /></li>
-  <li>Servermap: <span n:render="servermap" /></li>
-  <li>Timings:</li>
-  <ul>
-    <li>File Size: <span n:render="string" n:data="file_size" /> bytes</li>
-    <li>Total: <span n:render="time" n:data="time_total" />
-     (<span n:render="rate" n:data="rate_total" />)</li>
-    <ul>
-      <li>Storage Index: <span n:render="time" n:data="time_storage_index" />
-     (<span n:render="rate" n:data="rate_storage_index" />)</li>
-      <li>[Contacting Helper]: <span n:render="time" n:data="time_contacting_helper" /></li>
-      <ul>
-        <li>[Helper Already-In-Grid Check]: <span n:render="time" n:data="time_existence_check" /></li>
-      </ul>
-      <li>[Upload Ciphertext To Helper]: <span n:render="time" n:data="time_cumulative_fetch" />
-     (<span n:render="rate" n:data="rate_ciphertext_fetch" />)</li>
-
-      <li>Peer Selection: <span n:render="time" n:data="time_peer_selection" /></li>
-      <li>Encode And Push: <span n:render="time" n:data="time_total_encode_and_push" />
-        (<span n:render="rate" n:data="rate_encode_and_push" />)</li>
-      <ul>
-        <li>Cumulative Encoding: <span n:render="time" n:data="time_cumulative_encoding" />
-        (<span n:render="rate" n:data="rate_encode" />)</li>
-        <li>Cumulative Pushing: <span n:render="time" n:data="time_cumulative_sending" />
-        (<span n:render="rate" n:data="rate_push" />)</li>
-        <li>Send Hashes And Close: <span n:render="time" n:data="time_hashes_and_close" /></li>
-      </ul>
-      <li>[Helper Total]: <span n:render="time" n:data="time_helper_total" /></li>
-    </ul>
-  </ul>
-</ul>
-
-<div>Return to the <a href="/">Welcome Page</a></div>
-
-  </body>
-</html>
diff --git a/src/allmydata/web/upload-results.xhtml b/src/allmydata/web/upload-results.xhtml
new file mode 100644 (file)
index 0000000..8c6000f
--- /dev/null
@@ -0,0 +1,52 @@
+<html xmlns:n="http://nevow.com/ns/nevow/0.1">
+  <head>
+    <title>AllMyData - Tahoe - File Uploaded</title>
+    <!-- <link href="http://www.allmydata.com/common/css/styles.css"
+          rel="stylesheet" type="text/css"/> -->
+    <link href="/webform_css" rel="stylesheet" type="text/css"/>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  </head>
+  <body>
+
+<h1>Uploading File... <span n:render="string" n:data="done" /></h1>
+
+<h2>Upload Results:</h2>
+<ul>
+  <li>URI: <tt><span n:render="string" n:data="uri" /></tt></li>
+  <li>Download link: <span n:render="download_link" /></li>
+  <li>Sharemap: <span n:render="sharemap" /></li>
+  <li>Servermap: <span n:render="servermap" /></li>
+  <li>Timings:</li>
+  <ul>
+    <li>File Size: <span n:render="string" n:data="file_size" /> bytes</li>
+    <li>Total: <span n:render="time" n:data="time_total" />
+     (<span n:render="rate" n:data="rate_total" />)</li>
+    <ul>
+      <li>Storage Index: <span n:render="time" n:data="time_storage_index" />
+     (<span n:render="rate" n:data="rate_storage_index" />)</li>
+      <li>[Contacting Helper]: <span n:render="time" n:data="time_contacting_helper" /></li>
+      <ul>
+        <li>[Helper Already-In-Grid Check]: <span n:render="time" n:data="time_existence_check" /></li>
+      </ul>
+      <li>[Upload Ciphertext To Helper]: <span n:render="time" n:data="time_cumulative_fetch" />
+     (<span n:render="rate" n:data="rate_ciphertext_fetch" />)</li>
+
+      <li>Peer Selection: <span n:render="time" n:data="time_peer_selection" /></li>
+      <li>Encode And Push: <span n:render="time" n:data="time_total_encode_and_push" />
+        (<span n:render="rate" n:data="rate_encode_and_push" />)</li>
+      <ul>
+        <li>Cumulative Encoding: <span n:render="time" n:data="time_cumulative_encoding" />
+        (<span n:render="rate" n:data="rate_encode" />)</li>
+        <li>Cumulative Pushing: <span n:render="time" n:data="time_cumulative_sending" />
+        (<span n:render="rate" n:data="rate_push" />)</li>
+        <li>Send Hashes And Close: <span n:render="time" n:data="time_hashes_and_close" /></li>
+      </ul>
+      <li>[Helper Total]: <span n:render="time" n:data="time_helper_total" /></li>
+    </ul>
+  </ul>
+</ul>
+
+<div>Return to the <a href="/">Welcome Page</a></div>
+
+  </body>
+</html>
index 10a6e08a450785209d44f016d0d8214162e61fe4..1d8c7e0fd79e0f871b896808a18eaa00c7bc2b85 100644 (file)
   <li>Status: <span n:render="status"/></li>
 </ul>
 
+<div n:render="results">
+  <h2>Upload Results</h2>
+  <ul>
+    <li>Sharemap: <span n:render="sharemap" /></li>
+    <li>Servermap: <span n:render="servermap" /></li>
+    <li>Timings:</li>
+    <ul>
+      <li>File Size: <span n:render="string" n:data="file_size" /> bytes</li>
+      <li>Total: <span n:render="time" n:data="time_total" />
+      (<span n:render="rate" n:data="rate_total" />)</li>
+      <ul>
+        <li>Storage Index: <span n:render="time" n:data="time_storage_index" />
+        (<span n:render="rate" n:data="rate_storage_index" />)</li>
+        <li>[Contacting Helper]: <span n:render="time" n:data="time_contacting_helper" /></li>
+        <ul>
+          <li>[Helper Already-In-Grid Check]: <span n:render="time" n:data="time_existence_check" /></li>
+        </ul>
+        <li>[Upload Ciphertext To Helper]: <span n:render="time" n:data="time_cumulative_fetch" />
+        (<span n:render="rate" n:data="rate_ciphertext_fetch" />)</li>
+
+        <li>Peer Selection: <span n:render="time" n:data="time_peer_selection" /></li>
+        <li>Encode And Push: <span n:render="time" n:data="time_total_encode_and_push" />
+        (<span n:render="rate" n:data="rate_encode_and_push" />)</li>
+        <ul>
+          <li>Cumulative Encoding: <span n:render="time" n:data="time_cumulative_encoding" />
+          (<span n:render="rate" n:data="rate_encode" />)</li>
+          <li>Cumulative Pushing: <span n:render="time" n:data="time_cumulative_sending" />
+          (<span n:render="rate" n:data="rate_push" />)</li>
+          <li>Send Hashes And Close: <span n:render="time" n:data="time_hashes_and_close" /></li>
+        </ul>
+        <li>[Helper Total]: <span n:render="time" n:data="time_helper_total" /></li>
+      </ul>
+    </ul>
+  </ul>
+</div>
+
 <div>Return to the <a href="/">Welcome Page</a></div>
 
   </body>
index 31192991df999108d4d21682a98ebd9e6e5cf3d2..427e7ef72b7bd0bcee68f8cdb79f4d2febb7faea 100644 (file)
@@ -1372,51 +1372,8 @@ class UnlinkedPUTCreateDirectory(rend.Page):
         # XXX add redirect_to_result
         return d
 
-
-class UnlinkedPOSTCHKUploader(rend.Page):
-    """'POST /uri', to create an unlinked file."""
-    docFactory = getxmlfile("unlinked-upload.xhtml")
-
-    def __init__(self, client, req):
-        rend.Page.__init__(self)
-        # we start the upload now, and distribute notification of its
-        # completion to render_ methods with an ObserverList
-        assert req.method == "POST"
-        self._done = observer.OneShotObserverList()
-        fileobj = req.fields["file"].file
-        uploadable = FileHandle(fileobj)
-        d = client.upload(uploadable)
-        d.addBoth(self._done.fire)
-
-    def renderHTTP(self, ctx):
-        req = inevow.IRequest(ctx)
-        when_done = get_arg(req, "when_done", None)
-        if when_done:
-            # if when_done= is provided, return a redirect instead of our
-            # usual upload-results page
-            d = self._done.when_fired()
-            d.addCallback(lambda res: url.URL.fromString(when_done))
-            return d
-        return rend.Page.renderHTTP(self, ctx)
-
-    def upload_results(self):
-        return self._done.when_fired()
-
-    def data_done(self, ctx, data):
-        d = self.upload_results()
-        d.addCallback(lambda res: "done!")
-        return d
-
-    def data_uri(self, ctx, data):
-        d = self.upload_results()
-        d.addCallback(lambda res: res.uri)
-        return d
-
-    def render_download_link(self, ctx, data):
-        d = self.upload_results()
-        d.addCallback(lambda res: T.a(href="/uri/" + urllib.quote(res.uri))
-                      ["/uri/" + res.uri])
-        return d
+class UploadResultsRendererMixin:
+    # this requires a method named 'upload_results'
 
     def render_sharemap(self, ctx, data):
         d = self.upload_results()
@@ -1574,6 +1531,51 @@ class UnlinkedPOSTCHKUploader(rend.Page):
         d.addCallback(_convert)
         return d
 
+class UnlinkedPOSTCHKUploader(UploadResultsRendererMixin, rend.Page):
+    """'POST /uri', to create an unlinked file."""
+    docFactory = getxmlfile("upload-results.xhtml")
+
+    def __init__(self, client, req):
+        rend.Page.__init__(self)
+        # we start the upload now, and distribute notification of its
+        # completion to render_ methods with an ObserverList
+        assert req.method == "POST"
+        self._done = observer.OneShotObserverList()
+        fileobj = req.fields["file"].file
+        uploadable = FileHandle(fileobj)
+        d = client.upload(uploadable)
+        d.addBoth(self._done.fire)
+
+    def renderHTTP(self, ctx):
+        req = inevow.IRequest(ctx)
+        when_done = get_arg(req, "when_done", None)
+        if when_done:
+            # if when_done= is provided, return a redirect instead of our
+            # usual upload-results page
+            d = self._done.when_fired()
+            d.addCallback(lambda res: url.URL.fromString(when_done))
+            return d
+        return rend.Page.renderHTTP(self, ctx)
+
+    def upload_results(self):
+        return self._done.when_fired()
+
+    def data_done(self, ctx, data):
+        d = self.upload_results()
+        d.addCallback(lambda res: "done!")
+        return d
+
+    def data_uri(self, ctx, data):
+        d = self.upload_results()
+        d.addCallback(lambda res: res.uri)
+        return d
+
+    def render_download_link(self, ctx, data):
+        d = self.upload_results()
+        d.addCallback(lambda res: T.a(href="/uri/" + urllib.quote(res.uri))
+                      ["/uri/" + res.uri])
+        return d
+
 class UnlinkedPOSTSSKUploader(rend.Page):
     def renderHTTP(self, ctx):
         req = inevow.IRequest(ctx)
@@ -1608,9 +1610,25 @@ class UnlinkedPOSTCreateDirectory(rend.Page):
             d.addCallback(lambda dirnode: dirnode.get_uri())
         return d
 
-class UploadStatusPage(rend.Page):
+class UploadStatusPage(UploadResultsRendererMixin, rend.Page):
     docFactory = getxmlfile("upload-status.xhtml")
 
+    def __init__(self, data):
+        rend.Page.__init__(self, data)
+        self.upload_status = data
+
+    def upload_results(self):
+        return defer.maybeDeferred(self.upload_status.get_results)
+
+    def render_results(self, ctx, data):
+        d = self.upload_results()
+        def _got_results(results):
+            if results:
+                return ctx.tag
+            return ""
+        d.addCallback(_got_results)
+        return d
+
     def render_si(self, ctx, data):
         si_s = base32.b2a_or_none(data.get_storage_index())
         if si_s is None:
@@ -1677,11 +1695,9 @@ class Status(rend.Page):
     addSlash = True
 
     def data_active_uploads(self, ctx, data):
-        return [u for u in IClient(ctx).list_all_uploads()
-                if u.get_active()]
+        return [u for u in IClient(ctx).list_active_uploads()]
     def data_active_downloads(self, ctx, data):
-        return [d for d in IClient(ctx).list_all_downloads()
-                if d.get_active()]
+        return [d for d in IClient(ctx).list_active_downloads()]
     def data_recent_uploads(self, ctx, data):
         return [u for u in IClient(ctx).list_recent_uploads()
                 if not u.get_active()]