@memberjunction/ng-dashboards 5.46.0 → 5.48.0
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/DataExplorer/data-explorer-dashboard.component.d.ts +15 -4
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +45 -35
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.d.ts +7 -0
- package/dist/DataExplorer/models/explorer-state.interface.d.ts.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.js.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.d.ts +4 -0
- package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +39 -30
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +14 -12
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +6 -1
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/Lists/services/list-set-operations.service.d.ts +11 -0
- package/dist/Lists/services/list-set-operations.service.d.ts.map +1 -1
- package/dist/Lists/services/list-set-operations.service.js +27 -2
- package/dist/Lists/services/list-set-operations.service.js.map +1 -1
- package/dist/PredictiveStudio/at-risk.view-models.d.ts +33 -0
- package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +1 -1
- package/dist/PredictiveStudio/at-risk.view-models.js +69 -2
- package/dist/PredictiveStudio/at-risk.view-models.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-catalog.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-catalog.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-compare.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-compare.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-experiments.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-home.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-home.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-operate-dialog.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-operate-dialog.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.js +306 -297
- package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-production.component.js +2 -2
- package/dist/PredictiveStudio/components/ps-production.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-registry.component.d.ts +1 -1
- package/dist/PredictiveStudio/components/ps-registry.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-registry.component.js +15 -9
- package/dist/PredictiveStudio/components/ps-registry.component.js.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts +115 -0
- package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts.map +1 -0
- package/dist/PredictiveStudio/predictive-studio-agent-context.js +119 -0
- package/dist/PredictiveStudio/predictive-studio-agent-context.js.map +1 -0
- package/dist/PredictiveStudio/predictive-studio-copilot.view-models.d.ts +36 -0
- package/dist/PredictiveStudio/predictive-studio-copilot.view-models.d.ts.map +1 -0
- package/dist/PredictiveStudio/predictive-studio-copilot.view-models.js +52 -0
- package/dist/PredictiveStudio/predictive-studio-copilot.view-models.js.map +1 -0
- package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts +9 -1
- package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-models-resource.component.js +133 -56
- package/dist/PredictiveStudio/resources/ps-models-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +50 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +591 -165
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-resource-base.d.ts +17 -0
- package/dist/PredictiveStudio/resources/ps-resource-base.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-resource-base.js +27 -1
- package/dist/PredictiveStudio/resources/ps-resource-base.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts +11 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +199 -99
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
- package/package.json +59 -59
|
@@ -104,6 +104,17 @@ export interface SetOperationResult {
|
|
|
104
104
|
* Service for performing set operations on lists and preparing data for Venn visualization.
|
|
105
105
|
*/
|
|
106
106
|
export declare class ListSetOperationsService {
|
|
107
|
+
/**
|
|
108
|
+
* Defensive cap on membership rows loaded per operand query (P8). Set
|
|
109
|
+
* operations are in-memory over full member sets; beyond this size the
|
|
110
|
+
* result would be wrong-by-truncation, so we cap the load and warn —
|
|
111
|
+
* callers can check {@link WasLastLoadTruncated} to surface it.
|
|
112
|
+
*/
|
|
113
|
+
static readonly MAX_OPERAND_RECORDS = 50000;
|
|
114
|
+
/** True when the most recent membership load hit MAX_OPERAND_RECORDS — results may be incomplete. */
|
|
115
|
+
WasLastLoadTruncated: boolean;
|
|
116
|
+
/** Applies the operand cap: flags/warns when a membership query filled the cap. */
|
|
117
|
+
private trackTruncation;
|
|
107
118
|
private _provider;
|
|
108
119
|
/** Set the metadata provider this service should use. Components should call this after injection. */
|
|
109
120
|
set Provider(value: IMetadataProvider | null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-set-operations.service.d.ts","sourceRoot":"","sources":["../../../src/Lists/services/list-set-operations.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,iBAAiB,EAA4B,MAAM,sBAAsB,CAAC;AACtG,OAAO,EAAE,YAAY,EAAwC,MAAM,+BAA+B,CAAC;AAEnG,OAAO,EAAmB,UAAU,EAAE,MAAM,MAAM,CAAC;;AAmBnD;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,IAAI,EAAE,WAAW,CAAC;IAClB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB;;OAEG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,cAAc,GAAG,YAAY,GAAG,sBAAsB,GAAG,YAAY,CAAC;AAE3G;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,YAAY,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAcD;;GAEG;AACH,qBAGa,wBAAwB;IACnC,OAAO,CAAC,SAAS,CAAkC;IACnD,sGAAsG;IACtG,IAAW,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAElD;IACD,IAAW,QAAQ,IAAI,iBAAiB,CAEvC;IACD,OAAO,CAAC,cAAc,CAAuC;IACtD,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,CAAsC;IAG1E,OAAO,CAAC,gBAAgB,CAAkC;;IAI1D;;OAEG;IACG,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"list-set-operations.service.d.ts","sourceRoot":"","sources":["../../../src/Lists/services/list-set-operations.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -44,6 +44,22 @@ const VENN_COLORS = [
|
|
|
44
44
|
* Service for performing set operations on lists and preparing data for Venn visualization.
|
|
45
45
|
*/
|
|
46
46
|
export class ListSetOperationsService {
|
|
47
|
+
/**
|
|
48
|
+
* Defensive cap on membership rows loaded per operand query (P8). Set
|
|
49
|
+
* operations are in-memory over full member sets; beyond this size the
|
|
50
|
+
* result would be wrong-by-truncation, so we cap the load and warn —
|
|
51
|
+
* callers can check {@link WasLastLoadTruncated} to surface it.
|
|
52
|
+
*/
|
|
53
|
+
static MAX_OPERAND_RECORDS = 50000;
|
|
54
|
+
/** True when the most recent membership load hit MAX_OPERAND_RECORDS — results may be incomplete. */
|
|
55
|
+
WasLastLoadTruncated = false;
|
|
56
|
+
/** Applies the operand cap: flags/warns when a membership query filled the cap. */
|
|
57
|
+
trackTruncation(rowsReturned, context) {
|
|
58
|
+
if (rowsReturned >= ListSetOperationsService.MAX_OPERAND_RECORDS) {
|
|
59
|
+
this.WasLastLoadTruncated = true;
|
|
60
|
+
console.warn(`[ListSetOperations] ${context} hit the ${ListSetOperationsService.MAX_OPERAND_RECORDS}-row operand cap — set-operation results may be incomplete.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
47
63
|
_provider = null;
|
|
48
64
|
/** Set the metadata provider this service should use. Components should call this after injection. */
|
|
49
65
|
set Provider(value) {
|
|
@@ -70,12 +86,15 @@ export class ListSetOperationsService {
|
|
|
70
86
|
const listIds = lists.map(l => l.ID);
|
|
71
87
|
const listIdFilter = listIds.map(id => `'${id}'`).join(',');
|
|
72
88
|
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
89
|
+
this.WasLastLoadTruncated = false;
|
|
73
90
|
const result = await rv.RunView({
|
|
74
91
|
EntityName: 'MJ: List Details',
|
|
75
92
|
ExtraFilter: `ListID IN (${listIdFilter})`,
|
|
76
93
|
Fields: ['ListID', 'RecordID'],
|
|
77
|
-
ResultType: 'simple'
|
|
94
|
+
ResultType: 'simple',
|
|
95
|
+
MaxRows: ListSetOperationsService.MAX_OPERAND_RECORDS
|
|
78
96
|
});
|
|
97
|
+
this.trackTruncation(result.Results?.length ?? 0, 'calculateVennData');
|
|
79
98
|
// Build sets per list
|
|
80
99
|
const setsMap = new Map();
|
|
81
100
|
for (const listId of listIds) {
|
|
@@ -334,7 +353,9 @@ export class ListSetOperationsService {
|
|
|
334
353
|
ExtraFilter: `ListID IN (${listIdFilter})`,
|
|
335
354
|
Fields: ['ListID', 'RecordID'],
|
|
336
355
|
ResultType: 'simple',
|
|
356
|
+
MaxRows: ListSetOperationsService.MAX_OPERAND_RECORDS,
|
|
337
357
|
});
|
|
358
|
+
this.trackTruncation(result.Results?.length ?? 0, 'loadMissingLists');
|
|
338
359
|
for (const id of listIds) {
|
|
339
360
|
this.listDetailsCache.set(OperandCacheKey('list', id), new Set());
|
|
340
361
|
}
|
|
@@ -375,7 +396,9 @@ export class ListSetOperationsService {
|
|
|
375
396
|
ViewID: operand.id,
|
|
376
397
|
Fields: pkFields,
|
|
377
398
|
ResultType: 'simple',
|
|
399
|
+
MaxRows: ListSetOperationsService.MAX_OPERAND_RECORDS,
|
|
378
400
|
});
|
|
401
|
+
this.trackTruncation(result.Results?.length ?? 0, `loadSingleView(${operand.id})`);
|
|
379
402
|
const set = new Set();
|
|
380
403
|
if (result.Success && result.Results) {
|
|
381
404
|
for (const row of result.Results) {
|
|
@@ -445,8 +468,10 @@ export class ListSetOperationsService {
|
|
|
445
468
|
EntityName: 'MJ: List Details',
|
|
446
469
|
ExtraFilter: `ListID IN (${listIdFilter})`,
|
|
447
470
|
Fields: ['ListID', 'RecordID'],
|
|
448
|
-
ResultType: 'simple'
|
|
471
|
+
ResultType: 'simple',
|
|
472
|
+
MaxRows: ListSetOperationsService.MAX_OPERAND_RECORDS
|
|
449
473
|
});
|
|
474
|
+
this.trackTruncation(result.Results?.length ?? 0, 'ensureListsLoaded');
|
|
450
475
|
// Initialize sets for missing lists
|
|
451
476
|
for (const id of missingIds) {
|
|
452
477
|
this.listDetailsCache.set(OperandCacheKey('list', id), new Set());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-set-operations.service.js","sourceRoot":"","sources":["../../../src/Lists/services/list-set-operations.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { RunView, Metadata, IMetadataProvider, CompositeKey, EntityInfo } from '@memberjunction/core';\nimport { MJListEntity, MJListDetailEntity, MJUserViewEntity } from '@memberjunction/core-entities';\nimport { NormalizeUUID } from '@memberjunction/global';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\n/**\n * Normalize an `MJ: List Details.RecordID`-style string so set operations\n * compare correctly regardless of where the value originated.\n *\n * The wrinkle: SQL Server returns UUIDs uppercased, PostgreSQL returns\n * them lowercased — and the same DB can host List Details written by\n * different code paths with different casing. Composite-PK keys are\n * concatenated strings (`Field1|Value1||Field2|Value2`); normalizing\n * the whole string is safe — `NormalizeUUID` lowercases anything that\n * pattern-matches a UUID and is a no-op on other content. The pipe\n * separators in composite keys are unaffected.\n */\nfunction normalizeRecordId(raw: string | null | undefined): string {\n if (raw == null) return '';\n return NormalizeUUID(String(raw));\n}\n\n/**\n * Describes one operand for the Venn / set-op pipeline. The component\n * passes these in instead of raw `MJListEntity`s now that views are\n * supported; the service handles cache-keying + record-ID resolution.\n */\nexport interface SetOperand {\n kind: OperandKind;\n /** ID of the underlying MJ List or MJ User View. */\n id: string;\n /** Display name shown in the Venn legend + selection chips. */\n name: string;\n /** Entity ID — used to enforce the same-entity invariant on operations. */\n entityID: string;\n /** Entity name — denormalized for display. */\n entityName: string;\n /** Hex color from the predefined palette; chosen by the component. */\n color: string;\n}\n\n/**\n * Discriminates a Venn set / set-op operand between a saved MJ List and a\n * User View. The Venn diagram uses this to render views with a dashed\n * stroke (per mockup 10) so users can tell at a glance which inputs are\n * static (lists) vs. dynamic-at-resolution (views).\n */\nexport type OperandKind = 'list' | 'view';\n\n/**\n * Stable key for an operand across cache lookups + state persistence.\n * `list:<id>` and `view:<id>` deliberately namespace the ID so a view and\n * a list with the same UUID (extremely unlikely but possible) can't\n * collide in the cache.\n */\nexport function OperandCacheKey(kind: OperandKind, id: string): string {\n return `${kind}:${id}`;\n}\n\n/**\n * Represents a set (list or view) in the Venn diagram\n */\nexport interface VennSet {\n /** Stable cache key. Use `OperandCacheKey()` to construct. */\n operandKey: string;\n /** Discriminator — drives the dashed-vs-solid stroke in the Venn diagram. */\n kind: OperandKind;\n /** Underlying list / view ID. Same UUID space as the entity it points at. */\n listId: string;\n listName: string;\n color: string;\n recordIds: Set<string>;\n size: number;\n}\n\n/**\n * Represents an intersection region in the Venn diagram\n */\nexport interface VennIntersection {\n /**\n * IDs of lists that participate in this intersection\n */\n setIds: string[];\n\n /**\n * Labels of lists that participate in this intersection\n */\n setLabels: string[];\n\n /**\n * Number of records in this intersection\n */\n size: number;\n\n /**\n * Actual record IDs in this intersection\n */\n recordIds: string[];\n\n /**\n * Display label for this region\n */\n label: string;\n\n /**\n * Whether this region is currently selected\n */\n isSelected: boolean;\n}\n\n/**\n * Complete Venn data for visualization\n */\nexport interface VennData {\n sets: VennSet[];\n intersections: VennIntersection[];\n entityId?: string;\n entityName?: string;\n}\n\n/**\n * Set operation types\n */\nexport type SetOperation = 'union' | 'intersection' | 'difference' | 'symmetric_difference' | 'complement';\n\n/**\n * Result of a set operation\n */\nexport interface SetOperationResult {\n operation: SetOperation;\n inputSetIds: string[];\n resultRecordIds: string[];\n resultCount: number;\n}\n\n// Predefined color palette for Venn sets\nconst VENN_COLORS = [\n '#2196F3', // Blue\n '#4CAF50', // Green\n '#FF9800', // Orange\n '#E91E63', // Pink\n '#9C27B0', // Purple\n '#00BCD4', // Cyan\n '#F44336', // Red\n '#8BC34A' // Light Green\n];\n\n/**\n * Service for performing set operations on lists and preparing data for Venn visualization.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class ListSetOperationsService {\n private _provider: IMetadataProvider | null = null;\n /** Set the metadata provider this service should use. Components should call this after injection. */\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n private loadingSubject = new BehaviorSubject<boolean>(false);\n public loading$: Observable<boolean> = this.loadingSubject.asObservable();\n\n // Cache for list details\n private listDetailsCache = new Map<string, Set<string>>();\n\n constructor() {}\n\n /**\n * Load list data and calculate all intersections for Venn visualization\n */\n async calculateVennData(lists: MJListEntity[]): Promise<VennData> {\n if (lists.length === 0) {\n return { sets: [], intersections: [] };\n }\n\n this.loadingSubject.next(true);\n\n try {\n // Load all list details in a single query\n const listIds = lists.map(l => l.ID);\n const listIdFilter = listIds.map(id => `'${id}'`).join(',');\n\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple'\n });\n\n // Build sets per list\n const setsMap = new Map<string, Set<string>>();\n for (const listId of listIds) {\n setsMap.set(listId, new Set());\n }\n\n if (result.Success && result.Results) {\n for (const detail of result.Results) {\n const set = setsMap.get(detail.ListID);\n if (set) {\n set.add(detail.RecordID);\n }\n }\n }\n\n // Update cache — namespaced under `list:` so view operands can share\n // the same cache map without collision.\n for (const [listId, records] of setsMap) {\n this.listDetailsCache.set(OperandCacheKey('list', listId), records);\n }\n\n // Build VennSet objects. We keep the legacy `MJListEntity` overload's\n // shape but populate the new discriminator fields so the diagram +\n // downstream operations stay consistent regardless of which entry\n // point was used.\n const sets: VennSet[] = lists.map((list, index) => ({\n operandKey: OperandCacheKey('list', list.ID),\n kind: 'list' as const,\n listId: list.ID,\n listName: list.Name,\n color: VENN_COLORS[index % VENN_COLORS.length],\n recordIds: setsMap.get(list.ID) || new Set(),\n size: setsMap.get(list.ID)?.size || 0\n }));\n\n // Calculate all possible intersections\n const intersections = this.calculateAllIntersections(sets);\n\n // Get entity info from the first list (assuming all lists are same entity)\n const entityId = lists[0]?.EntityID;\n const entityName = lists[0]?.Entity;\n\n return { sets, intersections, entityId, entityName };\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /**\n * Generalized Venn-data loader supporting both lists and views.\n *\n * - Lists resolve via the same `MJ: List Details` query as the legacy\n * path (cached by `list:<id>`).\n * - Views resolve via `RunView({ViewID})` and primary-key serialization;\n * cached by `view:<id>` so picking the same view twice doesn't\n * re-query.\n *\n * The cache deliberately namespaces the kind — a list and a view that\n * shared the same UUID would still hit distinct entries.\n */\n async calculateVennDataForOperands(operands: SetOperand[]): Promise<VennData> {\n if (operands.length === 0) return { sets: [], intersections: [] };\n this.loadingSubject.next(true);\n try {\n await this.ensureOperandsLoaded(operands);\n\n const sets: VennSet[] = operands.map((op) => ({\n operandKey: OperandCacheKey(op.kind, op.id),\n kind: op.kind,\n listId: op.id,\n listName: op.name,\n color: op.color,\n recordIds: this.cacheGet(op) ?? new Set<string>(),\n size: this.cacheGet(op)?.size ?? 0,\n }));\n\n const intersections = this.calculateAllIntersections(sets);\n const entityId = operands[0]?.entityID;\n const entityName = operands[0]?.entityName;\n return { sets, intersections, entityId, entityName };\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /**\n * Operand-aware sibling of `performOperation`. `operandKeys` are the\n * `OperandCacheKey()` strings produced by `calculateVennDataForOperands`,\n * so callers can stay in operand-key space end-to-end.\n */\n async performOperationForOperands(\n operation: SetOperation,\n operands: SetOperand[],\n allOperands?: SetOperand[],\n ): Promise<SetOperationResult> {\n this.loadingSubject.next(true);\n try {\n await this.ensureOperandsLoaded([...operands, ...(allOperands ?? [])]);\n const sets = operands.map((op) => this.cacheGet(op) ?? new Set<string>());\n const resultSet = this.computeOperationResult(operation, sets, allOperands);\n return {\n operation,\n inputSetIds: operands.map((op) => OperandCacheKey(op.kind, op.id)),\n resultRecordIds: Array.from(resultSet),\n resultCount: resultSet.size,\n };\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /**\n * Calculate all possible intersections between the given sets\n */\n private calculateAllIntersections(sets: VennSet[]): VennIntersection[] {\n const intersections: VennIntersection[] = [];\n const n = sets.length;\n\n // Generate all non-empty subsets of sets\n for (let mask = 1; mask < (1 << n); mask++) {\n const subset: VennSet[] = [];\n for (let i = 0; i < n; i++) {\n if (mask & (1 << i)) {\n subset.push(sets[i]);\n }\n }\n\n // Calculate exclusive intersection (records in exactly these sets)\n const recordIds = this.calculateExclusiveIntersection(subset, sets);\n\n if (recordIds.length > 0 || subset.length === 1) {\n const setIds = subset.map(s => s.listId);\n const setLabels = subset.map(s => s.listName);\n\n intersections.push({\n setIds,\n setLabels,\n size: recordIds.length,\n recordIds,\n label: this.buildIntersectionLabel(setLabels),\n isSelected: false\n });\n }\n }\n\n // Sort by number of sets (single sets first, then pairs, etc.)\n intersections.sort((a, b) => a.setIds.length - b.setIds.length);\n\n return intersections;\n }\n\n /**\n * Calculate records that are in ALL of the specified sets but NONE of the other sets\n */\n private calculateExclusiveIntersection(includedSets: VennSet[], allSets: VennSet[]): string[] {\n if (includedSets.length === 0) return [];\n\n // Start with records in the first included set\n let result = new Set(includedSets[0].recordIds);\n\n // Intersect with all other included sets\n for (let i = 1; i < includedSets.length; i++) {\n result = this.intersect(result, includedSets[i].recordIds);\n }\n\n // Remove records that are in excluded sets\n const excludedSets = allSets.filter(s => !includedSets.includes(s));\n for (const excluded of excludedSets) {\n result = this.difference(result, excluded.recordIds);\n }\n\n return Array.from(result);\n }\n\n /**\n * Build a display label for an intersection\n */\n private buildIntersectionLabel(setLabels: string[]): string {\n if (setLabels.length === 1) {\n return `Only in ${setLabels[0]}`;\n }\n return setLabels.join(' ∩ ');\n }\n\n /**\n * Perform a set operation on multiple lists\n */\n async performOperation(\n operation: SetOperation,\n listIds: string[],\n allListIds?: string[]\n ): Promise<SetOperationResult> {\n this.loadingSubject.next(true);\n\n try {\n // Ensure we have the list details loaded\n await this.ensureListsLoaded(listIds);\n\n const sets = listIds.map(id => this.listDetailsCache.get(OperandCacheKey('list', id)) || new Set<string>());\n let resultSet: Set<string>;\n\n switch (operation) {\n case 'union':\n resultSet = this.unionAll(sets);\n break;\n\n case 'intersection':\n resultSet = this.intersectAll(sets);\n break;\n\n case 'difference':\n // A - B - C - ... (first set minus all others)\n if (sets.length < 2) {\n resultSet = sets[0] || new Set();\n } else {\n resultSet = new Set(sets[0]);\n for (let i = 1; i < sets.length; i++) {\n resultSet = this.difference(resultSet, sets[i]);\n }\n }\n break;\n\n case 'symmetric_difference':\n // Records that are in exactly one of the sets\n resultSet = this.symmetricDifferenceAll(sets);\n break;\n\n case 'complement':\n // Records NOT in any of the selected sets (requires allListIds)\n if (allListIds) {\n await this.ensureListsLoaded(allListIds);\n const allSets = allListIds.map(id => this.listDetailsCache.get(OperandCacheKey('list', id)) || new Set<string>());\n const allRecords = this.unionAll(allSets);\n const selectedRecords = this.unionAll(sets);\n resultSet = this.difference(allRecords, selectedRecords);\n } else {\n resultSet = new Set();\n }\n break;\n\n default:\n resultSet = new Set();\n }\n\n return {\n operation,\n inputSetIds: listIds,\n resultRecordIds: Array.from(resultSet),\n resultCount: resultSet.size\n };\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /**\n * Cached lookup keyed by operand kind+id. Lists and views share the\n * cache map but their entries can't collide thanks to the key namespace.\n */\n private cacheGet(operand: SetOperand): Set<string> | undefined {\n return this.listDetailsCache.get(OperandCacheKey(operand.kind, operand.id));\n }\n\n /**\n * Bulk-load any operands not already cached. Lists batch through a\n * single `MJ: List Details` query (same shape as before); views resolve\n * one at a time because each is a separate parameterized RunView. Both\n * paths populate the same cache map.\n */\n private async ensureOperandsLoaded(operands: SetOperand[]): Promise<void> {\n const missingLists: string[] = [];\n const missingViews: SetOperand[] = [];\n for (const op of operands) {\n if (this.cacheGet(op)) continue;\n if (op.kind === 'list') missingLists.push(op.id);\n else missingViews.push(op);\n }\n await Promise.all([\n missingLists.length > 0 ? this.loadMissingLists(missingLists) : Promise.resolve(),\n missingViews.length > 0 ? this.loadMissingViews(missingViews) : Promise.resolve(),\n ]);\n }\n\n private async loadMissingLists(listIds: string[]): Promise<void> {\n const listIdFilter = listIds.map((id) => `'${id}'`).join(',');\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple',\n });\n\n for (const id of listIds) {\n this.listDetailsCache.set(OperandCacheKey('list', id), new Set<string>());\n }\n if (result.Success && result.Results) {\n for (const detail of result.Results) {\n // Normalize on the ListID we look up by AND on the RecordID we\n // store — mixed casing on either side would otherwise produce\n // empty intersections between lists that actually share records.\n const set = this.listDetailsCache.get(OperandCacheKey('list', NormalizeUUID(detail.ListID)))\n ?? this.listDetailsCache.get(OperandCacheKey('list', detail.ListID));\n if (set) set.add(normalizeRecordId(detail.RecordID));\n }\n }\n }\n\n private async loadMissingViews(operands: SetOperand[]): Promise<void> {\n // Fire view queries in parallel — each one is small and independent.\n const tasks = operands.map((op) => this.loadSingleView(op));\n await Promise.all(tasks);\n }\n\n /**\n * Resolve a view to a set of record IDs in the canonical `MJ: List\n * Details.RecordID` format (single-PK → raw value; composite-PK →\n * `Field1|Value1||Field2|Value2`, matching `CompositeKey.ToConcatenatedString`).\n * This keeps view operands cache-compatible with list operands without\n * any transformation in the set-op logic.\n */\n private async loadSingleView(operand: SetOperand): Promise<void> {\n const md = this.Provider;\n const entityInfo = md.EntityByName(operand.entityName);\n if (!entityInfo) {\n this.listDetailsCache.set(OperandCacheKey('view', operand.id), new Set<string>());\n return;\n }\n const pkFields = entityInfo.PrimaryKeys.map((pk) => pk.Name);\n\n const rv = RunView.FromMetadataProvider(md);\n const result = await rv.RunView({\n ViewID: operand.id,\n Fields: pkFields,\n ResultType: 'simple',\n });\n\n const set = new Set<string>();\n if (result.Success && result.Results) {\n for (const row of result.Results) {\n set.add(this.serializeRecordId(entityInfo, row as Record<string, unknown>));\n }\n }\n this.listDetailsCache.set(OperandCacheKey('view', operand.id), set);\n }\n\n /**\n * Mirror of `ListOperations.serializeRecordId` so view operands produce\n * IDs that round-trip cleanly against MJ List Details.\n */\n private serializeRecordId(entityInfo: EntityInfo, row: Record<string, unknown>): string {\n if (entityInfo.PrimaryKeys.length === 1) {\n return normalizeRecordId(String(row[entityInfo.PrimaryKeys[0].Name]));\n }\n const ck = new CompositeKey();\n ck.KeyValuePairs = entityInfo.PrimaryKeys.map((pk) => ({\n FieldName: pk.Name,\n Value: row[pk.Name] as string | number | Date | null | undefined,\n }));\n return normalizeRecordId(ck.ToConcatenatedString());\n }\n\n /**\n * Pure compute step shared by the legacy and operand-aware paths.\n * Operates on raw `Set<string>` inputs so it stays agnostic to where\n * those sets came from.\n */\n private computeOperationResult(\n operation: SetOperation,\n sets: Set<string>[],\n allOperandsForComplement?: SetOperand[],\n ): Set<string> {\n switch (operation) {\n case 'union':\n return this.unionAll(sets);\n case 'intersection':\n return this.intersectAll(sets);\n case 'difference': {\n if (sets.length < 2) return sets[0] ?? new Set<string>();\n let r = new Set(sets[0]);\n for (let i = 1; i < sets.length; i++) r = this.difference(r, sets[i]);\n return r;\n }\n case 'symmetric_difference':\n return this.symmetricDifferenceAll(sets);\n case 'complement': {\n if (!allOperandsForComplement) return new Set<string>();\n const allSets = allOperandsForComplement.map((op) => this.cacheGet(op) ?? new Set<string>());\n return this.difference(this.unionAll(allSets), this.unionAll(sets));\n }\n default:\n return new Set<string>();\n }\n }\n\n /**\n * Ensure list details are loaded for the given list IDs. Cache keys use\n * the `list:<id>` namespace introduced for operand support; the legacy\n * list-only callers go through here without needing to know the format.\n */\n private async ensureListsLoaded(listIds: string[]): Promise<void> {\n const missingIds = listIds.filter(id => !this.listDetailsCache.has(OperandCacheKey('list', id)));\n\n if (missingIds.length === 0) return;\n\n const listIdFilter = missingIds.map(id => `'${id}'`).join(',');\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n const result = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple'\n });\n\n // Initialize sets for missing lists\n for (const id of missingIds) {\n this.listDetailsCache.set(OperandCacheKey('list', id), new Set());\n }\n\n if (result.Success && result.Results) {\n for (const detail of result.Results) {\n const set = this.listDetailsCache.get(OperandCacheKey('list', detail.ListID));\n if (set) {\n set.add(detail.RecordID);\n }\n }\n }\n }\n\n /**\n * Set intersection of two sets\n */\n private intersect(a: Set<string>, b: Set<string>): Set<string> {\n const result = new Set<string>();\n for (const item of a) {\n if (b.has(item)) {\n result.add(item);\n }\n }\n return result;\n }\n\n /**\n * Set difference (a - b)\n */\n private difference(a: Set<string>, b: Set<string>): Set<string> {\n const result = new Set<string>();\n for (const item of a) {\n if (!b.has(item)) {\n result.add(item);\n }\n }\n return result;\n }\n\n /**\n * Union of multiple sets\n */\n private unionAll(sets: Set<string>[]): Set<string> {\n const result = new Set<string>();\n for (const set of sets) {\n for (const item of set) {\n result.add(item);\n }\n }\n return result;\n }\n\n /**\n * Intersection of multiple sets\n */\n private intersectAll(sets: Set<string>[]): Set<string> {\n if (sets.length === 0) return new Set();\n if (sets.length === 1) return new Set(sets[0]);\n\n let result = new Set(sets[0]);\n for (let i = 1; i < sets.length; i++) {\n result = this.intersect(result, sets[i]);\n }\n return result;\n }\n\n /**\n * Symmetric difference of multiple sets (items in exactly one set)\n */\n private symmetricDifferenceAll(sets: Set<string>[]): Set<string> {\n if (sets.length === 0) return new Set();\n\n // Count occurrences of each item\n const counts = new Map<string, number>();\n for (const set of sets) {\n for (const item of set) {\n counts.set(item, (counts.get(item) || 0) + 1);\n }\n }\n\n // Keep only items that appear exactly once\n const result = new Set<string>();\n for (const [item, count] of counts) {\n if (count === 1) {\n result.add(item);\n }\n }\n return result;\n }\n\n /**\n * Get the color for a list by its index\n */\n getColorForIndex(index: number): string {\n return VENN_COLORS[index % VENN_COLORS.length];\n }\n\n /**\n * Clear the cache\n */\n clearCache(): void {\n this.listDetailsCache.clear();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"list-set-operations.service.js","sourceRoot":"","sources":["../../../src/Lists/services/list-set-operations.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { RunView, Metadata, IMetadataProvider, CompositeKey, EntityInfo } from '@memberjunction/core';\nimport { MJListEntity, MJListDetailEntity, MJUserViewEntity } from '@memberjunction/core-entities';\nimport { NormalizeUUID } from '@memberjunction/global';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\n/**\n * Normalize an `MJ: List Details.RecordID`-style string so set operations\n * compare correctly regardless of where the value originated.\n *\n * The wrinkle: SQL Server returns UUIDs uppercased, PostgreSQL returns\n * them lowercased — and the same DB can host List Details written by\n * different code paths with different casing. Composite-PK keys are\n * concatenated strings (`Field1|Value1||Field2|Value2`); normalizing\n * the whole string is safe — `NormalizeUUID` lowercases anything that\n * pattern-matches a UUID and is a no-op on other content. The pipe\n * separators in composite keys are unaffected.\n */\nfunction normalizeRecordId(raw: string | null | undefined): string {\n if (raw == null) return '';\n return NormalizeUUID(String(raw));\n}\n\n/**\n * Describes one operand for the Venn / set-op pipeline. The component\n * passes these in instead of raw `MJListEntity`s now that views are\n * supported; the service handles cache-keying + record-ID resolution.\n */\nexport interface SetOperand {\n kind: OperandKind;\n /** ID of the underlying MJ List or MJ User View. */\n id: string;\n /** Display name shown in the Venn legend + selection chips. */\n name: string;\n /** Entity ID — used to enforce the same-entity invariant on operations. */\n entityID: string;\n /** Entity name — denormalized for display. */\n entityName: string;\n /** Hex color from the predefined palette; chosen by the component. */\n color: string;\n}\n\n/**\n * Discriminates a Venn set / set-op operand between a saved MJ List and a\n * User View. The Venn diagram uses this to render views with a dashed\n * stroke (per mockup 10) so users can tell at a glance which inputs are\n * static (lists) vs. dynamic-at-resolution (views).\n */\nexport type OperandKind = 'list' | 'view';\n\n/**\n * Stable key for an operand across cache lookups + state persistence.\n * `list:<id>` and `view:<id>` deliberately namespace the ID so a view and\n * a list with the same UUID (extremely unlikely but possible) can't\n * collide in the cache.\n */\nexport function OperandCacheKey(kind: OperandKind, id: string): string {\n return `${kind}:${id}`;\n}\n\n/**\n * Represents a set (list or view) in the Venn diagram\n */\nexport interface VennSet {\n /** Stable cache key. Use `OperandCacheKey()` to construct. */\n operandKey: string;\n /** Discriminator — drives the dashed-vs-solid stroke in the Venn diagram. */\n kind: OperandKind;\n /** Underlying list / view ID. Same UUID space as the entity it points at. */\n listId: string;\n listName: string;\n color: string;\n recordIds: Set<string>;\n size: number;\n}\n\n/**\n * Represents an intersection region in the Venn diagram\n */\nexport interface VennIntersection {\n /**\n * IDs of lists that participate in this intersection\n */\n setIds: string[];\n\n /**\n * Labels of lists that participate in this intersection\n */\n setLabels: string[];\n\n /**\n * Number of records in this intersection\n */\n size: number;\n\n /**\n * Actual record IDs in this intersection\n */\n recordIds: string[];\n\n /**\n * Display label for this region\n */\n label: string;\n\n /**\n * Whether this region is currently selected\n */\n isSelected: boolean;\n}\n\n/**\n * Complete Venn data for visualization\n */\nexport interface VennData {\n sets: VennSet[];\n intersections: VennIntersection[];\n entityId?: string;\n entityName?: string;\n}\n\n/**\n * Set operation types\n */\nexport type SetOperation = 'union' | 'intersection' | 'difference' | 'symmetric_difference' | 'complement';\n\n/**\n * Result of a set operation\n */\nexport interface SetOperationResult {\n operation: SetOperation;\n inputSetIds: string[];\n resultRecordIds: string[];\n resultCount: number;\n}\n\n// Predefined color palette for Venn sets\nconst VENN_COLORS = [\n '#2196F3', // Blue\n '#4CAF50', // Green\n '#FF9800', // Orange\n '#E91E63', // Pink\n '#9C27B0', // Purple\n '#00BCD4', // Cyan\n '#F44336', // Red\n '#8BC34A' // Light Green\n];\n\n/**\n * Service for performing set operations on lists and preparing data for Venn visualization.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class ListSetOperationsService {\n /**\n * Defensive cap on membership rows loaded per operand query (P8). Set\n * operations are in-memory over full member sets; beyond this size the\n * result would be wrong-by-truncation, so we cap the load and warn —\n * callers can check {@link WasLastLoadTruncated} to surface it.\n */\n public static readonly MAX_OPERAND_RECORDS = 50000;\n\n /** True when the most recent membership load hit MAX_OPERAND_RECORDS — results may be incomplete. */\n public WasLastLoadTruncated = false;\n\n /** Applies the operand cap: flags/warns when a membership query filled the cap. */\n private trackTruncation(rowsReturned: number, context: string): void {\n if (rowsReturned >= ListSetOperationsService.MAX_OPERAND_RECORDS) {\n this.WasLastLoadTruncated = true;\n console.warn(`[ListSetOperations] ${context} hit the ${ListSetOperationsService.MAX_OPERAND_RECORDS}-row operand cap — set-operation results may be incomplete.`);\n }\n }\n\n private _provider: IMetadataProvider | null = null;\n /** Set the metadata provider this service should use. Components should call this after injection. */\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n private loadingSubject = new BehaviorSubject<boolean>(false);\n public loading$: Observable<boolean> = this.loadingSubject.asObservable();\n\n // Cache for list details\n private listDetailsCache = new Map<string, Set<string>>();\n\n constructor() {}\n\n /**\n * Load list data and calculate all intersections for Venn visualization\n */\n async calculateVennData(lists: MJListEntity[]): Promise<VennData> {\n if (lists.length === 0) {\n return { sets: [], intersections: [] };\n }\n\n this.loadingSubject.next(true);\n\n try {\n // Load all list details in a single query\n const listIds = lists.map(l => l.ID);\n const listIdFilter = listIds.map(id => `'${id}'`).join(',');\n\n const rv = RunView.FromMetadataProvider(this.Provider);\n this.WasLastLoadTruncated = false;\n const result = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple',\n MaxRows: ListSetOperationsService.MAX_OPERAND_RECORDS\n });\n this.trackTruncation(result.Results?.length ?? 0, 'calculateVennData');\n\n // Build sets per list\n const setsMap = new Map<string, Set<string>>();\n for (const listId of listIds) {\n setsMap.set(listId, new Set());\n }\n\n if (result.Success && result.Results) {\n for (const detail of result.Results) {\n const set = setsMap.get(detail.ListID);\n if (set) {\n set.add(detail.RecordID);\n }\n }\n }\n\n // Update cache — namespaced under `list:` so view operands can share\n // the same cache map without collision.\n for (const [listId, records] of setsMap) {\n this.listDetailsCache.set(OperandCacheKey('list', listId), records);\n }\n\n // Build VennSet objects. We keep the legacy `MJListEntity` overload's\n // shape but populate the new discriminator fields so the diagram +\n // downstream operations stay consistent regardless of which entry\n // point was used.\n const sets: VennSet[] = lists.map((list, index) => ({\n operandKey: OperandCacheKey('list', list.ID),\n kind: 'list' as const,\n listId: list.ID,\n listName: list.Name,\n color: VENN_COLORS[index % VENN_COLORS.length],\n recordIds: setsMap.get(list.ID) || new Set(),\n size: setsMap.get(list.ID)?.size || 0\n }));\n\n // Calculate all possible intersections\n const intersections = this.calculateAllIntersections(sets);\n\n // Get entity info from the first list (assuming all lists are same entity)\n const entityId = lists[0]?.EntityID;\n const entityName = lists[0]?.Entity;\n\n return { sets, intersections, entityId, entityName };\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /**\n * Generalized Venn-data loader supporting both lists and views.\n *\n * - Lists resolve via the same `MJ: List Details` query as the legacy\n * path (cached by `list:<id>`).\n * - Views resolve via `RunView({ViewID})` and primary-key serialization;\n * cached by `view:<id>` so picking the same view twice doesn't\n * re-query.\n *\n * The cache deliberately namespaces the kind — a list and a view that\n * shared the same UUID would still hit distinct entries.\n */\n async calculateVennDataForOperands(operands: SetOperand[]): Promise<VennData> {\n if (operands.length === 0) return { sets: [], intersections: [] };\n this.loadingSubject.next(true);\n try {\n await this.ensureOperandsLoaded(operands);\n\n const sets: VennSet[] = operands.map((op) => ({\n operandKey: OperandCacheKey(op.kind, op.id),\n kind: op.kind,\n listId: op.id,\n listName: op.name,\n color: op.color,\n recordIds: this.cacheGet(op) ?? new Set<string>(),\n size: this.cacheGet(op)?.size ?? 0,\n }));\n\n const intersections = this.calculateAllIntersections(sets);\n const entityId = operands[0]?.entityID;\n const entityName = operands[0]?.entityName;\n return { sets, intersections, entityId, entityName };\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /**\n * Operand-aware sibling of `performOperation`. `operandKeys` are the\n * `OperandCacheKey()` strings produced by `calculateVennDataForOperands`,\n * so callers can stay in operand-key space end-to-end.\n */\n async performOperationForOperands(\n operation: SetOperation,\n operands: SetOperand[],\n allOperands?: SetOperand[],\n ): Promise<SetOperationResult> {\n this.loadingSubject.next(true);\n try {\n await this.ensureOperandsLoaded([...operands, ...(allOperands ?? [])]);\n const sets = operands.map((op) => this.cacheGet(op) ?? new Set<string>());\n const resultSet = this.computeOperationResult(operation, sets, allOperands);\n return {\n operation,\n inputSetIds: operands.map((op) => OperandCacheKey(op.kind, op.id)),\n resultRecordIds: Array.from(resultSet),\n resultCount: resultSet.size,\n };\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /**\n * Calculate all possible intersections between the given sets\n */\n private calculateAllIntersections(sets: VennSet[]): VennIntersection[] {\n const intersections: VennIntersection[] = [];\n const n = sets.length;\n\n // Generate all non-empty subsets of sets\n for (let mask = 1; mask < (1 << n); mask++) {\n const subset: VennSet[] = [];\n for (let i = 0; i < n; i++) {\n if (mask & (1 << i)) {\n subset.push(sets[i]);\n }\n }\n\n // Calculate exclusive intersection (records in exactly these sets)\n const recordIds = this.calculateExclusiveIntersection(subset, sets);\n\n if (recordIds.length > 0 || subset.length === 1) {\n const setIds = subset.map(s => s.listId);\n const setLabels = subset.map(s => s.listName);\n\n intersections.push({\n setIds,\n setLabels,\n size: recordIds.length,\n recordIds,\n label: this.buildIntersectionLabel(setLabels),\n isSelected: false\n });\n }\n }\n\n // Sort by number of sets (single sets first, then pairs, etc.)\n intersections.sort((a, b) => a.setIds.length - b.setIds.length);\n\n return intersections;\n }\n\n /**\n * Calculate records that are in ALL of the specified sets but NONE of the other sets\n */\n private calculateExclusiveIntersection(includedSets: VennSet[], allSets: VennSet[]): string[] {\n if (includedSets.length === 0) return [];\n\n // Start with records in the first included set\n let result = new Set(includedSets[0].recordIds);\n\n // Intersect with all other included sets\n for (let i = 1; i < includedSets.length; i++) {\n result = this.intersect(result, includedSets[i].recordIds);\n }\n\n // Remove records that are in excluded sets\n const excludedSets = allSets.filter(s => !includedSets.includes(s));\n for (const excluded of excludedSets) {\n result = this.difference(result, excluded.recordIds);\n }\n\n return Array.from(result);\n }\n\n /**\n * Build a display label for an intersection\n */\n private buildIntersectionLabel(setLabels: string[]): string {\n if (setLabels.length === 1) {\n return `Only in ${setLabels[0]}`;\n }\n return setLabels.join(' ∩ ');\n }\n\n /**\n * Perform a set operation on multiple lists\n */\n async performOperation(\n operation: SetOperation,\n listIds: string[],\n allListIds?: string[]\n ): Promise<SetOperationResult> {\n this.loadingSubject.next(true);\n\n try {\n // Ensure we have the list details loaded\n await this.ensureListsLoaded(listIds);\n\n const sets = listIds.map(id => this.listDetailsCache.get(OperandCacheKey('list', id)) || new Set<string>());\n let resultSet: Set<string>;\n\n switch (operation) {\n case 'union':\n resultSet = this.unionAll(sets);\n break;\n\n case 'intersection':\n resultSet = this.intersectAll(sets);\n break;\n\n case 'difference':\n // A - B - C - ... (first set minus all others)\n if (sets.length < 2) {\n resultSet = sets[0] || new Set();\n } else {\n resultSet = new Set(sets[0]);\n for (let i = 1; i < sets.length; i++) {\n resultSet = this.difference(resultSet, sets[i]);\n }\n }\n break;\n\n case 'symmetric_difference':\n // Records that are in exactly one of the sets\n resultSet = this.symmetricDifferenceAll(sets);\n break;\n\n case 'complement':\n // Records NOT in any of the selected sets (requires allListIds)\n if (allListIds) {\n await this.ensureListsLoaded(allListIds);\n const allSets = allListIds.map(id => this.listDetailsCache.get(OperandCacheKey('list', id)) || new Set<string>());\n const allRecords = this.unionAll(allSets);\n const selectedRecords = this.unionAll(sets);\n resultSet = this.difference(allRecords, selectedRecords);\n } else {\n resultSet = new Set();\n }\n break;\n\n default:\n resultSet = new Set();\n }\n\n return {\n operation,\n inputSetIds: listIds,\n resultRecordIds: Array.from(resultSet),\n resultCount: resultSet.size\n };\n } finally {\n this.loadingSubject.next(false);\n }\n }\n\n /**\n * Cached lookup keyed by operand kind+id. Lists and views share the\n * cache map but their entries can't collide thanks to the key namespace.\n */\n private cacheGet(operand: SetOperand): Set<string> | undefined {\n return this.listDetailsCache.get(OperandCacheKey(operand.kind, operand.id));\n }\n\n /**\n * Bulk-load any operands not already cached. Lists batch through a\n * single `MJ: List Details` query (same shape as before); views resolve\n * one at a time because each is a separate parameterized RunView. Both\n * paths populate the same cache map.\n */\n private async ensureOperandsLoaded(operands: SetOperand[]): Promise<void> {\n const missingLists: string[] = [];\n const missingViews: SetOperand[] = [];\n for (const op of operands) {\n if (this.cacheGet(op)) continue;\n if (op.kind === 'list') missingLists.push(op.id);\n else missingViews.push(op);\n }\n await Promise.all([\n missingLists.length > 0 ? this.loadMissingLists(missingLists) : Promise.resolve(),\n missingViews.length > 0 ? this.loadMissingViews(missingViews) : Promise.resolve(),\n ]);\n }\n\n private async loadMissingLists(listIds: string[]): Promise<void> {\n const listIdFilter = listIds.map((id) => `'${id}'`).join(',');\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple',\n MaxRows: ListSetOperationsService.MAX_OPERAND_RECORDS,\n });\n this.trackTruncation(result.Results?.length ?? 0, 'loadMissingLists');\n\n for (const id of listIds) {\n this.listDetailsCache.set(OperandCacheKey('list', id), new Set<string>());\n }\n if (result.Success && result.Results) {\n for (const detail of result.Results) {\n // Normalize on the ListID we look up by AND on the RecordID we\n // store — mixed casing on either side would otherwise produce\n // empty intersections between lists that actually share records.\n const set = this.listDetailsCache.get(OperandCacheKey('list', NormalizeUUID(detail.ListID)))\n ?? this.listDetailsCache.get(OperandCacheKey('list', detail.ListID));\n if (set) set.add(normalizeRecordId(detail.RecordID));\n }\n }\n }\n\n private async loadMissingViews(operands: SetOperand[]): Promise<void> {\n // Fire view queries in parallel — each one is small and independent.\n const tasks = operands.map((op) => this.loadSingleView(op));\n await Promise.all(tasks);\n }\n\n /**\n * Resolve a view to a set of record IDs in the canonical `MJ: List\n * Details.RecordID` format (single-PK → raw value; composite-PK →\n * `Field1|Value1||Field2|Value2`, matching `CompositeKey.ToConcatenatedString`).\n * This keeps view operands cache-compatible with list operands without\n * any transformation in the set-op logic.\n */\n private async loadSingleView(operand: SetOperand): Promise<void> {\n const md = this.Provider;\n const entityInfo = md.EntityByName(operand.entityName);\n if (!entityInfo) {\n this.listDetailsCache.set(OperandCacheKey('view', operand.id), new Set<string>());\n return;\n }\n const pkFields = entityInfo.PrimaryKeys.map((pk) => pk.Name);\n\n const rv = RunView.FromMetadataProvider(md);\n const result = await rv.RunView({\n ViewID: operand.id,\n Fields: pkFields,\n ResultType: 'simple',\n MaxRows: ListSetOperationsService.MAX_OPERAND_RECORDS,\n });\n this.trackTruncation(result.Results?.length ?? 0, `loadSingleView(${operand.id})`);\n\n const set = new Set<string>();\n if (result.Success && result.Results) {\n for (const row of result.Results) {\n set.add(this.serializeRecordId(entityInfo, row as Record<string, unknown>));\n }\n }\n this.listDetailsCache.set(OperandCacheKey('view', operand.id), set);\n }\n\n /**\n * Mirror of `ListOperations.serializeRecordId` so view operands produce\n * IDs that round-trip cleanly against MJ List Details.\n */\n private serializeRecordId(entityInfo: EntityInfo, row: Record<string, unknown>): string {\n if (entityInfo.PrimaryKeys.length === 1) {\n return normalizeRecordId(String(row[entityInfo.PrimaryKeys[0].Name]));\n }\n const ck = new CompositeKey();\n ck.KeyValuePairs = entityInfo.PrimaryKeys.map((pk) => ({\n FieldName: pk.Name,\n Value: row[pk.Name] as string | number | Date | null | undefined,\n }));\n return normalizeRecordId(ck.ToConcatenatedString());\n }\n\n /**\n * Pure compute step shared by the legacy and operand-aware paths.\n * Operates on raw `Set<string>` inputs so it stays agnostic to where\n * those sets came from.\n */\n private computeOperationResult(\n operation: SetOperation,\n sets: Set<string>[],\n allOperandsForComplement?: SetOperand[],\n ): Set<string> {\n switch (operation) {\n case 'union':\n return this.unionAll(sets);\n case 'intersection':\n return this.intersectAll(sets);\n case 'difference': {\n if (sets.length < 2) return sets[0] ?? new Set<string>();\n let r = new Set(sets[0]);\n for (let i = 1; i < sets.length; i++) r = this.difference(r, sets[i]);\n return r;\n }\n case 'symmetric_difference':\n return this.symmetricDifferenceAll(sets);\n case 'complement': {\n if (!allOperandsForComplement) return new Set<string>();\n const allSets = allOperandsForComplement.map((op) => this.cacheGet(op) ?? new Set<string>());\n return this.difference(this.unionAll(allSets), this.unionAll(sets));\n }\n default:\n return new Set<string>();\n }\n }\n\n /**\n * Ensure list details are loaded for the given list IDs. Cache keys use\n * the `list:<id>` namespace introduced for operand support; the legacy\n * list-only callers go through here without needing to know the format.\n */\n private async ensureListsLoaded(listIds: string[]): Promise<void> {\n const missingIds = listIds.filter(id => !this.listDetailsCache.has(OperandCacheKey('list', id)));\n\n if (missingIds.length === 0) return;\n\n const listIdFilter = missingIds.map(id => `'${id}'`).join(',');\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n const result = await rv.RunView<{ ListID: string; RecordID: string }>({\n EntityName: 'MJ: List Details',\n ExtraFilter: `ListID IN (${listIdFilter})`,\n Fields: ['ListID', 'RecordID'],\n ResultType: 'simple',\n MaxRows: ListSetOperationsService.MAX_OPERAND_RECORDS\n });\n this.trackTruncation(result.Results?.length ?? 0, 'ensureListsLoaded');\n\n // Initialize sets for missing lists\n for (const id of missingIds) {\n this.listDetailsCache.set(OperandCacheKey('list', id), new Set());\n }\n\n if (result.Success && result.Results) {\n for (const detail of result.Results) {\n const set = this.listDetailsCache.get(OperandCacheKey('list', detail.ListID));\n if (set) {\n set.add(detail.RecordID);\n }\n }\n }\n }\n\n /**\n * Set intersection of two sets\n */\n private intersect(a: Set<string>, b: Set<string>): Set<string> {\n const result = new Set<string>();\n for (const item of a) {\n if (b.has(item)) {\n result.add(item);\n }\n }\n return result;\n }\n\n /**\n * Set difference (a - b)\n */\n private difference(a: Set<string>, b: Set<string>): Set<string> {\n const result = new Set<string>();\n for (const item of a) {\n if (!b.has(item)) {\n result.add(item);\n }\n }\n return result;\n }\n\n /**\n * Union of multiple sets\n */\n private unionAll(sets: Set<string>[]): Set<string> {\n const result = new Set<string>();\n for (const set of sets) {\n for (const item of set) {\n result.add(item);\n }\n }\n return result;\n }\n\n /**\n * Intersection of multiple sets\n */\n private intersectAll(sets: Set<string>[]): Set<string> {\n if (sets.length === 0) return new Set();\n if (sets.length === 1) return new Set(sets[0]);\n\n let result = new Set(sets[0]);\n for (let i = 1; i < sets.length; i++) {\n result = this.intersect(result, sets[i]);\n }\n return result;\n }\n\n /**\n * Symmetric difference of multiple sets (items in exactly one set)\n */\n private symmetricDifferenceAll(sets: Set<string>[]): Set<string> {\n if (sets.length === 0) return new Set();\n\n // Count occurrences of each item\n const counts = new Map<string, number>();\n for (const set of sets) {\n for (const item of set) {\n counts.set(item, (counts.get(item) || 0) + 1);\n }\n }\n\n // Keep only items that appear exactly once\n const result = new Set<string>();\n for (const [item, count] of counts) {\n if (count === 1) {\n result.add(item);\n }\n }\n return result;\n }\n\n /**\n * Get the color for a list by its index\n */\n getColorForIndex(index: number): string {\n return VENN_COLORS[index % VENN_COLORS.length];\n }\n\n /**\n * Clear the cache\n */\n clearCache(): void {\n this.listDetailsCache.clear();\n }\n}\n"]}
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
/** A scored record in the at-risk list. */
|
|
12
12
|
export interface AtRiskRow {
|
|
13
13
|
recordId: string;
|
|
14
|
+
/**
|
|
15
|
+
* Human-readable label for the record (e.g. the member's name/email), resolved from the model's
|
|
16
|
+
* target entity. Null until resolved; the UI falls back to {@link recordId} so the row is never blank.
|
|
17
|
+
*/
|
|
18
|
+
label: string | null;
|
|
14
19
|
/** 0–1 prediction score (probability / risk). */
|
|
15
20
|
score: number;
|
|
16
21
|
/** Risk as a 0–100 integer, for display. */
|
|
@@ -19,7 +24,28 @@ export interface AtRiskRow {
|
|
|
19
24
|
class: string | null;
|
|
20
25
|
/** Risk band, for color. */
|
|
21
26
|
band: 'high' | 'medium' | 'low';
|
|
27
|
+
/**
|
|
28
|
+
* Top signed per-record drivers behind THIS row's prediction (P1-5), humanized + one-hot-collapsed for
|
|
29
|
+
* display. `up: true` pushed the risk up, `false` down. Null when the model doesn't produce per-record
|
|
30
|
+
* attribution (tree/ensemble/multiclass) — the UI then shows the model's global drivers instead.
|
|
31
|
+
*/
|
|
32
|
+
drivers: RowDriver[] | null;
|
|
22
33
|
}
|
|
34
|
+
/** A humanized, signed per-record driver for the at-risk row's inline "why". */
|
|
35
|
+
export interface RowDriver {
|
|
36
|
+
/** Display label (humanized, one-hot base collapsed). */
|
|
37
|
+
label: string;
|
|
38
|
+
/** Signed contribution magnitude for this row. */
|
|
39
|
+
value: number;
|
|
40
|
+
/** Whether this pushed the risk UP (value > 0) or down. */
|
|
41
|
+
up: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build a human-readable label for a record from its (simple) row of the target entity — preferring a
|
|
45
|
+
* single `Name`, else `FirstName`+`LastName`, else `Email`, else any first non-empty string field.
|
|
46
|
+
* Returns null when nothing usable is found (caller falls back to the record id).
|
|
47
|
+
*/
|
|
48
|
+
export declare function labelFromRecord(row: Record<string, unknown> | undefined | null): string | null;
|
|
23
49
|
/** The raw per-record detail the list is built from (a slice of `MJ: Process Run Details`). */
|
|
24
50
|
export interface RunDetailLike {
|
|
25
51
|
recordId: string;
|
|
@@ -34,4 +60,11 @@ export declare function parseAtRiskRows(details: RunDetailLike[]): AtRiskRow[];
|
|
|
34
60
|
* highest-importance feature names — so a business user sees "what's driving this", not raw weights.
|
|
35
61
|
*/
|
|
36
62
|
export declare function topGlobalDrivers(featureImportanceJson: string | null | undefined, n?: number): string[];
|
|
63
|
+
/**
|
|
64
|
+
* Turn a raw feature/column name into a readable label — `RetentionOverdueInvoices` → `Retention Overdue
|
|
65
|
+
* Invoices`, `overdue_invoices` → `Overdue Invoices`, and a one-hot `MembershipType=Student` →
|
|
66
|
+
* `Membership Type = Student`. Splits camelCase + snake/kebab, spaces one-hot `=`, collapses whitespace,
|
|
67
|
+
* and capitalizes the first letter. Already-spaced labels pass through unchanged.
|
|
68
|
+
*/
|
|
69
|
+
export declare function humanizeFeatureName(name: string): string;
|
|
37
70
|
//# sourceMappingURL=at-risk.view-models.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"at-risk.view-models.d.ts","sourceRoot":"","sources":["../../src/PredictiveStudio/at-risk.view-models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"at-risk.view-models.d.ts","sourceRoot":"","sources":["../../src/PredictiveStudio/at-risk.view-models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAChC;;;;OAIG;IACH,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;CAC7B;AAED,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,EAAE,EAAE,OAAO,CAAC;CACb;AAkBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAe9F;AAED,+FAA+F;AAC/F,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAMD,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,CA8BrE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,CAAC,SAAI,GAAG,MAAM,EAAE,CA4BlG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQxD"}
|
|
@@ -8,6 +8,50 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Framework-free + deterministic → unit-tested with no Angular.
|
|
10
10
|
*/
|
|
11
|
+
/** Parse + humanize the raw per-record `drivers` (post-preprocessing `feature`/`value`) into {@link RowDriver}s. */
|
|
12
|
+
function parseRowDrivers(raw) {
|
|
13
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
14
|
+
return null;
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const d of raw) {
|
|
17
|
+
const feature = typeof d?.feature === 'string' ? d.feature : '';
|
|
18
|
+
const value = typeof d?.value === 'number' ? d.value : NaN;
|
|
19
|
+
if (!feature || !Number.isFinite(value))
|
|
20
|
+
continue;
|
|
21
|
+
// Keep the one-hot category: for a per-record "why", the category IS the story — "Membership Type =
|
|
22
|
+
// Student lowers risk" is actionable where a collapsed "Membership Type" is close to meaningless.
|
|
23
|
+
// (Collapsing across categories is only right for GLOBAL importance — see topGlobalDrivers.)
|
|
24
|
+
out.push({ label: humanizeFeatureName(feature), value, up: value > 0 });
|
|
25
|
+
}
|
|
26
|
+
return out.length > 0 ? out : null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build a human-readable label for a record from its (simple) row of the target entity — preferring a
|
|
30
|
+
* single `Name`, else `FirstName`+`LastName`, else `Email`, else any first non-empty string field.
|
|
31
|
+
* Returns null when nothing usable is found (caller falls back to the record id).
|
|
32
|
+
*/
|
|
33
|
+
export function labelFromRecord(row) {
|
|
34
|
+
if (!row)
|
|
35
|
+
return null;
|
|
36
|
+
const str = (v) => (typeof v === 'string' ? v.trim() : '');
|
|
37
|
+
const name = str(row['Name']);
|
|
38
|
+
if (name)
|
|
39
|
+
return name;
|
|
40
|
+
const full = `${str(row['FirstName'])} ${str(row['LastName'])}`.trim();
|
|
41
|
+
const email = str(row['Email']);
|
|
42
|
+
if (full && email)
|
|
43
|
+
return `${full} (${email})`;
|
|
44
|
+
if (full)
|
|
45
|
+
return full;
|
|
46
|
+
if (email)
|
|
47
|
+
return email;
|
|
48
|
+
for (const v of Object.values(row)) {
|
|
49
|
+
const s = str(v);
|
|
50
|
+
if (s && s.length <= 120)
|
|
51
|
+
return s;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
11
55
|
function bandFor(score) {
|
|
12
56
|
return score >= 0.7 ? 'high' : score >= 0.4 ? 'medium' : 'low';
|
|
13
57
|
}
|
|
@@ -29,7 +73,15 @@ export function parseAtRiskRows(details) {
|
|
|
29
73
|
if (typeof p.score !== 'number' || !Number.isFinite(p.score))
|
|
30
74
|
continue;
|
|
31
75
|
const score = p.score;
|
|
32
|
-
rows.push({
|
|
76
|
+
rows.push({
|
|
77
|
+
recordId: d.recordId,
|
|
78
|
+
label: null,
|
|
79
|
+
score,
|
|
80
|
+
riskPct: Math.round(score * 100),
|
|
81
|
+
class: p.class ?? null,
|
|
82
|
+
band: bandFor(score),
|
|
83
|
+
drivers: parseRowDrivers(p.drivers),
|
|
84
|
+
});
|
|
33
85
|
}
|
|
34
86
|
return rows.sort((a, b) => b.score - a.score);
|
|
35
87
|
}
|
|
@@ -72,6 +124,21 @@ export function topGlobalDrivers(featureImportanceJson, n = 3) {
|
|
|
72
124
|
continue;
|
|
73
125
|
byFeature.set(base, Math.max(byFeature.get(base) ?? 0, weight));
|
|
74
126
|
}
|
|
75
|
-
return [...byFeature.entries()].sort((a, b) => b[1] - a[1]).slice(0, n).map(([name]) => name);
|
|
127
|
+
return [...byFeature.entries()].sort((a, b) => b[1] - a[1]).slice(0, n).map(([name]) => humanizeFeatureName(name));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Turn a raw feature/column name into a readable label — `RetentionOverdueInvoices` → `Retention Overdue
|
|
131
|
+
* Invoices`, `overdue_invoices` → `Overdue Invoices`, and a one-hot `MembershipType=Student` →
|
|
132
|
+
* `Membership Type = Student`. Splits camelCase + snake/kebab, spaces one-hot `=`, collapses whitespace,
|
|
133
|
+
* and capitalizes the first letter. Already-spaced labels pass through unchanged.
|
|
134
|
+
*/
|
|
135
|
+
export function humanizeFeatureName(name) {
|
|
136
|
+
return name
|
|
137
|
+
.replace(/[_-]+/g, ' ')
|
|
138
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
139
|
+
.replace(/\s*=\s*/g, ' = ')
|
|
140
|
+
.replace(/\s+/g, ' ')
|
|
141
|
+
.trim()
|
|
142
|
+
.replace(/^\w/, (c) => c.toUpperCase());
|
|
76
143
|
}
|
|
77
144
|
//# sourceMappingURL=at-risk.view-models.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"at-risk.view-models.js","sourceRoot":"","sources":["../../src/PredictiveStudio/at-risk.view-models.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure derivations for the business workspace's **ranked at-risk list** — turning a model's per-record\n * scoring results (`MJ: Process Run Details` payloads) into a \"who to focus on\" list sorted by risk, and\n * the model's global feature importance into plain-language \"what's driving this\" drivers.\n *\n * Honest about the gap: MJ stores only GLOBAL feature importance (not per-record/SHAP), so we surface the\n * top global drivers once for the whole prediction rather than fabricating per-member attribution.\n *\n * Framework-free + deterministic → unit-tested with no Angular.\n */\n\n/** A scored record in the at-risk list. */\nexport interface AtRiskRow {\n recordId: string;\n /** 0–1 prediction score (probability / risk). */\n score: number;\n /** Risk as a 0–100 integer, for display. */\n riskPct: number;\n /** Predicted class label, when present (classification). */\n class: string | null;\n /** Risk band, for color. */\n band: 'high' | 'medium' | 'low';\n}\n\n/** The raw per-record detail the list is built from (a slice of `MJ: Process Run Details`). */\nexport interface RunDetailLike {\n recordId: string;\n ResultPayload?: string | null;\n}\n\nfunction bandFor(score: number): AtRiskRow['band'] {\n return score >= 0.7 ? 'high' : score >= 0.4 ? 'medium' : 'low';\n}\n\n/** Parse + rank the per-record predictions into the at-risk list (highest risk first). */\nexport function parseAtRiskRows(details: RunDetailLike[]): AtRiskRow[] {\n const rows: AtRiskRow[] = [];\n for (const d of details) {\n if (!d.ResultPayload) continue;\n let parsed: { score?: number; class?: string; output?: { score?: number; class?: string } };\n try {\n parsed = JSON.parse(d.ResultPayload);\n } catch {\n continue;\n }\n // Write-back runs nest the prediction under `output`; generic runs carry it at the top level.\n const p = parsed.output ?? parsed;\n if (typeof p.score !== 'number' || !Number.isFinite(p.score)) continue;\n const score = p.score;\n rows.push({ recordId: d.recordId, score, riskPct: Math.round(score * 100), class: p.class ?? null, band: bandFor(score) });\n }\n return rows.sort((a, b) => b.score - a.score);\n}\n\n/**\n * The top plain-language drivers for the whole prediction, from the model's global feature importance.\n * Accepts the object form (`{\"MembershipType=Student\": 0.9, ...}`) or the array form\n * (`[{feature, importance}]`), strips one-hot `=value` suffixes, de-duplicates, and returns the\n * highest-importance feature names — so a business user sees \"what's driving this\", not raw weights.\n */\nexport function topGlobalDrivers(featureImportanceJson: string | null | undefined, n = 3): string[] {\n if (!featureImportanceJson) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(featureImportanceJson);\n } catch {\n return [];\n }\n const pairs: Array<{ name: string; weight: number }> = [];\n if (Array.isArray(parsed)) {\n for (const e of parsed as Array<Record<string, unknown>>) {\n const name = (e.feature ?? e.name) as string | undefined;\n const weight = (e.importance ?? e.value) as number | undefined;\n if (typeof name === 'string' && typeof weight === 'number') pairs.push({ name, weight: Math.abs(weight) });\n }\n } else if (parsed && typeof parsed === 'object') {\n for (const [name, weight] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof weight === 'number') pairs.push({ name, weight: Math.abs(weight) });\n }\n }\n // Collapse one-hot columns (\"Feature=Value\" → \"Feature\"), keeping each feature's max weight.\n const byFeature = new Map<string, number>();\n for (const { name, weight } of pairs) {\n const base = name.split('=')[0].trim();\n if (!base) continue;\n byFeature.set(base, Math.max(byFeature.get(base) ?? 0, weight));\n }\n return [...byFeature.entries()].sort((a, b) => b[1] - a[1]).slice(0, n).map(([name]) => name);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"at-risk.view-models.js","sourceRoot":"","sources":["../../src/PredictiveStudio/at-risk.view-models.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure derivations for the business workspace's **ranked at-risk list** — turning a model's per-record\n * scoring results (`MJ: Process Run Details` payloads) into a \"who to focus on\" list sorted by risk, and\n * the model's global feature importance into plain-language \"what's driving this\" drivers.\n *\n * Honest about the gap: MJ stores only GLOBAL feature importance (not per-record/SHAP), so we surface the\n * top global drivers once for the whole prediction rather than fabricating per-member attribution.\n *\n * Framework-free + deterministic → unit-tested with no Angular.\n */\n\n/** A scored record in the at-risk list. */\nexport interface AtRiskRow {\n recordId: string;\n /**\n * Human-readable label for the record (e.g. the member's name/email), resolved from the model's\n * target entity. Null until resolved; the UI falls back to {@link recordId} so the row is never blank.\n */\n label: string | null;\n /** 0–1 prediction score (probability / risk). */\n score: number;\n /** Risk as a 0–100 integer, for display. */\n riskPct: number;\n /** Predicted class label, when present (classification). */\n class: string | null;\n /** Risk band, for color. */\n band: 'high' | 'medium' | 'low';\n /**\n * Top signed per-record drivers behind THIS row's prediction (P1-5), humanized + one-hot-collapsed for\n * display. `up: true` pushed the risk up, `false` down. Null when the model doesn't produce per-record\n * attribution (tree/ensemble/multiclass) — the UI then shows the model's global drivers instead.\n */\n drivers: RowDriver[] | null;\n}\n\n/** A humanized, signed per-record driver for the at-risk row's inline \"why\". */\nexport interface RowDriver {\n /** Display label (humanized, one-hot base collapsed). */\n label: string;\n /** Signed contribution magnitude for this row. */\n value: number;\n /** Whether this pushed the risk UP (value > 0) or down. */\n up: boolean;\n}\n\n/** Parse + humanize the raw per-record `drivers` (post-preprocessing `feature`/`value`) into {@link RowDriver}s. */\nfunction parseRowDrivers(raw: unknown): RowDriver[] | null {\n if (!Array.isArray(raw) || raw.length === 0) return null;\n const out: RowDriver[] = [];\n for (const d of raw as Array<{ feature?: unknown; value?: unknown }>) {\n const feature = typeof d?.feature === 'string' ? d.feature : '';\n const value = typeof d?.value === 'number' ? d.value : NaN;\n if (!feature || !Number.isFinite(value)) continue;\n // Keep the one-hot category: for a per-record \"why\", the category IS the story — \"Membership Type =\n // Student lowers risk\" is actionable where a collapsed \"Membership Type\" is close to meaningless.\n // (Collapsing across categories is only right for GLOBAL importance — see topGlobalDrivers.)\n out.push({ label: humanizeFeatureName(feature), value, up: value > 0 });\n }\n return out.length > 0 ? out : null;\n}\n\n/**\n * Build a human-readable label for a record from its (simple) row of the target entity — preferring a\n * single `Name`, else `FirstName`+`LastName`, else `Email`, else any first non-empty string field.\n * Returns null when nothing usable is found (caller falls back to the record id).\n */\nexport function labelFromRecord(row: Record<string, unknown> | undefined | null): string | null {\n if (!row) return null;\n const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\n const name = str(row['Name']);\n if (name) return name;\n const full = `${str(row['FirstName'])} ${str(row['LastName'])}`.trim();\n const email = str(row['Email']);\n if (full && email) return `${full} (${email})`;\n if (full) return full;\n if (email) return email;\n for (const v of Object.values(row)) {\n const s = str(v);\n if (s && s.length <= 120) return s;\n }\n return null;\n}\n\n/** The raw per-record detail the list is built from (a slice of `MJ: Process Run Details`). */\nexport interface RunDetailLike {\n recordId: string;\n ResultPayload?: string | null;\n}\n\nfunction bandFor(score: number): AtRiskRow['band'] {\n return score >= 0.7 ? 'high' : score >= 0.4 ? 'medium' : 'low';\n}\n\n/** Parse + rank the per-record predictions into the at-risk list (highest risk first). */\nexport function parseAtRiskRows(details: RunDetailLike[]): AtRiskRow[] {\n const rows: AtRiskRow[] = [];\n for (const d of details) {\n if (!d.ResultPayload) continue;\n let parsed: {\n score?: number;\n class?: string;\n drivers?: unknown;\n output?: { score?: number; class?: string; drivers?: unknown };\n };\n try {\n parsed = JSON.parse(d.ResultPayload);\n } catch {\n continue;\n }\n // Write-back runs nest the prediction under `output`; generic runs carry it at the top level.\n const p = parsed.output ?? parsed;\n if (typeof p.score !== 'number' || !Number.isFinite(p.score)) continue;\n const score = p.score;\n rows.push({\n recordId: d.recordId,\n label: null,\n score,\n riskPct: Math.round(score * 100),\n class: p.class ?? null,\n band: bandFor(score),\n drivers: parseRowDrivers(p.drivers),\n });\n }\n return rows.sort((a, b) => b.score - a.score);\n}\n\n/**\n * The top plain-language drivers for the whole prediction, from the model's global feature importance.\n * Accepts the object form (`{\"MembershipType=Student\": 0.9, ...}`) or the array form\n * (`[{feature, importance}]`), strips one-hot `=value` suffixes, de-duplicates, and returns the\n * highest-importance feature names — so a business user sees \"what's driving this\", not raw weights.\n */\nexport function topGlobalDrivers(featureImportanceJson: string | null | undefined, n = 3): string[] {\n if (!featureImportanceJson) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(featureImportanceJson);\n } catch {\n return [];\n }\n const pairs: Array<{ name: string; weight: number }> = [];\n if (Array.isArray(parsed)) {\n for (const e of parsed as Array<Record<string, unknown>>) {\n const name = (e.feature ?? e.name) as string | undefined;\n const weight = (e.importance ?? e.value) as number | undefined;\n if (typeof name === 'string' && typeof weight === 'number') pairs.push({ name, weight: Math.abs(weight) });\n }\n } else if (parsed && typeof parsed === 'object') {\n for (const [name, weight] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof weight === 'number') pairs.push({ name, weight: Math.abs(weight) });\n }\n }\n // Collapse one-hot columns (\"Feature=Value\" → \"Feature\"), keeping each feature's max weight.\n const byFeature = new Map<string, number>();\n for (const { name, weight } of pairs) {\n const base = name.split('=')[0].trim();\n if (!base) continue;\n byFeature.set(base, Math.max(byFeature.get(base) ?? 0, weight));\n }\n return [...byFeature.entries()].sort((a, b) => b[1] - a[1]).slice(0, n).map(([name]) => humanizeFeatureName(name));\n}\n\n/**\n * Turn a raw feature/column name into a readable label — `RetentionOverdueInvoices` → `Retention Overdue\n * Invoices`, `overdue_invoices` → `Overdue Invoices`, and a one-hot `MembershipType=Student` →\n * `Membership Type = Student`. Splits camelCase + snake/kebab, spaces one-hot `=`, collapses whitespace,\n * and capitalizes the first letter. Already-spaced labels pass through unchanged.\n */\nexport function humanizeFeatureName(name: string): string {\n return name\n .replace(/[_-]+/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/\\s*=\\s*/g, ' = ')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/^\\w/, (c) => c.toUpperCase());\n}\n"]}
|