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.
- teradataml/README.md +210 -0
- teradataml/__init__.py +1 -1
- teradataml/_version.py +1 -1
- teradataml/analytics/analytic_function_executor.py +162 -76
- teradataml/analytics/byom/__init__.py +1 -1
- teradataml/analytics/json_parser/__init__.py +2 -0
- teradataml/analytics/json_parser/analytic_functions_argument.py +95 -2
- teradataml/analytics/json_parser/metadata.py +22 -4
- teradataml/analytics/sqle/DecisionTreePredict.py +3 -2
- teradataml/analytics/sqle/NaiveBayesPredict.py +3 -2
- teradataml/analytics/sqle/__init__.py +3 -0
- teradataml/analytics/utils.py +4 -1
- teradataml/automl/__init__.py +2369 -464
- teradataml/automl/autodataprep/__init__.py +15 -0
- teradataml/automl/custom_json_utils.py +184 -112
- teradataml/automl/data_preparation.py +113 -58
- teradataml/automl/data_transformation.py +154 -53
- teradataml/automl/feature_engineering.py +113 -53
- teradataml/automl/feature_exploration.py +548 -25
- teradataml/automl/model_evaluation.py +260 -32
- teradataml/automl/model_training.py +399 -206
- teradataml/clients/auth_client.py +2 -2
- teradataml/common/aed_utils.py +11 -2
- teradataml/common/bulk_exposed_utils.py +4 -2
- teradataml/common/constants.py +62 -2
- teradataml/common/garbagecollector.py +50 -21
- teradataml/common/messagecodes.py +47 -2
- teradataml/common/messages.py +19 -1
- teradataml/common/sqlbundle.py +23 -6
- teradataml/common/utils.py +116 -10
- teradataml/context/aed_context.py +16 -10
- teradataml/data/Employee.csv +5 -0
- teradataml/data/Employee_Address.csv +4 -0
- teradataml/data/Employee_roles.csv +5 -0
- teradataml/data/JulesBelvezeDummyData.csv +100 -0
- teradataml/data/byom_example.json +5 -0
- teradataml/data/creditcard_data.csv +284618 -0
- teradataml/data/docs/byom/docs/ONNXSeq2Seq.py +255 -0
- teradataml/data/docs/sqle/docs_17_10/NGramSplitter.py +1 -1
- teradataml/data/docs/sqle/docs_17_20/NGramSplitter.py +1 -1
- teradataml/data/docs/sqle/docs_17_20/TextParser.py +1 -1
- teradataml/data/jsons/byom/ONNXSeq2Seq.json +287 -0
- teradataml/data/jsons/sqle/20.00/AI_AnalyzeSentiment.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_AskLLM.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_DetectLanguage.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_ExtractKeyPhrases.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_MaskPII.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_RecognizeEntities.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_RecognizePIIEntities.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_TextClassifier.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_TextEmbeddings.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_TextSummarize.json +3 -7
- teradataml/data/jsons/sqle/20.00/AI_TextTranslate.json +3 -7
- teradataml/data/jsons/sqle/20.00/TD_API_AzureML.json +151 -0
- teradataml/data/jsons/sqle/20.00/TD_API_Sagemaker.json +182 -0
- teradataml/data/jsons/sqle/20.00/TD_API_VertexAI.json +183 -0
- teradataml/data/load_example_data.py +29 -11
- teradataml/data/payment_fraud_dataset.csv +10001 -0
- teradataml/data/teradataml_example.json +67 -0
- teradataml/dataframe/copy_to.py +714 -54
- teradataml/dataframe/dataframe.py +1153 -33
- teradataml/dataframe/dataframe_utils.py +8 -3
- teradataml/dataframe/functions.py +168 -1
- teradataml/dataframe/setop.py +4 -1
- teradataml/dataframe/sql.py +141 -9
- teradataml/dbutils/dbutils.py +470 -35
- teradataml/dbutils/filemgr.py +1 -1
- teradataml/hyperparameter_tuner/optimizer.py +456 -142
- teradataml/lib/aed_0_1.dll +0 -0
- teradataml/lib/libaed_0_1.dylib +0 -0
- teradataml/lib/libaed_0_1.so +0 -0
- teradataml/lib/libaed_0_1_aarch64.so +0 -0
- teradataml/scriptmgmt/UserEnv.py +234 -34
- teradataml/scriptmgmt/lls_utils.py +43 -17
- teradataml/sdk/_json_parser.py +1 -1
- teradataml/sdk/api_client.py +9 -6
- teradataml/sdk/modelops/_client.py +3 -0
- teradataml/series/series.py +12 -7
- teradataml/store/feature_store/constants.py +601 -234
- teradataml/store/feature_store/feature_store.py +2886 -616
- teradataml/store/feature_store/mind_map.py +639 -0
- teradataml/store/feature_store/models.py +5831 -214
- teradataml/store/feature_store/utils.py +390 -0
- teradataml/table_operators/table_operator_util.py +1 -1
- teradataml/table_operators/templates/dataframe_register.template +6 -2
- teradataml/table_operators/templates/dataframe_udf.template +6 -2
- teradataml/utils/docstring.py +527 -0
- teradataml/utils/dtypes.py +93 -0
- teradataml/utils/internal_buffer.py +2 -2
- teradataml/utils/utils.py +41 -2
- teradataml/utils/validators.py +694 -17
- {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/METADATA +213 -2
- {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/RECORD +96 -81
- {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/WHEEL +0 -0
- {teradataml-20.0.0.6.dist-info → teradataml-20.0.0.7.dist-info}/top_level.txt +0 -0
- {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: <span style="text-decoration: underline; text-decoration-color: #a3c0e0;">__REPO__</span> (Data Domain: <span style="text-decoration: underline; text-decoration-color: #a3c0e0;">__DATA_DOMAIN__</span> )
|
|
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 = ' ';
|
|
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
|
+
"""
|