Skip to content

Commit 687705a

Browse files
Stack group-by over-time plots instead of overlaying (#112)
* Initial plan * Stack group-by over-time plots and add Other series for dandisets mode Agent-Logs-Url: https://github.com/dandi/access-page/sessions/d5cf8291-79e5-49fe-a8b1-5f88254ac527 Co-authored-by: CodyCBakerPhD <51133164+CodyCBakerPhD@users.noreply.github.com> * Show Other series in dandisets grouped mode for cumulative view too Agent-Logs-Url: https://github.com/dandi/access-page/sessions/14d0192f-8e95-4711-8042-114628a6f6f1 Co-authored-by: CodyCBakerPhD <51133164+CodyCBakerPhD@users.noreply.github.com> * Add Other series to asset_type grouped over-time plot Agent-Logs-Url: https://github.com/dandi/access-page/sessions/bdac8a00-2309-43ba-966b-4a50e6c97247 Co-authored-by: CodyCBakerPhD <51133164+CodyCBakerPhD@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: CodyCBakerPhD <51133164+CodyCBakerPhD@users.noreply.github.com>
1 parent 16c3dfb commit 687705a

1 file changed

Lines changed: 77 additions & 5 deletions

File tree

src/plots.js

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,13 +1120,20 @@ function load_over_time_plot(dandiset_id) {
11201120
// ── Grouped mode: overlay asset types ────────────────────────────────────
11211121
if (OVER_TIME_GROUP_BY === "asset_type") {
11221122
const tsv_url = `${BASE_TSV_URL}/${dandiset_id}/by_asset_type_per_week.tsv`;
1123+
const archive_tsv_url = `${BASE_TSV_URL}/${dandiset_id}/by_day.tsv`;
11231124

1124-
return fetch(tsv_url)
1125+
const asset_type_promise = fetch(tsv_url)
11251126
.then((r) => {
11261127
if (!r.ok) throw new Error(`HTTP ${r.status}`);
11271128
return r.text();
1128-
})
1129-
.then((text) => {
1129+
});
1130+
const archive_promise = fetch(archive_tsv_url)
1131+
.then((r) => r.ok ? r.text() : Promise.reject(new Error(`HTTP ${r.status}`)))
1132+
.then((text) => parse_by_day_tsv(text))
1133+
.catch(() => null);
1134+
1135+
return Promise.all([asset_type_promise, archive_promise])
1136+
.then(([text, archive_data]) => {
11301137
const { dates: raw_dates, asset_types, series_map } = parse_by_asset_type_per_week_tsv(text);
11311138

11321139
// Data is weekly; treat "daily" aggregation as weekly since no finer data exists
@@ -1159,9 +1166,42 @@ function load_over_time_plot(dandiset_id) {
11591166
};
11601167
});
11611168

1169+
// Build an "Other" series: archive total minus the sum of all asset types
1170+
if (archive_data) {
1171+
const archive_agg = aggregate_by_timebin(archive_data.dates, archive_data.bytes, effective_aggregation);
1172+
const archive_plot_data = USE_CUMULATIVE
1173+
? make_cumulative(archive_agg.bytes_sent)
1174+
: archive_agg.bytes_sent;
1175+
// Build per-date lookup for the sum of all asset-type series (already cumulative if USE_CUMULATIVE)
1176+
const series_by_date = new Map();
1177+
for (const series of plot_info) {
1178+
series.x.forEach((date, idx) => {
1179+
series_by_date.set(date, (series_by_date.get(date) || 0) + series.y[idx]);
1180+
});
1181+
}
1182+
const other_y = archive_agg.dates.map((date, i) => {
1183+
const asset_type_total = series_by_date.get(date) || 0;
1184+
return Math.max(0, archive_plot_data[i] - asset_type_total);
1185+
});
1186+
const other_human_readable = other_y.map((b) => format_bytes(b));
1187+
plot_info.push({
1188+
type: "bar",
1189+
name: "Other",
1190+
x: archive_agg.dates,
1191+
y: other_y,
1192+
text: archive_agg.dates.map((date, idx) =>
1193+
`Other<br>${bin_label_prefix}${date}<br>${other_human_readable[idx]}`
1194+
),
1195+
textposition: "none",
1196+
hoverinfo: "text",
1197+
marker: { color: "rgba(150,150,150,0.7)" },
1198+
});
1199+
all_dates_for_layout.push(...archive_agg.dates);
1200+
}
1201+
11621202
const unique_dates = [...new Set(all_dates_for_layout)].sort();
11631203
const layout = build_over_time_layout(unique_dates);
1164-
layout.barmode = "overlay";
1204+
layout.barmode = "stack";
11651205
layout.showlegend = true;
11661206
layout.legend = { title: { text: "Asset type" } };
11671207

@@ -1260,10 +1300,42 @@ function load_over_time_plot(dandiset_id) {
12601300
};
12611301
});
12621302

1303+
// Build an "Other" series: archive total minus the sum of all top-N dandisets
1304+
if (archive_data) {
1305+
const archive_agg = aggregate_by_timebin(archive_data.dates, archive_data.bytes, TIME_AGGREGATION);
1306+
const archive_plot_data = USE_CUMULATIVE
1307+
? make_cumulative(archive_agg.bytes_sent)
1308+
: archive_agg.bytes_sent;
1309+
// Build per-date lookup for the sum of top-N series (already cumulative if USE_CUMULATIVE)
1310+
const series_by_date = new Map();
1311+
for (const series of valid_series) {
1312+
series.dates.forEach((date, idx) => {
1313+
series_by_date.set(date, (series_by_date.get(date) || 0) + series.plot_data[idx]);
1314+
});
1315+
}
1316+
const other_y = archive_agg.dates.map((date, i) => {
1317+
const top_n_total = series_by_date.get(date) || 0;
1318+
return Math.max(0, archive_plot_data[i] - top_n_total);
1319+
});
1320+
const other_human_readable = other_y.map((b) => format_bytes(b));
1321+
plot_info.push({
1322+
type: "bar",
1323+
name: "Other",
1324+
x: archive_agg.dates,
1325+
y: other_y,
1326+
text: archive_agg.dates.map((date, idx) =>
1327+
`Other<br>${bin_label_prefix}${date}<br>${other_human_readable[idx]}`
1328+
),
1329+
textposition: "none",
1330+
hoverinfo: "text",
1331+
marker: { color: "rgba(150,150,150,0.7)" },
1332+
});
1333+
}
1334+
12631335
// Collect all dates across series for range-break calculation
12641336
const all_series_dates = valid_series.flatMap((s) => s.dates);
12651337
const layout = build_over_time_layout(all_series_dates);
1266-
layout.barmode = "overlay";
1338+
layout.barmode = "stack";
12671339
layout.legend = { title: { text: "Dandiset" } };
12681340

12691341
Plotly.newPlot(plot_element_id, plot_info, layout);

0 commit comments

Comments
 (0)