提交 e9f4f7ef 编写于 作者: B BingBlog

add images, scalar config

上级 8c9d8df8
/**
* get mock data
*
* @param {string} path request path
* @param {Object} queryParam query params
* @param {Object} postParam post params
* @return {Object}
*/
module.exports = function (path, queryParam, postParam) {
return {
// moock delay
_timeout: 0,
// mock http status
_status: 200,
// mock response data
_data: {
status: 0,
msg: 'SUCCESS',
data: [
{
"wall_time": 1512549785.061623,
"step": 60,
"query":
"sample=0&index=0&tag=input_reshape%2Finput%2Fimage%2F0&run=test",
"width": 28,
"height": 28
},
{"wall_time": 1512886109.672786, "step": 60, "query": "sample=0&index=1&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
{"wall_time": 1512886124.266915, "step": 210, "query": "sample=0&index=2&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
{"wall_time": 1512886138.898628, "step": 330, "query": "sample=0&index=3&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
{"wall_time": 1512886139.883663, "step": 340, "query": "sample=0&index=4&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
{"wall_time": 1512886147.195567, "step": 410, "query": "sample=0&index=5&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
{"wall_time": 1512886156.47856, "step": 500, "query": "sample=0&index=6&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
{"wall_time": 1512886187.82793, "step": 810, "query": "sample=0&index=7&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
{"wall_time": 1512886200.386198, "step": 950, "query": "sample=0&index=8&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
{"wall_time": 1512886204.224405, "step": 990, "query": "sample=0&index=9&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}
]
}
};
};
/**
* @file mock data
* @author autoresponse
*/
/* eslint-disable fecs-camelcase */
/**
* 获取 mock 响应数据
*
* @param {string} path 请求路径名
* @param {Object} queryParam 查询参数信息
* @param {Object} postParam post 的查询参数信息
* @return {Object}
*/
module.exports = function (path, queryParam, postParam) {
return {
// 可以通过该属性来设置响应的延时,也可以设为值为'0,100',表示随机 0-100ms 的延时,默认 0
_timeout: 0,
// 通过该状态来设置响应的 http 的状态码,默认 200
_status: 200,
// 对于要响应的 json 数据可以统一放在该字段里,也可以不使用该字段,直接跟 _xx 属性平级放
_data: []
};
};
/* eslint-enable fecs-camelcase */
/**
* get mock data
*
* @param {string} path request path
* @param {Object} queryParam query params
* @param {Object} postParam post params
* @return {Object}
*/
module.exports = function (path, queryParam, postParam) {
return {
// moock delay
_timeout: 0,
// mock http status
_status: 200,
// mock response data
_data: {
status: 0,
msg: 'SUCCESS',
data: {
"test": {
"input_reshape/input/image/6": {
"displayName": "input_reshape/input/image/6", "description": "", "samples": 1}, "input_reshape/input/image/7": {"displayName": "input_reshape/input/image/7", "description": "", "samples": 1}, "input_reshape/input/image/4": {"displayName": "input_reshape/input/image/4", "description": "", "samples": 1}, "input_reshape/input/image/5": {"displayName": "input_reshape/input/image/5", "description": "", "samples": 1}, "input_reshape/input/image/2": {"displayName": "input_reshape/input/image/2", "description": "", "samples": 1}, "input_reshape/input/image/3": {"displayName": "input_reshape/input/image/3", "description": "", "samples": 1}, "input_reshape/input/image/0": {"displayName": "input_reshape/input/image/0", "description": "", "samples": 1}, "input_reshape/input/image/1": {"displayName": "input_reshape/input/image/1", "description": "", "samples": 1}, "input_reshape/input/image/8": {"displayName": "input_reshape/input/image/8", "description": "", "samples": 1}, "input_reshape/input/image/9": {"displayName": "input_reshape/input/image/9", "description": "", "samples": 1}}, "train": {"input_reshape/input/image/6": {"displayName": "input_reshape/input/image/6", "description": "", "samples": 1}, "input_reshape/input/image/7": {"displayName": "input_reshape/input/image/7", "description": "", "samples": 1}, "input_reshape/input/image/4": {"displayName": "input_reshape/input/image/4", "description": "", "samples": 1}, "input_reshape/input/image/5": {"displayName": "input_reshape/input/image/5", "description": "", "samples": 1}, "input_reshape/input/image/2": {"displayName": "input_reshape/input/image/2", "description": "", "samples": 1}, "input_reshape/input/image/3": {"displayName": "input_reshape/input/image/3", "description": "", "samples": 1}, "input_reshape/input/image/0": {"displayName": "input_reshape/input/image/0", "description": "", "samples": 1}, "input_reshape/input/image/1": {"displayName": "input_reshape/input/image/1", "description": "", "samples": 1}, "input_reshape/input/image/8": {"displayName": "input_reshape/input/image/8", "description": "", "samples": 1}, "input_reshape/input/image/9": {"displayName": "input_reshape/input/image/9", "description": "", "samples": 1}}}
}
};
};
......@@ -16,7 +16,7 @@ module.exports = function (path, queryParam, postParam) {
_data: {
status: 0,
msg: 'SUCCESS',
data: ["train", "test", "model"]
data: ["train", "test"]
}
};
};
......@@ -8,9 +8,7 @@
"release": "cross-env NODE_ENV=production node ./tool/build.js",
"build": "cross-env NODE_ENV=dev node ./tool/build.js",
"dev": "cross-env NODE_ENV=dev node tool/dev-server.js",
"lint": "./node_modules/fecs/bin/fecs --rule",
"precommit": "npm run lint",
"prepush": "npm run lint"
"lint": "./node_modules/fecs/bin/fecs --rule"
},
"engines": {
"node": ">= 6.4.0"
......@@ -54,7 +52,6 @@
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.4",
"husky": "^0.14.3",
"json-loader": "^0.5.4",
"opn": "^5.1.0",
"optimize-css-assets-webpack-plugin": "^1.3.2",
......
......@@ -32,7 +32,7 @@ export default {
},
{
value: '2',
url: '/image',
url: '/images',
title: 'IMAGES'
}
]
......
......@@ -3,31 +3,57 @@
<div class="visual-dl-chart-box" style="{{computedStyle}}">
</div>
<div class="visual-dl-chart-actions">
<san-button on-click="expandArea">
<san-icon size="20">settings_overscan</san-icon>
</san-button>
<ui-dropdown-menu
san-if="downloadLink"
stlye="width:100px;"
hintText="download type"
items="{{runsItems}}"
value="{=downloadType=}"
/>
<san-button on-click="handleDownLoad">
<san-icon>file_download</san-icon>
<san-button
san-if="downloadLink"
on-click="handleDownLoad">
<san-icon size="20">file_download</san-icon>
</san-button>
</div>
</div>
</template>
<script>
// components
import Button from 'san-mui/Button';
import Icon from 'san-mui/Icon';
import DropDownMenu from '../../../common/ui/DropDownMenu';
import {generateJsonAndDownload} from '../../../common/util/downLoadFile';
import echarts from 'echarts';
// libs
import axios from 'axios';
import {isFinite, flatten, maxBy, minBy, sortBy, max, min} from 'lodash';
import echarts from 'echarts';
import {generateJsonAndDownload} from '../../../common/util/downLoadFile';
import {quantile} from '../../../common/util/index';
// service
import {getPluginScalarsScalars} from '../../../service';
const originLinesOpacity = 0.3;
const defaultSmoothWeight = 0.6;
const lineWidth = 1.5;
const minQuantile = 0.05;
const maxQuantile = 0.95;
// the time to refresh chart data
const intervalTime = 30;
export default {
components: {
'ui-dropdown-menu': DropDownMenu,
'san-button': Button,
'san-icon': Icon
},
computed: {
computedStyle() {
let width = this.data.get('width');
......@@ -36,12 +62,11 @@ export default {
+ 'width:' + width + 'px;';
}
},
initData() {
return {
width: 400,
height: 300,
// line config
options: {},
data: [
{
name: 'train',
......@@ -52,15 +77,49 @@ export default {
downloadType: ''
};
},
inited() {
this.watch('runsItems', val => {
this.initDownloadType();
});
this.watch('orginData', orginData => {
this.setChartData();
this.setChartsOutlier();
});
this.watch('smoothing', smoothing => {
this.setChartData();
});
this.watch('outlier', outlier => {
// outlier
this.setChartsOutlier();
});
this.watch('horizontal', horizontal => {
this.setChartHorizon(horizontal);
});
this.watch('runs', runs => {
this.setChartsRuns();
});
},
attached() {
let tagInfo = this.data.get('tagInfo');
this.initCharts(tagInfo);
if (this.data.get('running')) {
this.startInterval();
}
this.watch('running', running => {
running ? this.startInterval() : this.stopInterval();
});
},
detached() {
this.stopInterval();
},
initDownloadType() {
......@@ -74,24 +133,48 @@ export default {
initCharts(tagInfo) {
this.createChart();
this.setChartsOptions(tagInfo);
this.setChartsData(tagInfo);
this.getOriginChartsData(tagInfo);
},
createChart() {
let el = this.el.getElementsByClassName('visual-dl-chart-box')[0];
this.myChart = echarts.init(el);
},
setChartsOptions({tagList, tag}) {
let seriesOption = tagList.map(item => {
return {
let seriesOption = tagList.map(item => [
{
name: item.run,
type: 'line',
showSymbol: false,
hoverAnimation: false,
z: 0,
data: [],
smooth: true
};
});
animationDuration: 100,
lineStyle: {
normal: {
opacity: originLinesOpacity,
width: lineWidth
}
}
},
{
name: item.run,
type: 'line',
showSymbol: false,
hoverAnimation: false,
z: 1,
data: [],
animationDuration: 100,
lineStyle: {
normal: {
width: lineWidth
}
}
}
]
);
seriesOption = flatten(seriesOption);
let legendOptions = tagList.map(item => item.run);
let option = {
title: {
......@@ -103,9 +186,9 @@ export default {
tooltip: {
trigger: 'axis',
axisPointer: {
animation: false
animation: true
},
position: [10, 300]
position: ['10%', '90%']
},
toolbox: {
show: true,
......@@ -115,39 +198,26 @@ export default {
restore: {},
saveAsImage: {}
},
left: 40,
top: 270
left: '10%',
top: '90%'
},
legend: {
data: legendOptions,
top: 30
top: '10%'
},
grid: {
top: 70,
bottom: 50
left: '10%',
top: '20%',
right: '10%',
bottom: '20%'
},
xAxis: [
{
type: 'value',
boundaryGap: false
xAxis: {
type: 'value'
},
{
type: 'value',
boundaryGap: false
}
],
yAxis: {
type: 'value',
boundaryGap: [0, '100%'],
min(value) {
return value.min;
},
max(value) {
return value.max;
},
axisLabel: {
formatter(value, index) {
// keep 0.11111 to 0.111
return value.toString().slice(0, 5);
}
}
......@@ -156,7 +226,20 @@ export default {
};
this.myChart.setOption(option);
},
setChartsData({tagList, tag}) {
// get origin data per 60 seconds
startInterval() {
this.getOringDataInterval = setInterval(() => {
let tagInfo = this.data.get('tagInfo');
this.getOriginChartsData(tagInfo);
}, intervalTime * 1000);
},
stopInterval() {
clearInterval(this.getOringDataInterval);
},
getOriginChartsData({tagList, tag}) {
let requestList = tagList.map(item => {
let params = {
run: item.run,
......@@ -165,22 +248,41 @@ export default {
return getPluginScalarsScalars(params);
});
axios.all(requestList).then(resArray => {
let seriesData = resArray.map(res => {
return {
data: res.data,
this.data.set('orginData', resArray.map(res => res.data));
});
},
setChartData() {
let orginData = this.data.get('orginData');
let seriesData = orginData.map(lineData => {
// add the smoothed data
this.tansformDataset(lineData);
return [
{
data: lineData,
encode: {
// map 1 to xAixs
// map 1 to xAixs
x: [1],
// map 2 to yAixs
// map 2 to yAixs
y: [2]
}
};
},
{
data: lineData,
encode: {
// map 1 to xAixs
x: [1],
// map 3 to yAixs, the third number is smoothed value
y: [3]
}
}
];
});
this.myChart.setOption({
series: seriesData
});
series: flatten(seriesData)
});
},
getChartOptions() {
return this.myChart.getOption() || {};
},
......@@ -192,15 +294,199 @@ export default {
let tagInfo = this.data.get('tagInfo');
let fileName = tagInfo.tag.replace(/\//g, '-');
generateJsonAndDownload(sery.data, fileName);
},
tansformDataset(seriesData) {
let smoothing = this.data.get('smoothing');
// smooth
this.tansformData(seriesData, smoothing || defaultSmoothWeight);
},
/**
* @desc 1、add smooth data depend on smoothingWeight. see https://en.wikipedia.org/wiki/Moving_average for detail
* 2、add relative data
* @param {Object} echarts series Object
* @param {number} smoothingWeight smooth weight, between 0 ~ 1
*/
tansformData(seriesData, smoothingWeight) {
let data = seriesData;
let last = data.length > 0 ? 0 : NaN;
let numAccum = 0;
let startValue;
data.forEach((d, i) => {
let nextVal = d[2];
// second to millisecond
let millisecond = Math.floor(d[0] * 1000);
if (i === 0) {
startValue = millisecond;
}
// relative time, millisecond to hours
d[4] = Math.floor(millisecond - startValue) / (60 * 60 * 1000);
if (!isFinite(nextVal)) {
d[3] = nextVal;
} else {
last = last * smoothingWeight + (1 - smoothingWeight) * nextVal;
numAccum++;
let debiasWeight = 1;
if (smoothingWeight !== 1.0) {
debiasWeight = 1.0 - Math.pow(smoothingWeight, numAccum);
}
d[3] = last / debiasWeight;
}
});
},
// chart outlier options methods and functions ---- start
// compute Y domain from originData
setChartsOutlier(seriesData) {
let outlier = this.data.get('outlier');
let orginData = this.data.get('orginData');
let domainRangeArray = orginData.map(seriesData => this.computeDataRange(seriesData, outlier));
// compare,get the best Y domain。
let flattenNumbers = flatten(domainRangeArray);
let finalMax = max(flattenNumbers);
let finalMin = min(flattenNumbers);
// add padding
let PaddedYDomain = this.paddedYDomain(finalMin, finalMax);
this.setChartOutlierOptions(PaddedYDomain);
// store Y domain,if originData is not change,Y domain keep same.
},
// compute max and min from array, if outlier is true, return quantile range
computeDataRange(arr, isQuantile) {
// get data range
let max;
let min;
if (!isQuantile) {
// get the orgin data range
max = maxBy(arr, item => item[2])[2];
min = minBy(arr, item => item[2])[2];
}
else {
// get the quantile range
let sorted = sortBy(arr, [item => item[2]]);
min = quantile(sorted, minQuantile, item => item[2]);
max = quantile(arr, maxQuantile, item => item[2]);
}
return [min, max];
},
paddedYDomain(min, max) {
return {
max: max > 0 ? max * 1.1 : max * 0.9,
min: min > 0 ? min * 0.9 : min * 1.1
};
},
setChartOutlierOptions({min, max}) {
this.myChart.setOption({
yAxis: {
min,
max
}
});
},
// chart horizontal options methods and functions ---- start
setChartHorizon(horizontal) {
let seriesOption = this.myChart.getOption().series;
let encodeSeries = val => {
return {
encode: {
x: [val]
}
};
};
let stepSeies = seriesOption.map(item => encodeSeries(1));
let relativeSeies = seriesOption.map(item => encodeSeries(4));
let wallSeries = seriesOption.map(item => encodeSeries(0));
let horizontalToxAxisOptions = {
step: {
xAxis: {
type: 'value'
},
series: stepSeies
},
relative: {
xAxis: {
type: 'value'
},
series: relativeSeies
},
wall: {
xAxis: {
type: 'time'
},
series: wallSeries
}
};
this.myChart.setOption(horizontalToxAxisOptions[horizontal]);
},
expandArea() {
let isExpand = this.data.get('isExpand');
if (!isExpand) {
let el = this.el.getElementsByClassName('visual-dl-chart-box')[0];
el.style.width = '800px';
el.style.height = '600px';
this.data.set('isExpand', true);
this.myChart.resize({
width: 800,
height: 600
});
}
else {
let el = this.el.getElementsByClassName('visual-dl-chart-box')[0];
el.style.width = '400px';
el.style.height = '300px';
this.data.set('isExpand', false);
this.myChart.resize({
width: 400,
height: 300
});
}
},
setChartsRuns() {
// let tagInfo = this.data.get('tagInfo');
// let runs = this.data.get('config.runs');
// let seriesOption = [
// {
// lineStyle: {
// normal: {
// opacity: originLinesOpacity
// }
// }
// }
// ];
}
};
</script>
<style lang="stylus">
.visual-dl-charts
float left
margin-bottom 20px
.visual-dl-chart-actions
height 50px
margin-left 10%
.sm-form-item
width 300px
display inline-block
float left
width 100px
margin-top 0px
display block
.sm-button
float left
display block
height 20px
line-height 20px
margin-top 10px
padding 0 10px
</style>
<template>
<div class="visual-dl-image">
<h3>{{tagInfo.tag.displayName}}
<span>{{tagInfo.run}}</span>
</h3>
<p>
<span>Step:</span>
<span>{{imgData.step}};</span>
<span>{{imgData.wall_time | formatTime}}</span>
</p>
<san-slider
on-change="handleSlideChange($event)"
value="{{currentIndex}}"
min="{{slider.min}}"
max="{{steps}}"
step="{{1}}"
/>
<img src="{{imgData.imgSrc}}" width="{{imgData.width}}" height="{{imgData.height}}"/>
</div>
</template>
<script>
import Slider from 'san-mui/Slider';
import {getPluginImagesImages} from '../../../service';
export default {
components: {
'san-slider': Slider
},
computed: {
steps() {
let data = this.data.get('data') || [];
return data.length - 1;
}
},
filters: {
formatTime(value) {
if (!value) {
return;
}
let time = new Date();
time.setTime(value.toString().split('.')[0]);
return time;
}
},
initData() {
return {
slider: {
value: '0',
label: '',
min: 0,
step: 1
},
currentIndex: 0
};
},
inited() {
let {run, tag} = this.data.get('tagInfo');
let {displayName, samples} = tag;
let params = {
run,
displayName,
samples
};
getPluginImagesImages(params).then(({data}) => {
this.data.set('data', data);
this.data.set('currentIndex', data.length - 1);
});
// currentIndex change event
this.watch('currentIndex', index => {
/* eslint-disable fecs-camelcase */
let currentImgInfo = this.data.get('data') ? this.data.get('data')[index] : {};
let {height, width, query, step, wall_time} = currentImgInfo;
let url = '/data/plugin/images/individualImage?ts=' + wall_time;
let imgSrc = [url, query].join('&');
this.data.set('imgData', {
imgSrc,
height,
width,
step,
wall_time
});
/* eslint-enable fecs-camelcase */
});
},
handleSlideChange(val) {
this.data.set('currentIndex', val);
}
};
</script>
<style lang="stylus">
.visual-dl-image
float left
padding 10px
font-size 12px
.visual-dl-chart-actions
.sm-form-item
width 300px
display inline-block
</style>
......@@ -7,7 +7,7 @@
<span>{{title}}</span>
</h3>
<div
style="display:{{displayStyle}}"
san-if="{{isShow}}"
class="visaul-dl-expand-panel-content"
>
<slot></slot>
......@@ -16,12 +16,6 @@
</template>
<script>
export default {
computed: {
displayStyle() {
let isShow = this.data.get('isShow');
return isShow ? 'block' : 'none';
}
},
initData() {
return {
isShow: false
......
......@@ -9,6 +9,8 @@
max="{{max}}"
step="{{step}}"
/>
<span>{{value}}</span>
<!--
<san-input-number
min="{{min}}"
max="{{max}}"
......@@ -17,6 +19,7 @@
on-change="handlerChange($event)"
size="small">
</san-input-number>
-->
</div>
</div>
</template>
......@@ -24,6 +27,7 @@
<script>
import Slider from 'san-mui/Slider';
import InputNumber from 'san-mui/InputNumber';
import {debounce} from 'lodash';
export default {
components: {
'san-slider': Slider,
......@@ -35,11 +39,18 @@ export default {
label: '',
max: 0,
min: 0,
step: 0
step: 0,
debounce: 400
};
},
handleSlideChange(val) {
inited() {
let debounceTime = this.data.get('debounce');
this.handleSlideChange = debounce(val => {
this.data.set('value', val.toString());
}, debounceTime);
this.handlerChange = debounce(e => {
this.data.set('value', e.target.value);
}, debounceTime);
}
};
</script>
......@@ -53,7 +64,7 @@ export default {
margin-right 20px
margin-top 4px
float left
width 100px
width 160px
.sm-slider-thumb
transform translate(0, -50%)
.sm-inputNumber
......
......@@ -52,7 +52,22 @@ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fo
color #58666e
.sm-dropdown-menu
.sm-text-field
margin-bottom 0px
min-height 30px
border-bottom solid 1px #e4e4e4
.sm-text-field-content
padding 0
padding-left 4px
input
cursor pointer
.sm-dropdown-menu-icon
top 6px
right 0
bottom 0
.sm-icon
color #58666e
......@@ -64,12 +79,14 @@ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fo
.sm-menu-item.state-selected
color #ff4081
.sm-text-field
width 100%
.sm-text-field-input
color #58666e
.sm-form-item
margin-top 10px
overflow hidden
.label
color rgba(0,0,0,0.54)
......
import quantile from './quantile';
export {
quantile
};
export default function (x) {
return x === null ? NaN : +x;
}
import number from './number';
export default function (values, p, valueof) {
if (valueof == null) {
return valueof = number;
}
let n = values.length;
if (!(n)) {
return;
}
if ((p = +p) <= 0 || n < 2) {
return +valueof(values[0], 0, values);
}
if (p >= 1) {
return +valueof(values[n - 1], n - 1, values);
}
let i = (n - 1) * p;
let i0 = Math.floor(i);
let value0 = +valueof(values[i0], i0, values);
let value1 = +valueof(values[i0 + 1], i0 + 1, values);
return value0 + (value1 - value0) * (i - i0);
}
<template>
<div class="visual-dl-scalar-container">
<div class="visual-dl-scalar-left">
<div class="visual-dl-scalar-config-container">
<ui-config
runsItems="{{runsItems}}"
config="{=config=}"
></ui-config>
</div>
</div>
<div class="visual-dl-scalar-right">
<ui-chart-page
config="{{config}}"
runsItems="{{runsItems}}"
tagList="{{filteredTagsList}}"
title="Tags matching {{config.groupNameReg}}"
></ui-chart-page>
<ui-chart-page
san-for="item in groupedTags"
config="{{config}}"
runsItems="{{runsItems}}"
tagList="{{item.tags}}"
title="{{item.group}}"
></ui-chart-page>
</div>
</div>
</template>
<script>
import {getPluginImagesTags, getRuns} from '../service';
import config from './ui/config';
import chartPage from './ui/chartPage';
import {debounce, flatten, uniq} from 'lodash';
export default {
components: {
'ui-config': config,
'ui-chart-page': chartPage
},
computed: {
runsItems() {
let runsArray = this.data.get('runsArray') || [];
return runsArray.map(item => {
return {
name: item,
value: item
};
});
},
tagsList() {
let tags = this.data.get('tags');
let runs = Object.keys(tags);
let tagsArray = runs.map(run => Object.keys(tags[run]));
let allUniqTags = uniq(flatten(tagsArray));
// get the data for every chart
return allUniqTags.map(tag => {
let tagList = runs.map(run => {
return {
run,
tag: tags[run][tag]
};
});
return {
tagList,
tag,
group: tag.split('/')[0]
};
});
},
groupedTags() {
let tagsList = this.data.get('tagsList') || [];
// put data in group
let groupData = {};
tagsList.forEach(item => {
let group = item.group;
if (groupData[group] === undefined) {
groupData[group] = [];
groupData[group].push(item);
}
else {
groupData[group].push(item);
}
});
// to array
let groups = Object.keys(groupData);
return groups.map(group => {
return {
group,
tags: groupData[group]
};
});
}
},
initData() {
return {
runsArray: [],
tags: [],
config: {
groupNameReg: '.*',
smoothing: '0.5',
horizontal: '1',
sortingMethod: '2',
link: [],
chart: []
}
};
},
inited() {
getPluginImagesTags().then(({errno, data}) => {
this.data.set('tags', data);
// filter when inited
let groupNameReg = this.data.get('config.groupNameReg');
this.filterTagsList(groupNameReg);
});
getRuns().then(({errno, data}) => {
this.data.set('runsArray', data);
});
// need debounce, can't use computed
this.watch('config.groupNameReg', debounce(this.filterTagsList, 300));
},
filterTagsList(groupNameReg) {
let tagsList = this.data.get('tagsList') || [];
let regExp = new RegExp(groupNameReg);
let filtedTagsList = tagsList.filter(item => regExp.test(item.tag));
this.data.set('filteredTagsList', filtedTagsList);
}
};
</script>
<style lang="stylus">
@import '../style/variables';
+prefix-classes('visual-dl-scalar-')
.container
padding-left 300px
position relative
.left
width 280px
min-height 300px
border solid 1px #e4e4e4
position absolute
left 0
.right
width 100%
border solid 1px #e4e4e4
min-height 300px
padding 20px
</style>
import {router} from 'san-router';
import Images from './Images';
router.add({
target: '#content',
rule: '/images',
Component: Images
});
<template>
<div class="visual-dl-chart-page">
<ui-expand-panel title="{{title}}">
<div san-for="tag in filteredTagList" class="visual-dl-chart-box">
<ui-image
san-for="tag in tag.tagList"
tagInfo="{{tag}}"
config="{{config}}"
runsItems="{{runsItems}}"
></ui-image>
</div>
<ui-pagination
san-if="total > pageSize"
on-pageChange="handlePageChange($event)"
current="{{currentPage}}"
pageSize="{{pageSize}}"
total="{{total}}"
showSizeChanger="{{false}}"
/>
</ui-expand-panel>
</div>
</template>
<script>
import ExpandPanel from '../../common/ui/ExpandPanel';
import image from '../../common/ui/Charts/image';
import Pagination from 'san-mui/Pagination';
export default {
components: {
'ui-image': image,
'ui-expand-panel': ExpandPanel,
'ui-pagination': Pagination
},
computed: {
filteredTagList() {
let tagList = this.data.get('tagList') || [];
let currentPage = this.data.get('currentPage');
let pageSize = this.data.get('pageSize');
return tagList.slice((currentPage - 1) * pageSize, currentPage * pageSize);
},
total() {
let tagList = this.data.get('tagList') || [];
return tagList.length;
}
},
initData() {
return {
// current page
currentPage: 1,
// item per page
pageSize: 8
};
},
handlePageChange({pageNum}) {
this.data.set('currentPage', pageNum);
}
};
</script>
<style lang="stylus">
@import '../../style/variables';
+prefix-classes('visual-dl-')
.chart-page
.chart-box:after
content: "";
clear: both;
display: block;
</style>
<template>
<div class="visual-dl-scalar-config-com">
<san-text-field
hintText="input a tag group name to search"
label="Group name RegExp"
inputValue="{=config.groupNameReg=}"
/>
<ui-checkbox-group
value="{=config.imageSize=}"
items="{{imageSizeItems}}"
/>
<ui-checkbox-group
value="{=config.run=}"
label="Runs"
items="{{runsItems}}"
/>
<san-button class="visual-dl-scalar-run-toggle" variants="raised secondery">Toggle All Runs</san-button>
</div>
</template>
<script>
import TextField from 'san-mui/TextField';
import Slider from '../../common/ui/Slider';
import RadioGroup from '../../common/ui/RadioGroup';
import DropDownMenu from '../../common/ui/DropDownMenu';
import CheckBoxGroup from '../../common/ui/CheckBoxGroup';
import Button from 'san-mui/Button';
export default {
components: {
'san-text-field': TextField,
'ui-slider': Slider,
'ui-radio-group': RadioGroup,
'ui-dropdown-menu': DropDownMenu,
'ui-checkbox-group': CheckBoxGroup,
'san-button': Button
},
initData() {
return {
config: {
groupName: 'aa',
imageSize: '0.5',
run: ''
},
runsItems: [],
imageSizeItems: [
{
value: '1',
name: 'Show actual image size'
}
]
};
}
};
</script>
<style lang="stylus">
@import '../../style/variables';
+prefix-classes('visual-dl-scalar-')
.config-com
width 90%
margin 0 auto
.run-toggle
width 100%
margin-top 20px
</style>
......@@ -4,6 +4,7 @@ import './common/ui/ui-common.styl';
import './home/index';
import './scalars/index';
import './images/index';
import App from './App';
new App({
......
......@@ -10,14 +10,14 @@
</div>
<div class="visual-dl-scalar-right">
<ui-chart-page
config="{{config}}"
config="{{filteredConfig}}"
runsItems="{{runsItems}}"
tagList="{{filteredTagsList}}"
title="Tags matching {{config.groupNameReg}}"
></ui-chart-page>
<ui-chart-page
san-for="item in groupedTags"
config="{{config}}"
config="{{filteredConfig}}"
runsItems="{{runsItems}}"
tagList="{{item.tags}}"
title="{{item.group}}"
......@@ -30,7 +30,7 @@
import {getPluginScalarsTags, getRuns} from '../service';
import config from './ui/config';
import chartPage from './ui/chartPage';
import {debounce, flatten, uniq} from 'lodash';
import {debounce, flatten, uniq, isArray} from 'lodash';
export default {
components: {
'ui-config': config,
......@@ -91,6 +91,21 @@ export default {
tags: groupData[group]
};
});
},
filteredConfig() {
let tansformArr = ['downloadLink', 'outlier'];
let config = this.data.get('config') || {};
let filteredConfig = {};
Object.keys(config).forEach(key => {
let val = config[key];
if (tansformArr.indexOf(key) > -1) {
filteredConfig[key] = isArray(val) && val[0] === 'yes';
}
else {
filteredConfig[key] = val;
}
});
return filteredConfig;
}
},
initData() {
......@@ -99,11 +114,13 @@ export default {
tags: [],
config: {
groupNameReg: '.*',
smoothing: '0.5',
horizontal: '1',
smoothing: 0.6,
horizontal: 'step',
sortingMethod: '2',
link: [],
chart: []
downloadLink: [],
outlier: [],
runs: [],
running: true
}
};
},
......@@ -117,6 +134,7 @@ export default {
});
getRuns().then(({errno, data}) => {
this.data.set('runsArray', data);
this.data.set('config.runs', data);
});
// need debounce, can't use computed
......
<template>
<div class="visaul-dl-chart-page">
<div class="visual-dl-chart-page">
<ui-expand-panel title="{{title}}">
<div class="visaul-dl-chart-box">
<div class="visual-dl-chart-box">
<ui-chart
san-for="tag in filteredTagList"
tagInfo="{{tag}}"
config="{{config}}"
groupNameReg="{{config.groupNameReg}}"
smoothing="{{config.smoothing}}"
horizontal="{{config.horizontal}}"
sortingMethod="{{config.sortingMethod}}"
downloadLink="{{config.downloadLink}}"
outlier="{{config.outlier}}"
runs="{{config.runs}}"
running="{{config.running}}"
runsItems="{{runsItems}}"
></ui-chart>
</div>
......@@ -61,8 +68,8 @@ export default {
@import '../../style/variables';
+prefix-classes('visual-dl-')
.visaul-dl-chart-page
.visaul-dl-chart-box:after
.chart-page
.chart-box:after
content: "";
clear: both;
display: block;
......
......@@ -9,12 +9,12 @@
label="Smoothing"
value="{=config.smoothing=}"
min="{{0}}"
max="{{1}}"
max="{{0.999}}"
step="{{0.001}}"
/>
<ui-radio-group
label="Horizontal"
value="{{config.horizontal}}"
value="{=config.horizontal=}"
items="{{horizontalItems}}"
/>
<ui-dropdown-menu
......@@ -23,18 +23,25 @@
value="{=config.sortingMethod=}"
/>
<ui-checkbox-group
value="{=config.link=}"
value="{=config.downloadLink=}"
items="{{lnksItems}}"
/>
<ui-checkbox-group
value="{=config.chart=}"
value="{=config.outlier=}"
items="{{chartItems}}"
/>
<ui-checkbox-group
value="{=config.runs=}"
label="Runs"
items="{{runsItems}}"
/>
<san-button class="visual-dl-scalar-run-toggle" variants="raised secondery">Toggle All Runs</san-button>
<san-button
class="visual-dl-scalar-run-toggle"
variants="raised {{config.running ? 'secondery' : 'primary'}}"
on-click="toggleAllRuns"
>
{{config.running ? 'Running' : 'Stopped'}}
</san-button>
</div>
</template>
<script>
......@@ -56,59 +63,65 @@ export default {
initData() {
return {
config: {
groupName: 'aa',
smoothing: '0.5',
horizontal: '1',
groupNameReg: '.*',
smoothing: '0.6',
horizontal: 'step',
sortingMethod: '2',
link: [],
chart: []
downloadLink: [],
outlier: [],
running: true
},
horizontalItems: [
{
name: 'Step',
value: '1'
value: 'step'
},
{
name: 'Relative',
value: '2'
value: 'relative'
},
{
name: 'Wall',
value: '3'
value: 'wall'
}
],
sortingMethodItems: [
{
name: 'default',
value: '1'
value: 'default'
},
{
name: 'descending',
value: '2'
value: 'desc'
},
{
name: 'ascending',
value: '3'
value: 'asc'
},
{
name: 'nearest',
value: '4'
value: 'nearest'
}
],
runsItems: [],
lnksItems: [
{
value: '1',
value: 'yes',
name: 'Show data download links'
}
],
chartItems: [
{
value: '1',
value: 'yes',
name: 'Ignore outliers in chart scaling'
}
]
};
},
toggleAllRuns() {
let running = this.data.get('config.running');
this.data.set('config.running', !running);
this.fire('runningChange', running);
}
};
</script>
......
......@@ -5,3 +5,7 @@ export const getPluginScalarsTags = makeService('/data/plugin/scalars/tags');
export const getRuns = makeService('/data/runs');
export const getPluginScalarsScalars = makeService('/data/plugin/scalars/scalars');
export const getPluginImagesTags = makeService('/data/plugin/images/tags');
export const getPluginImagesImages = makeService('/data/plugin/images/images');
'use strict';
function noopReplace (val) { return val; }
function HtmlReplacePlugin(options) {
......
'use strict';
const webpack = require('webpack');
const rm = require('rimraf');
const ora = require('ora');
......
'use strict';
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
......
'use strict';
process.env.NODE_ENV = 'dev';
let devPort = 8999;
let opn = require('opn');
......
'use strict';
const path = require('path');
const projectPath = path.resolve(__dirname, '..');
const HtmlWebpackPlugin = require('html-webpack-plugin');
......
'use strict';
const webpack = require('webpack');
const path = require('path');
const projectPath = path.resolve(__dirname, '..');
......
'use strict';
const webpack = require('webpack');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
let merge = require('webpack-merge');
......
'use strict';
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册