mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-04-10 04:09:58 +00:00
webish: make upload timings visible on the recent uploads/downloads status page
This commit is contained in:
parent
436baa1b19
commit
c8e24f0904
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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": {
|
||||
|
@ -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
|
||||
|
@ -20,6 +20,42 @@
|
||||
<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>
|
||||
|
@ -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()]
|
||||
|
Loading…
x
Reference in New Issue
Block a user