]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/web/status.py
webish: add publish status
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / web / status.py
1
2 import time
3 from twisted.internet import defer
4 from nevow import rend, tags as T
5 from allmydata.util import base32, idlib
6 from allmydata.web.common import IClient, getxmlfile
7 from allmydata.interfaces import IUploadStatus, IDownloadStatus, \
8      IPublishStatus, IRetrieveStatus
9
10 def plural(sequence_or_length):
11     if isinstance(sequence_or_length, int):
12         length = sequence_or_length
13     else:
14         length = len(sequence_or_length)
15     if length == 1:
16         return ""
17     return "s"
18
19 class RateAndTimeMixin:
20
21     def render_time(self, ctx, data):
22         # 1.23s, 790ms, 132us
23         if data is None:
24             return ""
25         s = float(data)
26         if s >= 1.0:
27             return "%.2fs" % s
28         if s >= 0.01:
29             return "%dms" % (1000*s)
30         if s >= 0.001:
31             return "%.1fms" % (1000*s)
32         return "%dus" % (1000000*s)
33
34     def render_rate(self, ctx, data):
35         # 21.8kBps, 554.4kBps 4.37MBps
36         if data is None:
37             return ""
38         r = float(data)
39         if r > 1000000:
40             return "%1.2fMBps" % (r/1000000)
41         if r > 1000:
42             return "%.1fkBps" % (r/1000)
43         return "%dBps" % r
44
45 class UploadResultsRendererMixin(RateAndTimeMixin):
46     # this requires a method named 'upload_results'
47
48     def render_sharemap(self, ctx, data):
49         d = self.upload_results()
50         d.addCallback(lambda res: res.sharemap)
51         def _render(sharemap):
52             if sharemap is None:
53                 return "None"
54             l = T.ul()
55             for shnum in sorted(sharemap.keys()):
56                 l[T.li["%d -> %s" % (shnum, sharemap[shnum])]]
57             return l
58         d.addCallback(_render)
59         return d
60
61     def render_servermap(self, ctx, data):
62         d = self.upload_results()
63         d.addCallback(lambda res: res.servermap)
64         def _render(servermap):
65             if servermap is None:
66                 return "None"
67             l = T.ul()
68             for peerid in sorted(servermap.keys()):
69                 peerid_s = idlib.shortnodeid_b2a(peerid)
70                 shares_s = ",".join(["#%d" % shnum
71                                      for shnum in servermap[peerid]])
72                 l[T.li["[%s] got share%s: %s" % (peerid_s,
73                                                  plural(servermap[peerid]),
74                                                  shares_s)]]
75             return l
76         d.addCallback(_render)
77         return d
78
79     def data_file_size(self, ctx, data):
80         d = self.upload_results()
81         d.addCallback(lambda res: res.file_size)
82         return d
83
84     def _get_time(self, name):
85         d = self.upload_results()
86         d.addCallback(lambda res: res.timings.get(name))
87         return d
88
89     def data_time_total(self, ctx, data):
90         return self._get_time("total")
91
92     def data_time_storage_index(self, ctx, data):
93         return self._get_time("storage_index")
94
95     def data_time_contacting_helper(self, ctx, data):
96         return self._get_time("contacting_helper")
97
98     def data_time_existence_check(self, ctx, data):
99         return self._get_time("existence_check")
100
101     def data_time_cumulative_fetch(self, ctx, data):
102         return self._get_time("cumulative_fetch")
103
104     def data_time_helper_total(self, ctx, data):
105         return self._get_time("helper_total")
106
107     def data_time_peer_selection(self, ctx, data):
108         return self._get_time("peer_selection")
109
110     def data_time_total_encode_and_push(self, ctx, data):
111         return self._get_time("total_encode_and_push")
112
113     def data_time_cumulative_encoding(self, ctx, data):
114         return self._get_time("cumulative_encoding")
115
116     def data_time_cumulative_sending(self, ctx, data):
117         return self._get_time("cumulative_sending")
118
119     def data_time_hashes_and_close(self, ctx, data):
120         return self._get_time("hashes_and_close")
121
122     def _get_rate(self, name):
123         d = self.upload_results()
124         def _convert(r):
125             file_size = r.file_size
126             time = r.timings.get(name)
127             if time is None:
128                 return None
129             try:
130                 return 1.0 * file_size / time
131             except ZeroDivisionError:
132                 return None
133         d.addCallback(_convert)
134         return d
135
136     def data_rate_total(self, ctx, data):
137         return self._get_rate("total")
138
139     def data_rate_storage_index(self, ctx, data):
140         return self._get_rate("storage_index")
141
142     def data_rate_encode(self, ctx, data):
143         return self._get_rate("cumulative_encoding")
144
145     def data_rate_push(self, ctx, data):
146         return self._get_rate("cumulative_sending")
147
148     def data_rate_encode_and_push(self, ctx, data):
149         d = self.upload_results()
150         def _convert(r):
151             file_size = r.file_size
152             if file_size is None:
153                 return None
154             time1 = r.timings.get("cumulative_encoding")
155             if time1 is None:
156                 return None
157             time2 = r.timings.get("cumulative_sending")
158             if time2 is None:
159                 return None
160             try:
161                 return 1.0 * file_size / (time1+time2)
162             except ZeroDivisionError:
163                 return None
164         d.addCallback(_convert)
165         return d
166
167     def data_rate_ciphertext_fetch(self, ctx, data):
168         d = self.upload_results()
169         def _convert(r):
170             fetch_size = r.ciphertext_fetched
171             if fetch_size is None:
172                 return None
173             time = r.timings.get("cumulative_fetch")
174             if time is None:
175                 return None
176             try:
177                 return 1.0 * fetch_size / time
178             except ZeroDivisionError:
179                 return None
180         d.addCallback(_convert)
181         return d
182
183 class UploadStatusPage(UploadResultsRendererMixin, rend.Page):
184     docFactory = getxmlfile("upload-status.xhtml")
185
186     def __init__(self, data):
187         rend.Page.__init__(self, data)
188         self.upload_status = data
189
190     def upload_results(self):
191         return defer.maybeDeferred(self.upload_status.get_results)
192
193     def render_results(self, ctx, data):
194         d = self.upload_results()
195         def _got_results(results):
196             if results:
197                 return ctx.tag
198             return ""
199         d.addCallback(_got_results)
200         return d
201
202     def render_started(self, ctx, data):
203         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
204         started_s = time.strftime(TIME_FORMAT,
205                                   time.localtime(data.get_started()))
206         return started_s
207
208     def render_si(self, ctx, data):
209         si_s = base32.b2a_or_none(data.get_storage_index())
210         if si_s is None:
211             si_s = "(None)"
212         return si_s
213
214     def render_helper(self, ctx, data):
215         return {True: "Yes",
216                 False: "No"}[data.using_helper()]
217
218     def render_total_size(self, ctx, data):
219         size = data.get_size()
220         if size is None:
221             size = "(unknown)"
222         return size
223
224     def render_progress_hash(self, ctx, data):
225         progress = data.get_progress()[0]
226         # TODO: make an ascii-art bar
227         return "%.1f%%" % (100.0 * progress)
228
229     def render_progress_ciphertext(self, ctx, data):
230         progress = data.get_progress()[1]
231         # TODO: make an ascii-art bar
232         return "%.1f%%" % (100.0 * progress)
233
234     def render_progress_encode_push(self, ctx, data):
235         progress = data.get_progress()[2]
236         # TODO: make an ascii-art bar
237         return "%.1f%%" % (100.0 * progress)
238
239     def render_status(self, ctx, data):
240         return data.get_status()
241
242 class DownloadResultsRendererMixin(RateAndTimeMixin):
243     # this requires a method named 'download_results'
244
245     def render_servermap(self, ctx, data):
246         d = self.download_results()
247         d.addCallback(lambda res: res.servermap)
248         def _render(servermap):
249             if servermap is None:
250                 return "None"
251             l = T.ul()
252             for peerid in sorted(servermap.keys()):
253                 peerid_s = idlib.shortnodeid_b2a(peerid)
254                 shares_s = ",".join(["#%d" % shnum
255                                      for shnum in servermap[peerid]])
256                 l[T.li["[%s] has share%s: %s" % (peerid_s,
257                                                  plural(servermap[peerid]),
258                                                  shares_s)]]
259             return l
260         d.addCallback(_render)
261         return d
262
263     def render_servers_used(self, ctx, data):
264         d = self.download_results()
265         d.addCallback(lambda res: res.servers_used)
266         def _got(servers_used):
267             if not servers_used:
268                 return ""
269             peerids_s = ", ".join(["[%s]" % idlib.shortnodeid_b2a(peerid)
270                                    for peerid in servers_used])
271             return T.li["Servers Used: ", peerids_s]
272         d.addCallback(_got)
273         return d
274
275     def render_problems(self, ctx, data):
276         d = self.download_results()
277         d.addCallback(lambda res: res.server_problems)
278         def _got(server_problems):
279             if not server_problems:
280                 return ""
281             l = T.ul()
282             for peerid in sorted(server_problems.keys()):
283                 peerid_s = idlib.shortnodeid_b2a(peerid)
284                 l[T.li["[%s]: %s" % (peerid_s, server_problems[peerid])]]
285             return T.li["Server Problems:", l]
286         d.addCallback(_got)
287         return d
288
289     def data_file_size(self, ctx, data):
290         d = self.download_results()
291         d.addCallback(lambda res: res.file_size)
292         return d
293
294     def _get_time(self, name):
295         d = self.download_results()
296         d.addCallback(lambda res: res.timings.get(name))
297         return d
298
299     def data_time_total(self, ctx, data):
300         return self._get_time("total")
301
302     def data_time_peer_selection(self, ctx, data):
303         return self._get_time("peer_selection")
304
305     def data_time_uri_extension(self, ctx, data):
306         return self._get_time("uri_extension")
307
308     def data_time_hashtrees(self, ctx, data):
309         return self._get_time("hashtrees")
310
311     def data_time_segments(self, ctx, data):
312         return self._get_time("segments")
313
314     def data_time_cumulative_fetch(self, ctx, data):
315         return self._get_time("cumulative_fetch")
316
317     def data_time_cumulative_decode(self, ctx, data):
318         return self._get_time("cumulative_decode")
319
320     def data_time_cumulative_decrypt(self, ctx, data):
321         return self._get_time("cumulative_decrypt")
322
323     def _get_rate(self, name):
324         d = self.download_results()
325         def _convert(r):
326             file_size = r.file_size
327             time = r.timings.get(name)
328             if time is None:
329                 return None
330             try:
331                 return 1.0 * file_size / time
332             except ZeroDivisionError:
333                 return None
334         d.addCallback(_convert)
335         return d
336
337     def data_rate_total(self, ctx, data):
338         return self._get_rate("total")
339
340     def data_rate_segments(self, ctx, data):
341         return self._get_rate("segments")
342
343     def data_rate_fetch(self, ctx, data):
344         return self._get_rate("cumulative_fetch")
345
346     def data_rate_decode(self, ctx, data):
347         return self._get_rate("cumulative_decode")
348
349     def data_rate_decrypt(self, ctx, data):
350         return self._get_rate("cumulative_decrypt")
351
352     def render_server_timings(self, ctx, data):
353         d = self.download_results()
354         d.addCallback(lambda res: res.timings.get("fetch_per_server"))
355         def _render(per_server):
356             if per_server is None:
357                 return ""
358             l = T.ul()
359             for peerid in sorted(per_server.keys()):
360                 peerid_s = idlib.shortnodeid_b2a(peerid)
361                 times_s = ", ".join([self.render_time(None, t)
362                                      for t in per_server[peerid]])
363                 l[T.li["[%s]: %s" % (peerid_s, times_s)]]
364             return T.li["Per-Server Segment Fetch Response Times: ", l]
365         d.addCallback(_render)
366         return d
367
368 class DownloadStatusPage(DownloadResultsRendererMixin, rend.Page):
369     docFactory = getxmlfile("download-status.xhtml")
370
371     def __init__(self, data):
372         rend.Page.__init__(self, data)
373         self.download_status = data
374
375     def download_results(self):
376         return defer.maybeDeferred(self.download_status.get_results)
377
378     def render_results(self, ctx, data):
379         d = self.download_results()
380         def _got_results(results):
381             if results:
382                 return ctx.tag
383             return ""
384         d.addCallback(_got_results)
385         return d
386
387     def render_started(self, ctx, data):
388         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
389         started_s = time.strftime(TIME_FORMAT,
390                                   time.localtime(data.get_started()))
391         return started_s
392
393     def render_si(self, ctx, data):
394         si_s = base32.b2a_or_none(data.get_storage_index())
395         if si_s is None:
396             si_s = "(None)"
397         return si_s
398
399     def render_helper(self, ctx, data):
400         return {True: "Yes",
401                 False: "No"}[data.using_helper()]
402
403     def render_total_size(self, ctx, data):
404         size = data.get_size()
405         if size is None:
406             size = "(unknown)"
407         return size
408
409     def render_progress(self, ctx, data):
410         progress = data.get_progress()
411         # TODO: make an ascii-art bar
412         return "%.1f%%" % (100.0 * progress)
413
414     def render_status(self, ctx, data):
415         return data.get_status()
416
417 class RetrieveStatusPage(rend.Page, RateAndTimeMixin):
418     docFactory = getxmlfile("retrieve-status.xhtml")
419
420     def __init__(self, data):
421         rend.Page.__init__(self, data)
422         self.retrieve_status = data
423
424     def render_started(self, ctx, data):
425         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
426         started_s = time.strftime(TIME_FORMAT,
427                                   time.localtime(data.get_started()))
428         return started_s
429
430     def render_si(self, ctx, data):
431         si_s = base32.b2a_or_none(data.get_storage_index())
432         if si_s is None:
433             si_s = "(None)"
434         return si_s
435
436     def render_helper(self, ctx, data):
437         return {True: "Yes",
438                 False: "No"}[data.using_helper()]
439
440     def render_current_size(self, ctx, data):
441         size = data.get_size()
442         if size is None:
443             size = "(unknown)"
444         return size
445
446     def render_progress(self, ctx, data):
447         progress = data.get_progress()
448         # TODO: make an ascii-art bar
449         return "%.1f%%" % (100.0 * progress)
450
451     def render_status(self, ctx, data):
452         return data.get_status()
453
454     def render_encoding(self, ctx, data):
455         k, n = data.get_encoding()
456         return ctx.tag["Encoding: %s of %s" % (k, n)]
457
458     def render_search_distance(self, ctx, data):
459         d = data.get_search_distance()
460         return ctx.tag["Search Distance: %s peer%s" % (d, plural(d))]
461
462     def render_problems(self, ctx, data):
463         problems = data.problems
464         if not problems:
465             return ""
466         l = T.ul()
467         for peerid in sorted(problems.keys()):
468             peerid_s = idlib.shortnodeid_b2a(peerid)
469             l[T.li["[%s]: %s" % (peerid_s, problems[peerid])]]
470         return ctx.tag["Server Problems:", l]
471
472     def _get_rate(self, data, name):
473         file_size = self.retrieve_status.get_size()
474         time = self.retrieve_status.timings.get(name)
475         if time is None:
476             return None
477         try:
478             return 1.0 * file_size / time
479         except ZeroDivisionError:
480             return None
481
482     def data_time_total(self, ctx, data):
483         return self.retrieve_status.timings.get("total")
484     def data_rate_total(self, ctx, data):
485         return self._get_rate(data, "total")
486
487     def data_time_peer_selection(self, ctx, data):
488         return self.retrieve_status.timings.get("peer_selection")
489
490     def data_time_fetch(self, ctx, data):
491         return self.retrieve_status.timings.get("fetch")
492     def data_rate_fetch(self, ctx, data):
493         return self._get_rate(data, "fetch")
494
495     def data_time_cumulative_verify(self, ctx, data):
496         return self.retrieve_status.timings.get("cumulative_verify")
497
498     def data_time_decode(self, ctx, data):
499         return self.retrieve_status.timings.get("decode")
500     def data_rate_decode(self, ctx, data):
501         return self._get_rate(data, "decode")
502
503     def data_time_decrypt(self, ctx, data):
504         return self.retrieve_status.timings.get("decrypt")
505     def data_rate_decrypt(self, ctx, data):
506         return self._get_rate(data, "decrypt")
507
508     def render_server_timings(self, ctx, data):
509         per_server = self.retrieve_status.timings.get("fetch_per_server")
510         if not per_server:
511             return ""
512         l = T.ul()
513         for peerid in sorted(per_server.keys()):
514             peerid_s = idlib.shortnodeid_b2a(peerid)
515             times_s = ", ".join([self.render_time(None, t)
516                                  for t in per_server[peerid]])
517             l[T.li["[%s]: %s" % (peerid_s, times_s)]]
518         return T.li["Per-Server Fetch Response Times: ", l]
519
520
521 class PublishStatusPage(rend.Page, RateAndTimeMixin):
522     docFactory = getxmlfile("publish-status.xhtml")
523
524     def __init__(self, data):
525         rend.Page.__init__(self, data)
526         self.publish_status = data
527
528     def render_started(self, ctx, data):
529         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
530         started_s = time.strftime(TIME_FORMAT,
531                                   time.localtime(data.get_started()))
532         return started_s
533
534     def render_si(self, ctx, data):
535         si_s = base32.b2a_or_none(data.get_storage_index())
536         if si_s is None:
537             si_s = "(None)"
538         return si_s
539
540     def render_helper(self, ctx, data):
541         return {True: "Yes",
542                 False: "No"}[data.using_helper()]
543
544     def render_current_size(self, ctx, data):
545         size = data.get_size()
546         if size is None:
547             size = "(unknown)"
548         return size
549
550     def render_progress(self, ctx, data):
551         progress = data.get_progress()
552         # TODO: make an ascii-art bar
553         return "%.1f%%" % (100.0 * progress)
554
555     def render_status(self, ctx, data):
556         return data.get_status()
557
558     def render_encoding(self, ctx, data):
559         k, n = data.get_encoding()
560         return ctx.tag["Encoding: %s of %s" % (k, n)]
561
562     def render_peers_queried(self, ctx, data):
563         return ctx.tag["Peers Queried: ", data.peers_queried]
564
565     def render_sharemap(self, ctx, data):
566         sharemap = data.sharemap
567         if sharemap is None:
568             return ctx.tag["None"]
569         l = T.ul()
570         for shnum in sorted(sharemap.keys()):
571             l[T.li["%d -> Placed on " % shnum,
572                    ", ".join(["[%s]" % idlib.shortnodeid_b2a(peerid)
573                               for (peerid,seqnum,root_hash)
574                               in sharemap[shnum]])]]
575         return ctx.tag["Sharemap:", l]
576
577     def render_problems(self, ctx, data):
578         problems = data.problems
579         if not problems:
580             return ""
581         l = T.ul()
582         for peerid in sorted(problems.keys()):
583             peerid_s = idlib.shortnodeid_b2a(peerid)
584             l[T.li["[%s]: %s" % (peerid_s, problems[peerid])]]
585         return ctx.tag["Server Problems:", l]
586
587     def _get_rate(self, data, name):
588         file_size = self.publish_status.get_size()
589         time = self.publish_status.timings.get(name)
590         if time is None:
591             return None
592         try:
593             return 1.0 * file_size / time
594         except ZeroDivisionError:
595             return None
596
597     def data_time_total(self, ctx, data):
598         return self.publish_status.timings.get("total")
599     def data_rate_total(self, ctx, data):
600         return self._get_rate(data, "total")
601
602     def data_time_setup(self, ctx, data):
603         return self.publish_status.timings.get("setup")
604
605     def data_time_query(self, ctx, data):
606         return self.publish_status.timings.get("query")
607
608     def data_time_privkey(self, ctx, data):
609         return self.publish_status.timings.get("privkey")
610
611     def data_time_privkey_fetch(self, ctx, data):
612         return self.publish_status.timings.get("privkey_fetch")
613     def render_privkey_from(self, ctx, data):
614         peerid = data.privkey_from
615         if peerid:
616             return " (got from [%s])" % idlib.shortnodeid_b2a(peerid)
617         else:
618             return ""
619
620     def data_time_encrypt(self, ctx, data):
621         return self.publish_status.timings.get("encrypt")
622     def data_rate_encrypt(self, ctx, data):
623         return self._get_rate(data, "encrypt")
624
625     def data_time_encode(self, ctx, data):
626         return self.publish_status.timings.get("encode")
627     def data_rate_encode(self, ctx, data):
628         return self._get_rate(data, "encode")
629
630     def data_time_pack(self, ctx, data):
631         return self.publish_status.timings.get("pack")
632     def data_rate_pack(self, ctx, data):
633         return self._get_rate(data, "pack")
634     def data_time_sign(self, ctx, data):
635         return self.publish_status.timings.get("sign")
636
637     def data_time_push(self, ctx, data):
638         return self.publish_status.timings.get("push")
639     def data_rate_push(self, ctx, data):
640         return self._get_rate(data, "push")
641
642     def data_initial_read_size(self, ctx, data):
643         return self.publish_status.initial_read_size
644
645     def render_server_timings(self, ctx, data):
646         per_server = self.publish_status.timings.get("per_server")
647         if not per_server:
648             return ""
649         l = T.ul()
650         for peerid in sorted(per_server.keys()):
651             peerid_s = idlib.shortnodeid_b2a(peerid)
652             times = []
653             for op,t in per_server[peerid]:
654                 if op == "read":
655                     times.append( "(" + self.render_time(None, t) + ")" )
656                 else:
657                     times.append( self.render_time(None, t) )
658             times_s = ", ".join(times)
659             l[T.li["[%s]: %s" % (peerid_s, times_s)]]
660         return T.li["Per-Server Response Times: ", l]
661
662
663 class Status(rend.Page):
664     docFactory = getxmlfile("status.xhtml")
665     addSlash = True
666
667     def data_active_operations(self, ctx, data):
668         active =  (IClient(ctx).list_active_uploads() +
669                    IClient(ctx).list_active_downloads() +
670                    IClient(ctx).list_active_publish() +
671                    IClient(ctx).list_active_retrieve())
672         return active
673
674     def data_recent_operations(self, ctx, data):
675         recent = [o for o in (IClient(ctx).list_recent_uploads() +
676                               IClient(ctx).list_recent_downloads() +
677                               IClient(ctx).list_recent_publish() +
678                               IClient(ctx).list_recent_retrieve())
679                   if not o.get_active()]
680         recent.sort(lambda a,b: cmp(a.get_started(), b.get_started()))
681         recent.reverse()
682         return recent
683
684     def render_row(self, ctx, data):
685         s = data
686
687         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
688         started_s = time.strftime(TIME_FORMAT,
689                                   time.localtime(s.get_started()))
690         ctx.fillSlots("started", started_s)
691
692         si_s = base32.b2a_or_none(s.get_storage_index())
693         if si_s is None:
694             si_s = "(None)"
695         ctx.fillSlots("si", si_s)
696         ctx.fillSlots("helper", {True: "Yes",
697                                  False: "No"}[s.using_helper()])
698
699         size = s.get_size()
700         if size is None:
701             size = "(unknown)"
702         ctx.fillSlots("total_size", size)
703
704         progress = data.get_progress()
705         if IUploadStatus.providedBy(data):
706             link = "up-%d" % data.get_counter()
707             ctx.fillSlots("type", "upload")
708             # TODO: make an ascii-art bar
709             (chk, ciphertext, encandpush) = progress
710             progress_s = ("hash: %.1f%%, ciphertext: %.1f%%, encode: %.1f%%" %
711                           ( (100.0 * chk),
712                             (100.0 * ciphertext),
713                             (100.0 * encandpush) ))
714             ctx.fillSlots("progress", progress_s)
715         elif IDownloadStatus.providedBy(data):
716             link = "down-%d" % data.get_counter()
717             ctx.fillSlots("type", "download")
718             ctx.fillSlots("progress", "%.1f%%" % (100.0 * progress))
719         elif IPublishStatus.providedBy(data):
720             link = "publish-%d" % data.get_counter()
721             ctx.fillSlots("type", "publish")
722             ctx.fillSlots("progress", "%.1f%%" % (100.0 * progress))
723         else:
724             assert IRetrieveStatus.providedBy(data)
725             ctx.fillSlots("type", "retrieve")
726             link = "retrieve-%d" % data.get_counter()
727             ctx.fillSlots("progress", "%.1f%%" % (100.0 * progress))
728         ctx.fillSlots("status", T.a(href=link)[s.get_status()])
729         return ctx.tag
730
731     def childFactory(self, ctx, name):
732         client = IClient(ctx)
733         stype,count_s = name.split("-")
734         count = int(count_s)
735         if stype == "up":
736             for s in client.list_recent_uploads():
737                 if s.get_counter() == count:
738                     return UploadStatusPage(s)
739             for s in client.list_all_uploads():
740                 if s.get_counter() == count:
741                     return UploadStatusPage(s)
742         if stype == "down":
743             for s in client.list_recent_downloads():
744                 if s.get_counter() == count:
745                     return DownloadStatusPage(s)
746             for s in client.list_all_downloads():
747                 if s.get_counter() == count:
748                     return DownloadStatusPage(s)
749         if stype == "publish":
750             for s in client.list_recent_publish():
751                 if s.get_counter() == count:
752                     return PublishStatusPage(s)
753             for s in client.list_all_publish():
754                 if s.get_counter() == count:
755                     return PublishStatusPage(s)
756         if stype == "retrieve":
757             for s in client.list_recent_retrieve():
758                 if s.get_counter() == count:
759                     return RetrieveStatusPage(s)
760             for s in client.list_all_retrieve():
761                 if s.get_counter() == count:
762                     return RetrieveStatusPage(s)
763
764