webish: make upload timings visible on the recent uploads/downloads status page

This commit is contained in:
Brian Warner 2008-03-03 14:48:52 -07:00
parent 436baa1b19
commit c8e24f0904
8 changed files with 153 additions and 65 deletions

View File

@ -267,6 +267,7 @@ class Client(node.Node, testutil.PollMixin):
uploader = self.getServiceNamed("uploader") uploader = self.getServiceNamed("uploader")
return uploader.upload(uploadable) return uploader.upload(uploadable)
def list_all_uploads(self): def list_all_uploads(self):
uploader = self.getServiceNamed("uploader") uploader = self.getServiceNamed("uploader")
return uploader.list_all_uploads() return uploader.list_all_uploads()
@ -275,6 +276,16 @@ class Client(node.Node, testutil.PollMixin):
downloader = self.getServiceNamed("downloader") downloader = self.getServiceNamed("downloader")
return downloader.list_all_downloads() 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): def list_recent_uploads(self):
uploader = self.getServiceNamed("uploader") uploader = self.getServiceNamed("uploader")
return uploader.list_recent_uploads() return uploader.list_recent_uploads()

View File

@ -949,5 +949,8 @@ class Downloader(service.MultiService):
def list_all_downloads(self): def list_all_downloads(self):
return self._all_downloads.keys() 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): def list_recent_downloads(self):
return self._recent_download_status return self._recent_download_status

View File

