feat(plugin-chart-echarts): support non-timeseries x-axis (#17917)

* feat(plugin-chart-echarts): support non-timeseries x-axis

* fix tests

* change formula return type from Date to number

* add x_axis test coverage

* rename func and improve coverage

* add x-axis control to bar chart

* remove redundant console.log

* fix description

* make x-axis control mandatory

* 🙃

* fix x-axis formatter

* fix showValues

* fix implicit rDTTM_ALIAS references in postProcessing

* replace TIME_COLUMN with DTTM_ALIAS

* fix remaining implicit indexes

* fix: Disable filtering on wide result sets (#18021)

* fix: handle null values in time-series table (#18039)

* cleanup column_type_mappings (#17569)

Signed-off-by: Đặng Minh Dũng <dungdm93@live.com>

* important change to MakeFile (#18037)

* add missing is_timeseries to pivot op

Co-authored-by: Erik Ritter <erik.ritter@airbnb.com>
Co-authored-by: Grace Guo <grace.guo@airbnb.com>
Co-authored-by: Đặng Minh Dũng <dungdm93@live.com>
Co-authored-by: AAfghahi <48933336+AAfghahi@users.noreply.github.com>
This commit is contained in:
Ville Brofeldt
2022-01-21 21:23:23 +02:00
committed by GitHub
parent b083b3421f
commit e9651ea52f
42 changed files with 489 additions and 201 deletions

View File

@@ -34,11 +34,11 @@ import {
export function evalFormula(
formula: FormulaAnnotationLayer,
data: TimeseriesDataRecord[],
): [Date, number][] {
): [number, number][] {
const { value: expression } = formula;
return data.map(row => [
new Date(Number(row.__timestamp)),
Number(row.__timestamp),
evalExpression(expression, row.__timestamp as number),
]);
}

View File

@@ -16,7 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
import { TimeseriesDataRecord, NumberFormatter } from '@superset-ui/core';
import {
TimeseriesDataRecord,
NumberFormatter,
DTTM_ALIAS,
} from '@superset-ui/core';
import { CallbackDataParams, OptionName } from 'echarts/types/src/util/types';
import { TooltipMarker } from 'echarts/types/src/util/format';
import {
@@ -117,7 +121,7 @@ export function rebaseTimeseriesDatum(
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return data.map(row => {
const newRow: TimeseriesDataRecord = { __timestamp: '' };
const newRow: TimeseriesDataRecord = { [DTTM_ALIAS]: '' };
keys.forEach(key => {
const forecastContext = extractForecastSeriesContext(key);
const lowerKey = `${forecastContext.name}${ForecastSeriesEnum.ForecastLower}`;
@@ -131,7 +135,7 @@ export function rebaseTimeseriesDatum(
value -= row[lowerKey] as number;
}
const newKey =
key !== '__timestamp' && verboseMap[key] ? verboseMap[key] : key;
key !== DTTM_ALIAS && verboseMap[key] ? verboseMap[key] : key;
newRow[newKey] = value;
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-return

View File

@@ -21,11 +21,11 @@ import {
ChartDataResponseResult,
DataRecord,
DataRecordValue,
DTTM_ALIAS,
ensureIsArray,
GenericDataType,
NumberFormatter,
TimeFormatter,
TimeseriesDataRecord,
} from '@superset-ui/core';
import { format, LegendComponentOption, SeriesOption } from 'echarts';
import { NULL_STRING, TIMESERIES_CONSTANTS } from '../constants';
@@ -36,37 +36,40 @@ function isDefined<T>(value: T | undefined | null): boolean {
return value !== undefined && value !== null;
}
export function extractTimeseriesSeries(
data: TimeseriesDataRecord[],
opts: { fillNeighborValue?: number } = {},
export function extractSeries(
data: DataRecord[],
opts: {
fillNeighborValue?: number;
xAxis?: string;
removeNulls?: boolean;
} = {},
): SeriesOption[] {
const { fillNeighborValue } = opts;
const { fillNeighborValue, xAxis = DTTM_ALIAS, removeNulls = false } = opts;
if (data.length === 0) return [];
const rows: TimeseriesDataRecord[] = data.map(datum => ({
const rows: DataRecord[] = data.map(datum => ({
...datum,
__timestamp:
datum.__timestamp || datum.__timestamp === 0
? new Date(datum.__timestamp)
: null,
[xAxis]: datum[xAxis],
}));
return Object.keys(rows[0])
.filter(key => key !== '__timestamp')
.filter(key => key !== xAxis && key !== DTTM_ALIAS)
.map(key => ({
id: key,
name: key,
data: rows.map((row, idx) => {
const isNextToDefinedValue =
isDefined(rows[idx - 1]?.[key]) || isDefined(rows[idx + 1]?.[key]);
return [
row.__timestamp,
!isDefined(row[key]) &&
isNextToDefinedValue &&
fillNeighborValue !== undefined
? fillNeighborValue
: row[key],
];
}),
data: rows
.map((row, idx) => {
const isNextToDefinedValue =
isDefined(rows[idx - 1]?.[key]) || isDefined(rows[idx + 1]?.[key]);
return [
row[xAxis],
!isDefined(row[key]) &&
isNextToDefinedValue &&
fillNeighborValue !== undefined
? fillNeighborValue
: row[key],
];
})
.filter(obs => !removeNulls || (obs[0] !== null && obs[1] !== null)),
}));
}
@@ -102,7 +105,10 @@ export function formatSeriesName(
export const getColtypesMapping = ({
coltypes = [],
colnames = [],
}: ChartDataResponseResult): Record<string, GenericDataType> =>
}: Pick<ChartDataResponseResult, 'coltypes' | 'colnames'>): Record<
string,
GenericDataType
> =>
colnames.reduce(
(accumulator, item, index) => ({ ...accumulator, [item]: coltypes[index] }),
{},
@@ -119,7 +125,7 @@ export function extractGroupbyLabel({
groupby?: string[] | null;
numberFormatter?: NumberFormatter;
timeFormatter?: TimeFormatter;
coltypeMapping: Record<string, GenericDataType>;
coltypeMapping?: Record<string, GenericDataType>;
}): string {
return ensureIsArray(groupby)
.map(val =>