Open manzt opened 7 months ago
Notebooks test code with a widget to collect the Tilesets. A collection of tilesets (should be weakrefable), that handles fulfilling requests from the front end.
import higlass as hg
import ipywidgets
import traitlets
import itertools
class Tilesets(ipywidgets.DOMWidget):
value = traitlets.Int(0).tag(sync=True)
def __init__(self):
super().__init__()
self.on_msg(self._handle_custom_msg)
self.ts = dict()
def add(self, ts):
self.ts[ts.tileset.uid] = ts.tileset
return self
def _handle_custom_msg(self, data, buffers):
payload = data["payload"]
uuid = data["uuid"]
match payload:
case { "type": "tileset_info", "tilesetUid": uid }:
info = { uid: self.ts[uid].info() }
self.send({ "uuid": uuid, "payload": info })
case { "type": "tiles", "tileIds": tids }:
all_tiles = []
for uid, tids in itertools.groupby(
iterable=sorted(tids), key=lambda tid: tid.split(".")[0]
):
tiles = self.ts[uid].tiles(list(tids))
all_tiles.extend(tiles)
data = {tid: tval for tid, tval in all_tiles}
self.send({ "uuid": uuid, "payload": data })
case _:
raise ValueError("Something's wrong with the Internet")
ts = hg.cooler("./test.mcool")
tss = Tilesets().add(ts)
track = ts.track("heatmap")
hg.view(track, width=6).widget(ts=tss)
Just other ideas. A tile request "coordinator", that can be shared among tracks. It waits an animation frame to see all the desired tiles, and then dispatches a single server request.
class TileRequestCoordinator {
#model;
#requests;
#frameRequested = false;
constructor(model) {
this.#model = model;
this.#requests = [];
}
async fetchTilesetInfo({ tilesetUid }) {
let { data } = await send(this.#model, { type: "tileset_info", tilesetUid });
return data;
}
async fetchTiles({ tileIds }) {
if (!this.#frameRequested) {
this.#frameRequested = true;
requestAnimationFrame(() => this.#processRequests());
}
let { promise, resolve, reject } = Promise.withResolvers();
this.#requests.push({ tileIds, resolve, reject });
return promise;
}
async #processRequests() {
this.#frameRequested = false;
let ids = [...new Set(this.#requests.flatMap((r) => r.tileIds))];
let { data: resp } = await send(this.#model, { type: "tiles", tileIds: ids });
let data = tileResponseToData(resp, "jupyter", ids);
for (let { tileIds, resolve } of this.#requests) {
let tileData = Object.fromEntries(tileIds.map((id) => [id, data[id]]));
resolve(tileData);
}
this.#requests.length = 0;
}
registerTileset() {
throw new Error("Not implemented");
}
}
Relevant PR that benefits from a similar kind of architecture (and ability to drop jupyter-server-proxy):
Depends on https://github.com/higlass/higlass/pull/1194. I have just got this working locally with Vite.