Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jnumainville committed Apr 9, 2024
1 parent c295cbc commit 5d73861
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 143 deletions.
149 changes: 149 additions & 0 deletions plugins/ui/src/deephaven/ui/_internal/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,27 @@
from inspect import signature
import sys
from functools import partial
from deephaven.dtypes import Instant, ZonedDateTime, LocalDate
from deephaven.time import to_j_instant, to_j_zdt, to_j_local_date

from ..types import Date

DateConverter = (
Callable[[Date], Instant] # type: ignore
| Callable[[Date], ZonedDateTime] # type: ignore
| Callable[[Date], LocalDate] # type: ignore
)

_UNSAFE_PREFIX = "UNSAFE_"
_ARIA_PREFIX = "aria_"
_ARIA_PREFIX_REPLACEMENT = "aria-"

_CONVERTERS = {
"java.time.Instant": to_j_instant,
"java.time.ZonedDateTime": to_j_zdt,
"java.time.LocalDate": to_j_local_date,
}


def get_component_name(component: Any) -> str:
"""
Expand Down Expand Up @@ -197,3 +213,136 @@ def create_props(args: dict[str, Any]) -> tuple[tuple[Any], dict[str, Any]]:
children, props = args.pop("children"), args.pop("props")
props.update(args)
return children, props


def _convert_to_java_date(
date: Date,
) -> Instant | ZonedDateTime | LocalDate: # type: ignore
"""
Convert a Date to a Java date type.
In order of preference, tries to convert to Instant, ZonedDateTime, and LocalDate.
If none of these work, raises a TypeError.
Args:
date: The date to convert to a Java date type.
Returns:
The Java date type.
"""
try:
return to_j_instant(date)
except Exception:
# ignore, try next
pass

try:
return to_j_zdt(date)
except Exception:
# ignore, try next
pass

try:
return to_j_local_date(date)
except Exception:
raise TypeError(
f"Could not convert {date} to one of Instant, ZonedDateTime, or LocalDate."
)


def _jclass_converter(
value: Instant | ZonedDateTime | LocalDate, # type: ignore
) -> Callable[[Date], Any]:
"""
Get the converter for the Java date type.
Args:
value: The Java date type to get the converter for.
Returns:
The converter for the Java date type.
"""
return _CONVERTERS[str(value.jclass)[6:]]


def _wrap_date_callable(
date_callable: Callable[[Date], None],
converter: Callable[[Date], Any],
) -> Callable[[Date], None]:
"""
Wrap a callable to convert the Date argument to a Java date type.
This maintains the original callable signature so that the Date argument can be dropped.
Args:
date_callable: The callable to wrap.
converter: The date converter to use.
Returns:
The wrapped callable.
"""
return lambda date: wrap_callable(date_callable)(converter(date))


def prioritized_callable_date_converter(
props: dict[str, Any],
priority: Sequence[str],
default_converter: Callable[[Date], Any],
) -> DateConverter:
"""
Get a callable date converter based on the priority of the props.
If none of the priority props are present, uses the default converter.
Args:
props: The props passed to the component.
priority: The priority of the props to check.
default_converter: The default converter to use if none of the priority props are present.
Returns:
The callable date converter.
"""

for prop in priority:
if props.get(prop):
return _jclass_converter(props[prop])

return default_converter


def convert_date_props(
props: dict[str, Any],
simple_date_props: set[str],
list_date_props: set[str],
callable_date_props: set[str],
callable_date_converter: Callable[[dict[str, Any]], Callable[[Date], Any]],
) -> None:
"""
Convert date props to Java date types in place.
Args:
props: The props passed to the component.
simple_date_props: A set of simple date keys to convert. The prop value should be a single Date.
list_date_props: A set of list date keys to convert. The prop value should be a list of Dates.
callable_date_props: A set of callable date keys to convert.
The prop value should be a callable that takes a Date.
callable_date_converter: A callable that takes the props and
returns a date converter to be used for the callable date props.
Returns:
The converted props.
"""
for key in simple_date_props:
if props.get(key) is not None:
props[key] = _convert_to_java_date(props[key])

for key in list_date_props:
if props.get(key) is not None:
if not isinstance(props[key], list):
raise TypeError(f"{key} must be a list of Dates")
props[key] = [_convert_to_java_date(date) for date in props[key]]

converter = callable_date_converter(props)

for key in callable_date_props:
if props.get(key) is not None:
if not callable(props[key]):
raise TypeError(f"{key} must be a callable")
props[key] = _wrap_date_callable(props[key], converter)
2 changes: 2 additions & 0 deletions plugins/ui/src/deephaven/ui/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .list_view import list_view
from .list_action_group import list_action_group
from .list_action_menu import list_action_menu
from .date_picker import date_picker

from . import html

Expand All @@ -28,6 +29,7 @@
"content",
"contextual_help",
"dashboard",
"date_picker",
"flex",
"form",
"fragment",
Expand Down
175 changes: 32 additions & 143 deletions plugins/ui/src/deephaven/ui/components/date_picker.py
Original file line number Diff line number Diff line change
@@ -1,139 +1,33 @@
from __future__ import annotations

from typing import Any, Sequence, Callable

from deephaven.dtypes import Instant, ZonedDateTime, LocalDate
from deephaven.time import to_j_instant, to_j_zdt, to_j_local_date

from ..elements import Element
from .._internal.utils import create_props, wrap_callable
from deephaven.time import to_j_instant
from ..elements import Element, BaseElement
from .._internal.utils import (
create_props,
convert_date_props,
prioritized_callable_date_converter,
)
from ..types import Date, Granularity

DatePickerElement = Element

# All the props that can be date types
_SIMPLE_DATE_PROPS = {
"placeholder_value",
"value",
"default_value",
"min_value",
"max_value",
}
_LIST_DATE_PROPS = {"unavailable_values"}
_CALLABLE_DATE_PROPS = {"on_change"}

def convert_to_java_date(
date: Date,
) -> Instant | ZonedDateTime | LocalDate: # type: ignore
"""
Convert a Date to a Java date type.
In order of preference, tries to convert to Instant, ZonedDateTime, and LocalDate.
If none of these work, raises a TypeError.
Args:
date: The date to convert to a Java date type.
Returns:
The Java date type.
"""
try:
return to_j_instant(date)
except TypeError:
# ignore, try next
pass

try:
return to_j_zdt(date)
except TypeError:
# ignore, try next
pass

try:
return to_j_local_date(date)
except TypeError:
raise TypeError(
f"Could not convert {date} to one of Instant, ZonedDateTime, or LocalDate."
)


def date_converter(
props: dict[str, Any],
) -> Callable[[Date], Instant | ZonedDateTime | LocalDate]: # type: ignore
"""
Go through "value", "default_value", and "placeholder_value" in props to
determine the type of date converter to use.
If none of these are present, defaults to Instant.
This is used to convert callback arguments to Java date types.
Args:
props: The props to check for date types.
Returns:
The date converter.
"""
converters = {
Instant: to_j_instant,
ZonedDateTime: to_j_zdt,
LocalDate: to_j_local_date,
}

if "value" in props:
return converters[type(props["value"])]
elif "default_value" in props:
return converters[type(props["default_value"])]
elif "placeholder_value" in props:
return converters[type(props["placeholder_value"])]

return to_j_instant


def wrap_date_callable(
date_callable: Callable[[Date], None],
converter: Callable[[Date], Any],
) -> Callable[[Date], None]:
"""
Wrap a callable to convert the Date argument to a Java date type.
This maintains the original callable signature so that the Date argument can be dropped.
Args:
date_callable: The callable to wrap.
converter: The date converter to use.
Returns:
The wrapped callable.
"""
return lambda date: wrap_callable(date_callable)(converter(date))


def convert_date_props(
props: dict[str, Any],
simple_date_props: set[str],
list_date_props: set[str],
callable_date_props: set[str],
converter: Callable[[Date], Any],
) -> None:
"""
Convert date props to Java date types in place.
Args:
props: The props passed to the component.
simple_date_props: A set of simple date keys to convert. The prop value should be a single Date.
list_date_props: A set of list date keys to convert. The prop value should be a list of Dates.
callable_date_props: A set of callable date keys to convert.
The prop value should be a callable that takes a Date.
converter: The date converter to use.
Returns:
The converted props.
"""
for key in simple_date_props:
if key in props:
props[key] = convert_to_java_date(props[key])

for key in list_date_props:
if key in props:
if not isinstance(props[key], list):
raise TypeError(f"{key} must be a list of Dates")
props[key] = [convert_to_java_date(date) for date in props[key]]
# The priority of the date props to determine the format of the date passed to the callable date props
_DATE_PROPS_PRIORITY = ["value", "default_value", "placeholder_value"]

for key in callable_date_props:
if key in props:
if not callable(props[key]):
raise TypeError(f"{key} must be a callable")
props[key] = wrap_date_callable(props[key], converter)


def convert_date_picker_props(
def _convert_date_picker_props(
props: dict[str, Any],
) -> dict[str, Any]:
"""
Expand All @@ -145,20 +39,17 @@ def convert_date_picker_props(
Returns:
The converted props.
"""
simple_date_props = {
"placeholder_value",
"value",
"default_value",
"min_value",
"max_value",
}
list_date_props = {"unavailable_values"}
callable_date_props = {"on_change"}

converter = date_converter(props)

callable_date_converter = prioritized_callable_date_converter(
props, _DATE_PROPS_PRIORITY, to_j_instant
)

convert_date_props(
props, simple_date_props, list_date_props, callable_date_props, converter
props,
_SIMPLE_DATE_PROPS,
_LIST_DATE_PROPS,
_CALLABLE_DATE_PROPS,
callable_date_converter,
)

return props
Expand All @@ -174,7 +65,7 @@ def date_picker(
granularity: Granularity | None = None,
on_change: Callable[[Date], None] | None = None,
**props: Any,
) -> DatePickerElement | None:
) -> DatePickerElement:
"""
Expand All @@ -185,8 +76,6 @@ def date_picker(
"""
_, props = create_props(locals())

convert_date_picker_props(props)

print(props)
_convert_date_picker_props(props)

return None # BaseElement("deephaven.ui.components.DatePicker", **props)
return BaseElement("deephaven.ui.components.DatePicker", **props)

0 comments on commit 5d73861

Please sign in to comment.