Compare commits

...

16 Commits

Author SHA1 Message Date
Enzo Martellucci
bec3d94a5b Merge branch master into enxdev/refactor/typescript-migration-ChartRenderer 2025-03-15 17:07:39 +01:00
Đỗ Trọng Hải
a49a15f990 chore(docs): remove customized "Edit this page on GitHub" button (#32407)
Signed-off-by: hainenber <dotronghai96@gmail.com>
2025-03-15 21:27:10 +07:00
Evan Rusackas
eb39ddbfe3 feat(docs): Adding Kapa.ai integration (#32682) 2025-03-15 12:34:42 +07:00
dependabot[bot]
974d36d35e chore(deps): bump jinja2 from 3.1.5 to 3.1.6 in /superset/translations (#32580)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-15 12:17:18 +07:00
Beto Dealmeida
b64e3254fc feat: DB migration for dataset folders (#32680) 2025-03-14 17:16:02 -04:00
Maxime Beauchemin
9907db9e1a feat: add a note to install cors-related dependency when using ENABLE_CORS (#32662) 2025-03-14 10:33:31 -07:00
Michael S. Molina
b4dd64aa24 fix: Update RELEASING/README.md (#32678) 2025-03-14 10:28:18 -07:00
Pedro Martin-Steenstrup
6e049225f9 docs: add Hometogo to users list (#32668) 2025-03-14 10:27:28 -07:00
Beto Dealmeida
831369a44b fix(gsheets): update params from encrypted extra (#32661) 2025-03-14 12:00:53 -04:00
Evan Rusackas
7c9c30db1d chore(examples): Touching up Vehicle Sales a bit (#32623) 2025-03-14 09:31:02 -06:00
Vitor Avila
0c6d868483 fix(import): Import a DB connection with expanded rows enabled (#32657) 2025-03-14 12:02:39 -03:00
Andrey Yakir
777760b096 fix(dashboard): Ensure dashboardId is included in form_data for embedded mode (#32646) 2025-03-14 10:36:42 -04:00
Vitor Avila
e8ad096173 fix(sync perms): Avoid UnboundLocalError during perm sync for DBs that don't support catalogs (#32658) 2025-03-13 21:07:49 -03:00
Enzo Martellucci
c805c96f5a Merge branch 'master' into enxdev/refactor/typescript-migration-ChartRenderer 2025-02-25 09:29:36 +01:00
Enzo Martellucci
a3ec4080e6 wip(ChartRenderer) 2025-02-10 11:01:19 +01:00
Enzo Martellucci
3f6e511048 wip(ChartRenderer): migrates ChartRenderer to Ts 2025-02-06 14:41:31 +01:00
31 changed files with 1855 additions and 890 deletions

View File

@@ -452,7 +452,7 @@ cd ../
# Compile translations for the backend
./scripts/translations/generate_po_files.sh
./scripts/translations/generate_mo_files.sh
# update build version number
sed -i '' "s/version_string = .*/version_string = \"$SUPERSET_VERSION\"/" setup.py

View File

@@ -199,6 +199,7 @@ Join our growing community!
### Travel
- [Agoda](https://www.agoda.com/) [@lostseaway, @maiake, @obombayo]
- [HomeToGo](https://hometogo.com/) [@pedromartinsteenstrup]
- [Skyscanner](https://www.skyscanner.net/) [@cleslie, @stanhoucke]
### Others

View File

@@ -333,6 +333,31 @@ const config: Config = {
// src: 'https://www.bugherd.com/sidebarv2.js?apikey=enilpiu7bgexxsnoqfjtxa',
// async: true,
// },
'/script/matomo.js',
{
src: 'https://widget.kapa.ai/kapa-widget.bundle.js',
async: true,
'data-website-id': 'c6a8a8b8-3127-48f9-97a7-51e9e10d20d0',
'data-project-name': 'Apache Superset',
'data-project-color': '#1AA1C2',
'data-project-logo':
'https://images.seeklogo.com/logo-png/50/2/superset-icon-logo-png_seeklogo-500354.png',
'data-modal-override-open-id': 'ask-ai-input',
'data-modal-override-open-class': 'search-input',
'data-modal-open-by-default': 'true',
'data-modal-disclaimer':
'This is a custom LLM for Apache Superset with access to all [documentation](superset.apache.org/docs/intro/), [GitHub Open Issues, PRs and READMEs](github.com/apache/superset).&#10;&#10;Companies deploy assistants like this ([built by kapa.ai](https://kapa.ai)) on docs via [website widget](https://docs.kapa.ai/integrations/website-widget) (Docker, Reddit), in [support forms](https://docs.kapa.ai/integrations/support-form-deflector) for ticket deflection (Monday.com, Mapbox), or as [Slack bots](https://docs.kapa.ai/integrations/slack-bot) with private sources.',
'data-modal-example-questions':
'How do I use Docker Compose?,How to run Supersets on kubernetes?',
'data-button-text-color': '#FFFFFF',
'data-modal-header-bg-color': '#1AA1C2',
'data-modal-title-color': '#FFFFFF',
'data-modal-title': 'Superset Ask AI',
'data-modal-disclaimer-text-color': '#000000',
'data-consent-required': 'true',
'data-consent-screen-disclaimer':
"By clicking \"I agree, let's chat\", you consent to the use of the AI assistant in accordance with kapa.ai's [Privacy Policy](https://www.kapa.ai/content/privacy-policy). This service uses reCAPTCHA, which requires your consent to Google's [Privacy Policy](https://policies.google.com/privacy) and [Terms of Service](https://policies.google.com/terms). By proceeding, you explicitly agree to both kapa.ai's and Google's privacy policies.",
},
],
customFields: {
matomoUrl: 'https://analytics.apache.org',

View File

@@ -256,13 +256,3 @@ a > span > svg {
height: 28px;
}
}
/* Edit Button */
.edit-page-link {
position: sticky;
bottom: 0px;
right: 0px;
border-radius: 10px;
background-color: #ccc;
}

View File

@@ -1,61 +0,0 @@
/**
* 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 styled from '@emotion/styled';
import DocItem from '@theme-original/DocItem';
const EditPageLink = styled('a')`
position: fixed;
bottom: 40px;
right: 10px;
padding: 1rem;
padding-left: 4rem;
background-color: #444;
border-radius: 10px;
z-index: 9999;
background-image: url('/img/github-dark.png');
background-size: 2rem;
background-position: 1rem center;
background-repeat: no-repeat;
transition: background-color 0.3s; /* Smooth transition for hover effect */
bpx-shadow: 0 0 0 0 rgba(0, 0, 0, 0); /* Smooth transition for hover effect */
scale: 0.9;
transition: all 0.3s;
transform-origin: bottom right;
&:hover {
background-color: #333;
box-shadow: 5px 5px 10px 0 rgba(0, 0, 0, 0.3);
scale: 1;
}
`;
export default function DocItemWrapper(props) {
return (
<>
<EditPageLink
href={props.content.metadata.editUrl}
target="_blank"
rel="noopener noreferrer"
>
Edit this page on GitHub
</EditPageLink>
<DocItem {...props} />
</>
);
}

View File

@@ -17,15 +17,16 @@
* under the License.
*/
import { PureComponent } from 'react';
import { Dispatch } from 'redux';
import {
ensureIsArray,
FeatureFlag,
isFeatureEnabled,
logging,
SqlaFormData,
QueryFormData,
styled,
t,
SqlaFormData,
ClientErrorObject,
ChartDataResponse,
} from '@superset-ui/core';
@@ -39,53 +40,11 @@ import { getUrlParam } from 'src/utils/urlUtils';
import { isCurrentUserBot } from 'src/utils/isBot';
import { ChartSource } from 'src/types/ChartSource';
import { ResourceStatus } from 'src/hooks/apiResources/apiResources';
import { Dispatch } from 'redux';
import { Annotation } from 'src/explore/components/controls/AnnotationLayerControl';
import ChartRenderer from './ChartRenderer';
import { ChartErrorMessage } from './ChartErrorMessage';
import { getChartRequiredFieldsMissingMessage } from '../../utils/getChartRequiredFieldsMissingMessage';
export type ChartErrorType = Partial<ClientErrorObject>;
export interface ChartProps {
annotationData?: Annotation;
actions: Actions;
chartId: string;
datasource?: {
database?: {
name: string;
};
};
dashboardId?: number;
initialValues?: object;
formData: QueryFormData;
labelColors?: string;
sharedLabelColors?: string;
width: number;
height: number;
setControlValue: Function;
timeout?: number;
vizType: string;
triggerRender?: boolean;
force?: boolean;
isFiltersInitialized?: boolean;
chartAlert?: string;
chartStatus?: string;
chartStackTrace?: string;
queriesResponse: ChartDataResponse[];
triggerQuery?: boolean;
chartIsStale?: boolean;
errorMessage?: React.ReactNode;
addFilter?: (type: string) => void;
onQuery?: () => void;
onFilterMenuOpen?: (chartId: string, column: string) => void;
onFilterMenuClose?: (chartId: string, column: string) => void;
ownState: boolean;
postTransformProps?: Function;
datasetsStatus?: 'loading' | 'error' | 'complete';
isInView?: boolean;
emitCrossFilters?: boolean;
}
export type Actions = {
logEvent(
LOG_ACTIONS_RENDER_CHART: string,
@@ -111,7 +70,51 @@ export type Actions = {
dashboardId: number | undefined,
ownState: boolean,
): Dispatch;
chartRenderingSucceeded(arg0: { key: string }): Dispatch;
updateDataMask(chartId: string, dataMask: { dataMask: any }): Dispatch;
};
export type ChartErrorType = Partial<ClientErrorObject>;
export interface ChartProps {
annotationData?: Annotation;
actions: Actions;
chartId: string;
datasource?: {
database?: {
name: string;
};
};
dashboardId?: number;
initialValues?: object;
formData: QueryFormData;
labelColors?: string;
sharedLabelColors?: string;
width: number;
height: number;
setControlValue: Function;
timeout?: number;
vizType: string;
triggerRender?: boolean;
force?: boolean;
chartAlert?: string;
chartStatus?: string;
chartStackTrace?: string;
queriesResponse: ChartDataResponse[];
triggerQuery?: boolean;
chartIsStale?: boolean;
addFilter?: (type: string) => void;
onQuery?: () => void;
onFilterMenuOpen?: (chartId: string, column: string) => void;
onFilterMenuClose?: (chartId: string, column: string) => void;
ownState: boolean;
postTransformProps?: Function;
datasetsStatus?: 'loading' | 'error' | 'complete';
emitCrossFilters?: boolean;
errorMessage?: React.ReactNode;
isInView?: boolean;
filters?: string | string[];
}
const BLANK = {};
const NONEXISTENT_DATASET = t(
'The dataset associated with this chart no longer exists',
@@ -172,6 +175,12 @@ const MessageSpan = styled.span`
color: ${({ theme }) => theme.colors.grayscale.base};
`;
const MonospaceDiv = styled.div`
font-family: ${({ theme }) => theme.typography.families.monospace};
word-break: break-word;
overflow-x: auto;
white-space: pre-wrap;
`;
class Chart extends PureComponent<ChartProps, {}> {
static defaultProps = defaultProps;
@@ -267,7 +276,8 @@ class Chart extends PureComponent<ChartProps, {}> {
key={chartId}
chartId={chartId}
error={error}
subtitle={message}
subtitle={<MonospaceDiv>{message}</MonospaceDiv>}
copyText={message}
link={queryResponse ? queryResponse.link : undefined}
source={dashboardId ? ChartSource.Dashboard : ChartSource.Explore}
stackTrace={chartStackTrace}
@@ -296,7 +306,11 @@ class Chart extends PureComponent<ChartProps, {}> {
isCurrentUserBot() ? (
<ChartRenderer
{...this.props}
source={this.props.dashboardId ? 'dashboard' : 'explore'}
source={
this.props.dashboardId
? ChartSource.Dashboard
: ChartSource.Explore
}
data-test={this.props.vizType}
/>
) : (
@@ -331,7 +345,6 @@ class Chart extends PureComponent<ChartProps, {}> {
if (errorMessage && ensureIsArray(queriesResponse).length === 0) {
return (
<EmptyState
size="large"
title={t('Add required control values to preview chart')}
description={getChartRequiredFieldsMissingMessage(true)}
image="chart.svg"
@@ -347,7 +360,6 @@ class Chart extends PureComponent<ChartProps, {}> {
) {
return (
<EmptyState
size="large"
title={t('Your chart is ready to go!')}
description={
<span>

View File

@@ -1,381 +0,0 @@
/**
* 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 { snakeCase, isEqual, cloneDeep } from 'lodash';
import PropTypes from 'prop-types';
import { createRef, Component } from 'react';
import {
SuperChart,
logging,
Behavior,
t,
getChartMetadataRegistry,
VizType,
isFeatureEnabled,
FeatureFlag,
} from '@superset-ui/core';
import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils';
import { EmptyState } from 'src/components/EmptyState';
import { ChartSource } from 'src/types/ChartSource';
import ChartContextMenu from './ChartContextMenu/ChartContextMenu';
const propTypes = {
annotationData: PropTypes.object,
actions: PropTypes.object,
chartId: PropTypes.number.isRequired,
datasource: PropTypes.object,
initialValues: PropTypes.object,
formData: PropTypes.object.isRequired,
latestQueryFormData: PropTypes.object,
labelsColor: PropTypes.object,
labelsColorMap: PropTypes.object,
height: PropTypes.number,
width: PropTypes.number,
setControlValue: PropTypes.func,
vizType: PropTypes.string.isRequired,
triggerRender: PropTypes.bool,
// state
chartAlert: PropTypes.string,
chartStatus: PropTypes.string,
queriesResponse: PropTypes.arrayOf(PropTypes.object),
triggerQuery: PropTypes.bool,
chartIsStale: PropTypes.bool,
// dashboard callbacks
addFilter: PropTypes.func,
setDataMask: PropTypes.func,
onFilterMenuOpen: PropTypes.func,
onFilterMenuClose: PropTypes.func,
ownState: PropTypes.object,
postTransformProps: PropTypes.func,
source: PropTypes.oneOf([ChartSource.Dashboard, ChartSource.Explore]),
emitCrossFilters: PropTypes.bool,
};
const BLANK = {};
const BIG_NO_RESULT_MIN_WIDTH = 300;
const BIG_NO_RESULT_MIN_HEIGHT = 220;
const behaviors = [Behavior.InteractiveChart];
const defaultProps = {
addFilter: () => BLANK,
onFilterMenuOpen: () => BLANK,
onFilterMenuClose: () => BLANK,
initialValues: BLANK,
setControlValue() {},
triggerRender: false,
};
class ChartRenderer extends Component {
constructor(props) {
super(props);
const suppressContextMenu = getChartMetadataRegistry().get(
props.formData.viz_type ?? props.vizType,
)?.suppressContextMenu;
this.state = {
showContextMenu:
props.source === ChartSource.Dashboard &&
!suppressContextMenu &&
isFeatureEnabled(FeatureFlag.DrillToDetail),
inContextMenu: false,
legendState: undefined,
};
this.hasQueryResponseChange = false;
this.contextMenuRef = createRef();
this.handleAddFilter = this.handleAddFilter.bind(this);
this.handleRenderSuccess = this.handleRenderSuccess.bind(this);
this.handleRenderFailure = this.handleRenderFailure.bind(this);
this.handleSetControlValue = this.handleSetControlValue.bind(this);
this.handleOnContextMenu = this.handleOnContextMenu.bind(this);
this.handleContextMenuSelected = this.handleContextMenuSelected.bind(this);
this.handleContextMenuClosed = this.handleContextMenuClosed.bind(this);
this.handleLegendStateChanged = this.handleLegendStateChanged.bind(this);
this.onContextMenuFallback = this.onContextMenuFallback.bind(this);
this.hooks = {
onAddFilter: this.handleAddFilter,
onContextMenu: this.state.showContextMenu
? this.handleOnContextMenu
: undefined,
onError: this.handleRenderFailure,
setControlValue: this.handleSetControlValue,
onFilterMenuOpen: this.props.onFilterMenuOpen,
onFilterMenuClose: this.props.onFilterMenuClose,
onLegendStateChanged: this.handleLegendStateChanged,
setDataMask: dataMask => {
this.props.actions?.updateDataMask(this.props.chartId, dataMask);
},
};
// TODO: queriesResponse comes from Redux store but it's being edited by
// the plugins, hence we need to clone it to avoid state mutation
// until we change the reducers to use Redux Toolkit with Immer
this.mutableQueriesResponse = cloneDeep(this.props.queriesResponse);
}
shouldComponentUpdate(nextProps, nextState) {
const resultsReady =
nextProps.queriesResponse &&
['success', 'rendered'].indexOf(nextProps.chartStatus) > -1 &&
!nextProps.queriesResponse?.[0]?.error;
if (resultsReady) {
if (!isEqual(this.state, nextState)) {
return true;
}
this.hasQueryResponseChange =
nextProps.queriesResponse !== this.props.queriesResponse;
if (this.hasQueryResponseChange) {
this.mutableQueriesResponse = cloneDeep(nextProps.queriesResponse);
}
return (
this.hasQueryResponseChange ||
!isEqual(nextProps.datasource, this.props.datasource) ||
nextProps.annotationData !== this.props.annotationData ||
nextProps.ownState !== this.props.ownState ||
nextProps.filterState !== this.props.filterState ||
nextProps.height !== this.props.height ||
nextProps.width !== this.props.width ||
nextProps.triggerRender ||
nextProps.labelsColor !== this.props.labelsColor ||
nextProps.labelsColorMap !== this.props.labelsColorMap ||
nextProps.formData.color_scheme !== this.props.formData.color_scheme ||
nextProps.formData.stack !== this.props.formData.stack ||
nextProps.cacheBusterProp !== this.props.cacheBusterProp ||
nextProps.emitCrossFilters !== this.props.emitCrossFilters
);
}
return false;
}
handleAddFilter(col, vals, merge = true, refresh = true) {
this.props.addFilter(col, vals, merge, refresh);
}
handleRenderSuccess() {
const { actions, chartStatus, chartId, vizType } = this.props;
if (['loading', 'rendered'].indexOf(chartStatus) < 0) {
actions.chartRenderingSucceeded(chartId);
}
// only log chart render time which is triggered by query results change
// currently we don't log chart re-render time, like window resize etc
if (this.hasQueryResponseChange) {
actions.logEvent(LOG_ACTIONS_RENDER_CHART, {
slice_id: chartId,
viz_type: vizType,
start_offset: this.renderStartTime,
ts: new Date().getTime(),
duration: Logger.getTimestamp() - this.renderStartTime,
});
}
}
handleRenderFailure(error, info) {
const { actions, chartId } = this.props;
logging.warn(error);
actions.chartRenderingFailed(
error.toString(),
chartId,
info ? info.componentStack : null,
);
// only trigger render log when query is changed
if (this.hasQueryResponseChange) {
actions.logEvent(LOG_ACTIONS_RENDER_CHART, {
slice_id: chartId,
has_err: true,
error_details: error.toString(),
start_offset: this.renderStartTime,
ts: new Date().getTime(),
duration: Logger.getTimestamp() - this.renderStartTime,
});
}
}
handleSetControlValue(...args) {
const { setControlValue } = this.props;
if (setControlValue) {
setControlValue(...args);
}
}
handleOnContextMenu(offsetX, offsetY, filters) {
this.contextMenuRef.current.open(offsetX, offsetY, filters);
this.setState({ inContextMenu: true });
}
handleContextMenuSelected() {
this.setState({ inContextMenu: false });
}
handleContextMenuClosed() {
this.setState({ inContextMenu: false });
}
handleLegendStateChanged(legendState) {
this.setState({ legendState });
}
// When viz plugins don't handle `contextmenu` event, fallback handler
// calls `handleOnContextMenu` with no `filters` param.
onContextMenuFallback(event) {
if (!this.state.inContextMenu) {
event.preventDefault();
this.handleOnContextMenu(event.clientX, event.clientY);
}
}
render() {
const { chartAlert, chartStatus, chartId, emitCrossFilters } = this.props;
// Skip chart rendering
if (chartStatus === 'loading' || !!chartAlert || chartStatus === null) {
return null;
}
this.renderStartTime = Logger.getTimestamp();
const {
width,
height,
datasource,
annotationData,
initialValues,
ownState,
filterState,
chartIsStale,
formData,
latestQueryFormData,
postTransformProps,
} = this.props;
const currentFormData =
chartIsStale && latestQueryFormData ? latestQueryFormData : formData;
const vizType = currentFormData.viz_type || this.props.vizType;
// It's bad practice to use unprefixed `vizType` as classnames for chart
// container. It may cause css conflicts as in the case of legacy table chart.
// When migrating charts, we should gradually add a `superset-chart-` prefix
// to each one of them.
const snakeCaseVizType = snakeCase(vizType);
const chartClassName =
vizType === VizType.Table
? `superset-chart-${snakeCaseVizType}`
: snakeCaseVizType;
const webpackHash =
process.env.WEBPACK_MODE === 'development'
? `-${
// eslint-disable-next-line camelcase
typeof __webpack_require__ !== 'undefined' &&
// eslint-disable-next-line camelcase, no-undef
typeof __webpack_require__.h === 'function' &&
// eslint-disable-next-line no-undef, camelcase
__webpack_require__.h()
}`
: '';
let noResultsComponent;
const noResultTitle = t('No results were returned for this query');
const noResultDescription =
this.props.source === ChartSource.Explore
? t(
'Make sure that the controls are configured properly and the datasource contains data for the selected time range',
)
: undefined;
const noResultImage = 'chart.svg';
if (width > BIG_NO_RESULT_MIN_WIDTH && height > BIG_NO_RESULT_MIN_HEIGHT) {
noResultsComponent = (
<EmptyState
size="large"
title={noResultTitle}
description={noResultDescription}
image={noResultImage}
/>
);
} else {
noResultsComponent = (
<EmptyState size="small" title={noResultTitle} image={noResultImage} />
);
}
// Check for Behavior.DRILL_TO_DETAIL to tell if chart can receive Drill to
// Detail props or if it'll cause side-effects (e.g. excessive re-renders).
const drillToDetailProps = getChartMetadataRegistry()
.get(formData.viz_type)
?.behaviors.find(behavior => behavior === Behavior.DrillToDetail)
? { inContextMenu: this.state.inContextMenu }
: {};
return (
<>
{this.state.showContextMenu && (
<ChartContextMenu
ref={this.contextMenuRef}
id={chartId}
formData={currentFormData}
onSelection={this.handleContextMenuSelected}
onClose={this.handleContextMenuClosed}
/>
)}
<div
onContextMenu={
this.state.showContextMenu ? this.onContextMenuFallback : undefined
}
>
<SuperChart
disableErrorBoundary
key={`${chartId}${webpackHash}`}
id={`chart-id-${chartId}`}
className={chartClassName}
chartType={vizType}
width={width}
height={height}
annotationData={annotationData}
datasource={datasource}
initialValues={initialValues}
formData={currentFormData}
ownState={ownState}
filterState={filterState}
hooks={this.hooks}
behaviors={behaviors}
queriesData={this.mutableQueriesResponse}
onRenderSuccess={this.handleRenderSuccess}
onRenderFailure={this.handleRenderFailure}
noResults={noResultsComponent}
postTransformProps={postTransformProps}
emitCrossFilters={emitCrossFilters}
legendState={this.state.legendState}
{...drillToDetailProps}
/>
</div>
</>
);
}
}
ChartRenderer.propTypes = propTypes;
ChartRenderer.defaultProps = defaultProps;
export default ChartRenderer;

View File

@@ -0,0 +1,418 @@
/**
* 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 { snakeCase, cloneDeep, isEqual } from 'lodash';
import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
import {
SuperChart,
logging,
Behavior,
t,
isFeatureEnabled,
FeatureFlag,
getChartMetadataRegistry,
QueryFormData,
ChartDataResponse,
VizType as enumVizType,
JsonObject,
FilterState,
} from '@superset-ui/core';
import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils';
import { EmptyState } from 'src/components/EmptyState';
import { ChartSource } from 'src/types/ChartSource';
import { Annotation } from 'src/explore/components/controls/AnnotationLayerControl';
import ChartContextMenu from './ChartContextMenu/ChartContextMenu';
import { Actions } from './Chart';
type ChartRendererProps = {
dashboardId?: number;
latestQueryFormData?: QueryFormData;
labelsColorMap?: string;
setDataMask?: (dataMask: any) => void;
source?: ChartSource;
annotationData?: Annotation;
actions: Actions;
chartId: string;
datasource?: {
database?: {
name: string;
};
};
initialValues?: object;
formData: QueryFormData;
labelsColor?: string;
height?: number;
width?: number;
setControlValue?: Function;
vizType: string;
triggerRender?: boolean;
chartAlert?: string;
chartStatus?: string;
queriesResponse?: ChartDataResponse[];
triggerQuery?: boolean;
chartIsStale?: boolean;
filterState?: FilterState[];
addFilter?: (
col: string,
vals: string | string[],
merge: boolean,
refresh: boolean,
) => void;
onFilterMenuOpen?: (chartId: string, column: string) => void;
onFilterMenuClose?: (chartId: string, column: string) => void;
ownState?: boolean | JsonObject;
postTransformProps?: Function;
emitCrossFilters?: boolean;
};
const BLANK = {};
const BIG_NO_RESULT_MIN_WIDTH = 300;
const BIG_NO_RESULT_MIN_HEIGHT = 220;
const behaviors = [Behavior.InteractiveChart];
const ChartRenderer = (props: ChartRendererProps) => {
const {
annotationData,
actions,
chartId,
datasource,
initialValues = BLANK,
formData,
latestQueryFormData,
height,
width,
setControlValue,
vizType,
chartStatus,
queriesResponse = [],
chartIsStale,
chartAlert,
addFilter = () => BLANK,
setDataMask,
onFilterMenuOpen = () => BLANK,
onFilterMenuClose = () => BLANK,
ownState,
filterState,
postTransformProps,
source,
emitCrossFilters,
triggerRender,
labelsColor,
labelsColorMap,
} = props;
if (chartStatus === 'loading' || !!chartAlert || chartStatus === null) {
return null;
}
const suppressContextMenu = getChartMetadataRegistry().get(
formData.viz_type ?? vizType,
)?.suppressContextMenu;
const [showContextMenu, setShowContextMenu] = useState<Boolean>(false);
const [inContextMenu, setInContextMenu] = useState(false);
const [legendState, setLegendState] = useState<any>(undefined);
const contextMenuRef = useRef<any>(null);
const prevProps = useRef(props);
const mutableQueriesResponse = useRef(cloneDeep(queriesResponse));
const [hasQueryResponseChange, setHasQueryResponseChange] = useState(false);
const renderStartTime = useRef<number>(0);
const resultsReady = useMemo(
() =>
queriesResponse &&
['success', 'rendered'].includes(chartStatus) &&
!queriesResponse?.[0]?.error,
[queriesResponse, chartStatus],
);
const queryResponseChanged = useMemo(
() => queriesResponse !== prevProps.current.queriesResponse,
[queriesResponse],
);
const shouldRender = useMemo(
() =>
resultsReady &&
(queryResponseChanged ||
!isEqual(datasource, prevProps.current.datasource) ||
annotationData !== prevProps.current.annotationData ||
ownState !== prevProps.current.ownState ||
filterState !== prevProps.current.filterState ||
height !== prevProps.current.height ||
width !== prevProps.current.width ||
triggerRender ||
labelsColor !== prevProps.current.labelsColor ||
labelsColorMap !== prevProps.current.labelsColorMap ||
formData.color_scheme !== prevProps.current.formData.color_scheme ||
formData.stack !== prevProps.current.formData.stack ||
emitCrossFilters !== prevProps.current.emitCrossFilters),
[resultsReady, queryResponseChanged, props],
);
if (!shouldRender) {
return null;
}
useEffect(() => {
if (queryResponseChanged) {
setHasQueryResponseChange(true);
mutableQueriesResponse.current = cloneDeep(queriesResponse);
}
}, [queryResponseChanged, queriesResponse]);
useEffect(() => {
prevProps.current = props;
}, [props]);
useEffect(() => {
mutableQueriesResponse.current = cloneDeep(queriesResponse);
}, [queriesResponse]);
useEffect(() => {
setShowContextMenu(
source === ChartSource.Dashboard &&
!suppressContextMenu &&
isFeatureEnabled(FeatureFlag.DrillToDetail),
);
}, [source, suppressContextMenu]);
useEffect(() => {
// only log chart render time which is triggered by query results change
// currently we don't log chart re-render time, like window resize etc
if (hasQueryResponseChange) {
actions.logEvent(LOG_ACTIONS_RENDER_CHART, {
slice_id: chartId,
has_err: false,
error_details: '',
start_offset: renderStartTime.current,
ts: new Date().getTime(),
duration: Logger.getTimestamp() - renderStartTime.current,
});
}
}, [hasQueryResponseChange]);
/**
* Hooks region
*/
const handleAddFilter = useCallback(
(col: string, vals: string | string[], merge = true, refresh = true) => {
alert(col);
console.log(col, vals, merge, refresh);
addFilter(col, vals, merge, refresh);
},
[],
);
const handleOnContextMenu = useCallback(
(offsetX: number, offsetY: number, filters: undefined) => {
if (contextMenuRef.current) {
contextMenuRef.current.open(offsetX, offsetY, filters);
}
// setInContextMenu({ inContextMenu: true });
// setInContextMenu(true);
},
[],
);
const handleSetControlValue = useCallback(
(...args: string[]) => {
if (setControlValue) {
setControlValue(...args);
}
},
[setControlValue],
);
const handleRenderFailure = (
error: { toString: () => string },
info: { componentStack: string } | null,
) => {
logging.warn(error);
actions.chartRenderingFailed(
error.toString(),
chartId,
info ? info.componentStack : null,
);
// only trigger render log when query is changed
if (hasQueryResponseChange) {
actions.logEvent(LOG_ACTIONS_RENDER_CHART, {
slice_id: chartId,
has_err: true,
error_details: error.toString(),
start_offset: renderStartTime.current,
ts: new Date().getTime(),
duration: Logger.getTimestamp() - renderStartTime.current,
});
}
};
const handleRenderSuccess = useCallback(() => {
if (!['loading', 'rendered'].includes(chartStatus || '')) {
actions.chartRenderingSucceeded({ key: chartId });
}
actions.logEvent(LOG_ACTIONS_RENDER_CHART, {
slice_id: chartId,
has_err: false,
error_details: '',
start_offset: renderStartTime.current,
ts: new Date().getTime(),
duration: Logger.getTimestamp() - renderStartTime.current,
});
}, [actions, chartId, chartStatus, vizType]);
// end Hooks region
const currentFormData =
chartIsStale && latestQueryFormData ? latestQueryFormData : formData;
const snakeCaseVizType = snakeCase(currentFormData.viz_type || vizType);
const chartClassName =
vizType === enumVizType.Table
? `superset-chart-${snakeCaseVizType}`
: snakeCaseVizType;
const webpackHash =
process.env.WEBPACK_MODE === 'development'
? `-${
// eslint-disable-next-line camelcase
// @ts-ignore
typeof __webpack_require__ !== 'undefined' &&
// @ts-ignore
typeof __webpack_require__.h === 'function' &&
// eslint-disable-next-line camelcase, no-undef
// @ts-ignore
typeof __webpack_require__.h === 'function' &&
// eslint-disable-next-line no-undef, camelcase
// @ts-ignore
__webpack_require__.h()
}`
: '';
let noResultsComponent;
const noResultTitle = t('No results were returned for this query');
const noResultDescription =
source === ChartSource.Explore
? t(
'Make sure that the controls are configured properly and the datasource contains data for the selected time range',
)
: undefined;
const noResultImage = 'chart.svg';
if (
typeof width === 'number' &&
typeof height === 'number' &&
width > BIG_NO_RESULT_MIN_WIDTH &&
height > BIG_NO_RESULT_MIN_HEIGHT
) {
noResultsComponent = (
<EmptyState
size="large"
title={noResultTitle}
description={noResultDescription}
image={noResultImage}
/>
);
} else {
noResultsComponent = (
<EmptyState size="small" title={noResultTitle} image={noResultImage} />
);
}
// Check for Behavior.DRILL_TO_DETAIL to tell if chart can receive Drill to
// Detail props or if it'll cause side-effects (e.g. excessive re-renders).
const drillToDetailProps = getChartMetadataRegistry()
.get(formData.viz_type)
?.behaviors.find(behavior => behavior === Behavior.DrillToDetail)
? { inContextMenu }
: {};
const hooks = useMemo(
() => ({
onAddFilter: handleAddFilter,
onContextMenu: showContextMenu ? handleOnContextMenu : undefined,
onError: handleRenderFailure,
setControlValue: handleSetControlValue,
onFilterMenuOpen,
onFilterMenuClose,
onLegendChange: setLegendState,
setDataMask: (dataMask: any) =>
actions?.updateDataMask(chartId, { dataMask }),
}),
[
handleAddFilter,
showContextMenu,
handleOnContextMenu,
handleRenderFailure,
handleSetControlValue,
onFilterMenuOpen,
onFilterMenuClose,
setLegendState,
setDataMask,
chartId,
],
);
return (
<>
{showContextMenu && (
<ChartContextMenu
ref={contextMenuRef}
id={chartId as unknown as number}
formData={currentFormData}
onSelection={() => setInContextMenu(false)}
onClose={() => setInContextMenu(false)}
/>
)}
<div
onContextMenu={
showContextMenu
? event => {
event.preventDefault();
handleOnContextMenu(event.clientX, event.clientY, undefined);
}
: undefined
}
>
<SuperChart
disableErrorBoundary
key={`${chartId}${webpackHash}`}
id={`chart-id-${chartId}`}
className={chartClassName}
chartType={vizType}
width={width}
height={height}
annotationData={annotationData}
datasource={datasource}
initialValues={initialValues}
formData={currentFormData}
ownState={ownState}
filterState={filterState}
hooks={hooks}
behaviors={behaviors}
queriesData={mutableQueriesResponse.current}
onRenderSuccess={handleRenderSuccess}
onRenderFailure={handleRenderFailure}
noResults={noResultsComponent}
postTransformProps={postTransformProps}
// emitCrossFilters={emitCrossFilters}
legendState={legendState}
{...drillToDetailProps}
/>
</div>
</>
);
};
export default ChartRenderer;

View File

@@ -157,6 +157,7 @@ const Chart = props => {
state.datasources[chart.form_data.datasource]) ||
PLACEHOLDER_DATASOURCE,
);
const dashboardInfo = useSelector(state => state.dashboardInfo);
const [descriptionHeight, setDescriptionHeight] = useState(0);
const [height, setHeight] = useState(props.height);
@@ -304,6 +305,8 @@ const Chart = props => {
],
);
formData.dashboardId = dashboardInfo.id;
const onExploreChart = useCallback(
async clickEvent => {
const isOpenInNewTab =

View File

@@ -74,6 +74,7 @@ const defaultState = {
datasources: mockDatasource,
dashboardState: { editMode: false, expandedSlices: {} },
dashboardInfo: {
id: props.dashboardId,
superset_can_explore: false,
superset_can_share: false,
superset_can_csv: false,
@@ -165,7 +166,9 @@ test('should call exportChart when exportCSV is clicked', async () => {
expect(stubbedExportCSV).toHaveBeenCalledTimes(1);
expect(stubbedExportCSV).toHaveBeenCalledWith(
expect.objectContaining({
formData: expect.anything(),
formData: expect.objectContaining({
dashboardId: 111,
}),
resultType: 'full',
resultFormat: 'csv',
}),
@@ -195,6 +198,7 @@ test('should call exportChart with row_limit props.maxRows when exportFullCSV is
expect.objectContaining({
formData: expect.objectContaining({
row_limit: 666,
dashboardId: 111,
}),
resultType: 'full',
resultFormat: 'csv',
@@ -249,6 +253,7 @@ test('should call exportChart with row_limit props.maxRows when exportFullXLSX i
expect.objectContaining({
formData: expect.objectContaining({
row_limit: 666,
dashboardId: 111,
}),
resultType: 'full',
resultFormat: 'xlsx',

View File

@@ -249,11 +249,11 @@ class SyncPermissionsCommand(BaseCommand):
self, catalog: str | None, schemas: Iterable[str]
) -> None:
# rename existing catalog permission
new_catalog_perm_name = security_manager.get_catalog_perm(
self.db_connection.name,
catalog,
)
if catalog:
new_catalog_perm_name = security_manager.get_catalog_perm(
self.db_connection.name,
catalog,
)
new_catalog_vm = add_vm(db.session, security_manager, new_catalog_perm_name)
perm = security_manager.get_catalog_perm(
self.old_db_connection_name,

View File

@@ -817,6 +817,8 @@ EXPLORE_FORM_DATA_CACHE_CONFIG: CacheConfig = {
STORE_CACHE_KEYS_IN_METADATA_DB = False
# CORS Options
# NOTE: enabling this requires installing the cors-related python dependencies
# `pip install .[cors]` or `pip install apache-superset[cors]`, depending
ENABLE_CORS = False
CORS_OPTIONS: dict[Any, Any] = {}

View File

@@ -831,6 +831,7 @@ class ImportV1DatabaseExtraSchema(Schema):
disable_drill_to_detail = fields.Boolean(required=False)
allow_multi_catalog = fields.Boolean(required=False)
version = fields.String(required=False, allow_none=True)
schema_options = fields.Dict(keys=fields.Str(), values=fields.Raw())
class ImportV1DatabaseSchema(Schema):

View File

@@ -188,8 +188,10 @@ class GSheetsEngineSpec(ShillelaghEngineSpec):
"""
Remove `oauth2_client_info` from `encrypted_extra`.
"""
if "oauth2_client_info" in params.get("encrypted_extra", {}):
del params["encrypted_extra"]["oauth2_client_info"]
ShillelaghEngineSpec.update_params_from_encrypted_extra(database, params)
if "oauth2_client_info" in params:
del params["oauth2_client_info"]
@classmethod
def get_parameters_from_uri(

View File

@@ -0,0 +1,73 @@
# 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.
slice_name: Items Sold
description: null
certified_by: null
certification_details: null
viz_type: big_number
params:
datasource: 21__table
viz_type: big_number
slice_id: 115
x_axis: order_date
metric:
aggregate: SUM
column:
column_name: quantity_ordered
description: null
expression: null
filterable: true
groupby: true
id: 914
is_dttm: false
python_date_format: null
type: BIGINT
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: false
isNew: false
label: SUM(Sales)
optionName: metric_twq59hf4ej_g70qjfmehsq
sqlExpression: null
adhoc_filters:
- clause: WHERE
comparator: No filter
expressionType: SIMPLE
operator: TEMPORAL_RANGE
subject: order_date
show_trend_line: true
start_y_axis_at_zero: true
color_picker:
a: 1
b: 135
g: 122
r: 0
header_font_size: 0.4
subheader_font_size: 0.15
y_axis_format: SMART_NUMBER
time_format: smart_date
rolling_type: cumsum
extra_form_data: {}
dashboards:
- 9
query_context: '{"datasource":{"id":21,"type":"table"},"force":false,"queries":[{"filters":[{"col":"order_date","op":"TEMPORAL_RANGE","val":"No
filter"}],"extras":{"having":"","where":""},"applied_time_extras":{},"columns":[{"columnType":"BASE_AXIS","sqlExpression":"order_date","label":"order_date","expressionType":"SQL"}],"metrics":[{"aggregate":"SUM","column":{"column_name":"quantity_ordered","description":null,"expression":null,"filterable":true,"groupby":true,"id":914,"is_dttm":false,"python_date_format":null,"type":"BIGINT","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"SUM(Sales)","optionName":"metric_twq59hf4ej_g70qjfmehsq","sqlExpression":null}],"annotation_layers":[],"series_limit":0,"order_desc":true,"url_params":{},"custom_params":{},"custom_form_data":{},"post_processing":[{"operation":"pivot","options":{"index":["order_date"],"columns":[],"aggregates":{"SUM(Sales)":{"operator":"mean"}},"drop_missing_columns":true}},{"operation":"cum","options":{"operator":"sum","columns":{"SUM(Sales)":"SUM(Sales)"}}},{"operation":"flatten"}]}],"form_data":{"datasource":"21__table","viz_type":"big_number","slice_id":115,"x_axis":"order_date","metric":{"aggregate":"SUM","column":{"column_name":"quantity_ordered","description":null,"expression":null,"filterable":true,"groupby":true,"id":914,"is_dttm":false,"python_date_format":null,"type":"BIGINT","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"SUM(Sales)","optionName":"metric_twq59hf4ej_g70qjfmehsq","sqlExpression":null},"adhoc_filters":[{"clause":"WHERE","comparator":"No
filter","expressionType":"SIMPLE","operator":"TEMPORAL_RANGE","subject":"order_date"}],"show_trend_line":true,"start_y_axis_at_zero":true,"color_picker":{"a":1,"b":135,"g":122,"r":0},"header_font_size":0.4,"subheader_font_size":0.15,"y_axis_format":"SMART_NUMBER","time_format":"smart_date","rolling_type":"cumsum","extra_form_data":{},"dashboards":[9],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}'
cache_timeout: null
uuid: c3d643cd-fd6f-4659-a5b7-59402487a8d0
version: 1.0.0
dataset_uuid: e8623bb9-5e00-f531-506a-19607f5f8005

View File

@@ -0,0 +1,80 @@
# 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.
slice_name: Items by Product Line
description: null
certified_by: null
certification_details: null
viz_type: table
params:
datasource: 21__table
viz_type: table
slice_id: 111
query_mode: aggregate
groupby:
- product_line
temporal_columns_lookup:
order_date: true
metrics:
- aggregate: SUM
column:
column_name: quantity_ordered
description: null
expression: null
filterable: true
groupby: true
id: 914
is_dttm: false
optionName: _col_QuantityOrdered
python_date_format: null
type: BIGINT
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: true
isNew: false
label: '# of Products Sold'
optionName: metric_skdbciwba6g_z1r5w1pxlqj
sqlExpression: null
all_columns: []
percent_metrics: null
adhoc_filters:
- clause: WHERE
subject: order_date
operator: TEMPORAL_RANGE
comparator: No filter
expressionType: SIMPLE
order_by_cols: []
row_limit: null
order_desc: true
table_timestamp_format: smart_date
allow_render_html: true
show_cell_bars: true
color_pn: true
comparison_color_scheme: Green
comparison_type: values
extra_form_data: {}
dashboards:
- 9
query_context: '{"datasource":{"id":21,"type":"table"},"force":false,"queries":[{"filters":[{"col":"order_date","op":"TEMPORAL_RANGE","val":"No
filter"}],"extras":{"having":"","where":""},"applied_time_extras":{},"columns":["product_line"],"metrics":[{"aggregate":"SUM","column":{"column_name":"quantity_ordered","description":null,"expression":null,"filterable":true,"groupby":true,"id":914,"is_dttm":false,"optionName":"_col_QuantityOrdered","python_date_format":null,"type":"BIGINT","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":true,"isNew":false,"label":"#
of Products Sold","optionName":"metric_skdbciwba6g_z1r5w1pxlqj","sqlExpression":null}],"orderby":[[{"aggregate":"SUM","column":{"column_name":"quantity_ordered","description":null,"expression":null,"filterable":true,"groupby":true,"id":914,"is_dttm":false,"optionName":"_col_QuantityOrdered","python_date_format":null,"type":"BIGINT","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":true,"isNew":false,"label":"#
of Products Sold","optionName":"metric_skdbciwba6g_z1r5w1pxlqj","sqlExpression":null},false]],"annotation_layers":[],"series_limit":0,"order_desc":true,"url_params":{},"custom_params":{},"custom_form_data":{},"post_processing":[],"time_offsets":[]}],"form_data":{"datasource":"21__table","viz_type":"table","slice_id":111,"query_mode":"aggregate","groupby":["product_line"],"temporal_columns_lookup":{"order_date":true},"metrics":[{"aggregate":"SUM","column":{"column_name":"quantity_ordered","description":null,"expression":null,"filterable":true,"groupby":true,"id":914,"is_dttm":false,"optionName":"_col_QuantityOrdered","python_date_format":null,"type":"BIGINT","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":true,"isNew":false,"label":"#
of Products Sold","optionName":"metric_skdbciwba6g_z1r5w1pxlqj","sqlExpression":null}],"all_columns":[],"percent_metrics":null,"adhoc_filters":[{"clause":"WHERE","subject":"order_date","operator":"TEMPORAL_RANGE","comparator":"No
filter","expressionType":"SIMPLE"}],"order_by_cols":[],"row_limit":null,"order_desc":true,"table_timestamp_format":"smart_date","allow_render_html":true,"show_cell_bars":true,"color_pn":true,"comparison_color_scheme":"Green","comparison_type":"values","extra_form_data":{},"dashboards":[9],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}'
cache_timeout: null
uuid: b8b7ca30-6291-44b0-bc64-ba42e2892b86
version: 1.0.0
dataset_uuid: e8623bb9-5e00-f531-506a-19607f5f8005

View File

@@ -15,6 +15,9 @@
# specific language governing permissions and limitations
# under the License.
slice_name: Number of Deals (for each Combination)
description: null
certified_by: null
certification_details: null
viz_type: heatmap_v2
params:
adhoc_filters: []
@@ -42,10 +45,12 @@ params:
viz_type: heatmap_v2
xscale_interval: null
value_bounds:
- null
- null
- null
- null
y_axis_format: SMART_NUMBER
yscale_interval: null
annotation_layers: []
query_context: null
cache_timeout: null
uuid: bd20fc69-dd51-46c1-99b5-09e37a434bf1
version: 1.0.0

View File

@@ -15,6 +15,9 @@
# specific language governing permissions and limitations
# under the License.
slice_name: Overall Sales (By Product Line)
description: null
certified_by: null
certification_details: null
viz_type: pie
params:
adhoc_filters: []
@@ -61,6 +64,12 @@ params:
time_range: No filter
url_params: {}
viz_type: pie
annotation_layers: []
query_context: '{"datasource":{"id":21,"type":"table"},"force":false,"queries":[{"time_range":"No
filter","granularity":"order_date","filters":[],"extras":{"having":"","where":""},"applied_time_extras":{},"columns":["product_line"],"metrics":[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"(Sales)","optionName":"metric_3sk6pfj3m7i_64h77bs4sly","sqlExpression":null}],"annotation_layers":[],"series_limit":0,"order_desc":true,"url_params":{},"custom_params":{},"custom_form_data":{}}],"form_data":{"adhoc_filters":[],"annotation_layers":[],"color_scheme":"supersetColors","datasource":"21__table","donut":true,"granularity_sqla":"order_date","groupby":["product_line"],"innerRadius":41,"label_line":true,"label_type":"key","labels_outside":true,"metric":{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"(Sales)","optionName":"metric_3sk6pfj3m7i_64h77bs4sly","sqlExpression":null},"number_format":"SMART_NUMBER","outerRadius":65,"queryFields":{"groupby":"groupby","metric":"metrics"},"row_limit":null,"show_labels":true,"show_labels_threshold":2,"show_legend":false,"slice_id":120,"time_range":"No
filter","url_params":{},"viz_type":"pie","force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}'
cache_timeout: null
uuid: 09c497e0-f442-1121-c9e7-671e37750424
version: 1.0.0

View File

@@ -15,60 +15,84 @@
# specific language governing permissions and limitations
# under the License.
slice_name: Proportion of Revenue by Product Line
description: null
certified_by: null
certification_details: null
viz_type: echarts_area
params:
adhoc_filters: []
annotation_layers: []
bottom_margin: auto
color_scheme: supersetColors
comparison_type: values
contribution: true
datasource: 23__table
granularity_sqla: order_date
groupby:
- product_line
label_colors: {}
line_interpolation: linear
metrics:
- aggregate: SUM
column:
column_name: sales
description: null
expression: null
filterable: true
groupby: true
id: 917
is_dttm: false
optionName: _col_Sales
python_date_format: null
type: DOUBLE PRECISION
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: false
isNew: false
label: (Sales)
optionName: metric_3is69ofceho_6d0ezok7ry6
sqlExpression: null
order_desc: true
queryFields:
groupby: groupby
metrics: metrics
rich_tooltip: true
rolling_type: None
row_limit: null
show_brush: auto
show_legend: true
stacked_style: stack
time_grain_sqla: P1M
time_range: "2003-01-01T00:00:00 : 2005-06-01T00:00:00"
url_params: {}
datasource: 21__table
viz_type: echarts_area
x_axis_format: smart_date
x_ticks_layout: auto
y_axis_bounds:
- null
- null
slice_id: 116
x_axis: order_date
time_grain_sqla: P1M
x_axis_sort_asc: true
x_axis_sort_series: name
x_axis_sort_series_ascending: true
metrics:
- aggregate: SUM
column:
column_name: sales
description: null
expression: null
filterable: true
groupby: true
id: 917
is_dttm: false
optionName: _col_Sales
python_date_format: null
type: DOUBLE PRECISION
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: false
isNew: false
label: (Sales)
optionName: metric_3is69ofceho_6d0ezok7ry6
sqlExpression: null
groupby:
- product_line
adhoc_filters:
- clause: WHERE
subject: order_date
operator: TEMPORAL_RANGE
comparator: '2003-01-01T00:00:00 : 2005-06-01T00:00:00'
expressionType: SIMPLE
row_limit: null
truncate_metric: true
show_empty_columns: true
rolling_type: null
comparison_type: values
annotation_layers: []
forecastPeriods: 10
forecastInterval: 0.8
x_axis_title_margin: 15
y_axis_title_margin: 15
y_axis_title_position: Left
sort_series_type: sum
color_scheme: supersetColors
time_shift_color: true
seriesType: line
opacity: 0.2
stack: Stack
only_total: true
markerSize: 6
show_legend: true
legendType: scroll
legendOrientation: top
x_axis_time_format: smart_date
rich_tooltip: true
showTooltipTotal: true
tooltipTimeFormat: smart_date
y_axis_format: SMART_NUMBER
truncateXAxis: true
extra_form_data: {}
dashboards:
- 9
query_context: '{"datasource":{"id":21,"type":"table"},"force":false,"queries":[{"filters":[{"col":"order_date","op":"TEMPORAL_RANGE","val":"2003-01-01T00:00:00
: 2005-06-01T00:00:00"}],"extras":{"time_grain_sqla":"P1M","having":"","where":""},"applied_time_extras":{},"columns":[{"timeGrain":"P1M","columnType":"BASE_AXIS","sqlExpression":"order_date","label":"order_date","expressionType":"SQL"},"product_line"],"metrics":[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"(Sales)","optionName":"metric_3is69ofceho_6d0ezok7ry6","sqlExpression":null}],"orderby":[[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"(Sales)","optionName":"metric_3is69ofceho_6d0ezok7ry6","sqlExpression":null},false]],"annotation_layers":[],"series_columns":["product_line"],"series_limit":0,"order_desc":true,"url_params":{},"custom_params":{},"custom_form_data":{},"time_offsets":[],"post_processing":[{"operation":"pivot","options":{"index":["order_date"],"columns":["product_line"],"aggregates":{"(Sales)":{"operator":"mean"}},"drop_missing_columns":false}},{"operation":"rename","options":{"columns":{"(Sales)":null},"level":0,"inplace":true}},{"operation":"flatten"}]}],"form_data":{"datasource":"21__table","viz_type":"echarts_area","slice_id":116,"x_axis":"order_date","time_grain_sqla":"P1M","x_axis_sort_asc":true,"x_axis_sort_series":"name","x_axis_sort_series_ascending":true,"metrics":[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"(Sales)","optionName":"metric_3is69ofceho_6d0ezok7ry6","sqlExpression":null}],"groupby":["product_line"],"adhoc_filters":[{"clause":"WHERE","subject":"order_date","operator":"TEMPORAL_RANGE","comparator":"2003-01-01T00:00:00
: 2005-06-01T00:00:00","expressionType":"SIMPLE"}],"row_limit":null,"truncate_metric":true,"show_empty_columns":true,"rolling_type":null,"comparison_type":"values","annotation_layers":[],"forecastPeriods":10,"forecastInterval":0.8,"x_axis_title_margin":15,"y_axis_title_margin":15,"y_axis_title_position":"Left","sort_series_type":"sum","color_scheme":"supersetColors","time_shift_color":true,"seriesType":"line","opacity":0.2,"stack":"Stack","only_total":true,"markerSize":6,"show_legend":true,"legendType":"scroll","legendOrientation":"top","x_axis_time_format":"smart_date","rich_tooltip":true,"showTooltipTotal":true,"tooltipTimeFormat":"smart_date","y_axis_format":"SMART_NUMBER","truncateXAxis":true,"extra_form_data":{},"dashboards":[9],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}'
cache_timeout: null
uuid: 08aff161-f60c-4cb3-a225-dc9b1140d2e3
version: 1.0.0

View File

@@ -15,71 +15,86 @@
# specific language governing permissions and limitations
# under the License.
slice_name: Quarterly Sales
description: null
certified_by: null
certification_details: null
viz_type: echarts_timeseries_bar
params:
adhoc_filters: []
annotation_layers: []
bottom_margin: auto
color_scheme: supersetColors
comparison_type: null
datasource: 23__table
granularity_sqla: order_date
groupby: []
label_colors:
Classic Cars: "#5AC189"
Motorcycles: "#666666"
Planes: "#FCC700"
QuantityOrdered: "#454E7C"
SUM(Sales): "#1FA8C9"
Ships: "#A868B7"
Trains: "#3CCCCB"
Trucks and Buses: "#E04355"
Vintage Cars: "#FF7F44"
left_margin: auto
line_interpolation: linear
metrics:
- aggregate: SUM
column:
column_name: sales
description: null
expression: null
filterable: true
groupby: true
id: 917
is_dttm: false
optionName: _col_Sales
python_date_format: null
type: DOUBLE PRECISION
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: false
isNew: false
label: SUM(Sales)
optionName: metric_tjn8bh6y44_7o4etwsqhal
sqlExpression: null
order_desc: true
queryFields:
groupby: groupby
metrics: metrics
rich_tooltip: true
rolling_type: null
row_limit: 10000
show_brush: auto
show_legend: false
slice_id: 668
time_compare: null
time_grain_sqla: P3M
time_range: No filter
url_params: {}
datasource: 21__table
viz_type: echarts_timeseries_bar
x_axis_format: "%m/%d/%Y"
x_axis_label: Quarter starting
x_ticks_layout: auto
y_axis_bounds:
- null
- null
slice_id: 118
x_axis: order_date
time_grain_sqla: P3M
x_axis_sort_asc: true
x_axis_sort_series: name
x_axis_sort_series_ascending: true
metrics:
- aggregate: SUM
column:
column_name: sales
description: null
expression: null
filterable: true
groupby: true
id: 917
is_dttm: false
optionName: _col_Sales
python_date_format: null
type: DOUBLE PRECISION
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: false
isNew: false
label: SUM(Sales)
optionName: metric_tjn8bh6y44_7o4etwsqhal
sqlExpression: null
groupby:
- status
adhoc_filters:
- clause: WHERE
subject: order_date
operator: TEMPORAL_RANGE
comparator: No filter
expressionType: SIMPLE
row_limit: 10000
truncate_metric: true
show_empty_columns: true
rolling_type: null
time_compare: null
comparison_type: null
annotation_layers: []
forecastPeriods: 10
forecastInterval: 0.8
orientation: vertical
x_axis_title_margin: 15
y_axis_title_margin: 15
y_axis_title_position: Left
sort_series_type: sum
color_scheme: supersetColors
time_shift_color: true
stack: Stack
only_total: true
show_legend: true
legendType: scroll
legendOrientation: top
x_axis_time_format: smart_date
y_axis_format: null
y_axis_label: Total Sales
y_axis_bounds:
- null
- null
truncateXAxis: true
rich_tooltip: true
showTooltipTotal: true
tooltipTimeFormat: smart_date
extra_form_data: {}
dashboards:
- 9
query_context: '{"datasource":{"id":21,"type":"table"},"force":false,"queries":[{"filters":[{"col":"order_date","op":"TEMPORAL_RANGE","val":"No
filter"}],"extras":{"time_grain_sqla":"P3M","having":"","where":""},"applied_time_extras":{},"columns":[{"timeGrain":"P3M","columnType":"BASE_AXIS","sqlExpression":"order_date","label":"order_date","expressionType":"SQL"},"status"],"metrics":[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"SUM(Sales)","optionName":"metric_tjn8bh6y44_7o4etwsqhal","sqlExpression":null}],"orderby":[[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"SUM(Sales)","optionName":"metric_tjn8bh6y44_7o4etwsqhal","sqlExpression":null},false]],"annotation_layers":[],"row_limit":10000,"series_columns":["status"],"series_limit":0,"order_desc":true,"url_params":{},"custom_params":{},"custom_form_data":{},"time_offsets":[],"post_processing":[{"operation":"pivot","options":{"index":["order_date"],"columns":["status"],"aggregates":{"SUM(Sales)":{"operator":"mean"}},"drop_missing_columns":false}},{"operation":"rename","options":{"columns":{"SUM(Sales)":null},"level":0,"inplace":true}},{"operation":"flatten"}]}],"form_data":{"datasource":"21__table","viz_type":"echarts_timeseries_bar","slice_id":118,"x_axis":"order_date","time_grain_sqla":"P3M","x_axis_sort_asc":true,"x_axis_sort_series":"name","x_axis_sort_series_ascending":true,"metrics":[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"SUM(Sales)","optionName":"metric_tjn8bh6y44_7o4etwsqhal","sqlExpression":null}],"groupby":["status"],"adhoc_filters":[{"clause":"WHERE","subject":"order_date","operator":"TEMPORAL_RANGE","comparator":"No
filter","expressionType":"SIMPLE"}],"row_limit":10000,"truncate_metric":true,"show_empty_columns":true,"rolling_type":null,"time_compare":null,"comparison_type":null,"annotation_layers":[],"forecastPeriods":10,"forecastInterval":0.8,"orientation":"vertical","x_axis_title_margin":15,"y_axis_title_margin":15,"y_axis_title_position":"Left","sort_series_type":"sum","color_scheme":"supersetColors","time_shift_color":true,"stack":"Stack","only_total":true,"show_legend":true,"legendType":"scroll","legendOrientation":"top","x_axis_time_format":"smart_date","y_axis_format":null,"y_axis_bounds":[null,null],"truncateXAxis":true,"rich_tooltip":true,"showTooltipTotal":true,"tooltipTimeFormat":"smart_date","extra_form_data":{},"dashboards":[9],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}'
cache_timeout: null
uuid: 692aca26-a526-85db-c94c-411c91cc1077
version: 1.0.0

View File

@@ -0,0 +1,103 @@
# 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.
slice_name: Quarterly Sales (By Product Line)
description: null
certified_by: null
certification_details: null
viz_type: echarts_timeseries_bar
params:
datasource: 21__table
viz_type: echarts_timeseries_bar
slice_id: 113
x_axis: order_date
time_grain_sqla: P3M
metrics:
- aggregate: SUM
column:
column_name: sales
description: null
expression: null
filterable: true
groupby: true
id: 917
is_dttm: false
optionName: _col_Sales
python_date_format: null
type: DOUBLE PRECISION
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: false
isNew: false
label: SUM(Sales)
optionName: metric_tjn8bh6y44_7o4etwsqhal
sqlExpression: null
groupby:
- product_line
adhoc_filters:
- expressionType: SIMPLE
subject: order_date
operator: TEMPORAL_RANGE
comparator: No filter
clause: WHERE
sqlExpression: null
isExtra: false
isNew: false
datasourceWarning: false
filterOptionName: filter_skx80xwzof_2l0t7nomekl
order_desc: true
row_limit: 10000
truncate_metric: true
show_empty_columns: true
rolling_type: null
time_compare: null
comparison_type: null
annotation_layers: []
forecastPeriods: 10
forecastInterval: 0.8
orientation: vertical
x_axis_title_margin: 15
y_axis_title_margin: 15
y_axis_title_position: Left
sort_series_type: sum
color_scheme: supersetColors
time_shift_color: true
only_total: true
show_legend: true
legendType: scroll
legendOrientation: top
x_axis_time_format: smart_date
y_axis_format: null
y_axis_bounds:
- null
- null
truncateXAxis: true
rich_tooltip: true
showTooltipTotal: true
tooltipTimeFormat: smart_date
extra_form_data: {}
dashboards:
- 9
query_context: '{"datasource":{"id":21,"type":"table"},"force":false,"queries":[{"filters":[{"col":"order_date","op":"TEMPORAL_RANGE","val":"No
filter"}],"extras":{"time_grain_sqla":"P3M","having":"","where":""},"applied_time_extras":{},"columns":[{"timeGrain":"P3M","columnType":"BASE_AXIS","sqlExpression":"order_date","label":"order_date","expressionType":"SQL"},"product_line"],"metrics":[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"SUM(Sales)","optionName":"metric_tjn8bh6y44_7o4etwsqhal","sqlExpression":null}],"orderby":[[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"SUM(Sales)","optionName":"metric_tjn8bh6y44_7o4etwsqhal","sqlExpression":null},false]],"annotation_layers":[],"row_limit":10000,"series_columns":["product_line"],"series_limit":0,"order_desc":true,"url_params":{},"custom_params":{},"custom_form_data":{},"time_offsets":[],"post_processing":[{"operation":"pivot","options":{"index":["order_date"],"columns":["product_line"],"aggregates":{"SUM(Sales)":{"operator":"mean"}},"drop_missing_columns":false}},{"operation":"rename","options":{"columns":{"SUM(Sales)":null},"level":0,"inplace":true}},{"operation":"flatten"}]}],"form_data":{"datasource":"21__table","viz_type":"echarts_timeseries_bar","slice_id":113,"x_axis":"order_date","time_grain_sqla":"P3M","metrics":[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"SUM(Sales)","optionName":"metric_tjn8bh6y44_7o4etwsqhal","sqlExpression":null}],"groupby":["product_line"],"adhoc_filters":[{"expressionType":"SIMPLE","subject":"order_date","operator":"TEMPORAL_RANGE","comparator":"No
filter","clause":"WHERE","sqlExpression":null,"isExtra":false,"isNew":false,"datasourceWarning":false,"filterOptionName":"filter_skx80xwzof_2l0t7nomekl"}],"order_desc":true,"row_limit":10000,"truncate_metric":true,"show_empty_columns":true,"rolling_type":null,"time_compare":null,"comparison_type":null,"annotation_layers":[],"forecastPeriods":10,"forecastInterval":0.8,"orientation":"vertical","x_axis_title_margin":15,"y_axis_title_margin":15,"y_axis_title_position":"Left","sort_series_type":"sum","color_scheme":"supersetColors","time_shift_color":true,"only_total":true,"show_legend":true,"legendType":"scroll","legendOrientation":"top","x_axis_time_format":"smart_date","y_axis_format":null,"y_axis_bounds":[null,null],"truncateXAxis":true,"rich_tooltip":true,"showTooltipTotal":true,"tooltipTimeFormat":"smart_date","extra_form_data":{},"dashboards":[9],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}'
cache_timeout: null
uuid: db9609e4-9b78-4a32-87a7-4d9e19d51cd8
version: 1.0.0
dataset_uuid: e8623bb9-5e00-f531-506a-19607f5f8005

View File

@@ -15,6 +15,9 @@
# specific language governing permissions and limitations
# under the License.
slice_name: Revenue by Deal Size
description: null
certified_by: null
certification_details: null
viz_type: echarts_timeseries_bar
params:
adhoc_filters: []
@@ -27,30 +30,30 @@ params:
datasource: 23__table
granularity_sqla: order_date
groupby:
- deal_size
- deal_size
label_colors: {}
left_margin: auto
line_interpolation: linear
metrics:
- aggregate: SUM
column:
column_name: sales
description: null
expression: null
filterable: true
groupby: true
id: 917
is_dttm: false
optionName: _col_Sales
python_date_format: null
type: DOUBLE PRECISION
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: false
isNew: false
label: (Sales)
optionName: metric_3is69ofceho_6d0ezok7ry6
sqlExpression: null
- aggregate: SUM
column:
column_name: sales
description: null
expression: null
filterable: true
groupby: true
id: 917
is_dttm: false
optionName: _col_Sales
python_date_format: null
type: DOUBLE PRECISION
verbose_name: null
expressionType: SIMPLE
hasCustomLabel: false
isNew: false
label: (Sales)
optionName: metric_3is69ofceho_6d0ezok7ry6
sqlExpression: null
order_desc: true
queryFields:
groupby: groupby
@@ -61,15 +64,16 @@ params:
show_brush: auto
show_legend: true
time_grain_sqla: P1M
time_range: "2003-01-01T00:00:00 : 2005-06-01T00:00:00"
time_range: '2003-01-01T00:00:00 : 2005-06-01T00:00:00'
url_params: {}
viz_type: echarts_timeseries_bar
x_axis_format: smart_date
x_ticks_layout: auto
y_axis_bounds:
- null
- null
- null
- null
y_axis_format: SMART_NUMBER
query_context: null
cache_timeout: null
uuid: f065a533-2e13-42b9-bd19-801a21700dff
version: 1.0.0

View File

@@ -15,6 +15,9 @@
# specific language governing permissions and limitations
# under the License.
slice_name: Seasonality of Revenue (per Product Line)
description: null
certified_by: null
certification_details: null
viz_type: horizon
params:
adhoc_filters: []
@@ -53,6 +56,8 @@ params:
time_range: No filter
url_params: {}
viz_type: horizon
annotation_layers: []
query_context: null
cache_timeout: null
uuid: cf0da099-b3ab-4d94-ab62-cf353ac3c611
version: 1.0.0

View File

@@ -15,12 +15,15 @@
# specific language governing permissions and limitations
# under the License.
slice_name: Total Revenue
viz_type: big_number_total
description: null
certified_by: null
certification_details: null
viz_type: big_number
params:
adhoc_filters: []
datasource: 23__table
granularity_sqla: order_date
header_font_size: 0.4
datasource: 21__table
viz_type: big_number
slice_id: 114
x_axis: order_date
metric:
aggregate: SUM
column:
@@ -41,14 +44,35 @@ params:
label: (Sales)
optionName: metric_twq59hf4ej_g70qjfmehsq
sqlExpression: null
queryFields:
metric: metrics
subheader: ''
adhoc_filters:
- clause: WHERE
comparator: No filter
expressionType: SIMPLE
operator: TEMPORAL_RANGE
subject: order_date
show_trend_line: true
start_y_axis_at_zero: true
color_picker:
a: 1
b: 135
g: 122
r: 0
header_font_size: 0.4
subheader_font_size: 0.15
time_range: No filter
url_params: {}
viz_type: big_number_total
y_axis_format: $,.2f
y_axis_format: .3s
currency_format:
symbolPosition: prefix
symbol: USD
time_format: smart_date
rolling_type: cumsum
extra_form_data: {}
dashboards:
- 9
query_context: '{"datasource":{"id":21,"type":"table"},"force":false,"queries":[{"filters":[{"col":"order_date","op":"TEMPORAL_RANGE","val":"No
filter"}],"extras":{"having":"","where":""},"applied_time_extras":{},"columns":[{"columnType":"BASE_AXIS","sqlExpression":"order_date","label":"order_date","expressionType":"SQL"}],"metrics":[{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"(Sales)","optionName":"metric_twq59hf4ej_g70qjfmehsq","sqlExpression":null}],"annotation_layers":[],"series_limit":0,"order_desc":true,"url_params":{},"custom_params":{},"custom_form_data":{},"post_processing":[{"operation":"pivot","options":{"index":["order_date"],"columns":[],"aggregates":{"(Sales)":{"operator":"mean"}},"drop_missing_columns":true}},{"operation":"cum","options":{"operator":"sum","columns":{"(Sales)":"(Sales)"}}},{"operation":"flatten"}]}],"form_data":{"datasource":"21__table","viz_type":"big_number","slice_id":114,"x_axis":"order_date","metric":{"aggregate":"SUM","column":{"column_name":"sales","description":null,"expression":null,"filterable":true,"groupby":true,"id":917,"is_dttm":false,"optionName":"_col_Sales","python_date_format":null,"type":"DOUBLE
PRECISION","verbose_name":null},"expressionType":"SIMPLE","hasCustomLabel":false,"isNew":false,"label":"(Sales)","optionName":"metric_twq59hf4ej_g70qjfmehsq","sqlExpression":null},"adhoc_filters":[{"clause":"WHERE","comparator":"No
filter","expressionType":"SIMPLE","operator":"TEMPORAL_RANGE","subject":"order_date"}],"show_trend_line":true,"start_y_axis_at_zero":true,"color_picker":{"a":1,"b":135,"g":122,"r":0},"header_font_size":0.4,"subheader_font_size":0.15,"y_axis_format":".3s","currency_format":{"symbolPosition":"prefix","symbol":"USD"},"time_format":"smart_date","rolling_type":"cumsum","extra_form_data":{},"dashboards":[9],"force":false,"result_format":"json","result_type":"full"},"result_format":"json","result_type":"full"}'
cache_timeout: null
uuid: 7b12a243-88e0-4dc5-ac33-9a840bb0ac5a
version: 1.0.0

View File

@@ -16,10 +16,10 @@
# under the License.
dashboard_title: Sales Dashboard
description: null
css: ""
css: ''
slug: null
certified_by: ""
certification_details: ""
certified_by: ''
certification_details: ''
published: true
uuid: 04f79081-fb49-7bac-7f14-cc76cd2ad93b
position:
@@ -27,195 +27,195 @@ position:
children: []
id: CHART-1NOOLm5YPs
meta:
chartId: 2805
chartId: 115
height: 25
sliceName: Total Items Sold
sliceName: Items Sold
sliceNameOverride: Total Products Sold
uuid: c3d643cd-fd6f-4659-a5b7-59402487a8d0
width: 2
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
- COLUMN-8Rp54B6ikC
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
- COLUMN-8Rp54B6ikC
type: CHART
CHART-AYpv8gFi_q:
children: []
id: CHART-AYpv8gFi_q
meta:
chartId: 2810
chartId: 112
height: 70
sliceName: Number of Deals (for each Combination)
uuid: bd20fc69-dd51-46c1-99b5-09e37a434bf1
width: 6
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROW-0l1WcDzW3
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROW-0l1WcDzW3
type: CHART
CHART-KKT9BsnUst:
children: []
id: CHART-KKT9BsnUst
meta:
chartId: 2806
height: 59
chartId: 113
height: 50
sliceName: Quarterly Sales (By Product Line)
sliceNameOverride: Quarterly Revenue (By Product Line)
uuid: db9609e4-9b78-4a32-87a7-4d9e19d51cd8
width: 7
width: 6
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-oAtmu5grZ
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-oAtmu5grZ
type: CHART
CHART-OJ9aWDmn1q:
children: []
id: CHART-OJ9aWDmn1q
meta:
chartId: 2808
chartId: 116
height: 70
sliceName: Proportion of Revenue by Product Line
sliceNameOverride: Proportion of Monthly Revenue by Product Line
uuid: 08aff161-f60c-4cb3-a225-dc9b1140d2e3
width: 6
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROW-0l1WcDzW3
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROW-0l1WcDzW3
type: CHART
CHART-YFg-9wHE7s:
children: []
id: CHART-YFg-9wHE7s
meta:
chartId: 2811
chartId: 119
height: 49
sliceName: Seasonality of Revenue (per Product Line)
uuid: cf0da099-b3ab-4d94-ab62-cf353ac3c611
width: 6
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROW-E7MDSGfnm
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROW-E7MDSGfnm
type: CHART
CHART-_LMKI0D3tj:
children: []
id: CHART-_LMKI0D3tj
meta:
chartId: 2809
chartId: 117
height: 49
sliceName: Revenue by Deal Size
sliceNameOverride: Monthly Revenue by Deal SIze
uuid: f065a533-2e13-42b9-bd19-801a21700dff
width: 6
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROW-E7MDSGfnm
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROW-E7MDSGfnm
type: CHART
CHART-id4RGv80N-:
children: []
id: CHART-id4RGv80N-
meta:
chartId: 2807
height: 59
sliceName: Total Items Sold (By Product Line)
sliceNameOverride: Total Products Sold (By Product Line)
chartId: 111
height: 50
sliceName: Items by Product Line
sliceNameOverride: Products Sold By Product Line
uuid: b8b7ca30-6291-44b0-bc64-ba42e2892b86
width: 2
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-oAtmu5grZ
- COLUMN-G6_2DvG8aK
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-oAtmu5grZ
- COLUMN-G6_2DvG8aK
type: CHART
CHART-j24u8ve41b:
children: []
id: CHART-j24u8ve41b
meta:
chartId: 670
height: 59
chartId: 120
height: 50
sliceName: Overall Sales (By Product Line)
sliceNameOverride: Total Revenue (By Product Line)
sliceNameOverride: Total Revenue By Product
uuid: 09c497e0-f442-1121-c9e7-671e37750424
width: 3
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-oAtmu5grZ
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-oAtmu5grZ
type: CHART
CHART-lFanAaYKBK:
children: []
id: CHART-lFanAaYKBK
meta:
chartId: 2804
chartId: 114
height: 26
sliceName: Total Revenue
uuid: 7b12a243-88e0-4dc5-ac33-9a840bb0ac5a
width: 3
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
- COLUMN-8Rp54B6ikC
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
- COLUMN-8Rp54B6ikC
type: CHART
CHART-vomBOiI7U9:
children: []
id: CHART-vomBOiI7U9
meta:
chartId: 668
chartId: 118
height: 53
sliceName: Quarterly Sales
sliceNameOverride: Quarterly Revenue
uuid: 692aca26-a526-85db-c94c-411c91cc1077
width: 7
width: 6
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
type: CHART
COLUMN-8Rp54B6ikC:
children:
- CHART-lFanAaYKBK
- CHART-1NOOLm5YPs
- CHART-lFanAaYKBK
- CHART-1NOOLm5YPs
id: COLUMN-8Rp54B6ikC
meta:
background: BACKGROUND_TRANSPARENT
width: 2
width: 3
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
type: COLUMN
COLUMN-G6_2DvG8aK:
children:
- CHART-id4RGv80N-
- CHART-id4RGv80N-
id: COLUMN-G6_2DvG8aK
meta:
background: BACKGROUND_TRANSPARENT
width: 2
width: 3
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-oAtmu5grZ
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-oAtmu5grZ
type: COLUMN
DASHBOARD_VERSION_KEY: v2
GRID_ID:
children: []
id: GRID_ID
parents:
- ROOT_ID
- ROOT_ID
type: GRID
HEADER_ID:
id: HEADER_ID
@@ -226,10 +226,9 @@ position:
children: []
id: MARKDOWN--AtDSWnapE
meta:
code:
"# \U0001F697 Vehicle Sales Dashboard \U0001F3CD\n\nThis example dashboard\
\ provides insight into the business operations of vehicle seller. The dataset\
\ powering this dashboard can be found [here on Kaggle](https://www.kaggle.com/kyanyoga/sample-sales-data).\n\
code: "# \U0001F697 Vehicle Sales \U0001F3CD\n\nThis example dashboard provides\
\ insight into the business operations of vehicle seller. The dataset powering\
\ this dashboard can be found [here on Kaggle](https://www.kaggle.com/kyanyoga/sample-sales-data).\n\
\n### Timeline\n\nThe dataset contains data on all orders from the 2003 and\
\ 2004 fiscal years, and some orders from 2005.\n\n### Products Sold\n\nThis\
\ shop mainly sells the following products:\n\n- \U0001F697 Classic Cars\n\
@@ -239,113 +238,498 @@ position:
height: 53
width: 3
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROW-Tyv02UA_6W
type: MARKDOWN
ROOT_ID:
children:
- TABS-e5Ruro0cjP
- TABS-e5Ruro0cjP
id: ROOT_ID
type: ROOT
ROW-0l1WcDzW3:
children:
- CHART-OJ9aWDmn1q
- CHART-AYpv8gFi_q
- CHART-OJ9aWDmn1q
- CHART-AYpv8gFi_q
id: ROW-0l1WcDzW3
meta:
background: BACKGROUND_TRANSPARENT
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
type: ROW
ROW-E7MDSGfnm:
children:
- CHART-YFg-9wHE7s
- CHART-_LMKI0D3tj
- CHART-YFg-9wHE7s
- CHART-_LMKI0D3tj
id: ROW-E7MDSGfnm
meta:
background: BACKGROUND_TRANSPARENT
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-4fthLQmdX
type: ROW
ROW-Tyv02UA_6W:
children:
- COLUMN-8Rp54B6ikC
- CHART-vomBOiI7U9
- MARKDOWN--AtDSWnapE
- COLUMN-8Rp54B6ikC
- CHART-vomBOiI7U9
- MARKDOWN--AtDSWnapE
id: ROW-Tyv02UA_6W
meta:
background: BACKGROUND_TRANSPARENT
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
type: ROW
ROW-oAtmu5grZ:
children:
- COLUMN-G6_2DvG8aK
- CHART-KKT9BsnUst
- CHART-j24u8ve41b
- COLUMN-G6_2DvG8aK
- CHART-KKT9BsnUst
- CHART-j24u8ve41b
id: ROW-oAtmu5grZ
meta:
background: BACKGROUND_TRANSPARENT
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
- ROOT_ID
- TABS-e5Ruro0cjP
- TAB-d-E0Zc1cTH
type: ROW
TAB-4fthLQmdX:
children:
- ROW-0l1WcDzW3
- ROW-E7MDSGfnm
- ROW-0l1WcDzW3
- ROW-E7MDSGfnm
id: TAB-4fthLQmdX
meta:
text: "\U0001F9ED Exploratory"
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- ROOT_ID
- TABS-e5Ruro0cjP
type: TAB
TAB-d-E0Zc1cTH:
children:
- ROW-Tyv02UA_6W
- ROW-oAtmu5grZ
- ROW-Tyv02UA_6W
- ROW-oAtmu5grZ
id: TAB-d-E0Zc1cTH
meta:
text: "\U0001F3AF Sales Overview"
parents:
- ROOT_ID
- TABS-e5Ruro0cjP
- ROOT_ID
- TABS-e5Ruro0cjP
type: TAB
TABS-e5Ruro0cjP:
children:
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
id: TABS-e5Ruro0cjP
meta: {}
parents:
- ROOT_ID
- ROOT_ID
type: TABS
metadata:
timed_refresh_immune_slices: []
expanded_slices: {}
refresh_frequency: 0
default_filters: "{}"
default_filters: '{}'
color_scheme: supersetColors
label_colors:
Medium: "#1FA8C9"
Small: "#454E7C"
Large: "#5AC189"
SUM(SALES): "#1FA8C9"
Classic Cars: "#454E7C"
Vintage Cars: "#5AC189"
Motorcycles: "#FF7F44"
Trucks and Buses: "#666666"
Planes: "#E04355"
Ships: "#FCC700"
Trains: "#A868B7"
Medium: '#1FA8C9'
Small: '#454E7C'
Large: '#5AC189'
SUM(SALES): '#1FA8C9'
Classic Cars: '#454E7C'
Vintage Cars: '#5AC189'
Motorcycles: '#FF7F44'
Trucks and Buses: '#666666'
Planes: '#E04355'
Ships: '#FCC700'
Trains: '#A868B7'
native_filter_configuration:
- id: NATIVE_FILTER-HX2lV--YaAZRQfJ_yfYB2
controlValues:
enableEmptyFilter: false
defaultToFirstItem: false
multiSelect: true
searchAllOptions: false
inverseSelection: false
name: Country
filterType: filter_select
targets:
- column:
name: country
datasetUuid: e8623bb9-5e00-f531-506a-19607f5f8005
defaultDataMask:
extraFormData: {}
filterState: {}
ownState: {}
cascadeParentIds: []
scope:
rootPath:
- ROOT_ID
excluded: []
type: NATIVE_FILTER
description: ''
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
tabsInScope:
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
- id: NATIVE_FILTER-oCF7UtoHuDIBg44q5peth
controlValues:
enableEmptyFilter: false
name: Order Quantity
filterType: filter_range
targets:
- column:
name: quantity_ordered
datasetUuid: e8623bb9-5e00-f531-506a-19607f5f8005
defaultDataMask:
extraFormData: {}
filterState: {}
ownState: {}
cascadeParentIds: []
scope:
rootPath:
- ROOT_ID
excluded: []
type: NATIVE_FILTER
description: ''
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
tabsInScope:
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
- id: NATIVE_FILTER-V_UJOthxN8gCeYSD0id9b
controlValues:
enableEmptyFilter: false
name: Time Range
filterType: filter_time
targets:
- {}
defaultDataMask:
extraFormData: {}
filterState: {}
ownState: {}
cascadeParentIds: []
scope:
rootPath:
- ROOT_ID
excluded: []
type: NATIVE_FILTER
description: ''
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
tabsInScope:
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
- id: NATIVE_FILTER-t8xOh3el1KBWWiCIF5hIN
controlValues:
enableEmptyFilter: false
name: Time Grain
filterType: filter_timegrain
targets:
- datasetUuid: e8623bb9-5e00-f531-506a-19607f5f8005
defaultDataMask:
extraFormData: {}
filterState: {}
ownState: {}
cascadeParentIds: []
scope:
excluded: []
rootPath:
- ROOT_ID
type: NATIVE_FILTER
description: ''
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
tabsInScope:
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
- id: NATIVE_FILTER-9tGcTjqhNxOgX2AEPLVil
controlValues:
enableEmptyFilter: false
defaultToFirstItem: false
multiSelect: true
searchAllOptions: false
inverseSelection: false
name: Postal Code
filterType: filter_select
targets:
- column:
name: postal_code
datasetUuid: e8623bb9-5e00-f531-506a-19607f5f8005
defaultDataMask:
extraFormData: {}
filterState: {}
ownState: {}
cascadeParentIds: []
scope:
rootPath:
- ROOT_ID
excluded: []
type: NATIVE_FILTER
description: ''
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
tabsInScope:
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
- id: NATIVE_FILTER-pGnu5e_bg1IGz2wdzIuCA
controlValues:
enableEmptyFilter: false
defaultToFirstItem: false
multiSelect: true
searchAllOptions: false
inverseSelection: false
name: State
filterType: filter_select
targets:
- column:
name: state
datasetUuid: e8623bb9-5e00-f531-506a-19607f5f8005
defaultDataMask:
extraFormData: {}
filterState: {}
ownState: {}
cascadeParentIds: []
scope:
rootPath:
- ROOT_ID
excluded: []
type: NATIVE_FILTER
description: ''
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
tabsInScope:
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
- id: NATIVE_FILTER-EVb_e9pndL9UByuZt0z_w
controlValues:
enableEmptyFilter: false
name: MSRP
filterType: filter_range
targets:
- column:
name: msrp
datasetUuid: e8623bb9-5e00-f531-506a-19607f5f8005
defaultDataMask:
extraFormData: {}
filterState: {}
ownState: {}
cascadeParentIds: []
scope:
rootPath:
- ROOT_ID
excluded: []
type: NATIVE_FILTER
description: ''
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
tabsInScope:
- TAB-d-E0Zc1cTH
- TAB-4fthLQmdX
shared_label_colors:
- Classic Cars
- Motorcycles
- Planes
- Ships
- Trains
- Trucks and Buses
- Vintage Cars
map_label_colors:
Shipped: '#1FA8C9'
Cancelled: '#454E7C'
On Hold: '#5AC189'
Resolved: '#FF7F44'
In Process: '#666666'
Disputed: '#E04355'
color_scheme_domain:
- '#1FA8C9'
- '#454E7C'
- '#5AC189'
- '#FF7F44'
- '#666666'
- '#E04355'
- '#FCC700'
- '#A868B7'
- '#3CCCCB'
- '#A38F79'
- '#8FD3E4'
- '#A1A6BD'
- '#ACE1C4'
- '#FEC0A1'
- '#B2B2B2'
- '#EFA1AA'
- '#FDE380'
- '#D3B3DA'
- '#9EE5E5'
- '#D1C6BC'
cross_filters_enabled: true
chart_configuration:
'111':
id: 111
crossFilters:
scope: global
chartsInScope:
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
'113':
id: 113
crossFilters:
scope: global
chartsInScope:
- 111
- 112
- 114
- 115
- 116
- 117
- 118
- 119
- 120
'116':
id: 116
crossFilters:
scope: global
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 117
- 118
- 119
- 120
'117':
id: 117
crossFilters:
scope: global
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 118
- 119
- 120
'118':
id: 118
crossFilters:
scope: global
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 119
- 120
'120':
id: 120
crossFilters:
scope: global
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
global_chart_configuration:
scope:
rootPath:
- ROOT_ID
excluded: []
chartsInScope:
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
version: 1.0.0

View File

@@ -20,6 +20,7 @@ description: null
default_endpoint: null
offset: 0
cache_timeout: null
catalog: null
schema: null
sql: null
params: null
@@ -27,6 +28,8 @@ template_params: null
filter_select_enabled: true
fetch_values_predicate: null
extra: null
normalize_columns: false
always_filter_main_dttm: false
uuid: e8623bb9-5e00-f531-506a-19607f5f8005
metrics:
- metric_name: count
@@ -35,6 +38,7 @@ metrics:
expression: COUNT(*)
description: null
d3format: null
currency: null
extra: null
warning_text: null
columns:
@@ -43,251 +47,301 @@ columns:
is_dttm: true
is_active: true
type: TIMESTAMP WITHOUT TIME ZONE
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: price_each
verbose_name: null
is_dttm: false
is_active: true
type: DOUBLE PRECISION
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: sales
verbose_name: null
is_dttm: false
is_active: true
type: DOUBLE PRECISION
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: address_line1
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: address_line2
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: contact_last_name
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: contact_first_name
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: quantity_ordered
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: year
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: postal_code
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: customer_name
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: deal_size
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: state
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: status
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: order_line_number
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: quantity_ordered
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: order_number
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
- column_name: month
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: quarter
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: year
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: month
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: msrp
verbose_name: null
is_dttm: false
is_active: true
type: BIGINT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: contact_last_name
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: contact_first_name
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: postal_code
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: customer_name
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: deal_size
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: product_code
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: product_line
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: state
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: status
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: city
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: country
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: phone
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
- column_name: territory
verbose_name: null
is_dttm: false
is_active: true
type: TEXT
advanced_data_type: null
groupby: true
filterable: true
expression: null
description: null
python_date_format: null
extra: null
version: 1.0.0
database_uuid: a2dc77af-e654-49bb-b321-40f6b559a1ee
data: https://raw.githubusercontent.com/apache-superset/examples-data/lowercase_columns_examples/datasets/examples/sales.csv

View File

@@ -0,0 +1,42 @@
# 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.
"""Add folders column to datasets
Revision ID: 94e7a3499973
Revises: 74ad1125881c
Create Date: 2025-03-03 20:52:24.585143
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.types import JSON
# revision identifiers, used by Alembic.
revision = "94e7a3499973"
down_revision = "74ad1125881c"
def upgrade():
op.add_column(
"tables",
sa.Column("folders", JSON, nullable=True),
)
def downgrade():
op.drop_column("tables", "folders")

View File

@@ -15,4 +15,4 @@
# specific language governing permissions and limitations
# under the License.
Babel==2.9.1
jinja2==3.1.5
jinja2==3.1.6

View File

@@ -1103,6 +1103,11 @@ class TestDatabaseApi(SupersetTestCase):
assert rv.status_code == 201
assert "sqlalchemy_form" in response["result"]["configuration_method"]
# Cleanup
model = db.session.query(Database).get(response.get("id"))
db.session.delete(model)
db.session.commit()
def test_create_database_server_cert_validate(self):
"""
Database API: Test create server cert validation
@@ -3153,6 +3158,59 @@ class TestDatabaseApi(SupersetTestCase):
]
}
@mock.patch("superset.commands.database.importers.v1.utils.add_permissions")
def test_import_database_row_expansion_enabled(self, mock_add_permissions):
"""
Database API: Test import database with row expansion enabled.
"""
self.login(ADMIN_USERNAME)
uri = "api/v1/database/import/"
db_config = {
"database_name": "DB with expand rows enabled",
"allow_csv_upload": True,
"allow_ctas": True,
"allow_cvas": True,
"allow_dml": True,
"allow_run_async": False,
"cache_timeout": None,
"expose_in_sqllab": True,
"extra": {
"schema_options": {"expand_rows": True},
},
"sqlalchemy_uri": "postgresql://user:pass@host1",
"uuid": "b8a1ccd3-779d-4ab7-8ad8-9ab119d7ff90",
"version": "1.0.0",
}
buf = BytesIO()
with ZipFile(buf, "w") as bundle:
with bundle.open("database_export/metadata.yaml", "w") as fp:
fp.write(yaml.safe_dump(database_metadata_config).encode())
with bundle.open(
"database_export/databases/DB_with_expand_rows_enabled.yaml", "w"
) as fp:
fp.write(yaml.safe_dump(db_config).encode())
buf.seek(0)
form_data = {
"formData": (buf, "database_export.zip"),
"passwords": json.dumps(
{"databases/DB_with_expand_rows_enabled.yaml": "SECRET"}
),
}
rv = self.client.post(uri, data=form_data, content_type="multipart/form-data")
response = json.loads(rv.data.decode("utf-8"))
assert rv.status_code == 200
assert response == {"message": "OK"}
database = db.session.query(Database).filter_by(uuid=db_config["uuid"]).one()
assert database.extra == json.dumps({"schema_options": {"expand_rows": True}})
db.session.delete(database)
db.session.commit()
@mock.patch(
"superset.db_engine_specs.base.BaseEngineSpec.get_function_names",
)

View File

@@ -335,6 +335,54 @@ def test_rename_without_catalog(
assert schema2_pvm.view_menu.name == f"[{database_without_catalog.name}].[schema2]"
def test_rename_without_catalog_with_assets(
mocker: MockerFixture,
database_without_catalog: MockerFixture,
) -> None:
"""
Test that permissions are renamed correctly when the DB connection does not support
catalogs, and it has assets associated with it.
"""
database_dao = mocker.patch("superset.commands.database.update.DatabaseDAO")
original_database = mocker.MagicMock()
original_database.database_name = "my_db"
database_without_catalog.database_name = "my_other_db"
database_without_catalog.get_all_schema_names.return_value = ["schema1"]
database_dao.update.return_value = database_without_catalog
database_dao.find_by_id.return_value = original_database
sync_db_perms_dao = mocker.patch(
"superset.commands.database.sync_permissions.DatabaseDAO"
)
sync_db_perms_dao.find_by_id.return_value = database_without_catalog
mocker.patch("superset.commands.database.update.get_username")
mocker.patch("superset.security_manager.get_user_by_username")
dataset = mocker.MagicMock()
chart = mocker.MagicMock()
sync_db_perms_dao.get_datasets.return_value = [dataset]
dataset_dao = mocker.patch("superset.commands.database.sync_permissions.DatasetDAO")
dataset_dao.get_related_objects.return_value = {"charts": [chart]}
find_permission_view_menu = mocker.patch.object(
security_manager,
"find_permission_view_menu",
)
schema_pvm = mocker.MagicMock()
schema_pvm.view_menu.name = "[my_db].[schema1]"
find_permission_view_menu.side_effect = [
"[my_db].[schema1]",
schema_pvm,
]
UpdateDatabaseCommand(1, {}).run()
assert schema_pvm.view_menu.name == f"[{database_without_catalog.name}].[schema1]"
assert dataset.schema_perm == f"[{database_without_catalog.name}].[schema1]"
assert dataset.catalog_perm is None
assert chart.catalog_perm is None
assert chart.schema_perm == f"[{database_without_catalog.name}].[schema1]"
def test_update_with_oauth2(
mocker: MockerFixture,
database_needs_oauth2: MockerFixture,

View File

@@ -17,7 +17,7 @@
# pylint: disable=import-outside-toplevel, invalid-name, line-too-long
from typing import TYPE_CHECKING
from typing import Any, TYPE_CHECKING
from urllib.parse import parse_qs, urlparse
import pandas as pd
@@ -697,3 +697,23 @@ def test_get_oauth2_fresh_token(
},
timeout=30.0,
)
def test_update_params_from_encrypted_extra(mocker: MockerFixture) -> None:
"""
Test `update_params_from_encrypted_extra`.
"""
from superset.db_engine_specs.gsheets import GSheetsEngineSpec
database = mocker.MagicMock(
encrypted_extra=json.dumps(
{
"oauth2_client_info": "SECRET",
"foo": "bar",
}
)
)
params: dict[str, Any] = {}
GSheetsEngineSpec.update_params_from_encrypted_extra(database, params)
assert params == {"foo": "bar"}