Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preconfig conditional format #1259

Merged
merged 26 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
201b9f7
mitosheet: pre-config-formatting
aarondr77 Mar 10, 2024
f6e7e52
Merge branch 'better-default-number-formatting-1' into preconfig-cond…
aarondr77 Mar 10, 2024
3f62cf8
mitosheet: update app
aarondr77 Mar 10, 2024
63b4970
mitosheet: move column definitions to preprocessing
aarondr77 Mar 11, 2024
73b32f2
mitosheet: fix types
aarondr77 Mar 12, 2024
9171fbd
mitosheet: fix up tests
aarondr77 Mar 12, 2024
62b82e6
mitosheet: create new preprocessing step type
aarondr77 Mar 12, 2024
4043cc5
mitosheet: create sample app for testing
aarondr77 Mar 12, 2024
1dbfa8e
mitosheet: add basic test
aarondr77 Mar 12, 2024
f8a7ca8
mitosheet: create more tests
aarondr77 Mar 12, 2024
932fb35
mitosheet: fix types for python 3.6
aarondr77 Mar 12, 2024
a76069c
mitosheet: move out of preprocessing steps
aarondr77 Mar 12, 2024
4526f4e
mitosheet: new list for each dataframe
aarondr77 Mar 12, 2024
c612824
mitosheet: more error handling
aarondr77 Mar 12, 2024
ab73c09
mitosheet: more error handling and testing
aarondr77 Mar 12, 2024
174c349
mitosheet: add more tests
aarondr77 Mar 12, 2024
a48e878
mitosheet: cleanup
aarondr77 Mar 12, 2024
9e9f99b
mitosheet: fix all mypy issues
aarondr77 Mar 12, 2024
786f996
trymito: add back test
aarondr77 Mar 12, 2024
e1e548b
mitosheet: fix types
aarondr77 Mar 12, 2024
a4cfb21
mitosheet: fix failing streamlit test
aarondr77 Mar 12, 2024
d790393
mitosheet: tiny cleanup
aarondr77 Mar 13, 2024
ca0e964
Merge branch 'dev' into preconfig-conditional-format
aarondr77 Mar 13, 2024
c9b7923
mitosheet: paramaterize tests
aarondr77 Mar 13, 2024
4b72aee
mitosheet: address review
aarondr77 Mar 13, 2024
a487a74
mitosheet: fix failing pytest
aarondr77 Mar 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,11 @@ def get_table_styles_code(state: State, sheet_index: int) -> Optional[str]:

def get_conditional_format_code_list(state: State, sheet_index: int) -> Tuple[Optional[List[str]], Optional[bool]]:
"""Returns all the code to set the conditional formats"""
print('df_names: ', state.df_names)
print('sheet_index: ', sheet_index)
df_name = state.df_names[sheet_index]
df = state.dfs[sheet_index]

conditional_formats = state.df_formats[sheet_index]['conditional_formats']

# We get the conditional formatting results, and we filter out any columns that are
Expand Down Expand Up @@ -253,6 +256,8 @@ def check_conditional_filters_have_filter_condition_that_requires_whole_datafram

def get_dataframe_format_code(state: State, sheet_index: int) -> Tuple[Optional[str], Optional[bool]]:
"""Returns all the code to set the df_formatting on the dataframe from the state."""
print(state.df_names)
print(sheet_index)
df_name = state.df_names[sheet_index]
df = state.dfs[sheet_index]

