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

gr.DateTime component #8713

Merged
merged 29 commits into from
Jul 11, 2024
Merged

gr.DateTime component #8713

merged 29 commits into from
Jul 11, 2024

Conversation

aliabid94
Copy link
Collaborator

@aliabid94 aliabid94 commented Jul 8, 2024

Create a gr.DateTime component, and adds selection range ability to plots, so that exporation of data, especially that of time based data, becomes much more natural.

NOTE: The actual part where the graph re-rerenders to focus on the selected time range is broken because ALL plots currently are broken when it comes to accepting any component updates. Therefore sending an update via return gr.LinePlot(x_lim=[new_start_x, new_end_x]) does nothing. This will be fixed in a follow up PR that refactors all plot components to accept updates. Follow up PR will also have gr.TimeRange work with all plots (currently only works with LinePlots).

See datetimes demo.

@gradio-pr-bot
Copy link
Contributor

gradio-pr-bot commented Jul 8, 2024

🪼 branch checks and previews

Name Status URL
Spaces ready! Spaces preview
Website ready! Website preview
Storybook ready! Storybook preview
🦄 Changes detected! Details

Install Gradio from this PR

pip install https://gradio-builds.s3.amazonaws.com/77e04759927a338c8d2d36958c6090866b19c3cd/gradio-4.37.2-py3-none-any.whl

Install Gradio Python Client from this PR

pip install "gradio-client @ git+https://github.com/gradio-app/gradio@77e04759927a338c8d2d36958c6090866b19c3cd#subdirectory=client/python"

Install Gradio JS Client from this PR

npm install https://gradio-builds.s3.amazonaws.com/77e04759927a338c8d2d36958c6090866b19c3cd/gradio-client-1.2.1.tgz
@gradio-pr-bot
Copy link
Contributor

gradio-pr-bot commented Jul 8, 2024

🦄 change detected

This Pull Request includes changes to the following packages.

Package Version
@gradio/app minor
@gradio/icons minor
@gradio/plot minor
gradio minor

With the following changelog entry.

Time range component

⚠️ The changeset file for this pull request has been modified manually, so the changeset generation bot has been disabled. To go back into automatic mode, delete the changeset file.

Something isn't right?

  • Maintainers can change the version label to modify the version bump.
  • If the bot has failed to detect any changes, or if this pull request needs to update multiple packages to different versions or requires a more comprehensive changelog entry, maintainers can update the changelog file directly.
