未验证 提交 afc15b2c 编写于 作者: P Peter Pan 提交者: GitHub

add public path argument (#624)

* style: lint shell scripts

* fix: translation fix

* feat: support changing public path when start

* v2.0.0-beta.36

* feat: support changing public path when start

* v2.0.0-beta.37

* build: core build fix

* v2.0.0-beta.38

* build: build frontend from source code

* fix: clean static files when exit

* fix: dependency version mismatching

* chore: remove unused api route

* fix: navbar actived link missing

* feat: redirect root

* fix: scalar chart tooltip value error

* feat: dynamic render nav items

* fix: high-dimensional page background missing

* v2.0.0-beta.39

* build: stop using script mode

* v2.0.0-beta.40

* Revert "build: stop using script mode"

This reverts commit 6c1ca297.
上级 f551ca15
......@@ -61,7 +61,6 @@ yarn
[server](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/server/README.md)
[serverless](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/serverless/README.md)
[cli](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/cli/README.md)
[app](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/app/README.md)
[i18n](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/i18n/README.md)
[wasm](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/wasm/README.md)
[mock](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/mock/README.md)
......
......@@ -61,7 +61,6 @@ yarn
[server](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/server/README.md)
[serverless](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/serverless/README.md)
[cli](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/cli/README.md)
[app](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/app/README.md)
[i18n](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/i18n/README.md)
[wasm](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/wasm/README.md)
[mock](https://github.com/PaddlePaddle/VisualDL/blob/develop/frontend/packages/mock/README.md)
......
......@@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "2.0.0-beta.35",
"version": "2.0.0-beta.40",
"npmClient": "yarn",
"useWorkspaces": true,
"command": {
......
......@@ -4,7 +4,7 @@
"workspaces": [
"packages/*"
],
"version": "2.0.0-beta.35",
"version": "2.0.0",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
"visualdl",
......
../../../LICENSE
\ No newline at end of file
# VisualDL FrontEnd APP
UNDER DEVELOPMENT
appId: org.paddlepaddle.visualdl
productName: VisualDL
files:
- 'index.js'
- 'resources/**/*'
directories:
buildResources: ./resources
mac:
category: public.app-category.developer-tools
darkModeSupport: false
hardenedRuntime: true
gatekeeperAssess: false
target:
- zip
win:
target:
- nsis
verifyUpdateCodeSignature: false
linux:
target:
- AppImage
nsis:
perMachine: true
publish: null
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
const http = require('http');
const {app, BrowserWindow, screen, nativeImage} = require('electron');
const pm2 = require('pm2');
const host = 'localhost';
async function getPort() {
return new Promise((resolve, reject) => {
const server = http.createServer();
server.listen(0);
server.on('listening', () => {
resolve(server.address().port);
server.close();
});
server.on('error', reject);
});
}
async function createWindow() {
const {width, height} = screen.getPrimaryDisplay().workAreaSize;
const win = new BrowserWindow({
minWidth: 800,
minHeight: 600,
width,
height,
icon: nativeImage.createFromPath(path.join(__dirname, 'resources/icon.png')),
webPreferences: {
devTools: false,
nodeIntegration: true
},
show: false
});
win.once('ready-to-show', () => {
win.show();
});
return win;
}
function startServer({port, host}) {
return new Promise((resolve, reject) => {
pm2.connect(err => {
if (err) {
reject(err);
}
const app = require('@visualdl/server/ecosystem.config').apps[0];
pm2.start(
{
...app,
instances: 1,
env: {
...app.env,
HOST: host,
PORT: port,
BACKEND: 'http://127.0.0.1:8040'
}
},
err => {
pm2.disconnect();
if (err) {
reject(err);
} else {
console.log(`Server listening at http://${host}:${port}`);
resolve();
}
}
);
});
});
}
function stopServer() {
return new Promise((resolve, reject) => {
pm2.connect(err => {
if (err) {
reject(err);
}
pm2.killDaemon(err => {
pm2.disconnect();
if (err) {
reject(err);
} else {
console.log('Server stopped');
resolve();
}
});
});
});
}
app.on('ready', async () => {
let port = 0;
try {
port = await getPort();
await startServer({port, host});
} catch (e) {
console.error(e);
app.exit(1);
return;
}
try {
const win = await createWindow();
win.loadURL(`http://${host}:${port}`);
} catch (e) {
console.error(e);
app.exit(1);
}
});
// app.on('activate', async () => {
// if (BrowserWindow.getAllWindows().length === 0) {
// try {
// await createWindow();
// } catch (e) {
// console.error(e);
// app.exit(1);
// }
// }
// });
// Quit when all windows are closed.
app.on('window-all-closed', () => {
app.quit();
// if (process.platform !== 'darwin') {
// app.quit();
// }
});
app.on('will-quit', async event => {
event.preventDefault();
try {
await stopServer();
app.exit(0);
} catch (e) {
console.error(e);
app.exit(1);
}
});
{
"name": "@visualdl/app",
"version": "2.0.0-beta.35",
"private": true,
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
"visualdl",
"paddlepaddle",
"visualization",
"deep learning"
],
"homepage": "https://github.com/PaddlePaddle/VisualDL",
"bugs": {
"url": "https://github.com/PaddlePaddle/VisualDL/issues"
},
"license": "Apache-2.0",
"author": "PeterPanZH <littlepanzh@gmail.com> (https://github.com/PeterPanZH)",
"contributors": [
"Niandalu <littlepanzh@gmail.com> (https://github.com/Niandalu)"
],
"repository": {
"type": "git",
"url": "https://github.com/PaddlePaddle/VisualDL.git",
"directory": "frontend/packages/app"
},
"scripts": {
"dev": "electron index.js",
"build": "electron-builder -mwl",
"test": "echo \"Error: no test specified\" && exit 0"
},
"dependencies": {
"@visualdl/server": "2.0.0-beta.35",
"pm2": "4.4.0"
},
"devDependencies": {
"electron": "8.2.5",
"electron-builder": "22.6.0"
},
"engines": {
"node": ">=10",
"npm": ">=6"
},
"publishConfig": {
"access": "public"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}
<svg height="31" viewBox="0 0 50 31" width="50" xmlns="http://www.w3.org/2000/svg"><g fill="#fff" fill-rule="evenodd" transform="translate(0 .0625)"><path d="m2.446115.0742695c-1.32588958 0-2.40434375 1.0740707-2.40434375 2.39411403s1.07845417 2.39382156 2.40434375 2.39382156 2.40434375-1.07377823 2.40434375-2.39382156-1.07845417-2.39411403-2.40434375-2.39411403" transform="translate(23.010417 .023203)"/><path d="m.23204292 23.3006269h3.46595625c.06922708 0 .13199166-.0401668.16068125-.1029517l10.45446458-22.89241762c.0531687-.1163081-.0322146-.24831243-.1605833-.24831243h-3.4659563c-.0691292 0-.1318937.04026424-.1605833.10295168l-10.45456252 22.89241757c-.05307083.1163081.0323125.2483125.16058334.2483125" transform="translate(24.0875 6.652667)"/><path d="m2.48757292.0742695c-1.32588959 0-2.40434375 1.0740707-2.40434375 2.39411403s1.07845416 2.39382156 2.40434375 2.39382156c1.32588958 0 2.40434375-1.07377823 2.40434375-2.39382156s-1.07845417-2.39411403-2.40434375-2.39411403" transform="translate(43.866667 .023203)"/><path d="m17.4206283.05694515h-3.4659562c-.0691292 0-.1318938.04026424-.1605834.10304917l-4.08253745 8.93954011h-8.13765833c-.1075125 0-.2050375.06278493-.24900209.16047204l-1.31139791 2.91052983c-.04513959.1002219.02849375.2135077.13884583.2135077h8.05922708l-4.87203958 10.6682704c-.05307083.1163081.0323125.2483125.16058333.2483125h3.46595625c.06922709 0 .13199167-.0401668.16058334-.1029517l10.45456253-22.89241762c.0531687-.1163081-.0322146-.24831243-.1605834-.24831243" transform="translate(0 6.652667)"/><path d="m29.3354319 12.1665001c-.3381063-2.99856519-2.8049209-5.35027016-5.8800917-5.45507419-.0279062-.00107241-.0550292.00302226-.0813687.00887179-.0140021-.0049721-.0284938-.00887179-.0441605-.00887179h-2.1002145c-.0522875 0-.0995813.03032005-.121123.07779872l-1.2130895 2.67576877c-.0396563.08754793.0247729.18679491.1211229.18679491h2.1414375c.4538437-.00653197.7861729.02330061 1.1568854.08062598 1.7253896.26693343 2.7979687 1.52672661 2.7732937 3.19686421-.0235 1.5783974-1.3395979 2.8354608-2.9250645 2.8354608h-7.669225c-.1306209 0-.2491.0760439-.3029542.1943018l-1.2729167 2.7896396c-.0615896.1347341.0376.2880892.1863354.2880892h8.9637813c3.7159375 0 6.6818333-3.1934519 6.2673521-6.8702698"/><path d="m50.1567746 12.1665001c-.3381063-2.99856519-2.8049209-5.35027016-5.8801896-5.45507419-.0279063-.00107241-.0549313.00302226-.0813688.00887179-.0139041-.0049721-.0283958-.00887179-.0440625-.00887179h-2.1003125c-.0521895 0-.0995812.03032005-.1211229.07779872l-1.2130896 2.67576877c-.0395583.08754793.024675.18679491.1212209.18679491h2.1413396c.4539416-.00653197.7862708.02330061 1.1569833.08062598 1.7252917.26693343 2.7978708 1.52672661 2.7731958 3.19686421-.0235 1.5783974-1.3395979 2.8354608-2.9250646 2.8354608h-7.669127c-.1306209 0-.2491.0760439-.3030521.1943018l-1.2729167 2.7896396c-.0614916.1347341.0376979.2880892.1863354.2880892h8.9638792c3.7158396 0 6.6817354-3.1934519 6.2673521-6.8702698"/></g></svg>
\ No newline at end of file
{
"name": "@visualdl/cli",
"version": "2.0.0-beta.35",
"version": "2.0.0-beta.40",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
"visualdl",
......@@ -34,7 +34,7 @@
"dist"
],
"dependencies": {
"@visualdl/server": "2.0.0-beta.35",
"@visualdl/server": "2.0.0-beta.40",
"open": "7.0.3",
"ora": "4.0.4",
"pm2": "4.4.0",
......@@ -59,5 +59,5 @@
"pre-commit": "lint-staged"
}
},
"gitHead": "d9641c770d9bc4037b2f1cb644fba198bd11a752"
"gitHead": "ed915e4590c1527836cc802d7c6f5011a5b9a4a3"
}
......@@ -8,7 +8,7 @@ import {writeFileSync} from 'fs';
const next = require.resolve('next/dist/bin/next');
export const projectRoot = path.dirname(require.resolve('@visualdl/core'));
export default function (action: string, ...args: string[]): Promise<number> {
export default function (action: 'build' | 'export', ...args: string[]): Promise<number> {
return new Promise((resolve, reject) => {
const capitalizedAction = action.replace(/^./, w => w.toUpperCase());
......
......@@ -155,13 +155,13 @@ const ChartPage = <T extends Item>({
) : (
<Empty height={rem(500)}>
{search ? (
<Trans i18nKey="search-empty">
<Trans i18nKey="common:search-empty">
Nothing found. Please try again with another word.
<br />
Or you can <a onClick={() => setInputValue('')}>see all charts</a>.
</Trans>
) : (
t('empty')
t('common:empty')
)}
</Empty>
)}
......@@ -174,14 +174,14 @@ const ChartPage = <T extends Item>({
<div className={className}>
<Search>
<SearchInput
placeholder={t('search-tags')}
placeholder={t('common:search-tags')}
rounded
value={inputValue}
onChange={(value: string) => setInputValue(value)}
/>
</Search>
{searchValue ? (
<ChartCollapse title={t('search-result')} total={matchedTags.length}>
<ChartCollapse title={t('common:search-result')} total={matchedTags.length}>
{withCharts(pageMatchedTags, true)}
{pageMatchedTags.length ? <StyledPagination page={page} total={total} onChange={setPage} /> : null}
</ChartCollapse>
......@@ -198,7 +198,7 @@ const ChartPage = <T extends Item>({
))
) : (
<Empty height={`calc(100vh - ${headerHeight} - ${rem(96)})`}>
<Trans i18nKey="unselected-empty">
<Trans i18nKey="common:unselected-empty">
Nothing selected.
<br />
Please select display data from right side.
......
......@@ -40,7 +40,7 @@ const PropertyList = styled(DataList)`
const NodeInfo: FunctionComponent<NodeInfoProps> = props => {
const {t} = useTranslation('graphs');
if (!props.node) {
return <p>{t('click-node')}</p>;
return <p>{t('graphs:click-node')}</p>;
}
const node = props.node;
......@@ -50,10 +50,10 @@ const NodeInfo: FunctionComponent<NodeInfoProps> = props => {
return (
<PropertyList
items={[
{key: t('node-type'), value: typeName[node.type]},
{key: t('node-name'), value: node.name},
{key: t('node-data-shape'), value: node.shape},
{key: t('node-data-type'), value: node.data_type}
{key: t('graphs:node-type'), value: typeName[node.type]},
{key: t('graphs:node-name'), value: node.name},
{key: t('graphs:node-data-shape'), value: node.shape},
{key: t('graphs:node-data-type'), value: node.data_type}
]}
/>
);
......@@ -61,15 +61,15 @@ const NodeInfo: FunctionComponent<NodeInfoProps> = props => {
return (
<PropertyList
items={[
{key: t('node-type'), value: typeName[node.type]},
{key: t('input'), value: node.input},
{key: t('op-type'), value: node.opType},
{key: t('output'), value: node.output}
{key: t('graphs:node-type'), value: typeName[node.type]},
{key: t('graphs:input'), value: node.input},
{key: t('graphs:op-type'), value: node.opType},
{key: t('graphs:output'), value: node.output}
]}
/>
);
case 'unknown':
return <PropertyList items={[{key: t('node-type'), value: typeName[node.guessType]}]} />;
return <PropertyList items={[{key: t('graphs:node-type'), value: typeName[node.guessType]}]} />;
default:
return null;
}
......
......@@ -101,11 +101,11 @@ const HighDimensionalChart: FunctionComponent<HighDimensionalChartProps> = ({
}, [points]);
if (!data && error) {
return <Empty>{t('error')}</Empty>;
return <Empty>{t('common:error')}</Empty>;
}
if (!data && !loading) {
return <Empty>{t('empty')}</Empty>;
return <Empty>{t('common:empty')}</Empty>;
}
return <StyledScatterChart loading={loading} data={chartData} gl={dimension === '3d'} />;
......
......@@ -52,7 +52,7 @@ const Image = React.forwardRef<ImageRef, ImageProps>(({src, cache}, ref) => {
}
if (error) {
return <div>{t('error')}</div>;
return <div>{t('common:error')}</div>;
}
return <img src={url} />;
......
......@@ -15,19 +15,10 @@ import Icon from '~/components/Icon';
import {InitConfig} from '@visualdl/i18n';
import Language from '~/components/Language';
import ee from '~/utils/event';
import intersection from 'lodash/intersection';
import styled from 'styled-components';
import useNavItems from '~/hooks/useNavItems';
import {useRouter} from 'next/router';
const buildNavItems = process.env.NAV_ITEMS;
const allNavItems = ['scalars', 'samples', 'graphs', 'high-dimensional'];
const navItems = buildNavItems
? intersection(
buildNavItems.split(',').map(item => item.trim()),
allNavItems
)
: allNavItems;
const Nav = styled.nav`
background-color: ${navbarBackgroundColor};
color: ${textInvertColor};
......@@ -101,7 +92,11 @@ const changeLanguage = () => {
const Navbar: FunctionComponent = () => {
const {t, i18n} = useTranslation('common');
const {pathname} = useRouter();
const {pathname, basePath} = useRouter();
const navItems = useNavItems();
const path = useMemo(() => pathname.replace(basePath, ''), [pathname, basePath]);
const indexUrl = useMemo(() => {
// TODO: fix type
......@@ -125,7 +120,7 @@ const Navbar: FunctionComponent = () => {
return (
// https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-custom-component-that-wraps-an-a-tag
<Link href={href} key={name} passHref>
<NavItem active={pathname === href}>
<NavItem active={path === href}>
<span className="nav-text">{t(name)}</span>
</NavItem>
</Link>
......
......@@ -56,21 +56,21 @@ const Pagination: FunctionComponent<PaginationProps & WithStyled> = ({page, tota
<Wrapper className={className}>
<div>
<Button disabled={currentPage <= 1} onClick={() => setPage(currentPage - 1)}>
{t('previous-page')}
{t('common:previous-page')}
</Button>
<Button disabled={currentPage >= total} onClick={() => setPage(currentPage + 1)}>
{t('next-page')}
{t('common:next-page')}
</Button>
</div>
<div>
<span>{t('total-page', {count: total})}</span>
<span>{t('common:total-page', {count: total})}</span>
<Input
value={jumpPage}
onChange={value => setJumpPage(value)}
onKeyDown={e => e.key === 'Enter' && setPage(jumpPage)}
/>
<Button onClick={() => setPage(jumpPage)} type="primary">
{t('confirm')}
{t('common:confirm')}
</Button>
</div>
</Wrapper>
......
......@@ -135,16 +135,16 @@ const RunAside: FunctionComponent<RunAsideProps> = ({
<Aside>
{children}
<section className="run-section">
<Field className="run-select" label={t('select-runs')}>
<Field className="run-select" label={t('common:select-runs')}>
<SearchInput
className="search-input"
value={search}
onChange={setSearch}
placeholder={t('search-runs')}
placeholder={t('common:search-runs')}
rounded
/>
<Checkbox value={selectAll} onChange={toggleSelectAll}>
{t('select-all')}
{t('common:select-all')}
</Checkbox>
<div className="run-list">
{filteredRuns.map((run, index) => (
......
......@@ -177,7 +177,7 @@ const SampleChart: FunctionComponent<SampleChartProps> = ({run, tag, brightness,
items={[
{
icon: 'download',
tooltip: t('download-image'),
tooltip: t('samples:download-image'),
onClick: saveImage
}
]}
......
......@@ -45,7 +45,7 @@ const StepSlider: FunctionComponent<StepSliderProps> = ({onChange, onChangeCompl
return (
<>
<Label>
<span>{`${t('step')}: ${steps[step] ?? '...'}`}</span>
<span>{`${t('samples:step')}: ${steps[step] ?? '...'}`}</span>
{children && <span>{children}</span>}
</Label>
<FullWidthRangeSlider
......
......@@ -205,25 +205,25 @@ const ScalarChart: FunctionComponent<ScalarChartProps> = ({
{
icon: 'maximize',
activeIcon: 'minimize',
tooltip: t('maximize'),
activeTooltip: t('minimize'),
tooltip: t('scalars:maximize'),
activeTooltip: t('scalars:minimize'),
toggle: true,
onClick: toggleMaximized
},
{
icon: 'restore-size',
tooltip: t('restore'),
tooltip: t('scalars:restore'),
onClick: () => echart.current?.restore()
},
{
icon: 'log-axis',
tooltip: t('axis'),
tooltip: t('scalars:axis'),
toggle: true,
onClick: toggleYAxisType
},
{
icon: 'download',
tooltip: t('download-image'),
tooltip: t('scalars:download-image'),
onClick: () => echart.current?.saveAsImage()
}
]}
......
import React, {FunctionComponent, useEffect, useMemo} from 'react';
import {WithStyled, position, primaryColor, size} from '~/utils/style';
import {WithStyled, backgroundColor, position, primaryColor, size} from '~/utils/style';
import GridLoader from 'react-spinners/GridLoader';
import styled from 'styled-components';
......@@ -7,6 +7,7 @@ import useECharts from '~/hooks/useECharts';
const Wrapper = styled.div`
position: relative;
background-color: ${backgroundColor};
> .echarts {
height: 100%;
......
......@@ -215,7 +215,7 @@ const Select = <T extends unknown>({
? multiple
? (value as T[]).map(findLabelByValue).join(' / ')
: findLabelByValue(value as T)
: placeholder || t('select'),
: placeholder || t('common:select'),
[multiple, value, findLabelByValue, isSelected, placeholder, t]
);
......@@ -227,7 +227,7 @@ const Select = <T extends unknown>({
</Trigger>
<List opened={isOpened} empty={isListEmpty}>
{isListEmpty
? t('empty')
? t('common:empty')
: list.map((item, index) => {
if (multiple) {
return (
......
import {fetcher} from '~/utils/fetch';
import intersection from 'lodash/intersection';
import {useMemo} from 'react';
import useRequest from '~/hooks/useRequest';
const allNavItems = ['scalars', 'samples', 'high-dimensional'];
export const navMap = {
scalar: 'scalars',
image: 'samples',
embeddings: 'high-dimensional'
} as const;
const useNavItems = () => {
const {data: components} = useRequest<(keyof typeof navMap)[]>('/components', fetcher, {
refreshInterval: 61 * 1000,
dedupingInterval: 29 * 1000,
errorRetryInterval: 29 * 1000,
errorRetryCount: Number.POSITIVE_INFINITY,
revalidateOnFocus: true,
revalidateOnReconnect: true,
refreshWhenHidden: false,
refreshWhenOffline: false
});
const navItems = useMemo(() => intersection(components?.map(component => navMap[component]) ?? [], allNavItems), [
components
]);
return navItems;
};
export default useNavItems;
......@@ -31,7 +31,6 @@ module.exports = {
DEFAULT_LANGUAGE,
LOCALE_PATH,
LANGUAGES,
NAV_ITEMS: process.env.NAV_ITEMS,
PUBLIC_PATH: publicPath,
API_URL: apiUrl
},
......
{
"name": "@visualdl/core",
"version": "2.0.0-beta.35",
"version": "2.0.0-beta.40",
"title": "VisualDL",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
......@@ -31,8 +31,8 @@
"test": "echo \"Error: no test specified\" && exit 0"
},
"dependencies": {
"@visualdl/i18n": "2.0.0-beta.35",
"@visualdl/wasm": "2.0.0-beta.35",
"@visualdl/i18n": "2.0.0-beta.40",
"@visualdl/wasm": "2.0.0-beta.40",
"bignumber.js": "9.0.0",
"dagre-d3": "0.6.4",
"echarts": "4.7.0",
......@@ -73,7 +73,7 @@
"@types/react": "16.9.34",
"@types/react-dom": "16.9.7",
"@types/styled-components": "5.1.0",
"@visualdl/mock": "2.0.0-beta.35",
"@visualdl/mock": "2.0.0-beta.40",
"babel-plugin-emotion": "10.0.33",
"babel-plugin-styled-components": "1.10.7",
"babel-plugin-typescript-to-proptypes": "1.3.2",
......@@ -96,5 +96,5 @@
"pre-commit": "lint-staged"
}
},
"gitHead": "d9641c770d9bc4037b2f1cb644fba198bd11a752"
"gitHead": "ed915e4590c1527836cc802d7c6f5011a5b9a4a3"
}
......@@ -14,7 +14,7 @@ const Error = styled.div`
const Error404: NextI18NextPage = () => {
const {t} = useTranslation('errors');
return <Error>404 - {t('page-not-found')}</Error>;
return <Error>404 - {t('errors:page-not-found')}</Error>;
};
// 404 page cannot have getInitialProps or getServerSideProps
......
......@@ -48,6 +48,9 @@ export default class VDLDocument extends Document<VDLDocumentProps> {
<Html lang={language} dir={languageDir}>
<Head />
<body>
<script
dangerouslySetInnerHTML={{__html: `__vdl_public_path__='${process.env.PUBLIC_PATH}'`}}
></script>
<Main />
<NextScript />
</body>
......
......@@ -18,7 +18,11 @@ interface ErrorProps {
const Error: NextI18NextPage<ErrorProps> = ({statusCode}) => {
const {t} = useTranslation('errors');
return <ErrorDiv>{statusCode ? t('error-with-status', {statusCode}) : t('error-without-status')}</ErrorDiv>;
return (
<ErrorDiv>
{statusCode ? t('errors:error-with-status', {statusCode}) : t('errors:error-without-status')}
</ErrorDiv>
);
};
Error.getInitialProps = ({res, err}) => {
......
......@@ -264,21 +264,21 @@ const Graphs: NextI18NextPage = () => {
<AsideSection>
<SubSection>
<Button rounded type="primary" icon="download" onClick={downloadImage}>
{t('download-image')}
{t('graphs:download-image')}
</Button>
<Button rounded type="primary" icon="revert" onClick={fitScreen}>
{t('restore-image')}
{t('graphs:restore-image')}
</Button>
</SubSection>
<SubSection>
<Field label={`${t('scale')}:`}>
<Field label={`${t('graphs:scale')}:`}>
<RangeSlider min={MIN_SCALE} max={MAX_SCALE} step={0.1} value={scale} onChange={setScale} />
</Field>
</SubSection>
<SubSection>
<Field label={`${t('node-info')}:`} />
<Field label={`${t('graphs:node-info')}:`} />
<NodeInfo node={currentNode} />
</SubSection>
</AsideSection>
......
......@@ -77,13 +77,13 @@ const HighDimensional: NextI18NextPage = () => {
</Field>
<Field>
<Checkbox value={labelVisibility} onChange={setLabelVisibility}>
{t('display-all-label')}
{t('high-dimensional:display-all-label')}
</Checkbox>
</Field>
<AsideDivider />
<AsideTitle>
<StyledIcon type="dimension" />
{t('dimension')}
{t('high-dimensional:dimension')}
</AsideTitle>
<Field>
<RadioGroup value={dimension} onChange={value => setDimension(value)}>
......@@ -97,7 +97,7 @@ const HighDimensional: NextI18NextPage = () => {
<AsideDivider />
<AsideTitle>
<StyledIcon type="reduction" />
{t('reduction-method')}
{t('high-dimensional:reduction-method')}
</AsideTitle>
<Field>
<RadioGroup value={reduction} onChange={value => setReduction(value)}>
......
import {NextI18NextPage, Router} from '~/utils/i18n';
import React, {useEffect} from 'react';
import {headerHeight, primaryColor, size} from '~/utils/style';
import {useEffect} from 'react';
import HashLoader from 'react-spinners/HashLoader';
import styled from 'styled-components';
import useNavItems from '~/hooks/useNavItems';
const Loading = styled.div`
${size(`calc(100vh - ${headerHeight})`, '100vw')}
display: flex;
justify-content: center;
align-items: center;
overscroll-behavior: none;
cursor: progress;
`;
const Index: NextI18NextPage = () => {
const navItmes = useNavItems();
useEffect(() => {
Router.replace('/scalars');
}, []);
if (navItmes.length) {
Router.replace(`/${navItmes[0]}`);
}
}, [navItmes]);
return null;
return (
<Loading>
<HashLoader size="60px" color={primaryColor} />
</Loading>
);
};
Index.getInitialProps = () => {
return {
namespacesRequired: ['common']
namespacesRequired: []
};
};
......
......@@ -57,16 +57,16 @@ const Samples: NextI18NextPage = () => {
>
<section>
<Checkbox value={showActualSize} onChange={setShowActualSize}>
{t('show-actual-size')}
{t('samples:show-actual-size')}
</Checkbox>
</section>
<section>
<Field label={t('brightness')}>
<Field label={t('samples:brightness')}>
<Slider min={0} max={2} step={0.01} value={brightness} onChange={setBrightness} />
</Field>
</section>
<section>
<Field label={t('contrast')}>
<Field label={t('samples:contrast')}>
<Slider min={0} max={2} step={0.01} value={contrast} onChange={setContrast} />
</Field>
</section>
......
......@@ -48,7 +48,6 @@ const Scalars: NextI18NextPage = () => {
const [xAxis, setXAxis] = useState<XAxis>(xAxisValues[0]);
const [tooltipSorting, setTooltipSorting] = useState<TooltipSorting>(toolTipSortingValues[0]);
const onChangeTooltipSorting = (value: TooltipSorting) => setTooltipSorting(value);
const [ignoreOutliers, setIgnoreOutliers] = useState(false);
......@@ -62,24 +61,24 @@ const Scalars: NextI18NextPage = () => {
>
<section>
<Checkbox value={ignoreOutliers} onChange={setIgnoreOutliers}>
{t('ignore-outliers')}
{t('scalars:ignore-outliers')}
</Checkbox>
<TooltipSortingDiv>
<span>{t('tooltip-sorting')}</span>
<span>{t('scalars:tooltip-sorting')}</span>
<Select
list={toolTipSortingValues.map(value => ({label: t(`tooltip-sorting-value.${value}`), value}))}
value={tooltipSorting}
onChange={onChangeTooltipSorting}
onChange={setTooltipSorting}
/>
</TooltipSortingDiv>
</section>
<section>
<Field label={t('smoothing')}>
<Field label={t('scalars:smoothing')}>
<Slider min={0} max={0.99} step={0.01} value={smoothing} onChangeComplete={setSmoothing} />
</Field>
</section>
<section>
<Field label={t('x-axis')}>
<Field label={t('scalars:x-axis')}>
<RadioGroup value={xAxis} onChange={setXAxis}>
{xAxisValues.map(value => (
<RadioButton key={value} value={value}>
......
......@@ -8,7 +8,6 @@
"search-result": "搜索结果",
"search-empty": "没有找到您期望的内容,你可以尝试其他搜索词<1/>或者点击<3>查看全部图表</3>",
"unselected-empty": "未选中任何数据<1/>请在右侧操作栏选择要展示的数据",
"all": "全部",
"empty": "空空如也",
"select": "请选择",
"select-all": "全选",
......
......@@ -205,7 +205,7 @@ export const tooltip = (data: TooltipData[], i18n: I18n) => {
run: item.run,
// use precision then toString to remove trailling 0
smoothed: new BigNumber(data[indexPropMap.smoothed] ?? Number.NaN).precision(5).toString(),
value: new BigNumber(data[indexPropMap.smoothed] ?? Number.NaN).precision(5).toString(),
value: new BigNumber(data[indexPropMap.value] ?? Number.NaN).precision(5).toString(),
step: data[indexPropMap.step],
time: formatTime(data[indexPropMap.time], i18n.language),
// Relative display value should take easy-read into consideration.
......
{
"name": "@visualdl/i18n",
"version": "2.0.0-beta.35",
"version": "2.0.0-beta.40",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
"visualdl",
......@@ -73,5 +73,5 @@
"pre-commit": "lint-staged"
}
},
"gitHead": "d9641c770d9bc4037b2f1cb644fba198bd11a752"
"gitHead": "ed915e4590c1527836cc802d7c6f5011a5b9a4a3"
}
......@@ -96,9 +96,10 @@ export const createConfig = (userConfig: Config): Config => {
/*
Set client side backend
*/
const publicPath = (window as any).__vdl_public_path__ || '';
combinedConfig.backend = {
loadPath: `${process.env.PUBLIC_PATH}/${clientLocalePath}/${localeStructure}.${localeExtension}`,
addPath: `${process.env.PUBLIC_PATH}/${clientLocalePath}/${localeStructure}.missing.${localeExtension}`
loadPath: `${publicPath}/${clientLocalePath}/${localeStructure}.${localeExtension}`,
addPath: `${publicPath}/${clientLocalePath}/${localeStructure}.missing.${localeExtension}`
};
combinedConfig.ns = [combinedConfig.defaultNS];
......
export default ['scalar', 'image', 'embeddings'];
{
"name": "@visualdl/mock",
"version": "2.0.0-beta.35",
"version": "2.0.0-beta.40",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
"visualdl",
......@@ -56,5 +56,5 @@
"pre-commit": "lint-staged"
}
},
"gitHead": "d9641c770d9bc4037b2f1cb644fba198bd11a752"
"gitHead": "ed915e4590c1527836cc802d7c6f5011a5b9a4a3"
}
{
"name": "@visualdl/server",
"version": "2.0.0-beta.35",
"version": "2.0.0-beta.40",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
"visualdl",
......@@ -24,7 +24,7 @@
},
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon --watch index.ts --watch webpack.config.js --exec \"ts-node index.ts\"",
"build": "cross-env API_URL=/api ts-node build.ts",
"build": "cross-env API_URL=/api ts-node --script-mode build.ts",
"start": "pm2-runtime ecosystem.config.js",
"test": "echo \"Error: no test specified\" && exit 0"
},
......@@ -36,8 +36,8 @@
"ecosystem.config.d.ts"
],
"dependencies": {
"@visualdl/core": "2.0.0-beta.35",
"@visualdl/i18n": "2.0.0-beta.35",
"@visualdl/core": "2.0.0-beta.40",
"@visualdl/i18n": "2.0.0-beta.40",
"express": "4.17.1",
"http-proxy-middleware": "1.0.3",
"next": "9.3.6",
......@@ -49,7 +49,7 @@
"@types/shelljs": "0.8.7",
"@types/webpack": "4.41.12",
"@types/webpack-dev-middleware": "3.7.0",
"@visualdl/mock": "2.0.0-beta.35",
"@visualdl/mock": "2.0.0-beta.40",
"cross-env": "7.0.2",
"nodemon": "2.0.3",
"shelljs": "0.8.4",
......@@ -72,5 +72,5 @@
"pre-commit": "lint-staged"
}
},
"gitHead": "d9641c770d9bc4037b2f1cb644fba198bd11a752"
"gitHead": "ed915e4590c1527836cc802d7c6f5011a5b9a4a3"
}
......@@ -28,7 +28,11 @@ async function start() {
await builder('build');
await builder('export', '-o', dist);
} catch (e) {
process.exit(e);
if ('number' === typeof e) {
process.exit(e);
}
console.error(e);
process.exit(1);
}
process.exit(0);
}
......
{
"name": "@visualdl/serverless",
"version": "2.0.0-beta.35",
"version": "2.0.0-beta.40",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
"visualdl",
......@@ -27,13 +27,13 @@
"dist"
],
"scripts": {
"build": "cross-env PUBLIC_PATH=/app API_URL=/api ts-node build.ts",
"build": "cross-env ts-node --script-mode build.ts",
"test": "echo \"Error: no test specified\" && exit 0"
},
"devDependencies": {
"@types/node": "13.13.5",
"@types/rimraf": "3.0.0",
"@visualdl/core": "2.0.0-beta.35",
"@visualdl/core": "2.0.0-beta.40",
"cross-env": "7.0.2",
"rimraf": "3.0.2",
"ts-node": "8.10.1",
......@@ -51,5 +51,5 @@
"pre-commit": "lint-staged"
}
},
"gitHead": "d9641c770d9bc4037b2f1cb644fba198bd11a752"
"gitHead": "ed915e4590c1527836cc802d7c6f5011a5b9a4a3"
}
{
"name": "@visualdl/wasm",
"version": "2.0.0-beta.35",
"version": "2.0.0-beta.40",
"title": "VisualDL",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
......@@ -49,5 +49,5 @@
"pre-commit": "lint-staged"
}
},
"gitHead": "d9641c770d9bc4037b2f1cb644fba198bd11a752"
"gitHead": "ed915e4590c1527836cc802d7c6f5011a5b9a4a3"
}
......@@ -2,7 +2,7 @@
set -e
WORKING_PATH=`pwd`
WORKING_PATH=$(pwd)
SERVER_DIR="packages/server/dist"
SERVER_DIR_PATH="$WORKING_PATH/$SERVER_DIR"
SERVERLESS_DIR="packages/serverless/dist"
......@@ -10,15 +10,31 @@ SERVERLESS_DIR_PATH="$WORKING_PATH/$SERVERLESS_DIR"
OUTPUT="output"
OUTPUT_PATH="$WORKING_PATH/$OUTPUT"
# clean
rm -rf "$SERVER_DIR_PATH"
rm -rf "$SERVERLESS_DIR_PATH"
# build
npx lerna run build
if [ "$SCOPE" = "serverless" ]; then
npx lerna run --scope "@visualdl/serverless" --include-dependencies build
elif [ "$SCOPE" = "server" ]; then
npx lerna run --scope "@visualdl/server" --include-dependencies build
elif [ "$SCOPE" = "cli" ]; then
npx lerna run --scope "@visualdl/cli" --include-dependencies build
else
npx lerna run build
fi
# generate output
rm -rf $OUTPUT_PATH
mkdir -p $OUTPUT_PATH
rm -rf "$OUTPUT_PATH"
mkdir -p "$OUTPUT_PATH"
# package server files
(cd $SERVER_DIR_PATH && tar zcf $OUTPUT_PATH/server.tar.gz .)
if [ -d "$SERVER_DIR_PATH" ]; then
(cd "$SERVER_DIR_PATH" && tar zcf "${OUTPUT_PATH}/server.tar.gz" .)
fi
# package serverless files
(cd $SERVERLESS_DIR_PATH && tar zcf $OUTPUT_PATH/serverless.tar.gz .)
if [ -d "$SERVERLESS_DIR_PATH" ]; then
(cd "$SERVERLESS_DIR_PATH" && tar zcf "${OUTPUT_PATH}/serverless.tar.gz" .)
fi
......@@ -6,7 +6,7 @@ set -e
# https://rustup.rs/
if ! hash rustup 2>/dev/null; then
curl https://sh.rustup.rs -sSf | sh -s -- --no-modify-path --default-toolchain nightly -y
source $HOME/.cargo/env
source "$HOME/.cargo/env"
fi
......@@ -18,25 +18,11 @@ fi
# fi
# wine
if hash apt 2>/dev/null; then
sudo dpkg --add-architecture i386
wget -nc https://download.opensuse.org/repositories/Emulators:/Wine:/Debian/xUbuntu_18.04/Release.key
sudo apt-key add Release.key
sudo apt-add-repository 'deb https://download.opensuse.org/repositories/Emulators:/Wine:/Debian/xUbuntu_18.04/ ./'
wget -nc https://dl.winehq.org/wine-builds/winehq.key
sudo apt-key add winehq.key
sudo apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ bionic main'
sudo apt update
sudo apt install --install-recommends winehq-stable
fi
# yarn
curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"
if ! hash yarn 2>/dev/null; then
curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"
fi
# yarn install
yarn install --frozen-lockfile
此差异已折叠。
numpy == 1.16.6; python_version < "3.0"
numpy ; python_version >= "3.0"
recommonmark >= 0.6.0
numpy
requests
scipy == 1.2.3; python_version < "3.0"
scipy >= 1.4.1; python_version >= "3.0"
Sphinx == 1.8.5; python_version < "3.0"
Sphinx >= 2.4.4; python_version >= "3.0"
sphinx-rtd-theme >= 0.4.3
flake8 >= 3.7.9
Pillow == 6.2.2; python_version < "3.0"
Pillow >= 7.0.0; python_version >= "3.0"
......@@ -14,4 +10,4 @@ flask >= 1.1.1
Flask-Babel >= 1.0.0
six >= 1.14.0
protobuf >= 3.1.0
opencv-python
opencv-python
\ No newline at end of file
......@@ -2,106 +2,45 @@
set -e
TOP_DIR=$(pwd)
FRONTEND_DIR=$TOP_DIR/frontend
BACKEND_DIR=$TOP_DIR/visualdl
BUILD_DIR=$TOP_DIR/build
FRONTEND_DIR=${TOP_DIR}/frontend
BUILD_DIR=${TOP_DIR}/build
mkdir -p $BUILD_DIR
mkdir -p "$BUILD_DIR"
build_frontend_fake() {
mkdir -p "$BUILD_DIR/package/dist"
}
build_frontend_from_source() {
build_frontend_fake
cd $FRONTEND_DIR
./scripts/install.sh
./scripts/build.sh
# extract
tar zxf "$FRONTEND_DIR/output/serverless.tar.gz" -C "$BUILD_DIR/package/serverless"
}
build_frontend() {
local PACKAGE="@visualdl/serverless"
local NAME=${PACKAGE#*@}
local NAME=${NAME////-}
echo $NAME
local TAG="latest"
local TARBALL="${PACKAGE}@${TAG}"
# get version
local VERSION=`npm view ${TARBALL} dist-tags.${TAG}`
if [ "$?" -ne "0" ]; then
echo "Cannot get version"
exit 1
fi
local FILENAME="${NAME}-${VERSION}.tgz"
# get sha1sum
local SHA1SUM=`npm view ${TARBALL} dist.shasum`
if [ "$?" -ne "0" ]; then
echo "Cannot get sha1sum"
exit 1
fi
rm -f "$BUILD_DIR/${NAME}-*.tgz.sha1"
echo "${SHA1SUM} ${FILENAME}" > "$BUILD_DIR/${FILENAME}.sha1"
local DOWNLOAD="1"
# cached file exists
if [ -f "$BUILD_DIR/$FILENAME" ]; then
# check sha1sum
(cd $BUILD_DIR && sha1sum -c "${FILENAME}.sha1")
# check pass, use chached file
if [ "$?" -eq "0" ]; then
echo "Using cached npm package file ${FILENAME}"
DOWNLOAD="0"
fi
fi
if [ "$DOWNLOAD" -eq "1" ]; then
echo "Donwloading npm package, please wait..."
# remove cache
rm -f "$BUILD_DIR/${NAME}-*.tgz"
# download file
FILENAME=`(cd $BUILD_DIR && npm pack ${TARBALL})`
mkdir -p "$BUILD_DIR/package/dist"
# check sha1sum of downloaded file
(cd $BUILD_DIR && sha1sum -c "${FILENAME}.sha1")
if [ "$?" -ne "0" ]; then
echo "Check sum failed, download may not finish correctly."
exit 1
else
echo "Check sum pass."
fi
fi
cd "$FRONTEND_DIR"
./scripts/install.sh
SCOPE="serverless" PUBLIC_PATH="/{{PUBLIC_PATH}}" API_URL="/{{PUBLIC_PATH}}/api" ./scripts/build.sh
# extract
tar zxf "$BUILD_DIR/$FILENAME" -C "$BUILD_DIR"
tar zxf "$FRONTEND_DIR/output/serverless.tar.gz" -C "$BUILD_DIR/package/dist"
}
clean_env() {
rm -rf $TOP_DIR/visualdl/server/dist
rm -rf $BUILD_DIR/bdist*
rm -rf $BUILD_DIR/lib*
rm -rf $BUILD_DIR/temp*
rm -rf $BUILD_DIR/scripts*
rm -rf $BUILD_DIR/package
rm -rf "$TOP_DIR/visualdl/server/dist"
rm -rf "$BUILD_DIR/bdist*"
rm -rf "$BUILD_DIR/lib*"
rm -rf "$BUILD_DIR/temp*"
rm -rf "$BUILD_DIR/scripts*"
rm -rf "$BUILD_DIR/package"
}
package() {
cp -rf $BUILD_DIR/package/dist $TOP_DIR/visualdl/server/
cp -rf "$BUILD_DIR/package/dist" "$TOP_DIR/visualdl/server/"
}
ARG=$1
echo "ARG: " $ARG
echo "ARG: ${ARG}"
clean_env
if [ "$ARG" = "travis-CI" ]; then
if [[ "$ARG" = "travis-CI" ]]; then
build_frontend_fake
else
build_frontend
......
......@@ -19,6 +19,7 @@ import json
import os
import time
import sys
import signal
import multiprocessing
import threading
import re
......@@ -27,12 +28,11 @@ import requests
from visualdl.reader.reader import LogReader
from argparse import ArgumentParser
from flask import (Flask, Response, redirect, request, send_file,
send_from_directory)
from flask import (Flask, Response, redirect, request, send_file, send_from_directory)
from flask_babel import Babel
import visualdl.server
from visualdl.server import lib
from visualdl.server import (lib, template)
from visualdl.server.log import logger
from visualdl.python.cache import MemCache
......@@ -45,7 +45,8 @@ support_language = ["en", "zh"]
default_language = support_language[0]
server_path = os.path.abspath(os.path.dirname(sys.argv[0]))
static_file_path = os.path.join(SERVER_DIR, "./dist")
static_file_path = os.path.join(SERVER_DIR, "./static")
template_file_path = os.path.join(SERVER_DIR, "./dist")
mock_data_path = os.path.join(SERVER_DIR, "./mock_data/")
......@@ -56,13 +57,15 @@ class ParseArgs(object):
port=8040,
model_pb="",
cache_timeout=20,
language=None):
language=None,
public_path=None):
self.logdir = logdir
self.host = host
self.port = port
self.model_pb = model_pb
self.cache_timeout = cache_timeout
self.language = language
self.public_path = public_path
def try_call(function, *args, **kwargs):
......@@ -120,6 +123,14 @@ def parse_args():
type=str,
action="store",
help="set the default language")
parser.add_argument(
"-P",
"--public-path",
type=str,
action="store",
default="/app",
help="set public path"
)
args = parser.parse_args()
if not args.logdir:
......@@ -155,94 +166,86 @@ def create_app(args):
CACHE = MemCache(timeout=args.cache_timeout)
cache_get = lib.cache_get(CACHE)
public_path = args.public_path.rstrip('/')
api_path = public_path + '/api'
@babel.localeselector
def get_locale():
language = args.language
if not language or language not in support_language:
language = request.accept_languages.best_match(support_language)
return language
lang = args.language
if not lang or lang not in support_language:
lang = request.accept_languages.best_match(support_language)
return lang
@app.route("/")
def base():
return redirect(public_path, code=302)
@app.route(public_path + "/")
def index():
language = get_locale()
if language == default_language:
return redirect('/app/index', code=302)
return redirect('/app/' + language + '/index', code=302)
lang = get_locale()
if lang == default_language:
return redirect(public_path + '/index', code=302)
return redirect(public_path + '/' + lang + '/index', code=302)
@app.route('/app/<path:filename>')
@app.route(public_path + '/<path:filename>')
def serve_static(filename):
return send_from_directory(
os.path.join(server_path, static_file_path), filename
if re.search(r'\..+$', filename) else filename + '.html')
@app.route('/graphs/image')
def serve_graph():
return send_file(os.path.join(os.getcwd(), graph_image_path))
@app.route('/api/logdir')
def logdir():
result = gen_result(0, "", {"logdir": args.logdir})
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/language')
def language():
data = get_locale()
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route("/api/components")
@app.route(api_path + "/components")
def components():
data = cache_get('/data/components', lib.get_components, log_reader)
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/runs')
@app.route(api_path + '/runs')
def runs():
data = cache_get('/data/runs', lib.get_runs, log_reader)
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/tags')
@app.route(api_path + '/tags')
def tags():
data = cache_get('/data/tags', lib.get_tags, log_reader)
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/logs')
@app.route(api_path + '/logs')
def logs():
data = cache_get('/data/logs', lib.get_logs, log_reader)
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route("/api/scalars/tags")
@app.route(api_path + "/scalars/tags")
def scalar_tags():
data = cache_get("/data/plugin/scalars/tags", try_call,
lib.get_scalar_tags, log_reader)
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route("/api/images/tags")
@app.route(api_path + "/images/tags")
def image_tags():
data = cache_get("/data/plugin/images/tags", try_call,
lib.get_image_tags, log_reader)
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route("/api/audio/tags")
@app.route(api_path + "/audio/tags")
def audio_tags():
data = cache_get("/data/plugin/audio/tags", try_call,
lib.get_audio_tags, log_reader)
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route("/api/embeddings/tags")
@app.route(api_path + "/embeddings/tags")
def embeddings_tags():
data = cache_get("/data/plugin/embeddings/tags", try_call,
lib.get_embeddings_tags, log_reader)
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/scalars/list')
@app.route(api_path + '/scalars/list')
def scalars():
run = request.args.get('run')
tag = request.args.get('tag')
......@@ -251,7 +254,7 @@ def create_app(args):
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/images/list')
@app.route(api_path + '/images/list')
def images():
mode = request.args.get('run')
tag = request.args.get('tag')
......@@ -263,7 +266,7 @@ def create_app(args):
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/images/image')
@app.route(api_path + '/images/image')
def individual_image():
mode = request.args.get('run')
tag = request.args.get('tag') # include a index
......@@ -275,7 +278,7 @@ def create_app(args):
mode, tag, step_index)
return Response(data, mimetype="image/png")
@app.route('/api/embeddings/embedding')
@app.route(api_path + '/embeddings/embedding')
def embeddings():
run = request.args.get('run')
tag = request.args.get('tag', 'default')
......@@ -288,7 +291,7 @@ def create_app(args):
result = gen_result(0, "", data)
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/audio/list')
@app.route(api_path + '/audio/list')
def audio():
run = request.args.get('run')
tag = request.args.get('tag')
......@@ -300,7 +303,7 @@ def create_app(args):
return Response(json.dumps(result), mimetype='application/json')
@app.route('/api/audio/audio')
@app.route(api_path + '/audio/audio')
def individual_audio():
run = request.args.get('run')
tag = request.args.get('tag') # include a index
......@@ -319,6 +322,7 @@ def create_app(args):
def _open_browser(app, index_url):
while True:
# noinspection PyBroadException
try:
requests.get(index_url)
break
......@@ -333,6 +337,7 @@ def _run(logdir,
model_pb="",
cache_timeout=20,
language=None,
public_path="/app",
open_browser=False):
args = ParseArgs(
logdir=logdir,
......@@ -340,10 +345,11 @@ def _run(logdir,
port=port,
model_pb=model_pb,
cache_timeout=cache_timeout,
language=language)
language=language,
public_path=public_path)
logger.info(" port=" + str(args.port))
app = create_app(args)
index_url = "http://" + host + ":" + str(port)
index_url = "http://" + host + ":" + str(port) + args.public_path
if open_browser:
threading.Thread(
target=_open_browser, kwargs={"app": app,
......@@ -357,6 +363,7 @@ def run(logdir,
model_pb="",
cache_timeout=20,
language=None,
public_path="/app",
open_browser=False):
kwarg = {
"logdir": logdir,
......@@ -365,6 +372,7 @@ def run(logdir,
"model_pb": model_pb,
"cache_timeout": cache_timeout,
"language": language,
"public_path": public_path,
"open_browser": open_browser
}
......@@ -373,16 +381,27 @@ def run(logdir,
return p.pid
def render_template(args):
template.render(
template_file_path,
static_file_path,
PUBLIC_PATH=args.public_path.strip('/'))
def clean_template(signalnum, frame):
template.clean(static_file_path)
sys.exit(0)
def main():
args = parse_args()
render_template(args)
for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clean_template)
logger.info(" port=" + str(args.port))
app = create_app(args=args)
app.run(debug=False, host=args.host, port=args.port, threaded=False)
if __name__ == "__main__":
args = parse_args()
logger.info(" port=" + str(args.port))
app = create_app(args=args)
app.run(debug=False, host=args.host, port=args.port, threaded=False)
main()
# Copyright (c) 2017 VisualDL Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =======================================================================
import os
from shutil import (copytree, rmtree)
def render(path, dest, **context):
clean(dest)
copytree(path, dest)
for root, dirs, files in os.walk(dest):
for file in files:
if file.endswith(".html") or file.endswith(".js") or file.endswith(".css"):
file_path = os.path.join(root, file)
content = ""
with open(file_path, "r") as f:
content = f.read()
for key, value in context.items():
content = content.replace("{{" + key + "}}", value)
with open(file_path, "w") as f:
f.write(content)
def clean(path):
if os.path.exists(path):
rmtree(path)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册