Expand Down
8 changes: 6 additions & 2 deletions mitosheet/mitosheet/mito_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from mitosheet.steps_manager import StepsManager
from mitosheet.telemetry.telemetry_utils import (log, log_event_processed,
telemetry_turned_on)
from mitosheet.types import CodeOptions, MitoTheme, ParamMetadata
from mitosheet.types import CodeOptions, ColumnDefinintion, ConditionalFormat, MitoTheme, ParamMetadata
from mitosheet.updates.replay_analysis import REPLAY_ANALYSIS_UPDATE
from mitosheet.user.create import try_create_user_json_file
from mitosheet.user.db import USER_JSON_PATH, get_user_field
Expand Down Expand Up @@ -57,6 +57,7 @@ def __init__(
user_defined_importers: Optional[List[Callable]]=None,
user_defined_editors: Optional[List[Callable]]=None,
code_options: Optional[CodeOptions]=None,
column_definitions: Optional[List[ColumnDefinintion]]=None,
theme: Optional[MitoTheme]=None,
):
"""
Expand Down Expand Up @@ -92,7 +93,9 @@ def __init__(

if not os.path.exists(import_folder):
raise ValueError(f"Import folder {import_folder} does not exist. Please change the file path or create the folder.")


print(column_definitions)

# Set up the state container to hold private widget state
self.steps_manager = StepsManager(
args,
Expand All @@ -104,6 +107,7 @@ def __init__(
user_defined_importers=all_custom_importers,
user_defined_editors=user_defined_editors,
code_options=code_options,
column_definitions=column_definitions,
theme=theme
)

Expand Down
7 changes: 6 additions & 1 deletion mitosheet/mitosheet/preprocessing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""

from typing import List, Type
from mitosheet.preprocessing.preprocess_set_column_definitions import SetColumnDefininitionsPreprocessStepPerformer
from mitosheet.preprocessing.preprocess_step_performer import PreprocessStepPerformer
from mitosheet.preprocessing.preprocess_convert_to_dataframe import ConvertToDataframePreprocessStepPerformer
from mitosheet.preprocessing.preprocess_check_args_type import CheckArgsTypePreprocessStepPerformer
Expand All @@ -18,7 +19,7 @@

# NOTE: These should be in the order you want to apply them to the arguments,
# as they are run in a linear order
PREPROCESS_STEP_PERFORMERS: List[Type[PreprocessStepPerformer]] = [
DATAFRAME_CREATION_PREPROCESS_STEP_PERFORMERS: List[Type[PreprocessStepPerformer]] = [
# First, we make sure all the args are the right type
CheckArgsTypePreprocessStepPerformer,
# Then, we copy the args to make sure we don't change them accidently
Expand All @@ -27,3 +28,7 @@
ConvertToDataframePreprocessStepPerformer,
]

NON_DATAFRAME_CREATION_PREPROCESS_STEP_PERFORMERS: List[Type[PreprocessStepPerformer]] = [
SetColumnDefininitionsPreprocessStepPerformer,
]

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def preprocess_step_type(cls) -> str:
return 'check_args_type'

@classmethod
def execute(cls, args: Collection[Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:
def execute(cls, args: Collection[Any], kwargs: Dict[str, Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:
# We first validate all the parameters as either dataframes or strings
# but we also allow users to pass None values, which we just ignore (this
# makes variable number of inputs to the sheet possible).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def preprocess_step_type(cls) -> str:
return 'convert_to_dataframe'

@classmethod
def execute(cls, args: Collection[Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:
def execute(cls, args: Collection[Any], kwargs: Dict[str, Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:
df_args: List[pd.DataFrame] = []
df_names: List[str] = []

Expand Down
2 changes: 1 addition & 1 deletion mitosheet/mitosheet/preprocessing/preprocess_copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def preprocess_step_type(cls) -> str:
return 'copy'

@classmethod
def execute(cls, args: Collection[Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:
def execute(cls, args: Collection[Any], kwargs: Dict[str, Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:

new_args = []
for arg in args:
Expand Down
112 changes: 112 additions & 0 deletions mitosheet/mitosheet/preprocessing/preprocess_set_column_definitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python
# coding: utf-8

# Copyright (c) Saga Inc.
# Distributed under the terms of the GPL License.

from io import StringIO
from os.path import basename, normpath
import re
from typing import Any, Collection, Dict, List, Optional, Tuple

import pandas as pd

from mitosheet.code_chunks.step_performers.import_steps.simple_import_code_chunk import \
generate_read_csv_code
from mitosheet.preprocessing.preprocess_step_performer import \
PreprocessStepPerformer
from mitosheet.telemetry.telemetry_utils import log
from mitosheet.transpiler.transpile_utils import get_column_header_as_transpiled_code, get_column_header_list_as_transpiled_code, get_str_param_name
from mitosheet.types import ColumnDefinintion, ColumnDefinitionConditionalFormats, ConditionalFormat, DataframeFormat, StepsManagerType
from mitosheet.utils import get_valid_dataframe_name

def is_valid_hex_color(color: str) -> bool:

if not color.startswith('#'):
return False

match = re.search(r'^#(?:[0-9a-fA-F]{3}){1,2}$', color)
return match is not None


class SetColumnDefininitionsPreprocessStepPerformer(PreprocessStepPerformer):
"""
This preprocessing step is responsible for converting
all of the args to dataframes.

