@vue-skuilder/express 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-factory.d.ts.map +1 -1
- package/dist/app-factory.js +2 -0
- package/dist/app-factory.js.map +1 -1
- package/dist/routes/orchestration.d.ts +3 -0
- package/dist/routes/orchestration.d.ts.map +1 -0
- package/dist/routes/orchestration.js +374 -0
- package/dist/routes/orchestration.js.map +1 -0
- package/package.json +4 -4
- package/src/app-factory.ts +2 -0
- package/src/routes/orchestration.ts +443 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-factory.d.ts","sourceRoot":"","sources":["../src/app-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,aAAa,EAEd,MAAM,sBAAsB,CAAC;AAK9B,OAAO,OAAO,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"app-factory.d.ts","sourceRoot":"","sources":["../src/app-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,aAAa,EAEd,MAAM,sBAAsB,CAAC;AAK9B,OAAO,OAAO,MAAM,SAAS,CAAC;AAqB9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGzE,MAAM,WAAW,gBAAiB,SAAQ,OAAO,CAAC,OAAO;IACvD,IAAI,EAAE,aAAa,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,mBAAmB,GAAG,iBAAiB,CAAC;AAwBhE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAgMvE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DzE"}
|
package/dist/app-factory.js
CHANGED
|
@@ -15,6 +15,7 @@ import { classroomDbDesignDoc } from './design-docs.js';
|
|
|
15
15
|
import logger from './logger.js';
|
|
16
16
|
import logsRouter from './routes/logs.js';
|
|
17
17
|
import authRouter from './routes/auth.js';
|
|
18
|
+
import orchestrationRouter from './routes/orchestration.js';
|
|
18
19
|
import { applyUsersDesignDocs } from './couchdb/userDesignDocs.js';
|
|
19
20
|
/**
|
|
20
21
|
* Type guard to determine if config is ExpressServerConfig (programmatic usage)
|
|
@@ -61,6 +62,7 @@ export function createExpressApp(config) {
|
|
|
61
62
|
}));
|
|
62
63
|
app.use('/logs', logsRouter);
|
|
63
64
|
app.use('/auth', authRouter);
|
|
65
|
+
app.use('/orchestration', orchestrationRouter);
|
|
64
66
|
// Routes
|
|
65
67
|
app.get('/courses', (_req, res) => {
|
|
66
68
|
void (async () => {
|
package/dist/app-factory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-factory.js","sourceRoot":"","sources":["../src/app-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EACL,iBAAiB,IAAI,WAAW,EAEhC,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,WAAW,MAAM,qCAAqC,CAAC;AAC9D,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,UAAU,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"app-factory.js","sourceRoot":"","sources":["../src/app-factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestration.d.ts","sourceRoot":"","sources":["../../src/routes/orchestration.ts"],"names":[],"mappings":"AAkBA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAwaxB,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { requestIsAuthenticated } from '../couchdb/authentication.js';
|
|
3
|
+
import { useOrCreateCourseDB, getCouchDB } from '../couchdb/index.js';
|
|
4
|
+
import logger from '../logger.js';
|
|
5
|
+
import { aggregateOutcomesForGradient, computeStrategyGradient, runPeriodUpdate, getDefaultLearnableWeight, } from '@vue-skuilder/db';
|
|
6
|
+
const router = Router();
|
|
7
|
+
/**
|
|
8
|
+
* POST /orchestration/:courseId/update
|
|
9
|
+
*
|
|
10
|
+
* Trigger a period update for all strategies in a course.
|
|
11
|
+
* This aggregates user outcomes and adjusts strategy weights based on gradients.
|
|
12
|
+
*
|
|
13
|
+
* Requires authentication.
|
|
14
|
+
*
|
|
15
|
+
* Query params:
|
|
16
|
+
* - periodStart: ISO timestamp (optional, defaults to 7 days ago)
|
|
17
|
+
* - periodEnd: ISO timestamp (optional, defaults to now)
|
|
18
|
+
*/
|
|
19
|
+
router.post('/:courseId/update', (req, res) => {
|
|
20
|
+
void (async () => {
|
|
21
|
+
try {
|
|
22
|
+
const auth = await requestIsAuthenticated(req);
|
|
23
|
+
if (!auth) {
|
|
24
|
+
res.status(401).json({ error: 'Unauthorized' });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const { courseId } = req.params;
|
|
28
|
+
const periodEnd = req.query.periodEnd || new Date().toISOString();
|
|
29
|
+
const periodStart = req.query.periodStart ||
|
|
30
|
+
new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
31
|
+
logger.info(`[Orchestration] Period update requested for course ${courseId} ` +
|
|
32
|
+
`(${periodStart} to ${periodEnd})`);
|
|
33
|
+
// 1. Get course DB and strategies
|
|
34
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
35
|
+
const strategiesResp = await courseDB.find({
|
|
36
|
+
selector: { docType: 'NAVIGATION_STRATEGY' },
|
|
37
|
+
});
|
|
38
|
+
const strategies = strategiesResp.docs;
|
|
39
|
+
if (strategies.length === 0) {
|
|
40
|
+
res.json({ message: 'No strategies found', updated: 0 });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// 2. Aggregate user outcomes for this course
|
|
44
|
+
// Note: This queries across user DBs - requires admin access
|
|
45
|
+
const outcomes = await aggregateUserOutcomes(courseId, periodStart, periodEnd);
|
|
46
|
+
if (outcomes.length === 0) {
|
|
47
|
+
res.json({ message: 'No outcome records found', updated: 0 });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
logger.info(`[Orchestration] Found ${outcomes.length} outcome records for ${courseId}`);
|
|
51
|
+
// 3. Process each strategy
|
|
52
|
+
const results = [];
|
|
53
|
+
for (const strategy of strategies) {
|
|
54
|
+
const strategyId = strategy._id;
|
|
55
|
+
// Skip static weight strategies
|
|
56
|
+
if (strategy.staticWeight) {
|
|
57
|
+
logger.debug(`[Orchestration] Skipping static strategy ${strategyId}`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// Get or create learning state
|
|
61
|
+
let existingState;
|
|
62
|
+
try {
|
|
63
|
+
existingState = (await courseDB.get(`STRATEGY_LEARNING_STATE::${courseId}::${strategyId}`));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// No existing state, that's fine
|
|
67
|
+
}
|
|
68
|
+
// Aggregate observations for this strategy
|
|
69
|
+
const observations = aggregateOutcomesForGradient(outcomes, strategyId);
|
|
70
|
+
if (observations.length < 3) {
|
|
71
|
+
logger.debug(`[Orchestration] Insufficient observations for ${strategyId} (${observations.length})`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// Compute gradient
|
|
75
|
+
const gradient = computeStrategyGradient(observations);
|
|
76
|
+
if (!gradient) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// Get current weight
|
|
80
|
+
const currentWeight = strategy.learnable || getDefaultLearnableWeight();
|
|
81
|
+
// Run update
|
|
82
|
+
const input = {
|
|
83
|
+
courseId,
|
|
84
|
+
strategyId,
|
|
85
|
+
currentWeight,
|
|
86
|
+
gradient,
|
|
87
|
+
existingState,
|
|
88
|
+
};
|
|
89
|
+
const result = runPeriodUpdate(input);
|
|
90
|
+
// Persist updated strategy
|
|
91
|
+
if (result.updated) {
|
|
92
|
+
const updatedStrategy = {
|
|
93
|
+
...strategy,
|
|
94
|
+
learnable: result.newWeight,
|
|
95
|
+
};
|
|
96
|
+
await courseDB.insert(updatedStrategy);
|
|
97
|
+
}
|
|
98
|
+
// Persist learning state
|
|
99
|
+
await courseDB.insert(result.learningState);
|
|
100
|
+
results.push({
|
|
101
|
+
strategyId,
|
|
102
|
+
previousWeight: result.previousWeight.weight,
|
|
103
|
+
newWeight: result.newWeight.weight,
|
|
104
|
+
gradient: gradient.gradient,
|
|
105
|
+
rSquared: gradient.rSquared,
|
|
106
|
+
observations: observations.length,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
res.json({
|
|
110
|
+
message: 'Period update complete',
|
|
111
|
+
courseId,
|
|
112
|
+
periodStart,
|
|
113
|
+
periodEnd,
|
|
114
|
+
outcomesProcessed: outcomes.length,
|
|
115
|
+
strategiesUpdated: results.filter((r) => r.previousWeight !== r.newWeight).length,
|
|
116
|
+
results,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
logger.error(`[Orchestration] Period update error: ${error}`);
|
|
121
|
+
res.status(500).json({ error: 'Period update failed', details: String(error) });
|
|
122
|
+
}
|
|
123
|
+
})();
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* GET /orchestration/:courseId/state
|
|
127
|
+
*
|
|
128
|
+
* Get current learning state for all strategies in a course.
|
|
129
|
+
*/
|
|
130
|
+
router.get('/:courseId/state', (req, res) => {
|
|
131
|
+
void (async () => {
|
|
132
|
+
try {
|
|
133
|
+
const { courseId } = req.params;
|
|
134
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
135
|
+
const statesResp = await courseDB.find({
|
|
136
|
+
selector: { docType: 'STRATEGY_LEARNING_STATE' },
|
|
137
|
+
});
|
|
138
|
+
res.json({
|
|
139
|
+
courseId,
|
|
140
|
+
states: statesResp.docs,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
logger.error(`[Orchestration] Get state error: ${error}`);
|
|
145
|
+
res.status(500).json({ error: 'Failed to get state', details: String(error) });
|
|
146
|
+
}
|
|
147
|
+
})();
|
|
148
|
+
});
|
|
149
|
+
/**
|
|
150
|
+
* GET /orchestration/:courseId/strategy/:strategyId/history
|
|
151
|
+
*
|
|
152
|
+
* Get weight history for a specific strategy.
|
|
153
|
+
*/
|
|
154
|
+
router.get('/:courseId/strategy/:strategyId/history', (req, res) => {
|
|
155
|
+
void (async () => {
|
|
156
|
+
try {
|
|
157
|
+
const { courseId, strategyId } = req.params;
|
|
158
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
159
|
+
const stateId = `STRATEGY_LEARNING_STATE::${courseId}::${strategyId}`;
|
|
160
|
+
try {
|
|
161
|
+
const state = (await courseDB.get(stateId));
|
|
162
|
+
res.json({
|
|
163
|
+
courseId,
|
|
164
|
+
strategyId,
|
|
165
|
+
currentWeight: state.currentWeight,
|
|
166
|
+
regression: state.regression,
|
|
167
|
+
history: state.history,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
res.status(404).json({ error: 'No learning state found for this strategy' });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
logger.error(`[Orchestration] Get history error: ${error}`);
|
|
176
|
+
res.status(500).json({ error: 'Failed to get history', details: String(error) });
|
|
177
|
+
}
|
|
178
|
+
})();
|
|
179
|
+
});
|
|
180
|
+
/**
|
|
181
|
+
* GET /orchestration/:courseId/strategy/:strategyId/scatter
|
|
182
|
+
*
|
|
183
|
+
* Get scatter plot data (deviation vs outcome) for a specific strategy.
|
|
184
|
+
* Useful for visualizing the gradient and understanding the learning dynamics.
|
|
185
|
+
*
|
|
186
|
+
* Query params:
|
|
187
|
+
* - periodStart: ISO timestamp (optional, defaults to 7 days ago)
|
|
188
|
+
* - periodEnd: ISO timestamp (optional, defaults to now)
|
|
189
|
+
* - limit: max observations to return (optional, defaults to 1000)
|
|
190
|
+
*/
|
|
191
|
+
router.get('/:courseId/strategy/:strategyId/scatter', (req, res) => {
|
|
192
|
+
void (async () => {
|
|
193
|
+
try {
|
|
194
|
+
const { courseId, strategyId } = req.params;
|
|
195
|
+
const periodEnd = req.query.periodEnd || new Date().toISOString();
|
|
196
|
+
const periodStart = req.query.periodStart ||
|
|
197
|
+
new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
198
|
+
const limit = parseInt(req.query.limit || '1000', 10);
|
|
199
|
+
// Aggregate outcomes
|
|
200
|
+
const outcomes = await aggregateUserOutcomes(courseId, periodStart, periodEnd);
|
|
201
|
+
// Extract scatter data for this strategy
|
|
202
|
+
const scatterData = outcomes
|
|
203
|
+
.filter((outcome) => outcome.deviations[strategyId] !== undefined)
|
|
204
|
+
.map((outcome) => ({
|
|
205
|
+
deviation: outcome.deviations[strategyId],
|
|
206
|
+
outcome: outcome.outcomeValue,
|
|
207
|
+
userId: outcome.userId,
|
|
208
|
+
periodEnd: outcome.periodEnd,
|
|
209
|
+
}))
|
|
210
|
+
.slice(0, limit);
|
|
211
|
+
// Compute regression for visualization
|
|
212
|
+
const observations = aggregateOutcomesForGradient(outcomes, strategyId);
|
|
213
|
+
const gradient = observations.length >= 3 ? computeStrategyGradient(observations) : null;
|
|
214
|
+
res.json({
|
|
215
|
+
courseId,
|
|
216
|
+
strategyId,
|
|
217
|
+
periodStart,
|
|
218
|
+
periodEnd,
|
|
219
|
+
observations: scatterData.length,
|
|
220
|
+
data: scatterData,
|
|
221
|
+
regression: gradient,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
logger.error(`[Orchestration] Scatter plot error: ${error}`);
|
|
226
|
+
res.status(500).json({ error: 'Failed to get scatter data', details: String(error) });
|
|
227
|
+
}
|
|
228
|
+
})();
|
|
229
|
+
});
|
|
230
|
+
/**
|
|
231
|
+
* GET /orchestration/:courseId/weights
|
|
232
|
+
*
|
|
233
|
+
* Get current learned weights for all strategies in a course.
|
|
234
|
+
* Simpler alternative to /state endpoint - just returns weight/confidence without history.
|
|
235
|
+
*/
|
|
236
|
+
router.get('/:courseId/weights', (req, res) => {
|
|
237
|
+
void (async () => {
|
|
238
|
+
try {
|
|
239
|
+
const { courseId } = req.params;
|
|
240
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
241
|
+
// Get all strategies
|
|
242
|
+
const strategiesResp = await courseDB.find({
|
|
243
|
+
selector: { docType: 'NAVIGATION_STRATEGY' },
|
|
244
|
+
});
|
|
245
|
+
const strategies = strategiesResp.docs;
|
|
246
|
+
const weights = strategies.map((strategy) => ({
|
|
247
|
+
strategyId: strategy._id,
|
|
248
|
+
strategyName: strategy.name,
|
|
249
|
+
implementingClass: strategy.implementingClass,
|
|
250
|
+
learnable: strategy.learnable || null,
|
|
251
|
+
staticWeight: strategy.staticWeight || false,
|
|
252
|
+
}));
|
|
253
|
+
res.json({
|
|
254
|
+
courseId,
|
|
255
|
+
weights,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
logger.error(`[Orchestration] Get weights error: ${error}`);
|
|
260
|
+
res.status(500).json({ error: 'Failed to get weights', details: String(error) });
|
|
261
|
+
}
|
|
262
|
+
})();
|
|
263
|
+
});
|
|
264
|
+
/**
|
|
265
|
+
* GET /orchestration/:courseId/strategy/:strategyId/distribution
|
|
266
|
+
*
|
|
267
|
+
* Get the current bell curve distribution for a strategy.
|
|
268
|
+
* Shows how users are distributed across weight space based on current confidence.
|
|
269
|
+
*
|
|
270
|
+
* Query params:
|
|
271
|
+
* - samples: number of sample points (default: 100)
|
|
272
|
+
*/
|
|
273
|
+
router.get('/:courseId/strategy/:strategyId/distribution', (req, res) => {
|
|
274
|
+
void (async () => {
|
|
275
|
+
try {
|
|
276
|
+
const { courseId, strategyId } = req.params;
|
|
277
|
+
const samples = parseInt(req.query.samples || '100', 10);
|
|
278
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
279
|
+
// Get strategy
|
|
280
|
+
const strategy = (await courseDB.get(strategyId));
|
|
281
|
+
if (!strategy.learnable) {
|
|
282
|
+
res.json({
|
|
283
|
+
courseId,
|
|
284
|
+
strategyId,
|
|
285
|
+
message: 'Strategy does not have learnable weights',
|
|
286
|
+
distribution: null,
|
|
287
|
+
});
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const { weight, confidence } = strategy.learnable;
|
|
291
|
+
// Compute spread
|
|
292
|
+
const MIN_SPREAD = 0.1;
|
|
293
|
+
const MAX_SPREAD = 0.5;
|
|
294
|
+
const spread = MAX_SPREAD - confidence * (MAX_SPREAD - MIN_SPREAD);
|
|
295
|
+
// Generate distribution points
|
|
296
|
+
const distribution = [];
|
|
297
|
+
for (let i = 0; i < samples; i++) {
|
|
298
|
+
const deviation = -1 + (2 * i) / (samples - 1); // Range [-1, 1]
|
|
299
|
+
const effectiveWeight = Math.max(0.1, Math.min(3.0, weight + deviation * spread * weight));
|
|
300
|
+
// Gaussian density (unnormalized, for visualization)
|
|
301
|
+
const density = Math.exp(-0.5 * (deviation / 0.5) ** 2);
|
|
302
|
+
distribution.push({
|
|
303
|
+
deviation,
|
|
304
|
+
effectiveWeight,
|
|
305
|
+
density,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
res.json({
|
|
309
|
+
courseId,
|
|
310
|
+
strategyId,
|
|
311
|
+
peakWeight: weight,
|
|
312
|
+
confidence,
|
|
313
|
+
spread,
|
|
314
|
+
distribution,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
logger.error(`[Orchestration] Distribution error: ${error}`);
|
|
319
|
+
res.status(500).json({ error: 'Failed to get distribution', details: String(error) });
|
|
320
|
+
}
|
|
321
|
+
})();
|
|
322
|
+
});
|
|
323
|
+
// ============================================================================
|
|
324
|
+
// HELPER FUNCTIONS
|
|
325
|
+
// ============================================================================
|
|
326
|
+
/**
|
|
327
|
+
* Aggregate user outcome records across all user databases.
|
|
328
|
+
*
|
|
329
|
+
* This is a server-side operation that requires admin access to query
|
|
330
|
+
* across user databases.
|
|
331
|
+
*
|
|
332
|
+
* TODO: Consider adding a view/index for more efficient querying.
|
|
333
|
+
*/
|
|
334
|
+
async function aggregateUserOutcomes(courseId, periodStart, periodEnd) {
|
|
335
|
+
const couch = getCouchDB();
|
|
336
|
+
const outcomes = [];
|
|
337
|
+
try {
|
|
338
|
+
// Get list of all user databases
|
|
339
|
+
const allDbs = await couch.db.list();
|
|
340
|
+
const userDbs = allDbs.filter((db) => db.startsWith('userdb-'));
|
|
341
|
+
logger.debug(`[Orchestration] Scanning ${userDbs.length} user databases for outcomes`);
|
|
342
|
+
for (const dbName of userDbs) {
|
|
343
|
+
try {
|
|
344
|
+
const userDb = couch.use(dbName);
|
|
345
|
+
// Query for outcome records for this course in the time range
|
|
346
|
+
const result = await userDb.find({
|
|
347
|
+
selector: {
|
|
348
|
+
docType: 'USER_OUTCOME',
|
|
349
|
+
courseId,
|
|
350
|
+
periodEnd: {
|
|
351
|
+
$gte: periodStart,
|
|
352
|
+
$lte: periodEnd,
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
limit: 1000,
|
|
356
|
+
});
|
|
357
|
+
for (const doc of result.docs) {
|
|
358
|
+
outcomes.push(doc);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
// Skip databases that don't have the index or fail
|
|
363
|
+
logger.debug(`[Orchestration] Skipping ${dbName}: ${e}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return outcomes;
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
logger.error(`[Orchestration] Error aggregating outcomes: ${error}`);
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
export default router;
|
|
374
|
+
//# sourceMappingURL=orchestration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestration.js","sourceRoot":"","sources":["../../src/routes/orchestration.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.24",
|
|
7
7
|
"description": "an API",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"type": "module",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"author": "Colin Kennedy",
|
|
36
36
|
"license": "GPL-3.0-or-later",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@vue-skuilder/common": "^0.1.
|
|
39
|
-
"@vue-skuilder/db": "^0.1.
|
|
38
|
+
"@vue-skuilder/common": "^0.1.24",
|
|
39
|
+
"@vue-skuilder/db": "^0.1.24",
|
|
40
40
|
"axios": "^1.12.0",
|
|
41
41
|
"cookie-parser": "^1.4.7",
|
|
42
42
|
"cors": "^2.8.5",
|
|
@@ -77,5 +77,5 @@
|
|
|
77
77
|
"typescript-eslint": "^8.48.1",
|
|
78
78
|
"vitest": "^4.0.15"
|
|
79
79
|
},
|
|
80
|
-
"stableVersion": "0.1.
|
|
80
|
+
"stableVersion": "0.1.24"
|
|
81
81
|
}
|
package/src/app-factory.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { classroomDbDesignDoc } from './design-docs.js';
|
|
|
28
28
|
import logger from './logger.js';
|
|
29
29
|
import logsRouter from './routes/logs.js';
|
|
30
30
|
import authRouter from './routes/auth.js';
|
|
31
|
+
import orchestrationRouter from './routes/orchestration.js';
|
|
31
32
|
import type { ExpressServerConfig, EnvironmentConfig } from './types.js';
|
|
32
33
|
import { applyUsersDesignDocs } from './couchdb/userDesignDocs.js';
|
|
33
34
|
|
|
@@ -94,6 +95,7 @@ export function createExpressApp(config: AppConfig): express.Application {
|
|
|
94
95
|
);
|
|
95
96
|
app.use('/logs', logsRouter);
|
|
96
97
|
app.use('/auth', authRouter);
|
|
98
|
+
app.use('/orchestration', orchestrationRouter);
|
|
97
99
|
|
|
98
100
|
// Routes
|
|
99
101
|
app.get('/courses', (_req: Request, res: Response) => {
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { Router, Request, Response } from 'express';
|
|
2
|
+
import Nano from 'nano';
|
|
3
|
+
import { requestIsAuthenticated } from '../couchdb/authentication.js';
|
|
4
|
+
import { useOrCreateCourseDB, getCouchDB } from '../couchdb/index.js';
|
|
5
|
+
import logger from '../logger.js';
|
|
6
|
+
import {
|
|
7
|
+
aggregateOutcomesForGradient,
|
|
8
|
+
computeStrategyGradient,
|
|
9
|
+
runPeriodUpdate,
|
|
10
|
+
getDefaultLearnableWeight,
|
|
11
|
+
} from '@vue-skuilder/db';
|
|
12
|
+
import type {
|
|
13
|
+
UserOutcomeRecord,
|
|
14
|
+
StrategyLearningState,
|
|
15
|
+
PeriodUpdateInput,
|
|
16
|
+
} from '@vue-skuilder/db';
|
|
17
|
+
import type { ContentNavigationStrategyData } from '@vue-skuilder/db/core';
|
|
18
|
+
|
|
19
|
+
const router = Router();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* POST /orchestration/:courseId/update
|
|
23
|
+
*
|
|
24
|
+
* Trigger a period update for all strategies in a course.
|
|
25
|
+
* This aggregates user outcomes and adjusts strategy weights based on gradients.
|
|
26
|
+
*
|
|
27
|
+
* Requires authentication.
|
|
28
|
+
*
|
|
29
|
+
* Query params:
|
|
30
|
+
* - periodStart: ISO timestamp (optional, defaults to 7 days ago)
|
|
31
|
+
* - periodEnd: ISO timestamp (optional, defaults to now)
|
|
32
|
+
*/
|
|
33
|
+
router.post('/:courseId/update', (req: Request, res: Response) => {
|
|
34
|
+
void (async () => {
|
|
35
|
+
try {
|
|
36
|
+
const auth = await requestIsAuthenticated(req);
|
|
37
|
+
if (!auth) {
|
|
38
|
+
res.status(401).json({ error: 'Unauthorized' });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { courseId } = req.params;
|
|
43
|
+
const periodEnd = (req.query.periodEnd as string) || new Date().toISOString();
|
|
44
|
+
const periodStart =
|
|
45
|
+
(req.query.periodStart as string) ||
|
|
46
|
+
new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
47
|
+
|
|
48
|
+
logger.info(
|
|
49
|
+
`[Orchestration] Period update requested for course ${courseId} ` +
|
|
50
|
+
`(${periodStart} to ${periodEnd})`
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// 1. Get course DB and strategies
|
|
54
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
55
|
+
const strategiesResp = await courseDB.find({
|
|
56
|
+
selector: { docType: 'NAVIGATION_STRATEGY' },
|
|
57
|
+
});
|
|
58
|
+
const strategies = strategiesResp.docs as unknown as ContentNavigationStrategyData[];
|
|
59
|
+
|
|
60
|
+
if (strategies.length === 0) {
|
|
61
|
+
res.json({ message: 'No strategies found', updated: 0 });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 2. Aggregate user outcomes for this course
|
|
66
|
+
// Note: This queries across user DBs - requires admin access
|
|
67
|
+
const outcomes = await aggregateUserOutcomes(courseId, periodStart, periodEnd);
|
|
68
|
+
|
|
69
|
+
if (outcomes.length === 0) {
|
|
70
|
+
res.json({ message: 'No outcome records found', updated: 0 });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
logger.info(`[Orchestration] Found ${outcomes.length} outcome records for ${courseId}`);
|
|
75
|
+
|
|
76
|
+
// 3. Process each strategy
|
|
77
|
+
const results = [];
|
|
78
|
+
|
|
79
|
+
for (const strategy of strategies) {
|
|
80
|
+
const strategyId = strategy._id;
|
|
81
|
+
|
|
82
|
+
// Skip static weight strategies
|
|
83
|
+
if (strategy.staticWeight) {
|
|
84
|
+
logger.debug(`[Orchestration] Skipping static strategy ${strategyId}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Get or create learning state
|
|
89
|
+
let existingState: StrategyLearningState | undefined;
|
|
90
|
+
try {
|
|
91
|
+
existingState = (await courseDB.get(
|
|
92
|
+
`STRATEGY_LEARNING_STATE::${courseId}::${strategyId}`
|
|
93
|
+
)) as unknown as StrategyLearningState;
|
|
94
|
+
} catch {
|
|
95
|
+
// No existing state, that's fine
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Aggregate observations for this strategy
|
|
99
|
+
const observations = aggregateOutcomesForGradient(outcomes, strategyId);
|
|
100
|
+
|
|
101
|
+
if (observations.length < 3) {
|
|
102
|
+
logger.debug(
|
|
103
|
+
`[Orchestration] Insufficient observations for ${strategyId} (${observations.length})`
|
|
104
|
+
);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Compute gradient
|
|
109
|
+
const gradient = computeStrategyGradient(observations);
|
|
110
|
+
if (!gradient) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Get current weight
|
|
115
|
+
const currentWeight = strategy.learnable || getDefaultLearnableWeight();
|
|
116
|
+
|
|
117
|
+
// Run update
|
|
118
|
+
const input: PeriodUpdateInput = {
|
|
119
|
+
courseId,
|
|
120
|
+
strategyId,
|
|
121
|
+
currentWeight,
|
|
122
|
+
gradient,
|
|
123
|
+
existingState,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const result = runPeriodUpdate(input);
|
|
127
|
+
|
|
128
|
+
// Persist updated strategy
|
|
129
|
+
if (result.updated) {
|
|
130
|
+
const updatedStrategy = {
|
|
131
|
+
...strategy,
|
|
132
|
+
learnable: result.newWeight,
|
|
133
|
+
};
|
|
134
|
+
await courseDB.insert(updatedStrategy as unknown as Nano.MaybeDocument);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Persist learning state
|
|
138
|
+
await courseDB.insert(result.learningState as unknown as Nano.MaybeDocument);
|
|
139
|
+
|
|
140
|
+
results.push({
|
|
141
|
+
strategyId,
|
|
142
|
+
previousWeight: result.previousWeight.weight,
|
|
143
|
+
newWeight: result.newWeight.weight,
|
|
144
|
+
gradient: gradient.gradient,
|
|
145
|
+
rSquared: gradient.rSquared,
|
|
146
|
+
observations: observations.length,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
res.json({
|
|
151
|
+
message: 'Period update complete',
|
|
152
|
+
courseId,
|
|
153
|
+
periodStart,
|
|
154
|
+
periodEnd,
|
|
155
|
+
outcomesProcessed: outcomes.length,
|
|
156
|
+
strategiesUpdated: results.filter((r) => r.previousWeight !== r.newWeight).length,
|
|
157
|
+
results,
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
logger.error(`[Orchestration] Period update error: ${error}`);
|
|
161
|
+
res.status(500).json({ error: 'Period update failed', details: String(error) });
|
|
162
|
+
}
|
|
163
|
+
})();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* GET /orchestration/:courseId/state
|
|
168
|
+
*
|
|
169
|
+
* Get current learning state for all strategies in a course.
|
|
170
|
+
*/
|
|
171
|
+
router.get('/:courseId/state', (req: Request, res: Response) => {
|
|
172
|
+
void (async () => {
|
|
173
|
+
try {
|
|
174
|
+
const { courseId } = req.params;
|
|
175
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
176
|
+
|
|
177
|
+
const statesResp = await courseDB.find({
|
|
178
|
+
selector: { docType: 'STRATEGY_LEARNING_STATE' },
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
res.json({
|
|
182
|
+
courseId,
|
|
183
|
+
states: statesResp.docs,
|
|
184
|
+
});
|
|
185
|
+
} catch (error) {
|
|
186
|
+
logger.error(`[Orchestration] Get state error: ${error}`);
|
|
187
|
+
res.status(500).json({ error: 'Failed to get state', details: String(error) });
|
|
188
|
+
}
|
|
189
|
+
})();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* GET /orchestration/:courseId/strategy/:strategyId/history
|
|
194
|
+
*
|
|
195
|
+
* Get weight history for a specific strategy.
|
|
196
|
+
*/
|
|
197
|
+
router.get('/:courseId/strategy/:strategyId/history', (req: Request, res: Response) => {
|
|
198
|
+
void (async () => {
|
|
199
|
+
try {
|
|
200
|
+
const { courseId, strategyId } = req.params;
|
|
201
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
202
|
+
|
|
203
|
+
const stateId = `STRATEGY_LEARNING_STATE::${courseId}::${strategyId}`;
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const state = (await courseDB.get(stateId)) as StrategyLearningState;
|
|
207
|
+
res.json({
|
|
208
|
+
courseId,
|
|
209
|
+
strategyId,
|
|
210
|
+
currentWeight: state.currentWeight,
|
|
211
|
+
regression: state.regression,
|
|
212
|
+
history: state.history,
|
|
213
|
+
});
|
|
214
|
+
} catch {
|
|
215
|
+
res.status(404).json({ error: 'No learning state found for this strategy' });
|
|
216
|
+
}
|
|
217
|
+
} catch (error) {
|
|
218
|
+
logger.error(`[Orchestration] Get history error: ${error}`);
|
|
219
|
+
res.status(500).json({ error: 'Failed to get history', details: String(error) });
|
|
220
|
+
}
|
|
221
|
+
})();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* GET /orchestration/:courseId/strategy/:strategyId/scatter
|
|
226
|
+
*
|
|
227
|
+
* Get scatter plot data (deviation vs outcome) for a specific strategy.
|
|
228
|
+
* Useful for visualizing the gradient and understanding the learning dynamics.
|
|
229
|
+
*
|
|
230
|
+
* Query params:
|
|
231
|
+
* - periodStart: ISO timestamp (optional, defaults to 7 days ago)
|
|
232
|
+
* - periodEnd: ISO timestamp (optional, defaults to now)
|
|
233
|
+
* - limit: max observations to return (optional, defaults to 1000)
|
|
234
|
+
*/
|
|
235
|
+
router.get('/:courseId/strategy/:strategyId/scatter', (req: Request, res: Response) => {
|
|
236
|
+
void (async () => {
|
|
237
|
+
try {
|
|
238
|
+
const { courseId, strategyId } = req.params;
|
|
239
|
+
const periodEnd = (req.query.periodEnd as string) || new Date().toISOString();
|
|
240
|
+
const periodStart =
|
|
241
|
+
(req.query.periodStart as string) ||
|
|
242
|
+
new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
243
|
+
const limit = parseInt((req.query.limit as string) || '1000', 10);
|
|
244
|
+
|
|
245
|
+
// Aggregate outcomes
|
|
246
|
+
const outcomes = await aggregateUserOutcomes(courseId, periodStart, periodEnd);
|
|
247
|
+
|
|
248
|
+
// Extract scatter data for this strategy
|
|
249
|
+
const scatterData = outcomes
|
|
250
|
+
.filter((outcome) => outcome.deviations[strategyId] !== undefined)
|
|
251
|
+
.map((outcome) => ({
|
|
252
|
+
deviation: outcome.deviations[strategyId],
|
|
253
|
+
outcome: outcome.outcomeValue,
|
|
254
|
+
userId: outcome.userId,
|
|
255
|
+
periodEnd: outcome.periodEnd,
|
|
256
|
+
}))
|
|
257
|
+
.slice(0, limit);
|
|
258
|
+
|
|
259
|
+
// Compute regression for visualization
|
|
260
|
+
const observations = aggregateOutcomesForGradient(outcomes, strategyId);
|
|
261
|
+
const gradient = observations.length >= 3 ? computeStrategyGradient(observations) : null;
|
|
262
|
+
|
|
263
|
+
res.json({
|
|
264
|
+
courseId,
|
|
265
|
+
strategyId,
|
|
266
|
+
periodStart,
|
|
267
|
+
periodEnd,
|
|
268
|
+
observations: scatterData.length,
|
|
269
|
+
data: scatterData,
|
|
270
|
+
regression: gradient,
|
|
271
|
+
});
|
|
272
|
+
} catch (error) {
|
|
273
|
+
logger.error(`[Orchestration] Scatter plot error: ${error}`);
|
|
274
|
+
res.status(500).json({ error: 'Failed to get scatter data', details: String(error) });
|
|
275
|
+
}
|
|
276
|
+
})();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* GET /orchestration/:courseId/weights
|
|
281
|
+
*
|
|
282
|
+
* Get current learned weights for all strategies in a course.
|
|
283
|
+
* Simpler alternative to /state endpoint - just returns weight/confidence without history.
|
|
284
|
+
*/
|
|
285
|
+
router.get('/:courseId/weights', (req: Request, res: Response) => {
|
|
286
|
+
void (async () => {
|
|
287
|
+
try {
|
|
288
|
+
const { courseId } = req.params;
|
|
289
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
290
|
+
|
|
291
|
+
// Get all strategies
|
|
292
|
+
const strategiesResp = await courseDB.find({
|
|
293
|
+
selector: { docType: 'NAVIGATION_STRATEGY' },
|
|
294
|
+
});
|
|
295
|
+
const strategies = strategiesResp.docs as unknown as ContentNavigationStrategyData[];
|
|
296
|
+
|
|
297
|
+
const weights = strategies.map((strategy) => ({
|
|
298
|
+
strategyId: strategy._id,
|
|
299
|
+
strategyName: strategy.name,
|
|
300
|
+
implementingClass: strategy.implementingClass,
|
|
301
|
+
learnable: strategy.learnable || null,
|
|
302
|
+
staticWeight: strategy.staticWeight || false,
|
|
303
|
+
}));
|
|
304
|
+
|
|
305
|
+
res.json({
|
|
306
|
+
courseId,
|
|
307
|
+
weights,
|
|
308
|
+
});
|
|
309
|
+
} catch (error) {
|
|
310
|
+
logger.error(`[Orchestration] Get weights error: ${error}`);
|
|
311
|
+
res.status(500).json({ error: 'Failed to get weights', details: String(error) });
|
|
312
|
+
}
|
|
313
|
+
})();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* GET /orchestration/:courseId/strategy/:strategyId/distribution
|
|
318
|
+
*
|
|
319
|
+
* Get the current bell curve distribution for a strategy.
|
|
320
|
+
* Shows how users are distributed across weight space based on current confidence.
|
|
321
|
+
*
|
|
322
|
+
* Query params:
|
|
323
|
+
* - samples: number of sample points (default: 100)
|
|
324
|
+
*/
|
|
325
|
+
router.get('/:courseId/strategy/:strategyId/distribution', (req: Request, res: Response) => {
|
|
326
|
+
void (async () => {
|
|
327
|
+
try {
|
|
328
|
+
const { courseId, strategyId } = req.params;
|
|
329
|
+
const samples = parseInt((req.query.samples as string) || '100', 10);
|
|
330
|
+
const courseDB = await useOrCreateCourseDB(courseId);
|
|
331
|
+
|
|
332
|
+
// Get strategy
|
|
333
|
+
const strategy = (await courseDB.get(strategyId)) as unknown as ContentNavigationStrategyData;
|
|
334
|
+
|
|
335
|
+
if (!strategy.learnable) {
|
|
336
|
+
res.json({
|
|
337
|
+
courseId,
|
|
338
|
+
strategyId,
|
|
339
|
+
message: 'Strategy does not have learnable weights',
|
|
340
|
+
distribution: null,
|
|
341
|
+
});
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const { weight, confidence } = strategy.learnable;
|
|
346
|
+
|
|
347
|
+
// Compute spread
|
|
348
|
+
const MIN_SPREAD = 0.1;
|
|
349
|
+
const MAX_SPREAD = 0.5;
|
|
350
|
+
const spread = MAX_SPREAD - confidence * (MAX_SPREAD - MIN_SPREAD);
|
|
351
|
+
|
|
352
|
+
// Generate distribution points
|
|
353
|
+
const distribution = [];
|
|
354
|
+
for (let i = 0; i < samples; i++) {
|
|
355
|
+
const deviation = -1 + (2 * i) / (samples - 1); // Range [-1, 1]
|
|
356
|
+
const effectiveWeight = Math.max(0.1, Math.min(3.0, weight + deviation * spread * weight));
|
|
357
|
+
|
|
358
|
+
// Gaussian density (unnormalized, for visualization)
|
|
359
|
+
const density = Math.exp(-0.5 * (deviation / 0.5) ** 2);
|
|
360
|
+
|
|
361
|
+
distribution.push({
|
|
362
|
+
deviation,
|
|
363
|
+
effectiveWeight,
|
|
364
|
+
density,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
res.json({
|
|
369
|
+
courseId,
|
|
370
|
+
strategyId,
|
|
371
|
+
peakWeight: weight,
|
|
372
|
+
confidence,
|
|
373
|
+
spread,
|
|
374
|
+
distribution,
|
|
375
|
+
});
|
|
376
|
+
} catch (error) {
|
|
377
|
+
logger.error(`[Orchestration] Distribution error: ${error}`);
|
|
378
|
+
res.status(500).json({ error: 'Failed to get distribution', details: String(error) });
|
|
379
|
+
}
|
|
380
|
+
})();
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// ============================================================================
|
|
384
|
+
// HELPER FUNCTIONS
|
|
385
|
+
// ============================================================================
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Aggregate user outcome records across all user databases.
|
|
389
|
+
*
|
|
390
|
+
* This is a server-side operation that requires admin access to query
|
|
391
|
+
* across user databases.
|
|
392
|
+
*
|
|
393
|
+
* TODO: Consider adding a view/index for more efficient querying.
|
|
394
|
+
*/
|
|
395
|
+
async function aggregateUserOutcomes(
|
|
396
|
+
courseId: string,
|
|
397
|
+
periodStart: string,
|
|
398
|
+
periodEnd: string
|
|
399
|
+
): Promise<UserOutcomeRecord[]> {
|
|
400
|
+
const couch = getCouchDB();
|
|
401
|
+
const outcomes: UserOutcomeRecord[] = [];
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
// Get list of all user databases
|
|
405
|
+
const allDbs = await couch.db.list();
|
|
406
|
+
const userDbs = allDbs.filter((db) => db.startsWith('userdb-'));
|
|
407
|
+
|
|
408
|
+
logger.debug(`[Orchestration] Scanning ${userDbs.length} user databases for outcomes`);
|
|
409
|
+
|
|
410
|
+
for (const dbName of userDbs) {
|
|
411
|
+
try {
|
|
412
|
+
const userDb = couch.use(dbName);
|
|
413
|
+
|
|
414
|
+
// Query for outcome records for this course in the time range
|
|
415
|
+
const result = await userDb.find({
|
|
416
|
+
selector: {
|
|
417
|
+
docType: 'USER_OUTCOME',
|
|
418
|
+
courseId,
|
|
419
|
+
periodEnd: {
|
|
420
|
+
$gte: periodStart,
|
|
421
|
+
$lte: periodEnd,
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
limit: 1000,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
for (const doc of result.docs) {
|
|
428
|
+
outcomes.push(doc as unknown as UserOutcomeRecord);
|
|
429
|
+
}
|
|
430
|
+
} catch (e) {
|
|
431
|
+
// Skip databases that don't have the index or fail
|
|
432
|
+
logger.debug(`[Orchestration] Skipping ${dbName}: ${e}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return outcomes;
|
|
437
|
+
} catch (error) {
|
|
438
|
+
logger.error(`[Orchestration] Error aggregating outcomes: ${error}`);
|
|
439
|
+
return [];
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export default router;
|