teradataml 20.0.0.6__py3-none-any.whl → 20.0.0.7__py3-none-any.whl

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.

Potentially problematic release.


This version of teradataml might be problematic. Click here for more details.

Files changed (96) hide show
  1. teradataml/README.md +210 -0
  2. teradataml/__init__.py +1 -1
  3. teradataml/_version.py +1 -1
  4. teradataml/analytics/analytic_function_executor.py +162 -76
  5. teradataml/analytics/byom/__init__.py +1 -1
  6. teradataml/analytics/json_parser/__init__.py +2 -0
  7. teradataml/analytics/json_parser/analytic_functions_argument.py +95 -2
  8. teradataml/analytics/json_parser/metadata.py +22 -4
  9. teradataml/analytics/sqle/DecisionTreePredict.py +3 -2
  10. teradataml/analytics/sqle/NaiveBayesPredict.py +3 -2
  11. teradataml/analytics/sqle/__init__.py +3 -0
  12. teradataml/analytics/utils.py +4 -1
  13. teradataml/automl/__init__.py +2369 -464
  14. teradataml/automl/autodataprep/__init__.py +15 -0
  15. teradataml/automl/custom_json_utils.py +184 -112
  16. teradataml/automl/data_preparation.py +113 -58
  17. teradataml/automl/data_transformation.py +154 -53
  18. teradataml/automl/feature_engineering.py +113 -53
  19. teradataml/automl/feature_exploration.py +548 -25
  20. teradataml/automl/model_evaluation.py +260 -32
  21. teradataml/automl/model_training.py +399 -206
  22. teradataml/clients/auth_client.py +2 -2
  23. teradataml/common/aed_utils.py +11 -2
  24. teradataml/common/bulk_exposed_utils.py +4 -2
  25. teradataml/common/constants.py +62 -2
  26. teradataml/common/garbagecollector.py +50 -21
  27. teradataml/common/messagecodes.py +47 -2
  28. teradataml/common/messages.py +19 -1
  29. teradataml/common/sqlbundle.py +23 -6
  30. teradataml/common/utils.py +116 -10
  31. teradataml/context/aed_context.py +16 -10
  32. teradataml/data/Employee.csv +5 -0
  33. teradataml/data/Employee_Address.csv +4 -0
  34. teradataml/data/Employee_roles.csv +5 -0
  35. teradataml/data/JulesBelvezeDummyData.csv +100 -0
  36. teradataml/data/byom_example.json +5 -0
  37. teradataml/data/creditcard_data.csv +284618 -0
  38. teradataml/data/docs/byom/docs/ONNXSeq2Seq.py +255 -0
  39. teradataml/data/docs/sqle/docs_17_10/NGramSplitter.py +1 -1
  40. teradataml/data/docs/sqle/docs_17_20/NGramSplitter.py +1 -1
  41. teradataml/data/docs/sqle/docs_17_20/TextParser.py +1 -1
  42. teradataml/data/jsons/byom/ONNXSeq2Seq.json +287 -0
  43. teradataml/data/jsons/sqle/20.00/AI_AnalyzeSentiment.json +3 -7
  44. teradataml/data/jsons/sqle/20.00/AI_AskLLM.json +3 -7
  45. teradataml/data/jsons/sqle/20.00/AI_DetectLanguage.json +3 -7
  46. teradataml/data/jsons/sqle/20.00/AI_ExtractKeyPhrases.json +3 -7
  47. teradataml/data/jsons/sqle/20.00/AI_MaskPII.json +3 -7
  48. teradataml/data/jsons/sqle/20.00/AI_RecognizeEntities.json +3 -7
  49. teradataml/data/jsons/sqle/20.00/AI_RecognizePIIEntities.json +3 -7
  50. teradataml/data/jsons/sqle/20.00/AI_TextClassifier.json +3 -7
  51. teradataml/data/jsons/sqle/20.00/AI_TextEmbeddings.json +3 -7
  52. teradataml/data/jsons/sqle/20.00/AI_TextSummarize.json +3 -7
  53. teradataml/data/jsons/sqle/20.00/AI_TextTranslate.json +3 -7
  54. teradataml/data/jsons/sqle/20.00/TD_API_AzureML.json +151 -0
  55. teradataml/data/jsons/sqle/20.00/TD_API_Sagemaker.json +182 -0
  56. teradataml/data/jsons/sqle/20.00/TD_API_VertexAI.json +183 -0
  57. teradataml/data/load_example_data.py +29 -11
  58. teradataml/data/payment_fraud_dataset.csv +10001 -0
  59. teradataml/data/teradataml_example.json +67 -0
  60. teradataml/dataframe/copy_to.py +714 -54
  61. teradataml/dataframe/dataframe.py +1153 -33
  62. teradataml/dataframe/dataframe_utils.py +8 -3
  63. teradataml/dataframe/functions.py +168 -1
  64. teradataml/dataframe/setop.py +4 -1
  65. teradataml/dataframe/sql.py +141 -9
  66. teradataml/dbutils/dbutils.py +470 -35
  67. teradataml/dbutils/filemgr.py +1 -1
  68. teradataml/hyperparameter_tuner/optimizer.py +456 -142
  69. teradataml/lib/aed_0_1.dll +0 -0
  70. teradataml/lib/libaed_0_1.dylib +0 -0
  71. teradataml/lib/libaed_0_1.so +0 -0
  72. teradataml/lib/libaed_0_1_aarch64.so +0 -0
  73. teradataml/scriptmgmt/UserEnv.py +234 -34
  74. teradataml/scriptmgmt/lls_utils.py +43 -17
  75. teradataml/sdk/_json_parser.py +1 -1
  76. teradataml/sdk/api_client.py +9 -6
  77. teradataml/sdk/modelops/_client.py +3 -0
  78. teradataml/series/series.py +12 -7
  79. teradataml/store/feature_store/constants.py +601 -234
  80. teradataml/store/feature_store/feature_store.py +2886 -616
  81. teradataml/store/feature_store/mind_map.py +639 -0
  82. teradataml/store/feature_store/models.py +5831 -214
  83. teradataml/store/feature_store/utils.py +390 -0
  84. teradataml/table_operators/table_operator_util.py +1 -1
  85. teradataml/table_operators/templates/dataframe_register.template +6 -2
  86. teradataml/table_operators/templates/dataframe_udf.template +6 -2
  87. teradataml/utils/docstring.py +527 -0
  88. teradataml/utils/dtypes.py +93 -0
  89. teradataml/utils/internal_buffer.py +2 -2
  90. teradataml/utils/utils.py +41 -2
  91. teradataml/utils/validators.py +694 -17
  92. {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/METADATA +213 -2
  93. {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/RECORD +96 -81
  94. {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/WHEEL +0 -0
  95. {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/top_level.txt +0 -0
  96. {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/zip-safe +0 -0
@@ -0,0 +1,639 @@
1
+ """
2
+ Copyright (c) 2025 by Teradata Corporation. All rights reserved.
3
+ TERADATA CORPORATION CONFIDENTIAL AND TRADE SECRET
4
+
5
+ Primary Owner: pradeep.garre@teradata.com
6
+ Secondary Owner: adithya.avvaru@teradata.com
7
+
8
+ This file implements the mind map for Teradata Enterprise Feature Store.
9
+ """
10
+
11
+ _TD_FS_MindMap_Template = """
12
+ <style>
13
+ .mindmap-header-container {
14
+ width: 980px;
15
+ margin: 0 auto;
16
+ border: 1px solid #ccc;
17
+ background: #fafbfc;
18
+ border-radius: 0;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ height: 64px;
23
+ margin-top: 20px;
24
+ }
25
+ .mindmap-header-single {
26
+ width: 100%;
27
+ text-align: center;
28
+ font-size: 1.25em;
29
+ font-weight: 600;
30
+ color: #2d3a4b;
31
+ letter-spacing: 0.5px;
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ height: 100%;
36
+ }
37
+ /* ...existing code... */
38
+ #container_fs_i {
39
+ width: 980px;
40
+ margin: 0 auto 40px auto;
41
+ position: relative;
42
+ border: 1px solid #ccc;
43
+ background: #fafbfc;
44
+ border-top: none;
45
+ border-radius: 0;
46
+ }
47
+ .mindmap-inner {
48
+ display: flex;
49
+ flex-direction: row;
50
+ justify-content: space-between;
51
+ align-items: stretch;
52
+ width: 100%;
53
+ margin-bottom: 50px;
54
+ gap: 32px;
55
+ padding-top: 30px;
56
+ }
57
+ .column {
58
+ flex: 1;
59
+ display: flex;
60
+ flex-direction: column;
61
+ justify-content: flex-start;
62
+ align-items: center;
63
+ margin: 0;
64
+ position: relative;
65
+ background: #fff;
66
+ border: 1.5px solid #e0e6ef;
67
+ border-radius: 14px;
68
+ box-shadow: 0 2px 8px #0001;
69
+ padding: 0 0 18px 0;
70
+ min-width: 140px;
71
+ min-height: 500px;
72
+ transition: box-shadow 0.2s;
73
+ }
74
+ /* Add left margin to first column and right margin to last column for centering */
75
+ .column#data_sources_col_fs_i {
76
+ margin-left: 32px;
77
+ }
78
+ .column#dataset_catalog_col_fs_i {
79
+ margin-right: 32px;
80
+ }
81
+ .column:hover {
82
+ box-shadow: 0 4px 16px #0002;
83
+ }
84
+ .column-title {
85
+ font-weight: bold;
86
+ font-size: 18px;
87
+ margin-bottom: 10px;
88
+ color: #2d3a4b;
89
+ width: 100%;
90
+ text-align: center;
91
+ border-bottom: 1.5px solid #e0e6ef;
92
+ padding: 16px 0 8px 0;
93
+ background: #f7fafd;
94
+ border-top-left-radius: 14px;
95
+ border-top-right-radius: 14px;
96
+ box-sizing: border-box;
97
+ }
98
+ .box {
99
+ width: 120px;
100
+ min-height: 40px;
101
+ background: #e3eaf2;
102
+ border: 2px solid #7da0ca;
103
+ border-radius: 8px;
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ /* margin: 16px 0; */
108
+ font-size: 15px;
109
+ color: #2d3a4b;
110
+ position: relative;
111
+ z-index: 2;
112
+ box-shadow: 0 2px 6px #0001;
113
+ transition: box-shadow 0.2s, border-color 0.2s, background 0.2s;
114
+ cursor: pointer;
115
+ word-break: break-word;
116
+ white-space: normal;
117
+ overflow-wrap: break-word;
118
+ padding: 4px 8px;
119
+ text-align: center;
120
+ overflow: hidden;
121
+ }
122
+ .box.highlight {
123
+ /* Slightly brighten and bring to front, but keep original color */
124
+ filter: brightness(1.15);
125
+ box-shadow: 0 0 12px 2px #eab676, 0 2px 6px #0001;
126
+ opacity: 1 !important;
127
+ z-index: 10;
128
+ }
129
+ .box.related {
130
+ /* Gold color for related objects, slightly brightened, bring to front */
131
+ background: #eab676 !important;
132
+ border-color: #e28743 !important;
133
+ filter: brightness(1.10);
134
+ box-shadow: 0 0 8px 1px #e28743 !important, 0 2px 6px #eab676;
135
+ opacity: 1 !important;
136
+ z-index: 9;
137
+ }
138
+ .box.dimmed {
139
+ opacity: 0.20 !important;
140
+ filter: grayscale(0.25) brightness(0.80);
141
+ }
142
+ .feature-box {
143
+ background: #f7e6c7;
144
+ border-color: #e0b96a;
145
+ }
146
+ .dataset-box {
147
+ background: #d6f5e3;
148
+ border-color: #6ac7a0;
149
+ }
150
+ .fp-box {
151
+ background: #f2d6e6;
152
+ border-color: #c76aa0;
153
+ }
154
+ .ds-box {
155
+ background: #e3eaf2;
156
+ border-color: #7da0ca;
157
+ }
158
+ #svg_fs_i {
159
+ position: absolute;
160
+ left: 0;
161
+ top: 0;
162
+ width: 100%;
163
+ pointer-events: none;
164
+ z-index: 1000;
165
+ }
166
+ .svg-curve {
167
+ transition: filter 0.2s, stroke 0.2s, stroke-width 0.2s, opacity 0.2s;
168
+ filter: none;
169
+ }
170
+ .svg-curve.highlight {
171
+ /* Slightly brighten, keep original color, bring to front */
172
+ filter: brightness(1.15) drop-shadow(0 0 6px #8884);
173
+ stroke-width: 5 !important;
174
+ opacity: 1 !important;
175
+ }
176
+ .svg-curve.related {
177
+ filter: brightness(1.10) drop-shadow(0 0 4px #8882);
178
+ stroke-width: 4 !important;
179
+ opacity: 0.95 !important;
180
+ }
181
+ .svg-curve.dimmed {
182
+ opacity: 0.10 !important;
183
+ filter: grayscale(0.25) brightness(0.80);
184
+ }
185
+ </style>
186
+ <div class="mindmap-header-container">
187
+ <div class="mindmap-header-single" id="mindmap-header-single">
188
+ Repo: &nbsp;<span style="text-decoration: underline; text-decoration-color: #a3c0e0;">__REPO__</span>&nbsp; (Data Domain: &nbsp;<span style="text-decoration: underline; text-decoration-color: #a3c0e0;">__DATA_DOMAIN__</span>&nbsp;)
189
+ </div>
190
+ </div>
191
+ <div id="container_fs_i">
192
+ <div class="mindmap-inner">
193
+ <svg id="svg_fs_i"></svg>
194
+ <div class="column" id="data_sources_col_fs_i" style="margin-left: 20px;">
195
+ <div class="column-title">Data Sources</div>
196
+ </div>
197
+ <div class="column" id="feature_processes_col_fs_i">
198
+ <div class="column-title">Feature Processes</div>
199
+ </div>
200
+ <div class="column" id="feature_catalog_col_fs_i">
201
+ <div class="column-title">Feature Catalog</div>
202
+ </div>
203
+ <div class="column" id="dataset_catalog_col_fs_i" style="margin-right: 20px;">
204
+ <div class="column-title">Dataset Catalog</div>
205
+ </div>
206
+ </div>
207
+ <div id="mindmap-timestamp_fs_i" style="position: absolute; right: 24px; bottom: 8px; color: #666; font-size: 10.75px; font-family: 'Segoe UI', Arial, sans-serif; z-index: 2000; pointer-events: none; user-select: none; font-style: italic; font-weight: 600; letter-spacing: 0.1px;">
208
+ <span style="font-style: italic; font-weight: 600; font-size: 10.75px;">Mind map for Feature Store is generated at <span id="mindmap-timestamp-value_fs_i">__MINDMAP_TIMESTAMP__</span>.</span>
209
+ </div>
210
+ </div>
211
+ <script>
212
+ /*
213
+ ===================== MIND MAP DATA STRUCTURE =====================
214
+ The following variables are injected from Python and define the mind map:
215
+
216
+ 1. dataSources: Array of objects, each with 'id' and 'label'.
217
+ Example:
218
+ [
219
+ { id: "ds1_fs_xxx", label: "Data Source 1" },
220
+ { id: "ds2_fs_xxx", label: "Data Source 2" }
221
+ ]
222
+
223
+ 2. featureProcesses: Array of objects, each with 'id' and 'label'.
224
+ Example:
225
+ [
226
+ { id: "fp1_fs_xxx", label: "Feature Process 1" },
227
+ { id: "fp2_fs_xxx", label: "Feature Process 2" }
228
+ ]
229
+
230
+ 3. features: Array of objects, each with 'id' and 'label'.
231
+ Example:
232
+ [
233
+ { id: "f1_fs_xxx", label: "Feature 1" },
234
+ { id: "f2_fs_xxx", label: "Feature 2" }
235
+ ]
236
+
237
+ 4. datasets: Array of objects, each with 'id' and 'label'.
238
+ Example:
239
+ [
240
+ { id: "d1_fs_xxx", label: "Dataset 1" },
241
+ { id: "d2_fs_xxx", label: "Dataset 2" }
242
+ ]
243
+
244
+ 5. dataSourceMap: Object mapping dataSource id to array of featureProcess ids.
245
+ Example:
246
+ {
247
+ "ds1_fs_xxx": ["fp1_fs_xxx", "fp2_fs_xxx"],
248
+ "ds2_fs_xxx": ["fp2_fs_xxx"]
249
+ }
250
+
251
+ 6. featureProcessMap: Object mapping featureProcess id to array of feature ids.
252
+ Example:
253
+ {
254
+ "fp1_fs_xxx": ["f1_fs_xxx", "f2_fs_xxx"],
255
+ "fp2_fs_xxx": ["f2_fs_xxx", "f3_fs_xxx"]
256
+ }
257
+
258
+ 7. datasetFeatureMap: Object mapping dataset id to array of feature ids.
259
+ Example:
260
+ {
261
+ "d1_fs_xxx": ["f1_fs_xxx", "f2_fs_xxx"],
262
+ "d2_fs_xxx": ["f2_fs_xxx", "f3_fs_xxx"]
263
+ }
264
+
265
+ All ids must be unique and consistent across these variables. The '_fs_xxx' suffix is added for uniqueness.
266
+ ====================================================================
267
+ */
268
+ // --- Mind Map Data ---
269
+ var dataSources = __DATA_SOURCES__;
270
+ var featureProcesses = __FEATURE_PROCESSES__;
271
+ var features = __FEATURES__;
272
+ var datasets = __DATASETS__;
273
+ var dataSourceMap = __DATA_SOURCE_MAP__;
274
+ var featureProcessMap = __FEATURE_PROCESS_MAP__;
275
+ var datasetFeatureMap = __DATASET_FEATURE_MAP__;
276
+
277
+ // ...existing JS code (renderFeatureStoreMindMap, etc.)...
278
+ function renderFeatureStoreMindMap(dataSources, featureProcesses, features, datasets, dataSourceMap, featureProcessMap, datasetFeatureMap) {
279
+ // --- Helper: Create Box ---
280
+ function createBox(obj, cls) {
281
+ var div = document.createElement('div');
282
+ div.className = 'box ' + cls;
283
+ div.id = obj.id;
284
+ if (obj.invisible) {
285
+ div.style.opacity = '0';
286
+ div.style.pointerEvents = 'none';
287
+ div.innerHTML = '&nbsp;';
288
+ } else {
289
+ // Show only first 14 characters, add ellipsis if longer, show full label on hover
290
+ var span = document.createElement('span');
291
+ var label = obj.label || '';
292
+ if (label.length > 14) {
293
+ span.textContent = label.slice(0, 14) + '...';
294
+ } else {
295
+ span.textContent = label;
296
+ }
297
+ span.style.display = 'inline-block';
298
+ span.style.width = '100%';
299
+ span.style.whiteSpace = 'nowrap';
300
+ span.style.overflow = 'hidden';
301
+ span.style.textOverflow = 'ellipsis';
302
+ span.title = label;
303
+ div.appendChild(span);
304
+ }
305
+ return div;
306
+ }
307
+ // --- Declare all main DOM elements by ID at the top ---
308
+ var container_fs_v = document.getElementById('container_fs_i');
309
+ var svg_fs_v = document.getElementById('svg_fs_i');
310
+ var dsCol = document.getElementById('data_sources_col_fs_i');
311
+ var fpCol = document.getElementById('feature_processes_col_fs_i');
312
+ var fCol = document.getElementById('feature_catalog_col_fs_i');
313
+ var dCol = document.getElementById('dataset_catalog_col_fs_i');
314
+
315
+ // Helper: Add EMPTY label if needed
316
+ function addEmptyLabel(col) {
317
+ var emptyDiv = document.createElement('div');
318
+ emptyDiv.textContent = 'EMPTY';
319
+ emptyDiv.style.color = '#aaa';
320
+ emptyDiv.style.fontSize = '18px';
321
+ emptyDiv.style.fontWeight = 'bold';
322
+ emptyDiv.style.position = 'absolute';
323
+ emptyDiv.style.top = '50%';
324
+ emptyDiv.style.left = '50%';
325
+ emptyDiv.style.transform = 'translate(-50%, -50%)';
326
+ emptyDiv.style.pointerEvents = 'none';
327
+ emptyDiv.style.userSelect = 'none';
328
+ emptyDiv.className = 'empty-label';
329
+ col.appendChild(emptyDiv);
330
+ }
331
+
332
+ // Remove all children except column-title
333
+ [dsCol, fpCol, fCol, dCol].forEach(function(col) {
334
+ Array.from(col.children).forEach(function(child) {
335
+ if (!child.classList.contains('column-title')) col.removeChild(child);
336
+ });
337
+ });
338
+
339
+ // Helper to spread children vertically with equal space
340
+ function spreadChildren(col, items, boxClass) {
341
+ if (items.length === 0) {
342
+ addEmptyLabel(col);
343
+ return;
344
+ }
345
+ var title = col.querySelector('.column-title');
346
+ var titleHeight = title ? title.offsetHeight : 0;
347
+ var n = items.length;
348
+ var boxHeight = 40; // Should match .box height in CSS
349
+ var topMargin = 16; // Margin between title and first child
350
+ var bottomMargin = 24; // Margin between last child and bottom
351
+ var minGap = 30; // Minimum margin between child boxes
352
+ var colHeight = col.offsetHeight;
353
+ var availableHeight = colHeight - titleHeight - topMargin - bottomMargin;
354
+ var gap = n > 1 ? Math.max((availableHeight - n * boxHeight) / (n - 1), minGap) : (availableHeight - boxHeight) / 2;
355
+ var totalChildrenHeight = n * boxHeight + (n - 1) * gap;
356
+ // If gap is forced to minGap, center the group vertically
357
+ var startY = titleHeight + topMargin;
358
+ if (n > 1 && gap === minGap) {
359
+ var extraSpace = availableHeight - (n * boxHeight + (n - 1) * minGap);
360
+ startY += Math.max(extraSpace / 2, 0);
361
+ }
362
+ if (n === 1) {
363
+ startY += (availableHeight - boxHeight) / 2;
364
+ }
365
+ items.forEach(function(item, i) {
366
+ var box = createBox(item, boxClass);
367
+ box.style.position = 'absolute';
368
+ box.style.left = '50%';
369
+ box.style.transform = 'translateX(-50%)';
370
+ box.style.top = (startY + i * (boxHeight + gap)) + 'px';
371
+ col.appendChild(box);
372
+ });
373
+ col.style.position = 'relative';
374
+ }
375
+
376
+ spreadChildren(dsCol, dataSources, 'ds-box');
377
+ spreadChildren(fpCol, featureProcesses, 'fp-box');
378
+ spreadChildren(fCol, features, 'feature-box');
379
+ spreadChildren(dCol, datasets, 'dataset-box');
380
+
381
+ // --- Dynamically adjust all main columns to equal height ---
382
+ var minHeight = 500;
383
+ var boxHeight = 40;
384
+ var topMargin = 16;
385
+ var bottomMargin = 24;
386
+ var minGap = 30;
387
+ var titleHeight = dsCol.querySelector('.column-title').offsetHeight;
388
+ function getColumnRequiredHeight(numChildren) {
389
+ if (numChildren === 0) return minHeight;
390
+ // Height = title + topMargin + bottomMargin + children + gaps
391
+ return titleHeight + topMargin + bottomMargin + (numChildren * boxHeight) + ((numChildren - 1) * minGap);
392
+ }
393
+ var dsColHeight = getColumnRequiredHeight(dataSources.length);
394
+
395
+ var fpColHeight = getColumnRequiredHeight(featureProcesses.length);
396
+ var fColHeight = getColumnRequiredHeight(features.length);
397
+ var dColHeight = getColumnRequiredHeight(datasets.length);
398
+ var maxColHeight = Math.max(dsColHeight, fpColHeight, fColHeight, dColHeight, minHeight);
399
+
400
+ // Add extra bottom margin (e.g., 100px) to the container height
401
+ var containerExtraBottom = 100;
402
+ var containerHeight = maxColHeight + containerExtraBottom;
403
+
404
+ // Set all columns to the same height, and container to be larger
405
+ container_fs_v.style.height = containerHeight + 'px';
406
+ container_fs_v.style.minHeight = (minHeight + containerExtraBottom) + 'px';
407
+ svg_fs_v.setAttribute('height', containerHeight);
408
+ svg_fs_v.style.height = containerHeight + 'px';
409
+ dsCol.style.minHeight = dsCol.style.height = maxColHeight + 'px';
410
+ fpCol.style.minHeight = fpCol.style.height = maxColHeight + 'px';
411
+ fCol.style.minHeight = fCol.style.height = maxColHeight + 'px';
412
+ dCol.style.minHeight = dCol.style.height = maxColHeight + 'px';
413
+
414
+ function getEntryExitOffset(el, direction) {
415
+ const rect = el.getBoundingClientRect();
416
+ const parentRect = container_fs_v.getBoundingClientRect();
417
+ if (direction === 'right') {
418
+ return {
419
+ x: rect.right - parentRect.left,
420
+ y: rect.top - parentRect.top + rect.height/2
421
+ };
422
+ } else if (direction === 'left') {
423
+ return {
424
+ x: rect.left - parentRect.left,
425
+ y: rect.top - parentRect.top + rect.height/2
426
+ };
427
+ } else {
428
+ return {
429
+ x: rect.left - parentRect.left + rect.width/2,
430
+ y: rect.top - parentRect.top + rect.height/2
431
+ };
432
+ }
433
+ }
434
+ // --- Helper: Draw Curve ---
435
+ function drawCurve(svg, fromElem, toElem, color, fromDir, toDir, meta) {
436
+ if (!fromElem || !toElem) return;
437
+ const from = getEntryExitOffset(fromElem, fromDir);
438
+ const to = getEntryExitOffset(toElem, toDir);
439
+ const x1 = from.x;
440
+ const y1 = from.y;
441
+ const x2 = to.x;
442
+ const y2 = to.y;
443
+ const dx = Math.max(Math.abs(x2 - x1) * 0.3, 40);
444
+ const path = `M${x1},${y1} C${x1+dx},${y1} ${x2-dx},${y2} ${x2},${y2}`;
445
+ const curve = document.createElementNS('http://www.w3.org/2000/svg', 'path');
446
+ curve.setAttribute('d', path);
447
+ curve.setAttribute('stroke', color);
448
+ curve.setAttribute('stroke-width', '3');
449
+ curve.setAttribute('fill', 'none');
450
+ curve.setAttribute('opacity', '0.8');
451
+ curve.classList.add('svg-curve');
452
+ if (meta) {
453
+ curve.dataset.from = meta.from;
454
+ curve.dataset.to = meta.to;
455
+ curve.dataset.type = meta.type;
456
+ }
457
+ svg.appendChild(curve);
458
+ }
459
+ // --- Draw All Curves and Store Meta ---
460
+ function drawMindMapLinks() {
461
+ const svg = svg_fs_v;
462
+ while (svg.firstChild) svg.removeChild(svg.firstChild);
463
+ setTimeout(function() {
464
+ // DataSource to FeatureProcess
465
+ Object.keys(dataSourceMap).forEach(function(dsId) {
466
+ dataSourceMap[dsId].forEach(function(fpId) {
467
+ drawCurve(svg, document.getElementById(dsId), document.getElementById(fpId), '#7da0ca', 'right', 'left', {from: dsId, to: fpId, type: 'ds-fp'});
468
+ });
469
+ });
470
+ // FeatureProcess to Features
471
+ Object.keys(featureProcessMap).forEach(function(fpId) {
472
+ featureProcessMap[fpId].forEach(function(fId) {
473
+ drawCurve(svg, document.getElementById(fpId), document.getElementById(fId), '#c76aa0', 'right', 'left', {from: fpId, to: fId, type: 'fp-f'});
474
+ });
475
+ });
476
+ // Features to Datasets (note: map is dataset -> features)
477
+ Object.keys(datasetFeatureMap).forEach(function(dId) {
478
+ datasetFeatureMap[dId].forEach(function(fId) {
479
+ drawCurve(svg, document.getElementById(fId), document.getElementById(dId), '#6ac7a0', 'right', 'left', {from: fId, to: dId, type: 'f-d'});
480
+ });
481
+ });
482
+ attachHoverEvents();
483
+ }, 100);
484
+ }
485
+
486
+ // --- Highlight Logic ---
487
+ function clearHighlights() {
488
+ container_fs_v.querySelectorAll('.box').forEach(function(box) {
489
+ box.classList.remove('highlight', 'related', 'dimmed');
490
+ });
491
+ container_fs_v.querySelectorAll('.svg-curve').forEach(function(curve) {
492
+ curve.classList.remove('highlight', 'related', 'dimmed');
493
+ });
494
+ }
495
+
496
+ function attachHoverEvents() {
497
+ // Remove previous listeners by cloning
498
+ container_fs_v.querySelectorAll('.box').forEach(function(box) {
499
+ var newBox = box.cloneNode(true);
500
+ box.parentNode.replaceChild(newBox, box);
501
+ });
502
+
503
+ function dimUnrelated(relatedBoxIds, relatedCurveSelector) {
504
+ // Dim unrelated boxes
505
+ container_fs_v.querySelectorAll('.box').forEach(function(box) {
506
+ if (!relatedBoxIds.includes(box.id)) {
507
+ box.classList.add('dimmed');
508
+ }
509
+ });
510
+ // Dim unrelated curves
511
+ container_fs_v.querySelectorAll('.svg-curve').forEach(function(curve) {
512
+ if (!curve.matches(relatedCurveSelector)) {
513
+ curve.classList.add('dimmed');
514
+ }
515
+ });
516
+ }
517
+
518
+ // Data Sources
519
+ dataSources.forEach(function(ds) {
520
+ var el = document.getElementById(ds.id);
521
+ if (!el) return;
522
+ el.addEventListener('mouseenter', function() {
523
+ clearHighlights();
524
+ el.classList.add('highlight');
525
+ var relatedBoxIds = [ds.id];
526
+ (dataSourceMap[ds.id]||[]).forEach(function(fpId) {
527
+ var fpEl = document.getElementById(fpId);
528
+ if (fpEl) fpEl.classList.add('related');
529
+ relatedBoxIds.push(fpId);
530
+ container_fs_v.querySelectorAll('.svg-curve[data-from="'+ds.id+'"][data-to="'+fpId+'"]').forEach(function(curve) {
531
+ if (curve.dataset.type === 'ds-fp') curve.classList.add('highlight');
532
+ });
533
+ });
534
+ dimUnrelated(relatedBoxIds, '.svg-curve.highlight');
535
+ });
536
+ el.addEventListener('mouseleave', clearHighlights);
537
+ });
538
+ // Feature Processes
539
+ featureProcesses.forEach(function(fp) {
540
+ var el = document.getElementById(fp.id);
541
+ if (!el) return;
542
+ el.addEventListener('mouseenter', function() {
543
+ clearHighlights();
544
+ el.classList.add('highlight');
545
+ var relatedBoxIds = [fp.id];
546
+ // Related Data Sources
547
+ Object.keys(dataSourceMap).forEach(function(dsId) {
548
+ if ((dataSourceMap[dsId]||[]).includes(fp.id)) {
549
+ var dsEl = document.getElementById(dsId);
550
+ if (dsEl) dsEl.classList.add('related');
551
+ relatedBoxIds.push(dsId);
552
+ container_fs_v.querySelectorAll('.svg-curve[data-from="'+dsId+'"][data-to="'+fp.id+'"]').forEach(function(curve) {
553
+ if (curve.dataset.type === 'ds-fp') curve.classList.add('highlight');
554
+ });
555
+ }
556
+ });
557
+ // Related Features
558
+ (featureProcessMap[fp.id]||[]).forEach(function(fId) {
559
+ var fEl = document.getElementById(fId);
560
+ if (fEl) fEl.classList.add('related');
561
+ relatedBoxIds.push(fId);
562
+ container_fs_v.querySelectorAll('.svg-curve[data-from="'+fp.id+'"][data-to="'+fId+'"]').forEach(function(curve) {
563
+ if (curve.dataset.type === 'fp-f') curve.classList.add('highlight');
564
+ });
565
+ });
566
+ dimUnrelated(relatedBoxIds, '.svg-curve.highlight');
567
+ });
568
+ el.addEventListener('mouseleave', clearHighlights);
569
+ });
570
+ // Features
571
+ features.forEach(function(f) {
572
+ var el = document.getElementById(f.id);
573
+ if (!el) return;
574
+ el.addEventListener('mouseenter', function() {
575
+ clearHighlights();
576
+ el.classList.add('highlight');
577
+ var relatedBoxIds = [f.id];
578
+ // Related Feature Processes
579
+ Object.keys(featureProcessMap).forEach(function(fpId) {
580
+ if ((featureProcessMap[fpId]||[]).includes(f.id)) {
581
+ var fpEl = document.getElementById(fpId);
582
+ if (fpEl) fpEl.classList.add('related');
583
+ relatedBoxIds.push(fpId);
584
+ container_fs_v.querySelectorAll('.svg-curve[data-from="'+fpId+'"][data-to="'+f.id+'"]').forEach(function(curve) {
585
+ if (curve.dataset.type === 'fp-f') curve.classList.add('highlight');
586
+ });
587
+ }
588
+ });
589
+ // Related Datasets
590
+ Object.keys(datasetFeatureMap).forEach(function(dId) {
591
+ if ((datasetFeatureMap[dId]||[]).includes(f.id)) {
592
+ var dEl = document.getElementById(dId);
593
+ if (dEl) dEl.classList.add('related');
594
+ relatedBoxIds.push(dId);
595
+ container_fs_v.querySelectorAll('.svg-curve[data-from="'+f.id+'"][data-to="'+dId+'"]').forEach(function(curve) {
596
+ if (curve.dataset.type === 'f-d') curve.classList.add('highlight');
597
+ });
598
+ }
599
+ });
600
+ dimUnrelated(relatedBoxIds, '.svg-curve.highlight');
601
+ });
602
+ el.addEventListener('mouseleave', clearHighlights);
603
+ });
604
+ // Datasets
605
+ datasets.forEach(function(d) {
606
+ var el = document.getElementById(d.id);
607
+ if (!el) return;
608
+ el.addEventListener('mouseenter', function() {
609
+ clearHighlights();
610
+ el.classList.add('highlight');
611
+ var relatedBoxIds = [d.id];
612
+ // Related Features
613
+ (datasetFeatureMap[d.id]||[]).forEach(function(fId) {
614
+ var fEl = document.getElementById(fId);
615
+ if (fEl) fEl.classList.add('related');
616
+ relatedBoxIds.push(fId);
617
+ container_fs_v.querySelectorAll('.svg-curve[data-from="'+fId+'"][data-to="'+d.id+'"]').forEach(function(curve) {
618
+ if (curve.dataset.type === 'f-d') curve.classList.add('highlight');
619
+ });
620
+ });
621
+ dimUnrelated(relatedBoxIds, '.svg-curve.highlight');
622
+ });
623
+ el.addEventListener('mouseleave', clearHighlights);
624
+ });
625
+ }
626
+ drawMindMapLinks();
627
+
628
+ }
629
+ renderFeatureStoreMindMap(
630
+ dataSources,
631
+ featureProcesses,
632
+ features,
633
+ datasets,
634
+ dataSourceMap,
635
+ featureProcessMap,
636
+ datasetFeatureMap
637
+ );
638
+ </script>
639
+ """