Skip to content

Commit

Permalink
Time Grain enhancements (#2651)
Browse files Browse the repository at this point in the history
* add quarter time grain

* Set new allowed grains

* Refactor grain tests:

* Deprecate getTimeGrainOptions

* Add missing comma
  • Loading branch information
djbarnwal authored Jun 20, 2023
1 parent f4fd7a9 commit c5bf0f9
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
TIME_GRAIN_DAY = "daily",
TIME_GRAIN_WEEK = "weekly",
TIME_GRAIN_MONTH = "monthly",
TIME_GRAIN_QUARTER = "quarterly",
TIME_GRAIN_YEAR = "yearly",
}
Expand Down
2 changes: 2 additions & 0 deletions web-common/src/features/dashboards/proto-state/fromProto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ function fromTimeGrainProto(timeGrain: TimeGrain): V1TimeGrain {
return V1TimeGrain.TIME_GRAIN_WEEK;
case TimeGrain.MONTH:
return V1TimeGrain.TIME_GRAIN_MONTH;
case TimeGrain.QUARTER:
return V1TimeGrain.TIME_GRAIN_QUARTER;
case TimeGrain.YEAR:
return V1TimeGrain.TIME_GRAIN_YEAR;
}
Expand Down
2 changes: 2 additions & 0 deletions web-common/src/features/dashboards/proto-state/toProto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ function toTimeGrainProto(timeGrain: V1TimeGrain) {
return TimeGrainProto.WEEK;
case V1TimeGrain.TIME_GRAIN_MONTH:
return TimeGrainProto.MONTH;
case V1TimeGrain.TIME_GRAIN_QUARTER:
return TimeGrainProto.QUARTER;
case V1TimeGrain.TIME_GRAIN_YEAR:
return TimeGrainProto.YEAR;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,5 @@
import { V1TimeGrain } from "../../../runtime-client";
import { getDefaultTimeGrain, getTimeGrainOptions } from "./time-range-utils";

describe("getTimeGrainOptions", () => {
it("should return an array of available time grains", () => {
const timeGrains = getTimeGrainOptions(
new Date("2020-03-01"),
new Date("2020-03-31")
);
expect(timeGrains).toEqual([
{
enabled: false,
timeGrain: V1TimeGrain.TIME_GRAIN_MINUTE,
},
{
enabled: true,
timeGrain: V1TimeGrain.TIME_GRAIN_HOUR,
},
{
enabled: true,
timeGrain: V1TimeGrain.TIME_GRAIN_DAY,
},
{
enabled: true,
timeGrain: V1TimeGrain.TIME_GRAIN_WEEK,
},
{
enabled: false,
timeGrain: V1TimeGrain.TIME_GRAIN_MONTH,
},
{
enabled: false,
timeGrain: V1TimeGrain.TIME_GRAIN_YEAR,
},
]);
});
});
import { getDefaultTimeGrain } from "./time-range-utils";

describe("getDefaultTimeGrain", () => {
it("should return the default time grain (for a 5 day time range)", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte";
import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte";
import { TIME_GRAIN } from "@rilldata/web-common/lib/time/config";
import { getTimeGrainOptions } from "@rilldata/web-common/lib/time/grains";
import type { TimeGrainOption } from "@rilldata/web-common/lib/time/types";
import { getAllowedTimeGrains } from "@rilldata/web-common/lib/time/grains";
import type { TimeGrain } from "@rilldata/web-common/lib/time/types";
import {
createQueryServiceColumnTimeRange,
V1Model,
Expand Down Expand Up @@ -54,29 +54,27 @@
};
}
let selectableTimeGrains: TimeGrainOption[] = [];
let allowedTimeGrains: TimeGrain[] = [];
let maxTimeGrainPossibleIndex = 0;
const timeGrains = Object.values(TIME_GRAIN);
$: if (allTimeRange) {
// FIXME: we should be deprecating this getTimeGrainOptions in favor of getAllowedTimeGrains.
selectableTimeGrains = getTimeGrainOptions(
allowedTimeGrains = getAllowedTimeGrains(
allTimeRange.start,
allTimeRange.end
);
maxTimeGrainPossibleIndex =
selectableTimeGrains.length -
1 -
selectableTimeGrains
.slice()
.reverse()
.findIndex((grain) => grain.enabled);
const maxTimeGrainPossible =
allowedTimeGrains[allowedTimeGrains.length - 1];
maxTimeGrainPossibleIndex = timeGrains.findIndex(
(grain) => grain.label === maxTimeGrainPossible.label
);
}
$: isValidTimeGrain =
defaultTimeGrainValue === "__DEFAULT_VALUE__" ||
Object.values(TIME_GRAIN).some(
(timeGrain) => timeGrain.label === defaultTimeGrainValue
);
timeGrains.some((timeGrain) => timeGrain.label === defaultTimeGrainValue);
$: level = isValidTimeGrain ? "" : "error";
Expand All @@ -102,7 +100,7 @@
},
]
: []),
...(selectableTimeGrains.map((grain, i) => {
...(timeGrains.map((grain, i) => {
const isGrainPossible = i <= maxTimeGrainPossibleIndex;
return {
divider: false,
Expand Down
9 changes: 9 additions & 0 deletions web-common/src/lib/time/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,15 @@ export const TIME_GRAIN: Record<AvailableTimeGrain, TimeGrain> = {
month: "short",
},
},
TIME_GRAIN_QUARTER: {
grain: V1TimeGrain.TIME_GRAIN_QUARTER,
label: "quarter",
duration: Period.QUARTER,
formatDate: {
year: "numeric",
month: "short",
},
},
TIME_GRAIN_YEAR: {
grain: V1TimeGrain.TIME_GRAIN_YEAR,
label: "year",
Expand Down
39 changes: 17 additions & 22 deletions web-common/src/lib/time/grains/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ import { Period, TimeGrain } from "../types";

const allowedGrainTests = [
{
test: "should return TIME_GRAIN_MINUTE for < 2 hours",
test: "should return TIME_GRAIN_MINUTE for < 1 hour",
start: new Date(0),
end: new Date(
2 * durationToMillis(TIME_GRAIN.TIME_GRAIN_HOUR.duration) - 1
),
end: new Date(durationToMillis(TIME_GRAIN.TIME_GRAIN_HOUR.duration) - 1),
expected: [TIME_GRAIN.TIME_GRAIN_MINUTE],
},
{
Expand All @@ -32,18 +30,11 @@ const allowedGrainTests = [
end: new Date(durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration) - 1),
expected: [TIME_GRAIN.TIME_GRAIN_HOUR],
},

{
test: "should return TIME_GRAIN_HOUR for 24 hours",
start: new Date(0),
end: new Date(durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration)),
expected: [TIME_GRAIN.TIME_GRAIN_HOUR],
},
{
test: "should return TIME_GRAIN_HOUR and TIME_GRAIN_DAY if otherwise < 14 days",
test: "should return TIME_GRAIN_HOUR and TIME_GRAIN_DAY if otherwise < 7 days",
start: new Date(0),
end: new Date(
durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration) * 14 - 1
),
end: new Date(durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration) * 7 - 1),
expected: [TIME_GRAIN.TIME_GRAIN_HOUR, TIME_GRAIN.TIME_GRAIN_DAY],
},
{
Expand All @@ -59,34 +50,38 @@ const allowedGrainTests = [
],
},
{
test: "should return TIME_GRAIN_DAY, TIME_GRAIN_WEEK, and TIME_GRAIN_MONTH if otherwise < 90 days",
test: "should return TIME_GRAIN_WEEK, TIME_GRAIN_MONTH, and TIME_GRAIN_YEAR if otherwise < 90 days",
start: new Date(0),
end: new Date(
3 * durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration) * 30 - 1
durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration) * 3 * 30 - 1
),
expected: [TIME_GRAIN.TIME_GRAIN_DAY, TIME_GRAIN.TIME_GRAIN_WEEK],
expected: [
TIME_GRAIN.TIME_GRAIN_DAY,
TIME_GRAIN.TIME_GRAIN_WEEK,
TIME_GRAIN.TIME_GRAIN_MONTH,
],
},
{
test: "should return TIME_GRAIN_WEEK, TIME_GRAIN_MONTH, and TIME_GRAIN_YEAR if otherwise < 3 years",
test: "should return TIME_GRAIN_WEEK, TIME_GRAIN_MONTH, TIME_GRAIN_QUARTER and TIME_GRAIN_YEAR if otherwise < 1 year",
start: new Date(0),
end: new Date(
3 * durationToMillis(TIME_GRAIN.TIME_GRAIN_YEAR.duration) - 1
),
end: new Date(durationToMillis(TIME_GRAIN.TIME_GRAIN_YEAR.duration) - 1),
expected: [
TIME_GRAIN.TIME_GRAIN_DAY,
TIME_GRAIN.TIME_GRAIN_WEEK,
TIME_GRAIN.TIME_GRAIN_MONTH,
TIME_GRAIN.TIME_GRAIN_QUARTER,
],
},
{
test: "should return TIME_GRAIN_MONTH, TIME_GRAIN_YEAR, and TIME_GRAIN_QUARTER if otherwise < 10 years",
test: "should return TIME_GRAIN_WEEK, TIME_GRAIN_MONTH, TIME_GRAIN_QUARTER and TIME_GRAIN_YEAR if otherwise < 10 years",
start: new Date(0),
end: new Date(
10 * durationToMillis(TIME_GRAIN.TIME_GRAIN_YEAR.duration) - 1
),
expected: [
TIME_GRAIN.TIME_GRAIN_WEEK,
TIME_GRAIN.TIME_GRAIN_MONTH,
TIME_GRAIN.TIME_GRAIN_QUARTER,
TIME_GRAIN.TIME_GRAIN_YEAR,
],
},
Expand Down
54 changes: 16 additions & 38 deletions web-common/src/lib/time/grains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { V1TimeGrain } from "@rilldata/web-common/runtime-client";
import { Duration } from "luxon";
import { TIME_GRAIN } from "../config";
import { getTimeWidth } from "../transforms";
import type { TimeGrain, TimeGrainOption } from "../types";
import type { TimeGrain } from "../types";

export function unitToTimeGrain(unit: string): V1TimeGrain {
return (
Expand All @@ -20,30 +20,6 @@ export function durationToMillis(duration: string): number {
return Duration.fromISO(duration).toMillis();
}

// FIXME: what is the difference between this and getAllowedTimeGrains?
// It appears that we're using this instead of getAllowedTimeGrains.
// I think we should deprecate this function as soon as possible.
export function getTimeGrainOptions(start: Date, end: Date): TimeGrainOption[] {
const timeGrains: TimeGrainOption[] = [];
const timeRangeDurationMs = getTimeWidth(start, end);

for (const timeGrain of Object.values(TIME_GRAIN)) {
// only show a time grain if it results in a reasonable number of points on the line chart
const MINIMUM_POINTS_ON_LINE_CHART = 3;
const MAXIMUM_POINTS_ON_LINE_CHART = 2500;
const timeGrainDurationMs = durationToMillis(timeGrain.duration);
const pointsOnLineChart = timeRangeDurationMs / timeGrainDurationMs;
const showTimeGrain =
pointsOnLineChart >= MINIMUM_POINTS_ON_LINE_CHART &&
pointsOnLineChart <= MAXIMUM_POINTS_ON_LINE_CHART;
timeGrains.push({
...timeGrain,
enabled: showTimeGrain,
});
}
return timeGrains;
}

// Get the default grain for a given time range.
export function getDefaultTimeGrain(start: Date, end: Date): TimeGrain {
const timeRangeDurationMs = end.getTime() - start.getTime();
Expand Down Expand Up @@ -72,14 +48,11 @@ export function getDefaultTimeGrain(start: Date, end: Date): TimeGrain {
}
}

// Return time grains that are allowed for a given time range. Note that
// this function is similar to getTimeGrainOptions. We should deprecate getTimeGrainOptions
// in favor of this logic.
// Return time grains that are allowed for a given time range.
export function getAllowedTimeGrains(start: Date, end: Date): TimeGrain[] {
const timeRangeDurationMs = getTimeWidth(start, end);
if (
timeRangeDurationMs <
2 * durationToMillis(TIME_GRAIN.TIME_GRAIN_HOUR.duration)
timeRangeDurationMs < durationToMillis(TIME_GRAIN.TIME_GRAIN_HOUR.duration)
) {
return [TIME_GRAIN.TIME_GRAIN_MINUTE];
} else if (
Expand All @@ -88,17 +61,17 @@ export function getAllowedTimeGrains(start: Date, end: Date): TimeGrain[] {
) {
return [TIME_GRAIN.TIME_GRAIN_MINUTE, TIME_GRAIN.TIME_GRAIN_HOUR];
} else if (
timeRangeDurationMs <= durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration)
timeRangeDurationMs < durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration)
) {
return [TIME_GRAIN.TIME_GRAIN_HOUR];
} else if (
timeRangeDurationMs <=
14 * durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration)
timeRangeDurationMs <
7 * durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration)
) {
return [TIME_GRAIN.TIME_GRAIN_HOUR, TIME_GRAIN.TIME_GRAIN_DAY];
} else if (
timeRangeDurationMs <
durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration) * 30
30 * durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration)
) {
return [
TIME_GRAIN.TIME_GRAIN_HOUR,
Expand All @@ -107,22 +80,27 @@ export function getAllowedTimeGrains(start: Date, end: Date): TimeGrain[] {
];
} else if (
timeRangeDurationMs <
3 * durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration) * 30
3 * 30 * durationToMillis(TIME_GRAIN.TIME_GRAIN_DAY.duration)
) {
return [TIME_GRAIN.TIME_GRAIN_DAY, TIME_GRAIN.TIME_GRAIN_WEEK];
return [
TIME_GRAIN.TIME_GRAIN_DAY,
TIME_GRAIN.TIME_GRAIN_WEEK,
TIME_GRAIN.TIME_GRAIN_MONTH,
];
} else if (
timeRangeDurationMs <
3 * durationToMillis(TIME_GRAIN.TIME_GRAIN_YEAR.duration)
timeRangeDurationMs < durationToMillis(TIME_GRAIN.TIME_GRAIN_YEAR.duration)
) {
return [
TIME_GRAIN.TIME_GRAIN_DAY,
TIME_GRAIN.TIME_GRAIN_WEEK,
TIME_GRAIN.TIME_GRAIN_MONTH,
TIME_GRAIN.TIME_GRAIN_QUARTER,
];
} else {
return [
TIME_GRAIN.TIME_GRAIN_WEEK,
TIME_GRAIN.TIME_GRAIN_MONTH,
TIME_GRAIN.TIME_GRAIN_QUARTER,
TIME_GRAIN.TIME_GRAIN_YEAR,
];
}
Expand Down
5 changes: 0 additions & 5 deletions web-common/src/lib/time/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,6 @@ export interface TimeGrain {
formatDate: Intl.DateTimeFormatOptions;
}

// FIXME: is this needed?
export interface TimeGrainOption extends TimeGrain {
enabled: boolean;
}

// limit the set of available time grains to those supported
// by th dashboard.
export type AvailableTimeGrain = Exclude<
Expand Down

2 comments on commit c5bf0f9

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉 Published on https://ui.rilldata.com as production
🚀 Deployed on https://6492c4e692bf6b3b006e5cbd--rill-ui.netlify.app

Please sign in to comment.