Files
wekan/server/lib/scrumChartData.js
Lauri Ojansivu b6ca287df3 Keep sprint snapshot rows outside the sprint document.
A sprint's start and close snapshots kept one row per card inside the
sprint document, so a sprint over about 15,000 cards could not be closed.
The rows now go to their own collection, scrumSnapshotRows, in immutable
chunks of 2,000 named by sprint, kind, snapshot time and chunk number, so a
retried start or close, an undo or a redo finds the same rows. The sprint
keeps a header and the report's totals, computed from all rows.

The board data computes each sprint's report on the server and sends only
snapshot headers: from the stored totals, or for an assigned-only reader
from the rows they may see. The scope replay, the transfer export and the
board copy read the rows from wherever they are kept; the transfer import
writes them back as chunks through the import journal. Snapshots stored
inline before this keep working.

Thanks to xet7 !
2026-10-03 00:38:38 +03:00

20 lines
1.3 KiB
JavaScript

import { Meteor } from 'meteor/meteor';
import { getScrumBoardData, getScrumDailyHistory } from '/server/scrum';
const { velocityReports } = require('/models/lib/scrumReports');
export async function loadScrumChartData(userId, boardId, chartKey, options = {}) {
if (chartKey === 'scrumDaily') {
if (typeof options.sprintId !== 'string' || !options.sprintId || options.sprintId.length > 200) throw new Meteor.Error('invalid-sprint');
return getScrumDailyHistory(userId, boardId, options.sprintId);
}
const data = await getScrumBoardData(userId, boardId);
if (data.importPending) throw new Meteor.Error('scrum-import-pending', 'The Scrum import is incomplete. Report exports are unavailable.');
// Reports computed by getScrumBoardData, from the rows the reader may see.
if (chartKey === 'scrumVelocity') return { reports: velocityReports(data.sprints), partial: data.partial };
if (chartKey !== 'scrumSprint') throw new Meteor.Error('invalid-chart');
if (typeof options.sprintId !== 'string' || options.sprintId.length > 200) throw new Meteor.Error('invalid-sprint');
const sprint = data.sprints.find(s => s._id === options.sprintId);
if (!sprint) throw new Meteor.Error('not-found', 'Sprint not found on this board');
return { reports: sprint.closeSnapshot && sprint.report ? [sprint.report] : [], partial: data.partial };
}