@ -1389,19 +1389,21 @@ class IClient(Interface):
class IClientStatus(Interface): class IClientStatus(Interface):
def list_all_uploads(): def list_all_uploads():
"""Return a list of IUploadStatus objects, one for each upload which """Return a list of uploader objects, one for each upload which
currently has an object available. This uses weakrefs to track the currently has an object available (tracked with weakrefs). This is
objects, so it may report uploads which have already finished. Use intended for debugging purposes."""
get_active() to filter these out.""" def list_active_uploads():
"""Return a list of active IUploadStatus objects."""
def list_recent_uploads(): def list_recent_uploads():
"""Return a list of IUploadStatus objects for the most recently """Return a list of IUploadStatus objects for the most recently
started uploads.""" started uploads."""
def list_all_downloads(): def list_all_downloads():
"""Return a list of IDownloadStatus objects, one for each download """Return a list of downloader objects, one for each download which
which currently has an object available. This uses weakrefs to track currently has an object available (tracked with weakrefs). This is
the objects, so it may report downloadswhich have already finished. intended for debugging purposes."""
Use get_active() to filter these out.""" def list_active_downloads():
"""Return a list of active IDownloadStatus objects."""
def list_recent_downloads(): def list_recent_downloads():
"""Return a list of IDownloadStatus objects for the most recently """Return a list of IDownloadStatus objects for the most recently
started downloads.""" started downloads."""
@ -1433,6 +1435,10 @@ class IUploadStatus(Interface):
three numbers and report the sum to the user.""" three numbers and report the sum to the user."""
def get_active(): def get_active():
"""Return True if the upload is currently active, False if not.""" """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(): def get_counter():
"""Each upload status gets a unique number: this method returns that """Each upload status gets a unique number: this method returns that
number. This provides a handle to this particular upload, so a web number. This provides a handle to this particular upload, so a web

View File

@ -32,8 +32,8 @@ class FakeClient(service.MultiService):
} }
introducer_furl = "None" introducer_furl = "None"
introducer_client = FakeIntroducerClient() introducer_client = FakeIntroducerClient()
_all_uploads = [upload.UploadStatus()] _all_upload_status = [upload.UploadStatus()]
_all_downloads = [download.DownloadStatus()] _all_download_status = [download.DownloadStatus()]
def connected_to_introducer(self): def connected_to_introducer(self):
return False return False
@ -71,14 +71,18 @@ class FakeClient(service.MultiService):
return d return d
def list_all_uploads(self): def list_all_uploads(self):
return self._all_uploads return []
def list_all_downloads(self): 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): def list_recent_uploads(self):
return self._all_uploads return self._all_upload_status
def list_recent_downloads(self): def list_recent_downloads(self):
return self._all_downloads return self._all_download_status
class WebMixin(object): class WebMixin(object):

View File

@ -573,6 +573,7 @@ class UploadStatus:
self.status = "Not started" self.status = "Not started"
self.progress = [0.0, 0.0, 0.0] self.progress = [0.0, 0.0, 0.0]
self.active = True self.active = True
self.results = None
self.counter = self.statusid_counter.next() self.counter = self.statusid_counter.next()
def get_storage_index(self): def get_storage_index(self):
@ -587,6 +588,8 @@ class UploadStatus:
return tuple(self.progress) return tuple(self.progress)
def get_active(self): def get_active(self):
return self.active return self.active
def get_results(self):
return self.results
def get_counter(self): def get_counter(self):
return self.counter return self.counter
@ -603,6 +606,8 @@ class UploadStatus:
self.progress[which] = value self.progress[which] = value
def set_active(self, value): def set_active(self, value):
self.active = value self.active = value
def set_results(self, value):
self.results = value
class CHKUploader: class CHKUploader:
peer_selector_class = Tahoe2PeerSelector peer_selector_class = Tahoe2PeerSelector
@ -616,6 +621,7 @@ class CHKUploader:
self._upload_status = UploadStatus() self._upload_status = UploadStatus()
self._upload_status.set_helper(False) self._upload_status.set_helper(False)
self._upload_status.set_active(True) self._upload_status.set_active(True)
self._upload_status.set_results(self._results)
def log(self, *args, **kwargs): def log(self, *args, **kwargs):
if "parent" not in kwargs: if "parent" not in kwargs:
@ -776,6 +782,7 @@ class LiteralUploader:
s.set_helper(False) s.set_helper(False)
s.set_progress(0, 1.0) s.set_progress(0, 1.0)
s.set_active(False) s.set_active(False)
s.set_results(self._results)
def start(self, uploadable): def start(self, uploadable):
uploadable = IUploadable(uploadable) uploadable = IUploadable(uploadable)
@ -971,6 +978,7 @@ class AssistedUploader:
return d return d
self.log("helper says file is already uploaded") self.log("helper says file is already uploaded")
self._upload_status.set_progress(1, 1.0) self._upload_status.set_progress(1, 1.0)
self._upload_status.set_results(upload_results)
return upload_results return upload_results
def _build_readcap(self, upload_results): def _build_readcap(self, upload_results):
@ -996,6 +1004,7 @@ class AssistedUploader:
r.timings["helper_total"] = r.timings["total"] r.timings["helper_total"] = r.timings["total"]
r.timings["total"] = now - self._started r.timings["total"] = now - self._started
self._upload_status.set_status("Done") self._upload_status.set_status("Done")
self._upload_status.set_results(r)
return r return r
def get_upload_status(self): def get_upload_status(self):
@ -1200,5 +1209,8 @@ class Uploader(service.MultiService):
def list_all_uploads(self): def list_all_uploads(self):
return self._all_uploads.keys() 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): def list_recent_uploads(self):
return self._recent_upload_status return self._recent_upload_status

View File

@ -20,6 +20,42 @@
<li>Status: <span n:render="status"/></li> <li>Status: <span n:render="status"/></li>
</ul> </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> <div>Return to the <a href="/">Welcome Page</a></div>
</body> </body>

View File

@ -1372,51 +1372,8 @@ class UnlinkedPUTCreateDirectory(rend.Page):
# XXX add redirect_to_result # XXX add redirect_to_result
return d return d
class UploadResultsRendererMixin:
class UnlinkedPOSTCHKUploader(rend.Page): # this requires a method named 'upload_results'
"""'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
def render_sharemap(self, ctx, data): def render_sharemap(self, ctx, data):
d = self.upload_results() d = self.upload_results()
@ -1574,6 +1531,51 @@ class UnlinkedPOSTCHKUploader(rend.Page):
d.addCallback(_convert) d.addCallback(_convert)
return d 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): class UnlinkedPOSTSSKUploader(rend.Page):
def renderHTTP(self, ctx): def renderHTTP(self, ctx):
req = inevow.IRequest(ctx) req = inevow.IRequest(ctx)
@ -1608,9 +1610,25 @@ class UnlinkedPOSTCreateDirectory(rend.Page):
d.addCallback(lambda dirnode: dirnode.get_uri()) d.addCallback(lambda dirnode: dirnode.get_uri())
return d return d
class UploadStatusPage(rend.Page): class UploadStatusPage(UploadResultsRendererMixin, rend.Page):
docFactory = getxmlfile("upload-status.xhtml") 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): def render_si(self, ctx, data):
si_s = base32.b2a_or_none(data.get_storage_index()) si_s = base32.b2a_or_none(data.get_storage_index())
if si_s is None: if si_s is None:
@ -1677,11 +1695,9 @@ class Status(rend.Page):
addSlash = True addSlash = True
def data_active_uploads(self, ctx, data): def data_active_uploads(self, ctx, data):
return [u for u in IClient(ctx).list_all_uploads() return [u for u in IClient(ctx).list_active_uploads()]
if u.get_active()]
def data_active_downloads(self, ctx, data): def data_active_downloads(self, ctx, data):
return [d for d in IClient(ctx).list_all_downloads() return [d for d in IClient(ctx).list_active_downloads()]
if d.get_active()]
def data_recent_uploads(self, ctx, data): def data_recent_uploads(self, ctx, data):
return [u for u in IClient(ctx).list_recent_uploads() return [u for u in IClient(ctx).list_recent_uploads()
if not u.get_active()] if not u.get_active()]