Source code for st_aggrid

from operator import index
import os
import streamlit.components.v1 as components
import pandas as pd
import numpy as np
import simplejson
import warnings
from dotenv import load_dotenv
import typing

from st_aggrid.grid_options_builder import GridOptionsBuilder
from st_aggrid.shared import GridUpdateMode, DataReturnMode, JsCode, walk_gridOptions
from numbers import Number

load_dotenv()

_RELEASE = os.getenv("AGGRID_RELEASE", "true").lower() == "true"

if not _RELEASE:
    print("WARNING: Running de development mode")
    _component_func = components.declare_component(
        "agGrid",
        url="http://localhost:3001",
    )
else:
    parent_dir = os.path.dirname(os.path.abspath(__file__))
    build_dir = os.path.join(parent_dir, "frontend", "build")
    _component_func = components.declare_component("agGrid", path=build_dir)


[docs]def AgGrid( dataframe: pd.DataFrame, gridOptions: typing.Dict = None, height: int = 400, width=None, fit_columns_on_grid_load: bool = False, update_mode: GridUpdateMode = GridUpdateMode.VALUE_CHANGED | GridUpdateMode.SELECTION_CHANGED, data_return_mode: DataReturnMode = "as_input", allow_unsafe_jscode: bool = False, enable_enterprise_modules: bool = False, license_key: str = None, try_to_convert_back_to_original_types: bool = True, conversion_errors: str = "coerce", reload_data: bool = False, theme: str = "light", key: typing.Any = None, custom_css=None, **default_column_parameters, ) -> typing.Dict: """ Render the Grid Component using the ag-Grid JavaScript library and a dataframe. Args: dataframe: The underlying dataframe to be displayed in the grid. gridOption: A dictionary of options for ag-grid. Documentation on http://www.ag-grid.com If None default grid options will be created with GridOptionsBuilder.from_dataframe() call. height: The grid height, by default 400 width: .. deprecated:: 0.2.0 The grid width, by default None fit_columns_on_grid_load: Automatically fit columns to the grid width. Defaults to False. update_mode: Defines how the grid will send results back to streamlit. must be either a string, one or a bitwise combination of: + GridUpdateMode.NO_UPDATE + GridUpdateMode.MANUAL + GridUpdateMode.VALUE_CHANGED + GridUpdateMode.SELECTION_CHANGED + GridUpdateMode.FILTERING_CHANGED + GridUpdateMode.SORTING_CHANGED + GridUpdateMode.MODEL_CHANGED .. note:: When using *GridUpdateMode.MANUAL* a save button will be drawn on top of grid. .. note:: modes can be combined with bitwise OR operator *|* for instance: GridUpdateMode = VALUE_CHANGED | SELECTION_CHANGED | FILTERING_CHANGED | SORTING_CHANGED Defaults to GridUpdateMode.VALUE_CHANGED | SELECTION_CHANGED. data_return_mode: Defines how the data will be retrieved from components client side. One of: + DataReturnMode.AS_INPUT -> Returns grid data as inputed. Includes cell editions + DataReturnMode.FILTERED -> Returns filtered grid data, maintains input order + DataReturnMode.FILTERED_AND_SORTED -> Returns grid data filtered and sorted Defaults to DataReturnMode.AS_INPUT. allow_unsafe_jscode: Allows javascript code to be injected in gridOptions. Defaults to False. enable_enterprise_modules: Loads Ag-Grid enterprise modules (check licensing). Defaults to False. license_key: License key for enterprise modules. Defaults to None. try_to_convert_back_to_original_types: Attempts to convert back to original data types. Defaults to True. conversion_errors: Behaviour when conversion fails. One of: + 'raise' -> invalid parsing will raise an exception. + 'coerce' -> then invalid parsing will be set as NaT/NaN. + 'ignore' -> invalid parsing will return the input. Defaults to 'coerce'. reload_data: Force AgGrid to reload data using api calls. Should be false on most use cases. Defaults to False. theme: Theme used by ag-grid. One of: + 'streamlit' -> follows default streamlit colors + 'light' -> ag-grid balham-light theme + 'dark' -> ag-grid balham-dark theme + 'blue' -> ag-grid blue theme + 'fresh' -> ag-grid fresh theme + 'material' -> ag-grid material theme Defaults to 'light'. key (typing.Any, optional): Streamlit key argument. Check streamlit's documentation. Defaults to None. For convenience, additional keyword arguments will be merged to gridOptions defaultColDef. Returns: dict: A dictionary with members: data -> grid's data, including edited values. selected -> list of selected rows """ if width: warnings.warn( "DEPRECATION Warning: width parameter is deprecated and will be removed on next version." ) response = {} response["data"] = dataframe response["selected_rows"] = [] # basic numpy types of dataframe frame_dtypes = dict(zip(dataframe.columns, (t.kind for t in dataframe.dtypes))) # if no gridOptions is passed, builds a default one. if gridOptions == None: gb = GridOptionsBuilder.from_dataframe(dataframe, **default_column_parameters) gridOptions = gb.build() def get_row_data(df): def cast_to_serializable(value): if isinstance(value, pd.DataFrame): return get_row_data(value) isoformat = getattr(value, 'isoformat', None) if ((isoformat) and callable(isoformat)): return isoformat() elif isinstance(value, Number): if (np.isnan(value) or np.isinf(value)): return value.__str__() return value else: return value.__str__() json_frame = df.applymap(cast_to_serializable) row_data = json_frame.to_dict(orient="records") row_data = simplejson.dumps(row_data, ignore_nan=True) return row_data row_data = get_row_data(dataframe) if allow_unsafe_jscode: walk_gridOptions( gridOptions, lambda v: v.js_code if isinstance(v, JsCode) else v ) _available_themes = ["streamlit", "light", "dark", "blue", "fresh", "material"] if (not isinstance(theme, str)) or (not theme in _available_themes): raise ValueError( f"{theme} is not valid. Available options: {_available_themes}" ) try: if not isinstance(data_return_mode, (str, DataReturnMode)): raise ValueError(f"{data_return_mode} is not valid.") if isinstance(data_return_mode, str): data_return_mode = DataReturnMode[data_return_mode.upper()] except: raise ValueError(f"{data_return_mode} is not valid.") try: if not isinstance(update_mode, (str, GridUpdateMode)): raise ValueError(f"{update_mode} is not valid.") if isinstance(update_mode, str): update_mode = GridUpdateMode[update_mode.upper()] except: raise ValueError(f"{data_return_mode} is not valid.") custom_css = custom_css or dict() try: component_value = _component_func( gridOptions=gridOptions, row_data=row_data, height=height, width=width, fit_columns_on_grid_load=fit_columns_on_grid_load, update_mode=update_mode, data_return_mode=data_return_mode, frame_dtypes=frame_dtypes, allow_unsafe_jscode=allow_unsafe_jscode, enable_enterprise_modules=enable_enterprise_modules, license_key=license_key, default=None, reload_data=reload_data, theme=theme, custom_css=custom_css, key=key ) except components.components.MarshallComponentException as ex: # a more complete error message. args = list(ex.args) args[ 0 ] += ". If you're using custom JsCode objects on gridOptions, ensure that allow_unsafe_jscode is True." ex = components.components.MarshallComponentException(*args) raise (ex) if component_value: if isinstance(component_value, str): component_value = simplejson.loads(component_value) frame = pd.DataFrame(component_value["rowData"]) original_types = component_value["originalDtypes"] if not frame.empty: # maybe this is not the best solution. Should it store original types? What happens when grid pivots? if try_to_convert_back_to_original_types: numeric_columns = { k: v for k, v in original_types.items() if v in ["i", "u", "f"] } if numeric_columns: frame.loc[:, numeric_columns] = frame.loc[:, numeric_columns].apply( pd.to_numeric, errors=conversion_errors ) text_columns = { k: v for k, v in original_types.items() if v in ["O", "S", "U"] } if text_columns: frame.loc[:, text_columns] = frame.loc[:, text_columns].astype(str) date_columns = {k: v for k, v in original_types.items() if v in ["M"]} if date_columns: frame.loc[:, date_columns] = frame.loc[:, date_columns].apply( pd.to_datetime, errors=conversion_errors ) timedelta_columns = { k: v for k, v in original_types.items() if v in ["m"] } if timedelta_columns: def cast_to_timedelta(s): try: return pd.Timedelta(s) except: return s frame.loc[:, timedelta_columns] = frame.loc[ :, timedelta_columns ].apply(cast_to_timedelta) response["data"] = frame response["selected_rows"] = component_value["selectedRows"] return response