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
83from pandas import DataFrame
94
105from 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
5358def _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
6873def _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
8595def _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
0 commit comments