提交 6e213677 编写于 作者: N nepeplwu 提交者: Nicky Chan

fix #506 (#508)

* front-end add vue-i18n dependency

* back-end support language selection with option --language or -L

* front-end add language support with vue-i18n
上级 da716c85
......@@ -29,6 +29,7 @@
"qs": "^6.5.1",
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vue-i18n": ">=7.0.0",
"vuetify": "^0.17.7",
"xlsx": "^0.11.3"
},
......
......@@ -9,6 +9,7 @@
<script>
import AppMenu from './common/component/AppMenu';
import {getLanguage} from './service';
export default {
name: 'App',
......@@ -21,6 +22,10 @@ export default {
};
},
created() {
getLanguage().then(({errno, data}) => {
this.$i18n.locale = data;
});
if (location.hash && location.hash != '#/') {
this.initialRoute = /(\#\/)(\w*)([?|&]{0,1})/.exec(location.hash)[2];
} else {
......
{
"lang": {
"runs": "Runs",
"notRunsSelect": "None selected",
"metrics": "metrics",
"samples": "samples",
"graphs": "graphs",
"HighDimensional": "HighDimensional",
"text": "Text",
"image": "Image",
"audio": "Audio",
"showActualImageSize": "Show actual image size",
"startRunning": "Running",
"stopRunning": "Stopped",
"searchTagInReg": "Search tags in RegExp",
"scalars": "Scalars",
"smoothing": "Smoothing",
"xAxis": "X-axis",
"toolTipSorting": "Tooltip sorting",
"ignoreOutliers": "Ignore outliers in chart scaling",
"histogram": "Histogram",
"mode": "Mode",
"step": "Step",
"relative": "Relative",
"wall": "Wall Time",
"default": "default",
"descending": "descending",
"ascending": "ascending",
"nearest": "nearest",
"overlay": "Overlay",
"offset": "Offset",
"downloadImage": "Download image",
"restoreImage": "Restore image",
"scale": "Scale",
"nodeInfo": "Node Info",
"nodeName": "Name",
"nodeType": "Node Type",
"dataShape": "Shape",
"dataType": "Data Type",
"input": "Input",
"output": "Output",
"opType": "Operator Type",
"search": "Search",
"searchByLabel": "Search by label",
"dimension": "Dimension",
"2D": "2D",
"3D": "3D",
"displayAllLabel": "Display All Labels",
"reductionMethod": "Reduction Method",
"downloadDataInJson": "Download data in JSON",
"all": "All"
}
}
{
"lang": {
"runs": "数据流",
"notRunsSelect": "未选中任何数据",
"metrics": "度量数据",
"samples": "样本数据",
"graphs": "网络结构",
"HighDimensional": "高维数据映射",
"text": "文本",
"image": "图片",
"audio": "音频",
"showActualImageSize": "按真实大小显示",
"startRunning": "运行",
"stopRunning": "停止",
"searchTagInReg": "搜索标签(支持正则)",
"scalars": "标量",
"smoothing": "平滑度",
"xAxis": "x轴",
"toolTipSorting": "标签排序方法",
"ignoreOutliers": "图表缩放时忽略极端值",
"histogram": "直方图",
"mode": "模式",
"step": "步",
"relative": "相对值",
"wall": "时间",
"default": "默认",
"descending": "降序",
"ascending": "升序",
"nearest": "最近",
"overlay": "概览",
"offset": "偏移",
"downloadImage": "下载图片",
"restoreImage": "还原图片",
"scale": "比例",
"nodeInfo": "节点信息",
"nodeName": "节点名称",
"nodeType": "节点类型",
"dataShape": "数据形状",
"dataType": "数据类型",
"input": "输入",
"output": "输出",
"opType": "算子类型",
"search": "搜索",
"searchByLabel": "通过标签搜索",
"dimension": "维度",
"2D": "二维",
"3D": "三维",
"displayAllLabel": "展示所有标签",
"reductionMethod": "降维方法",
"downloadDataInJson": "下载数据(json格式)",
"all": "全部"
}
}
......@@ -7,37 +7,37 @@
<v-toolbar-title class="appbar-menu-title"/>
<v-toolbar-items>
<v-menu
open-on-hover
offset-y
:close-on-content-click="false">
<v-btn
slot="activator"
depressed
@mouseover="runsSelectorOpen = true"
@mouseout="runsSelectorOpen = false"
:ripple="false"
:color="runsSelectorOpen ? 'dark_primary' : 'primary'"
:class="runsSelectorOpen ? 'runs-selected-menu-open' : 'runs-selected-menu'"
> <span class="runs-selected-text">Runs: {{ runs.length == 0 ? 'None selected' : runs.join(', ') }} </span>
<v-icon>arrow_drop_down</v-icon>
</v-btn>
<v-list dense>
<v-list-tile
v-for="(item, index) in availableRuns"
:key="index"
<v-menu
open-on-hover
offset-y
:close-on-content-click="false">
<v-btn
slot="activator"
depressed
@mouseover="runsSelectorOpen = true"
@mouseout="runsSelectorOpen = false">
<v-list-tile-action>
<v-checkbox
:value="item"
:key="item"
:label="item"
v-model="runs" />
</v-list-tile-action>
</v-list-tile>
</v-list>
</v-menu>
@mouseout="runsSelectorOpen = false"
:ripple="false"
:color="runsSelectorOpen ? 'dark_primary' : 'primary'"
:class="runsSelectorOpen ? 'runs-selected-menu-open' : 'runs-selected-menu'"
> <span class="runs-selected-text">{{ $t("lang.runs") }}: {{ runs.length == 0 ? $t("lang.notRunsSelect") : runs.join(', ') }} </span>
<v-icon>arrow_drop_down</v-icon>
</v-btn>
<v-list dense>
<v-list-tile
v-for="(item, index) in availableRuns"
:key="index"
@mouseover="runsSelectorOpen = true"
@mouseout="runsSelectorOpen = false">
<v-list-tile-action>
<v-checkbox
:value="item"
:key="item"
:label="item"
v-model="runs" />
</v-list-tile-action>
</v-list-tile>
</v-list>
</v-menu>
<v-btn
v-for="item in items"
......@@ -46,7 +46,7 @@
dark
:class="selected.toLowerCase() === item.name.toLowerCase() ? 'menu-item-selected': 'menu-item'"
@click="handleItemClick(item)"
>{{ item.title }}</v-btn>
>{{ $t('lang.' + item.name) }}</v-btn>
</v-toolbar-items>
</v-toolbar>
</div>
......@@ -95,29 +95,29 @@ export default {
};
},
created() {
this.runs = this.$route.query.runs; //maintain runs state after refresh
this.runs = this.$route.query.runs; // maintain runs state after refresh
getRuns().then(({errno, data}) => {
this.availableRuns = data;
if (!this.runs) this.runs = data;
//use replace here instead of push so that user cannot go back to empty run state
this.$router.replace( { path: this.initialRoute, query: { runs: this.runs }});
// use replace here instead of push so that user cannot go back to empty run state
this.$router.replace( {path: this.initialRoute, query: {runs: this.runs}});
});
},
watch: {
runs: function(val) {
if (this.runs) {
this.$router.push( {query: { runs: this.runs }});
}
if (this.runs) {
this.$router.push( {query: {runs: this.runs}});
}
},
'$route'(to, from) { // this will get called when back button is hit that changes path or query
this.runs = this.$route.query.runs;
this.selected = this.$route.name;
},
'$route' (to, from) { //this will get called when back button is hit that changes path or query
this.runs = this.$route.query.runs;
this.selected = this.$route.name;
}
},
methods: {
handleItemClick: function(item) {
this.selected = item.name;
this.$router.push( { path: item.url, query: { runs: this.runs }});
this.$router.push( {path: item.url, query: {runs: this.runs}});
},
},
};
......@@ -174,5 +174,4 @@ export default {
overflow visible
</style>
......@@ -7,7 +7,7 @@
@click="handleDownload"
dark>
<v-icon style="margin-right: 6px">file_download</v-icon>
Download image
{{ $t("lang.downloadImage") }}
</v-btn>
<v-btn
......@@ -16,11 +16,11 @@
@click="resetImage"
dark>
<v-icon style="margin-right: 6px">restore</v-icon>
Restore image
{{ $t("lang.restoreImage") }}
</v-btn>
<v-slider
label="Scale"
:label="$t('lang.scale')"
max="1"
min="0.1"
step="0.1"
......@@ -30,22 +30,22 @@
<div class="node-info">
<h3>Node Info: </h3>
<h3>{{ $t("lang.nodeInfo") }}: </h3>
<div v-if="curNode.nodeType === 'input'">
<div>Node Type: {{ curNode.nodeType }}</div>
<div>Name: {{ curNode.nodeInfo.name }}</div>
<div>Shape: {{ curNode.nodeInfo.shape }}</div>
<div>Data Type: {{ curNode.nodeInfo.data_type }}</div>
<div>{{ $t("lang.nodeType") }}: {{ curNode.nodeType }}</div>
<div>{{ $t("lang.nodeName") }}: {{ curNode.nodeInfo.name }}</div>
<div>{{ $t("lang.dataShape") }}: {{ curNode.nodeInfo.shape }}</div>
<div>{{ $t("lang.dataType") }}: {{ curNode.nodeInfo.data_type }}</div>
</div>
<div v-else-if="curNode.nodeType === 'output'">
<div>Node Type: {{ curNode.nodeType }}</div>
<div>{{ $t("lang.nodeType") }}: {{ curNode.nodeType }}</div>
</div>
<div v-else-if="curNode.nodeType === 'operator'">
<div>Node Type: {{ curNode.nodeType }}</div>
<div>Input: {{ curNode.nodeInfo.input }}</div>
<div>Operator Type: {{ curNode.nodeInfo.opType }}</div>
<div>Output: {{ curNode.nodeInfo.output }}</div>
<div>{{ $t("lang.nodeType") }}: {{ curNode.nodeType }}</div>
<div>{{ $t("lang.input") }}: {{ curNode.nodeInfo.input }}</div>
<div>{{ $t("lang.opType") }}: {{ curNode.nodeInfo.opType }}</div>
<div>{{ $t("lang.output") }}: {{ curNode.nodeInfo.output }}</div>
</div>
<div v-else/>
</div>
......
......@@ -2,8 +2,8 @@
<div class="visual-dl-page-config-com">
<v-text-field
label="Search"
hint="Search by label"
:label="$t('lang.search')"
:hint="$t('lang.searchByLabel')"
v-model="config.searchText"
dark
/>
......@@ -11,24 +11,24 @@
<v-checkbox
class="visual-dl-page-config-checkbox"
label="Display All Labels"
:label="$t('lang.displayAllLabel')"
v-model="config.displayWordLabel"
dark/>
<v-radio-group
label="Dimension"
:label="$t('lang.dimension')"
v-model="config.dimension"
dark>
<v-radio
label="2D"
:label="$t('lang.2D')"
value="2"/>
<v-radio
label="3D"
:label="$t('lang.3D')"
value="3"/>
</v-radio-group>
<v-radio-group
label="Reduction Method"
:label="$t('lang.reductionMethod')"
v-model="config.reduction"
dark>
<v-radio
......@@ -49,7 +49,7 @@
dark
block
>
{{ config.running ? 'Running' : 'Stopped' }}
{{ config.running ? $t('lang.startRunning') : $t('lang.stopRunning') }}
</v-btn>
</div>
</template>
......
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import App from './App';
import Vuetify from 'vuetify';
import router from '@/router';
Vue.config.productionTip = false;
Vue.use(VueI18n);
const DEFAULT_LANG = 'zh';
const locales = {
zh: require('./assets/language/zh.json'),
en: require('./assets/language/en.json'),
};
const i18n = new VueI18n({
locale: DEFAULT_LANG,
messages: locales,
});
Vue.use(Vuetify, {
theme: {
primary: '#008c99',
......@@ -20,6 +33,7 @@ Vue.use(Vuetify, {
new Vue({
el: '#root',
router,
i18n: i18n,
components: {App},
template: '<App/>',
});
......@@ -8,15 +8,17 @@
color="tag_background"
class="visual-dl-tags-tab">
<v-icon>search</v-icon>
<input type="search" v-model="config.groupNameReg"
autocomplete="false"
placeholder="Search tags in RegExp"
class="visual-dl-tags-search-input">
<input
type="search"
v-model="config.groupNameReg"
autocomplete="false"
:placeholder="$t('lang.searchTagInReg')"
class="visual-dl-tags-search-input">
</v-card>
<ui-tags-tab
:total="tagsListCount(allTagsMatchingList)"
:title="config.groupNameReg.trim().length == 0 ? 'All' : config.groupNameReg"
:title="config.groupNameReg.trim().length == 0 ? $t('lang.all') : config.groupNameReg"
:active="selectedGroup === '' "
@click="selectedGroup = '' "
/>
......@@ -24,6 +26,7 @@
v-for="item in groupedTags"
:total="tagsListCount(item.tags)"
:title="item.group"
:key="item.group"
:active="item.group === selectedGroup"
@click="selectedGroup = item.group"
/>
......
......@@ -2,79 +2,79 @@
<div class="visual-dl-page-config-com">
<v-checkbox
class="visual-dl-page-config-checkbox"
label="Scalars"
:label="$t('lang.scalars')"
v-model="config.scalar.display"
:disabled="!config.scalar.enabled"
dark/>
<div class="visual-dl-page-component-block">
<div class="visual-dl-page-control-block">
<span :class="'visual-dl-page-control-span' + (config.scalar.display ? '' : ' visual-dl-page-disabled-text')">Smoothing</span>
<v-slider
:max="0.99"
:min="0"
:step="0.01"
v-model="smoothingValue"
class="visual-dl-page-smoothing-slider"
dark
:disabled="!config.scalar.display"/>
<span :class="'visual-dl-page-slider-span' + (config.scalar.display ? '' : ' visual-dl-page-disabled-text')">{{ smoothingValue }}</span>
</div>
<div class="visual-dl-page-control-block">
<span :class="'visual-dl-page-control-span' + (config.scalar.display ? '' : ' visual-dl-page-disabled-text')">X-axis</span>
<v-select
:items="horizontalItems"
v-model="config.horizontal"
class="visual-dl-page-config-selector"
dark
dense
:disabled="!config.scalar.display"
/>
</div>
<div class="visual-dl-page-control-block">
<span :class="'visual-dl-page-control-span' + (config.scalar.display ? '' : ' visual-dl-page-disabled-text')">{{ $t("lang.smoothing") }}</span>
<v-slider
:max="0.99"
:min="0"
:step="0.01"
v-model="smoothingValue"
class="visual-dl-page-smoothing-slider"
dark
:disabled="!config.scalar.display"/>
<span :class="'visual-dl-page-slider-span' + (config.scalar.display ? '' : ' visual-dl-page-disabled-text')">{{ smoothingValue }}</span>
</div>
<div class="visual-dl-page-control-block">
<span :class="'visual-dl-page-control-span' + (config.scalar.display ? '' : ' visual-dl-page-disabled-text')">Tooltip sorting</span>
<v-select
:items="sortingMethodItems"
v-model="config.sortingMethod"
class="visual-dl-page-config-selector"
dark
dense
:disabled="!config.scalar.display"
/>
</div>
<div class="visual-dl-page-control-block">
<span :class="'visual-dl-page-control-span' + (config.scalar.display ? '' : ' visual-dl-page-disabled-text')">{{ $t("lang.xAxis") }}</span>
<v-select
:items="horizontalItems"
v-model="config.horizontal"
class="visual-dl-page-config-selector"
dark
dense
:disabled="!config.scalar.display"
/>
</div>
<v-checkbox
class="visual-dl-page-outliers-checkbox"
label="Ignore outliers in chart scaling"
v-model="config.outlier"
<div class="visual-dl-page-control-block">
<span :class="'visual-dl-page-control-span' + (config.scalar.display ? '' : ' visual-dl-page-disabled-text')">{{ $t("lang.toolTipSorting") }}</span>
<v-select
:items="sortingMethodItems"
v-model="config.sortingMethod"
class="visual-dl-page-config-selector"
dark
:disabled="!config.scalar.display"/>
dense
:disabled="!config.scalar.display"
/>
</div>
<v-checkbox
class="visual-dl-page-outliers-checkbox"
:label="$t('lang.ignoreOutliers')"
v-model="config.outlier"
dark
:disabled="!config.scalar.display"/>
</div>
<v-checkbox
class="visual-dl-page-config-checkbox"
label="Histogram"
:label="$t('lang.histogram')"
v-model="config.histogram.display"
:disabled="!config.histogram.enabled"
dark/>
<div class="visual-dl-page-component-block">
<div class="visual-dl-page-control-block">
<span :class="'visual-dl-page-control-span' + (config.histogram.display ? '' : ' visual-dl-page-disabled-text')">Mode</span>
<v-select
:items="chartTypeItems"
v-model="config.chartType"
class="visual-dl-page-config-selector"
dark
dense
:disabled="!config.histogram.display"
/>
</div>
<div class="visual-dl-page-component-block">
<div class="visual-dl-page-control-block">
<span :class="'visual-dl-page-control-span' + (config.histogram.display ? '' : ' visual-dl-page-disabled-text')">{{ $t("lang.mode") }}</span>
<v-select
:items="chartTypeItems"
v-model="config.chartType"
class="visual-dl-page-config-selector"
dark
dense
:disabled="!config.histogram.display"
/>
</div>
</div>
<v-btn
:color="config.running ? 'primary' : 'error'"
......@@ -85,7 +85,7 @@
dark
block
>
{{ config.running ? 'Running' : 'Stopped' }}
{{ config.running ? $t("lang.startRunning") : $t("lang.stopRunning") }}
</v-btn>
</div>
</template>
......@@ -100,36 +100,59 @@ export default {
},
data() {
return {
horizontalItems: [
smoothingValue: this.config.smoothing,
isDemo: process.env.NODE_ENV === 'demo',
};
},
computed: {
sortingMethodItems() {
return [
{
text: 'Step',
value: 'step',
text: this.$i18n.t('lang.default'),
value: 'default',
},
{
text: 'Relative',
value: 'relative',
text: this.$i18n.t('lang.descending'),
value: 'descending',
},
{
text: 'Wall Time',
value: 'wall',
text: this.$i18n.t('lang.ascending'),
value: 'ascending',
},
],
sortingMethodItems: [
'default', 'descending', 'ascending', 'nearest',
],
chartTypeItems: [
{
text: 'Overlay',
text: this.$i18n.t('lang.nearest'),
value: 'nearest',
},
];
},
chartTypeItems() {
return [
{
text: this.$i18n.t('lang.overlay'),
value: 'overlay',
},
{
text: 'Offset',
text: this.$i18n.t('lang.offset'),
value: 'offset',
},
],
smoothingValue: this.config.smoothing,
isDemo: process.env.NODE_ENV === 'demo',
};
];
},
horizontalItems() {
return [
{
text: this.$i18n.t('lang.step'),
value: 'step',
},
{
text: this.$i18n.t('lang.relative'),
value: 'relative',
},
{
text: this.$i18n.t('lang.wall'),
value: 'wall',
},
];
},
},
watch: {
smoothingValue: _.debounce(
......
......@@ -61,7 +61,7 @@
<v-list dense>
<v-list-tile>
<v-list-tile-content>
<v-list-tile-title>Download data in JSON</v-list-tile-title>
<v-list-tile-title>{{ $t("lang.downloadDataInJson") }}</v-list-tile-title>
</v-list-tile-content>
<v-list-tile-action>
<v-icon>expand_more</v-icon>
......
......@@ -2,21 +2,23 @@
<div class="visual-dl-page-container">
<div class="visual-dl-page-left">
<div>
<div>
<v-card
hover
color="tag_background"
class="visual-dl-tags-tab">
<v-icon>search</v-icon>
<input type="search" v-model="config.groupNameReg"
autocomplete="false"
placeholder="Search tags in RegExp"
class="visual-dl-tags-search-input">
<input
type="search"
v-model="config.groupNameReg"
autocomplete="false"
:placeholder="$t('lang.searchTagInReg')"
class="visual-dl-tags-search-input">
</v-card>
<ui-tags-tab
:total="tagsListCount(allTagsMatchingList)"
:title="config.groupNameReg.trim().length == 0 ? 'All' : config.groupNameReg"
:title="config.groupNameReg.trim().length == 0 ? $t('lang.all') : config.groupNameReg"
:active="selectedGroup === '' "
@click="selectedGroup = '' "
/>
......@@ -24,6 +26,7 @@
v-for="item in groupedTags"
:total="tagsListCount(item.tags)"
:title="item.group"
:key="item.group"
:active="item.group === selectedGroup"
@click="selectedGroup = item.group"
/>
......@@ -63,24 +66,24 @@ export default {
'ui-tags-tab': TagsTab,
},
props: {
runs: {
type: Array,
required: true,
},
runs: {
type: Array,
required: true,
},
},
data() {
return {
tagInfo: { image: {}, audio: {}, text: {} },
tagInfo: {image: {}, audio: {}, text: {}},
config: {
groupNameReg: '',
image: { enabled: false, display: false },
audio: { enabled: false, display: false },
text: { enabled: false, display: false },
image: {enabled: false, display: false},
audio: {enabled: false, display: false},
text: {enabled: false, display: false},
isActualImageSize: false,
runs: [],
running: true,
},
filteredTagsList: { image: {}, audio: {}, text: {} },
filteredTagsList: {image: {}, audio: {}, text: {}},
selectedGroup: '',
};
},
......@@ -107,7 +110,6 @@ export default {
let list = {};
Object.keys(this.tagInfo).forEach((type) => {
let tags = this.tagInfo[type];
let runs = Object.keys(tags);
......@@ -133,7 +135,6 @@ export default {
return list;
},
groupedTags() {
let tagsList = this.tagsList || [];
// put data in group
......@@ -143,11 +144,10 @@ export default {
let tagsForEachType = tagsList[type];
tagsForEachType.forEach((item) => {
let group = item.group;
if (groupData[group] === undefined) {
groupData[group] = {}
groupData[group] = {};
}
if (groupData[group][type] === undefined) {
groupData[group][type] = [];
......@@ -207,9 +207,9 @@ export default {
'config.groupNameReg': function(val) {
this.throttledFilterTagsList();
},
runs: function(val) {
this.config.runs = val;
}
'runs': function(val) {
this.config.runs = val;
},
},
methods: {
filterTagsList(groupNameReg) {
......
......@@ -2,30 +2,30 @@
<div class="visual-dl-page-config-com">
<v-checkbox
class="visual-dl-page-config-checkbox"
label="Image"
:label="$t('lang.image')"
v-model="config.image.display"
:disabled="!config.image.enabled"
dark/>
<div class="visual-dl-page-component-block">
<v-checkbox
class="visual-dl-page-subconfig-checkbox"
label="Show actual image size"
v-model="config.isActualImageSize"
dark
:disabled="!config.image.display"/>
<v-checkbox
class="visual-dl-page-subconfig-checkbox"
:label="$t('lang.showActualImageSize')"
v-model="config.isActualImageSize"
dark
:disabled="!config.image.display"/>
</div>
<v-checkbox
class="visual-dl-page-config-checkbox"
label="Audio"
:label="$t('lang.audio')"
v-model="config.audio.display"
:disabled="!config.audio.enabled"
dark/>
<v-checkbox
class="visual-dl-page-config-checkbox"
label="Text"
:label="$t('lang.text')"
v-model="config.text.display"
:disabled="!config.text.enabled"
dark/>
......@@ -39,7 +39,7 @@
dark
block
>
{{ config.running ? 'Running' : 'Stopped' }}
{{ config.running ? $t('lang.startRunning') : $t('lang.stopRunning') }}
</v-btn>
</div>
</template>
......
......@@ -4,6 +4,8 @@ export const getPluginScalarsTags = makeService('/data/plugin/scalars/tags');
export const getRuns = makeService('/data/runs');
export const getLanguage = makeService('/data/language');
export const getPluginScalarsScalars = makeService('/data/plugin/scalars/scalars');
export const getPluginImagesTags = makeService('/data/plugin/images/tags');
......
......@@ -48,6 +48,9 @@ error_sleep_time = 2 # seconds
SERVER_DIR = os.path.join(visualdl.ROOT, 'server')
support_language = ["en", "zh"]
default_language = support_language[0]
def try_call(function, *args, **kwargs):
res = lib.retry(error_retry_times, function, error_sleep_time, *args,
......@@ -98,6 +101,14 @@ def parse_args():
default=20,
help="memory cache timeout duration in seconds, default 20",
)
parser.add_argument(
"-L",
"--language",
type=str,
default=default_language,
action="store",
help="set the default language")
args = parser.parse_args()
if not args.logdir:
parser.print_help()
......@@ -162,6 +173,15 @@ def runs():
return Response(json.dumps(result), mimetype='application/json')
@app.route('/data/language')
def language():
data = args.language
if not data in support_language:
data = default_language
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route("/data/plugin/scalars/tags")
def scalar_tags():
data = cache_get("/data/plugin/scalars/tags", try_call,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册