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

feat: python date picker implementation #409

Merged
merged 9 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
2 changes: 1 addition & 1 deletion plugins/ui/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,7 @@ ui.date_picker(
granularity: Granularity | None = None,
on_change: Callable[[Date], None] | None = None,
**props: Any
) -> ListViewElement
) -> DatePickerElement
```

###### Parameters
Expand Down
173 changes: 172 additions & 1 deletion plugins/ui/src/deephaven/ui/_internal/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@
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

_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 @@ -194,6 +204,167 @@ def create_props(args: dict[str, Any]) -> tuple[tuple[Any], dict[str, Any]]:
Returns:
A tuple of children and props
"""
children, props = args.pop("children"), args.pop("props")
children, props = args.pop("children", tuple()), args.pop("props", {})
props.update(args)
return children, props


def _convert_to_java_date(
date: Date,
) -> Instant | ZonedDateTime | LocalDate: # type: ignore
jnumainville marked this conversation as resolved.
Show resolved Hide resolved
"""
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 get_jclass_name(value: Any) -> str:
"""
Get the name of the Java class of the value.

Args:
value: The value to get the Java class name of.

Returns:
The name of the Java class of the value.
"""
return str(value.jclass)[6:]


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[get_jclass_name(value)]


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_converter(
props: dict[str, Any],
priority: Sequence[str],
default_converter: Callable[[Date], Any],
) -> Callable[[Date], Any]:
"""
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
jnumainville marked this conversation as resolved.
Show resolved Hide resolved


def convert_list_prop(
key: str,
value: list[Date],
) -> list[Instant | ZonedDateTime | LocalDate]: # type: ignore
"""
Convert a list of Dates to Java date types.

Args:
key: The key of the prop.
value: A list of Dates to convert to Java date types.

Returns:
The list of Java date types.
"""
if value is not None:
if not isinstance(value, list):
raise TypeError(f"{key} must be a list of Dates")
jnumainville marked this conversation as resolved.
Show resolved Hide resolved
return [_convert_to_java_date(date) for date in value]
return []


def convert_date_props(
props: dict[str, Any],
simple_date_props: set[str],
callable_date_props: set[str],
priority: Sequence[str],
default_converter: Callable[[Date], Any] = to_j_instant,
) -> 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.
callable_date_props: A set of callable date keys to convert.
The prop value should be a callable that takes a Date.
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 converted props.
"""
for key in simple_date_props:
if props.get(key) is not None:
props[key] = _convert_to_java_date(props[key])

# the simple props must be converted before this to simplify the callable conversion
converter = _prioritized_callable_converter(props, priority, default_converter)
jnumainville marked this conversation as resolved.
Show resolved Hide resolved

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
Loading
Loading