mirror of
https://github.com/apache/superset.git
synced 2026-04-28 04:25:07 +00:00
Compare commits
16 Commits
elizabeth/
...
enxdev/ref
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bec3d94a5b | ||
|
|
a49a15f990 | ||
|
|
eb39ddbfe3 | ||
|
|
974d36d35e | ||
|
|
b64e3254fc | ||
|
|
9907db9e1a | ||
|
|
b4dd64aa24 | ||
|
|
6e049225f9 | ||
|
|
831369a44b | ||
|
|
7c9c30db1d | ||
|
|
0c6d868483 | ||
|
|
777760b096 | ||
|
|
e8ad096173 | ||
|
|
c805c96f5a | ||
|
|
a3ec4080e6 | ||
|
|
3f6e511048 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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). 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',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
418
superset-frontend/src/components/Chart/ChartRenderer.tsx
Normal file
418
superset-frontend/src/components/Chart/ChartRenderer.tsx
Normal 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;
|
||||
@@ -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 =
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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] = {}
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"}
|
||||
|
||||
Reference in New Issue
Block a user