If it fails to convert any of the arguments to a dataframe,
then it will throw an error.
"""

@classmethod
def preprocess_step_version(cls) -> int:
return 1

@classmethod
def preprocess_step_type(cls) -> str:
return 'set_column_definitions'

@classmethod
def execute(cls, args: Collection[Any], kwargs: Dict[str, Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:

column_definitions: List[ColumnDefinintion] = kwargs['column_definitions'] if 'column_definitions' in kwargs else None

if column_definitions is None:
# If no column_definitions are provided, end early
return args, None, {}

df_formats = []

for sheetIndex in range(len(column_definitions)):

df_format: DataframeFormat = {
'columns': {},
'headers': {},
'rows': {'even': {}, 'odd': {}},
'border': {},
'conditional_formats': []
}

conditional_formats = []
for column_defintion in column_definitions:
conditional_formats_list: List[ColumnDefinitionConditionalFormats] = column_defintion['conditional_formats']
for conditional_format in conditional_formats_list:

font_color = conditional_format.get('font_color', None)
background_color = conditional_format.get('background_color', None)

if font_color is None and background_color is None:
raise ValueError(f"column_definititon has invalid conditional_format rules. It must set the font_color, background_color, or both.")

invalid_hex_color_error_message = "The {variable} {color} set in column_definititon is not a valid hex color. It should start with '#' and be followed by the letters from a-f, A-F and/or digits from 0-9. The length of the hexadecimal color code should be either 6 or 3, excluding '#' symbol"
if font_color and not is_valid_hex_color(font_color):
raise ValueError(invalid_hex_color_error_message.format(variable="font_color", color=font_color))

# Validate a string is a hex value for a color
if background_color and not is_valid_hex_color(background_color):
raise ValueError(invalid_hex_color_error_message.format(variable="background_color", color=background_color))

new_conditional_format: ConditionalFormat = {
'format_uuid': 'preset_conditional_format',
'columnIDs': column_defintion['columns'],
'filters': conditional_format['filters'],
'invalidFilterColumnIDs': [],
'color': font_color,
'backgroundColor': conditional_format['background_color']
}

conditional_formats.append(new_conditional_format)

df_format['conditional_formats'] = conditional_formats
df_formats.append(df_format)

return args, None, {
'df_formats': df_formats,
}

@classmethod
def transpile(cls, steps_manager: StepsManagerType, execution_data: Optional[Dict[str, Any]]) -> Tuple[List[str], List[str]]:
"""
We don't transpile anything here because we let the transpile funciton handle dataframe formatting separetly
"""
return [], []
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def preprocess_step_type(cls) -> str:

@classmethod
@abstractmethod
def execute(cls, args: Collection[Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:
def execute(cls, args: Collection[Any], kwargs: Dict[str, Any]) -> Tuple[List[Any], Optional[List[str]], Optional[Dict[str, Any]]]:
"""
Execute always returns the new list of arguments, as well as the names of these arguements, as well as execution_data
for this preprocess step.
Expand Down
3 changes: 3 additions & 0 deletions mitosheet/mitosheet/pro/conditional_formatting_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def get_conditonal_formatting_result(
formatted_result: ConditionalFormattingCellResults = dict()

for conditional_format in conditional_formatting_rules:
print(conditional_format)
try:

format_uuid = conditional_format["format_uuid"]
Expand Down Expand Up @@ -52,7 +53,9 @@ def get_conditonal_formatting_result(
# are sent to the frontend
json_index = json.dumps(index, cls=NpEncoder)
formatted_result[column_id][json_index] = {'backgroundColor': backgroundColor, 'color': color}

except Exception as e:
print("IN THIS EXCEPTION")
if format_uuid not in invalid_conditional_formats:
invalid_conditional_formats[format_uuid] = []
invalid_conditional_formats[format_uuid].append(column_id)
Expand Down
2 changes: 1 addition & 1 deletion mitosheet/mitosheet/step_performers/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,5 +260,5 @@ def check_filters_contain_condition_that_needs_full_df(filters: List[Union[Filte
filter_group: FilterGroup = filter_or_group #type: ignore
if check_filters_contain_condition_that_needs_full_df(filter_group["filters"]):
return True

return False
29 changes: 22 additions & 7 deletions mitosheet/mitosheet/steps_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
from mitosheet.enterprise.mito_config import MitoConfig
from mitosheet.enterprise.telemetry.mito_log_uploader import MitoLogUploader
from mitosheet.experiments.experiment_utils import get_current_experiment
from mitosheet.pro.conditional_formatting_utils import get_conditonal_formatting_result
from mitosheet.step_performers.column_steps.set_column_formula import get_user_defined_sheet_function_objects
from mitosheet.step_performers.import_steps.dataframe_import import DataframeImportStepPerformer
from mitosheet.step_performers.import_steps.excel_range_import import ExcelRangeImportStepPerformer
from mitosheet.step_performers.user_defined_import import UserDefinedImportStepPerformer
from mitosheet.telemetry.telemetry_utils import log
from mitosheet.preprocessing import PREPROCESS_STEP_PERFORMERS
from mitosheet.preprocessing import DATAFRAME_CREATION_PREPROCESS_STEP_PERFORMERS, NON_DATAFRAME_CREATION_PREPROCESS_STEP_PERFORMERS
from mitosheet.saved_analyses.save_utils import get_analysis_exists
from mitosheet.state import State
from mitosheet.step import Step
Expand All @@ -35,7 +36,7 @@
SnowflakeImportStepPerformer
from mitosheet.transpiler.transpile import transpile
from mitosheet.transpiler.transpile_utils import get_default_code_options
from mitosheet.types import CodeOptions, MitoTheme, ParamMetadata
from mitosheet.types import CodeOptions, ColumnDefinintion, ConditionalFormat, DataframeFormat, MitoTheme, ParamMetadata
from mitosheet.updates import UPDATES
from mitosheet.user.utils import is_enterprise, is_running_test
from mitosheet.utils import NpEncoder, dfs_to_array_for_json, get_new_id, is_default_df_names
Expand Down Expand Up @@ -187,6 +188,7 @@ def __init__(
user_defined_importers: Optional[List[Callable]]=None,
user_defined_editors: Optional[List[Callable]]=None,
code_options: Optional[CodeOptions]=None,
column_definitions: Optional[List[ColumnDefinintion]]=None,
theme: Optional[MitoTheme]=None,
):
"""
Expand Down Expand Up @@ -217,15 +219,25 @@ def __init__(
for arg in args
]

self.original_kwargs = {
'column_definitions': column_definitions
}

# Then, we go through the process of actually preprocessing the args
# saving any data that we need to transpilate it later this
self.preprocess_execution_data = {}
df_names = None
for preprocess_step_performers in PREPROCESS_STEP_PERFORMERS:
args, df_names, execution_data = preprocess_step_performers.execute(args)
for dataframe_creation_preprocess_step_performer in DATAFRAME_CREATION_PREPROCESS_STEP_PERFORMERS:
args, df_names, execution_data = dataframe_creation_preprocess_step_performer.execute(args, self.original_kwargs)
self.preprocess_execution_data[
dataframe_creation_preprocess_step_performer.preprocess_step_type()
] = execution_data

for dataframe_creation_preprocess_step_performer in NON_DATAFRAME_CREATION_PREPROCESS_STEP_PERFORMERS:
_, _, execution_data = dataframe_creation_preprocess_step_performer.execute(args, self.original_kwargs)
self.preprocess_execution_data[
preprocess_step_performers.preprocess_step_type()
] = execution_data
dataframe_creation_preprocess_step_performer.preprocess_step_type()
] = execution_data

# We set the original_args_raw_strings. If we later have an args update, then these
# are overwritten by the args update (and are actually correct). But since we don't
Expand Down Expand Up @@ -255,6 +267,8 @@ def __init__(
# The version of the public interface used by this analysis
self.public_interface_version = 3

df_formats: List[DataframeFormat] = self.preprocess_execution_data['set_column_definitions']['df_formats'] if 'df_formats' in self.preprocess_execution_data['set_column_definitions'] else None

# Then we initialize the analysis with just a simple initialize step
self.steps_including_skipped: List[Step] = [
Step(
Expand All @@ -265,7 +279,8 @@ def __init__(
df_names=df_names,
user_defined_functions=self.user_defined_functions,
user_defined_importers=self.user_defined_importers,
user_defined_editors=self.user_defined_editors
user_defined_editors=self.user_defined_editors,
df_formats=df_formats
),
{}
)
Expand Down
29 changes: 29 additions & 0 deletions mitosheet/mitosheet/streamlit/v1/apps/column_definitions_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pandas as pd
import streamlit as st
from mitosheet.streamlit.v1 import spreadsheet

st.set_page_config(layout="wide")
st.title('Tesla Stock Volume Analysis')

df = pd.DataFrame({
'A': [1, 2, 3, 4, 5, 6, 7, 8, 9],
'B': [1, 2, 3, 4, 5, 6, 7, 8, 9]
})
new_dfs, code = spreadsheet(
df,
height='700px',
column_definitions= [
{
'columns': ['A', 'B', 'C'],
'conditional_formats': [{
'filters': [{'condition': 'greater', 'value': 5}],
'font_color': '#c30010',
'background_color': '#ffcbd1'
}]
}
]

)

st.write(new_dfs)
st.code(code)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
'a': [1, 2, 3],
'b': [4, 5, 6]
})
new_dfs, code = spreadsheet(df, height='1000px')
new_dfs, code = spreadsheet(df)

st.write(new_dfs)
st.code(code)
Loading
Loading