Skip to content

Commit

Permalink
feat(category): categories are now tracked
Browse files Browse the repository at this point in the history
  • Loading branch information
kirangadhave committed Nov 6, 2023
1 parent 8d49cd0 commit fe47576
Show file tree
Hide file tree
Showing 46 changed files with 1,923 additions and 1,780 deletions.
1,181 changes: 54 additions & 1,127 deletions examples/test_ext_widget.ipynb

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"@anywidget/types": "^0.1.4",
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.1",
"@hello-pangea/dnd": "^16.3.0",
"@hookstate/core": "^4.0.1",
"@hookstate/localstored": "^4.0.2",
"@hookstate/subscribable": "^4.0.0",
Expand All @@ -85,6 +86,7 @@
"@tabler/icons-react": "^2.40.0",
"@trrack/core": "^1.3.0",
"@trrack/vis-react": "1.4.0-alpha.3",
"add": "^2.0.6",
"d3": "^7.8.5",
"dayjs": "^1.11.10",
"esbuild": "^0.19.4",
Expand Down
60 changes: 58 additions & 2 deletions persist_ext/internals/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,62 @@
import persist_ext.internals.data.get_generated_df as df
import altair as alt
from persist_ext.internals.data.idfy import ID_COLUMN
import persist_ext.internals.vis as vis
from persist_ext.internals.utils.logger import Out, logger
from persist_ext.internals.data.prepare import prepare
from persist_ext.internals.utils import dev
from persist_ext.internals.utils.logger import Out, logger
from persist_ext.internals.widgets.persist_output.widget import (
DEFAULT_DATA_ACCESSOR,
PersistWidget,
)
from persist_ext.internals.widgets.vegalite_chart.utils import (
pop_data_defs_from_charts_recursive,
)

dev.DEV = False


def Persist(
chart=None,
data=None,
df_name="persist_df",
id_column=ID_COLUMN,
data_accessor=DEFAULT_DATA_ACCESSOR,
):
if chart is None and data is None:
raise ValueError(
"Need a valid vega altair chart and/or dataframe to be provided."
)

# If visualizing charts
if chart is not None:
if data is None: # if data is not pass explicitly
chart_data = getattr(chart, "data", alt.Undefined)
if chart_data is alt.Undefined: # if chart does not have top level data
raise ValueError(
"""
Cannot infer dataset from vega altair specification. The data might be specified in subcharts.
Persist does not support such charts.
Please provide data at the top, or pass in the dataset explicitly as second arugment.
"""
)
chart_data = prepare(chart_data, id_column)
chart.data = chart_data
else: # if data is passed
chart = pop_data_defs_from_charts_recursive(chart, [])
chart.data = prepare(data, id_column)

print("Displaying an interactive Vega-Altair Chart")
return PersistWidget(
chart, df_name=df_name, id_column=id_column, data_accessor=data_accessor
)

if data is not None: # if only showing dataframe
data = prepare(data, id_column)
print("Displaying an interactive DataTable")
return PersistWidget(
data, df_name=df_name, id_column=id_column, data_accessor=data_accessor
)


__all__ = ["vis", "logger", "Out", "df", "prepare"]
__all__ = ["vis", "logger", "Out", "df", "prepare", "dev", "Persist"]
2 changes: 1 addition & 1 deletion persist_ext/internals/data/idfy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ID_COLUMN = "__id_column"


def idfy_dataframe(df, id_column=ID_COLUMN):
def idfy_dataframe(df, id_column):
if id_column not in df:
ids = df.index + 1
df.insert(0, id_column, ids)
Expand Down
4 changes: 2 additions & 2 deletions persist_ext/internals/data/prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
)


def prepare(df, preprocess_fn=DEFAULT_PREPROCESS_FN):
def prepare(df, id_column, preprocess_fn=DEFAULT_PREPROCESS_FN):
df = is_dataframe_or_url(df, preprocess_fn) # check if is valid dataframe
df = idfy_dataframe(df) # add id column
df = idfy_dataframe(df, id_column) # add id column

