diff --git a/README.md b/README.md
index 721aa7ee114223bdf7f4b91e65aeb10bbfc19069..37cdaed11c432976fe8065ecaa29e157376e3511 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ cp -r frontend/dist server/visualdl/frontend/dist
#### Step 3: build and install Python package
```shell
cd server/
-python setup.py bdist_wheel
+sh build.sh
cd dist
sudo pip install --upgrade visualdl-0.0.1-py2-none-any.whl
```
diff --git a/frontend/mock/data/plugin/images/images.js b/frontend/mock/data/plugin/images/images.js
new file mode 100644
index 0000000000000000000000000000000000000000..98ee5bc9531160294bf73ffc368b66dd2b571dce
--- /dev/null
+++ b/frontend/mock/data/plugin/images/images.js
@@ -0,0 +1,42 @@
+/**
+ * get mock data
+ *
+ * @param {string} path request path
+ * @param {Object} queryParam query params
+ * @param {Object} postParam post params
+ * @return {Object}
+ */
+module.exports = function (path, queryParam, postParam) {
+ return {
+ // moock delay
+ _timeout: 0,
+ // mock http status
+ _status: 200,
+ // mock response data
+ _data: {
+ status: 0,
+ msg: 'SUCCESS',
+ data: [
+ {
+ "wall_time": 1512549785.061623,
+ "step": 60,
+ "query":
+ "sample=0&index=0&tag=input_reshape%2Finput%2Fimage%2F0&run=test",
+ "width": 28,
+ "height": 28
+ },
+ {"wall_time": 1512886109.672786, "step": 60, "query": "sample=0&index=1&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
+ {"wall_time": 1512886124.266915, "step": 210, "query": "sample=0&index=2&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
+ {"wall_time": 1512886138.898628, "step": 330, "query": "sample=0&index=3&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
+ {"wall_time": 1512886139.883663, "step": 340, "query": "sample=0&index=4&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
+ {"wall_time": 1512886147.195567, "step": 410, "query": "sample=0&index=5&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
+ {"wall_time": 1512886156.47856, "step": 500, "query": "sample=0&index=6&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
+ {"wall_time": 1512886187.82793, "step": 810, "query": "sample=0&index=7&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
+ {"wall_time": 1512886200.386198, "step": 950, "query": "sample=0&index=8&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28},
+ {"wall_time": 1512886204.224405, "step": 990, "query": "sample=0&index=9&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}
+ ]
+ }
+ };
+};
+
+
diff --git a/frontend/mock/data/plugin/images/individualImage.js b/frontend/mock/data/plugin/images/individualImage.js
new file mode 100644
index 0000000000000000000000000000000000000000..c284c9ddfd2baad62afed489dfc5e09b23945328
--- /dev/null
+++ b/frontend/mock/data/plugin/images/individualImage.js
@@ -0,0 +1,22 @@
+/**
+ * get mock data
+ *
+ * @param {string} path request path
+ * @param {Object} queryParam query params
+ * @param {Object} postParam post params
+ * @return {Object}
+ */
+module.exports = function (path, queryParam, postParam) {
+ return {
+ // moock delay
+ _timeout: 0,
+ // mock http status
+ _status: 200,
+ // mock response data
+ _data: {
+ status: 0,
+ msg: 'SUCCESS',
+ data: ''
+ }
+ };
+};
diff --git a/frontend/mock/data/plugin/images/tags.js b/frontend/mock/data/plugin/images/tags.js
new file mode 100644
index 0000000000000000000000000000000000000000..bf0b3acf59b82d9bf9a6f020c30755bfc6b7f74a
--- /dev/null
+++ b/frontend/mock/data/plugin/images/tags.js
@@ -0,0 +1,25 @@
+/**
+ * get mock data
+ *
+ * @param {string} path request path
+ * @param {Object} queryParam query params
+ * @param {Object} postParam post params
+ * @return {Object}
+ */
+ module.exports = function (path, queryParam, postParam) {
+ return {
+ // moock delay
+ _timeout: 0,
+ // mock http status
+ _status: 200,
+ // mock response data
+ _data: {
+ status: 0,
+ msg: 'SUCCESS',
+ data: {
+ "test": {
+ "input_reshape/input/image/6": {
+ "displayName": "input_reshape/input/image/6", "description": "", "samples": 1}, "input_reshape/input/image/7": {"displayName": "input_reshape/input/image/7", "description": "", "samples": 1}, "input_reshape/input/image/4": {"displayName": "input_reshape/input/image/4", "description": "", "samples": 1}, "input_reshape/input/image/5": {"displayName": "input_reshape/input/image/5", "description": "", "samples": 1}, "input_reshape/input/image/2": {"displayName": "input_reshape/input/image/2", "description": "", "samples": 1}, "input_reshape/input/image/3": {"displayName": "input_reshape/input/image/3", "description": "", "samples": 1}, "input_reshape/input/image/0": {"displayName": "input_reshape/input/image/0", "description": "", "samples": 1}, "input_reshape/input/image/1": {"displayName": "input_reshape/input/image/1", "description": "", "samples": 1}, "input_reshape/input/image/8": {"displayName": "input_reshape/input/image/8", "description": "", "samples": 1}, "input_reshape/input/image/9": {"displayName": "input_reshape/input/image/9", "description": "", "samples": 1}}, "train": {"input_reshape/input/image/6": {"displayName": "input_reshape/input/image/6", "description": "", "samples": 1}, "input_reshape/input/image/7": {"displayName": "input_reshape/input/image/7", "description": "", "samples": 1}, "input_reshape/input/image/4": {"displayName": "input_reshape/input/image/4", "description": "", "samples": 1}, "input_reshape/input/image/5": {"displayName": "input_reshape/input/image/5", "description": "", "samples": 1}, "input_reshape/input/image/2": {"displayName": "input_reshape/input/image/2", "description": "", "samples": 1}, "input_reshape/input/image/3": {"displayName": "input_reshape/input/image/3", "description": "", "samples": 1}, "input_reshape/input/image/0": {"displayName": "input_reshape/input/image/0", "description": "", "samples": 1}, "input_reshape/input/image/1": {"displayName": "input_reshape/input/image/1", "description": "", "samples": 1}, "input_reshape/input/image/8": {"displayName": "input_reshape/input/image/8", "description": "", "samples": 1}, "input_reshape/input/image/9": {"displayName": "input_reshape/input/image/9", "description": "", "samples": 1}}}
+ }
+ };
+};
diff --git a/frontend/mock/data/runs.js b/frontend/mock/data/runs.js
index 4e9854de65a0a6026d55fdd2c3b8234dacb3354c..1a7897bc5266a1d5fb1936c1bf32b6e1753f2a13 100644
--- a/frontend/mock/data/runs.js
+++ b/frontend/mock/data/runs.js
@@ -16,7 +16,7 @@ module.exports = function (path, queryParam, postParam) {
_data: {
status: 0,
msg: 'SUCCESS',
- data: ["train", "test", "model"]
+ data: ["train", "test"]
}
};
};
diff --git a/frontend/package.json b/frontend/package.json
index 7297c4fccc410b815795f4f6767fb40588adc6b6..c131147fce20094c2ccee76968e1b27506ef3b9b 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,9 +6,7 @@
"release": "cross-env NODE_ENV=production node ./tool/build.js",
"build": "cross-env NODE_ENV=dev node ./tool/build.js",
"dev": "cross-env NODE_ENV=dev node tool/dev-server.js",
- "lint": "./node_modules/fecs/bin/fecs --rule",
- "precommit": "npm run lint",
- "prepush": "npm run lint"
+ "lint": "./node_modules/fecs/bin/fecs --rule"
},
"engines": {
"node": ">= 6.4.0"
@@ -52,7 +50,6 @@
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.4",
- "husky": "^0.14.3",
"json-loader": "^0.5.4",
"opn": "^5.1.0",
"optimize-css-assets-webpack-plugin": "^1.3.2",
diff --git a/frontend/src/common/component/AppMenu.san b/frontend/src/common/component/AppMenu.san
index df93520329b4d0610bb639288cb9fa06d6cabfc9..a89279c97d018ccea216d710ee154197114dadd2 100644
--- a/frontend/src/common/component/AppMenu.san
+++ b/frontend/src/common/component/AppMenu.san
@@ -32,7 +32,7 @@ export default {
},
{
value: '2',
- url: '/image',
+ url: '/images',
title: 'IMAGES'
}
]
diff --git a/frontend/src/common/component/Charts/chart.san b/frontend/src/common/component/Charts/chart.san
index 9aad89535359f8ea4165d968a29ff0e00e78aaa9..98f4eb0b0803a9b69377e258526dbd4ec5eb4a5a 100644
--- a/frontend/src/common/component/Charts/chart.san
+++ b/frontend/src/common/component/Charts/chart.san
@@ -3,26 +3,52 @@
+
+ settings_overscan
+
-
- file_download
+
+ file_download
diff --git a/frontend/src/common/component/Charts/image.san b/frontend/src/common/component/Charts/image.san
new file mode 100644
index 0000000000000000000000000000000000000000..03288b0c85daee659e9c7330fc44a7a40276c8c0
--- /dev/null
+++ b/frontend/src/common/component/Charts/image.san
@@ -0,0 +1,101 @@
+
+
+
{{tagInfo.tag.displayName}}
+ {{tagInfo.run}}
+
+
+ Step:
+ {{imgData.step}};
+ {{imgData.wall_time | formatTime}}
+
+
+

+
+
+
+
+
diff --git a/frontend/src/common/component/ExpandPanel.san b/frontend/src/common/component/ExpandPanel.san
index 243e43296db59c6814af72791f4e051c88397475..0ddf3f648545119dc3fa962ad07c84a2872f2c80 100644
--- a/frontend/src/common/component/ExpandPanel.san
+++ b/frontend/src/common/component/ExpandPanel.san
@@ -5,9 +5,13 @@
on-click="handleHeadClick()"
>
{{title}}
+
+ {{iconName}}
+ ({{info}})
+
@@ -15,11 +19,15 @@
@@ -53,7 +64,7 @@ export default {
margin-right 20px
margin-top 4px
float left
- width 100px
+ width 160px
.sm-slider-thumb
transform translate(0, -50%)
.sm-inputNumber
diff --git a/frontend/src/common/component/ui-common.styl b/frontend/src/common/component/ui-common.styl
index 9384719dd62a022d7df263c09f8f11ea205cd171..9266923a6c32687d93792e99190d0dc9468ff692 100644
--- a/frontend/src/common/component/ui-common.styl
+++ b/frontend/src/common/component/ui-common.styl
@@ -1,3 +1,7 @@
+html {
+ font-family: "RobotoDraft", "Roboto", sans-serif;
+}
+
// initual style
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; } body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; } h1, h2, h3, h4, h5, h6{ font-size:100%; } address, cite, dfn, em, var { font-style:normal; } code, kbd, pre, samp { font-family:couriernew, courier, monospace; } small{ font-size:12px; } ul, ol { list-style:none; } a { text-decoration:none; } a:hover { text-decoration:underline; } sup { vertical-align:text-top; } sub{ vertical-align:text-bottom; } legend { color:#000; } fieldset, img { border:0; } button, input, select, textarea { font-size:100%; } table { border-collapse:collapse; border-spacing:0; }
@@ -5,8 +9,8 @@ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fo
// modify some styles of san-mui
.sm-icon
- width 100%
- height 100%
+ width 20px
+ height 20px
overflow hidden
.sm-radio,
@@ -52,7 +56,22 @@ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fo
color #58666e
.sm-dropdown-menu
+ .sm-text-field
+ margin-bottom 0px
+ min-height 30px
+ border-bottom solid 1px #e4e4e4
+
+ .sm-text-field-content
+ padding 0
+ padding-left 4px
+
+ input
+ cursor pointer
+
.sm-dropdown-menu-icon
+ top 6px
+ right 0
+ bottom 0
.sm-icon
color #58666e
@@ -64,12 +83,14 @@ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fo
.sm-menu-item.state-selected
color #ff4081
-
+.sm-text-field
+ width 100%
.sm-text-field-input
color #58666e
.sm-form-item
margin-top 10px
+ overflow hidden
.label
color rgba(0,0,0,0.54)
diff --git a/frontend/src/common/util/http.js b/frontend/src/common/util/http.js
index 9f77fdedd1ca18b6009301488ef695d424729c8f..3d62938635a8d57bf7f06b619025aed426192e4e 100644
--- a/frontend/src/common/util/http.js
+++ b/frontend/src/common/util/http.js
@@ -1,6 +1,6 @@
import axios from 'axios';
import qs from 'qs';
-import Notification from '../ui/Notification';
+import Notification from '../component/Notification';
const STATUS = 'status';
const STATUSINFO = 'msg';
diff --git a/frontend/src/common/util/index.js b/frontend/src/common/util/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..4f0efd2c205b4f6dd5bd7c2335d6af6a7c638a3f
--- /dev/null
+++ b/frontend/src/common/util/index.js
@@ -0,0 +1,5 @@
+import quantile from './quantile';
+
+export {
+ quantile
+};
diff --git a/frontend/src/common/util/number.js b/frontend/src/common/util/number.js
new file mode 100644
index 0000000000000000000000000000000000000000..0adc69d8b0c90e3385945393acae53b69f1fb1bc
--- /dev/null
+++ b/frontend/src/common/util/number.js
@@ -0,0 +1,3 @@
+export default function (x) {
+ return x === null ? NaN : +x;
+}
diff --git a/frontend/src/common/util/quantile.js b/frontend/src/common/util/quantile.js
new file mode 100644
index 0000000000000000000000000000000000000000..11db42b7d591eccdb02df04bbada8d08fa6d7b55
--- /dev/null
+++ b/frontend/src/common/util/quantile.js
@@ -0,0 +1,21 @@
+import number from './number';
+export default function (values, p, valueof) {
+ if (valueof == null) {
+ return valueof = number;
+ }
+ let n = values.length;
+ if (!(n)) {
+ return;
+ }
+ if ((p = +p) <= 0 || n < 2) {
+ return +valueof(values[0], 0, values);
+ }
+ if (p >= 1) {
+ return +valueof(values[n - 1], n - 1, values);
+ }
+ let i = (n - 1) * p;
+ let i0 = Math.floor(i);
+ let value0 = +valueof(values[i0], i0, values);
+ let value1 = +valueof(values[i0 + 1], i0 + 1, values);
+ return value0 + (value1 - value0) * (i - i0);
+}
diff --git a/frontend/src/images/Images.san b/frontend/src/images/Images.san
new file mode 100644
index 0000000000000000000000000000000000000000..fab509cf14ebc2701be636dba099b3f632301286
--- /dev/null
+++ b/frontend/src/images/Images.san
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/images/index.js b/frontend/src/images/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..01d1fd72d5686124edd7042ee4d52c50c4a5f0a1
--- /dev/null
+++ b/frontend/src/images/index.js
@@ -0,0 +1,9 @@
+import {router} from 'san-router';
+
+import Images from './Images';
+
+router.add({
+ target: '#content',
+ rule: '/images',
+ Component: Images
+});
diff --git a/frontend/src/images/ui/chartPage.san b/frontend/src/images/ui/chartPage.san
new file mode 100644
index 0000000000000000000000000000000000000000..19ddfb157925f1f9dd00396303ff74da74293e6e
--- /dev/null
+++ b/frontend/src/images/ui/chartPage.san
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/images/ui/config.san b/frontend/src/images/ui/config.san
new file mode 100644
index 0000000000000000000000000000000000000000..0cfdb4cb53332f066b97802d953ecaa7b8335fa8
--- /dev/null
+++ b/frontend/src/images/ui/config.san
@@ -0,0 +1,55 @@
+
+
+
+
+
+ Toggle All Runs
+
+
+
+
diff --git a/frontend/src/index.js b/frontend/src/index.js
index 4c2921d0a70d2bfeb3b31ddcff191a4d0d13a969..99656cf8bb6b292748cc6c89ced167eb406f6846 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.js
@@ -4,6 +4,7 @@ import './common/component/ui-common.styl';
import './home/index';
import './scalars/index';
+import './images/index';
import App from './App';
new App({
diff --git a/frontend/src/scalars/Scalars.san b/frontend/src/scalars/Scalars.san
index 7c907ce9b4b7b548a950847f54b8571b1388dad7..bdee9d625fc164ca1233e97f6b36c2d367528dba 100644
--- a/frontend/src/scalars/Scalars.san
+++ b/frontend/src/scalars/Scalars.san
@@ -10,14 +10,14 @@
{
+ let val = config[key];
+ if (tansformArr.indexOf(key) > -1) {
+ filteredConfig[key] = isArray(val) && val[0] === 'yes';
+ }
+ else {
+ filteredConfig[key] = val;
+ }
+ });
+ return filteredConfig;
}
},
initData() {
@@ -99,11 +114,13 @@ export default {
tags: [],
config: {
groupNameReg: '.*',
- smoothing: '0.5',
- horizontal: '1',
- sortingMethod: '2',
- link: [],
- chart: []
+ smoothing: 0.6,
+ horizontal: 'step',
+ sortingMethod: 'default',
+ downloadLink: [],
+ outlier: [],
+ runs: [],
+ running: true
}
};
},
@@ -117,6 +134,7 @@ export default {
});
getRuns().then(({errno, data}) => {
this.data.set('runsArray', data);
+ this.data.set('config.runs', data);
});
// need debounce, can't use computed
diff --git a/frontend/src/scalars/ui/chartPage.san b/frontend/src/scalars/ui/chartPage.san
index 3272ea0c081a5bd909ad3bc0cc82ccec32bdb418..96ca0a5c97b9bb6ead6cd352cf86d4e9c3bea338 100644
--- a/frontend/src/scalars/ui/chartPage.san
+++ b/frontend/src/scalars/ui/chartPage.san
@@ -1,11 +1,18 @@
-
-
-
+
+
+
@@ -61,11 +68,11 @@ export default {
@import '../../style/variables';
+prefix-classes('visual-dl-')
- .visaul-dl-chart-page
- .visaul-dl-chart-box:after
- content: "";
- clear: both;
- display: block;
+ .chart-page
+ .chart-box:after
+ content: "";
+ clear: both;
+ display: block;
diff --git a/frontend/src/scalars/ui/config.san b/frontend/src/scalars/ui/config.san
index cd67cdf93b559786eb57efe809915cc369284db0..19af47ee08bb9c8d77076700ea3eb190befe8642 100644
--- a/frontend/src/scalars/ui/config.san
+++ b/frontend/src/scalars/ui/config.san
@@ -9,12 +9,12 @@
label="Smoothing"
value="{=config.smoothing=}"
min="{{0}}"
- max="{{1}}"
+ max="{{0.999}}"
step="{{0.001}}"
/>
- Toggle All Runs
+
+ {{config.running ? 'Running' : 'Stopped'}}
+
diff --git a/frontend/src/service.js b/frontend/src/service.js
index e30dfbf3355167d1fae3a48359b1c13e176a096c..d759f32b4e5c9bfc395013b473c7431b0218a816 100644
--- a/frontend/src/service.js
+++ b/frontend/src/service.js
@@ -5,3 +5,7 @@ export const getPluginScalarsTags = makeService('/data/plugin/scalars/tags');
export const getRuns = makeService('/data/runs');
export const getPluginScalarsScalars = makeService('/data/plugin/scalars/scalars');
+
+export const getPluginImagesTags = makeService('/data/plugin/images/tags');
+
+export const getPluginImagesImages = makeService('/data/plugin/images/images');
diff --git a/frontend/tool/HtmlReplacePlugin.js b/frontend/tool/HtmlReplacePlugin.js
index ce67a520d36f6c7d0fa0fe61e9cee42241cec5f6..31acce6c1e436f51c390bc486b5bedef2a7d6492 100644
--- a/frontend/tool/HtmlReplacePlugin.js
+++ b/frontend/tool/HtmlReplacePlugin.js
@@ -1,3 +1,4 @@
+'use strict';
function noopReplace (val) { return val; }
function HtmlReplacePlugin(options) {
diff --git a/frontend/tool/build.js b/frontend/tool/build.js
index d8a67b1f106f82df771200a8c6bb096c441d77cc..3cfaf7cc1b5a81a1cafc8b3c828540eafe3942a9 100644
--- a/frontend/tool/build.js
+++ b/frontend/tool/build.js
@@ -1,3 +1,4 @@
+'use strict';
const webpack = require('webpack');
const rm = require('rimraf');
const ora = require('ora');
diff --git a/frontend/tool/dev-client.js b/frontend/tool/dev-client.js
index d882b7bf83be35b3fca65f4d790c7451120e35c9..61627601e3a46c71554ba1c2f94dbea798d0a9cd 100644
--- a/frontend/tool/dev-client.js
+++ b/frontend/tool/dev-client.js
@@ -1,3 +1,4 @@
+'use strict';
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
diff --git a/frontend/tool/dev-server.js b/frontend/tool/dev-server.js
index 98299e0c2632d7b0633a5685525e63fe424d9ea1..feed66fb507a1860b349d36d6c5615d044b2b67a 100644
--- a/frontend/tool/dev-server.js
+++ b/frontend/tool/dev-server.js
@@ -1,3 +1,4 @@
+'use strict';
process.env.NODE_ENV = 'dev';
let devPort = 8999;
let opn = require('opn');
diff --git a/frontend/tool/entry.js b/frontend/tool/entry.js
index 78754c6a5dc394ade713c1522657690d9be3b3fc..f19b4630a06662757d1eb34e930048ace2623cf6 100644
--- a/frontend/tool/entry.js
+++ b/frontend/tool/entry.js
@@ -1,3 +1,4 @@
+'use strict';
const path = require('path');
const projectPath = path.resolve(__dirname, '..');
const HtmlWebpackPlugin = require('html-webpack-plugin');
diff --git a/frontend/tool/webpack.config.js b/frontend/tool/webpack.config.js
index 24764208c456bb784655b5934e7c272782abf101..ae2862a0a93abee232ea94946053c174d2ee9b59 100644
--- a/frontend/tool/webpack.config.js
+++ b/frontend/tool/webpack.config.js
@@ -1,3 +1,4 @@
+'use strict';
const webpack = require('webpack');
const path = require('path');
const projectPath = path.resolve(__dirname, '..');
diff --git a/frontend/tool/webpack.dev.config.js b/frontend/tool/webpack.dev.config.js
index 5b16958fafa422373ac0ba00075de5616d4d97ca..334304e4e06c4d112f8214e9b0b9d4222b86edf2 100644
--- a/frontend/tool/webpack.dev.config.js
+++ b/frontend/tool/webpack.dev.config.js
@@ -1,3 +1,4 @@
+'use strict';
const webpack = require('webpack');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
let merge = require('webpack-merge');
diff --git a/frontend/tool/webpack.prod.config.js b/frontend/tool/webpack.prod.config.js
index fa73c7837ef2a7ea12803ba5d6885f01520bbc8b..7c76cff713e00e38222f4b9764729c054b334b60 100644
--- a/frontend/tool/webpack.prod.config.js
+++ b/frontend/tool/webpack.prod.config.js
@@ -1,3 +1,4 @@
+'use strict';
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
diff --git a/server/build.sh b/server/build.sh
new file mode 100644
index 0000000000000000000000000000000000000000..c9c490768e44e3faa525f9f58ff68e1ff9d4f9a3
--- /dev/null
+++ b/server/build.sh
@@ -0,0 +1,7 @@
+script=$(readlink -f "$0")
+script_path=$(dirname "$script")
+
+pushd $script_path
+ protoc visualdl/onnx/onnx.proto --python_out .
+ python setup.py bdist_wheel
+popd
diff --git a/server/setup.py b/server/setup.py
index 6b8c71ed23929662725d683af708a20e10b5eb10..b20a62b86891819e50e76bcb561d6ac4f4f0e829 100644
--- a/server/setup.py
+++ b/server/setup.py
@@ -1,6 +1,9 @@
from setuptools import setup
-packages = ['visualdl', 'visualdl.frontend.dist', 'visualdl.mock']
+packages = ['visualdl',
+ 'visualdl.onnx',
+ 'visualdl.mock',
+ 'visualdl.frontend.dist']
setup(
name="visualdl",
diff --git a/server/visualdl/graph.py b/server/visualdl/graph.py
new file mode 100644
index 0000000000000000000000000000000000000000..e9a996bc8edcb06699440fba1f543629265d3428
--- /dev/null
+++ b/server/visualdl/graph.py
@@ -0,0 +1,49 @@
+import onnx
+import json
+from google.protobuf.json_format import MessageToJson
+
+
+def reorganize_inout(json_obj, key):
+ """
+ :param json_obj: the model's json obj
+ :param key: "input or output"
+ :return:
+ """
+ for index in range(len(json_obj[key])):
+ var = json_obj[key][index]
+ var_new = dict()
+
+ # set name
+ var_new['name'] = var['name']
+
+ tensor_type = var['type']['tensorType']
+
+ # set data_type
+ var_new['data_type'] = tensor_type['elemType']
+
+ # set shape
+ shape = [dim['dimValue'] for dim in tensor_type['shape']['dim']]
+ var_new['shape'] = shape
+
+ json_obj[key][index] = var_new
+
+
+def load_model(model_pb_path):
+ model = onnx.load(model_pb_path)
+ graph = model.graph
+ del graph.initializer[:]
+
+ # to json string
+ json_str = MessageToJson(model.graph)
+ json_obj = json.loads(json_str)
+ reorganize_inout(json_obj, 'input')
+ reorganize_inout(json_obj, 'output')
+ return json.dumps(json_obj, sort_keys=True, indent=4, separators=(',', ': '))
+
+
+if __name__ == '__main__':
+ import os
+ import sys
+ current_path = os.path.abspath(os.path.dirname(sys.argv[0]))
+ json_str = load_model(current_path + "/mock/inception_v1.pb")
+ print(json_str)
diff --git a/server/visualdl/onnx/READEME.md b/server/visualdl/onnx/READEME.md
new file mode 100644
index 0000000000000000000000000000000000000000..22e92699745dba7db39332261032a80ebf95727d
--- /dev/null
+++ b/server/visualdl/onnx/READEME.md
@@ -0,0 +1,2 @@
+## Note
+the onnx.proto is port from onnx commit c3137751923f576d14b4212ea444d1562b127a66
diff --git a/server/visualdl/onnx/__init__.py b/server/visualdl/onnx/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c77f8f6a5dbcc5981d15c06de215b7dffb8216b
--- /dev/null
+++ b/server/visualdl/onnx/__init__.py
@@ -0,0 +1,43 @@
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import google.protobuf.message
+
+from .onnx_pb2 import ModelProto
+
+
+def load(obj):
+ '''
+ Loads a binary protobuf that stores onnx model
+
+ @params
+ Takes a file-like object (has "read" function)
+ or a string containing a file name
+ @return ONNX ModelProto object
+ '''
+ if hasattr(obj, 'read') and callable(obj.read):
+ s = obj.read()
+ else:
+ with open(obj, 'rb') as f:
+ s = f.read()
+ return load_from_string(s)
+
+
+def load_from_string(s):
+ '''
+ Loads a binary string that stores onnx model
+
+ @params
+ Takes a string object containing protobuf
+ @return ONNX ModelProto object
+ '''
+ model = ModelProto()
+ decoded = model.ParseFromString(s)
+ # in python implementation ParseFromString returns None
+ if decoded is not None and decoded != len(s):
+ raise google.protobuf.message.DecodeError(
+ "Protobuf decoding consumed too few bytes: {} out of {}".format(
+ decoded, len(s)))
+ return model
diff --git a/server/visualdl/onnx/onnx.proto b/server/visualdl/onnx/onnx.proto
new file mode 100644
index 0000000000000000000000000000000000000000..3a8bb9b66604c45d016fd05f0419a08c40d2d04c
--- /dev/null
+++ b/server/visualdl/onnx/onnx.proto
@@ -0,0 +1,420 @@
+//
+// WARNING: This file is automatically generated! Please edit onnx.in.proto.
+//
+
+
+// Copyright (c) Facebook Inc. and Microsoft Corporation.
+// Licensed under the MIT license.
+
+syntax = "proto2";
+
+package onnx;
+
+// Note [Release]
+// We are still in the very early stage of defining ONNX. The current
+// version of ONNX is a starting point. While we are actively working
+// towards a complete spec, we would like to get the community involved
+// by sharing our working version of ONNX.
+
+// Note [Protobuf compatibility]
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Based on experience working with downstream vendors, we generally can't
+// assume recent versions of protobufs. This means that we do not use any
+// protobuf features that are only available in proto3.
+//
+// Here are the most notable contortions we have to carry out to work around
+// these limitations:
+//
+// - No 'map' (added protobuf 3.0). We instead represent mappings as lists
+// of key-value pairs, where order does not matter and duplicates
+// are not allowed.
+
+// Note [Namespaces]
+// ~~~~~~~~~~~~~~~~~
+// ONNX gives explicit names to graphs, intermediate values and
+// serialized tensors. To make it easier to generate names, we organize
+// these into separate namespaces (so, e.g., a graph can have the same
+// name as a serialized tensor.) The namespaces are as follows:
+//
+// - Node: These names identify specific nodes in the graph (but not, necessarily
+// any particular input or output of the node.
+// - Graph: These names identify graphs in the protobuf.
+// - Attribute: These names identify attribute names for extra attributes that
+// are passed to operators.
+// - Operator: These names identify particular operators.
+// - Value: These names identify intermediate values (typically tensors) flowing through
+// the computation of a graph.
+// - Shape: These names represent parameters for unknown shape dimensions.
+//
+// We specify the namespace of a name in ONNX as comments in the form
+// of "namespace {Node,Graph,Operator,Attribute,Value,Shape}". Framework is responsible
+// for supporting the namespaces.
+//
+// Naming things is hard. Every element with a name has an optional doc_string associated
+// with it, providing a human-readable description in text markdown.
+
+// To be compatible with both proto2 and proto3, we will use a version number
+// that is not defined by the default value but an explicit enum number.
+enum Version {
+ // proto3 requires the first enum value to be zero.
+ // We add this just to appease the compiler.
+ _START_VERSION = 0;
+ // The version field is always serialized and we will use it to store the
+ // version that the graph is generated from. This helps us set up version
+ // control. We should use version as
+ // xx(major) - xx(minor) - xxxx(bugfix)
+ // and we are starting with 0x00000001 (0.0.1), which was the
+ // version we published on Oct 10, 2017.
+ IR_VERSION_2017_10_10 = 0x00000001;
+
+ // IR_VERSION 0.0.2 published on Oct 30, 2017
+ // - Added type discriminator to AttributeProto to support proto3 users
+ IR_VERSION_2017_10_30 = 0x00000002;
+
+ // IR VERSION 0.0.3 published on Nov 3, 2017
+ // - For operator versioning:
+ // - Added new message OperatorSetIdProto
+ // - Added opset_import in ModelProto
+ // - For vendor extensions, added domain in NodeProto
+ IR_VERSION = 0x00000003;
+}
+
+// A named attribute containing either singular float, integer, string
+// and tensor values, or repeated float, integer, string and tensor values.
+// An AttributeProto MUST contain the name field, and *only one* of the
+// following content fields, effectively enforcing a C/C++ union equivalent.
+message AttributeProto {
+
+ // Note: this enum is structurally identical to the OpSchema::AttrType
+ // enum defined in schema.h. If you rev one, you likely need to rev the other.
+ enum AttributeType {
+ UNDEFINED = 0;
+ FLOAT = 1;
+ INT = 2;
+ STRING = 3;
+ TENSOR = 4;
+ GRAPH = 5;
+
+ FLOATS = 6;
+ INTS = 7;
+ STRINGS = 8;
+ TENSORS = 9;
+ GRAPHS = 10;
+ }
+
+ // The name field MUST be present for this version of the IR.
+ optional string name = 1; // namespace Attribute
+
+ // A human-readable documentation for this attribute. Markdown is allowed.
+ optional string doc_string = 13;
+
+ // The type field MUST be present for this version of the IR.
+ // For 0.0.1 versions of the IR, this field was not defined, and
+ // implementations needed to use has_field hueristics to determine
+ // which value field was in use. For IR_VERSION 0.0.2 or later, this
+ // field MUST be set and match the f|i|s|t|... field in use. This
+ // change was made to accomodate proto3 implementations.
+ optional AttributeType type = 20; // discriminator that indicates which field below is in use
+
+ // Exactly ONE of the following fields must be present for this version of the IR
+ optional float f = 2; // float
+ optional int64 i = 3; // int
+ optional bytes s = 4; // UTF-8 string
+ optional TensorProto t = 5; // tensor value
+ optional GraphProto g = 6; // graph
+ // Do not use field below, it's deprecated.
+ // optional ValueProto v = 12; // value - subsumes everything but graph
+
+ repeated float floats = 7; // list of floats
+ repeated int64 ints = 8; // list of ints
+ repeated bytes strings = 9; // list of UTF-8 strings
+ repeated TensorProto tensors = 10; // list of tensors
+ repeated GraphProto graphs = 11; // list of graph
+}
+
+// Defines information on value, including the name, the type, and
+// the shape of the value.
+message ValueInfoProto {
+ // This field MUST be present in this version of the IR.
+ optional string name = 1; // namespace Value
+ // This field MUST be present in this version of the IR.
+ optional TypeProto type = 2;
+ // A human-readable documentation for this value. Markdown is allowed.
+ optional string doc_string = 3;
+}
+
+// NodeProto stores a node that is similar to the notion of "layer"
+// or "operator" in many deep learning frameworks. For example, it can be a
+// node of type "Conv" that takes in an image, a filter tensor and a bias
+// tensor, and produces the convolved output.
+message NodeProto {
+ repeated string input = 1; // namespace Value
+ repeated string output = 2; // namespace Value
+
+ // An optional identifier for this node in a graph.
+ // This field MAY be absent in ths version of the IR.
+ optional string name = 3; // namespace Node
+
+ // The symbolic identifier of the Operator to execute.
+ optional string op_type = 4; // namespace Operator
+ // The domain of the OperatorSet that specifies the operator named by op_type.
+ optional string domain = 7; // namespace Domain
+
+ // Additional named attributes.
+ // NOTE: Simply using ValueProto.NameValuePairProto is the most general
+ // solution. I kept AttributeProto to minimize churn on CI results.
+ repeated AttributeProto attribute = 5;
+
+ // A human-readable documentation for this node. Markdown is allowed.
+ optional string doc_string = 6;
+}
+
+// ModelProto is a top-level file/container format for bundling a ML model.
+// The semantics of the model are described by the GraphProto that represents
+// a parameterized computation graph against a set of named operators that are
+// defined independently from the graph.
+message ModelProto {
+ // The version of the IR this model targets. See Version enum above.
+ // This field MUST be present.
+ optional int64 ir_version = 1;
+
+ // The OperatorSets this model relies on.
+ // All ModelProtos MUST have at least one entry that
+ // specifies which version of the ONNX OperatorSet is
+ // being imported.
+ //
+ // All nodes in the ModelProto's graph will bind against the operator
+ // with the same-domain/same-op_type operator with the HIGHEST version
+ // in the referenced operator sets.
+ repeated OperatorSetIdProto opset_import = 8;
+
+ // The name of the framework or tool used to generate this model.
+ // This field SHOULD be present to indicate which implementation/tool/framework
+ // emitted the model.
+ optional string producer_name = 2;
+
+ // The version of the framework or tool used to generate this model.
+ // This field SHOULD be present to indicate which implementation/tool/framework
+ // emitted the model.
+ optional string producer_version = 3;
+
+ // Domain name of the model.
+ // We use reverse domain names as name space indicators. For example:
+ // `com.facebook.fair` or `com.microsoft.cognitiveservices`
+ //
+ // Together with `model_version` and GraphProto.name, this forms the unique identity of
+ // the graph.
+ optional string domain = 4;
+
+ // The version of the graph encoded. See Version enum below.
+ optional int64 model_version = 5;
+
+ // A human-readable documentation for this model. Markdown is allowed.
+ optional string doc_string = 6;
+
+ // The parameterized graph that is evaluated to execute the model.
+ optional GraphProto graph = 7;
+
+ // Named metadata values; keys should be distinct.
+ repeated StringStringEntryProto metadata_props = 14;
+};
+
+// StringStringEntryProto follows the pattern for cross-proto-version maps.
+// See https://developers.google.com/protocol-buffers/docs/proto3#maps
+message StringStringEntryProto {
+ optional string key = 1;
+ optional string value= 2;
+};
+
+// GraphProto defines a parameterized series of nodes to form a directed acyclic graph.
+// This is the equivalent of the "network" and "graph" in many deep learning
+// frameworks.
+message GraphProto {
+ // The nodes in the graph.
+ repeated NodeProto node = 1;
+
+ // The name of the graph.
+ optional string name = 2; // namespace Graph
+
+ // A list of named tensor values (constants), used to specify default
+ // values for some of the inputs of the graph.
+ // Each TensorProto entry must have a distinct name (within the list) that
+ // also appears in the input list.
+ // In an evaluation, the default value specified here is used if and only if
+ // user specifies no value for the corresponding input parameter.
+ // May be used to pass serialized parameters for networks.
+ repeated TensorProto initializer = 5;
+
+ // A human-readable documentation for this graph. Markdown is allowed.
+ optional string doc_string = 10;
+
+ // The inputs and outputs of the graph.
+ repeated ValueInfoProto input = 11;
+ repeated ValueInfoProto output = 12;
+
+ // Information for the values in the graph. The ValueInfoProto.name's
+ // must be distinct. It is optional for a value to appear in value_info list.
+ repeated ValueInfoProto value_info = 13;
+
+ // DO NOT USE the following fields, they were deprecated before
+ // repeated string input = 3;
+ // repeated string output = 4;
+ // optional int64 ir_version = 6;
+ // optional int64 producer_version = 7;
+ // optional string producer_tag = 8;
+ // optional string domain = 9;
+}
+
+// A message defined to store a tensor in its serialized format.
+message TensorProto {
+ enum DataType {
+ UNDEFINED = 0;
+ // Basic types.
+ FLOAT = 1; // float
+ UINT8 = 2; // uint8_t
+ INT8 = 3; // int8_t
+ UINT16 = 4; // uint16_t
+ INT16 = 5; // int16_t
+ INT32 = 6; // int32_t
+ INT64 = 7; // int64_t
+ STRING = 8; // string
+ BOOL = 9; // bool
+
+ // Advanced types
+ FLOAT16 = 10;
+ DOUBLE = 11;
+ UINT32 = 12;
+ UINT64 = 13;
+ COMPLEX64 = 14; // complex with float32 real and imaginary components
+ COMPLEX128 = 15; // complex with float64 real and imaginary components
+ // Future extensions go here.
+ }
+
+ // The shape of the tensor.
+ repeated int64 dims = 1;
+
+ // The data type of the tensor.
+ optional DataType data_type = 2;
+
+ // For very large tensors, we may want to store them in chunks, in which
+ // case the following fields will specify the segment that is stored in
+ // the current TensorProto.
+ message Segment {
+ optional int64 begin = 1;
+ optional int64 end = 2;
+ }
+ optional Segment segment = 3;
+
+ // Tensor content must be in the row major order.
+ //
+ // Depending on the data_type field, exactly one of the fields below with
+ // name ending in _data is used to store the elements of the tensor.
+
+ // For float and complex64 values
+ // Complex64 tensors are encoded as a single array of floats,
+ // with the real components appearing in odd numbered positions,
+ // and the corresponding imaginary component apparing in the
+ // subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i]
+ // is encoded as [1.0, 2.0 ,3.0 ,4.0]
+ // When this field is present, the data_type field MUST be FLOAT or COMPLEX64.
+ repeated float float_data = 4 [packed = true];
+
+ // For int32, uint8, int8, uint16, int16, bool, and float16 values
+ // float16 values must be bit-wise converted to an uint16_t prior
+ // to writing to the buffer.
+ // When this field is present, the data_type field MUST be
+ // INT32, INT16, INT8, UINT16, INT8, BOOL, or FLOAT32
+ repeated int32 int32_data = 5 [packed = true];
+
+ // For strings.
+ // Each element of string_data is a UTF-8 encoded Unicode
+ // string. No trailing null, no leading BOM. The protobuf "string"
+ // scalar type is not used to match ML community conventions.
+ // When this field is present, the data_type field MUST be STRING
+ repeated bytes string_data = 6;
+
+ // For int64.
+ // When this field is present, the data_type field MUST be INT64
+ repeated int64 int64_data = 7 [packed = true];
+
+ // Optionally, a name for the tensor.
+ optional string name = 8; // namespace Value
+
+ // A human-readable documentation for this tensor. Markdown is allowed.
+ optional string doc_string = 12;
+
+ // Serializations can either use one of the fields above, or use this
+ // raw bytes field. The only exception is the string case, where one is
+ // required to store the content in the repeated bytes string_data field.
+ //
+ // When this raw_data field is used to store tensor value, elements MUST
+ // be stored in as fixed-width, little-endian order.
+ // Floating-point data types MUST be stored in IEEE 754 format.
+ // Complex64 elements must be written as two consecutive FLOAT values, real component first.
+ // Complex128 elements must be written as two consecutive DOUBLE values, real component first.
+ // Boolean type MUST be written one byte per tensor element (00000001 for true, 00000000 for false).
+ //
+ // Note: the advantage of specific field rather than the raw_data field is
+ // that in some cases (e.g. int data), protobuf does a better packing via
+ // variable length storage, and may lead to smaller binary footprint.
+ // When this field is present, the data_type field MUST NOT be STRING or UNDEFINED
+ optional bytes raw_data = 9;
+
+ // For double
+ // Complex64 tensors are encoded as a single array of doubles,
+ // with the real components appearing in odd numbered positions,
+ // and the corresponding imaginary component apparing in the
+ // subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i]
+ // is encoded as [1.0, 2.0 ,3.0 ,4.0]
+ // When this field is present, the data_type field MUST be DOUBLE or COMPLEX128
+ repeated double double_data = 10 [packed = true];
+
+ // For uint64 and uint32 values
+ // When this field is present, the data_type field MUST be
+ // UINT32 or UINT64
+ repeated uint64 uint64_data = 11 [packed = true];
+}
+
+// Defines a tensor shape. A dimension can be either an integer value
+// or a symbolic variable. A symbolic variable represents an unknown
+// dimension.
+message TensorShapeProto {
+ message Dimension {
+ oneof value {
+ int64 dim_value = 1;
+ string dim_param = 2; // namespace Shape
+ };
+ };
+ repeated Dimension dim = 1;
+}
+
+// Define the types.
+message TypeProto {
+
+ message Tensor {
+ // This field MUST NOT have the value of UNDEFINED
+ // This field MUST be present for this version of the IR.
+ optional TensorProto.DataType elem_type = 1;
+ optional TensorShapeProto shape = 2;
+ }
+
+
+ oneof value {
+ // The type of a tensor.
+ Tensor tensor_type = 1;
+
+ }
+}
+
+// OperatorSets are uniquely identified by a (domain, opset_version) pair.
+message OperatorSetIdProto {
+ // The domain of the operator set being identified.
+ // The empty string ("") or absence of this field implies the operator
+ // set that is defined as part of the ONNX specification.
+ // This field MUST be present in this version of the IR when referring to any other operator set.
+ optional string domain = 1;
+
+ // The version of the operator set being identified.
+ // This field MUST be present in this version of the IR.
+ optional int64 version = 2;
+}
\ No newline at end of file
diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py
index 6620bef8222aa208113289be80bb5929f6c86886..fd1446464cab39b433f175c557d22d84ee2e9b4e 100644
--- a/server/visualdl/visual_dl.py
+++ b/server/visualdl/visual_dl.py
@@ -12,6 +12,8 @@ import storage
import visualdl.mock.data as mock_data
import visualdl.mock.tags as mock_tags
from visualdl.log import logger
+import storage
+import graph
app = Flask(__name__, static_url_path="")
@@ -149,6 +151,11 @@ def individual_image():
imagefile, as_attachment=True, attachment_filename='img.png')
return response
+@app.route('/data/plugin/graphs/graph')
+def graph():
+ model_json = graph.load_model("")
+ return Response(model_json, mimetype='application/json')
+
if __name__ == '__main__':
logger.info(" port=" + str(options.port))