+ def _find_overlap(self, events, start_key, end_key):
+ # given a list of event dicts, return a new list in which each event
+ # has an extra "row" key (an int, starting at 0). This is a hint to
+ # our JS frontend about how to overlap the parts of the graph it is
+ # drawing.
+
+ # we must always make a copy, since we're going to be adding "row"
+ # keys and don't want to change the original objects. If we're
+ # stringifying serverids, we'll also be changing the serverid keys.
+ new_events = []
+ rows = []
+ for ev in events:
+ ev = ev.copy()
+ if "serverid" in ev:
+ ev["serverid"] = base32.b2a(ev["serverid"])
+ # find an empty slot in the rows
+ free_slot = None
+ for row,finished in enumerate(rows):
+ if finished is not None:
+ if ev[start_key] > finished:
+ free_slot = row
+ break
+ if free_slot is None:
+ free_slot = len(rows)
+ rows.append(ev[end_key])
+ else:
+ rows[free_slot] = ev[end_key]
+ ev["row"] = free_slot
+ new_events.append(ev)
+ return new_events
+
+ def _find_overlap_requests(self, events):
+ """We compute a three-element 'row tuple' for each event: (serverid,
+ shnum, row). All elements are ints. The first is a mapping from
+ serverid to group number, the second is a mapping from shnum to
+ subgroup number. The third is a row within the subgroup.
+
+ We also return a list of lists of rowcounts, so renderers can decide
+ how much vertical space to give to each row.
+ """
+
+ serverid_to_group = {}
+ groupnum_to_rows = {} # maps groupnum to a table of rows. Each table
+ # is a list with an element for each row number
+ # (int starting from 0) that contains a
+ # finish_time, indicating that the row is empty
+ # beyond that time. If finish_time is None, it
+ # indicate a response that has not yet
+ # completed, so the row cannot be reused.
+ new_events = []
+ for ev in events:
+ # DownloadStatus promises to give us events in temporal order
+ ev = ev.copy()
+ ev["serverid"] = base32.b2a(ev["serverid"])
+ if ev["serverid"] not in serverid_to_group:
+ groupnum = len(serverid_to_group)
+ serverid_to_group[ev["serverid"]] = groupnum
+ groupnum = serverid_to_group[ev["serverid"]]
+ if groupnum not in groupnum_to_rows:
+ groupnum_to_rows[groupnum] = []
+ rows = groupnum_to_rows[groupnum]
+ # find an empty slot in the rows
+ free_slot = None
+ for row,finished in enumerate(rows):
+ if finished is not None:
+ if ev["start_time"] > finished:
+ free_slot = row
+ break
+ if free_slot is None:
+ free_slot = len(rows)
+ rows.append(ev["finish_time"])
+ else:
+ rows[free_slot] = ev["finish_time"]
+ ev["row"] = (groupnum, free_slot)
+ new_events.append(ev)
+ # maybe also return serverid_to_group, groupnum_to_rows, and some
+ # indication of the highest finish_time
+ #
+ # actually, return the highest rownum for each groupnum
+ highest_rownums = [len(groupnum_to_rows[groupnum])
+ for groupnum in range(len(serverid_to_group))]
+ return new_events, highest_rownums
+
+ def child_event_json(self, ctx):
+ inevow.IRequest(ctx).setHeader("content-type", "text/plain")
+ data = { } # this will be returned to the GET
+ ds = self.download_status
+
+ data["read"] = self._find_overlap(ds.read_events,
+ "start_time", "finish_time")
+ data["segment"] = self._find_overlap(ds.segment_events,
+ "start_time", "finish_time")
+ data["dyhb"] = self._find_overlap(ds.dyhb_requests,
+ "start_time", "finish_time")
+ data["block"],data["block_rownums"] = self._find_overlap_requests(ds.block_requests)
+
+ servernums = {}
+ serverid_strings = {}
+ for d_ev in data["dyhb"]:
+ if d_ev["serverid"] not in servernums:
+ servernum = len(servernums)
+ servernums[d_ev["serverid"]] = servernum
+ #title= "%s: %s" % ( ",".join([str(shnum) for shnum in shnums]))
+ serverid_strings[servernum] = d_ev["serverid"][:4]
+ data["server_info"] = dict([(serverid, {"num": servernums[serverid],
+ "color": self.color(base32.a2b(serverid)),
+ "short": serverid_strings[servernums[serverid]],
+ })
+ for serverid in servernums.keys()])
+ data["num_serverids"] = len(serverid_strings)
+ # we'd prefer the keys of serverids[] to be ints, but this is JSON,
+ # so they get converted to strings. Stupid javascript.
+ data["serverids"] = serverid_strings
+ data["bounds"] = {"min": ds.first_timestamp,
+ "max": ds.last_timestamp,
+ }
+ # for testing
+ ## data["bounds"]["max"] = tfmt(max([d_ev["finish_time"]
+ ## for d_ev in data["dyhb"]
+ ## if d_ev["finish_time"] is not None]
+ ## ))