Skip to content

Commit

Permalink
fix: implement centralized state, fix rendering with query
Browse files Browse the repository at this point in the history
  • Loading branch information
voznik committed Mar 18, 2024
1 parent c09a626 commit 75ec3f3
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 242 deletions.
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
"statusBarItem.remoteForeground": "#e7e7e7"
},
"peacock.color": "#8d2089",
"python.defaultInterpreterPath": "/home/voznik/.cache/pypoetry/virtualenvs/streamlit-rxdb-dataframe-zuiqvGqO-py3.10"
"python.defaultInterpreterPath": "/home/voznik/.cache/pypoetry/virtualenvs/streamlit-rxdb-dataframe-zuiqvGqO-py3.10",
"flake8.args": ["--max-line-length=100"],
"flake8.importStrategy": "fromEnvironment",
"black-formatter.args": ["--line-length=100"]
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"dep-graph": "nx dep-graph",
"analyze:demo": "node --max_old_space_size=8000 ./node_modules/.bin/source-map-explorer dist/demo/{vendor,main}.*.js{,.map} -m --no-border-checks",
"analyze:standalone": "node --max_old_space_size=8000 ./node_modules/.bin/source-map-explorer dist/standalone/browser/{chunk,main}-*.js{,.map} -m --no-border-checks",
"analyze:streamlit": "node --max_old_space_size=8000 ./node_modules/.bin/source-map-explorer packages/streamlit-rxdb-dataframe/rxdb_dataframe/frontend/build/index.js{,.map} -m --no-border-checks",
"analyze:file": "node --max_old_space_size=8000 ./node_modules/.bin/source-map-explorer dist/demo/{vendor,main}.*.js{,.map} -m --no-border-checks --html dist/out/result.html",
"changelog:write": "conventional-changelog -p angular -i CHANGELOG.md -s --config=tools/scripts/conventional-changelog-config.js --skip-unstable",
"compodoc": "nx run tools:compodoc"
Expand Down
239 changes: 142 additions & 97 deletions packages/streamlit-rxdb-dataframe/example.py
Original file line number Diff line number Diff line change
@@ -1,119 +1,164 @@
import json
import os
from typing import Dict, List
import streamlit as st
from streamlit.runtime.caching import cache_data
from rxdb_dataframe import (
RXDB_COLLECTION_EDITOR_KEY,
RxCollectionCreator,
RxJsonSchema,
get_dataframe_by_schema,
reset_editing_state,
RxDBSessionState,
rxdb_dataframe,
get_column_config,
)
import datetime

# from jsonschema import validate, TypeChecker
# from pandas.api.types import ( is_bool_dtype, is_categorical_dtype, is_datetime64_any_dtype, is_numeric_dtype, is_object_dtype, ) # noqa: E501

collection_name = "todo"
todoSchema: RxJsonSchema = {
"type": "object",
"title": "Todo",
"description": "Todo Schema",
"required": ["id", "title", "createdAt"],
"version": 0,
"properties": {
"id": {
"type": "string",
"format": "uuid",
"title": "Id",
"pattern": "^(.*)$",
"maxLength": 36,
"readOnly": True,
},
"title": {
"type": "string",
"title": "Title",
"minLength": 3,
},
"completed": {"type": "boolean", "title": "Done"},
"createdAt": {
"type": "string",
"title": "Created Date",
"format": "date-time",
"readOnly": True,
},
"last_modified": {
"type": "integer",
"format": "time",
"title": "Last Modified Date",
"description": "Last modified date in milliseconds, "
+ "automatically updated by database",
"multipleOf": 1,
},
},
"primaryKey": "id",
"attachments": {"encrypted": False},
}
current_dir = os.path.dirname(os.path.abspath(__file__))
data_dir = os.path.join(current_dir, "rxdb_dataframe/frontend/public/assets/data")

initial_docs = [
{
"id": "ac3ef2c6-c98b-43e1-9047-71d68b1f92f4",
"title": "Python Todo list example",
"completed": True,
"createdAt": datetime.datetime.fromtimestamp(1546300800).isoformat(),
"last_modified": 1546300800000,
},
{
"id": "a4c6a479-7cca-4d3b-ab90-45d3eaa957f3",
"title": "Python example 2",
"completed": False,
"createdAt": datetime.datetime.fromtimestamp(1548979200).isoformat(),
"last_modified": 1548979200000,
},
{
"id": "a4c6a479-7cca-4d3b-bc10-45d3eaa957r5",
"title": 'Use "@ngx-odm/rxdb" in your project',
"completed": False,
"createdAt": datetime.datetime.now().isoformat(),
"last_modified": int(
datetime.datetime.now().timestamp() * 1000
), # in milliseconds
},
]
collection_name = "todo"
todoSchema: Dict = json.load(open(os.path.join(data_dir, "todo.schema.json")))
initial_docs: List = json.load(open(os.path.join(data_dir, "col.dump.json")))["docs"]
collection_config: RxCollectionCreator = {
"name": collection_name,
"schema": todoSchema, # to load schema from remote url pass None
"schema": todoSchema, # to auto load schema from remote url pass None
"localDocuments": True,
"options": {
# 'schemaUrl': 'assets/data/todo.schema.json',
"initialDocs": initial_docs,
"recreate": True,
"recreate": False,
},
}
collection_query = {"selector": {}, "sort": [{"createdAt": "desc"}]}
df = get_dataframe_by_schema(todoSchema)
column_config = get_column_config(todoSchema)
data = rxdb_dataframe(
collection_config, dataframe=df, query=collection_query, with_rev=False
)