return df
36 changes: 36 additions & 0 deletions persist_ext/internals/data/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
def is_int(value):
if isinstance(value, int):
return True

try:
int(value)
return True
except ValueError:
return False


def is_float(value):
if isinstance(value, float):
return True

try:
float(value)
return True
except ValueError:
return False


def is_numeric(value):
return is_int(value) or is_float(value)


def is_str(value):
return isinstance(value, str)


def set_df_attr(df, key, value):
df.attrs[key] = value


def get_df_attr(df, key):
return df.attrs[key]
55 changes: 55 additions & 0 deletions persist_ext/internals/persist_magics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from IPython import get_ipython
from IPython.core.magic import Magics, magics_class, cell_magic, needs_local_scope
from IPython.core.magic_arguments import magic_arguments, argument, parse_argstring
from altair import TopLevelSpec
from pandas import DataFrame

from persist_ext.internals.widgets.persist_output.widget import PersistWidget


@magics_class
class PersistMagic(Magics):
@staticmethod
def initialize():
get_ipython().register_magics(PersistMagic)

def __init__(self, shell):
super(PersistMagic, self).__init__(shell)
self.call_counter = 0

@cell_magic
@magic_arguments()
@needs_local_scope
@argument(
"--df_name",
dest="df_name",
help="Enter name to use for dynamic dataframe",
default="persist_df",
)
def persist_cell(self, line, cell, local_ns):
args = parse_argstring(self.persist_cell, line)

execution_result = get_ipython().run_cell(cell)
result = execution_result.result

if not isinstance(result, DataFrame) and not isinstance(result, TopLevelSpec):
return result

# dataframe_name_ids = dict()
#
# tree = ast.parse(cell)
#
# # Walk throught the AST and assign uids to all dataframe variables
# # Store the mappings in a dict
# for node in ast.walk(tree):
# if isinstance(node, ast.Name):
# var_name = node.id
# var_value = local_ns[var_name]
#
# if isinstance(var_value, DataFrame):
# persist_uid = uuid.uuid4()
# set_df_attr(local_ns[var_name], "", persist_uid)
# print(local_ns[var_name].attrs)
# dataframe_name_ids[persist_uid] = var_name

PersistWidget(result, args.df_name)
2 changes: 1 addition & 1 deletion persist_ext/internals/utils/dev.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
DEV = True
DEV = False
5 changes: 3 additions & 2 deletions persist_ext/internals/utils/entry_paths.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import json
import pathlib

from persist_ext.internals.utils.dev import DEV
from persist_ext.internals.utils import dev


root_path = pathlib.Path(__file__).parent.parent.parent

Expand Down Expand Up @@ -35,7 +36,7 @@ def get_widget_esm_css(key):
widget = widget_name_map[key]
_CSS = ""

if DEV:
if dev.DEV:
_ESM = (
DEV_BASE
+ f"/{ src_base_dir }/{widget['dir']}/{(widget['srcFileName'] + '?anywidget')}"
Expand Down
22 changes: 22 additions & 0 deletions persist_ext/internals/widgets/base/base_anywidget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import anywidget

from persist_ext.internals.utils.entry_paths import get_widget_esm_css


class BaseAnyWidget(anywidget.AnyWidget):
"""
Basic anywidget setup by _esm and _css
"""

def __init__(self, widget_key=None, *args, **kwargs):
if widget_key is None:
raise ValueError("widget_key cannot be none")

esm, css = get_widget_esm_css(widget_key)
self._esm = esm
self._css = css

if type(self) is BaseAnyWidget:
raise NotImplementedError("Cannot create instance of this base class")

super(BaseAnyWidget, self).__init__(*args, **kwargs)

0 comments on commit fe47576

Please sign in to comment.