bulltrackers-module 1.0.106 → 1.0.108
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.
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* passes computed dependencies in-memory.
|
|
7
7
|
* NEW: Checks for root data (portfolios, insights, social)
|
|
8
8
|
* before processing any calculations for a given day.
|
|
9
|
+
*
|
|
10
|
+
* MODIFICATION: Meta calculations now receive rootData in their
|
|
11
|
+
* dependencies object to allow them to stream users.
|
|
9
12
|
*/
|
|
10
13
|
|
|
11
14
|
const { FieldPath } = require('@google-cloud/firestore');
|
|
@@ -161,7 +164,8 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
161
164
|
`Pass ${passNum} (Meta)`,
|
|
162
165
|
config,
|
|
163
166
|
dependencies,
|
|
164
|
-
dailyResultsCache // <-- PASS THE CACHE
|
|
167
|
+
dailyResultsCache, // <-- PASS THE CACHE
|
|
168
|
+
rootData // <--- !! MODIFICATION: PASS rootData !!
|
|
165
169
|
);
|
|
166
170
|
|
|
167
171
|
// Add results to cache
|
|
@@ -540,9 +544,18 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
540
544
|
/**
|
|
541
545
|
* Internal sub-pipe: Runs "meta" or "backtest" computations for a single date.
|
|
542
546
|
* MODIFIED: Accepts in-memory cache and passes dependencies to calcs.
|
|
547
|
+
* MODIFIED: Accepts rootData and adds it to the dependencies object.
|
|
543
548
|
* Returns a map of results.
|
|
544
549
|
*/
|
|
545
|
-
async function runMetaComputation(
|
|
550
|
+
async function runMetaComputation(
|
|
551
|
+
dateToProcess,
|
|
552
|
+
calculationsToRun,
|
|
553
|
+
passName,
|
|
554
|
+
config,
|
|
555
|
+
dependencies,
|
|
556
|
+
dailyResultsCache,
|
|
557
|
+
rootData // <--- !! MODIFICATION: ACCEPT rootData !!
|
|
558
|
+
) {
|
|
546
559
|
const { db, logger } = dependencies;
|
|
547
560
|
const dateStr = dateToProcess.toISOString().slice(0, 10);
|
|
548
561
|
logger.log('INFO', `[${passName}] Starting run for ${dateStr} with ${calculationsToRun.length} calcs.`);
|
|
@@ -552,6 +565,13 @@ async function runMetaComputation(dateToProcess, calculationsToRun, passName, co
|
|
|
552
565
|
try {
|
|
553
566
|
const resultsCollectionRef = db.collection(config.resultsCollection).doc(dateStr).collection(config.resultsSubcollection);
|
|
554
567
|
let successCount = 0;
|
|
568
|
+
|
|
569
|
+
// --- !! MODIFICATION: Add rootData to the dependencies object !! ---
|
|
570
|
+
const dependenciesForMetaCalc = {
|
|
571
|
+
...dependencies,
|
|
572
|
+
rootData: rootData
|
|
573
|
+
};
|
|
574
|
+
// --- END MODIFICATION ---
|
|
555
575
|
|
|
556
576
|
for (const manifestCalc of calculationsToRun) {
|
|
557
577
|
const calcName = normalizeName(manifestCalc.name);
|
|
@@ -586,7 +606,7 @@ async function runMetaComputation(dateToProcess, calculationsToRun, passName, co
|
|
|
586
606
|
// --- Call process with the dependencies ---
|
|
587
607
|
const result = await Promise.resolve(instance.process(
|
|
588
608
|
dateStr,
|
|
589
|
-
|
|
609
|
+
dependenciesForMetaCalc, // <-- PASS MODIFIED DEPS
|
|
590
610
|
config,
|
|
591
611
|
computedDependencies // <-- PASS IN-MEMORY DEPS
|
|
592
612
|
));
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Main pipe: pipe.maintenance.runSpeculatorCleanup
|
|
3
|
+
* REFACTORED: Now stateless and receives dependencies.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { FieldValue } = require('@google-cloud/firestore');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Main pipe: pipe.maintenance.runSpeculatorCleanup
|
|
10
|
+
* Orchestrates the cleanup process.
|
|
11
|
+
* @param {object} config - Configuration object.
|
|
12
|
+
* @param {object} dependencies - Contains db, logger.
|
|
13
|
+
*/
|
|
14
|
+
exports.runCleanup = async (config, dependencies) => {
|
|
15
|
+
const { logger } = dependencies;
|
|
16
|
+
logger.log('INFO', '[CleanupHelpers] Running cleanup orchestrator...');
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// Start cleanup for pending users
|
|
20
|
+
const { batch: batchAfterPending, count: pendingRemoved } = await cleanupPendingSpeculators(config, dependencies);
|
|
21
|
+
|
|
22
|
+
// Continue with the same batch for stale speculators
|
|
23
|
+
const { batch: finalBatch, count: staleRemoved } = await cleanupStaleSpeculators(config, dependencies, batchAfterPending);
|
|
24
|
+
|
|
25
|
+
if (pendingRemoved > 0 || staleRemoved > 0) {
|
|
26
|
+
await finalBatch.commit();
|
|
27
|
+
logger.log('SUCCESS', `[CleanupHelpers] Cleanup commit successful. Removed ${pendingRemoved} pending, ${staleRemoved} stale speculators.`);
|
|
28
|
+
return { pendingRemoved, staleRemoved };
|
|
29
|
+
} else {
|
|
30
|
+
logger.log('SUCCESS', '[CleanupHelpers] No stale users found in pending or blocks.');
|
|
31
|
+
return { pendingRemoved: 0, staleRemoved: 0 };
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.log('ERROR', '[CleanupHelpers] FATAL error during cleanup orchestration', { errorMessage: error.message, errorStack: error.stack });
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Internal sub-pipe for cleaning pending speculators.
|
|
41
|
+
*/
|
|
42
|
+
async function cleanupPendingSpeculators(config, dependencies) {
|
|
43
|
+
const { db, logger } = dependencies;
|
|
44
|
+
logger.log('INFO', '[CleanupHelpers] Starting pending speculator cleanup...');
|
|
45
|
+
|
|
46
|
+
const batch = db.batch(); // Use db from dependencies
|
|
47
|
+
let stalePendingUsersRemoved = 0;
|
|
48
|
+
const pendingCollectionRef = db.collection(config.pendingSpeculatorsCollectionName); // Use db
|
|
49
|
+
const staleThreshold = new Date();
|
|
50
|
+
staleThreshold.setHours(staleThreshold.getHours() - config.pendingGracePeriodHours);
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const pendingSnapshot = await pendingCollectionRef.get();
|
|
54
|
+
if (pendingSnapshot.empty) {
|
|
55
|
+
logger.log('INFO', '[CleanupHelpers] Pending speculators collection is empty.');
|
|
56
|
+
return { batch, count: 0 };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const doc of pendingSnapshot.docs) {
|
|
60
|
+
const pendingData = doc.data().users || {};
|
|
61
|
+
const updates = {};
|
|
62
|
+
let updatesInDoc = 0;
|
|
63
|
+
|
|
64
|
+
for (const userId in pendingData) {
|
|
65
|
+
const timestamp = pendingData[userId]?.toDate ? pendingData[userId].toDate() : null;
|
|
66
|
+
if (timestamp && timestamp < staleThreshold) {
|
|
67
|
+
updates[`users.${userId}`] = FieldValue.delete();
|
|
68
|
+
stalePendingUsersRemoved++;
|
|
69
|
+
updatesInDoc++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (updatesInDoc > 0) {
|
|
74
|
+
logger.log('TRACE', `[CleanupHelpers] Marking ${updatesInDoc} users for removal from pending doc ${doc.id}`);
|
|
75
|
+
batch.update(doc.ref, updates);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
logger.log('INFO', `[CleanupHelpers] Marked ${stalePendingUsersRemoved} total stale pending users for removal.`);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
logger.log('ERROR', '[CleanupHelpers] Error cleaning pending speculators', { errorMessage: error.message });
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
return { batch, count: stalePendingUsersRemoved };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Internal sub-pipe for cleaning stale speculators.
|
|
88
|
+
*/
|
|
89
|
+
async function cleanupStaleSpeculators(config, dependencies, batch) {
|
|
90
|
+
const { db, logger } = dependencies;
|
|
91
|
+
logger.log('INFO', '[CleanupHelpers] Starting stale speculator cleanup from blocks...');
|
|
92
|
+
let totalUsersRemoved = 0;
|
|
93
|
+
const blocksCollectionRef = db.collection(config.speculatorBlocksCollectionName); // Use db
|
|
94
|
+
const gracePeriodDate = new Date();
|
|
95
|
+
gracePeriodDate.setDate(gracePeriodDate.getDate() - config.activityGracePeriodDays);
|
|
96
|
+
const blockCountsUpdate = {};
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const blocksSnapshot = await blocksCollectionRef.get();
|
|
100
|
+
if (blocksSnapshot.empty) {
|
|
101
|
+
logger.log('INFO', '[CleanupHelpers] Speculator blocks collection is empty.');
|
|
102
|
+
return { batch, count: 0 };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const doc of blocksSnapshot.docs) {
|
|
106
|
+
const blockId = doc.id;
|
|
107
|
+
const blockData = doc.data();
|
|
108
|
+
const users = blockData.users || {};
|
|
109
|
+
let usersRemovedFromBlock = 0;
|
|
110
|
+
const updates = {};
|
|
111
|
+
|
|
112
|
+
for (const userKey in users) {
|
|
113
|
+
const userId = userKey.split('.')[1];
|
|
114
|
+
if (!userId) continue;
|
|
115
|
+
|
|
116
|
+
const userData = users[userKey];
|
|
117
|
+
const lastHeldTimestamp = userData.lastHeldSpeculatorAsset?.toDate ? userData.lastHeldSpeculatorAsset.toDate() : null;
|
|
118
|
+
|
|
119
|
+
if (lastHeldTimestamp && lastHeldTimestamp < gracePeriodDate) {
|
|
120
|
+
updates[userKey] = FieldValue.delete();
|
|
121
|
+
usersRemovedFromBlock++;
|
|
122
|
+
|
|
123
|
+
if (userData.instruments && Array.isArray(userData.instruments)) {
|
|
124
|
+
userData.instruments.forEach(instrumentId => {
|
|
125
|
+
const instrumentBlockKey = `${instrumentId}_${blockId}`;
|
|
126
|
+
if (!blockCountsUpdate[instrumentBlockKey]) {
|
|
127
|
+
blockCountsUpdate[instrumentBlockKey] = 0;
|
|
128
|
+
}
|
|
129
|
+
blockCountsUpdate[instrumentBlockKey]--;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (usersRemovedFromBlock > 0) {
|
|
136
|
+
logger.log('TRACE', `[CleanupHelpers] Marking ${usersRemovedFromBlock} users for removal from block ${blockId}.`);
|
|
137
|
+
batch.update(doc.ref, updates);
|
|
138
|
+
totalUsersRemoved += usersRemovedFromBlock;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (totalUsersRemoved > 0 && Object.keys(blockCountsUpdate).length > 0) {
|
|
143
|
+
const countsRef = db.doc(config.speculatorBlockCountsDocPath); // Use db
|
|
144
|
+
const finalCountUpdates = {};
|
|
145
|
+
for (const key in blockCountsUpdate) {
|
|
146
|
+
finalCountUpdates[`counts.${key}`] = FieldValue.increment(blockCountsUpdate[key]);
|
|
147
|
+
}
|
|
148
|
+
logger.log('TRACE', '[CleanupHelpers] Staging block count decrements.', { updates: finalCountUpdates });
|
|
149
|
+
batch.set(countsRef, finalCountUpdates, { merge: true });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
logger.log('INFO', `[CleanupHelpers] Marked ${totalUsersRemoved} total stale speculators for removal from blocks.`);
|
|
153
|
+
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.log('ERROR', '[CleanupHelpers] Error cleaning stale speculators', { errorMessage: error.message });
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { batch, count: totalUsersRemoved };
|
|
160
|
+
}
|