bulltrackers-module 1.0.140 → 1.0.141
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.
|
@@ -7,7 +7,8 @@ const { normalizeName, commitBatchInChunks } = require('../utils/utils.js');
|
|
|
7
7
|
function groupByPass(manifest) { return manifest.reduce((acc, calc) => { (acc[calc.pass] = acc[calc.pass] || []).push(calc); return acc; }, {}); }
|
|
8
8
|
|
|
9
9
|
/** * --- MODIFIED: Returns detailed missing dependencies for logging ---
|
|
10
|
-
* Stage 2: Check root data dependencies for a calc
|
|
10
|
+
* Stage 2: Check root data dependencies for a calc
|
|
11
|
+
* --- THIS FUNCTION IS NOW MORE GRANULAR ---
|
|
11
12
|
*/
|
|
12
13
|
function checkRootDependencies(calcManifest, rootDataStatus) {
|
|
13
14
|
const missing = [];
|
|
@@ -23,7 +24,7 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/** * --- MODIFIED: Uses earliestDates map to avoid unnecessary queries ---
|
|
26
|
-
* Stage 3: Check root data availability for a date
|
|
27
|
+
* Stage 3: Check root data availability for a date
|
|
27
28
|
*/
|
|
28
29
|
async function checkRootDataAvailability(dateStr, config, dependencies, earliestDates) {
|
|
29
30
|
const { logger } = dependencies;
|
|
@@ -33,7 +34,9 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
|
|
|
33
34
|
let hasPortfolio = false, hasInsights = false, hasSocial = false, hasHistory = false;
|
|
34
35
|
try {
|
|
35
36
|
const tasks = [];
|
|
36
|
-
|
|
37
|
+
// This logic is correct. It *avoids* calling get...Refs
|
|
38
|
+
// if the dateToProcess is before the earliest known data.
|
|
39
|
+
if (dateToProcess >= earliestDates.portfolio)
|
|
37
40
|
{tasks.push(getPortfolioPartRefs(config, dependencies, dateStr).then(res => {portfolioRefs = res;hasPortfolio = !!(res?.length);}));}
|
|
38
41
|
if (dateToProcess >= earliestDates.insights) {
|
|
39
42
|
tasks.push(loadDailyInsights(config, dependencies, dateStr).then(res => {insightsData = res;hasInsights = !!res;}));}
|
|
@@ -41,8 +44,17 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
|
|
|
41
44
|
tasks.push(loadDailySocialPostInsights(config, dependencies, dateStr).then(res => {socialData = res;hasSocial = !!res;}));}
|
|
42
45
|
if (dateToProcess >= earliestDates.history) {
|
|
43
46
|
tasks.push(getHistoryPartRefs(config, dependencies, dateStr).then(res => {historyRefs = res;hasHistory = !!(res?.length);}));}
|
|
47
|
+
|
|
44
48
|
await Promise.all(tasks);
|
|
45
|
-
|
|
49
|
+
|
|
50
|
+
// --- NEW: Log what was *actually* found ---
|
|
51
|
+
logger.log('INFO', `[PassRunner] Data availability for ${dateStr}: P:${hasPortfolio}, I:${hasInsights}, S:${hasSocial}, H:${hasHistory}`);
|
|
52
|
+
|
|
53
|
+
if (!(hasPortfolio || hasInsights || hasSocial || hasHistory)) {
|
|
54
|
+
logger.log('WARN', `[PassRunner] No root data at all for ${dateStr}.`);
|
|
55
|
+
// We return null to skip the entire day
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
46
58
|
return {portfolioRefs, insightsData,socialData,historyRefs,status: { hasPortfolio, hasInsights, hasSocial, hasHistory }};
|
|
47
59
|
} catch (err) { logger.log('ERROR', `[PassRunner] Error checking data for ${dateStr}`, { errorMessage: err.message }); return null; }
|
|
48
60
|
}
|
|
@@ -70,34 +82,85 @@ async function fetchExistingResults(dateStr, calcsInPass, fullManifest, config,
|
|
|
70
82
|
return fetched;
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* --- ENTIRELY REWRITTEN: Stage 5: Filter calculations ---
|
|
87
|
+
* This function now implements your "even better design".
|
|
88
|
+
* It calculates the *true earliest run date* for every calculation
|
|
89
|
+
* and filters them out *before* the "Running..." log ever appears.
|
|
74
90
|
*/
|
|
75
|
-
function filterCalculations(standardCalcs, metaCalcs, rootDataStatus, existingResults, passToRun, dateStr,
|
|
91
|
+
function filterCalculations(standardCalcs, metaCalcs, rootDataStatus, existingResults, passToRun, dateStr, earliestDates, logger) {
|
|
76
92
|
const skipped = new Set();
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
93
|
+
const dateToProcess = new Date(dateStr + 'T00:00:00Z');
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* --- CORRECTED LOGIC ---
|
|
97
|
+
* Helper to get the true earliest date a calc can run.
|
|
98
|
+
*/
|
|
99
|
+
const getTrueEarliestRunDate = (calc) => {
|
|
100
|
+
let earliestRunDate = new Date('1970-01-01T00:00:00Z'); // Start at zero
|
|
101
|
+
const dependencies = calc.rootDataDependencies || [];
|
|
102
|
+
|
|
103
|
+
// 1. Find the LATEST "today" dependency
|
|
104
|
+
// This is the date where all *today* data is first available
|
|
105
|
+
for (const dep of dependencies) {
|
|
106
|
+
if (dep === 'portfolio' && earliestDates.portfolio > earliestRunDate) earliestRunDate = earliestDates.portfolio;
|
|
107
|
+
if (dep === 'history' && earliestDates.history > earliestRunDate) earliestRunDate = earliestDates.history;
|
|
108
|
+
if (dep === 'social' && earliestDates.social > earliestRunDate) earliestRunDate = earliestDates.social;
|
|
109
|
+
if (dep === 'insights' && earliestDates.insights > earliestRunDate) earliestRunDate = earliestDates.insights;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 2. If the calc is historical, shift the *final* date by +1
|
|
113
|
+
// This universally applies the "+1" rule if *any* yesterday data is needed,
|
|
114
|
+
// (as long as we found a dependency in step 1).
|
|
115
|
+
if (calc.isHistorical && earliestRunDate.getTime() > 0) {
|
|
116
|
+
earliestRunDate.setUTCDate(earliestRunDate.getUTCDate() + 1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return earliestRunDate;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const filterCalc = (calc) => {
|
|
123
|
+
// 1. Skip if result already exists
|
|
124
|
+
if (existingResults[calc.name]) {
|
|
125
|
+
logger.log('TRACE', `[Pass ${passToRun}] Skipping ${calc.name} for ${dateStr}. Result already exists.`);
|
|
126
|
+
skipped.add(calc.name);
|
|
82
127
|
return false;
|
|
83
128
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
129
|
+
|
|
130
|
+
// 2. Check *true* earliest run date
|
|
131
|
+
const earliestRunDate = getTrueEarliestRunDate(calc);
|
|
132
|
+
if (dateToProcess < earliestRunDate) {
|
|
133
|
+
logger.log('TRACE', `[Pass ${passToRun}] Skipping ${calc.name} for ${dateStr}. Date is before true earliest run date (${earliestRunDate.toISOString().slice(0, 10)}).`);
|
|
134
|
+
skipped.add(calc.name);
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 3. Check if *today's* root data was *actually* found
|
|
139
|
+
// This handles gaps *after* the earliest date
|
|
140
|
+
const { canRun, missing: missingRoot } = checkRootDependencies(calc, rootDataStatus);
|
|
141
|
+
if (!canRun) {
|
|
142
|
+
logger.log('INFO', `[Pass ${passToRun}] Skipping ${calc.name} for ${dateStr}. Data missing for this date: [${missingRoot.join(', ')}]`);
|
|
143
|
+
skipped.add(calc.name);
|
|
92
144
|
return false;
|
|
93
145
|
}
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
146
|
+
|
|
147
|
+
// 4. (For Meta Calcs) Check computed dependencies
|
|
148
|
+
if (calc.type === 'meta') {
|
|
149
|
+
const missingDeps = (calc.dependencies || []).map(normalizeName).filter(d => !existingResults[d]);
|
|
150
|
+
if (missingDeps.length > 0) {
|
|
151
|
+
logger.log('WARN', `[Pass ${passToRun} Meta] Skipping ${calc.name} for ${dateStr}. Missing computed deps: [${missingDeps.join(', ')}]`);
|
|
152
|
+
skipped.add(calc.name);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// If it passed all checks, run it.
|
|
158
|
+
return true;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const standardCalcsToRun = standardCalcs.filter(filterCalc);
|
|
162
|
+
const metaCalcsToRun = metaCalcs.filter(filterCalc);
|
|
163
|
+
|
|
101
164
|
return { standardCalcsToRun, metaCalcsToRun };
|
|
102
165
|
}
|
|
103
166
|
|
|
@@ -107,21 +170,91 @@ function initializeCalculators(calcs, logger) { const state = {}; for (const c o
|
|
|
107
170
|
|
|
108
171
|
/** Stage 7: Load historical data required for calculations */
|
|
109
172
|
async function loadHistoricalData(date, calcs, config, deps, rootData) { const updated = {...rootData}, dStr=date.toISOString().slice(0,10); const tasks = [];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
173
|
+
const needsYesterdayPortfolio = calcs.some(c => c.isHistorical && c.rootDataDependencies.includes('portfolio'));
|
|
174
|
+
const needsTodayHistory = calcs.some(c => c.rootDataDependencies.includes('history'));
|
|
175
|
+
const needsYesterdayHistory = calcs.some(c => c.isHistorical && c.rootDataDependencies.includes('history'));
|
|
176
|
+
// --- NEW: Add checks for historical insights and social ---
|
|
177
|
+
const needsYesterdayInsights = calcs.some(c => c.isHistorical && c.rootDataDependencies.includes('insights'));
|
|
178
|
+
const needsYesterdaySocial = calcs.some(c => c.isHistorical && c.rootDataDependencies.includes('social'));
|
|
179
|
+
|
|
180
|
+
// --- MODIFIED: Be smarter about loading data ---
|
|
181
|
+
if(needsYesterdayPortfolio) {
|
|
182
|
+
tasks.push((async()=>{ const prev=new Date(date); prev.setUTCDate(prev.getUTCDate()-1); const prevStr=prev.toISOString().slice(0,10);
|
|
183
|
+
logger.log('INFO', `[PassRunner] Loading YESTERDAY portfolio data for ${prevStr}`);
|
|
184
|
+
updated.yesterdayPortfolios=await loadFullDayMap(config,deps,await getPortfolioPartRefs(config,deps,prevStr));
|
|
185
|
+
})());
|
|
186
|
+
}
|
|
187
|
+
if(needsTodayHistory) {
|
|
188
|
+
tasks.push((async()=>{
|
|
189
|
+
logger.log('INFO', `[PassRunner] Loading TODAY history data for ${dStr}`);
|
|
190
|
+
updated.todayHistoryData=await loadFullDayMap(config,deps,rootData.historyRefs);
|
|
191
|
+
})());
|
|
192
|
+
}
|
|
193
|
+
if(needsYesterdayHistory) {
|
|
194
|
+
tasks.push((async()=>{ const prev=new Date(date); prev.setUTCDate(prev.getUTCDate()-1); const prevStr=prev.toISOString().slice(0,10);
|
|
195
|
+
logger.log('INFO', `[PassRunner] Loading YESTERDAY history data for ${prevStr}`);
|
|
196
|
+
updated.yesterdayHistoryData=await loadFullDayMap(config,deps,await getHistoryPartRefs(config,deps,prevStr));
|
|
197
|
+
})());
|
|
198
|
+
}
|
|
199
|
+
// --- NEW: Load historical insights/social ---
|
|
200
|
+
if(needsYesterdayInsights) {
|
|
201
|
+
tasks.push((async()=>{ const prev=new Date(date); prev.setUTCDate(prev.getUTCDate()-1); const prevStr=prev.toISOString().slice(0,10);
|
|
202
|
+
logger.log('INFO', `[PassRunner] Loading YESTERDAY insights data for ${prevStr}`);
|
|
203
|
+
updated.yesterdayInsights=await loadDailyInsights(config,deps,prevStr);
|
|
204
|
+
})());
|
|
205
|
+
}
|
|
206
|
+
if(needsYesterdaySocial) {
|
|
207
|
+
tasks.push((async()=>{ const prev=new Date(date); prev.setUTCDate(prev.getUTCDate()-1); const prevStr=prev.toISOString().slice(0,10);
|
|
208
|
+
logger.log('INFO', `[PassRunner] Loading YESTERDAY social data for ${prevStr}`);
|
|
209
|
+
updated.yesterdaySocialPostInsights=await loadDailySocialPostInsights(config,deps,prevStr);
|
|
210
|
+
})());
|
|
211
|
+
}
|
|
212
|
+
|
|
113
213
|
await Promise.all(tasks); return updated;
|
|
114
214
|
}
|
|
115
215
|
|
|
216
|
+
|
|
116
217
|
/** * --- MODIFIED: Stage 8: Stream and process data for standard calculations ---
|
|
117
218
|
* This function now uses an async generator to stream portfolio data
|
|
118
219
|
* instead of loading it all into memory.
|
|
119
220
|
*/
|
|
120
221
|
async function streamAndProcess(dateStr, state, passName, config, deps, rootData) {
|
|
121
222
|
const { logger, calculationUtils } = deps;
|
|
223
|
+
// --- MODIFIED: yesterdayInsights/Social are now loaded by loadHistoricalData ---
|
|
122
224
|
const { todayInsights, yesterdayInsights, todaySocialPostInsights, yesterdaySocialPostInsights, todayHistoryData, yesterdayHistoryData, yesterdayPortfolios } = rootData;
|
|
123
|
-
|
|
225
|
+
|
|
226
|
+
// --- NEW: Check if streaming is even needed ---
|
|
227
|
+
const calcsThatStreamPortfolio = Object.values(state).filter(calc => calc && calc.manifest && (calc.manifest.rootDataDependencies.includes('portfolio') || calc.manifest.category === 'speculators'));
|
|
228
|
+
|
|
124
229
|
const context={instrumentMappings:(await calculationUtils.loadInstrumentMappings()).instrumentToTicker, sectorMapping:(await calculationUtils.loadInstrumentMappings()).instrumentToSector, todayDateStr:dateStr, dependencies:deps, config};
|
|
230
|
+
|
|
231
|
+
// --- MODIFIED: Run non-streaming calcs first (social/insights) ---
|
|
232
|
+
// This allows them to run even if portfolio data is missing
|
|
233
|
+
let firstUser=true; // Used to run them only once
|
|
234
|
+
for(const name in state){
|
|
235
|
+
const calc=state[name]; if(!calc||typeof calc.process!=='function') continue;
|
|
236
|
+
const cat=calc.manifest.category;
|
|
237
|
+
if(cat==='socialPosts'||cat==='insights') {
|
|
238
|
+
if (firstUser) {
|
|
239
|
+
logger.log('INFO', `[${passName}] Running non-streaming calc: ${name}`);
|
|
240
|
+
let args=[null,null,null,{...context, userType: 'n/a'},todayInsights,yesterdayInsights,todaySocialPostInsights,yesterdaySocialPostInsights,todayHistoryData,yesterdayHistoryData];
|
|
241
|
+
// Pass historical data if needed
|
|
242
|
+
if(calc.manifest.isHistorical) {
|
|
243
|
+
args=[null,null,null,{...context, userType: 'n/a'},todayInsights,yesterdayInsights,todaySocialPostInsights,yesterdaySocialPostInsights,todayHistoryData,yesterdayHistoryData];
|
|
244
|
+
}
|
|
245
|
+
try{ await Promise.resolve(calc.process(...args)); } catch(e){logger.log('WARN',`Process error ${name} (non-stream)`,{err:e.message});}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
if (calcsThatStreamPortfolio.length === 0) {
|
|
252
|
+
logger.log('INFO', `[${passName}] No portfolio-streaming calcs to run for ${dateStr}. Skipping stream.`);
|
|
253
|
+
return; // Exit stream function
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
logger.log('INFO', `[${passName}] Streaming portfolio data for ${calcsThatStreamPortfolio.length} calcs...`);
|
|
257
|
+
|
|
125
258
|
for await (const chunk of streamPortfolioData(config, deps, dateStr)) {
|
|
126
259
|
for(const uid in chunk){ const p=chunk[uid]; if(!p) continue;
|
|
127
260
|
const userType=p.PublicPositions?'speculator':'normal';
|
|
@@ -129,9 +262,18 @@ async function streamAndProcess(dateStr, state, passName, config, deps, rootData
|
|
|
129
262
|
for(const name in state){
|
|
130
263
|
const calc=state[name]; if(!calc||typeof calc.process!=='function') continue;
|
|
131
264
|
const cat=calc.manifest.category, isSocialOrInsights=cat==='socialPosts'||cat==='insights', isHistorical=calc.manifest.isHistorical, isSpec=cat==='speculators';
|
|
265
|
+
|
|
266
|
+
// --- MODIFIED: Skip social/insights here, they ran above ---
|
|
267
|
+
if(isSocialOrInsights) continue;
|
|
268
|
+
|
|
132
269
|
let args=[p,null,uid,context,todayInsights,yesterdayInsights,todaySocialPostInsights,yesterdaySocialPostInsights,todayHistoryData,yesterdayHistoryData];
|
|
133
|
-
|
|
134
|
-
if(isHistorical){
|
|
270
|
+
|
|
271
|
+
if(isHistorical){
|
|
272
|
+
const pY=yesterdayPortfolios ? yesterdayPortfolios[uid] : null; // Check if yesterdayPortfolios exists
|
|
273
|
+
// V3 behavioural calcs (like history aggregator) *can* run without pY
|
|
274
|
+
if(!pY && (cat !== 'behavioural' && name !== 'historical-performance-aggregator')) continue;
|
|
275
|
+
args=[p,pY,uid,context,todayInsights,yesterdayInsights,todaySocialPostInsights,yesterdaySocialPostInsights,todayHistoryData,yesterdayHistoryData];
|
|
276
|
+
}
|
|
135
277
|
if((userType==='normal'&&isSpec)||(userType==='speculator'&&!isSpec&&name!=='users-processed')) continue;
|
|
136
278
|
try{ await Promise.resolve(calc.process(...args)); } catch(e){logger.log('WARN',`Process error ${name} for ${uid}`,{err:e.message});} }
|
|
137
279
|
firstUser=false;
|
|
@@ -142,11 +284,20 @@ async function streamAndProcess(dateStr, state, passName, config, deps, rootData
|
|
|
142
284
|
/** Stage 9: Run standard computations */
|
|
143
285
|
async function runStandardComputationPass(date, calcs, passName, config, deps, rootData) {
|
|
144
286
|
const dStr = date.toISOString().slice(0, 10), logger = deps.logger;
|
|
145
|
-
|
|
287
|
+
// --- THIS IS THE CRITICAL CHANGE ---
|
|
288
|
+
// If 'calcs' is empty *because of the new filter*, this log won't even appear.
|
|
289
|
+
if (calcs.length === 0) {
|
|
290
|
+
logger.log('INFO', `[${passName}] No standard calcs to run for ${dateStr} after filtering.`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
// This log now only appears if there is *actually* work to do.
|
|
146
294
|
logger.log('INFO', `[${passName}] Running ${dStr} with ${calcs.length} calcs.`);
|
|
295
|
+
|
|
147
296
|
const fullRoot = await loadHistoricalData(date, calcs, config, deps, rootData);
|
|
148
297
|
const state = initializeCalculators(calcs, logger);
|
|
298
|
+
|
|
149
299
|
await streamAndProcess(dStr, state, passName, config, deps, fullRoot);
|
|
300
|
+
|
|
150
301
|
let success = 0;
|
|
151
302
|
const standardWrites = [];
|
|
152
303
|
const shardedWrites = {};
|
|
@@ -188,25 +339,19 @@ async function runStandardComputationPass(date, calcs, passName, config, deps, r
|
|
|
188
339
|
const shardedDocWrites = [];
|
|
189
340
|
let docRef;
|
|
190
341
|
if (docPath.includes('/')) {
|
|
191
|
-
// Path is absolute, e.g., 'social_prediction_regime_state/history'
|
|
192
342
|
docRef = deps.db.doc(docPath);
|
|
193
343
|
} else {
|
|
194
|
-
// Path is a docId, e.g., 'user_profile_history_shard_0'
|
|
195
|
-
// We must infer its collection from config.
|
|
196
344
|
const collection = (docPath.startsWith('user_profile_history'))
|
|
197
|
-
? config.shardedUserProfileCollection
|
|
198
|
-
: config.shardedProfitabilityCollection;
|
|
345
|
+
? config.shardedUserProfileCollection
|
|
346
|
+
: config.shardedProfitabilityCollection;
|
|
199
347
|
docRef = deps.db.collection(collection).doc(docPath);
|
|
200
348
|
}
|
|
201
|
-
// Ensure data is a valid object before pushing
|
|
202
349
|
if (docData && typeof docData === 'object' && !Array.isArray(docData)) {
|
|
203
350
|
shardedDocWrites.push({ ref: docRef, data: docData });
|
|
204
351
|
} else {
|
|
205
352
|
logger.log('ERROR', `[${passName}] Invalid sharded document data for ${docPath}. Not an object.`, { data: docData });
|
|
206
353
|
}
|
|
207
|
-
// Commit this single document write (or small batch if logic is changed later)
|
|
208
354
|
if (shardedDocWrites.length > 0) {
|
|
209
|
-
// Use the docPath in the operation name for clearer logging
|
|
210
355
|
await commitBatchInChunks(config, deps, shardedDocWrites, `${passName} Sharded ${docPath} ${dStr}`);
|
|
211
356
|
}
|
|
212
357
|
}
|
|
@@ -216,8 +361,17 @@ async function runStandardComputationPass(date, calcs, passName, config, deps, r
|
|
|
216
361
|
/** Stage 10: Run meta computations */
|
|
217
362
|
async function runMetaComputationPass(date, calcs, passName, config, deps, fetchedDeps, rootData) {
|
|
218
363
|
const dStr = date.toISOString().slice(0, 10), logger = deps.logger;
|
|
219
|
-
|
|
364
|
+
// --- THIS IS THE CRITICAL CHANGE ---
|
|
365
|
+
if (calcs.length === 0) {
|
|
366
|
+
logger.log('INFO', `[${passName}] No meta calcs to run for ${dStr} after filtering.`);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
220
369
|
logger.log('INFO', `[${passName}] Running ${dStr} with ${calcs.length} calcs.`);
|
|
370
|
+
|
|
371
|
+
// --- NEW: Load historical data for meta calcs if needed ---
|
|
372
|
+
// (This might be redundant if standard pass ran, but meta-calcs can run standalone)
|
|
373
|
+
const fullRoot = await loadHistoricalData(date, calcs, config, deps, rootData);
|
|
374
|
+
|
|
221
375
|
let success = 0;
|
|
222
376
|
const standardWrites = [];
|
|
223
377
|
const shardedWrites = {};
|
|
@@ -226,7 +380,8 @@ async function runMetaComputationPass(date, calcs, passName, config, deps, fetch
|
|
|
226
380
|
if (typeof Cl !== 'function') {logger.log('ERROR', `Invalid class ${name}`);continue;}
|
|
227
381
|
const inst = new Cl();
|
|
228
382
|
try {
|
|
229
|
-
|
|
383
|
+
// --- MODIFIED: Pass fullRoot to meta calcs ---
|
|
384
|
+
const result = await Promise.resolve(inst.process(dStr, { ...deps, rootData: fullRoot }, config, fetchedDeps));
|
|
230
385
|
if (result && Object.keys(result).length > 0) {const standardResult = {}; for (const key in result) {
|
|
231
386
|
if (key.startsWith('sharded_')) {const shardedData = result[key];for (const collectionName in shardedData)
|
|
232
387
|
{if (!shardedWrites[collectionName]) shardedWrites[collectionName] = {};Object.assign(shardedWrites[collectionName], shardedData[collectionName]);}
|