if interactive is not None:
warnings.warn(
"The `interactive` parameter is deprecated and will be removed in a future version. "
"The LinePlot component is always interactive."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean its always static, right? We use "interactive" to mean that a user can change the value of the component by interacting with it via the Gradio UI

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I suppose. Before this PR, interactive for plots specifically meant that you could hover over point for extra info, it had nothing to do with the normal static vs interactive definition.

@abidlabs
Copy link
Member

abidlabs commented Jul 8, 2024

Very interesting PR @aliabid94. Functionality generally looks good, but my main concern is that this PR introduces some high-level abstractions that are quite rigid and may not be flexible enough for building apps generally with Gradio.

  1. Let's start with gr.TimeRange() itself:
image

This doesn't feel like a single component, but rather a collection of buttons, two calendar components, and some links. We don't even have a Calendar component, so it seems like quite a complex component to add all at once. Its also inflexible -- what if a user doesn't want to have the buttons, or wants to display the calendars stacked vertically instead of horizontally? That being said, I understand the value of having both of the Calendar components together, as it enables things like the quick links. I would suggest at least leaving the buttons out of this component -- they can be added by a user if they want to enable the undo feature or have a specific button to apply the changes. Instead of bind()-ing to the button, you could bind to any change of either of the calendars

  1. Speaking of .bind(), this feels like a very, very specific method and I don't think it should be a part of the core TimeRange() component. We don't have any similar method in any component that ties two components together in this manner. Its very rigid as well -- what if I want to bind things together and have them update as soon as I change the value of one of the calendar components? What if I want to run another function every time I change the calendar value? I'm wondering if we even need this at all? How hard would it be to recreate the sync_plots demo if we manually created the event listeners. If we do want to go down this route, then I'd suggest having a gr.bind() general purpose method that connects two components' values together with appropriate triggers for each component, instead of having something so specific to gr.TimeRange()
@aliabid94
Copy link
Collaborator Author

aliabid94 commented Jul 8, 2024

Functionality generally looks good, but my main concern is that this PR introduces some high-level abstractions that are quite rigid and may not be flexible enough for building apps generally with Gradio... We don't even have a Calendar component, so it seems like quite a complex component to add all at once.

We should definitely have a standalone calendar component, a gr.DateTime perhaps. I can add it in this PR. Then users can create custom logic for interacting with time as well, including some of the suggestions you made.

However, it is no replacement for gr.TimeRange. Gradio would be no replacement for Grafana if you had to write the 50-100 additional lines of logic that TimeRange allows by customly combining gr.DateTime and gr.Button, The function.bind is an absolutely critical way of interacting and keeping time plots in sync. So is the back button. So are quick links for "last 15 min" etc. These are essential interactions that have made tools like grafana so popular for dashboarding, and they need to come out of the box with gradio.

@abidlabs
Copy link
Member

abidlabs commented Jul 9, 2024

Synced with @aliabid94 on this and this is where we ended up:

We should definitely have a standalone calendar component, a gr.DateTime perhaps. I can add it in this PR. Then users can create custom logic for interacting with time as well, including some of the suggestions you made.

I agree that having a dedicated gr.TimeRange component makes sense, as well as a standalone gr.DateTime. But here are the changes we converged on:

  • Instead of having a separate back button, we should redesign the ui so that it is stylistically similar to the "undo" button of the ImageEditor
  • Instead of having a separate apply button, we should have the changes of the gr.TimeRange take place when a valid date is entered in the textbox and the user presses enter or somehow blurs the textbox OR if a date is selected in the calendar. There should be no separate Apply button
  • We should add css to style the calendar better. Right now, the calendar styling uses the default blue instead of using the primary color of the Gradio app and I believe does not respect dark mode
  • Suggestion: rename this component to gr.DateTimeRange if we are adding a gr.DateTime as well.

However, we could not converge on the second concern:

Speaking of .bind(), this feels like a very, very specific method and I don't think it should be a part of the core TimeRange() component

I understand that this method makes creating time series dashboard a breeze. However, it feels out of place to me as a method of gr.TimeRange() and I wonder if there's a way we can support this behavior that is more consistent with our other components. I discussed a general-purpose gr.bind() method that can bind the values of any two components but it wouldn't quite work in this case where we are binding the selected range of a line plot with the value of a gr.TimeRange and also the x-lims of other line plots.

cc @freddyaboulton @hysts if you have any thoughts on a good API for the second concern

@abidlabs
Copy link
Member

abidlabs commented Jul 9, 2024

In the spirit of experimentation, let's go ahead with .bind() and see how the community uses this! cc @aliabid94

@aliabid94
Copy link
Collaborator Author

Added a gr.DateTime as well. Addressed almost everything I think, I cannot theme the calendar popup because it is not part of the DOM, its part of the browser. I think that's ok. Ready for review.

@freddyaboulton
Copy link
Collaborator

freddyaboulton commented Jul 10, 2024

Will review now!

In general though, if we want to experiment with API design, we should do so via custom components as opposed to merging directly into core. It lets us iterate way faster and we don't have to worry about backwards compatibility or waiting on a core release to get bug-fixes and small improvements out in the hands of users.

I think this has worked well already in the past:

  • gr.Leaderboard - Experimented with evaluation team to get it right for them but decided it was too niche for core.
  • gr.AgentChatbot - Worked with transformers team to get the API right and now will merge into core.

We can make some custom components more visible by displaying in the docs page/ new website section like "Gradio Dashboarding"

Copy link
Collaborator

@freddyaboulton freddyaboulton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some small comments but functionally works great and the now syntax is really slick. Love the e2e tests as well!


return (parse_item(value[0]), parse_item(value[1]))

def api_info(self) -> dict[str, Any]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will give more detailed info in the view_api page -

{'properties': {'date_string': {'description': 'Date string in format YYYY-MM-DD HH:MM:SS',
   'maxItems': 2,
   'minItems': 2,
   'prefixItems': [{'type': 'string'}, {'type': 'string'}],
   'title': 'Date String',
   'type': 'array'}},
 'required': ['date_string'],
 'title': 'DateTimeModel',
 'type': 'object'}
gradio/components/datetime_range.py Outdated Show resolved Hide resolved
gradio/components/datetime_range.py Outdated Show resolved Hide resolved
gradio/components/datetime_range.py Outdated Show resolved Hide resolved
gradio/components/datetime.py Outdated Show resolved Hide resolved
gradio/components/datetime.py Show resolved Hide resolved
else:
return datetime.fromtimestamp(value).strftime(self.time_format)

def api_info(self) -> dict[str, Any]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here about the api info as datetime_range

gradio/components/datetime_range.py Outdated Show resolved Hide resolved
js/datetime/Example.svelte Outdated Show resolved Hide resolved
@abidlabs
Copy link
Member

+1 on the custom component route for experimentation and quicker iteration. In addition to @freddyaboulton's examples, we also do this with gr.Slider (core component) and SliderRange (custom component)

This could be a great way to get more visibility for custom components as well & we can add to core later, in 5.x if needed.

@aliabid94
Copy link
Collaborator Author

Hmm ok I'll move out DateTimeRange to a custom component for now. I do think users see custom components as "less supported" which hurts their adoption - I've had this reaction when I've suggested users use Modal, which is custom.

show_label: if True, will display label. If False, the copy button is hidden as well as well as the label.
include_time: If True, the component will include time selection. If False, only date selection will be available.
type: The type of the value. Can be "timestamp", "datetime", or "string". If "timestamp", the value will be a tuple of two numbers representing the start and end date in seconds since epoch. If "datetime", the value will be a tuple of two datetime objects. If "string", the value will be the date entered by the user.
timezone: The timezone to use for timestamps, such as "US/Pacific" or "Europe/Paris". If None, the timezone will be the local timezone.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local to the developer or to the user of the app?

@abidlabs
Copy link
Member

For the website to build correctly with the docs for this component, you'll need to add a:

  • js/_website/src/lib/templates/gradio/03_components/datetime.svx file manually (copy-paste another component's)
  • also I think you need to add a demo/datetime_component/run.py demo
Copy link
Member

@abidlabs abidlabs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM @aliabid94! Just some minor comments above

@aliabid94 aliabid94 changed the title Time range component Jul 11, 2024
@aliabid94 aliabid94 merged commit e3c7079 into main Jul 11, 2024
10 checks passed
@aliabid94 aliabid94 deleted the time_range_component branch July 11, 2024 03:55
@pngwn pngwn mentioned this pull request Jul 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
4 participants