bulltrackers-module 1.0.269 → 1.0.270
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.
|
@@ -1,20 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Build Reporter & Auto-Runner.
|
|
3
3
|
* Generates a "Pre-Flight" report of what the computation system WILL do.
|
|
4
|
-
*
|
|
5
|
-
* UPDATED:
|
|
4
|
+
* REFACTORED: Strict 5-category reporting with date-based exclusion logic.
|
|
5
|
+
* UPDATED: Added meta stats to compare Included vs Expected computation counts per date.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const { analyzeDateExecution } = require('../WorkflowOrchestrator');
|
|
9
9
|
const { fetchComputationStatus } = require('../persistence/StatusRepository');
|
|
10
10
|
const { normalizeName, getExpectedDateStrings, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils');
|
|
11
11
|
const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
|
|
12
|
-
const { commitBatchInChunks } = require('../persistence/FirestoreUtils');
|
|
12
|
+
const { commitBatchInChunks } = require('../persistence/FirestoreUtils');
|
|
13
13
|
const pLimit = require('p-limit');
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
|
|
16
16
|
const packageVersion = packageJson.version;
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Helper: Determines if a calculation should be excluded from the report
|
|
20
|
+
* because the date is prior to the earliest possible data existence.
|
|
21
|
+
*/
|
|
22
|
+
function isDateBeforeAvailability(dateStr, calcManifest) {
|
|
23
|
+
const targetDate = new Date(dateStr + 'T00:00:00Z');
|
|
24
|
+
const deps = calcManifest.rootDataDependencies || [];
|
|
25
|
+
|
|
26
|
+
// If no data dependencies, it's always valid (e.g., pure math)
|
|
27
|
+
if (deps.length === 0) return false;
|
|
28
|
+
|
|
29
|
+
for (const dep of deps) {
|
|
30
|
+
// Map dependency name to start date
|
|
31
|
+
let startDate = null;
|
|
32
|
+
if (dep === 'portfolio') startDate = DEFINITIVE_EARLIEST_DATES.portfolio;
|
|
33
|
+
else if (dep === 'history') startDate = DEFINITIVE_EARLIEST_DATES.history;
|
|
34
|
+
else if (dep === 'social') startDate = DEFINITIVE_EARLIEST_DATES.social;
|
|
35
|
+
else if (dep === 'insights') startDate = DEFINITIVE_EARLIEST_DATES.insights;
|
|
36
|
+
else if (dep === 'price') startDate = DEFINITIVE_EARLIEST_DATES.price;
|
|
37
|
+
|
|
38
|
+
// If we have a start date and the target is BEFORE it, exclude this calc.
|
|
39
|
+
if (startDate && targetDate < startDate) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
18
46
|
/**
|
|
19
47
|
* AUTO-RUN ENTRY POINT
|
|
20
48
|
*/
|
|
@@ -57,17 +85,17 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
|
|
|
57
85
|
const datesToCheck = getExpectedDateStrings(startDate, today);
|
|
58
86
|
const manifestMap = new Map(manifest.map(c => [normalizeName(c.name), c]));
|
|
59
87
|
|
|
60
|
-
// Main Report Header
|
|
88
|
+
// Main Report Header
|
|
61
89
|
const reportHeader = {
|
|
62
90
|
buildId,
|
|
63
91
|
packageVersion: packageVersion,
|
|
64
92
|
generatedAt: new Date().toISOString(),
|
|
65
|
-
summary: {},
|
|
93
|
+
summary: {},
|
|
66
94
|
_sharded: true
|
|
67
95
|
};
|
|
68
96
|
|
|
69
|
-
let
|
|
70
|
-
let
|
|
97
|
+
let totalRun = 0;
|
|
98
|
+
let totalReRun = 0;
|
|
71
99
|
const detailWrites = [];
|
|
72
100
|
|
|
73
101
|
const limit = pLimit(20);
|
|
@@ -97,31 +125,78 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
|
|
|
97
125
|
|
|
98
126
|
const analysis = analyzeDateExecution(dateStr, manifest, rootDataStatus, dailyStatus, manifestMap, prevDailyStatus);
|
|
99
127
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
// ---------------------------------------------------------
|
|
129
|
+
// STRICT 5-CATEGORY MAPPING
|
|
130
|
+
// ---------------------------------------------------------
|
|
131
|
+
const dateSummary = {
|
|
132
|
+
run: [], // New / No Hash / "Runnable"
|
|
133
|
+
rerun: [], // Hash Mismatch / Category Migration
|
|
134
|
+
blocked: [], // Missing Data (Today) / Dependency Missing
|
|
135
|
+
impossible: [], // Missing Data (Historical) / Impossible Dependency
|
|
136
|
+
uptodate: [], // Hash Match (Previously "Skipped")
|
|
137
|
+
|
|
138
|
+
// [NEW] Metadata for Verification
|
|
139
|
+
meta: {
|
|
140
|
+
totalIncluded: 0,
|
|
141
|
+
totalExpected: 0,
|
|
142
|
+
match: false
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Calculate Expected Count (Computations in manifest that exist for this date)
|
|
147
|
+
const expectedCount = manifest.filter(c => !isDateBeforeAvailability(dateStr, c)).length;
|
|
148
|
+
dateSummary.meta.totalExpected = expectedCount;
|
|
149
|
+
|
|
150
|
+
// Helper to push only if date is valid for this specific calc
|
|
151
|
+
const pushIfValid = (targetArray, item, extraReason = null) => {
|
|
152
|
+
const calcManifest = manifestMap.get(item.name);
|
|
153
|
+
if (calcManifest && isDateBeforeAvailability(dateStr, calcManifest)) {
|
|
154
|
+
return; // EXCLUDED: Date is before data exists
|
|
155
|
+
}
|
|
156
|
+
targetArray.push({ name: item.name, reason: item.reason || extraReason });
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// 1. RUN (New)
|
|
160
|
+
analysis.runnable.forEach(item => pushIfValid(dateSummary.run, item, "New Calculation"));
|
|
161
|
+
|
|
162
|
+
// 2. RE-RUN (Hash Mismatch)
|
|
163
|
+
analysis.reRuns.forEach(item => pushIfValid(dateSummary.rerun, item, "Hash Mismatch"));
|
|
164
|
+
|
|
165
|
+
// 3. BLOCKED (Temporary Issues)
|
|
166
|
+
// Merging 'blocked' and 'failedDependency' as both are temporary blocks
|
|
167
|
+
analysis.blocked.forEach(item => pushIfValid(dateSummary.blocked, item));
|
|
168
|
+
analysis.failedDependency.forEach(item => pushIfValid(dateSummary.blocked, item, "Dependency Missing"));
|
|
169
|
+
|
|
170
|
+
// 4. IMPOSSIBLE (Permanent Issues)
|
|
171
|
+
analysis.impossible.forEach(item => pushIfValid(dateSummary.impossible, item));
|
|
172
|
+
|
|
173
|
+
// 5. UP-TO-DATE (Previously "Skipped")
|
|
174
|
+
analysis.skipped.forEach(item => pushIfValid(dateSummary.uptodate, item, "Up To Date"));
|
|
175
|
+
|
|
176
|
+
// Calculate Included Count
|
|
177
|
+
const includedCount = dateSummary.run.length +
|
|
178
|
+
dateSummary.rerun.length +
|
|
179
|
+
dateSummary.blocked.length +
|
|
180
|
+
dateSummary.impossible.length +
|
|
181
|
+
dateSummary.uptodate.length;
|
|
182
|
+
|
|
183
|
+
dateSummary.meta.totalIncluded = includedCount;
|
|
184
|
+
dateSummary.meta.match = (includedCount === expectedCount);
|
|
185
|
+
|
|
186
|
+
if (!dateSummary.meta.match) {
|
|
187
|
+
logger.log('WARN', `[BuildReporter] ⚠️ Mismatch on ${dateStr}: Expected ${expectedCount} but got ${includedCount}.`);
|
|
123
188
|
}
|
|
124
|
-
|
|
189
|
+
|
|
190
|
+
// ALWAYS WRITE THE REPORT (No filtering based on activity)
|
|
191
|
+
const detailRef = db.collection('computation_build_records').doc(buildId).collection('details').doc(dateStr);
|
|
192
|
+
detailWrites.push({
|
|
193
|
+
ref: detailRef,
|
|
194
|
+
data: dateSummary
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
stats: { run: dateSummary.run.length, rerun: dateSummary.rerun.length }
|
|
199
|
+
};
|
|
125
200
|
|
|
126
201
|
} catch (err) {
|
|
127
202
|
logger.log('ERROR', `[BuildReporter] Error analyzing date ${dateStr}: ${err.message}`);
|
|
@@ -133,12 +208,16 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
|
|
|
133
208
|
|
|
134
209
|
results.forEach(res => {
|
|
135
210
|
if (res) {
|
|
136
|
-
|
|
137
|
-
|
|
211
|
+
totalRun += res.stats.run;
|
|
212
|
+
totalReRun += res.stats.rerun;
|
|
138
213
|
}
|
|
139
214
|
});
|
|
140
215
|
|
|
141
|
-
reportHeader.summary = {
|
|
216
|
+
reportHeader.summary = {
|
|
217
|
+
totalReRuns: totalReRun,
|
|
218
|
+
totalNew: totalRun,
|
|
219
|
+
scanRange: `${datesToCheck[0]} to ${datesToCheck[datesToCheck.length-1]}`
|
|
220
|
+
};
|
|
142
221
|
|
|
143
222
|
const reportRef = db.collection('computation_build_records').doc(buildId);
|
|
144
223
|
await reportRef.set(reportHeader);
|
|
@@ -150,7 +229,7 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
|
|
|
150
229
|
|
|
151
230
|
await db.collection('computation_build_records').doc('latest').set({ ...reportHeader, note: "Latest build report pointer (See subcollection for details)." });
|
|
152
231
|
|
|
153
|
-
logger.log('SUCCESS', `[BuildReporter] Report ${buildId} saved. Re-runs: ${
|
|
232
|
+
logger.log('SUCCESS', `[BuildReporter] Report ${buildId} saved. Re-runs: ${totalReRun}, New: ${totalRun}.`);
|
|
154
233
|
|
|
155
234
|
return {
|
|
156
235
|
success: true,
|