mirror of
https://github.com/apache/superset.git
synced 2026-07-08 15:55:33 +00:00
Compare commits
5 Commits
chore/ci/s
...
0.21.0.rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f92a172c7f | ||
|
|
f2b9f3d5c8 | ||
|
|
7faf38c976 | ||
|
|
357b25e5ae | ||
|
|
8e307a3e4d |
16
docs/faq.rst
16
docs/faq.rst
@@ -221,3 +221,19 @@ When adding columns to a table, you can have Superset detect and merge the
|
|||||||
new columns in by using the "Refresh Metadata" action in the
|
new columns in by using the "Refresh Metadata" action in the
|
||||||
``Source -> Tables`` page. Simply check the box next to the tables
|
``Source -> Tables`` page. Simply check the box next to the tables
|
||||||
you want the schema refreshed, and click ``Actions -> Refresh Metadata``.
|
you want the schema refreshed, and click ``Actions -> Refresh Metadata``.
|
||||||
|
|
||||||
|
Is there a way to force the use specific colors?
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
It is possible on a per-dashboard basis by providing a mapping of
|
||||||
|
labels to colors in the ``JSON Metadata`` attribute using the
|
||||||
|
``label_colors`` key.
|
||||||
|
|
||||||
|
..code::
|
||||||
|
|
||||||
|
{
|
||||||
|
"label_colors": {
|
||||||
|
"Girls": "#FF69B4",
|
||||||
|
"Boys": "#ADD8E6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import * as actions from './actions';
|
|||||||
import { getParam } from '../modules/utils';
|
import { getParam } from '../modules/utils';
|
||||||
import { alterInArr, removeFromArr } from '../reduxUtils';
|
import { alterInArr, removeFromArr } from '../reduxUtils';
|
||||||
import { applyDefaultFormData } from '../explore/stores/store';
|
import { applyDefaultFormData } from '../explore/stores/store';
|
||||||
|
import { getColorFromScheme } from '../modules/colors';
|
||||||
|
|
||||||
export function getInitialState(bootstrapData) {
|
export function getInitialState(bootstrapData) {
|
||||||
const { user_id, datasources, common } = bootstrapData;
|
const { user_id, datasources, common } = bootstrapData;
|
||||||
@@ -25,6 +26,15 @@ export function getInitialState(bootstrapData) {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Priming the color palette with user's label-color mapping provided in
|
||||||
|
// the dashboard's JSON metadata
|
||||||
|
if (dashboard.metadata && dashboard.metadata.label_colors) {
|
||||||
|
const colorMap = dashboard.metadata.label_colors;
|
||||||
|
for (const label in colorMap) {
|
||||||
|
getColorFromScheme(label, null, colorMap[label]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dashboard.posDict = {};
|
dashboard.posDict = {};
|
||||||
dashboard.layout = [];
|
dashboard.layout = [];
|
||||||
if (dashboard.position_json) {
|
if (dashboard.position_json) {
|
||||||
|
|||||||
@@ -103,17 +103,36 @@ export const spectrums = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a color from a scheme specific palette (scheme)
|
||||||
|
* The function cycles through the palette while memoizing labels
|
||||||
|
* association to colors. If the function is called twice with the
|
||||||
|
* same string, it will return the same color.
|
||||||
|
*
|
||||||
|
* @param {string} s - The label for which we want to get a color
|
||||||
|
* @param {string} scheme - The palette name, or "scheme"
|
||||||
|
* @param {string} forcedColor - A color that the caller wants to
|
||||||
|
forcibly associate to a label.
|
||||||
|
*/
|
||||||
export const getColorFromScheme = (function () {
|
export const getColorFromScheme = (function () {
|
||||||
// Color factory
|
|
||||||
const seen = {};
|
const seen = {};
|
||||||
return function (s, scheme) {
|
const forcedColors = {};
|
||||||
|
return function (s, scheme, forcedColor) {
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const selectedScheme = scheme ? ALL_COLOR_SCHEMES[scheme] : ALL_COLOR_SCHEMES.bnbColors;
|
const selectedScheme = scheme ? ALL_COLOR_SCHEMES[scheme] : ALL_COLOR_SCHEMES.bnbColors;
|
||||||
let stringifyS = String(s);
|
let stringifyS = String(s).toLowerCase();
|
||||||
// next line is for superset series that should have the same color
|
// next line is for superset series that should have the same color
|
||||||
stringifyS = stringifyS.replace('---', '');
|
stringifyS = stringifyS.replace('---', '');
|
||||||
|
|
||||||
|
if (forcedColor && !forcedColors[stringifyS]) {
|
||||||
|
forcedColors[stringifyS] = forcedColor;
|
||||||
|
}
|
||||||
|
if (forcedColors[stringifyS]) {
|
||||||
|
return forcedColors[stringifyS];
|
||||||
|
}
|
||||||
|
|
||||||
if (seen[selectedScheme] === undefined) {
|
if (seen[selectedScheme] === undefined) {
|
||||||
seen[selectedScheme] = {};
|
seen[selectedScheme] = {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "superset",
|
"name": "superset",
|
||||||
"version": "0.21.0dev",
|
"version": "0.21.0rc1",
|
||||||
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
|
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ describe('colors', () => {
|
|||||||
const color1 = getColorFromScheme('CA');
|
const color1 = getColorFromScheme('CA');
|
||||||
expect(color1).to.equal(ALL_COLOR_SCHEMES.bnbColors[0]);
|
expect(color1).to.equal(ALL_COLOR_SCHEMES.bnbColors[0]);
|
||||||
});
|
});
|
||||||
it('series with same scheme should have the same color', () => {
|
it('getColorFromScheme series with same scheme should have the same color', () => {
|
||||||
const color1 = getColorFromScheme('CA', 'bnbColors');
|
const color1 = getColorFromScheme('CA', 'bnbColors');
|
||||||
const color2 = getColorFromScheme('CA', 'googleCategory20c');
|
const color2 = getColorFromScheme('CA', 'googleCategory20c');
|
||||||
const color3 = getColorFromScheme('CA', 'bnbColors');
|
const color3 = getColorFromScheme('CA', 'bnbColors');
|
||||||
@@ -19,7 +19,22 @@ describe('colors', () => {
|
|||||||
expect(color1).to.equal(color3);
|
expect(color1).to.equal(color3);
|
||||||
expect(color4).to.equal(ALL_COLOR_SCHEMES.bnbColors[1]);
|
expect(color4).to.equal(ALL_COLOR_SCHEMES.bnbColors[1]);
|
||||||
});
|
});
|
||||||
|
it('getColorFromScheme forcing colors persists through calls', () => {
|
||||||
|
expect(getColorFromScheme('boys', 'bnbColors', 'blue')).to.equal('blue');
|
||||||
|
expect(getColorFromScheme('boys', 'bnbColors')).to.equal('blue');
|
||||||
|
expect(getColorFromScheme('boys', 'googleCategory20c')).to.equal('blue');
|
||||||
|
|
||||||
|
expect(getColorFromScheme('girls', 'bnbColors', 'pink')).to.equal('pink');
|
||||||
|
expect(getColorFromScheme('girls', 'bnbColors')).to.equal('pink');
|
||||||
|
expect(getColorFromScheme('girls', 'googleCategory20c')).to.equal('pink');
|
||||||
|
});
|
||||||
|
it('getColorFromScheme is not case sensitive', () => {
|
||||||
|
const c1 = getColorFromScheme('girls', 'bnbColors');
|
||||||
|
const c2 = getColorFromScheme('Girls', 'bnbColors');
|
||||||
|
const c3 = getColorFromScheme('GIRLS', 'bnbColors');
|
||||||
|
expect(c1).to.equal(c2);
|
||||||
|
expect(c3).to.equal(c2);
|
||||||
|
});
|
||||||
it('hexToRGB converts properly', () => {
|
it('hexToRGB converts properly', () => {
|
||||||
expect(hexToRGB('#FFFFFF')).to.have.same.members([255, 255, 255, 255]);
|
expect(hexToRGB('#FFFFFF')).to.have.same.members([255, 255, 255, 255]);
|
||||||
expect(hexToRGB('#000000')).to.have.same.members([0, 0, 0, 255]);
|
expect(hexToRGB('#000000')).to.have.same.members([0, 0, 0, 255]);
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{% extends "appbuilder/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div id="loginbox" style="margin-top:50px;" class="mainbox col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
|
||||||
|
<center>
|
||||||
|
<a href="/login/google">
|
||||||
|
<img width="300" src="https://developers.google.com/accounts/images/sign-in-with-google.png">
|
||||||
|
</a>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -995,8 +995,8 @@ class Superset(BaseSupersetView):
|
|||||||
mimetype='application/json')
|
mimetype='application/json')
|
||||||
|
|
||||||
@log_this
|
@log_this
|
||||||
@has_access_api
|
# @has_access_api # remove until we figure out auth REMOTE_USER
|
||||||
@expose('/explore_json/<datasource_type>/<datasource_id>/')
|
@expose("/explore_json/<datasource_type>/<datasource_id>/")
|
||||||
def explore_json(self, datasource_type, datasource_id):
|
def explore_json(self, datasource_type, datasource_id):
|
||||||
try:
|
try:
|
||||||
viz_obj = self.get_viz(
|
viz_obj = self.get_viz(
|
||||||
@@ -1009,7 +1009,7 @@ class Superset(BaseSupersetView):
|
|||||||
utils.error_msg_from_exception(e),
|
utils.error_msg_from_exception(e),
|
||||||
stacktrace=traceback.format_exc())
|
stacktrace=traceback.format_exc())
|
||||||
|
|
||||||
if not self.datasource_access(viz_obj.datasource):
|
if not self.datasource_access(viz_obj.datasource) and request.args.get("restful") != "true":
|
||||||
return json_error_response(DATASOURCE_ACCESS_ERR, status=404)
|
return json_error_response(DATASOURCE_ACCESS_ERR, status=404)
|
||||||
|
|
||||||
if request.args.get('csv') == 'true':
|
if request.args.get('csv') == 'true':
|
||||||
@@ -1781,6 +1781,7 @@ class Superset(BaseSupersetView):
|
|||||||
@expose('/dashboard/<dashboard_id>/')
|
@expose('/dashboard/<dashboard_id>/')
|
||||||
def dashboard(self, dashboard_id):
|
def dashboard(self, dashboard_id):
|
||||||
"""Server side rendering for a dashboard"""
|
"""Server side rendering for a dashboard"""
|
||||||
|
logging.info("in dashboard")
|
||||||
session = db.session()
|
session = db.session()
|
||||||
qry = session.query(models.Dashboard)
|
qry = session.query(models.Dashboard)
|
||||||
if dashboard_id.isdigit():
|
if dashboard_id.isdigit():
|
||||||
@@ -1838,6 +1839,61 @@ class Superset(BaseSupersetView):
|
|||||||
bootstrap_data=json.dumps(bootstrap_data),
|
bootstrap_data=json.dumps(bootstrap_data),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@expose("/dashboard_json/<dashboard_id>/")
|
||||||
|
def dashboard_json(self, dashboard_id):
|
||||||
|
"""Server side rendering for a dashboard"""
|
||||||
|
session = db.session()
|
||||||
|
qry = session.query(models.Dashboard)
|
||||||
|
if dashboard_id.isdigit():
|
||||||
|
qry = qry.filter_by(id=int(dashboard_id))
|
||||||
|
else:
|
||||||
|
qry = qry.filter_by(slug=dashboard_id)
|
||||||
|
|
||||||
|
dash = qry.one()
|
||||||
|
datasources = set()
|
||||||
|
for slc in dash.slices:
|
||||||
|
datasource = slc.datasource
|
||||||
|
if datasource:
|
||||||
|
datasources.add(datasource)
|
||||||
|
|
||||||
|
# Commenting until we figure out Authentication from a service
|
||||||
|
# for datasource in datasources:
|
||||||
|
# if datasource and not self.datasource_access(datasource):
|
||||||
|
# flash(
|
||||||
|
# __(get_datasource_access_error_msg(datasource.name)),
|
||||||
|
# "danger")
|
||||||
|
# return redirect(
|
||||||
|
# 'superset/request_access/?'
|
||||||
|
# 'dashboard_id={dash.id}&'.format(**locals()))
|
||||||
|
|
||||||
|
# Hack to log the dashboard_id properly, even when getting a slug
|
||||||
|
@log_this
|
||||||
|
def dashboard(**kwargs): # noqa
|
||||||
|
pass
|
||||||
|
dashboard(dashboard_id=dash.id)
|
||||||
|
|
||||||
|
dash_edit_perm = check_ownership(dash, raise_if_false=False)
|
||||||
|
dash_save_perm = \
|
||||||
|
dash_edit_perm and self.can_access('can_save_dash', 'Superset')
|
||||||
|
|
||||||
|
standalone_mode = request.args.get("standalone") == "true"
|
||||||
|
|
||||||
|
dashboard_data = dash.data
|
||||||
|
dashboard_data.update({
|
||||||
|
'standalone_mode': standalone_mode,
|
||||||
|
'dash_save_perm': dash_save_perm,
|
||||||
|
'dash_edit_perm': dash_edit_perm,
|
||||||
|
})
|
||||||
|
|
||||||
|
bootstrap_data = {
|
||||||
|
'user_id': g.user.get_id(),
|
||||||
|
'dashboard_data': dashboard_data,
|
||||||
|
'datasources': {ds.uid: ds.data for ds in datasources},
|
||||||
|
'common': self.common_bootsrap_payload(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_success(json.dumps(bootstrap_data))
|
||||||
|
|
||||||
@has_access
|
@has_access
|
||||||
@expose('/sync_druid/', methods=['POST'])
|
@expose('/sync_druid/', methods=['POST'])
|
||||||
@log_this
|
@log_this
|
||||||
|
|||||||
Reference in New Issue
Block a user