未验证 提交 6dfa1d42 编写于 作者: J Jeff Wang 提交者: GitHub

Eslint precommit (#368)

* Create pre-commit with ESLint

* add vue-eslint-parser

* Add prop-name-casing and script-indent to the eslint rule.

* Update Javascript and Vue files with the proper style

* Comment out ESlint pre-commit first to udpate styles
上级 d64553b6
#!/bin/bash
# if there are no staged changes, we can exit immediately
# this is fast and prevents issues when popping a stash we didn't create
STAGED_CHANGES=`git diff-index --cached HEAD --name-only --diff-filter ACMR`
if [ -z "$STAGED_CHANGES" ]; then
exit 0
fi
# Capture the path to the eslint
cd frontend
ESLINT_EXECUTABLE=$(npm bin)/eslint
cd ..
# Test against with both .js and .vue files
git diff --cached --name-only --diff-filter ACMR | egrep '.(js|vue)$' | xargs $ESLINT_EXECUTABLE --fix
RESULT=$?
[ $RESULT -ne 0 ] && exit 1
exit 0
...@@ -33,3 +33,11 @@ ...@@ -33,3 +33,11 @@
entry: flake8 entry: flake8
language: system language: system
files: \.(py)$ files: \.(py)$
#- repo: local
# hooks:
# - id: eslint-format-checker
# name: eslint-format-checker
# description: Format files with ESLint.
# entry: bash ./.eslint_format.hook
# language: system
...@@ -9,5 +9,7 @@ module.exports = { ...@@ -9,5 +9,7 @@ module.exports = {
// override/add rules settings here, such as: // override/add rules settings here, such as:
'vue/no-unused-vars': 'warn', 'vue/no-unused-vars': 'warn',
'max-len': ["warn", 120], 'max-len': ["warn", 120],
"vue/prop-name-casing": ["error"],
'vue/script-indent': 'error',
} }
} }
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
"stylus": "^0.54.5", "stylus": "^0.54.5",
"stylus-loader": "^3.0.1", "stylus-loader": "^3.0.1",
"url-loader": "^0.5.8", "url-loader": "^0.5.8",
"vue-eslint-parser": "^2.0.3",
"vue-jest": "^1.0.2", "vue-jest": "^1.0.2",
"vue-loader": "^13.3.0", "vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1", "vue-style-loader": "^3.0.1",
......
...@@ -11,22 +11,22 @@ ...@@ -11,22 +11,22 @@
import AppMenu from './common/component/AppMenu'; import AppMenu from './common/component/AppMenu';
export default { export default {
name: 'App', name: 'App',
components: { components: {
AppMenu, AppMenu,
}, },
data() { data() {
return { return {
initialRoute: 'scalars', initialRoute: 'scalars',
}; };
}, },
created() { created() {
if (location.hash && location.hash != '#/') { if (location.hash && location.hash != '#/') {
this.initialRoute = /(\#\/)(\w*)([?|&]{0,1})/.exec(location.hash)[2]; this.initialRoute = /(\#\/)(\w*)([?|&]{0,1})/.exec(location.hash)[2];
} else { } else {
location.hash = '#/scalars'; location.hash = '#/scalars';
} }
}, },
}; };
</script> </script>
......
...@@ -38,127 +38,127 @@ import Config from './ui/Config'; ...@@ -38,127 +38,127 @@ import Config from './ui/Config';
import AudioPanelContainer from './ui/AudioPanelContainer'; import AudioPanelContainer from './ui/AudioPanelContainer';
export default { export default {
name: 'Images', name: 'Images',
components: { components: {
'ui-config': Config, 'ui-config': Config,
'ui-audio-container': AudioPanelContainer, 'ui-audio-container': AudioPanelContainer,
}, },
data() { data() {
return {
runsArray: [],
tags: [],
config: {
groupNameReg: '.*',
isActualImageSize: false,
runs: [],
running: true,
},
filteredTagsList: [],
};
},
computed: {
runsItems() {
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return { return {
runsArray: [], name: item,
tags: [], value: item,
config: {
groupNameReg: '.*',
isActualImageSize: false,
runs: [],
running: true,
},
filteredTagsList: [],
}; };
});
}, },
computed: { tagsList() {
runsItems() { let tags = this.tags;
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return {
name: item,
value: item,
};
});
},
tagsList() {
let tags = this.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 let runs = Object.keys(tags);
return allUniqTags.map((tag) => { let tagsArray = runs.map((run) => Object.keys(tags[run]));
let tagList = runs.map((run) => { let allUniqTags = uniq(flatten(tagsArray));
return {
run,
tag: tags[run][tag],
};
}).filter((item) => item.tag !== undefined);
return {
tagList,
tag,
group: tag.split('/')[0],
};
});
},
groupedTags() {
let tagsList = this.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 // get the data for every chart
let groups = Object.keys(groupData); return allUniqTags.map((tag) => {
return groups.map((group) => { let tagList = runs.map((run) => {
return { return {
group, run,
tags: groupData[group], tag: tags[run][tag],
}; };
}); }).filter((item) => item.tag !== undefined);
}, return {
filteredConfig() { tagList,
let config = this.config || {}; tag,
let filteredConfig = {}; group: tag.split('/')[0],
Object.keys(config).forEach((key) => { };
let val = config[key]; });
filteredConfig[key] = val;
});
return filteredConfig;
},
}, },
created() { groupedTags() {
getPluginAudioTags().then(({errno, data}) => { let tagsList = this.tagsList || [];
this.tags = data; // put data in group
let groupData = {};
// filter when inited tagsList.forEach((item) => {
let groupNameReg = this.config.groupNameReg; let group = item.group;
this.filterTagsList(groupNameReg); if (groupData[group] === undefined) {
}); groupData[group] = [];
groupData[group].push(item);
} else {
groupData[group].push(item);
}
});
getRuns().then(({errno, data}) => { // to array
this.runsArray = data; let groups = Object.keys(groupData);
this.config.runs = data; return groups.map((group) => {
}); return {
group,
tags: groupData[group],
};
});
}, },
mounted() { filteredConfig() {
autoAdjustHeight(); let config = this.config || {};
let filteredConfig = {};
Object.keys(config).forEach((key) => {
let val = config[key];
filteredConfig[key] = val;
});
return filteredConfig;
}, },
watch: { },
'config.groupNameReg': function(val) { created() {
this.throttledFilterTagsList(); getPluginAudioTags().then(({errno, data}) => {
}, this.tags = data;
// filter when inited
let groupNameReg = this.config.groupNameReg;
this.filterTagsList(groupNameReg);
});
getRuns().then(({errno, data}) => {
this.runsArray = data;
this.config.runs = data;
});
},
mounted() {
autoAdjustHeight();
},
watch: {
'config.groupNameReg': function(val) {
this.throttledFilterTagsList();
}, },
methods: { },
filterTagsList(groupNameReg) { methods: {
if (!groupNameReg) { filterTagsList(groupNameReg) {
this.filteredTagsList = []; if (!groupNameReg) {
return; this.filteredTagsList = [];
} return;
let tagsList = this.tagsList || []; }
let regExp = new RegExp(groupNameReg); let tagsList = this.tagsList || [];
this.filteredTagsList = tagsList.filter((item) => regExp.test(item.tag)); let regExp = new RegExp(groupNameReg);
}, this.filteredTagsList = tagsList.filter((item) => regExp.test(item.tag));
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
}, },
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
},
}; };
</script> </script>
......
...@@ -3,7 +3,7 @@ import {router} from 'san-router'; ...@@ -3,7 +3,7 @@ import {router} from 'san-router';
import Audio from './Audio'; import Audio from './Audio';
router.add({ router.add({
target: '#content', target: '#content',
rule: '/audio', rule: '/audio',
Component: Audio, Component: Audio,
}); });
...@@ -31,99 +31,99 @@ import {getPluginAudioAudio} from '../../service'; ...@@ -31,99 +31,99 @@ import {getPluginAudioAudio} from '../../service';
const intervalTime = 30; const intervalTime = 30;
export default { export default {
props: ['tagInfo', 'runs', 'running', 'runsItems'], props: ['tagInfo', 'runs', 'running', 'runsItems'],
computed: { computed: {
steps() { steps() {
let data = this.data || []; let data = this.data || [];
return data.length - 1; return data.length - 1;
},
}, },
filters: { },
formatTime: function(value) { filters: {
if (!value) { formatTime: function(value) {
return; if (!value) {
} return;
// The value was made in seconds, must convert it to milliseconds }
let time = new Date(value * 1000); // The value was made in seconds, must convert it to milliseconds
let options = { let time = new Date(value * 1000);
weekday: 'short', year: 'numeric', month: 'short', let options = {
day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', weekday: 'short', year: 'numeric', month: 'short',
}; day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit',
return time.toLocaleDateString('en-US', options); };
}, return time.toLocaleDateString('en-US', options);
}, },
data() { },
return { data() {
currentIndex: 0, return {
slider: { currentIndex: 0,
value: '0', slider: {
label: '', value: '0',
min: 0, label: '',
step: 1, min: 0,
}, step: 1,
audioData: {}, },
data: [], audioData: {},
data: [],
};
},
created() {
this.getOriginAudioData();
},
mounted() {
if (this.running) {
this.startInterval();
}
},
beforeDestroy() {
this.stopInterval();
},
watch: {
running: function(val) {
val ? this.startInterval() : this.stopInterval();
},
currentIndex: function(index) {
if (this.data && this.data[index]) {
let currentAudioInfo = this.data ? this.data[index] : {};
let {query, step, wall_time} = currentAudioInfo;
let url = '/data/plugin/audio/individualAudio?ts=' + wall_time;
let audioSrc = [url, query].join('&');
this.audioData = {
audioSrc,
step,
wall_time,
}; };
}
}, },
created() { },
methods: {
stopInterval() {
clearInterval(this.getOringDataInterval);
},
// get origin data per {{intervalTime}} seconds
startInterval() {
this.getOringDataInterval = setInterval(() => {
this.getOriginAudioData(); this.getOriginAudioData();
}, intervalTime * 1000);
}, },
mounted() { getOriginAudioData() {
if (this.running) { // let {run, tag} = this.tagInfo;
this.startInterval(); let run = this.tagInfo.run;
let tag = this.tagInfo.tag;
let {displayName, samples} = tag;
let params = {
run,
tag: displayName,
samples,
};
getPluginAudioAudio(params).then(({status, data}) => {
if (status === 0) {
this.data = data;
this.currentIndex = data.length - 1;
} }
});
}, },
},
beforeDestroy() {
this.stopInterval();
},
watch: {
running: function(val) {
val ? this.startInterval() : this.stopInterval();
},
currentIndex: function(index) {
if (this.data && this.data[index]) {
let currentAudioInfo = this.data ? this.data[index] : {};
let {query, step, wall_time} = currentAudioInfo;
let url = '/data/plugin/audio/individualAudio?ts=' + wall_time;
let audioSrc = [url, query].join('&');
this.audioData = {
audioSrc,
step,
wall_time,
};
}
},
},
methods: {
stopInterval() {
clearInterval(this.getOringDataInterval);
},
// get origin data per {{intervalTime}} seconds
startInterval() {
this.getOringDataInterval = setInterval(() => {
this.getOriginAudioData();
}, intervalTime * 1000);
},
getOriginAudioData() {
// let {run, tag} = this.tagInfo;
let run = this.tagInfo.run;
let tag = this.tagInfo.tag;
let {displayName, samples} = tag;
let params = {
run,
tag: displayName,
samples,
};
getPluginAudioAudio(params).then(({status, data}) => {
if (status === 0) {
this.data = data;
this.currentIndex = data.length - 1;
}
});
},
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -29,41 +29,41 @@ import AudioPanel from './AudioPanel'; ...@@ -29,41 +29,41 @@ import AudioPanel from './AudioPanel';
import {cloneDeep, flatten} from 'lodash'; import {cloneDeep, flatten} from 'lodash';
export default { export default {
props: ['config', 'runsItems', 'tagList', 'title'], props: ['config', 'runsItems', 'tagList', 'title'],
components: { components: {
'ui-audio': AudioPanel, 'ui-audio': AudioPanel,
'ui-expand-panel': ExpandPanel, 'ui-expand-panel': ExpandPanel,
},
computed: {
filteredRunsList() {
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return flatten(list.slice().map((item) => {
return item.tagList.filter((one) => runs.includes(one.run));
}));
}, },
computed: {
filteredRunsList() {
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return flatten(list.slice().map((item) => {
return item.tagList.filter((one) => runs.includes(one.run));
}));
},
filteredPageList() { filteredPageList() {
let list = this.filteredRunsList || []; let list = this.filteredRunsList || [];
return list.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize); return list.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize);
},
total() {
let list = this.filteredRunsList || [];
return list.length;
},
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
}, },
data() { total() {
return { let list = this.filteredRunsList || [];
// current page return list.length;
currentPage: 1,
// item per page
pageSize: 8,
};
}, },
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
},
data() {
return {
// current page
currentPage: 1,
// item per page
pageSize: 8,
};
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -32,19 +32,19 @@ ...@@ -32,19 +32,19 @@
<script> <script>
export default { export default {
props: { props: {
runsItems: Array, runsItems: Array,
config: Object, config: Object,
}, },
data() { data() {
return { return {
}; };
}, },
methods: { methods: {
toggleAllRuns() { toggleAllRuns() {
this.config.running = !this.config.running; this.config.running = !this.config.running;
},
}, },
},
}; };
</script> </script>
......
...@@ -22,58 +22,58 @@ ...@@ -22,58 +22,58 @@
<script> <script>
export default { export default {
props: ['initialRoute'], props: ['initialRoute'],
name: 'AppMenu', name: 'AppMenu',
data() { data() {
return { return {
selected: this.initialRoute, selected: this.initialRoute,
items: [ items: [
{ {
url: '/scalars', url: '/scalars',
title: 'SCALARS', title: 'SCALARS',
name: 'scalars', name: 'scalars',
}, },
{ {
url: '/images', url: '/images',
title: 'IMAGES', title: 'IMAGES',
name: 'images', name: 'images',
}, },
{ {
url: '/audio', url: '/audio',
title: 'AUDIO', title: 'AUDIO',
name: 'audio', name: 'audio',
}, },
{ {
url: '/histograms', url: '/histograms',
title: 'HISTOGRAMS', title: 'HISTOGRAMS',
name: 'histograms', name: 'histograms',
}, },
{ {
url: '/graphs', url: '/graphs',
title: 'GRAPHS', title: 'GRAPHS',
name: 'graphs', name: 'graphs',
}, },
{ {
url: '/texts', url: '/texts',
title: 'TEXTS', title: 'TEXTS',
name: 'texts', name: 'texts',
}, },
/* // Hide the top menu /* // Hide the top menu
{ {
url: '/HighDimensional', url: '/HighDimensional',
title: 'HighDimensional', title: 'HighDimensional',
name: 'HighDimensional' name: 'HighDimensional'
} }
*/ */
], ],
}; };
}, },
methods: { methods: {
handleItemClick: function(item) { handleItemClick: function(item) {
this.selected = item.name; this.selected = item.name;
this.$router.push(item.url); this.$router.push(item.url);
},
}, },
},
}; };
</script> </script>
......
...@@ -22,20 +22,20 @@ ...@@ -22,20 +22,20 @@
</template> </template>
<script> <script>
export default { export default {
props: { props: {
title: String, title: String,
info: Number, info: Number,
}, },
computed: { computed: {
iconName() { iconName() {
return this.isShow ? 'expand_less' : 'expand_more'; return this.isShow ? 'expand_less' : 'expand_more';
},
},
data() {
return {
isShow: true,
};
}, },
},
data() {
return {
isShow: true,
};
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -11,10 +11,10 @@ const instances = []; ...@@ -11,10 +11,10 @@ const instances = [];
* @return {number} final top dist * @return {number} final top dist
*/ */
function calcTopDist(topDist = 16) { function calcTopDist(topDist = 16) {
for (let i = 0, len = instances.length; i < len; i++) { for (let i = 0, len = instances.length; i < len; i++) {
topDist += (instances[i].vm.el.offsetHeight + 16); topDist += (instances[i].vm.el.offsetHeight + 16);
} }
return topDist; return topDist;
} }
/** /**
...@@ -24,28 +24,28 @@ function calcTopDist(topDist = 16) { ...@@ -24,28 +24,28 @@ function calcTopDist(topDist = 16) {
* @return {NotificationItem} instance * @return {NotificationItem} instance
*/ */
const notification = function(options = {}) { const notification = function(options = {}) {
options.top = calcTopDist(options.offset); options.top = calcTopDist(options.offset);
const {onClose, onClick} = options; const {onClose, onClick} = options;
delete options.onClick; delete options.onClick;
delete options.onClose; delete options.onClose;
delete options.offset; delete options.offset;
const instance = { const instance = {
vm: new NotificationItem({ vm: new NotificationItem({
data: options, data: options,
}), }),
id: `notification_${seed++}`, id: `notification_${seed++}`,
}; };
if (typeof onClick === 'function') { if (typeof onClick === 'function') {
instance.vm.on('itemClick', onClick); instance.vm.on('itemClick', onClick);
} }
instance.vm.on('close', () => { instance.vm.on('close', () => {
notification.close(instance.id, onClose); notification.close(instance.id, onClose);
}); });
instance.vm.attach(document.body); instance.vm.attach(document.body);
instances.push(instance); instances.push(instance);
return instance.vm; return instance.vm;
}; };
/** /**
...@@ -55,44 +55,44 @@ const notification = function(options = {}) { ...@@ -55,44 +55,44 @@ const notification = function(options = {}) {
* @param {Function} onClose cusmtom func * @param {Function} onClose cusmtom func
*/ */
notification.close = function(id, onClose) { notification.close = function(id, onClose) {
let index; let index;
let removedHeight; let removedHeight;
let len = instances.length; let len = instances.length;
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
if (id === instances[i].id) { if (id === instances[i].id) {
if (typeof onClose === 'function') { if (typeof onClose === 'function') {
onClose(instances[i]); onClose(instances[i]);
} }
index = i; index = i;
removedHeight = instances[i].vm.el.offsetHeight; removedHeight = instances[i].vm.el.offsetHeight;
// distroy instance // distroy instance
instances[i].vm.dispose(); instances[i].vm.dispose();
instances[i] = null; instances[i] = null;
// reomve instance fron instances // reomve instance fron instances
instances.splice(i, 1); instances.splice(i, 1);
break; break;
}
} }
// change the left notification's height }
if (len > 1) { // change the left notification's height
for (let i = index; i < len - 1; i++) { if (len > 1) {
instances[i].vm.el.style.top = `${parseInt(instances[i].vm.el.style.top, 10) - removedHeight - 16}px`; for (let i = index; i < len - 1; i++) {
} instances[i].vm.el.style.top = `${parseInt(instances[i].vm.el.style.top, 10) - removedHeight - 16}px`;
} }
}
}; };
// fout type func // fout type func
['success', 'warning', 'info', 'error'].forEach((type) => { ['success', 'warning', 'info', 'error'].forEach((type) => {
notification[type] = (options) => { notification[type] = (options) => {
if (typeof options === 'string') { if (typeof options === 'string') {
options = { options = {
message: options, message: options,
}; };
} }
options = options || {}; options = options || {};
options.type = type; options.type = type;
return notification(options); return notification(options);
}; };
}); });
export default notification; export default notification;
export default function autoAdjustHeight() { export default function autoAdjustHeight() {
// compute the container height // compute the container height
let containerHeight = window.innerHeight - 64;
let containerLeftEl = document.getElementsByClassName('visual-dl-page-left')[0];
let containerRightEl = document.getElementsByClassName('visual-dl-page-right')[0];
containerLeftEl.style.height = containerHeight + 'px';
containerRightEl.style.height = containerHeight + 'px';
window.addEventListener('resize', () => {
let containerHeight = window.innerHeight - 64; let containerHeight = window.innerHeight - 64;
let containerLeftEl = document.getElementsByClassName('visual-dl-page-left')[0];
let containerRightEl = document.getElementsByClassName('visual-dl-page-right')[0];
containerLeftEl.style.height = containerHeight + 'px'; containerLeftEl.style.height = containerHeight + 'px';
containerRightEl.style.height = containerHeight + 'px'; containerRightEl.style.height = containerHeight + 'px';
window.addEventListener('resize', () => { });
let containerHeight = window.innerHeight - 64;
containerLeftEl.style.height = containerHeight + 'px';
containerRightEl.style.height = containerHeight + 'px';
});
} }
...@@ -3,19 +3,19 @@ import FileSaver from 'file-saver'; ...@@ -3,19 +3,19 @@ import FileSaver from 'file-saver';
const aoaToSheet = XLSX.utils.aoa_to_sheet; const aoaToSheet = XLSX.utils.aoa_to_sheet;
const saveAs = FileSaver.saveAs; const saveAs = FileSaver.saveAs;
function s2ab(s) { function s2ab(s) {
if (typeof ArrayBuffer !== 'undefined') { if (typeof ArrayBuffer !== 'undefined') {
let buf = new ArrayBuffer(s.length); let buf = new ArrayBuffer(s.length);
let view = new Uint8Array(buf); let view = new Uint8Array(buf);
for (let i = 0; i !== s.length; ++i) {
view[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
}
let buf = new Array(s.length);
for (let i = 0; i !== s.length; ++i) { for (let i = 0; i !== s.length; ++i) {
buf[i] = s.charCodeAt(i) & 0xFF; view[i] = s.charCodeAt(i) & 0xFF;
} }
return buf; return buf;
}
let buf = new Array(s.length);
for (let i = 0; i !== s.length; ++i) {
buf[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
} }
/** /**
...@@ -26,28 +26,28 @@ function s2ab(s) { ...@@ -26,28 +26,28 @@ function s2ab(s) {
* @param {string} name filename * @param {string} name filename
*/ */
export const generateXLSXandAutoDownload = function(data, name) { export const generateXLSXandAutoDownload = function(data, name) {
let wopts = { let wopts = {
bookType: 'xlsx', bookType: 'xlsx',
bookSST: false, bookSST: false,
type: 'binary', type: 'binary',
}; };
let ws = aoaToSheet(data); let ws = aoaToSheet(data);
let wb = { let wb = {
SheetNames: ['Export'], SheetNames: ['Export'],
Sheets: {}, Sheets: {},
Props: {}, Props: {},
}; };
wb.Sheets.Export = ws; wb.Sheets.Export = ws;
let wbout = XLSX.write(wb, wopts); let wbout = XLSX.write(wb, wopts);
saveAs( saveAs(
new Blob( new Blob(
[s2ab(wbout)], [s2ab(wbout)],
{ {
type: 'application/octet-stream', type: 'application/octet-stream',
} }
), ),
name + '.xlsx' || 'sheetjs.xlsx' name + '.xlsx' || 'sheetjs.xlsx'
); );
}; };
/** /**
...@@ -58,13 +58,13 @@ export const generateXLSXandAutoDownload = function(data, name) { ...@@ -58,13 +58,13 @@ export const generateXLSXandAutoDownload = function(data, name) {
* @param {string} name filename * @param {string} name filename
*/ */
export const generateJsonAndDownload = function(data, name) { export const generateJsonAndDownload = function(data, name) {
saveAs( saveAs(
new Blob( new Blob(
[s2ab(JSON.stringify(data, null, ' '))], [s2ab(JSON.stringify(data, null, ' '))],
{ {
type: 'application/octet-stream', type: 'application/octet-stream',
} }
), ),
name + '.json' || 'json.json' name + '.json' || 'json.json'
); );
}; };
...@@ -6,35 +6,35 @@ const STATUS = 'status'; ...@@ -6,35 +6,35 @@ const STATUS = 'status';
const STATUSINFO = 'msg'; const STATUSINFO = 'msg';
const instance = axios.create({ const instance = axios.create({
baseURL: '/', baseURL: '/',
timeout: 30000, timeout: 30000,
}); });
// for better ux, don't send the error msg because there will be too mutch error // for better ux, don't send the error msg because there will be too mutch error
const responseErrorStatus = (response) => { const responseErrorStatus = (response) => {
const data = response.data; const data = response.data;
// if (data[STATUS] !== 0) { // if (data[STATUS] !== 0) {
// Notification.error(data[STATUSINFO]); // Notification.error(data[STATUSINFO]);
// return Promise.reject(data); // return Promise.reject(data);
// } // }
return data; return data;
}; };
// for better ux, don't send the error msg because there will be too mutch error // for better ux, don't send the error msg because there will be too mutch error
const responseNetError = (error) => { const responseNetError = (error) => {
// Notification.error('net error'); // Notification.error('net error');
return Promise.reject(error); return Promise.reject(error);
}; };
// post from // post from
const formInstance = axios.create({ const formInstance = axios.create({
baseURL: '/', baseURL: '/',
timeout: 3000, timeout: 3000,
transformRequest: [(data) => qs.stringify(data)], transformRequest: [(data) => qs.stringify(data)],
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json,application/vnd.ms-excel', 'Accept': 'application/json,application/vnd.ms-excel',
}, },
}); });
...@@ -43,10 +43,10 @@ formInstance.interceptors.response.use(responseErrorStatus, responseNetError); ...@@ -43,10 +43,10 @@ formInstance.interceptors.response.use(responseErrorStatus, responseNetError);
instance.interceptors.response.use(responseErrorStatus, responseNetError); instance.interceptors.response.use(responseErrorStatus, responseNetError);
export const makeService = (url, opt = {method: 'get'}) => (params = {}) => { export const makeService = (url, opt = {method: 'get'}) => (params = {}) => {
if (opt.method === 'delete' || opt.method === 'get') { if (opt.method === 'delete' || opt.method === 'get') {
params = {params}; params = {params};
} }
return instance[opt.method](url, params); return instance[opt.method](url, params);
}; };
export const makeFormService = (url, method = 'post') => (params = {}) => formInstance[method](url, params); export const makeFormService = (url, method = 'post') => (params = {}) => formInstance[method](url, params);
import quantile from './quantile'; import quantile from './quantile';
export { export {
quantile, quantile,
}; };
export default function(x) { export default function(x) {
return x === null ? NaN : +x; return x === null ? NaN : +x;
} }
import number from './number'; import number from './number';
export default function(values, p, valueof) { export default function(values, p, valueof) {
if (valueof == null) { if (valueof == null) {
return valueof = number; return valueof = number;
} }
let n = values.length; let n = values.length;
if (!(n)) { if (!(n)) {
return; return;
} }
if ((p = +p) <= 0 || n < 2) { if ((p = +p) <= 0 || n < 2) {
return +valueof(values[0], 0, values); return +valueof(values[0], 0, values);
} }
if (p >= 1) { if (p >= 1) {
return +valueof(values[n - 1], n - 1, values); return +valueof(values[n - 1], n - 1, values);
} }
let i = (n - 1) * p; let i = (n - 1) * p;
let i0 = Math.floor(i); let i0 = Math.floor(i);
let value0 = +valueof(values[i0], i0, values); let value0 = +valueof(values[i0], i0, values);
let value1 = +valueof(values[i0 + 1], i0 + 1, values); let value1 = +valueof(values[i0 + 1], i0 + 1, values);
return value0 + (value1 - value0) * (i - i0); return value0 + (value1 - value0) * (i - i0);
} }
import {router} from 'san-router'; import {router} from 'san-router';
export function routeTo(url, params = {}) { export function routeTo(url, params = {}) {
let paramsArr = Object.keys(params).map((key) => `${key}=${params[key]}`); let paramsArr = Object.keys(params).map((key) => `${key}=${params[key]}`);
let urlParams = (url.indexOf('?') > -1 ? '&' : '?') + paramsArr.join('&'); let urlParams = (url.indexOf('?') > -1 ? '&' : '?') + paramsArr.join('&');
router.locator.redirect(urlParams.length > 1 ? `${url}${urlParams}` : url); router.locator.redirect(urlParams.length > 1 ? `${url}${urlParams}` : url);
} }
...@@ -29,33 +29,33 @@ import Chart from './ui/Chart'; ...@@ -29,33 +29,33 @@ import Chart from './ui/Chart';
export default { export default {
components: { components: {
'ui-config': Config, 'ui-config': Config,
'ui-chart': Chart, 'ui-chart': Chart,
},
name: 'Graph',
data() {
return {
config: {
scale: 0.5,
},
fitScreen: {fitScreen: false},
download: {download: false},
curNode: {},
};
},
mounted() {
autoAdjustHeight();
},
methods: {
handleFitScreen() {
this.fitScreen = {fitScreen: true};
this.config.scale = 0.5;
}, },
name: 'Graph', handleDownload() {
data() { this.download = {fitScreen: true};
return {
config: {
scale: 0.5,
},
fitScreen: {fitScreen: false},
download: {download: false},
curNode: {},
};
},
mounted() {
autoAdjustHeight();
},
methods: {
handleFitScreen() {
this.fitScreen = {fitScreen: true};
this.config.scale = 0.5;
},
handleDownload() {
this.download = {fitScreen: true};
},
}, },
},
}; };
</script> </script>
......
...@@ -3,7 +3,7 @@ import {router} from 'san-router'; ...@@ -3,7 +3,7 @@ import {router} from 'san-router';
import Graph from './Graph'; import Graph from './Graph';
router.add({ router.add({
target: '#content', target: '#content',
rule: '/graphs', rule: '/graphs',
Component: Graph, Component: Graph,
}); });
...@@ -7,271 +7,271 @@ ...@@ -7,271 +7,271 @@
</template> </template>
<script> <script>
// libs // libs
import echarts from 'echarts'; import echarts from 'echarts';
import { import {
dragMovelHandler, dragMovelHandler,
tansformElement, tansformElement,
relativeMove, relativeMove,
} from './dragHelper'; } from './dragHelper';
// service // service
import {getPluginGraphsGraph} from '../../service'; import {getPluginGraphsGraph} from '../../service';
// https://github.com/taye/interact.js // https://github.com/taye/interact.js
import interact from 'interactjs'; import interact from 'interactjs';
// for d3 drawing // for d3 drawing
import * as d3 from 'd3'; import * as d3 from 'd3';
export default { export default {
props: { props: {
'fitScreen': { 'fitScreen': {
type: Function, type: Function,
required: true, required: true,
}, },
'download': { 'download': {
type: Function, type: Function,
required: true, required: true,
}, },
'scale': { 'scale': {
type: Number, type: Number,
default: 1, default: 1,
}, },
'curNode': { 'curNode': {
type: Object, type: Object,
default: {}, default: {},
}}, }},
computed: { computed: {
computedWidth() { computedWidth() {
let scale = this.scale; let scale = this.scale;
return Math.floor(scale * 2 * 700); return Math.floor(scale * 2 * 700);
}, },
}, },
data() { data() {
return { return {
myCY: null, myCY: null,
graphUrl: '', graphUrl: '',
}; };
}, },
watch: { watch: {
fitScreen: function(val) { fitScreen: function(val) {
this.clearDragedTransform(this.getBigImgEl()); this.clearDragedTransform(this.getBigImgEl());
this.clearDragedTransform(this.getSmallImgDragHandler()); this.clearDragedTransform(this.getSmallImgDragHandler());
}, },
download: function(val) { download: function(val) {
if (this.myCY) { if (this.myCY) {
let aEl = document.createElement('a'); let aEl = document.createElement('a');
aEl.href = this.myCY.png(); aEl.href = this.myCY.png();
aEl.download = 'graph.png'; aEl.download = 'graph.png';
aEl.click(); aEl.click();
} }
}, },
}, },
mounted() { mounted() {
this.getOriginChartsData(); this.getOriginChartsData();
let chartScope = this; let chartScope = this;
getPluginGraphsGraph().then(({errno, data}) => { getPluginGraphsGraph().then(({errno, data}) => {
let graphData = data.data; let graphData = data.data;
// d3 svg drawing // d3 svg drawing
let g = new dagreD3.graphlib.Graph() let g = new dagreD3.graphlib.Graph()
.setGraph({}) .setGraph({})
.setDefaultEdgeLabel(function() { .setDefaultEdgeLabel(function() {
return {}; return {};
}); });
// eslint-disable-next-line // eslint-disable-next-line
let render = new dagreD3.render(); let render = new dagreD3.render();
let nodeKeys = []; let nodeKeys = [];
let buildInputNodeLabel = function(inputNode) { let buildInputNodeLabel = function(inputNode) {
// TODO(daming-lu): need more complex compound node // TODO(daming-lu): need more complex compound node
let nodeLabel = 'id: ' + inputNode['name'] + '\n' let nodeLabel = 'id: ' + inputNode['name'] + '\n'
+ 'type: ' + inputNode['data_type'] + '\n' + 'type: ' + inputNode['data_type'] + '\n'
+ 'dims: ' + inputNode['shape'].join(' x '); + 'dims: ' + inputNode['shape'].join(' x ');
return nodeLabel; return nodeLabel;
}; };
// add input nodes // add input nodes
for (let i=0; i<graphData['input'].length; ++i) { for (let i=0; i<graphData['input'].length; ++i) {
let curInputNode = graphData['input'][i]; let curInputNode = graphData['input'][i];
let nodeKey = curInputNode['name']; let nodeKey = curInputNode['name'];
g.setNode( g.setNode(
nodeKey, nodeKey,
{ {
label: buildInputNodeLabel(curInputNode), label: buildInputNodeLabel(curInputNode),
class: 'input', class: 'input',
style: 'stroke: #A3D39C; stroke-width: 3px; ' + style: 'stroke: #A3D39C; stroke-width: 3px; ' +
'stroke-dasharray: 5, 5;', 'stroke-dasharray: 5, 5;',
labelStyle: 'font-size: 0.8em;', labelStyle: 'font-size: 0.8em;',
} }
); );
nodeKeys.push(nodeKey); nodeKeys.push(nodeKey);
} }
// add operator nodes then add edges from inputs to operator and from operator to output // add operator nodes then add edges from inputs to operator and from operator to output
for (let i=0; i<graphData['node'].length; ++i) { for (let i=0; i<graphData['node'].length; ++i) {
let curOperatorNode = graphData['node'][i]; let curOperatorNode = graphData['node'][i];
let nodeKey = 'opNode_' + i; let nodeKey = 'opNode_' + i;
// add operator node // add operator node
let curOpLabel = curOperatorNode['opType']; let curOpLabel = curOperatorNode['opType'];
g.setNode( g.setNode(
nodeKey, nodeKey,
{ {
label: curOpLabel + ' '.repeat(Math.floor(curOpLabel.length/5)), label: curOpLabel + ' '.repeat(Math.floor(curOpLabel.length/5)),
class: 'operator', class: 'operator',
style: 'opacity: 0.5;', style: 'opacity: 0.5;',
} }
); );
nodeKeys.push(nodeKey); nodeKeys.push(nodeKey);
// add output node // add output node
let outputNodeKey = curOperatorNode['output'][0]; let outputNodeKey = curOperatorNode['output'][0];
let outputPadding = ' '.repeat(Math.floor(outputNodeKey.length/2)); let outputPadding = ' '.repeat(Math.floor(outputNodeKey.length/2));
g.setNode( g.setNode(
outputNodeKey, outputNodeKey,
{ {
label: outputNodeKey + outputPadding, label: outputNodeKey + outputPadding,
class: 'output', class: 'output',
style: 'opacity: 0.5;' + style: 'opacity: 0.5;' +
'stroke-width: 2px; ' + 'stroke-width: 2px; ' +
'stroke-dasharray: 5, 5;', 'stroke-dasharray: 5, 5;',
shape: 'diamond', shape: 'diamond',
} }
); );
nodeKeys.push(outputNodeKey); nodeKeys.push(outputNodeKey);
// add edges from inputs to node and from node to output // add edges from inputs to node and from node to output
for (let e=0; e<curOperatorNode['input'].length; ++e) { for (let e=0; e<curOperatorNode['input'].length; ++e) {
g.setEdge(curOperatorNode['input'][e], nodeKey); g.setEdge(curOperatorNode['input'][e], nodeKey);
} }
g.setEdge(nodeKey, curOperatorNode['output'][0]); g.setEdge(nodeKey, curOperatorNode['output'][0]);
} }
// TODO(daming-lu): add prettier styles to diff nodes // TODO(daming-lu): add prettier styles to diff nodes
let svg = d3.select('svg') let svg = d3.select('svg')
.attr('font-family', 'sans-serif') .attr('font-family', 'sans-serif')
.attr('font-size', '28px'); .attr('font-size', '28px');
render(d3.select('svg g'), g); render(d3.select('svg g'), g);
// adjust viewBox so that the whole graph can be shown, with scroll bar // adjust viewBox so that the whole graph can be shown, with scroll bar
svg.attr('viewBox', '0 0 ' + g.graph().width + ' ' + g.graph().height); svg.attr('viewBox', '0 0 ' + g.graph().width + ' ' + g.graph().height);
svg.selectAll('.node').on('click', function(d, i) { svg.selectAll('.node').on('click', function(d, i) {
chartScope.curNode = g.node(d); chartScope.curNode = g.node(d);
let nodeType = chartScope.curNode.class; let nodeType = chartScope.curNode.class;
let nodeInfo = null; let nodeInfo = null;
if (nodeType === 'operator') { if (nodeType === 'operator') {
let opIndex = d.slice(7); // remove prefix "opNode_" let opIndex = d.slice(7); // remove prefix "opNode_"
nodeInfo = graphData.node[opIndex]; nodeInfo = graphData.node[opIndex];
} else if (nodeType === 'input') { } else if (nodeType === 'input') {
nodeInfo = graphData.input[d-1]; nodeInfo = graphData.input[d-1];
} else { } else {
nodeInfo = 'output'; nodeInfo = 'output';
} }
chartScope.$emit('curNodeUpdated', chartScope.$emit('curNodeUpdated',
{ {
'nodeType': nodeType, 'nodeType': nodeType,
'nodeInfo': nodeInfo, 'nodeInfo': nodeInfo,
}); });
}); });
}); });
},
methods: {
createChart() {
let el = this.el.getElementsByClassName('visual-dl-chart-box')[0];
this.myChart = echarts.init(el);
},
initChartOption(data) {
this.setChartOptions(data);
},
setChartOptions(data) {
this.myChart.setOption(data);
},
getOriginChartsData() {
getPluginGraphsGraph().then(({status, data}) => {
if (status === 0 && data.url) {
this.graphUrl = data.url;
this.addDragEventForImg();
}
});
},
clearDragedTransform(dragImgEl) {
dragImgEl.style.transform = 'none';
dragImgEl.setAttribute('data-x', 0);
dragImgEl.setAttribute('data-y', 0);
},
getBigImgEl() {
return this.$refs.draggable;
},
getSmallImgEl() {
return this.$refs.small_img;
},
getSmallImgDragHandler() {
return this.$refs.screen_handler;
},
addDragEventForImg() {
let chartScope = this;
// target elements with the "draggable" class
interact('.draggable').draggable({
// enable inertial throwing
inertia: true,
autoScroll: true,
// call this function on every dragmove event
onmove(event) {
dragMovelHandler(event, (target, x, y) => {
tansformElement(target, x, y);
// compute the proportional value
let triggerEl = chartScope.getBigImgEl();
let relativeEl = chartScope.getSmallImgDragHandler();
relativeMove({triggerEl, x, y}, relativeEl);
});
}, },
});
methods: {
createChart() { interact('.screen-handler').draggable({
let el = this.el.getElementsByClassName('visual-dl-chart-box')[0]; // enable inertial throwing
this.myChart = echarts.init(el); inertia: true,
}, autoScroll: true,
restrict: {
initChartOption(data) { restriction: 'parent',
this.setChartOptions(data); endOnly: false,
}, elementRect: {
setChartOptions(data) { top: 0,
this.myChart.setOption(data); left: 0,
}, bottom: 1,
right: 1,
getOriginChartsData() { },
getPluginGraphsGraph().then(({status, data}) => {
if (status === 0 && data.url) {
this.graphUrl = data.url;
this.addDragEventForImg();
}
});
},
clearDragedTransform(dragImgEl) {
dragImgEl.style.transform = 'none';
dragImgEl.setAttribute('data-x', 0);
dragImgEl.setAttribute('data-y', 0);
},
getBigImgEl() {
return this.$refs.draggable;
},
getSmallImgEl() {
return this.$refs.small_img;
},
getSmallImgDragHandler() {
return this.$refs.screen_handler;
},
addDragEventForImg() {
let chartScope = this;
// target elements with the "draggable" class
interact('.draggable').draggable({
// enable inertial throwing
inertia: true,
autoScroll: true,
// call this function on every dragmove event
onmove(event) {
dragMovelHandler(event, (target, x, y) => {
tansformElement(target, x, y);
// compute the proportional value
let triggerEl = chartScope.getBigImgEl();
let relativeEl = chartScope.getSmallImgDragHandler();
relativeMove({triggerEl, x, y}, relativeEl);
});
},
});
interact('.screen-handler').draggable({
// enable inertial throwing
inertia: true,
autoScroll: true,
restrict: {
restriction: 'parent',
endOnly: false,
elementRect: {
top: 0,
left: 0,
bottom: 1,
right: 1,
},
},
// call this function on every dragmove event
onmove(event) {
dragMovelHandler(event, (target, x, y) => {
tansformElement(target, x, y);
// compute the proportional value
let triggerEl = chartScope.getSmallImgEl();
let relativeEl = chartScope.getBigImgEl();
relativeMove({triggerEl, x, y}, relativeEl);
});
},
});
},
}, },
}; // call this function on every dragmove event
onmove(event) {
dragMovelHandler(event, (target, x, y) => {
tansformElement(target, x, y);
// compute the proportional value
let triggerEl = chartScope.getSmallImgEl();
let relativeEl = chartScope.getBigImgEl();
relativeMove({triggerEl, x, y}, relativeEl);
});
},
});
},
},
};
</script> </script>
<style lang="stylus"> <style lang="stylus">
.node rect .node rect
......
...@@ -54,15 +54,15 @@ ...@@ -54,15 +54,15 @@
<script> <script>
export default { export default {
props: ['config', 'curNode'], props: ['config', 'curNode'],
methods: { methods: {
handleFitScreen() { handleFitScreen() {
this.$emit('fitScreen'); this.$emit('fitScreen');
},
handleDownload() {
this.$emit('download');
},
}, },
handleDownload() {
this.$emit('download');
},
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
export const dragMovelHandler = (event, callback) => { export const dragMovelHandler = (event, callback) => {
let target = event.target; let target = event.target;
// keep the dragged position in the data-x/data-y attributes // keep the dragged position in the data-x/data-y attributes
let x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx; let x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
let y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy; let y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
callback(target, x, y); callback(target, x, y);
}; };
export const tansformElement = (target, x, y) => { export const tansformElement = (target, x, y) => {
// translate the element // translate the element
target.style.webkitTransform = target.style.transform target.style.webkitTransform = target.style.transform
= 'translate(' + x + 'px, ' + y + 'px)'; = 'translate(' + x + 'px, ' + y + 'px)';
// update the posiion attributes // update the posiion attributes
target.setAttribute('data-x', x); target.setAttribute('data-x', x);
target.setAttribute('data-y', y); target.setAttribute('data-y', y);
}; };
export const relativeMove = ({triggerEl, x, y}, relativeEl) => { export const relativeMove = ({triggerEl, x, y}, relativeEl) => {
let {offsetWidth: tWidth, offsetHeight: tHeight} = triggerEl; let {offsetWidth: tWidth, offsetHeight: tHeight} = triggerEl;
let {offsetWidth: rWidth, offsetHeight: rHeight} = relativeEl; let {offsetWidth: rWidth, offsetHeight: rHeight} = relativeEl;
let rX = (x / tWidth) * rWidth; let rX = (x / tWidth) * rWidth;
let rY = (y / tHeight) * rHeight; let rY = (y / tHeight) * rHeight;
tansformElement(relativeEl, -rX, -rY); tansformElement(relativeEl, -rX, -rY);
}; };
...@@ -26,56 +26,56 @@ import Config from './ui/Config'; ...@@ -26,56 +26,56 @@ import Config from './ui/Config';
import Chart from './ui/Chart'; import Chart from './ui/Chart';
export default { export default {
components: { components: {
'ui-config': Config, 'ui-config': Config,
'ui-chart': Chart, 'ui-chart': Chart,
},
name: 'HighDimensional',
data() {
return {
config: {
searchText: '',
displayWordLabel: true,
dimension: '2',
reduction: 'tsne',
running: true,
},
embedding_data: [],
};
},
created() {
this.fetchDatasets();
},
watch: {
'config.dimension': function(val) {
this.fetchDatasets();
}, },
name: 'HighDimensional', 'config.reduction': function(val) {
data() { this.fetchDatasets();
return {
config: {
searchText: '',
displayWordLabel: true,
dimension: '2',
reduction: 'tsne',
running: true,
},
embedding_data: [],
};
}, },
created() { },
this.fetchDatasets(); mounted() {
}, autoAdjustHeight();
watch: { },
'config.dimension': function(val) { methods: {
this.fetchDatasets(); fetchDatasets() {
}, // Fetch the data from the server. Passing dimension and reduction method
'config.reduction': function(val) { let params = {
this.fetchDatasets(); dimension: this.config.dimension,
}, reduction: this.config.reduction,
}, };
mounted() { getHighDimensionalDatasets(params).then(({errno, data}) => {
autoAdjustHeight(); let vector_data = data.embedding;
}, let labels = data.labels;
methods: {
fetchDatasets() {
// Fetch the data from the server. Passing dimension and reduction method
let params = {
dimension: this.config.dimension,
reduction: this.config.reduction,
};
getHighDimensionalDatasets(params).then(({errno, data}) => {
let vector_data = data.embedding;
let labels = data.labels;
for ( let i = 0; i < vector_data.length; i ++) { for ( let i = 0; i < vector_data.length; i ++) {
vector_data[i].push(labels[i]); vector_data[i].push(labels[i]);
} }
this.embedding_data = vector_data; this.embedding_data = vector_data;
}); });
},
}, },
},
}; };
</script> </script>
......
...@@ -14,179 +14,179 @@ import echarts from 'echarts'; ...@@ -14,179 +14,179 @@ import echarts from 'echarts';
import 'echarts-gl'; import 'echarts-gl';
export default { export default {
props: ['config', 'displayWordLabel', 'searchText', 'embedding_data', 'dimension'], props: ['config', 'displayWordLabel', 'searchText', 'embedding_data', 'dimension'],
data() { data() {
return { return {
width: 900, width: 900,
height: 600, height: 600,
}; };
},
computed: {
computedStyle() {
return 'height:' + this.height + 'px;' +
'width:' + this.width + 'px;';
}, },
computed: { },
computedStyle() { created() {},
return 'height:' + this.height + 'px;' + mounted() {
'width:' + this.width + 'px;'; this.createChart();
}, this.myChart.showLoading();
},
created() {},
mounted() {
this.createChart();
this.myChart.showLoading();
this.set2DChartOptions();
this.setDisplayWordLabel();
},
watch: {
embedding_data: function(val) {
this.myChart.hideLoading();
this.myChart.setOption({
series: [{
// Grab the 'matched' series data
name: 'all',
data: val,
}],
});
},
displayWordLabel: function(val) {
this.setDisplayWordLabel();
},
dimension: function(val) {
this.myChart.clear();
this.myChart.showLoading();
if (val === '2') {
this.set2DChartOptions(); this.set2DChartOptions();
this.setDisplayWordLabel(); this.setDisplayWordLabel();
} else {
this.set3DChartOptions();
this.setDisplayWordLabel();
}
}, },
watch: { searchText: function(val) {
embedding_data: function(val) { // Filter the data that has the hasPrefix
this.myChart.hideLoading(); let matched_words = [];
this.myChart.setOption({ if (val != '') {
series: [{ val = val.toLowerCase();
// Grab the 'matched' series data
name: 'all',
data: val,
}],
});
},
displayWordLabel: function(val) {
this.setDisplayWordLabel();
},
dimension: function(val) {
this.myChart.clear();
this.myChart.showLoading();
if (val === '2') {
this.set2DChartOptions();
this.setDisplayWordLabel();
} else {
this.set3DChartOptions();
this.setDisplayWordLabel();
}
},
searchText: function(val) {
// Filter the data that has the hasPrefix
let matched_words = [];
if (val != '') {
val = val.toLowerCase();
function hasPrefix(value) { function hasPrefix(value) {
let word = value[value.length - 1]; let word = value[value.length - 1];
return (typeof word == 'string' && word.toLowerCase().startsWith(val)); return (typeof word == 'string' && word.toLowerCase().startsWith(val));
} }
matched_words = this.embedding_data.filter(hasPrefix); matched_words = this.embedding_data.filter(hasPrefix);
} }
// Update the matched series data // Update the matched series data
this.myChart.setOption({ this.myChart.setOption({
series: [{ series: [{
// Grab the 'matched' series data // Grab the 'matched' series data
name: 'matched', name: 'matched',
data: matched_words, data: matched_words,
}], }],
}); });
},
}, },
methods: { },
createChart() { methods: {
// Initialize the eChartBox createChart() {
let el = this.$refs.chartBox; // Initialize the eChartBox
this.myChart = echarts.init(el); let el = this.$refs.chartBox;
}, this.myChart = echarts.init(el);
set2DChartOptions() { },
let typeD = 'normal'; set2DChartOptions() {
let option = { let typeD = 'normal';
xAxis: {}, let option = {
yAxis: {}, xAxis: {},
series: [{ yAxis: {},
name: 'all', series: [{
symbolSize: 10, name: 'all',
data: this.embedding_data, symbolSize: 10,
type: 'scatter', data: this.embedding_data,
}, type: 'scatter',
{ },
name: 'matched', {
animation: false, name: 'matched',
symbolSize: 10, animation: false,
data: [], symbolSize: 10,
itemStyle: { data: [],
normal: { itemStyle: {
opacity: 1, normal: {
}, opacity: 1,
}, },
label: { },
normal: { label: {
show: true, normal: {
formatter: function(param) { show: true,
return param.data[param.data.length - 1]; formatter: function(param) {
}, return param.data[param.data.length - 1];
position: 'top', },
}, position: 'top',
}, },
type: 'scatter', },
}, type: 'scatter',
], },
}; ],
this.myChart.setOption(option); };
}, this.myChart.setOption(option);
set3DChartOptions() { },
let symbolSize = 2.5; set3DChartOptions() {
let option3d = { let symbolSize = 2.5;
grid3D: {}, let option3d = {
xAxis3D: { grid3D: {},
type: 'category', xAxis3D: {
}, type: 'category',
yAxis3D: {},
xAxis3D: {},
zAxis3D: {},
dataset: {
source: this.embedding_data,
},
series: [
{
name: 'all',
type: 'scatter3D',
symbolSize: symbolSize,
data: [],
},
{
name: 'matched',
animation: false,
symbolSize: symbolSize,
data: [],
label: {
normal: {
show: true,
formatter: function(param) {
return param.data[param.data.length - 1];
},
position: 'top',
},
},
type: 'scatter3D',
},
],
};
this.myChart.setOption(option3d);
}, },
setDisplayWordLabel() { yAxis3D: {},
this.myChart.setOption({ xAxis3D: {},
series: [{ zAxis3D: {},
// Grab the 'all' series data dataset: {
name: 'all', source: this.embedding_data,
label: {
normal: {
show: this.displayWordLabel,
formatter: function(param) {
return param.data[param.data.length - 1];
},
position: 'top',
},
emphasis: {
show: true,
},
},
}],
});
}, },
series: [
{
name: 'all',
type: 'scatter3D',
symbolSize: symbolSize,
data: [],
},
{
name: 'matched',
animation: false,
symbolSize: symbolSize,
data: [],
label: {
normal: {
show: true,
formatter: function(param) {
return param.data[param.data.length - 1];
},
position: 'top',
},
},
type: 'scatter3D',
},
],
};
this.myChart.setOption(option3d);
},
setDisplayWordLabel() {
this.myChart.setOption({
series: [{
// Grab the 'all' series data
name: 'all',
label: {
normal: {
show: this.displayWordLabel,
formatter: function(param) {
return param.data[param.data.length - 1];
},
position: 'top',
},
emphasis: {
show: true,
},
},
}],
});
}, },
},
}; };
</script> </script>
......
...@@ -54,19 +54,19 @@ ...@@ -54,19 +54,19 @@
<script> <script>
export default { export default {
props: { props: {
runsItems: Array, runsItems: Array,
config: Object, config: Object,
}, },
data() { data() {
return { return {
}; };
}, },
methods: { methods: {
toggleAllRuns() { toggleAllRuns() {
this.config.running = !this.config.running; this.config.running = !this.config.running;
},
}, },
},
}; };
</script> </script>
......
...@@ -35,118 +35,118 @@ import {debounce, flatten, uniq} from 'lodash'; ...@@ -35,118 +35,118 @@ import {debounce, flatten, uniq} from 'lodash';
import autoAdjustHeight from '../common/util/autoAdjustHeight'; import autoAdjustHeight from '../common/util/autoAdjustHeight';
export default { export default {
components: { components: {
'ui-config': Config, 'ui-config': Config,
'ui-chart-page': ChartPage, 'ui-chart-page': ChartPage,
},
computed: {
runsItems() {
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return {
name: item,
value: item,
};
});
}, },
computed: { tagsList() {
runsItems() { let tags = this.tags;
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return {
name: item,
value: item,
};
});
},
tagsList() {
let tags = this.tags;
let runs = Object.keys(tags); let runs = Object.keys(tags);
let tagsArray = runs.map((run) => Object.keys(tags[run])); let tagsArray = runs.map((run) => Object.keys(tags[run]));
let allUniqTags = uniq(flatten(tagsArray)); let allUniqTags = uniq(flatten(tagsArray));
// get the data for every chart // get the data for every chart
return allUniqTags.map((tag) => { return allUniqTags.map((tag) => {
let tagList = runs.map((run) => { let tagList = runs.map((run) => {
return { return {
run, run,
tag: tags[run][tag], tag: tags[run][tag],
}; };
}).filter((item) => item.tag !== undefined); }).filter((item) => item.tag !== undefined);
return {
tagList,
tag,
group: tag.split('/')[0],
};
});
},
groupedTags() {
let tagsList = this.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],
};
});
},
},
data() {
return { return {
runsArray: [], tagList,
tags: [], tag,
config: { group: tag.split('/')[0],
groupNameReg: '.*',
horizontal: 'step',
chartType: 'offset',
runs: [],
running: true,
},
filteredTagsList: [],
}; };
});
}, },
created() { groupedTags() {
getPluginHistogramsTags().then(({errno, data}) => { let tagsList = this.tagsList || [];
this.tags = data; // put data in group
// filter when inited let groupData = {};
let groupNameReg = this.config.groupNameReg; tagsList.forEach((item) => {
this.filterTagsList(groupNameReg); let group = item.group;
}); if (groupData[group] === undefined) {
getRuns().then(({errno, data}) => { groupData[group] = [];
this.runsArray = data; groupData[group].push(item);
this.config.runs = data; } else {
}); groupData[group].push(item);
}, }
});
mounted() { // to array
autoAdjustHeight(); let groups = Object.keys(groupData);
return groups.map((group) => {
return {
group,
tags: groupData[group],
};
});
}, },
watch: { },
'config.groupNameReg': function(val) { data() {
this.throttledFilterTagsList(); return {
}, runsArray: [],
tags: [],
config: {
groupNameReg: '.*',
horizontal: 'step',
chartType: 'offset',
runs: [],
running: true,
},
filteredTagsList: [],
};
},
created() {
getPluginHistogramsTags().then(({errno, data}) => {
this.tags = data;
// filter when inited
let groupNameReg = this.config.groupNameReg;
this.filterTagsList(groupNameReg);
});
getRuns().then(({errno, data}) => {
this.runsArray = data;
this.config.runs = data;
});
},
mounted() {
autoAdjustHeight();
},
watch: {
'config.groupNameReg': function(val) {
this.throttledFilterTagsList();
}, },
methods: { },
filterTagsList(groupNameReg) { methods: {
if (!groupNameReg) { filterTagsList(groupNameReg) {
this.filteredTagsList = []; if (!groupNameReg) {
return; this.filteredTagsList = [];
} return;
let tagsList = this.tagsList || []; }
let regExp = new RegExp(groupNameReg); let tagsList = this.tagsList || [];
let filtedTagsList = tagsList.filter((item) => regExp.test(item.tag)); let regExp = new RegExp(groupNameReg);
this.filteredTagsList = filtedTagsList; let filtedTagsList = tagsList.filter((item) => regExp.test(item.tag));
}, this.filteredTagsList = filtedTagsList;
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
}, },
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
},
}; };
</script> </script>
......
import {min, max, range} from 'lodash'; import {min, max, range} from 'lodash';
export function tansformBackendData(histogramData) { export function tansformBackendData(histogramData) {
let [time, step, items] = histogramData; let [time, step, items] = histogramData;
return { return {
time, time,
step, step,
min: min(items.map(([left, right, count]) => left)), min: min(items.map(([left, right, count]) => left)),
max: max(items.map(([left, right, count]) => right)), max: max(items.map(([left, right, count]) => right)),
items: items.map(([left, right, count]) => ({left, right, count})), items: items.map(([left, right, count]) => ({left, right, count})),
}; };
} }
export function computeNewHistogram(histogram, min, max, binsNum = 30) { export function computeNewHistogram(histogram, min, max, binsNum = 30) {
if (max === min) { if (max === min) {
// Create bins even if all the data has a single value. // Create bins even if all the data has a single value.
max = min * 1.1 + 1; max = min * 1.1 + 1;
min = min / 1.1 - 1; min = min / 1.1 - 1;
}
let stepWidth = (max - min) / binsNum;
let itemIndex = 0;
return range(min, max, stepWidth).map((binLeft) => {
let binRight = binLeft + stepWidth;
let yValue = 0;
while (itemIndex < histogram.items.length) {
let itemRight = Math.min(max, histogram.items[itemIndex].right);
let itemLeft = Math.max(min, histogram.items[itemIndex].left);
let overlap = Math.min(itemRight, binRight) - Math.max(itemLeft, binLeft);
let count = (overlap / (itemRight - itemLeft)) * histogram.items[itemIndex].count;
yValue += overlap > 0 ? count : 0;
// If `itemRight` is bigger than `binRight`, then this bin is
// finished and there also has data for the next bin, so don't increment
// `itemIndex`.
if (itemRight > binRight) {
break;
}
itemIndex++;
} }
let stepWidth = (max - min) / binsNum; return {x: binLeft, dx: stepWidth, y: yValue};
let itemIndex = 0; });
return range(min, max, stepWidth).map((binLeft) => {
let binRight = binLeft + stepWidth;
let yValue = 0;
while (itemIndex < histogram.items.length) {
let itemRight = Math.min(max, histogram.items[itemIndex].right);
let itemLeft = Math.max(min, histogram.items[itemIndex].left);
let overlap = Math.min(itemRight, binRight) - Math.max(itemLeft, binLeft);
let count = (overlap / (itemRight - itemLeft)) * histogram.items[itemIndex].count;
yValue += overlap > 0 ? count : 0;
// If `itemRight` is bigger than `binRight`, then this bin is
// finished and there also has data for the next bin, so don't increment
// `itemIndex`.
if (itemRight > binRight) {
break;
}
itemIndex++;
}
return {x: binLeft, dx: stepWidth, y: yValue};
});
} }
export function tansformToVisData(tempData, time, step) { export function tansformToVisData(tempData, time, step) {
return tempData.map(function(dataItem) { return tempData.map(function(dataItem) {
return [time, step, dataItem.x + dataItem.dx / 2, Math.floor(dataItem.y)]; return [time, step, dataItem.x + dataItem.dx / 2, Math.floor(dataItem.y)];
}); });
} }
export function originDataToChartData(originData) { export function originDataToChartData(originData) {
let tempData = originData.map(tansformBackendData); let tempData = originData.map(tansformBackendData);
let globalMin = min(tempData.map(({min}) => min)); let globalMin = min(tempData.map(({min}) => min));
let globalMax = max(tempData.map(({max}) => max)); let globalMax = max(tempData.map(({max}) => max));
let chartData = tempData.map(function(item) { let chartData = tempData.map(function(item) {
let histoBins = computeNewHistogram(item, globalMin, globalMax); let histoBins = computeNewHistogram(item, globalMin, globalMax);
let {time, step} = item; let {time, step} = item;
return {
time,
step,
items: tansformToVisData(histoBins, time, step),
};
});
return { return {
min: globalMin, time,
max: globalMax, step,
chartData, items: tansformToVisData(histoBins, time, step),
}; };
});
return {
min: globalMin,
max: globalMax,
chartData,
};
} }
...@@ -3,7 +3,7 @@ import {router} from 'san-router'; ...@@ -3,7 +3,7 @@ import {router} from 'san-router';
import Histogram from './Histogram'; import Histogram from './Histogram';
router.add({ router.add({
target: '#content', target: '#content',
rule: '/histograms', rule: '/histograms',
Component: Histogram, Component: Histogram,
}); });
...@@ -40,483 +40,483 @@ const p = Math.max(0, precisionRound(0.01, 1.01) - 1); ...@@ -40,483 +40,483 @@ const p = Math.max(0, precisionRound(0.01, 1.01) - 1);
const yValueFormat = format('.' + p + 'e'); const yValueFormat = format('.' + p + 'e');
export default { export default {
props: ['tagInfo', 'runs', 'chartType', 'running', 'runsItems'], props: ['tagInfo', 'runs', 'chartType', 'running', 'runsItems'],
data() { data() {
return { return {
originData: [], originData: [],
isExpand: false, isExpand: false,
}; };
},
watch: {
originData: function(val) {
this.initChartOption();
}, },
watch: { chartType: function(val) {
originData: function(val) { this.initChartOption();
this.initChartOption();
},
chartType: function(val) {
this.initChartOption();
},
running: function(val) {
val ? this.startInterval() : this.stopInterval();
},
isExpand: function(val) {
this.expandArea(val);
},
}, },
mounted() { running: function(val) {
let tagInfo = this.tagInfo; val ? this.startInterval() : this.stopInterval();
this.initChart(tagInfo); },
if (this.running) { isExpand: function(val) {
this.startInterval(); this.expandArea(val);
} },
},
mounted() {
let tagInfo = this.tagInfo;
this.initChart(tagInfo);
if (this.running) {
this.startInterval();
}
},
beforeDestroy() {
this.stopInterval();
},
methods: {
initChart(tagInfo) {
this.createChart();
this.getOriginChartData(tagInfo);
}, },
beforeDestroy() { createChart() {
this.stopInterval(); let el = this.$refs.visual_dl_chart_box;
this.myChart = echarts.init(el);
}, },
methods: { initChartOption() {
initChart(tagInfo) { this.myChart.clear();
this.createChart(); let zr = this.myChart.getZr();
this.getOriginChartData(tagInfo); let hoverDots = zrDrawElement.hoverDots;
}, if (hoverDots != null && hoverDots.length !== 0) {
hoverDots.forEach((dot) => zr.remove(dot));
}
let chartType = this.chartType;
let data = this.originData;
let visData = originDataToChartData(data);
let tagInfo = this.tagInfo;
let title = tagInfo.tag.displayName + '(' + tagInfo.run + ')';
this.setChartOptions(visData, title, chartType);
},
createChart() { setChartOptions(visData, tag, chartType) {
let el = this.$refs.visual_dl_chart_box; let grid = {
this.myChart = echarts.init(el); left: 45,
top: 60,
right: 40,
bottom: 36,
};
let title = {
text: tag,
textStyle: {
fontSize: '12',
fontWeight: 'normal',
}, },
};
if (chartType === 'overlay') {
this.setOverlayChartOption(visData, title, grid);
} else if (chartType === 'offset') {
this.setOffsetChartOption(visData, title, grid);
}
},
initChartOption() { setOverlayChartOption({chartData, min, max}, title, grid) {
this.myChart.clear(); let seriesOption = chartData.map(({time, step, items}) => ({
let zr = this.myChart.getZr(); name: 'step' + step,
let hoverDots = zrDrawElement.hoverDots; type: 'line',
if (hoverDots != null && hoverDots.length !== 0) { showSymbol: false,
hoverDots.forEach((dot) => zr.remove(dot)); hoverAnimation: false,
} z: 0,
let chartType = this.chartType; data: items,
let data = this.originData; animationDuration: 100,
let visData = originDataToChartData(data); lineStyle: {
let tagInfo = this.tagInfo; normal: {
let title = tagInfo.tag.displayName + '(' + tagInfo.run + ')'; width: 1,
this.setChartOptions(visData, title, chartType); color: '#008c99',
},
}, },
encode: {
setChartOptions(visData, tag, chartType) { x: [2],
let grid = { y: [3],
left: 45,
top: 60,
right: 40,
bottom: 36,
};
let title = {
text: tag,
textStyle: {
fontSize: '12',
fontWeight: 'normal',
},
};
if (chartType === 'overlay') {
this.setOverlayChartOption(visData, title, grid);
} else if (chartType === 'offset') {
this.setOffsetChartOption(visData, title, grid);
}
}, },
})
setOverlayChartOption({chartData, min, max}, title, grid) { );
let seriesOption = chartData.map(({time, step, items}) => ({ let option = {
name: 'step' + step, title: title,
type: 'line', axisPointer: {
showSymbol: false, link: {xAxisIndex: 'all'},
hoverAnimation: false, show: true,
z: 0, snap: true,
data: items, triggerTooltip: true,
animationDuration: 100,
lineStyle: {
normal: {
width: 1,
color: '#008c99',
},
},
encode: {
x: [2],
y: [3],
},
})
);
let option = {
title: title,
axisPointer: {
link: {xAxisIndex: 'all'},
show: true,
snap: true,
triggerTooltip: true,
},
grid: grid,
xAxis: {
type: 'value',
},
yAxis: {
type: 'value',
axisLine: {
onZero: false,
},
axisLabel: {
formatter(value, index) {
return yValueFormat(value);
},
},
axisPointer: {
label: {
formatter({value}) {
return yValueFormat(value);
},
},
},
},
series: seriesOption,
};
let zr1 = this.myChart.getZr();
zr1.on('mousemove', function(e) {
zr1.remove(zrDrawElement.hoverLine);
zr1.remove(zrDrawElement.tooltip);
zr1.remove(zrDrawElement.tooltipX);
zr1.remove(zrDrawElement.tooltipY);
zrDrawElement.hoverDots.forEach((dot) => zr1.remove(dot));
zrDrawElement.hoverDots.length = 0;
});
this.myChart.setOption(option, {notMerge: true});
}, },
grid: grid,
xAxis: {
type: 'value',
},
yAxis: {
type: 'value',
axisLine: {
onZero: false,
},
axisLabel: {
formatter(value, index) {
return yValueFormat(value);
},
},
axisPointer: {
label: {
formatter({value}) {
return yValueFormat(value);
},
},
},
},
series: seriesOption,
};
let zr1 = this.myChart.getZr();
zr1.on('mousemove', function(e) {
zr1.remove(zrDrawElement.hoverLine);
zr1.remove(zrDrawElement.tooltip);
zr1.remove(zrDrawElement.tooltipX);
zr1.remove(zrDrawElement.tooltipY);
zrDrawElement.hoverDots.forEach((dot) => zr1.remove(dot));
zrDrawElement.hoverDots.length = 0;
});
this.myChart.setOption(option, {notMerge: true});
},
setOffsetChartOption({chartData, min, max}, title, grid) { setOffsetChartOption({chartData, min, max}, title, grid) {
let rawData = []; let rawData = [];
let minX = min; let minX = min;
let maxX = max; let maxX = max;
let minZ = Infinity; let minZ = Infinity;
let maxZ = -Infinity; let maxZ = -Infinity;
let ecChart = this.myChart; let ecChart = this.myChart;
let maxStep = -Infinity; let maxStep = -Infinity;
let minStep = Infinity; let minStep = Infinity;
grid.top = 126; grid.top = 126;
grid.left = 16; grid.left = 16;
grid.right = 40; grid.right = 40;
chartData.forEach(function(dataItem) { chartData.forEach(function(dataItem) {
let lineData = []; let lineData = [];
maxStep = Math.max(dataItem.step, maxStep); maxStep = Math.max(dataItem.step, maxStep);
minStep = Math.min(dataItem.step, minStep); minStep = Math.min(dataItem.step, minStep);
dataItem.items.forEach(([time, step, x, y]) => { dataItem.items.forEach(([time, step, x, y]) => {
minZ = Math.min(minZ, y); minZ = Math.min(minZ, y);
maxZ = Math.max(maxZ, y); maxZ = Math.max(maxZ, y);
lineData.push(x, step, y); lineData.push(x, step, y);
}); });
rawData.push(lineData); rawData.push(lineData);
}); });
let option = { let option = {
textStyle: { textStyle: {
fontFamily: 'Merriweather Sans', fontFamily: 'Merriweather Sans',
}, },
title, title,
color: ['#006069'], color: ['#006069'],
visualMap: { visualMap: {
type: 'continuous', type: 'continuous',
show: false, show: false,
min: minStep, min: minStep,
max: maxStep, max: maxStep,
dimension: 1, dimension: 1,
inRange: { inRange: {
colorLightness: [0.2, 0.4], colorLightness: [0.2, 0.4],
}, },
}, },
xAxis: { xAxis: {
min: minX, min: minX,
max: maxX, max: maxX,
axisLine: { axisLine: {
onZero: false, onZero: false,
}, },
axisLabel: { axisLabel: {
fontSize: '11', fontSize: '11',
formatter: function(value) { formatter: function(value) {
return Math.round(value * 100) / 100; return Math.round(value * 100) / 100;
}, },
}, },
splitLine: { splitLine: {
show: false, show: false,
}, },
}, },
yAxis: { yAxis: {
position: 'right', position: 'right',
axisLine: { axisLine: {
onZero: false, onZero: false,
}, },
inverse: true, inverse: true,
splitLine: { splitLine: {
show: false, show: false,
}, },
axisLabel: { axisLabel: {
fontSize: '11', fontSize: '11',
}, },
}, },
grid, grid,
series: [{ series: [{
type: 'custom', type: 'custom',
dimensions: ['x', 'y'], dimensions: ['x', 'y'],
renderItem: function(params, api) { renderItem: function(params, api) {
let points = makePolyPoints( let points = makePolyPoints(
params.dataIndex, params.dataIndex,
api.value, api.value,
api.coord, api.coord,
params.coordSys.y - 10 params.coordSys.y - 10
); );
return { return {
type: 'polygon', type: 'polygon',
silent: true, silent: true,
shape: { shape: {
points, points,
}, },
style: api.style({ style: api.style({
stroke: '#bbb', stroke: '#bbb',
lineWidth: 1, lineWidth: 1,
}), }),
};
},
data: rawData,
}],
}; };
},
function makePolyPoints(dataIndex, getValue, getCoord, yValueMapHeight) { data: rawData,
let points = []; }],
for (let i = 0; i < rawData[dataIndex].length;) { };
let x = getValue(i++);
let y = getValue(i++); function makePolyPoints(dataIndex, getValue, getCoord, yValueMapHeight) {
let z = getValue(i++); let points = [];
points.push(getPoint(x, y, z, getCoord, yValueMapHeight)); for (let i = 0; i < rawData[dataIndex].length;) {
} let x = getValue(i++);
return points; let y = getValue(i++);
} let z = getValue(i++);
points.push(getPoint(x, y, z, getCoord, yValueMapHeight));
function getPoint(x, y, z, getCoord, yValueMapHeight) { }
let pt = getCoord([x, y]); return points;
// linear map in z axis }
pt[1] -= (z - minZ) / (maxZ - minZ) * yValueMapHeight;
return pt; function getPoint(x, y, z, getCoord, yValueMapHeight) {
} let pt = getCoord([x, y]);
// linear map in z axis
let zr = ecChart.getZr(); pt[1] -= (z - minZ) / (maxZ - minZ) * yValueMapHeight;
return pt;
function removeTooltip() { }
if (zrDrawElement.hoverLine) {
zr.remove(zrDrawElement.hoverLine); let zr = ecChart.getZr();
zr.remove(zrDrawElement.tooltip);
zrDrawElement.hoverDots.forEach((dot) => zr.remove(dot)); function removeTooltip() {
zrDrawElement.hoverDots.length = 0; if (zrDrawElement.hoverLine) {
zr.remove(zrDrawElement.tooltipX); zr.remove(zrDrawElement.hoverLine);
zr.remove(zrDrawElement.tooltipY); zr.remove(zrDrawElement.tooltip);
} zrDrawElement.hoverDots.forEach((dot) => zr.remove(dot));
} zrDrawElement.hoverDots.length = 0;
zr.remove(zrDrawElement.tooltipX);
zr.on('mouseout', (e) => { zr.remove(zrDrawElement.tooltipY);
removeTooltip(); }
}); }
zr.on('mousemove', (e) => { zr.on('mouseout', (e) => {
removeTooltip(); removeTooltip();
let nearestIndex = findNearestValue(e.offsetX, e.offsetY); });
if (nearestIndex) {
let getCoord = function(pt) { zr.on('mousemove', (e) => {
return ecChart.convertToPixel('grid', pt); removeTooltip();
}; let nearestIndex = findNearestValue(e.offsetX, e.offsetY);
let gridRect = ecChart.getModel().getComponent('grid', 0).coordinateSystem.getRect(); if (nearestIndex) {
let getCoord = function(pt) {
let linePoints = makePolyPoints( return ecChart.convertToPixel('grid', pt);
nearestIndex.itemIndex, };
function(i) { let gridRect = ecChart.getModel().getComponent('grid', 0).coordinateSystem.getRect();
return rawData[nearestIndex.itemIndex][i];
}, let linePoints = makePolyPoints(
getCoord, nearestIndex.itemIndex,
gridRect.y - 10 function(i) {
); return rawData[nearestIndex.itemIndex][i];
},
zr.add(zrDrawElement.hoverLine = new echarts.graphic.Polyline({ getCoord,
silent: true, gridRect.y - 10
shape: { );
points: linePoints,
}, zr.add(zrDrawElement.hoverLine = new echarts.graphic.Polyline({
style: { silent: true,
stroke: '#5c5c5c', shape: {
lineWidth: 2, points: linePoints,
}, },
z: 999, style: {
})); stroke: '#5c5c5c',
lineWidth: 2,
let itemX; },
rawData.forEach((dataItem) => { z: 999,
let binIndex = nearestIndex.binIndex; }));
let x = dataItem[binIndex * 3];
let y = dataItem[binIndex * 3 + 1]; let itemX;
let z = dataItem[binIndex * 3 + 2]; rawData.forEach((dataItem) => {
let pt = getPoint(x, y, z, getCoord, gridRect.y - 10); let binIndex = nearestIndex.binIndex;
itemX = pt[0]; let x = dataItem[binIndex * 3];
let dot = new echarts.graphic.Circle({ let y = dataItem[binIndex * 3 + 1];
shape: { let z = dataItem[binIndex * 3 + 2];
cx: pt[0], let pt = getPoint(x, y, z, getCoord, gridRect.y - 10);
cy: pt[1], itemX = pt[0];
r: 3, let dot = new echarts.graphic.Circle({
}, shape: {
style: { cx: pt[0],
fill: '#000', cy: pt[1],
stroke: '#ccc', r: 3,
lineWidth: 1, },
}, style: {
z: 1000, fill: '#000',
}); stroke: '#ccc',
zr.add(dot); lineWidth: 1,
zrDrawElement.hoverDots.push(dot); },
}); z: 1000,
let hoveredItem = chartData[nearestIndex.itemIndex];
zrDrawElement.tooltip = new echarts.graphic.Text({
position: [e.offsetX + 30, e.offsetY - 50],
style: {
fontFamily: 'Merriweather Sans',
text: yValueFormat(hoveredItem.items[nearestIndex.binIndex][3]),
textFill: '#000',
fontSize: 14,
textBackgroundColor: '#eee',
textBorderColor: '#008c99',
textBorderWidth: 2,
textBorderRadius: 5,
textPadding: 10,
rich: {},
},
z: 2000,
});
zr.add(zrDrawElement.tooltip);
zrDrawElement.tooltipX = new echarts.graphic.Text({
position: [
itemX,
gridRect.y + gridRect.height,
],
style: {
fontFamily: 'Merriweather Sans',
text: Math.round(hoveredItem.items[nearestIndex.binIndex][2] * 1000) / 1000,
textFill: '#fff',
textAlign: 'center',
fontSize: 12,
textBackgroundColor: '#333',
textBorderWidth: 2,
textPadding: [5, 7],
rich: {},
},
z: 2000,
});
zr.add(zrDrawElement.tooltipX);
zrDrawElement.tooltipY = new echarts.graphic.Text({
position: [
gridRect.x + gridRect.width,
linePoints[linePoints.length - 1][1],
],
style: {
fontFamily: 'Merriweather Sans',
text: hoveredItem.step,
textFill: '#fff',
textVerticalAlign: 'middle',
fontSize: 12,
textBackgroundColor: '#333',
textBorderWidth: 2,
textPadding: [5, 7],
rich: {},
},
z: 2000,
});
zr.add(zrDrawElement.tooltipY);
}
}); });
zr.add(dot);
function findNearestValue(px, py) { zrDrawElement.hoverDots.push(dot);
let value = ecChart.convertFromPixel('grid', [px, py]); });
let itemIndex;
let nearestY = Infinity; let hoveredItem = chartData[nearestIndex.itemIndex];
let binIndex; zrDrawElement.tooltip = new echarts.graphic.Text({
chartData.forEach((item, index) => { position: [e.offsetX + 30, e.offsetY - 50],
let dist = Math.abs(value[1] - item.step); style: {
if (dist < nearestY) { fontFamily: 'Merriweather Sans',
nearestY = dist; text: yValueFormat(hoveredItem.items[nearestIndex.binIndex][3]),
itemIndex = index; textFill: '#000',
} fontSize: 14,
}); textBackgroundColor: '#eee',
if (itemIndex != null) { textBorderColor: '#008c99',
let dataItem = chartData[itemIndex]; textBorderWidth: 2,
let nearestX = Infinity; textBorderRadius: 5,
dataItem.items.forEach((item, index) => { textPadding: 10,
let dist = Math.abs(item[2] - value[0]); rich: {},
if (dist < nearestX) { },
nearestX = dist; z: 2000,
binIndex = index; });
} zr.add(zrDrawElement.tooltip);
});
if (binIndex != null) { zrDrawElement.tooltipX = new echarts.graphic.Text({
return { position: [
itemIndex: itemIndex, itemX,
binIndex: binIndex, gridRect.y + gridRect.height,
}; ],
} style: {
} fontFamily: 'Merriweather Sans',
text: Math.round(hoveredItem.items[nearestIndex.binIndex][2] * 1000) / 1000,
textFill: '#fff',
textAlign: 'center',
fontSize: 12,
textBackgroundColor: '#333',
textBorderWidth: 2,
textPadding: [5, 7],
rich: {},
},
z: 2000,
});
zr.add(zrDrawElement.tooltipX);
zrDrawElement.tooltipY = new echarts.graphic.Text({
position: [
gridRect.x + gridRect.width,
linePoints[linePoints.length - 1][1],
],
style: {
fontFamily: 'Merriweather Sans',
text: hoveredItem.step,
textFill: '#fff',
textVerticalAlign: 'middle',
fontSize: 12,
textBackgroundColor: '#333',
textBorderWidth: 2,
textPadding: [5, 7],
rich: {},
},
z: 2000,
});
zr.add(zrDrawElement.tooltipY);
}
});
function findNearestValue(px, py) {
let value = ecChart.convertFromPixel('grid', [px, py]);
let itemIndex;
let nearestY = Infinity;
let binIndex;
chartData.forEach((item, index) => {
let dist = Math.abs(value[1] - item.step);
if (dist < nearestY) {
nearestY = dist;
itemIndex = index;
}
});
if (itemIndex != null) {
let dataItem = chartData[itemIndex];
let nearestX = Infinity;
dataItem.items.forEach((item, index) => {
let dist = Math.abs(item[2] - value[0]);
if (dist < nearestX) {
nearestX = dist;
binIndex = index;
} }
ecChart.setOption(option, {notMerge: true}); });
}, if (binIndex != null) {
return {
itemIndex: itemIndex,
binIndex: binIndex,
};
}
}
}
ecChart.setOption(option, {notMerge: true});
},
// get origin data per 60 seconds // get origin data per 60 seconds
startInterval() { startInterval() {
this.getOringDataInterval = setInterval(() => { this.getOringDataInterval = setInterval(() => {
let tagInfo = this.tagInfo; let tagInfo = this.tagInfo;
this.getOriginChartData(tagInfo); this.getOriginChartData(tagInfo);
}, intervalTime * 1000); }, intervalTime * 1000);
}, },
stopInterval() { stopInterval() {
clearInterval(this.getOringDataInterval); clearInterval(this.getOringDataInterval);
}, },
getOriginChartData(tagInfo) { getOriginChartData(tagInfo) {
let run = tagInfo.run; let run = tagInfo.run;
let tag = tagInfo.tag; let tag = tagInfo.tag;
let params = { let params = {
run, run,
tag: tag.displayName, tag: tag.displayName,
}; };
getPluginHistogramsHistograms(params).then(({status, data}) => { getPluginHistogramsHistograms(params).then(({status, data}) => {
if (status === 0) { if (status === 0) {
this.originData = data; this.originData = data;
} }
}); });
}, },
expandArea(expand) { expandArea(expand) {
let pageBoxWidth = document.getElementsByClassName('visual-dl-chart-page')[0].offsetWidth; let pageBoxWidth = document.getElementsByClassName('visual-dl-chart-page')[0].offsetWidth;
let width = pageBoxWidth * 0.96; // 4% margin let width = pageBoxWidth * 0.96; // 4% margin
if (expand) { if (expand) {
let el = this.$refs.visual_dl_chart_box; let el = this.$refs.visual_dl_chart_box;
el.style.width = width + 'px'; el.style.width = width + 'px';
el.style.height = '600px'; el.style.height = '600px';
this.isExpand = true; this.isExpand = true;
this.myChart.resize({ this.myChart.resize({
width: width, width: width,
height: 600, height: 600,
}); });
} else { } else {
let el = this.$refs.visual_dl_chart_box; let el = this.$refs.visual_dl_chart_box;
el.style.width = '400px'; el.style.width = '400px';
el.style.height = '300px'; el.style.height = '300px';
this.isExpand = false; this.isExpand = false;
this.myChart.resize({ this.myChart.resize({
width: 400, width: 400,
height: 300, height: 300,
}); });
} }
},
}, },
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -31,50 +31,50 @@ import Chart from './Chart'; ...@@ -31,50 +31,50 @@ import Chart from './Chart';
import {cloneDeep, flatten} from 'lodash'; import {cloneDeep, flatten} from 'lodash';
export default { export default {
props: ['config', 'runsItems', 'tagList', 'title'], props: ['config', 'runsItems', 'tagList', 'title'],
components: { components: {
'ui-chart': Chart, 'ui-chart': Chart,
'ui-expand-panel': ExpandPanel, 'ui-expand-panel': ExpandPanel,
},
computed: {
filteredRunsList() {
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return flatten(list.slice().map((item) => {
return item.tagList.filter((one) => runs.includes(one.run));
}));
}, },
computed: {
filteredRunsList() {
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return flatten(list.slice().map((item) => {
return item.tagList.filter((one) => runs.includes(one.run));
}));
},
filteredPageList() { filteredPageList() {
let list = this.filteredRunsList; let list = this.filteredRunsList;
let currentPage = this.currentPage; let currentPage = this.currentPage;
let pageSize = this.pageSize; let pageSize = this.pageSize;
return list.slice((currentPage - 1) * pageSize, currentPage * pageSize); return list.slice((currentPage - 1) * pageSize, currentPage * pageSize);
},
total() {
let list = this.filteredRunsList || [];
return list.length;
},
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
}, },
data() { total() {
return { let list = this.filteredRunsList || [];
// current page return list.length;
currentPage: 1,
// item per page
pageSize: 4,
};
}, },
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
},
data() {
return {
// current page
currentPage: 1,
// item per page
pageSize: 4,
};
},
methods: { methods: {
handlePageChange({pageNum}) { handlePageChange({pageNum}) {
this.currentPage = pageNum; this.currentPage = pageNum;
},
}, },
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -42,27 +42,27 @@ ...@@ -42,27 +42,27 @@
</template> </template>
<script> <script>
export default { export default {
props: ['config', 'runsItems'], props: ['config', 'runsItems'],
data() { data() {
return { return {
chartTypeItems: [ chartTypeItems: [
{ {
name: 'Overlay', name: 'Overlay',
value: 'overlay', value: 'overlay',
}, },
{ {
name: 'Offset', name: 'Offset',
value: 'offset', value: 'offset',
},
],
};
},
methods: {
toggleAllRuns() {
let running = this.config.running;
this.config.running = !running;
}, },
],
};
},
methods: {
toggleAllRuns() {
let running = this.config.running;
this.config.running = !running;
}, },
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -38,128 +38,128 @@ import Config from './ui/Config'; ...@@ -38,128 +38,128 @@ import Config from './ui/Config';
import ChartPage from './ui/ChartPage'; import ChartPage from './ui/ChartPage';
export default { export default {
name: 'Images', name: 'Images',
components: { components: {
'ui-config': Config, 'ui-config': Config,
'ui-chart-page': ChartPage, 'ui-chart-page': ChartPage,
}, },
data() { data() {
return {
runsArray: [],
tags: [],
config: {
groupNameReg: '.*',
isActualImageSize: false,
runs: [],
running: true,
},
filteredTagsList: [],
};
},
computed: {
runsItems() {
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return { return {
runsArray: [], name: item,
tags: [], value: item,
config: {
groupNameReg: '.*',
isActualImageSize: false,
runs: [],
running: true,
},
filteredTagsList: [],
}; };
});
}, },
computed: { tagsList() {
runsItems() { let tags = this.tags;
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return {
name: item,
value: item,
};
});
},
tagsList() {
let tags = this.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 let runs = Object.keys(tags);
return allUniqTags.map((tag) => { let tagsArray = runs.map((run) => Object.keys(tags[run]));
let tagList = runs.map((run) => { let allUniqTags = uniq(flatten(tagsArray));
return {
run,
tag: tags[run][tag],
};
}).filter((item) => item.tag !== undefined);
return {
tagList,
tag,
group: tag.split('/')[0],
};
});
},
groupedTags() {
let tagsList = this.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 // get the data for every chart
let groups = Object.keys(groupData); return allUniqTags.map((tag) => {
return groups.map((group) => { let tagList = runs.map((run) => {
return { return {
group, run,
tags: groupData[group], tag: tags[run][tag],
}; };
}); }).filter((item) => item.tag !== undefined);
}, return {
filteredConfig() { tagList,
let tansformArr = ['isActualImageSize']; tag,
let config = this.config || {}; group: tag.split('/')[0],
let filteredConfig = {}; };
Object.keys(config).forEach((key) => { });
let val = config[key];
filteredConfig[key] = val;
});
return filteredConfig;
},
}, },
created() { groupedTags() {
getPluginImagesTags().then(({errno, data}) => { let tagsList = this.tagsList || [];
this.tags = data; // put data in group
let groupData = {};
// filter when inited tagsList.forEach((item) => {
let groupNameReg = this.config.groupNameReg; let group = item.group;
this.filterTagsList(groupNameReg); if (groupData[group] === undefined) {
}); groupData[group] = [];
groupData[group].push(item);
} else {
groupData[group].push(item);
}
});
getRuns().then(({errno, data}) => { // to array
this.runsArray = data; let groups = Object.keys(groupData);
this.config.runs = data; return groups.map((group) => {
}); return {
group,
tags: groupData[group],
};
});
}, },
mounted() { filteredConfig() {
autoAdjustHeight(); let tansformArr = ['isActualImageSize'];
let config = this.config || {};
let filteredConfig = {};
Object.keys(config).forEach((key) => {
let val = config[key];
filteredConfig[key] = val;
});
return filteredConfig;
}, },
watch: { },
'config.groupNameReg': function(val) { created() {
this.throttledFilterTagsList(); getPluginImagesTags().then(({errno, data}) => {
}, this.tags = data;
// filter when inited
let groupNameReg = this.config.groupNameReg;
this.filterTagsList(groupNameReg);
});
getRuns().then(({errno, data}) => {
this.runsArray = data;
this.config.runs = data;
});
},
mounted() {
autoAdjustHeight();
},
watch: {
'config.groupNameReg': function(val) {
this.throttledFilterTagsList();
}, },
methods: { },
filterTagsList(groupNameReg) { methods: {
if (!groupNameReg) { filterTagsList(groupNameReg) {
this.filteredTagsList = []; if (!groupNameReg) {
return; this.filteredTagsList = [];
} return;
let tagsList = this.tagsList || []; }
let regExp = new RegExp(groupNameReg); let tagsList = this.tagsList || [];
this.filteredTagsList = tagsList.filter((item) => regExp.test(item.tag)); let regExp = new RegExp(groupNameReg);
}, this.filteredTagsList = tagsList.filter((item) => regExp.test(item.tag));
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
}, },
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
},
}; };
</script> </script>
......
...@@ -3,7 +3,7 @@ import {router} from 'san-router'; ...@@ -3,7 +3,7 @@ import {router} from 'san-router';
import Images from './Images'; import Images from './Images';
router.add({ router.add({
target: '#content', target: '#content',
rule: '/images', rule: '/images',
Component: Images, Component: Images,
}); });
...@@ -30,41 +30,41 @@ import Image from './Image'; ...@@ -30,41 +30,41 @@ import Image from './Image';
import {cloneDeep, flatten} from 'lodash'; import {cloneDeep, flatten} from 'lodash';
export default { export default {
props: ['config', 'runsItems', 'tagList', 'title'], props: ['config', 'runsItems', 'tagList', 'title'],
components: { components: {
'ui-image': Image, 'ui-image': Image,
'ui-expand-panel': ExpandPanel, 'ui-expand-panel': ExpandPanel,
},
computed: {
filteredRunsList() {
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return flatten(list.slice().map((item) => {
return item.tagList.filter((one) => runs.includes(one.run));
}));
}, },
computed: {
filteredRunsList() {
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return flatten(list.slice().map((item) => {
return item.tagList.filter((one) => runs.includes(one.run));
}));
},
filteredPageList() { filteredPageList() {
let list = this.filteredRunsList || []; let list = this.filteredRunsList || [];
return list.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize); return list.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize);
},
total() {
let list = this.filteredRunsList || [];
return list.length;
},
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
}, },
data() { total() {
return { let list = this.filteredRunsList || [];
// current page return list.length;
currentPage: 1,
// item per page
pageSize: 8,
};
}, },
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
},
data() {
return {
// current page
currentPage: 1,
// item per page
pageSize: 8,
};
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -37,19 +37,19 @@ ...@@ -37,19 +37,19 @@
<script> <script>
export default { export default {
props: { props: {
runsItems: Array, runsItems: Array,
config: Object, config: Object,
}, },
data() { data() {
return { return {
}; };
}, },
methods: { methods: {
toggleAllRuns() { toggleAllRuns() {
this.config.running = !this.config.running; this.config.running = !this.config.running;
},
}, },
},
}; };
</script> </script>
......
...@@ -32,111 +32,111 @@ const defaultImgHeight = 300; ...@@ -32,111 +32,111 @@ const defaultImgHeight = 300;
const intervalTime = 30; const intervalTime = 30;
export default { export default {
props: ['tagInfo', 'isActualImageSize', 'runs', 'running', 'runsItems'], props: ['tagInfo', 'isActualImageSize', 'runs', 'running', 'runsItems'],
computed: { computed: {
steps() { steps() {
let data = this.data || []; let data = this.data || [];
return data.length - 1; return data.length - 1;
},
imageWidth() {
return this.isActualImageSize ? this.imgData.width : defaultImgWidth;
},
imageHeight() {
return this.isActualImageSize ? this.imgData.height : defaultImgHeight;
},
}, },
filters: { imageWidth() {
formatTime: function(value) { return this.isActualImageSize ? this.imgData.width : defaultImgWidth;
if (!value) {
return;
}
// The value was made in seconds, must convert it to milliseconds
let time = new Date(value * 1000);
let options = {
weekday: 'short', year: 'numeric', month: 'short',
day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit',
};
return time.toLocaleDateString('en-US', options);
},
}, },
data() { imageHeight() {
return { return this.isActualImageSize ? this.imgData.height : defaultImgHeight;
currentIndex: 0,
slider: {
value: '0',
label: '',
min: 0,
step: 1,
},
imgData: {},
data: [],
height: defaultImgHeight,
weight: defaultImgWidth,
};
},
created() {
this.getOriginChartsData();
}, },
mounted() { },
if (this.running) { filters: {
this.startInterval(); formatTime: function(value) {
} if (!value) {
return;
}
// The value was made in seconds, must convert it to milliseconds
let time = new Date(value * 1000);
let options = {
weekday: 'short', year: 'numeric', month: 'short',
day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit',
};
return time.toLocaleDateString('en-US', options);
}, },
},
data() {
return {
currentIndex: 0,
slider: {
value: '0',
label: '',
min: 0,
step: 1,
},
imgData: {},
data: [],
height: defaultImgHeight,
weight: defaultImgWidth,
};
},
created() {
this.getOriginChartsData();
},
mounted() {
if (this.running) {
this.startInterval();
}
},
beforeDestroy() { beforeDestroy() {
this.stopInterval(); this.stopInterval();
}, },
watch: { watch: {
running: function(val) { running: function(val) {
val ? this.startInterval() : this.stopInterval(); val ? this.startInterval() : this.stopInterval();
},
currentIndex: function(index) {
/* eslint-disable fecs-camelcase */
if (this.data && this.data[index]) {
let currentImgInfo = this.data ? this.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.imgData = {
imgSrc,
height,
width,
step,
wall_time,
};
}
/* eslint-enable fecs-camelcase */
},
}, },
methods: { currentIndex: function(index) {
stopInterval() { /* eslint-disable fecs-camelcase */
clearInterval(this.getOringDataInterval); if (this.data && this.data[index]) {
}, let currentImgInfo = this.data ? this.data[index] : {};
// get origin data per {{intervalTime}} seconds let {height, width, query, step, wall_time} = currentImgInfo;
startInterval() { let url = '/data/plugin/images/individualImage?ts=' + wall_time;
this.getOringDataInterval = setInterval(() => { let imgSrc = [url, query].join('&');
this.getOriginChartsData(); this.imgData = {
}, intervalTime * 1000); imgSrc,
}, height,
getOriginChartsData() { width,
// let {run, tag} = this.tagInfo; step,
let run = this.tagInfo.run; wall_time,
let tag = this.tagInfo.tag; };
let {displayName, samples} = tag; }
let params = { /* eslint-enable fecs-camelcase */
run, },
tag: displayName, },
samples, methods: {
}; stopInterval() {
getPluginImagesImages(params).then(({status, data}) => { clearInterval(this.getOringDataInterval);
if (status === 0) { },
this.data = data; // get origin data per {{intervalTime}} seconds
this.currentIndex = data.length - 1; startInterval() {
} this.getOringDataInterval = setInterval(() => {
}); this.getOriginChartsData();
}, }, intervalTime * 1000);
},
getOriginChartsData() {
// let {run, tag} = this.tagInfo;
let run = this.tagInfo.run;
let tag = this.tagInfo.tag;
let {displayName, samples} = tag;
let params = {
run,
tag: displayName,
samples,
};
getPluginImagesImages(params).then(({status, data}) => {
if (status === 0) {
this.data = data;
this.currentIndex = data.length - 1;
}
});
}, },
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -7,17 +7,17 @@ import router from '@/router'; ...@@ -7,17 +7,17 @@ import router from '@/router';
Vue.config.productionTip = false; Vue.config.productionTip = false;
Vue.use(Vuetify, { Vue.use(Vuetify, {
theme: { theme: {
primary: '#008c99', primary: '#008c99',
accent: '#008c99', accent: '#008c99',
toolbox_icon: '#999999', toolbox_icon: '#999999',
}, },
}); });
/* eslint-disable no-new */ /* eslint-disable no-new */
new Vue({ new Vue({
el: '#root', el: '#root',
router, router,
components: {App}, components: {App},
template: '<App/>', template: '<App/>',
}); });
...@@ -37,120 +37,120 @@ import Config from './ui/Config'; ...@@ -37,120 +37,120 @@ import Config from './ui/Config';
import ChartPage from './ui/ChartPage'; import ChartPage from './ui/ChartPage';
export default { export default {
components: { components: {
'ui-config': Config, 'ui-config': Config,
'ui-chart-page': ChartPage, 'ui-chart-page': ChartPage,
}, },
data() { data() {
return {
runsArray: [],
tags: [],
config: {
groupNameReg: '.*',
smoothing: 0.6,
horizontal: 'step',
sortingMethod: 'default',
outlier: false,
runs: [],
running: true,
},
filteredTagsList: [],
};
},
computed: {
runsItems() {
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return { return {
runsArray: [], name: item,
tags: [], value: item,
config: {
groupNameReg: '.*',
smoothing: 0.6,
horizontal: 'step',
sortingMethod: 'default',
outlier: false,
runs: [],
running: true,
},
filteredTagsList: [],
}; };
});
}, },
computed: { tagsList() {
runsItems() { let tags = this.tags;
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return {
name: item,
value: item,
};
});
},
tagsList() {
let tags = this.tags;
let runs = Object.keys(tags); let runs = Object.keys(tags);
let tagsArray = runs.map((run) => Object.keys(tags[run])); let tagsArray = runs.map((run) => Object.keys(tags[run]));
let allUniqTags = uniq(flatten(tagsArray)); let allUniqTags = uniq(flatten(tagsArray));
// get the data for every chart // get the data for every chart
return allUniqTags.map((tag) => { return allUniqTags.map((tag) => {
let tagList = runs.map((run) => { let tagList = runs.map((run) => {
return { return {
run, run,
tag: tags[run][tag], tag: tags[run][tag],
}; };
}).filter((item) => item.tag !== undefined); }).filter((item) => item.tag !== undefined);
return { return {
tagList, tagList,
tag, tag,
group: tag.split('/')[0], group: tag.split('/')[0],
}; };
}); });
}, },
groupedTags() { groupedTags() {
let tagsList = this.tagsList || []; let tagsList = this.tagsList || [];
// put data in group // put data in group
let groupData = {}; let groupData = {};
tagsList.forEach((item) => { tagsList.forEach((item) => {
let group = item.group; let group = item.group;
if (groupData[group] === undefined) { if (groupData[group] === undefined) {
groupData[group] = []; groupData[group] = [];
groupData[group].push(item); groupData[group].push(item);
} else { } else {
groupData[group].push(item); groupData[group].push(item);
} }
}); });
// to array // to array
let groups = Object.keys(groupData); let groups = Object.keys(groupData);
return groups.map((group) => { return groups.map((group) => {
return { return {
group, group,
tags: groupData[group], tags: groupData[group],
}; };
}); });
},
}, },
created() { },
getPluginScalarsTags().then(({errno, data}) => { created() {
this.tags = data; getPluginScalarsTags().then(({errno, data}) => {
this.tags = data;
// filter when inited // filter when inited
let groupNameReg = this.config.groupNameReg; let groupNameReg = this.config.groupNameReg;
this.filterTagsList(groupNameReg); this.filterTagsList(groupNameReg);
}); });
getRuns().then(({errno, data}) => { getRuns().then(({errno, data}) => {
this.runsArray = data; this.runsArray = data;
this.config.runs = data; this.config.runs = data;
}); });
}, },
mounted() { mounted() {
autoAdjustHeight(); autoAdjustHeight();
}, },
watch: { watch: {
'config.groupNameReg': function(val) { 'config.groupNameReg': function(val) {
this.throttledFilterTagsList(); this.throttledFilterTagsList();
},
}, },
methods: { },
filterTagsList(groupNameReg) { methods: {
if (!groupNameReg) { filterTagsList(groupNameReg) {
this.filteredTagsList = []; if (!groupNameReg) {
return; this.filteredTagsList = [];
} return;
let tagsList = this.tagsList || []; }
let regExp = new RegExp(groupNameReg); let tagsList = this.tagsList || [];
this.filteredTagsList = tagsList.filter((item) => regExp.test(item.tag)); let regExp = new RegExp(groupNameReg);
}, this.filteredTagsList = tagsList.filter((item) => regExp.test(item.tag));
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
}, },
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
},
}; };
</script> </script>
......
...@@ -3,13 +3,13 @@ import {router} from 'san-router'; ...@@ -3,13 +3,13 @@ import {router} from 'san-router';
import Scalar from './Scalars'; import Scalar from './Scalars';
router.add({ router.add({
target: '#content', target: '#content',
rule: '/', rule: '/',
Component: Scalar, Component: Scalar,
}); });
router.add({ router.add({
target: '#content', target: '#content',
rule: '/scalars', rule: '/scalars',
Component: Scalar, Component: Scalar,
}); });
...@@ -102,566 +102,566 @@ const maxQuantile = 0.95; ...@@ -102,566 +102,566 @@ const maxQuantile = 0.95;
const intervalTime = 15; const intervalTime = 15;
export default { export default {
props: ['tagInfo', 'groupNameReg', 'smoothing', 'horizontal', 'sortingMethod', 'outlier', 'runs', props: ['tagInfo', 'groupNameReg', 'smoothing', 'horizontal', 'sortingMethod', 'outlier', 'runs',
'running', 'runsItems'], 'running', 'runsItems'],
computed: { computed: {
computedStyle() { computedStyle() {
return 'height:' + this.height + 'px;' return 'height:' + this.height + 'px;'
+ 'width:' + this.width + 'px;'; + 'width:' + this.width + 'px;';
},
}, },
data() { },
return { data() {
width: 400, return {
height: 300, width: 400,
isExpand: false, height: 300,
isSelectZoomEnable: true, isExpand: false,
originData: [], isSelectZoomEnable: true,
}; originData: [],
};
},
watch: {
originData: function(val) {
this.setChartData();
this.setChartsOutlier();
this.setChartHorizon();
},
smoothing: function(val) {
this.setChartData();
},
outlier: function(val) {
this.setChartsOutlier();
},
horizontal: function(val) {
this.setChartHorizon();
},
tagInfo: function(val) {
// Should Clean up the chart before each use.
this.myChart.clear();
this.setChartsOptions(val);
this.getOriginChartData(val);
},
isExpand: function(val) {
this.expandArea(val);
},
isSelectZoomEnable: function(val) {
this.toggleSelectZoom(val);
},
},
mounted() {
this.initChart(this.tagInfo);
this.toggleSelectZoom(true);
if (this.running) {
this.startInterval();
}
this.$watch('running', function(running) {
running ? this.startInterval() : this.stopInterval();
});
},
beforeDestroy() {
this.stopInterval();
},
methods: {
// Create a Scalar Chart, initialize it with default settings, then load datas
initChart(tagInfo) {
this.createChart();
this.setChartsOptions(tagInfo);
this.getOriginChartData(tagInfo);
}, },
watch: {
originData: function(val) { createChart() {
this.setChartData(); let el = this.$refs.chartBox;
this.setChartsOutlier(); this.myChart = echarts.init(el);
this.setChartHorizon(); },
setChartsOptions({tagList, tag}) {
// Create two lines, one line is original, the other line is for smoothing
let seriesOption = tagList.map((item) => [
{
name: item.run,
type: 'line',
showSymbol: false,
hoverAnimation: false,
z: 0,
data: [],
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 instance = this;
let option = {
textStyle: {
fontFamily: 'Merriweather Sans',
}, },
smoothing: function(val) { color: [
this.setChartData(); '#008c99',
'#c23531',
'#FF9900',
'#109618',
'#990099',
'#3B3EAC',
'#DD4477',
'#AAAA11',
'#5574A6',
'#8B0707',
],
title: {
text: tag,
textStyle: {
fontSize: 13,
fontWeight: 'normal',
},
}, },
outlier: function(val) { tooltip: {
this.setChartsOutlier(); trigger: 'axis',
axisPointer: {
animation: true,
},
textStyle: {
fontSize: '13',
},
position: ['10%', '90%'],
formatter(params, ticket, callback) {
let data = instance.getFormatterPoints(params[0].data);
return instance.transformFormatterData(data);
},
}, },
horizontal: function(val) { toolbox: {
this.setChartHorizon(); show: true,
showTitle: false,
itemSize: 0,
feature: {
dataZoom: {},
},
}, },
tagInfo: function(val) { legend: {
// Should Clean up the chart before each use. data: legendOptions,
this.myChart.clear(); top: 39,
this.setChartsOptions(val);
this.getOriginChartData(val);
}, },
isExpand: function(val) { grid: {
this.expandArea(val); left: 48,
top: 75,
right: 40,
bottom: 36,
}, },
isSelectZoomEnable: function(val) { xAxis: {
this.toggleSelectZoom(val); type: 'value',
axisLabel: {
fontSize: '11',
},
splitNumber: this.isExpand ? 10 : 5,
}, },
yAxis: {
type: 'value',
axisLabel: {
fontSize: '11',
formatter(value) {
return value.toString().slice(0, 5);
},
},
},
series: seriesOption,
};
this.myChart.setOption(option);
}, },
mounted() {
this.initChart(this.tagInfo);
this.toggleSelectZoom(true);
if (this.running) {
this.startInterval();
}
this.$watch('running', function(running) { // Get origin data per 60 seconds
running ? this.startInterval() : this.stopInterval(); startInterval() {
}); this.getOriginDataInterval = setInterval(() => {
this.getOriginChartData(this.tagInfo);
}, intervalTime * 1000);
}, },
beforeDestroy() { stopInterval() {
this.stopInterval(); clearInterval(this.getOriginDataInterval);
}, },
methods: {
// Create a Scalar Chart, initialize it with default settings, then load datas
initChart(tagInfo) {
this.createChart();
this.setChartsOptions(tagInfo);
this.getOriginChartData(tagInfo);
},
createChart() {
let el = this.$refs.chartBox;
this.myChart = echarts.init(el);
},
setChartsOptions({tagList, tag}) {
// Create two lines, one line is original, the other line is for smoothing
let seriesOption = tagList.map((item) => [
{
name: item.run,
type: 'line',
showSymbol: false,
hoverAnimation: false,
z: 0,
data: [],
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 instance = this;
let option = {
textStyle: {
fontFamily: 'Merriweather Sans',
},
color: [
'#008c99',
'#c23531',
'#FF9900',
'#109618',
'#990099',
'#3B3EAC',
'#DD4477',
'#AAAA11',
'#5574A6',
'#8B0707',
],
title: {
text: tag,
textStyle: {
fontSize: 13,
fontWeight: 'normal',
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: true,
},
textStyle: {
fontSize: '13',
},
position: ['10%', '90%'],
formatter(params, ticket, callback) {
let data = instance.getFormatterPoints(params[0].data);
return instance.transformFormatterData(data);
},
},
toolbox: {
show: true,
showTitle: false,
itemSize: 0,
feature: {
dataZoom: {},
},
},
legend: {
data: legendOptions,
top: 39,
},
grid: {
left: 48,
top: 75,
right: 40,
bottom: 36,
},
xAxis: {
type: 'value',
axisLabel: {
fontSize: '11',
},
splitNumber: this.isExpand ? 10 : 5,
},
yAxis: {
type: 'value',
axisLabel: {
fontSize: '11',
formatter(value) {
return value.toString().slice(0, 5);
},
},
},
series: seriesOption,
};
this.myChart.setOption(option);
},
// Get origin data per 60 seconds getOriginChartData({tagList, tag}) {
startInterval() { let requestList = tagList.map((item) => {
this.getOriginDataInterval = setInterval(() => { let params = {
this.getOriginChartData(this.tagInfo); run: item.run,
}, intervalTime * 1000); tag: tag,
}, };
return getPluginScalarsScalars(params);
});
axios.all(requestList).then((resArray) => {
if (resArray.every((res) => res.status === 0)) {
this.originData = resArray.map((res) => res.data);
}
});
},
stopInterval() { setChartData() {
clearInterval(this.getOriginDataInterval); let seriesData = this.originData.map((lineData) => {
}, if (lineData.length == 0) return [];
// add the smoothed data
this.transformDataset(lineData);
return [
{
data: lineData,
encode: {
// map 1 dimension to xAixs.
x: [1],
// map 2 dimension to yAixs.
y: [2],
},
},
{
data: lineData,
encode: {
// Map 1 dimension to xAixs.
x: [1],
// Map 3 dimension to yAixs,
// the third number is smoothed value.
y: [3],
},
},
];
});
this.myChart.setOption({
series: flatten(seriesData),
});
},
getOriginChartData({tagList, tag}) { getChartOptions() {
let requestList = tagList.map((item) => { return this.myChart.getOption() || {};
let params = { },
run: item.run, handleDownLoad(runItemForDownload) {
tag: tag, let options = this.getChartOptions();
}; let series = options.series || [];
return getPluginScalarsScalars(params); let seriesItem = series.find((item) => item.name === runItemForDownload) || {};
}); let fileName = this.tagInfo.tag.replace(/\//g, '-');
axios.all(requestList).then((resArray) => { generateJsonAndDownload(seriesItem.data, fileName);
if (resArray.every((res) => res.status === 0)) { },
this.originData = resArray.map((res) => res.data);
}
});
},
setChartData() { transformDataset(seriesData) {
let seriesData = this.originData.map((lineData) => { // smooth
if (lineData.length == 0) return []; this.transformData(seriesData, this.smoothing);
// add the smoothed data },
this.transformDataset(lineData);
return [
{
data: lineData,
encode: {
// map 1 dimension to xAixs.
x: [1],
// map 2 dimension to yAixs.
y: [2],
},
},
{
data: lineData,
encode: {
// Map 1 dimension to xAixs.
x: [1],
// Map 3 dimension to yAixs,
// the third number is smoothed value.
y: [3],
},
},
];
});
this.myChart.setOption({
series: flatten(seriesData),
});
},
getChartOptions() { /**
return this.myChart.getOption() || {}; * @desc 1、add smooth data depend on smoothingWeight. see https://en.wikipedia.org/wiki/Moving_average for detail
}, * 2、add relative data
handleDownLoad(runItemForDownload) { * @param {Object} echarts series Object
let options = this.getChartOptions(); * @param {number} smoothingWeight smooth weight, between 0 ~ 1
let series = options.series || []; */
let seriesItem = series.find((item) => item.name === runItemForDownload) || {}; transformData(seriesData, smoothingWeight) {
let fileName = this.tagInfo.tag.replace(/\//g, '-'); let data = seriesData;
generateJsonAndDownload(seriesItem.data, fileName); 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;
}
});
},
transformDataset(seriesData) { // Chart outlier options methods and functions ---- start.
// smooth // Compute Y domain from originData.
this.transformData(seriesData, this.smoothing); setChartsOutlier() {
}, let domainRangeArray = this.originData.map((seriesData) => this.computeDataRange(seriesData, this.outlier));
/** // Compare, get the best Y domain.
* @desc 1、add smooth data depend on smoothingWeight. see https://en.wikipedia.org/wiki/Moving_average for detail let flattenNumbers = flatten(domainRangeArray);
* 2、add relative data let finalMax = max(flattenNumbers);
* @param {Object} echarts series Object let finalMin = min(flattenNumbers);
* @param {number} smoothingWeight smooth weight, between 0 ~ 1
*/
transformData(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. // Add padding.
// Compute Y domain from originData. let PaddedYDomain = this.paddedYDomain(finalMin, finalMax);
setChartsOutlier() {
let domainRangeArray = this.originData.map((seriesData) => this.computeDataRange(seriesData, this.outlier));
// Compare, get the best Y domain. this.setChartOutlierOptions(PaddedYDomain);
let flattenNumbers = flatten(domainRangeArray);
let finalMax = max(flattenNumbers);
let finalMin = min(flattenNumbers);
// Add padding. // Store Y domain, if originData is not change, Y domain keep same.
let PaddedYDomain = this.paddedYDomain(finalMin, finalMax); },
this.setChartOutlierOptions(PaddedYDomain); // Compute max and min from array, if outlier is true, return quantile range.
computeDataRange(arr, isQuantile) {
// Get data range.
if (arr.length == 0) return [];
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];
},
// Store Y domain, if originData is not change, Y domain keep same. paddedYDomain(min, max) {
}, return {
max: max > 0 ? max * 1.1 : max * 0.9,
min: min > 0 ? min * 0.9 : min * 1.1,
};
},
// Compute max and min from array, if outlier is true, return quantile range. setChartOutlierOptions({min, max}) {
computeDataRange(arr, isQuantile) { this.myChart.setOption({
// Get data range. yAxis: {
if (arr.length == 0) return []; min,
let max; 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) { // Chart horizontal options methods and functions ---- start.
return { setChartHorizon() {
max: max > 0 ? max * 1.1 : max * 0.9, let seriesOption = this.myChart.getOption().series;
min: min > 0 ? min * 0.9 : min * 1.1, let encodeSeries = (val) => {
}; return {
encode: {
x: [val],
},
};
};
let stepSeries = seriesOption.map((item) => encodeSeries(1));
let relativeSeries = seriesOption.map((item) => encodeSeries(4));
let wallSeries = seriesOption.map((item) => encodeSeries(0));
let horizontalToxAxisOptions = {
step: {
xAxis: {
type: 'value',
axisLabel: {
fontSize: '11',
},
splitNumber: this.isExpand ? 10 : 5,
},
series: stepSeries,
}, },
relative: {
setChartOutlierOptions({min, max}) { xAxis: {
this.myChart.setOption({ type: 'value',
yAxis: { axisLabel: {
min, fontSize: '11',
max, },
}, splitNumber: this.isExpand ? 10 : 5,
}); },
series: relativeSeries,
}, },
wall: {
// Chart horizontal options methods and functions ---- start. xAxis: {
setChartHorizon() { type: 'time',
let seriesOption = this.myChart.getOption().series; axisLabel: {
let encodeSeries = (val) => { fontSize: '11',
return { },
encode: { },
x: [val], series: wallSeries,
},
};
};
let stepSeries = seriesOption.map((item) => encodeSeries(1));
let relativeSeries = seriesOption.map((item) => encodeSeries(4));
let wallSeries = seriesOption.map((item) => encodeSeries(0));
let horizontalToxAxisOptions = {
step: {
xAxis: {
type: 'value',
axisLabel: {
fontSize: '11',
},
splitNumber: this.isExpand ? 10 : 5,
},
series: stepSeries,
},
relative: {
xAxis: {
type: 'value',
axisLabel: {
fontSize: '11',
},
splitNumber: this.isExpand ? 10 : 5,
},
series: relativeSeries,
},
wall: {
xAxis: {
type: 'time',
axisLabel: {
fontSize: '11',
},
},
series: wallSeries,
},
};
this.myChart.setOption(horizontalToxAxisOptions[this.horizontal]);
}, },
};
this.myChart.setOption(horizontalToxAxisOptions[this.horizontal]);
},
expandArea(expand) { expandArea(expand) {
let pageBoxWidth = document.getElementsByClassName('visual-dl-chart-page-box')[0].offsetWidth; let pageBoxWidth = document.getElementsByClassName('visual-dl-chart-page-box')[0].offsetWidth;
let width = pageBoxWidth * 0.96; // 4% margin let width = pageBoxWidth * 0.96; // 4% margin
if (expand) { if (expand) {
let el = this.$refs.chartBox; let el = this.$refs.chartBox;
el.style.width = width + 'px'; el.style.width = width + 'px';
el.style.height = '600px'; el.style.height = '600px';
this.myChart.resize({ this.myChart.resize({
width: width, width: width,
height: 600, height: 600,
}); });
} else { } else {
let el = this.$refs.chartBox; let el = this.$refs.chartBox;
el.style.width = '400px'; el.style.width = '400px';
el.style.height = '300px'; el.style.height = '300px';
this.myChart.resize({ this.myChart.resize({
width: 400, width: 400,
height: 300, height: 300,
}); });
} }
this.myChart.setOption({ this.myChart.setOption({
xAxis: { xAxis: {
splitNumber: this.isExpand ? 10 : 5, splitNumber: this.isExpand ? 10 : 5,
},
});
}, },
});
},
toggleSelectZoom(enable) { toggleSelectZoom(enable) {
let instance = this; let instance = this;
setTimeout(function() { setTimeout(function() {
instance.myChart.dispatchAction({ instance.myChart.dispatchAction({
type: 'takeGlobalCursor', type: 'takeGlobalCursor',
key: 'dataZoomSelect', key: 'dataZoomSelect',
dataZoomSelectActive: enable, dataZoomSelectActive: enable,
}); });
}, 0); }, 0);
}, },
restoreChart() { restoreChart() {
this.myChart.dispatchAction({ this.myChart.dispatchAction({
type: 'restore', type: 'restore',
}); });
}, },
saveChartAsImage() { saveChartAsImage() {
let dataUrl = this.myChart.getDataURL({ let dataUrl = this.myChart.getDataURL({
pixelRatio: 1, pixelRatio: 1,
backgroundColor: '#fff', backgroundColor: '#fff',
}); });
let fileName = this.tagInfo.tag.replace(/\//g, '-'); let fileName = this.tagInfo.tag.replace(/\//g, '-');
let link = document.createElement('a'); let link = document.createElement('a');
link.download = fileName; link.download = fileName;
link.href = dataUrl; link.href = dataUrl;
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
}, },
getFormatterPoints(data) { getFormatterPoints(data) {
let originData = this.originData; let originData = this.originData;
let tagList = this.tagInfo.tagList; let tagList = this.tagInfo.tagList;
let sortingMethod = this.sortingMethod; let sortingMethod = this.sortingMethod;
// Can't know exactly the tigger runs. // Can't know exactly the tigger runs.
// If the step is same, regard the point as the trigger point. // If the step is same, regard the point as the trigger point.
let [, step, triggerValue] = data; let [, step, triggerValue] = data;
let points = originData.map((series, index) => { let points = originData.map((series, index) => {
let nearestItem; let nearestItem;
if (step === 0) { if (step === 0) {
nearestItem = series[0]; nearestItem = series[0];
} else { } else {
for (let i = 0; i < series.length; i++) { for (let i = 0; i < series.length; i++) {
let item = series[i]; let item = series[i];
if (item[1] === step) { if (item[1] === step) {
nearestItem = item; nearestItem = item;
break; break;
}
if (item[1] > step) {
let index = i - 1;
nearestItem = series[index >= 0 ? index : 0];
break;
}
if (!nearestItem) {
nearestItem = series[series.length - 1];
}
}
}
return {
run: tagList[index].run,
item: nearestItem,
};
});
if (sortingMethod === 'default' || !sortingMethod) {
return points;
} }
let sortedPoints; if (item[1] > step) {
switch (sortingMethod) { let index = i - 1;
case 'descending': nearestItem = series[index >= 0 ? index : 0];
sortedPoints = sortBy(points, (one) => one.item[3]); break;
sortedPoints.reverse();
break;
case 'ascending':
sortedPoints = sortBy(points, (one) => one.item[3]);
break;
case 'nearest':
// Compare other ponts width the trigger point, caculate the nearest sort.
sortedPoints = sortBy(points, (one) => one.item[3] - triggerValue);
break;
default:
sortedPoints = points;
} }
return sortedPoints; if (!nearestItem) {
}, nearestItem = series[series.length - 1];
}
}
}
return {
run: tagList[index].run,
item: nearestItem,
};
});
if (sortingMethod === 'default' || !sortingMethod) {
return points;
}
let sortedPoints;
switch (sortingMethod) {
case 'descending':
sortedPoints = sortBy(points, (one) => one.item[3]);
sortedPoints.reverse();
break;
case 'ascending':
sortedPoints = sortBy(points, (one) => one.item[3]);
break;
case 'nearest':
// Compare other ponts width the trigger point, caculate the nearest sort.
sortedPoints = sortBy(points, (one) => one.item[3] - triggerValue);
break;
default:
sortedPoints = points;
}
return sortedPoints;
},
transformFormatterData(data) { transformFormatterData(data) {
let indexPropMap = { let indexPropMap = {
Time: 0, Time: 0,
Step: 1, Step: 1,
Value: 2, Value: 2,
Smoothed: 3, Smoothed: 3,
Relative: 4, Relative: 4,
}; };
let widthPropMap = { let widthPropMap = {
Run: 40, Run: 40,
Time: 120, Time: 120,
Step: 40, Step: 40,
Value: 50, Value: 50,
Smoothed: 60, Smoothed: 60,
Relative: 60, Relative: 60,
}; };
let transformedData = data.map((item) => { let transformedData = data.map((item) => {
let data = item.item; let data = item.item;
return { return {
Run: item.run, Run: item.run,
// Keep six number for easy-read. // Keep six number for easy-read.
Smoothed: data[indexPropMap.Smoothed].toString().slice(0, 6), Smoothed: data[indexPropMap.Smoothed].toString().slice(0, 6),
Value: data[indexPropMap.Value].toString().slice(0, 6), Value: data[indexPropMap.Value].toString().slice(0, 6),
Step: data[indexPropMap.Step], Step: data[indexPropMap.Step],
Time: moment(Math.floor(data[indexPropMap.Time] * 1000), 'x').format('YYYY-MM-DD HH:mm:ss'), Time: moment(Math.floor(data[indexPropMap.Time] * 1000), 'x').format('YYYY-MM-DD HH:mm:ss'),
// Relative display value should take easy-read into consideration. // Relative display value should take easy-read into consideration.
// Better to tranform data to 'day:hour', 'hour:minutes', 'minute: seconds' and second only. // Better to tranform data to 'day:hour', 'hour:minutes', 'minute: seconds' and second only.
Relative: Math.floor(data[indexPropMap.Relative] * 60 * 60) + 's', Relative: Math.floor(data[indexPropMap.Relative] * 60 * 60) + 's',
}; };
}); });
let headerHtml = '<tr style="font-size:14px;">'; let headerHtml = '<tr style="font-size:14px;">';
headerHtml += Object.keys(transformedData[0]).map((key) => { headerHtml += Object.keys(transformedData[0]).map((key) => {
return '<td style="padding: 0 4px; font-family: \'Merriweather Sans\'; font-weight: bold; width:' + widthPropMap[key] + 'px;">' + key + '</td>'; return '<td style="padding: 0 4px; font-family: \'Merriweather Sans\'; font-weight: bold; width:' + widthPropMap[key] + 'px;">' + key + '</td>';
}).join(''); }).join('');
headerHtml += '</tr>'; headerHtml += '</tr>';
let content = transformedData.map((item) => { let content = transformedData.map((item) => {
let str = '<tr style="font-size:12px;">'; let str = '<tr style="font-size:12px;">';
str += Object.keys(item).map((val) => { str += Object.keys(item).map((val) => {
return '<td style="padding: 0 4px">' + item[val] + '</td>'; return '<td style="padding: 0 4px">' + item[val] + '</td>';
}).join(''); }).join('');
str += '</tr>'; str += '</tr>';
return str; return str;
}).join(''); }).join('');
return '<table style="text-align: left;table-layout: fixed;width: 480px;"><thead>' + headerHtml + '</thead>' return '<table style="text-align: left;table-layout: fixed;width: 480px;"><thead>' + headerHtml + '</thead>'
+ '<tbody>' + content + '</tbody><table>'; + '<tbody>' + content + '</tbody><table>';
},
}, },
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -34,42 +34,42 @@ import Chart from './Chart'; ...@@ -34,42 +34,42 @@ import Chart from './Chart';
import {cloneDeep} from 'lodash'; import {cloneDeep} from 'lodash';
export default { export default {
components: { components: {
'ui-chart': Chart, 'ui-chart': Chart,
'ui-expand-panel': ExpandPanel, 'ui-expand-panel': ExpandPanel,
// 'ui-pagination': Pagination // 'ui-pagination': Pagination
},
props: ['config', 'runsItems', 'tagList', 'title'],
computed: {
filteredRunsList() {
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return list.slice().map((item) => {
item.tagList = item.tagList.filter((one) => runs.includes(one.run));
return item;
});
}, },
props: ['config', 'runsItems', 'tagList', 'title'], filteredTagList() {
computed: { let tagList = this.filteredRunsList || [];
filteredRunsList() { return tagList.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize);
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return list.slice().map((item) => {
item.tagList = item.tagList.filter((one) => runs.includes(one.run));
return item;
});
},
filteredTagList() {
let tagList = this.filteredRunsList || [];
return tagList.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize);
},
total() {
let tagList = this.tagList || [];
return tagList.length;
},
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
}, },
data() { total() {
return { let tagList = this.tagList || [];
// current page return tagList.length;
currentPage: 1,
// item per page
pageSize: 8,
};
}, },
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
},
data() {
return {
// current page
currentPage: 1,
// item per page
pageSize: 8,
};
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -76,44 +76,44 @@ ...@@ -76,44 +76,44 @@
<script> <script>
export default { export default {
props: { props: {
runsItems: Array, runsItems: Array,
config: Object, config: Object,
}, },
data() { data() {
return { return {
horizontalItems: [ horizontalItems: [
{ {
name: 'Step', name: 'Step',
value: 'step', value: 'step',
}, },
{ {
name: 'Relative', name: 'Relative',
value: 'relative', value: 'relative',
}, },
{ {
name: 'Wall', name: 'Wall',
value: 'wall', value: 'wall',
},
],
sortingMethodItems: [
'default', 'descending', 'ascending', 'nearest',
],
smoothingValue: this.config.smoothing,
};
},
watch: {
smoothingValue: _.debounce(
function() {
this.config.smoothing = this.smoothingValue;
}, 50
),
},
methods: {
toggleAllRuns() {
this.config.running = !this.config.running;
}, },
],
sortingMethodItems: [
'default', 'descending', 'ascending', 'nearest',
],
smoothingValue: this.config.smoothing,
};
},
watch: {
smoothingValue: _.debounce(
function() {
this.config.smoothing = this.smoothingValue;
}, 50
),
},
methods: {
toggleAllRuns() {
this.config.running = !this.config.running;
}, },
},
}; };
</script> </script>
......
...@@ -38,126 +38,126 @@ import Config from './ui/Config'; ...@@ -38,126 +38,126 @@ import Config from './ui/Config';
import ChartPage from './ui/ChartPage'; import ChartPage from './ui/ChartPage';
export default { export default {
name: 'Texts', name: 'Texts',
components: { components: {
'ui-config': Config, 'ui-config': Config,
'ui-chart-page': ChartPage, 'ui-chart-page': ChartPage,
}, },
data() { data() {
return {
runsArray: [],
tags: [],
config: {
groupNameReg: '.*',
runs: [],
running: true,
},
filteredTagsList: [],
};
},
computed: {
runsItems() {
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return { return {
runsArray: [], name: item,
tags: [], value: item,
config: {
groupNameReg: '.*',
runs: [],
running: true,
},
filteredTagsList: [],
}; };
});
}, },
computed: { tagsList() {
runsItems() { let tags = this.tags;
let runsArray = this.runsArray || [];
return runsArray.map((item) => {
return {
name: item,
value: item,
};
});
},
tagsList() {
let tags = this.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 let runs = Object.keys(tags);
return allUniqTags.map((tag) => { let tagsArray = runs.map((run) => Object.keys(tags[run]));
let tagList = runs.map((run) => { let allUniqTags = uniq(flatten(tagsArray));
return {
run,
tag: tags[run][tag],
};
}).filter((item) => item.tag !== undefined);
return {
tagList,
tag,
group: tag.split('/')[0],
};
});
},
groupedTags() {
let tagsList = this.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 // get the data for every chart
let groups = Object.keys(groupData); return allUniqTags.map((tag) => {
return groups.map((group) => { let tagList = runs.map((run) => {
return { return {
group, run,
tags: groupData[group], tag: tags[run][tag],
}; };
}); }).filter((item) => item.tag !== undefined);
}, return {
filteredConfig() { tagList,
let config = this.config || {}; tag,
let filteredConfig = {}; group: tag.split('/')[0],
Object.keys(config).forEach((key) => { };
let val = config[key]; });
filteredConfig[key] = val;
});
return filteredConfig;
},
}, },
created() { groupedTags() {
getPluginTextsTags().then(({errno, data}) => { let tagsList = this.tagsList || [];
this.tags = data; // put data in group
let groupData = {};
// filter when inited tagsList.forEach((item) => {
let groupNameReg = this.config.groupNameReg; let group = item.group;
this.filterTagsList(groupNameReg); if (groupData[group] === undefined) {
}); groupData[group] = [];
groupData[group].push(item);
} else {
groupData[group].push(item);
}
});
getRuns().then(({errno, data}) => { // to array
this.runsArray = data; let groups = Object.keys(groupData);
this.config.runs = data; return groups.map((group) => {
}); return {
group,
tags: groupData[group],
};
});
}, },
mounted() { filteredConfig() {
autoAdjustHeight(); let config = this.config || {};
let filteredConfig = {};
Object.keys(config).forEach((key) => {
let val = config[key];
filteredConfig[key] = val;
});
return filteredConfig;
}, },
watch: { },
'config.groupNameReg': function(val) { created() {
this.throttledFilterTagsList(); getPluginTextsTags().then(({errno, data}) => {
}, this.tags = data;
// filter when inited
let groupNameReg = this.config.groupNameReg;
this.filterTagsList(groupNameReg);
});
getRuns().then(({errno, data}) => {
this.runsArray = data;
this.config.runs = data;
});
},
mounted() {
autoAdjustHeight();
},
watch: {
'config.groupNameReg': function(val) {
this.throttledFilterTagsList();
}, },
methods: { },
filterTagsList(groupNameReg) { methods: {
if (!groupNameReg) { filterTagsList(groupNameReg) {
this.filteredTagsList = []; if (!groupNameReg) {
return; this.filteredTagsList = [];
} return;
let tagsList = this.tagsList || []; }
let regExp = new RegExp(groupNameReg); let tagsList = this.tagsList || [];
this.filteredTagsList = tagsList.filter((item) => regExp.test(item.tag)); let regExp = new RegExp(groupNameReg);
}, this.filteredTagsList = tagsList.filter((item) => regExp.test(item.tag));
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
}, },
throttledFilterTagsList: _.debounce(
function() {
this.filterTagsList(this.config.groupNameReg);
}, 300
),
},
}; };
</script> </script>
......
...@@ -3,7 +3,7 @@ import {router} from 'san-router'; ...@@ -3,7 +3,7 @@ import {router} from 'san-router';
import Texts from './Texts'; import Texts from './Texts';
router.add({ router.add({
target: '#content', target: '#content',
rule: '/texts', rule: '/texts',
Component: Texts, Component: Texts,
}); });
...@@ -28,100 +28,100 @@ import {getPluginTextsTexts} from '../../service'; ...@@ -28,100 +28,100 @@ import {getPluginTextsTexts} from '../../service';
const intervalTime = 30; const intervalTime = 30;
export default { export default {
props: ['tagInfo', 'runs', 'running', 'runsItems'], props: ['tagInfo', 'runs', 'running', 'runsItems'],
computed: { computed: {
steps() { steps() {
let data = this.data || []; let data = this.data || [];
return data.length - 1; return data.length - 1;
},
}, },
filters: { },
formatTime: function(value) { filters: {
if (!value) { formatTime: function(value) {
return; if (!value) {
} return;
// The value was made in seconds, must convert it to milliseconds }
let time = new Date(value * 1000); // The value was made in seconds, must convert it to milliseconds
let options = { let time = new Date(value * 1000);
weekday: 'short', year: 'numeric', month: 'short', let options = {
day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', weekday: 'short', year: 'numeric', month: 'short',
}; day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit',
return time.toLocaleDateString('en-US', options); };
}, return time.toLocaleDateString('en-US', options);
}, },
data() { },
return { data() {
currentIndex: 0, return {
slider: { currentIndex: 0,
value: '0', slider: {
label: '', value: '0',
min: 0, label: '',
step: 1, min: 0,
}, step: 1,
textData: {}, },
data: [], textData: {},
data: [],
};
},
created() {
this.getOriginChartsData();
},
mounted() {
if (this.running) {
this.startInterval();
}
},
beforeDestroy() {
this.stopInterval();
},
watch: {
running: function(val) {
val ? this.startInterval() : this.stopInterval();
},
currentIndex: function(index) {
if (this.data && this.data[index]) {
let currentTextInfo = this.data ? this.data[index] : {};
let wall_time = currentTextInfo[0];
let step = currentTextInfo[1];
let message = currentTextInfo[2];
this.textData = {
step,
wall_time,
message,
}; };
}
},
},
methods: {
stopInterval() {
clearInterval(this.getOringDataInterval);
}, },
created() { // get origin data per {{intervalTime}} seconds
startInterval() {
this.getOringDataInterval = setInterval(() => {
this.getOriginChartsData(); this.getOriginChartsData();
}, intervalTime * 1000);
}, },
mounted() { getOriginChartsData() {
if (this.running) { // let {run, tag} = this.tagInfo;
this.startInterval(); let run = this.tagInfo.run;
let tag = this.tagInfo.tag;
let {displayName, samples} = tag;
let params = {
run,
tag: displayName,
samples,
};
getPluginTextsTexts(params).then(({status, data}) => {
if (status === 0) {
this.data = data;
this.currentIndex = data.length - 1;
} }
});
}, },
},
beforeDestroy() {
this.stopInterval();
},
watch: {
running: function(val) {
val ? this.startInterval() : this.stopInterval();
},
currentIndex: function(index) {
if (this.data && this.data[index]) {
let currentTextInfo = this.data ? this.data[index] : {};
let wall_time = currentTextInfo[0];
let step = currentTextInfo[1];
let message = currentTextInfo[2];
this.textData = {
step,
wall_time,
message,
};
}
},
},
methods: {
stopInterval() {
clearInterval(this.getOringDataInterval);
},
// get origin data per {{intervalTime}} seconds
startInterval() {
this.getOringDataInterval = setInterval(() => {
this.getOriginChartsData();
}, intervalTime * 1000);
},
getOriginChartsData() {
// let {run, tag} = this.tagInfo;
let run = this.tagInfo.run;
let tag = this.tagInfo.tag;
let {displayName, samples} = tag;
let params = {
run,
tag: displayName,
samples,
};
getPluginTextsTexts(params).then(({status, data}) => {
if (status === 0) {
this.data = data;
this.currentIndex = data.length - 1;
}
});
},
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -31,40 +31,40 @@ import Chart from './Chart'; ...@@ -31,40 +31,40 @@ import Chart from './Chart';
import {cloneDeep, flatten} from 'lodash'; import {cloneDeep, flatten} from 'lodash';
export default { export default {
components: { components: {
'ui-chart': Chart, 'ui-chart': Chart,
'ui-expand-panel': ExpandPanel, 'ui-expand-panel': ExpandPanel,
},
props: ['config', 'runsItems', 'tagList', 'title'],
computed: {
filteredRunsList() {
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return flatten(list.slice().map((item) => {
return item.tagList.filter((one) => runs.includes(one.run));
}));
}, },
props: ['config', 'runsItems', 'tagList', 'title'], filteredPageList() {
computed: { let list = this.filteredRunsList || [];
filteredRunsList() { return list.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize);
let tagList = this.tagList || [];
let runs = this.config.runs || [];
let list = cloneDeep(tagList);
return flatten(list.slice().map((item) => {
return item.tagList.filter((one) => runs.includes(one.run));
}));
},
filteredPageList() {
let list = this.filteredRunsList || [];
return list.slice((this.currentPage - 1) * this.pageSize, this.currentPage * this.pageSize);
},
total() {
let tagList = this.tagList || [];
return tagList.length;
},
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
}, },
data() { total() {
return { let tagList = this.tagList || [];
// current page return tagList.length;
currentPage: 1,
// item per page
pageSize: 8,
};
}, },
pageLength() {
return Math.ceil(this.total / this.pageSize);
},
},
data() {
return {
// current page
currentPage: 1,
// item per page
pageSize: 8,
};
},
}; };
</script> </script>
<style lang="stylus"> <style lang="stylus">
......
...@@ -34,19 +34,19 @@ ...@@ -34,19 +34,19 @@
<script> <script>
export default { export default {
props: { props: {
runsItems: Array, runsItems: Array,
config: Object, config: Object,
}, },
data() { data() {
return { return {
}; };
}, },
methods: { methods: {
toggleAllRuns() { toggleAllRuns() {
this.config.running = !this.config.running; this.config.running = !this.config.running;
},
}, },
},
}; };
</script> </script>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册