superset 图表种类扩展
superset目前发布的版本是0.28,由于其存在多处已知bug,本文我选用的是最新的0.33版本。0.33版本和0.28版本的目录结构已经产生了很多区别,而且多数文件夹名称也被修改,网上的资源多数是基于0.28版本或者更旧的版本,并没有太大借鉴意义。以下流程以添加一个echarts的散点图为例,简述superset扩展图表的开发流程,希望能起到一定的帮助。
目录
三、在\superset\assets\src\visualizations下修改文件
四、在\superset\assets\src\explore\controlPanels下修改文件
一、为superset配置echarts
在superset\assets\packages.json中的“dependencies”中添加"echarts": "^4.2.0-rc.2",版本号就用自己的echarts版本号
由于echarts散点图需要使用到reactify,所以在这里我们顺手也加上reactify的修改
然后打开命令行终端,执行以下命令:
cd superset/assets npm install -d npm run dev |
二、修改viz.py文件
viz.py 这个文件起到一个类似于视图的功能,从前端接受请求后进行一系列处理。我们可以看到,一个图例就是一个类。经过分析已有的这些类,发现这些类大部分是继承了BaseViz这个基类,并且主要重写了query_obj和get_data这两个方法。其中query_obj,顾名思义,是用来构造查询条件的。而查询条件来源于页面左侧的组件,这些组件的信息存储在form_data的一个字典中,query_obj方法中将会处理form_data,构造除自己需要的查询条件并返回,在BaseViz这个基类中,会根据这个查询条件来查询出数据。然后是get_data方法,get_data接收一个参数df(pandas的DataFrame类型),也就是query_obj之后查询出来的数据,也可以结合你自己构造的form_data中的一些条件对df进行处理。处理之后的数据以dict格式返回,这个dict会交给js进行下一步处理,也就是展示了。
除了两个比较重要的方法之外,我们自定义的类还应该有两个重要的类属性viz_type和verbose_name。其中viz_type是标识当前类,也就是我们自定义图例的名称,注意这个名称应该与后面的js也结合起来。verbose_name则是相当于别名,展示在前端的。
因为我们添加的是一个时间序列的散点图,这里我们把类命名为TimeSeriesScatterViz
class TimeSeriesScatterViz(BaseViz): viz_type = 'time_series_scatter' verbose_name = "Time Series Scatter" sort_series = False is_timeseries = True def query_obj(self): d = super(TimeSeriesScatterViz, self).query_obj() fd = self.form_data #form_data中包含界面左侧组件内容 if not fd.get('all_columns'): #这个字段对应×××组件,不为空 raise Exception('Choose Columns') if fd.get('all_columns'): d['columns'] = fd.get('all_columns') # all_columns是左侧组件名,后面会提到 # 这里的例子非常简单,其实可以做很多事 return d def get_data(self, df): # df是pandas的DataFrame类型 # 这里的例子非常简单,其实可以做很多事 data = np.array(df).tolist() #假设数据很简单,不需要做别的处理 # 如果除了绘图用的数据还有别的信息,可以构造一个字典来返回 # data = {'plot_data':plot_data,'other_info':other_info} return data |
三、在\superset\assets\src\visualizations下修改文件
按照已有的方式,我们将会为自己的图例增加一个文件夹,名字跟前面的类名一样就叫TimeSeriesScatter。文件夹之下还应该有一个文件夹,叫images,这个文件夹下面存放两个图片文件,分别是thumbnail.png和thumbnailLager.png(自己去echarts官网截个散点图,文件名和格式写对就行 https://www.echartsjs.com/examples/zh/index.html)展示在选图例的界面中,区别在尺寸大小不一样。
然后还应该有transformProps.js、TimeSeriesScatterChartPlugin.js、TimeSeriesScatter.js、ReactTimeSeriesScatter.js这四个文件
这里一定注意文件名不能错,要跟文件内部对象名对应,否则superset会找不到对应文件。比如你的类名叫ClassName。那么你应该有ClassName.js、ClassNameChartPlugin.js、RectClassName.js、transformProps.js这四个文件。我们示例的文件内容如下:
// ReactTimeSeriesScatter.js import reactify from '@superset-ui/chart/esm/components/reactify'; import Component from './TimeSeriesScatter'; export default reactify(Component); |
// TimeSeriesScatter.js import echarts from 'echarts'; import d3 from 'd3'; import PropTypes from 'prop-types'; const propTypes = { data: PropTypes.array, width: PropTypes.number, height: PropTypes.number, }; //检查类型,其中data包含viz.py中返回的数据,width和height为图表宽高 function TimeSeriesScatter(element, props){ const { width, height, data, } = props; // console.log(data) 可以检查一下data内容 const div = d3.select(element, props); var html = '<div id="time_series_scatter" style="height:' + height + 'px; width:' + width + 'px;"></div>'; div.html(html); //给echarts添加div var myChart = echarts.init(document.getElementById('time_series_scatter')); //初始化echarts,接下来的就是echarts内容了 var option = { tooltip: { formatter: '{b}: {c}' }, xAxis: { type: 'time', scale: true }, yAxis: { type: 'value', scale: true }, series: [{ type: 'scatter', symbolSize: 20, }, { type: 'scatter', data: data, }] }; myChart.setOption(option); } TimeSeriesScatter.displayName = 'Time Series Scatter'; TimeSeriesScatter.propTypes = propTypes; export default TimeSeriesScatter; |
// TimeSeriesScatterChartPlugin.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('Time Series Scatter'), description: '', credits: ['http://echarts.baidu.com/examples/editor.html?c=scatter-effect'], thumbnail, }); export default class TimeSeriesScatterChartPlugin extends ChartPlugin { constructor() { super({ metadata, transformProps, loadChart: () => import('./ReactTimeSeriesScatter.js'), }); } } |
// transformProps.js export default function transformProps(chartProps) { const { width, height, payload } = chartProps; //console.log(chartProps); 可以用来验证数据是否正确 return { data: payload.data, width, height, }; } |
四、在\superset\assets\src\explore\controlPanels下修改文件
这个文件夹下放的文件主要是来配置左侧的一些组件。对于新图例来说,需要添加一个新的文件来配置图例所需组件
//superset\assets\src\explore\controlPanels\TimeSeriesScatter.js import { t } from '@superset-ui/translation'; export default { controlPanelSections: [ { label: t('NOT GROUPED BY'), //控制块标题,可以有多个控制块,一块包含多个组件 description: t('Use this section if you want to query atomic rows'), //描述 expanded: true, controlSetRows: [ ['all_columns'], //使用的组件名 ['row_limit', null], ], }, ], }; |
其中\superset\assets\src\explore\controls.jsx文件(注意路径)定义了组件的形式与功能,如果需要自定义组件,可以在这个文件下开发,具体可以参考已有的部分。选取对自己的图例合适的组件也需要多看这个文件。比如在此例子中直接使用了controls.jsx里的Columns组件all_columns:
做完以上工作,需要使配置组件的js生效,还要修改superset\assets\src\explore\controlPanels\index.js、superset\assets\src\visualizations\presets\LegacyChartPreset.js
五、npm重编译 & 重启superset服务
//重新编译 npm run dev //重启服务 gunicorn -w 2 --timeout 120 -b 0.0.0.0:8000 --limit-request-line 0 --limit-request-field_size 0 --reload superset:app |
注意:如果在测试环境不想每次修改完js文件都手动重新编译可以选择热更新 packages.json->scripts->dev->命令行里添加“ --hot”
实测效果如下,其他图表也可以按照此流程进行添加