Skip to content

feat: DH-18165: Add calendar argument to several dx charts#1122

Merged
jnumainville merged 20 commits intodeephaven:mainfrom
jnumainville:18165_businesstime
Apr 7, 2025
Merged

feat: DH-18165: Add calendar argument to several dx charts#1122
jnumainville merged 20 commits intodeephaven:mainfrom
jnumainville:18165_businesstime

Conversation

@jnumainville
Copy link
Copy Markdown
Collaborator

@jnumainville jnumainville commented Feb 27, 2025

Fixes DH-18165

Adds calendar option to several chart types. Some good examples are in the docs and e2e tests.

I also noticed some of the js tests were not quite correct when making my new ones. Since there was just a few straightforward changes I made them in this ticket.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

PR Overview

This PR adds support for a calendar argument to several chart types in the dx charts library, along with updates to documentation and tests. Key changes include:

  • Adding a calendar parameter to chart APIs and propagating the calendar option to underlying plotly figures.
  • Updating documentation for OHLC, candlestick, scatter, line, and area charts to include calendar usage.
  • Adjusting tests and internal implementation (e.g. in FigureCalendar, _private_utils, and PartitionManager) for calendar handling.

Reviewed Changes

File Description
plugins/plotly-express/src/deephaven/plot/express/deephaven_figure/FigureCalendar.py Introduces the FigureCalendar class and methods to convert calendar data for the frontend.
plugins/plotly-express/docs/*.md Updates documentation for various chart types to include calendar usage examples.
plugins/plotly-express/src/js/src/PlotlyExpressChartModel.test.ts Updates tests to accommodate new calendar behavior and related events.
plugins/plotly-express/src/deephaven/plot/express/plots/*.py Adds calendar parameters to chart plotting functions and updates logic to force svg render mode when a calendar is provided.
Other files Adjustments in data generators, custom draw functions, and internal utils to support the new calendar functionality, plus minor grammar fixes.

Copilot reviewed 37 out of 37 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (1)

plugins/plotly-express/src/deephaven/plot/express/deephaven_figure/FigureCalendar.py:216

  • Overriding dict as a method might cause confusion with the built-in dict attribute. Consider renaming this method to to_dict() to clearly indicate its purpose.
def __dict__(self) -> FigureCalendarDict | None:

Comment thread plugins/plotly-express/src/deephaven/plot/express/data/data_generators.py Outdated
Comment thread plugins/plotly-express/src/js/src/PlotlyExpressChartModel.ts Outdated
Comment thread plugins/plotly-express/src/js/src/PlotlyExpressChartModel.ts Outdated
Comment thread plugins/plotly-express/src/js/src/PlotlyExpressChartModel.ts Outdated
Comment on lines +69 to +72
this.calendar = {
...calendar,
timeZone,
} as unknown as DhType.calendar.BusinessCalendar;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmmm this isn't quite right... you need to cast it, because the holiday.date is just a string instead of the LocalDateWrapper type that BusinessCalendar has... And this is currently working because we just call holiday.date.toString() in ChartUtils.
At the very least there needs to be some comments about why that's being done. But I'd rather wrap the string in an object that does match the interface correctly, so converting those holiday.dates to something that conforms to:

	export interface LocalDateWrapper {
		valueOf():string;
		getYear():number;
		getMonthValue():number;
		getDayOfMonth():number;
		toString():string;
	}

Otherwise we open ourselves up to issues later.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 4, 2025

plotly-express docs preview (Available for 14 days)

@github-actions
Copy link
Copy Markdown

plotly-express docs preview (Available for 14 days)

@github-actions
Copy link
Copy Markdown

plotly-express docs preview (Available for 14 days)

@jnumainville jnumainville marked this pull request as ready for review March 31, 2025 21:45
@jnumainville
Copy link
Copy Markdown
Collaborator Author

jnumainville commented Mar 31, 2025

There is a small bug where the loading spinner is not dismissed correctly when creating multiple charts I am investigating but otherwise this is ready for review

update - this is fixed

margaretkennedy
margaretkennedy previously approved these changes Apr 1, 2025
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2025

plotly-express docs preview (Available for 14 days)

@jnumainville jnumainville requested a review from mofojed April 1, 2025 19:09
Copy link
Copy Markdown
Member

@mofojed mofojed left a comment

Choose a reason for hiding this comment

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

I think I got hung up on some of the diff by changes brought over by indicator merge... but there's still a couple spots that should be cleaned up here.

Comment on lines +347 to +373
@@ -334,6 +335,45 @@ export class PlotlyExpressChartModel extends ChartModel {
return timeZone !== newTimeZone && newTimeZone != null;
}

/**
* Update the calendar object from the data
* @param data The new data to update the calendar from
*/
updateCalendar(data: PlotlyChartWidgetData): void {
const { calendar } = data.figure.deephaven;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This casting is gross, and then doing a .forEach is pretty nasty when you should just do a .map. Rewritten so you don't need casts (and probably add a name to FigureCalendar so that you don't need to add a name here):

