Skip to content

Commit 9eb372a

Browse files
committed
Add logic to create calculated columns.
1 parent 340f1a4 commit 9eb372a

File tree

4 files changed

+127
-78
lines changed

4 files changed

+127
-78
lines changed

dss-plugin-visual-edit/python-lib/aggrid_utils.py

Lines changed: 108 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
"""
2-
AG Grid utility functions to generate column configuration for Dash AG Grid.
3-
"""
4-
5-
import logging
6-
from typing import Union
1+
from typing import List, Union
72

83
from pandas import DataFrame
94

105
from DataEditor import DataEditor
6+
from webapp.config.models import EditSchema
7+
118

9+
def _get_user_column_def_override(col_name: str, edit_schema: List[EditSchema]) -> dict:
10+
for col_def in edit_schema:
11+
if col_def.name == col_name:
12+
return col_def.aggrid_col_def
13+
return {}
1214

13-
def _get_column_aggrid_data_type(dku_col_type: str, dku_col_meaning: str) -> dict:
15+
16+
def _to_aggrid_data_type(dku_col_type: str, dku_col_meaning: str) -> dict:
1417
if dku_col_type in ["int", "bigint", "double", "float"]:
1518
return {"cellDataType": "number"}
1619
elif dku_col_type == "boolean":
@@ -26,7 +29,7 @@ def _get_column_aggrid_data_type(dku_col_type: str, dku_col_meaning: str) -> dic
2629
return {"cellDataType": "text"}
2730

2831

29-
def _get_column_aggrid_cell_editor(dku_col_type: str, dku_col_meaning: str) -> dict:
32+
def _to_aggrid_cell_editor(dku_col_type: str, dku_col_meaning: str) -> dict:
3033
if dku_col_type in ["int", "bigint", "double", "float"]:
3134
return {"cellEditor": "agNumericCellEditor"}
3235
elif dku_col_type == "boolean":
@@ -37,8 +40,10 @@ def _get_column_aggrid_cell_editor(dku_col_type: str, dku_col_meaning: str) -> d
3740
return {"cellEditor": "agLargeTextCellEditor", "cellEditorPopup": True}
3841

3942