def on_change():
"""
Called after RxDB Dataframe component applies changes
"""
st.success("on_change")
reset_editing_state(collection_name)
state = RxDBSessionState()
# operators = ["$eq", "$gt", "$gte", "$lt", "$lte", "$ne"]


def on_change_dataframe(rxdb_state: RxDBSessionState):
print("RxDBDataframe component on_change call")
print("collection.info()", rxdb_state.info)
print("dataframe.head()", rxdb_state.dataframe.head())


st.data_editor(
data,
key=collection_name,
num_rows="dynamic",
use_container_width=True,
column_config=column_config,
column_order=["title", "completed", "createdAt"],
on_change=on_change,
@cache_data
def apply_row_style(row):
return (
["color:green"] * len(row) if row.completed else ["color:grey"] * len(row) # default color
) # noqa: E501


query = state.query
display = st.radio(
"Display RxDB collection as:",
options=["dataframe", "data_editor", "table"],
horizontal=True,
)
filter = st.radio(
label="Filter data using RxDB **RxQuery**",
help="MangoQuery syntax",
label_visibility="visible",
options=["all", "active", "completed"],
horizontal=True,
)

st.sidebar.title("Collection Query")
st.sidebar.json(collection_query)
# st.sidebar.title("State")
# st.sidebar.json(st.session_state.to_dict())
if filter == "active":
query = {"selector": {"completed": {"$eq": False}}}
elif filter == "completed":
query = {"selector": {"completed": {"$eq": True}}}
else:
query = {"selector": {}}

df = rxdb_dataframe(
collection_config,
query=query,
with_rev=False,
on_change=on_change_dataframe,
)

if display == "data_editor":
st.data_editor(
df.style.apply(apply_row_style, axis=1),
use_container_width=True,
hide_index=True,
column_config=state.column_config,
column_order=["title", "completed", "createdAt"],
num_rows="dynamic",
key=RXDB_COLLECTION_EDITOR_KEY,
)
elif display == "dataframe":
st.dataframe(
df.style.apply(apply_row_style, axis=1),
use_container_width=True,
hide_index=True,
column_config=state.column_config,
column_order=["title", "completed", "createdAt"],
)
else:
st.table(
df.style.apply(apply_row_style, axis=1),
)


with st.sidebar:
st.write("RxDB Collection Config")
st.json(collection_config, expanded=False)

st.write("RsDB Session State:")
st.json(st.session_state, expanded=False)
# TODO: Add form to construct RxQuery
# query_container = st.container()
# with query_container:
# to_filter_columns = st.multiselect(
# "Filter dataframe on",
# df.columns,
# key=f"{prefix}_multiselect",
# )
# filters: Dict[str, Any] = dict()
# for column in to_filter_columns:
# left, right = st.columns((1, 20))
# # Treat columns with < 10 unique values as categorical
# if is_categorical_dtype(df[column]) or df[column].nunique() < 10:
# left.write("↳")
# filters[column] = right.multiselect(
# f"Values for {column}",
# df[column].unique(),
# default=list(df[column].unique()),
# key=f"{prefix}_{column}",
# )
# df = df[df[column].isin(filters[column])]
# elif is_numeric_dtype(df[column]):
# left.write("↳")
# _min = float(df[column].min())
# _max = float(df[column].max())
# step = (_max - _min) / 100
# filters[column] = right.slider(
# f"Values for {column}",
# _min,
# _max,
# (_min, _max),
# step=step,
# key=f"{prefix}_{column}",
# )
# df = df[df[column].between(*filters[column])]
# elif is_datetime64_any_dtype(df[column]):
# left.write("↳")
# filters[column] = right.date_input(
# f"Values for {column}",
# value=(
# df[column].min(),
# df[column].max(),
# ),
# key=f"{prefix}_{column}",
# )
# if len(filters[column]) == 2:
# filters[column] = tuple(map(pd.to_datetime, filters[column])) # noqa: E501
# start_date, end_date = filters[column]
# df = df.loc[df[column].between(start_date, end_date)]
# else:
# left.write("↳")
# filters[column] = right.text_input(
# f"Pattern in {column}",
# key=f"{prefix}_{column}",
# )
# if filters[column]:
# print(filters[column])
10 changes: 5 additions & 5 deletions packages/streamlit-rxdb-dataframe/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ flake8-annotations-complexity = "^0.0.8"

[tool.isort]
profile = "black"
line_length = 88
line_length = 100
skip = ["./.venv", "./direnv", ".env"]

[tool.black]
Expand All @@ -50,10 +50,10 @@ exclude = '''
)
'''
include = '\.pyi?$'
line-length = 88
line-length = 100

[tool.flake8]
max-line-length = 88
max-line-length = 100
ignore = ["E501"]

[tool.mypy]
Expand All @@ -75,9 +75,9 @@ exclude = [
".direnv",
"streamlit_patches.py",
]
target-version = "py39"
target-version = "py310"
ignore = ["E501"]
line-length = 88
line-length = 100
select = ["B", "E", "F", "W", "I"]

[tool.ruff.per-file-ignores]
Loading

0 comments on commit 75ec3f3

Please sign in to comment.