一、安装环境要求:
1、win7_64或win10_64,
2、python3.7,
3、superset0.27.0(因0.28.0目录发生变更,暂不适用)
4、.net framework4.6,
5、vc++14.0(http://go.microsoft.com/fwlink/?LinkId=691126,注意vc2015需卸载)
二、修改pip源,在用户目录下新建pip文件夹,新建pip.ini:
[global]
index-url=https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host=pypi.tuna.tsinghua.edu.cn
disable-pip-version-check = true
timeout = 6000
三、安装:
1、安装
c:
cd/
md superset
cd superset
pip install virtualenv #安装python虚拟环境库,py3.7自带,py -3 -m venv venvname
virtualenv venv #新建虚拟环境env文件夹
cd env\scripts
activate #激活虚拟环境
pip install --upgrade setuptools pip #更新pip,setuptools
pip install sasl(http://www.lfd.uci.edu/~gohlke/pythonlibs/#sasl) cryptography #安装库
pip install click==6.7 colorama==0.3.9 markdown==2.6.11 flask-sqlalchemy==2.1 (numpy)
pip install superset==0.27.0
pip install -r c:\requirements.txt (-r requirements-dev.txt) #安装依赖库,从github下载
python3.66不用修改,python3.7中async为关键字,修改3处:
在site-packages\superset\views\cores.js文件内: 修改变量async为async_in
2、启动服务,安装完成后执行以下命令可以使用:
cd env\lib\site-packages\superset\bin
fabmanager create-admin --app superset #创建用户
python superset db upgrade #升级数据库
python superset load_examples #加载例子
python superset init #初始化
python superset runserver -d #启动服务
四、集成echarts环境搭建:
npm config set registry http://registry.npm.taobao.org/ #设置npm源
npm install yarn -g #安装yarn依赖包管理工具
yarn config set registry http://registry.npm.taobao.org/ #设置yarn源
yarn add webpack@1.14.0 #添加库
yarn add webpack@3.1.2
yarn add cross-env
修改site-packages\superset\package.json: NODE_ENV前加cross-env,4处
yarn install #初始化
npm run dev | more # | 管道命令,方便查看错误,如无错误,可进行在线编译添加echarts
npm run dev 生成目录site-packages\superset\static\assets\dist,在python3.66下集成echarts生成dist目录后,覆盖到python3.7环境的dist,重启服务可以使用。
五、漏斗示例(修改时需启动服务和在线编译窗口同时运行):
1、下载echarts,存储site-packages\superset\static\assests\src\exploer\echarts.js
2、site-packages\superset\templates\supersetbase.html: 增加一行:
<script src="/static/assests/src/exploer/controlPanels/echarts.js"></script>
3、site-packages\superset\package.json:
"dependencies": {,"echarts": "^4.1.0",} # 注意','
4、site-packages\superset\static\assets\src\visualizations\index.js,增加:
VIZ_TYPES = { echarts_funnel:'echarts_funnel',}
vizMap = { [VIZ_TYPES.echarts_funnel]:require('./echarts_funnel.js'),}
5、site-packages\superset\viz.py: 增加类class EchartsFunnelViz(BaseViz):{},如下:
class EchartsFunnelViz(BaseViz):
# Funnel Chart
viz_type = 'echarts_funnel'
verbose_name = _('echarts_funnel')
is_timeseries = False
def get_data(self, df):
metric = self.metric_labels[0]
df = df.pivot_table(
index=self.groupby,
values=[metric])
df.sort_values(by=metric, ascending=False,inplace=True)
df = df.reset_index()
df.columns = ['name','value']
# mydata = [{'name':'p1','value':40},{'name':'p2','value':30},{'name':'p3','value':20}] #测试数据
return df.to_dict(orient='records')
6、site-packages\superset\static\assets\src\explore\vistypes.js:
在export const visTypes:{}增加新类型{ echarts_funnel: {}},如下:
echarts_funnel: {
label: t('echarts_funnel'),
showOnExplore: true,
controlPanelSections: [
{
label: t('Query'),
expanded: true,
controlSetRows: [
['metric'],
['adhoc_filters'],
['groupby'],
['limit'],
],
},
{
label: t('Chart Options'), // 这个是额外控制选项
expanded: true,
controlSetRows: [
['pie_label_type'],
['donut', 'show_legend'],
['labels_outside'],
['color_scheme'],
],
},
],
},
7、site-packages\superset\static\assets\src\visualizations: 新增加EchartsFunnel.js,如下:
import echarts from 'echarts';
function EchartsFunnel(slice, payload) { //两个参数payload就是 viz.py返回来的数据 slice不不知道从哪⾥里里来的似乎除了了编号没什什么⽤用
const div = d3.select(slice.selector); //这4⾏行行是创建⼀一个div⽤用来绑定给echats做显示的基础画板,并制定slice的id号
const sliceId = 'echarts_slice_' + slice.formData.slice_id;
const html = '<div id=' + sliceId + ' style="width:' + slice.width() + 'px;height:' + slice.height() + 'px;"></div>';
div.html(html); // reset
const myChart = echarts.init(document.getElementById(sliceId));
const mydata = payload.data;
// const mydata = [{'name': 'RDAA201851940000000001', 'value': 1},{'name': 'RDAA201851940000000002', 'value': 1}];
var option = {
title: {
text: '漏漏⽃斗图',
subtext: '图例例'
},
tooltip: {
trigger: 'item',
formatter: "{a} <br/>{b} : {c}%"
},
toolbox: {
feature: {
dataView: {readOnly: false},
restore: {},
saveAsImage: {}
}
},
legend: {
data: mydata.name
},
calculable: true,
series: [
{
name:'漏漏⽃斗图',
type:'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending',
gap: 2,
label: {
normal: {
show: true,
position: 'inside'
},
emphasis: {
textStyle: {
fontSize: 20
}
}
},
labelLine: {
normal: {
length: 10,
lineStyle: {
width: 1,
type: 'solid'
}
}
},
itemStyle: {
normal: {
borderColor: '#fff',
borderWidth: 1
}
},
data:mydata
}
]
};
// 使⽤用刚指定的配置项和数据显示图表。
myChart.setOption(option);
}
//
module.exports = EchartsFunnel;
8、增加图片到superset/static/assets/images/viz_thumbnails:
添加名字(echarts_funnel.png)要和vistypes.js中的名字一致
六、superset0.29rc7
1. 离线安装superset(复制的均为下载的包文件)
虚拟环境安装pip install --upgrade pip setuptools, pip install sasl(下载sasl文件),
删除C:\incubator-superset-0.29.0rc7\superset\static下的assets
复制C:\incubator-superset-0.29.0rc7\superset\assets到C:\incubator-superset-0.29.0rc7\superset\static下
修改 requirements.txt, 删除cryptography, 并单独安装cryptography和docs\requirements和requirements.txt
修改 requirements-dev.txt, 删除mysqlclient并单独安装, 并单独安装requirements-dev.txt
修改setup.py第18行with io.open('C:\incubator-superset-0.29.0rc7\README.md',
复制C:\incubator-superset-0.29.0rc7\superset文件夹到C:\venv\Lib\site-packages下,
并到C:\venv\Lib\site-packages下执行python c:\incubator-superset-0.29rc7\setup.py
复制.flaskenv到superset目录,并修改为:FLASK_APP=:app FLASK_ENV=development
复制src目录到asset
其它同上.
yarn add python.dotenv , # 加载flask环境变量(.flaskenv),或使用npm命令,下同
yarn install ,# 初始化依赖库
yarn run build , # 可不执行,重新使用webpack打包
npm run dev , # 可实时webpack打包, 在线修改时使用
flask1.0.2:
命令python superset runserver -d 修改为:
在superset目录下执行:
flask run -h 0.0.0.0 -p 8080 --with-threads --reload --debugger
因windows与linux不同,sqllab编辑器执行错误,需修改superset\utils\core.py:
def __enter__(self):
try:
#signal.signal(signal.SIGALRM, self.handle_timeout)
#signal.alarm(self.seconds)
pass
except ValueError as e:
logging.warning("timeout can't be used in the current context")
logging.exception(e)
def __exit__(self, type, value, traceback):
try:
#signal.alarm(0)
pass
except ValueError as e:
logging.warning("timeout can't be used in the current context")
logging.exception(e)
2.1 仿写已存在的CountryMap(修改后需重新编译)
引导界面
A、 在superset\viz.py增加
class EchartsCountryMapViz(BaseViz):
"""A country centric"""
viz_type = 'echarts_country_map'
verbose_name = _('Echarts Country Map')
is_timeseries = False
credits = 'From bl.ocks.org By john-guerra'
def query_obj(self):
qry = super(EchartsCountryMapViz, self).query_obj()
qry['metrics'] = [
self.form_data['metric']]
qry['groupby'] = [self.form_data['entity']]
return qry
def get_data(self, df):
fd = self.form_data
cols = [fd.get('entity')]
metric = self.metric_labels[0]
cols += [metric]
ndf = df[cols]
df = ndf
df.columns = ['country_id', 'metric']
d = df.to_dict(orient='records')
return d
控制面板
B、 在superset/assets/src/explore/controlPanels/index.js引入
import EchartsCountryMap from './EchartsCountryMap';
export const controlPanelConfigs = { echarts_country_map: EchartsCountryMap, }
C、 在superset/assets/src/explore/controlPanels/下新建EchartsCountryMap.js(左侧选项)
import { t } from '@superset-ui/translation';
export default {
controlPanelSections: [
{
label: t('Query'),
expanded: true,
controlSetRows: [
['entity'],
['metric'],
],
},
{
label: t('Options'),
controlSetRows: [
['select_country'],
['number_format'],
['linear_color_scheme'],
],
},
],
controlOverrides: {
entity: {
label: t('ISO 3166-2 codes of region/province/department'),
description: t('It\'s ISO 3166-2 of your region/province/department in your table. (see documentation for list of ISO 3166-2)'),
},
metric: {
label: t('Metric'),
description: 'Metric to display bottom title',
},
linear_color_scheme: {
renderTrigger: false,
},
},
};
对应适当的切片,及图表文件
D、 修改superset\static\assets\src\visualizations\presets\MapChartPreset.js,
(一般为CommonChartPreset.js)
import { Preset } from '@superset-ui/core';
import CountryMapChartPlugin from '../CountryMap/CountryMapChartPlugin';
import MapBoxChartPlugin from '../MapBox/MapBoxChartPlugin';
import WorldMapChartPlugin from '../WorldMap/WorldMapChartPlugin';
import EchartsCountryMapChartPlugin from '../EchartsCountryMap/EchartsCountryMapChartPlugin';
export default class MapChartPreset extends Preset {
constructor() {
super({
name: 'Maps',
plugins: [
new CountryMapChartPlugin().configure({ key: 'country_map' }),
new MapBoxChartPlugin().configure({ key: 'mapbox' }),
new WorldMapChartPlugin().configure({ key: 'world_map' }),
new EchartsCountryMapChartPlugin().configure({ key: 'echarts_country_map' }),
],
});
}
}
E、 复制countryMap,并改名为EchartsCountryMap
⑴修改transformProps.js: 在const {}和return {}新增字段:‘sliceId,’
⑵参照countryMap修改EchartsCountryMap.css文件和EchartsCountryMapChartPlugin.js和EchartsReactCountryMap.js文件(改名).
⑶修改EchartsCountryMap.js: 或使用countryMap.js文件,如:
import echarts from 'echarts';
import PropTypes from 'prop-types';
import d3 from 'd3';
import { format as d3Format } from 'd3-format';
import './EchartsCountryMap.css';
import { getSequentialSchemeRegistry } from '@superset-ui/color';
const propTypes = {
data: PropTypes.arrayOf(PropTypes.shape({
country_id: PropTypes.string,
metric: PropTypes.number,
})),
width: PropTypes.number,
height: PropTypes.number,
country: PropTypes.string,
linearColorScheme: PropTypes.string,
mapBaseUrl: PropTypes.string,
numberFormat: PropTypes.string,
sliceId: PropTypes.number,
};
function EchartsCountryMap(element, props) {
const {
data,
width,
height,
country,
linearColorScheme,
mapBaseUrl = '/static/assets/src/visualizations/EchartsCountryMap/countries',
numberFormat,
sliceId,
} = props;
const div = d3.select(element);
const sliceID = 'echarts_slice_' + sliceId;
const html = '<div id=' + sliceID + ' style="width:' + width + 'px;height:' + height + 'px;"></div>';
div.html(html); // reset
const myChart = echarts.init(document.getElementById(sliceID));
const json = data;
let oldFeatures = {};
const features = [];
const resultFeatures = {};
const colorScale = getSequentialSchemeRegistry().get(linearColorScheme);
const colors=colorScale.getColors();
const format = d3Format(numberFormat);
let formatter;
if (format === 'none') {
formatter = '';
} else if (format === 'province') {
formatter = '{b}';
} else if (format === 'province+number') {
formatter = '{b}:{c}';
} else if (format === 'number') {
formatter = '{c}';
}
let max = null;
let min = null;
//
const dataAll = [];
$.each(json, function (i, item) {
const d = {};
d.name = item.country_id;
d.value = item.metric;
if (max == null || d.value > max) {
max = d.value;
}
if (min == null || d.value < min) {
min = d.value;
}
dataAll.push(d);
});
const countryKey = country.toLowerCase();
const url = `${mapBaseUrl}/${countryKey}.geojson`;
d3.json(url, function (datas) {
oldFeatures = datas.features;
// domecharts
// var name;
$.each(oldFeatures, function (i, oldFeature) {
const feature = {};
feature.type = oldFeature.type;
feature.id = oldFeature.properties.ID_0;
feature.properties = {};
feature.properties.name = oldFeature.properties.NL_NAME_1;
if (oldFeature.properties.NL_NAME_1 == null) {
feature.properties.name = oldFeature.properties.NAME_1;
} else {
feature.properties.name = oldFeature.properties.NL_NAME_1;
}
feature.geometry = oldFeature.geometry;
features.push(feature);
});
resultFeatures.type = datas.type;
resultFeatures.features = features;
echarts.registerMap('USA', resultFeatures, {
Alaska: { //
left: -131,
top: 25,
width: 15,
},
Hawaii: {
left: -110, //
top: 28,
width: 5,
},
'Puerto Rico': { //
left: -76,
top: 26,
width: 2,
},
});
const option = {
//
tooltip: {
trigger: 'item',
showDelay: 0,
transitionDuration: 0.2,
formatter(params) {
let value = (params.value + '').split('.');
value = value[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, '$1,');
return params.seriesName + '<br/>' + params.name + ': ' + value;
},
},
//
visualMap: {
left: 'right',
min,
max,
inRange: {
color: colors,
},
text: ['High', 'Low'], //
calculable: true,
},
//
toolbox: {
show: true,
// orient: 'Horizontal',
left: 'left',
top: 'top',
feature: {
restore: {},
dataView: { readOnly: false },
saveAsImage: {},
},
},
//
series: [
{
name: '',
type: 'map',
roam: true,
map: 'USA',
// map: fd.select_country.toUpperCase(),
label: {
normal: {
show: true, //
textStyle: { color: '#238e23' }, //
formatter,
},
emphasis: {//
show: true,
textStyle: { color: '#800080' },
},
},
// itemStyle: {
// normal: {
// show: true,//
// textStyle: {color: "#c71585"},//
// formatter: '{c}'
// },
// emphasis: {label: {show: true},formatter:'{c}'}
// },
//
textFixed: {
Alaska: [20, -20],
},
data: dataAll,
},
],
};
// myChart.dispatchAction({
// type:'mapunselected',
// seriesId:string,
// name:'a',
// selected:a
// });
myChart.setOption(option);
});
}
EchartsCountryMap.displayName = 'EchartsCountryMap';
EchartsCountryMap.propTypes = propTypes;
export default EchartsCountryMap;
2.2 集成echarts的仪表盘gauge,
文件位置均在site-packages\superset,修改后需重新编译
A、从后台获取数据 修改viz.py, 增加:
class Gauge(BaseViz):
"""Gauge"""
viz_type = 'gauge'
verbose_name = _('Gauge')
is_timeseries = False
def get_data(self, df):
metric = self.metric_labels[0]
df = df.pivot_table(
index = 1,#self.groupby,
values=[metric])
return df.to_dict(orient='records')
B、控制面板引导文件 修改 \static\assets\src\explore\controlPanels\index.js,增加:
import Gauge from './Gauge';
export const controlPanelConfigs = {
...
gauge: Gauge,
};
C、控制面板 在\static\assets\src\explore\controlPanels\下增加文件gauge.js:
import { t } from '@superset-ui/translation';
export default {
controlPanelSections: [
{
label: t('Query'),
expanded: true,
controlSetRows: [
['metric'],
],
},
{
label: t('Chart Options'),
expanded: true,
controlSetRows: [
],
},
],
controlOverrides: {
row_limit: {
default: 25,
},
},
};
D可视化文件配置 在static\assets\src\visualizations\下新建gauge文件夹:
1、新建images文件夹,并存放thumbnail.png图片
2、新建css文件:(图表使用)
.partition {
position: relative;
}
.partition .chart {
display: block;
margin: auto;
font-size: 11px;
}
.partition rect {
stroke: #eee;
fill: #aaa;
fill-opacity: .8;
transition: fill-opacity 180ms linear;
cursor: pointer;
}
.partition rect:hover {
fill-opacity: 1;
}
.partition g text {
font-weight: bold;
fill: rgba(0, 0, 0, 0.8);
}
.partition g:hover text {
fill: rgba(0, 0, 0, 1);
}
.partition .partition-tooltip {
position: absolute;
top: 0;
left: 0;
opacity: 0;
padding: 5px;
pointer-events: none;
background-color: rgba(255,255,255, 0.75);
border-radius: 5px;
}
.partition-tooltip td {
padding-left: 5px;
font-size: 11px;
}
3、新建GaugeChartPlugin.js, 通过index.js调用本组件。
import { t } from '@superset-ui/translation';
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
const metadata = new ChartMetadata({
name: t('Gauge'),
description: '',
thumbnail,
});
export default class GaugeChartPlugin extends ChartPlugin {
constructor() {
super({
metadata,
transformProps,
loadChart: () => import('./ReactGauge.js'),
});
}
}
4、新建ReactGauge.js, react引用
import reactify from '../../utils/reactify';
import Component from './Gauge';
export default reactify(Component);
5、新建transformProps.js, 整理可提供的变量参数
export default function transformProps(chartProps) {
const { width, height, datasource, formData, payload } = chartProps;
const {
metrics,
dateTimeFormat,
} = formData;
const { verboseMap } = datasource;
return {
width,
height,
data: payload.data,
metrics,
dateTimeFormat,
};
}
6、新建Gauge.js, 生成图表文件
import echarts from 'echarts';
import d3 from 'd3';
import PropTypes from 'prop-types';
import { hierarchy } from 'd3-hierarchy';
import { CategoricalColorNamespace } from '@superset-ui/color';
import { d3TimeFormatPreset } from '../../modules/utils';
import './Gauge.css';
const propTypes = {
width: PropTypes.number,
height: PropTypes.number,
data: PropTypes.arrayOf(PropTypes.shape({
count: PropTypes.number,
}))
};
function Gauge(element, props){
// 建立chart图表的容器
const div = d3.select(element);
const divId = 'echarts_guage' + "_guage_ID";
var html = `<div id="${divId}" style="width: ${props.width}px;height: ${props.height}px;"></div>`;
div.html(html); // 重新加载
// 初始化
var myChart = echarts.init(document.getElementById(divId));
// 图表参数配置
var option = {
tooltip : {
formatter: "{a} <br/>{c} {b}"
},
toolbox: {
show: true,
feature: {
restore: {show: true},
saveAsImage: {show: true}
}
},
series : [
{
name: '速度',
type: 'gauge',
z: 1,
min: 0,
max: 50000,
splitNumber: 10,
radius: '100%',
axisLine: { // 坐标轴线
lineStyle: { // 属性lineStyle控制线条样式
width: 25
}
},
axisTick: { // 坐标轴小标记
length: 35, // 属性length控制线长
lineStyle: { // 属性lineStyle控制线条样式
color: 'auto'
}
},
splitLine: { // 分隔线
length: 40, // 属性length控制线长
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
color: 'auto'
}
},
axisLabel: {
backgroundColor: 'auto',
borderRadius: 2,
color: '#eee',
padding: 10,
textShadowBlur: 2,
textShadowOffsetX: 1,
textShadowOffsetY: 1,
textShadowColor: '#222'
},
title : {
// 其余属性默认使用全局文本样式,详见TEXTSTYLE
fontWeight: 'bolder',
fontSize: 40,
fontStyle: 'italic',
},
detail : {
// 其余属性默认使用全局文本样式,详见TEXTSTYLE
formatter: function (value) {
value = (value + '').split('.');
value.length < 2 && (value.push('00'));
return ('00' + value[0]).slice(-2) + '.' + (value[1] + '00').slice(0, 2);},
fontWeight: 'bolder',
borderRadius: 3,
backgroundColor: '#444',
borderColor: '#aaa',
shadowBlur: 5,
shadowColor: '#333',
shadowOffsetX: 0,
shadowOffsetY: 3,
borderWidth: 2,
textBorderColor: '#000',
textBorderWidth: 2,
textShadowBlur: 2,
textShadowColor: '#fff',
textShadowOffsetX: 0,
textShadowOffsetY: 0,
fontFamily: 'Arial',
width: 100,
color: '#eee',
rich: {}
},
data:[{value: props.data[0].count, name: 'km/h'}]
}
]
};
// 参数加载
myChart.setOption(option);
};
Gauge.displayName = 'Gauge';
Gauge.propTypes = propTypes;
export default Gauge;
八、匿名登录和默认语言
1,修改config.py: PUBLIC_ROLE_LIKE_GAMMA = True
2, 网页admin登录superset,安全>用户列表>修改public,增加
all datasource access on all_datasource_access
all database access on all_database_access
3, superset\bin下,执行python superset init,初始化用户角色和权限
4,到看板页面,分享相应看板的链接。
5,修改config.py:BABEL_DEFAULT_LOCALE = 'zh'