40-
def _get_pk_column_def_aggrid(col_name: str, dku_col_type: str, dku_col_meaning: str, show_header_filter: bool) -> dict:
41-
ag_data_type = _get_column_aggrid_data_type(dku_col_type, dku_col_meaning)
43+
def _get_pk_column_def_aggrid(
44+
col_name: str, dku_col_type: str, dku_col_meaning: str, show_header_filter: bool, edit_schema: List[EditSchema]
45+
) -> dict:
46+
ag_data_type = _to_aggrid_data_type(dku_col_type, dku_col_meaning)
4247
col_def = {
4348
"field": col_name,
4449
"headerName": col_name,
@@ -47,13 +52,13 @@ def _get_pk_column_def_aggrid(col_name: str, dku_col_type: str, dku_col_meaning:
4752
"filter": show_header_filter,
4853
"filterParams": {"buttons": ["reset", "apply"], "closeOnApply": True},
4954
}
50-
return col_def | ag_data_type
55+
return col_def | ag_data_type | _get_user_column_def_override(col_name, edit_schema)
5156

5257

5358
def _get_display_column_def_aggrid(
54-
col_name: str, dku_col_type: str, dku_col_meaning: str, show_header_filter: bool
59+
col_name: str, dku_col_type: str, dku_col_meaning: str, show_header_filter: bool, edit_schema: List[EditSchema]
5560
) -> dict:
56-
ag_data_type = _get_column_aggrid_data_type(dku_col_type, dku_col_meaning)
61+
ag_data_type = _to_aggrid_data_type(dku_col_type, dku_col_meaning)
5762
col_def = {
5863
"field": col_name,
5964
"headerName": col_name,
@@ -62,14 +67,19 @@ def _get_display_column_def_aggrid(
6267
"filter": show_header_filter,
6368
"filterParams": {"buttons": ["reset", "apply"], "closeOnApply": True},
6469
}
65-
return col_def | ag_data_type
70+
return col_def | ag_data_type | _get_user_column_def_override(col_name, edit_schema)
6671

6772

6873
def _get_editable_column_def_aggrid(
69-
col_name: str, dku_col_type: str, dku_col_meaning: str, show_header_filter: bool, freeze_editable_columns: bool
74+
col_name: str,
75+
dku_col_type: str,
76+
dku_col_meaning: str,
77+
show_header_filter: bool,
78+
freeze_editable_columns: bool,
79+
edit_schema: List[EditSchema],
7080
) -> dict:
71-
ag_data_type = _get_column_aggrid_data_type(dku_col_type, dku_col_meaning)
72-
ag_cell_editor = _get_column_aggrid_cell_editor(dku_col_type, dku_col_meaning)
81+
ag_data_type = _to_aggrid_data_type(dku_col_type, dku_col_meaning)
82+
ag_cell_editor = _to_aggrid_cell_editor(dku_col_type, dku_col_meaning)
7383
# Alternate red shades for editable columns (even: deep red, odd: light red)
7484
col_def = {
7585
"field": col_name,
@@ -79,84 +89,125 @@ def _get_editable_column_def_aggrid(
7989
"filter": show_header_filter,
8090
"filterParams": {"buttons": ["reset", "apply"], "closeOnApply": True},
8191
}
82-
return col_def | ag_data_type | ag_cell_editor
92+
return col_def | ag_data_type | ag_cell_editor | _get_user_column_def_override(col_name, edit_schema)
8393

8494

8595
def _get_linked_record_column_def_aggrid(
8696
col_name: str, dku_col_type: str, dku_col_meaning: str, linked_ds_name: str, show_header_filter: bool
8797
) -> dict:
88-
ag_data_type = _get_column_aggrid_data_type(dku_col_type, dku_col_meaning)
98+
ag_data_type = _to_aggrid_data_type(dku_col_type, dku_col_meaning)
8999
return {
90100
"field": col_name,
91101
"headerName": col_name,
92102
"editable": True,
93103
"cellEditor": "agRichSelectCellEditor",
94104
"cellEditorParams": {
95-
# "values": [0, 1, 2, 3, 4, 5], # Placeholder values; in practice, this should be dynamically fetched
96-
# "values": { "function": "dkuLinkedRecordsCellEditor(params)" },
97105
"function": "dkuLinkedRecordsCellEditor(params)",
98-
# "allowTyping": True,
99-
# "filterList": True,
100-
# "highlightMatch": True
101106
},
102107
"linkedDatasetName": linked_ds_name,
103108
"filter": show_header_filter,
104109
"resizable": True,
105110
} | ag_data_type
106111

107112

108-
def get_columns_aggrid(de: DataEditor, show_header_filter=True, freeze_editable_columns=False):
109-
# Similar logic to Tabulator, but adapted for AG Grid
110-
linked_record_names = []
111-
if de.linked_records:
112-
try:
113-
linked_records_df = de.linked_records_df
114-
linked_record_names = linked_records_df.index.values.tolist()
115-
except Exception:
116-
logging.exception("Failed to get linked record names.")
117-
118-
ag_cols = []
119-
113+
def _get_pk_column_defs(de: DataEditor, edit_schema: List[EditSchema], show_header_filter: bool) -> List[dict]:
120114
ag_pk_cols = []
121-
for pk_col in de.primary_keys:
122-
col_type: str = de.schema_columns_df.loc[pk_col, "type"] # type: ignore
115+
for col_name in de.primary_keys:
116+
col_type: str = de.schema_columns_df.loc[col_name, "type"] # type: ignore
123117
col_meaning: str = (
124-
de.schema_columns_df.loc[pk_col, "meaning_id"] if "meaning_id" in de.schema_columns_df.columns else ""
118+
de.schema_columns_df.loc[col_name, "meaning_id"] if "meaning_id" in de.schema_columns_df.columns else ""
125119
) # type: ignore
126-
col_def = _get_pk_column_def_aggrid(pk_col, col_type, col_meaning, show_header_filter)
120+
col_def = _get_pk_column_def_aggrid(col_name, col_type, col_meaning, show_header_filter, edit_schema)
127121
ag_pk_cols.append(col_def)
122+
return ag_pk_cols
128123

129-
ag_cols.append({"headerName": "Row identification", "children": ag_pk_cols, "marryChildren": True})
130124

125+
def _get_display_column_defs(de: DataEditor, edit_schema: List[EditSchema], show_header_filter: bool) -> List[dict]:
131126
ag_display_cols = []
132-
for display_col in de.display_column_names:
133-
col_type: str = de.schema_columns_df.loc[display_col, "type"] # type: ignore
127+
for col_name in de.display_column_names:
128+
col_type: str = de.schema_columns_df.loc[col_name, "type"] # type: ignore
134129
col_meaning: str = (
135-
de.schema_columns_df.loc[display_col, "meaning"] if "meaning" in de.schema_columns_df.columns else ""
130+
de.schema_columns_df.loc[col_name, "meaning"] if "meaning" in de.schema_columns_df.columns else ""
136131
) # type: ignore
137-
col_def = _get_display_column_def_aggrid(display_col, col_type, col_meaning, show_header_filter)
132+
col_def = _get_display_column_def_aggrid(col_name, col_type, col_meaning, show_header_filter, edit_schema)
138133
ag_display_cols.append(col_def)
134+
return ag_display_cols
139135

140-
ag_cols.append({"headerName": "Contextual information", "children": ag_display_cols, "marryChildren": True})
141136

142-
schema_df = de.schema_columns_df
143-
for col_name in de.editable_column_names:
144-
col_type = schema_df.loc[col_name, "type"] # type: ignore
137+
def _get_editable_column_defs(
138+
de: DataEditor, edit_schema: List[EditSchema], show_header_filter: bool, freeze_editable_columns: bool
139+
) -> List[dict]:
140+
ag_editable_cols = []
141+
linked_record_names = de.linked_records_df.index.values.tolist()
142+
editable_column_names = set(de.editable_column_names) - set(linked_record_names)
143+
for col_name in editable_column_names:
144+
col_type: str = de.schema_columns_df.loc[col_name, "type"] # type: ignore
145145
col_meaning: str = (
146146
de.schema_columns_df.loc[col_name, "meaning"] if "meaning" in de.schema_columns_df.columns else ""
147147
) # type: ignore
148-
if col_name in linked_record_names:
149-
# Linked record column
148+
col_def = _get_editable_column_def_aggrid(
149+
col_name,
150+
col_type,
151+
col_meaning,
152+
show_header_filter,
153+
freeze_editable_columns,
154+
edit_schema,
155+
)
156+
ag_editable_cols.append(col_def)
157+
return ag_editable_cols
158+
159+
160+
def _get_linked_record_column_defs(
161+
de: DataEditor, edit_schema: List[EditSchema], show_header_filter: bool
162+
) -> List[dict]:
163+
ag_linked_record_cols = []
164+
if de.linked_records:
165+
linked_records_df = de.linked_records_df
166+
linked_record_names = linked_records_df.index.values.tolist()
167+
for col_name in linked_record_names:
168+
col_type: str = de.schema_columns_df.loc[col_name, "type"] # type: ignore
169+
col_meaning: str = (
170+
de.schema_columns_df.loc[col_name, "meaning"] if "meaning" in de.schema_columns_df.columns else ""
171+
) # type: ignore
150172
linked_ds_name: str = linked_records_df.loc[col_name, "ds_name"] # type: ignore
151173
col_def = _get_linked_record_column_def_aggrid(
152174
col_name, col_type, col_meaning, linked_ds_name, show_header_filter
153175
)
154-
ag_cols.append(col_def)
155-
continue
156-
col_def = _get_editable_column_def_aggrid(
157-
col_name, col_type, col_meaning, show_header_filter, freeze_editable_columns
158-
)
159-
ag_cols.append(col_def)
176+
ag_linked_record_cols.append(col_def)
177+
return ag_linked_record_cols
178+
179+
180+
def _get_extra_column_defs(de: DataEditor, edit_schema: List[EditSchema]) -> List[dict]:
181+
ag_extra_cols = []
182+
all_col_names = de.primary_keys + de.display_column_names + de.editable_column_names
183+
extra_col_defs = set([col.name for col in edit_schema]) - set(all_col_names)
184+
for extra_col_name in extra_col_defs:
185+
col_def = next((col.aggrid_col_def for col in edit_schema if col.name == extra_col_name), None)
186+
if col_def:
187+
ag_extra_cols.append(col_def)
188+
return ag_extra_cols
189+
190+
191+
def get_columns_aggrid(
192+
de: DataEditor, edit_schema: List[EditSchema], show_header_filter=True, freeze_editable_columns=False
193+
) -> List[dict]:
194+
ag_cols = []
195+
196+
pk_col_defs = _get_pk_column_defs(de, edit_schema, show_header_filter)
197+
ag_cols.append({"headerName": "Row identification", "children": pk_col_defs, "marryChildren": True})
198+
199+
display_col_defs = _get_display_column_defs(de, edit_schema, show_header_filter)
200+
ag_cols.append({"headerName": "Contextual information", "children": display_col_defs, "marryChildren": True})
201+
202+
editable_col_defs = _get_editable_column_defs(de, edit_schema, show_header_filter, freeze_editable_columns)
203+
ag_cols.extend(editable_col_defs)
204+
205+
linked_record_col_defs = _get_linked_record_column_defs(de, edit_schema, show_header_filter)
206+
ag_cols.extend(linked_record_col_defs)
207+
208+
extra_col_defs = _get_extra_column_defs(de, edit_schema)
209+
ag_cols.extend(extra_col_defs)
210+
160211
return ag_cols
161212

162213

dss-plugin-visual-edit/python-lib/webapp/config/loader.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
from __future__ import annotations
2+
23
import json
34
import logging
45
import os
5-
from typing import Any, List
6-
from dataiku.customwebapp import get_webapp_config
76
from json import load
87
from os import getenv
8+
from typing import Any, List
9+
10+
from dataiku.customwebapp import get_webapp_config
11+
912
from webapp.config.models import (
1013
Config,
1114
EditSchema,
12-
LinkedRecordInfo,
1315
LinkedRecord,
16+
LinkedRecordInfo,
1417
)
1518

1619

@@ -24,9 +27,7 @@ def __init__(self) -> None:
2427
self.__validate_original_dataset_name__(typed_config.original_dataset)
2528

2629
else:
27-
original_ds_name = self.__validate_original_dataset_name__(
28-
getenv("ORIGINAL_DATASET")
29-
)
30+
original_ds_name = self.__validate_original_dataset_name__(getenv("ORIGINAL_DATASET"))
3031
with open(f"../../webapp-settings/{original_ds_name}.json") as fp:
3132
dic_config = load(fp)
3233
typed_config = Config(**dic_config)
@@ -39,9 +40,7 @@ def __init__(self) -> None:
3940
self.freeze_editable_columns = typed_config.freeze_editable_columns
4041
self.group_column_names = typed_config.group_column_names
4142
self.linked_records_count = typed_config.linked_records_count
42-
self.linked_records = self.__get_linked_records__(
43-
dic_config, self.linked_records_count
44-
)
43+
self.linked_records = self.__get_linked_records__(dic_config, self.linked_records_count)
4544

4645
self.editschema_manual = self.__cast_editschema__(typed_config)
4746

@@ -82,9 +81,7 @@ def __get_project_key__(self) -> str:
8281
)
8382
return key
8483

85-
def __get_linked_records__(
86-
self, params, linked_records_count: int
87-
) -> List[LinkedRecord]:
84+
def __get_linked_records__(self, params, linked_records_count: int) -> List[LinkedRecord]:
8885
"""
8986
Get linked records from webapp parameters, as a list of dictionaries
9087
"""
@@ -102,10 +99,6 @@ def __get_linked_records__(
10299
if not ds_lookup_columns:
103100
ds_lookup_columns = []
104101
linked_records.append(
105-
LinkedRecord(
106-
LinkedRecordInfo(
107-
name, ds_name, ds_key, ds_label, ds_lookup_columns
108-
)
109-
)
102+
LinkedRecord(LinkedRecordInfo(name, ds_name, ds_key, ds_label, ds_lookup_columns))
110103
)
111104
return linked_records

dss-plugin-visual-edit/python-lib/webapp/config/models.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
from __future__ import annotations
2-
from pydantic import BaseModel
2+
33
from dataclasses import dataclass
44
from typing import Any, List
5-
from DatasetSQL import DatasetSQL
5+
66
from pandas import DataFrame
7+
from pydantic import BaseModel
8+
9+
from DatasetSQL import DatasetSQL
710

811

912
class EditSchema(BaseModel):
1013
name: str
11-
type: str
14+
aggrid_col_def: dict
1215

1316

1417
@dataclass(frozen=True)

dss-plugin-visual-edit/webapps/visual-edit/backend.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@
8181
)
8282

8383

84-
columns = get_columns_aggrid(de, webapp_config.show_header_filter, webapp_config.freeze_editable_columns)
84+
columns = get_columns_aggrid(
85+
de, webapp_config.editschema_manual, webapp_config.show_header_filter, webapp_config.freeze_editable_columns
86+
)
8587

8688
last_build_date_initial = ""
8789
last_build_date_ok = False

0 commit comments

Comments
 (0)