superset安装与集成echarts

一、安装环境要求:

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 [email protected] #添加库
	yarn add [email protected]
	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'

猜你喜欢

转载自blog.csdn.net/dzhmjl3/article/details/87877662