Skip to content

Commit

Permalink
feat: add Current time-range options for time filter (#28637)
Browse files Browse the repository at this point in the history
Co-authored-by: Evan Rusackas <[email protected]>
  • Loading branch information
pranav1699 and rusackas authored Jun 6, 2024
1 parent f2e020e commit 066f6b1
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
AdvancedFrame,
DateLabel,
} from './components';
import { CurrentCalendarFrame } from './components/CurrentCalendarFrame';

const StyledRangeType = styled(Select)`
width: 272px;
Expand Down Expand Up @@ -201,6 +202,7 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
if (
guessedFrame === 'Common' ||
guessedFrame === 'Calendar' ||
guessedFrame === 'Current' ||
guessedFrame === 'No filter'
) {
setActualTimeRange(value);
Expand Down Expand Up @@ -296,6 +298,12 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
{frame === 'Calendar' && (
<CalendarFrame value={timeRangeValue} onChange={setTimeRangeValue} />
)}
{frame === 'Current' && (
<CurrentCalendarFrame
value={timeRangeValue}
onChange={setTimeRangeValue}
/>
)}
{frame === 'Advanced' && (
<AdvancedFrame value={timeRangeValue} onChange={setTimeRangeValue} />
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { useEffect } from 'react';

Check failure on line 19 in superset-frontend/src/explore/components/controls/DateFilterControl/components/CurrentCalendarFrame.tsx

View workflow job for this annotation

GitHub Actions / frontend-build

Default React import is not required due to automatic JSX runtime in React 16.4
import { t } from '@superset-ui/core';
import { Radio } from 'src/components/Radio';
import {
CURRENT_RANGE_OPTIONS,
CURRENT_CALENDAR_RANGE_SET,
} from 'src/explore/components/controls/DateFilterControl/utils';
import { CurrentRangeType, CurrentWeek, FrameComponentProps } from '../types';

export function CurrentCalendarFrame({ onChange, value }: FrameComponentProps) {
useEffect(() => {
if (!CURRENT_CALENDAR_RANGE_SET.has(value as CurrentRangeType)) {
onChange(CurrentWeek);
}
}, [value]);

if (!CURRENT_CALENDAR_RANGE_SET.has(value as CurrentRangeType)) {
return null;
}

return (
<>
<div className="section-title">
{t('Configure Time Range: Current...')}
</div>
<Radio.Group
value={value}
onChange={(e: any) => {
let newValue = e.target.value;
// Sanitization: Trim whitespace
newValue = newValue.trim();
// Validation: Check if the value is non-empty
if (newValue === '') {
return;
}
onChange(newValue);
}}
>
{CURRENT_RANGE_OPTIONS.map(({ value, label }) => (
<Radio key={value} value={value} className="vertical-radio">
{label}
</Radio>
))}
</Radio.Group>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
export { CommonFrame } from './CommonFrame';
export { CalendarFrame } from './CalendarFrame';
export { CurrentCalendarFrame } from './CurrentCalendarFrame';
export { CustomFrame } from './CustomFrame';
export { AdvancedFrame } from './AdvancedFrame';
export { DateLabel } from './DateLabel';
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';

Check failure on line 19 in superset-frontend/src/explore/components/controls/DateFilterControl/tests/CurrentCalendarFrame.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-build

Default React import is not required due to automatic JSX runtime in React 16.4
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect'; // For advanced DOM assertions
import { CurrentCalendarFrame } from '../components/CurrentCalendarFrame';
import { CurrentWeek } from '../types';

const mockOnChange = jest.fn();

test('calls onChange(CurrentWeek) when value is invalid', () => {
render(<CurrentCalendarFrame onChange={mockOnChange} value="InvalidValue" />);
expect(mockOnChange).toHaveBeenCalledWith(CurrentWeek);
});

test('returns null if value is not a valid CurrentRangeType', () => {
const { container } = render(
<CurrentCalendarFrame onChange={mockOnChange} value="InvalidValue" />,
);
expect(container.childNodes.length).toBe(0);
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type SelectOptionType = {
export type FrameType =
| 'Common'
| 'Calendar'
| 'Current'
| 'Custom'
| 'Advanced'
| 'No filter';
Expand Down Expand Up @@ -85,6 +86,18 @@ export type CalendarRangeType =
| typeof PreviousCalendarMonth
| typeof PreviousCalendarYear;

export const CurrentDay = 'Current day';
export const CurrentWeek = 'Current week';
export const CurrentMonth = 'Current month';
export const CurrentYear = 'Current year';
export const CurrentQuarter = 'Current quarter';
export type CurrentRangeType =
| typeof CurrentDay
| typeof CurrentWeek
| typeof CurrentMonth
| typeof CurrentQuarter
| typeof CurrentYear;

export type FrameComponentProps = {
onChange: (timeRange: string) => void;
value: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@ import {
PreviousCalendarYear,
CommonRangeType,
CalendarRangeType,
CurrentRangeType,
CurrentWeek,
CurrentMonth,
CurrentYear,
CurrentQuarter,
CurrentDay,
} from 'src/explore/components/controls/DateFilterControl/types';

export const FRAME_OPTIONS: SelectOptionType[] = [
{ value: 'Common', label: t('Last') },
{ value: 'Calendar', label: t('Previous') },
{ value: 'Current', label: t('Current') },
{ value: 'Custom', label: t('Custom') },
{ value: 'Advanced', label: t('Advanced') },
{ value: 'No filter', label: t('No filter') },
Expand All @@ -48,16 +55,24 @@ export const COMMON_RANGE_VALUES_SET = new Set(

export const CALENDAR_RANGE_OPTIONS: SelectOptionType[] = [
{ value: PreviousCalendarWeek, label: t('previous calendar week') },
{
value: PreviousCalendarMonth,
label: t('previous calendar month'),
},
{ value: PreviousCalendarMonth, label: t('previous calendar month') },
{ value: PreviousCalendarYear, label: t('previous calendar year') },
];
export const CALENDAR_RANGE_VALUES_SET = new Set(
CALENDAR_RANGE_OPTIONS.map(({ value }) => value),
);

export const CURRENT_RANGE_OPTIONS: SelectOptionType[] = [
{ value: CurrentDay, label: t('Current day') },
{ value: CurrentWeek, label: t('Current week') },
{ value: CurrentMonth, label: t('Current month') },
{ value: CurrentQuarter, label: t('Current quarter') },
{ value: CurrentYear, label: t('Current year') },
];
export const CURRENT_RANGE_VALUES_SET = new Set(
CURRENT_RANGE_OPTIONS.map(({ value }) => value),
);

const GRAIN_OPTIONS = [
{ value: 'second', label: (rel: string) => t('Seconds %s', rel) },
{ value: 'minute', label: (rel: string) => t('Minutes %s', rel) },
Expand Down Expand Up @@ -107,6 +122,14 @@ export const CALENDAR_RANGE_SET: Set<CalendarRangeType> = new Set([
PreviousCalendarYear,
]);

export const CURRENT_CALENDAR_RANGE_SET: Set<CurrentRangeType> = new Set([
CurrentDay,
CurrentWeek,
CurrentMonth,
CurrentQuarter,
CurrentYear,
]);

export const MOMENT_FORMAT = 'YYYY-MM-DD[T]HH:mm:ss';
export const SEVEN_DAYS_AGO = moment()
.utc()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useSelector } from 'react-redux';
import {
COMMON_RANGE_VALUES_SET,
CALENDAR_RANGE_VALUES_SET,
CURRENT_RANGE_VALUES_SET,
customTimeRangeDecode,
} from '.';
import { FrameType } from '../types';
Expand All @@ -32,6 +33,9 @@ export const guessFrame = (timeRange: string): FrameType => {
if (CALENDAR_RANGE_VALUES_SET.has(timeRange)) {
return 'Calendar';
}
if (CURRENT_RANGE_VALUES_SET.has(timeRange)) {
return 'Current';
}
if (timeRange === NO_TIME_RANGE) {
return 'No filter';
}
Expand Down
30 changes: 30 additions & 0 deletions superset/utils/date_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,36 @@ def get_since_until( # pylint: disable=too-many-arguments,too-many-locals,too-m
and separator not in time_range
):
time_range = "DATETRUNC(DATEADD(DATETIME('today'), -1, YEAR), YEAR) : DATETRUNC(DATETIME('today'), YEAR)" # pylint: disable=line-too-long,useless-suppression
if (
time_range
and time_range.startswith("Current day")
and separator not in time_range
):
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, DAY), DAY) : DATETRUNC(DATEADD(DATETIME('today'), 1, DAY), DAY)" # pylint: disable=line-too-long,useless-suppression
if (
time_range
and time_range.startswith("Current week")
and separator not in time_range
):
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, WEEK), WEEK) : DATETRUNC(DATEADD(DATETIME('today'), 1, WEEK), WEEK)" # pylint: disable=line-too-long,useless-suppression
if (
time_range
and time_range.startswith("Current month")
and separator not in time_range
):
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, MONTH), MONTH) : DATETRUNC(DATEADD(DATETIME('today'), 1, MONTH), MONTH)" # pylint: disable=line-too-long,useless-suppression
if (
time_range
and time_range.startswith("Current quarter")
and separator not in time_range
):
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, QUARTER), QUARTER) : DATETRUNC(DATEADD(DATETIME('today'), 1, QUARTER), QUARTER)" # pylint: disable=line-too-long,useless-suppression
if (
time_range
and time_range.startswith("Current year")
and separator not in time_range
):
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, YEAR), YEAR) : DATETRUNC(DATEADD(DATETIME('today'), 1, YEAR), YEAR)" # pylint: disable=line-too-long,useless-suppression

if time_range and separator in time_range:
time_range_lookup = [
Expand Down
20 changes: 20 additions & 0 deletions tests/unit_tests/utils/date_parser_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,26 @@ def test_get_since_until() -> None:
expected = datetime(2015, 1, 1, 0, 0, 0), datetime(2016, 1, 1, 0, 0, 0)
assert result == expected

result = get_since_until("Current day")
expected = datetime(2016, 11, 7, 0, 0, 0), datetime(2016, 11, 8, 0, 0, 0)
assert result == expected

result = get_since_until("Current week")
expected = datetime(2016, 11, 7, 0, 0, 0), datetime(2016, 11, 14, 0, 0, 0)
assert result == expected

result = get_since_until("Current month")
expected = datetime(2016, 11, 1, 0, 0, 0), datetime(2016, 12, 1, 0, 0, 0)
assert result == expected

result = get_since_until("Current quarter")
expected = datetime(2016, 10, 1, 0, 0, 0), datetime(2017, 1, 1, 0, 0, 0)
assert result == expected

result = get_since_until("Current year")
expected = expected = datetime(2016, 1, 1, 0, 0, 0), datetime(2017, 1, 1, 0, 0, 0)
assert result == expected

# Tests for our new instant_time_comparison logic and Feature Flag off
result = get_since_until(
time_range="2000-01-01T00:00:00 : 2018-01-01T00:00:00",
Expand Down

0 comments on commit 066f6b1

Please sign in to comment.