Suggested change
const newCalendar = {
...calendar,
timeZone,
} as unknown as DhType.calendar.BusinessCalendar;
// Holidays should be converted to LocalDate objects so
// they have all the necessary methods to match the BusinessCalendar interface
newCalendar.holidays.forEach((holiday, i) => {
const { date } = holiday;
// date is a really a string at this point, but it should be a LocalDate object
const dateObj = new Date(date as unknown as string);
const year = dateObj.getFullYear();
const month = dateObj.getMonth();
const day = dateObj.getDate();
newCalendar.holidays[i] = {
...newCalendar.holidays[i],
date: {
valueOf: () => date,
getYear: () => year,
getMonthValue: () => month,
getDayOfMonth: () => day,
toString: () => date,
} as unknown as DhType.LocalDateWrapper,
};
});
this.calendar = newCalendar;
this.calendar = {
...calendar,
timeZone,
name: 'PlotlyExpressCalendar',
holidays: calendar.holidays.map((holiday, i) => {
const { date } = holiday;
// date is a really a string at this point, but it should be a LocalDate object
const dateObj = new Date(date as unknown as string);
const year = dateObj.getFullYear();
const month = dateObj.getMonth();
const day = dateObj.getDate();
return {
...holiday,
date: {
valueOf: () => date,
getYear: () => year,
getMonthValue: () => month,
getDayOfMonth: () => day,
toString: () => date,
} as unknown as DhType.LocalDateWrapper,
};
}),
};

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

* @param id The table ID to unsubscribe from
*/
unsubscribeTable(id: number): void {
this.tableSubscriptionMap.get(id)?.close();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Optimization: Opt to do the this.isSubscribed check first - no need to do the work in timeZoneChanged if we're not subscribed.

Suggested change
if (this.timeZoneChanged(formatter) && this.isSubscribed) {
if (this.isSubscribed && this.timeZoneChanged(formatter)) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

Comment on lines +428 to +432

/**
* Check if the data at the selector should be replaced with a single value instead of an array
* @param data The data to check
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We shouldn't be casting stuff like this. That's an indication we're doing something wrong; casting should be a last resort, not used liberally like this (particularly casting to any). We're essentially rendering the type checking system useless.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not sure why that was so ugly in the first place, done.

if (formatter != null && calendar != null) {
const layoutUpdate: Partial<Layout> = {};

Object.keys(layout).forEach(key => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could do a filter here which might be a little clearer, instead of having the if block within, e.g.

    Object.keys(layout).filter(key => key.includes('axis')).forEach(key => {
      const axis = layout[key as keyof Layout];

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2025

plotly-express docs preview (Available for 14 days)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2025

plotly-express docs preview (Available for 14 days)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2025

plotly-express docs preview (Available for 14 days)

@jnumainville jnumainville requested a review from mofojed April 7, 2025 16:26
@jnumainville jnumainville merged commit 47a2d71 into deephaven:main Apr 7, 2025
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants