diff --git a/apps/go.work b/apps/go.work deleted file mode 100644 index 8c0bcc8c..00000000 --- a/apps/go.work +++ /dev/null @@ -1 +0,0 @@ -go 1.18 diff --git a/apps/rath-service/README.md b/apps/rath-service/README.md new file mode 100644 index 00000000..ccfb69c6 --- /dev/null +++ b/apps/rath-service/README.md @@ -0,0 +1,3 @@ +## WIP RATH Service :construction: + +designed for RATH's server side persistent storage. \ No newline at end of file diff --git a/apps/testDatasets/sales.sql b/apps/test-datasets/sales.sql similarity index 100% rename from apps/testDatasets/sales.sql rename to apps/test-datasets/sales.sql diff --git a/buildspec.yml b/buildspec.yml index 69a87b89..d11685ea 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -12,7 +12,6 @@ phases: build: commands: - echo Building... - - yarn workspace @kanaries/graphic-walker build - yarn workspace rath-client build2 artifacts: files: diff --git a/package.json b/package.json index f4fde654..c2d7f64b 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,19 @@ { - "name": "visual-insights", + "name": "Rath", "private": true, "version": "1.0.0", - "description": "![](https://www.travis-ci.org/ObservedObserver/showme.svg?branch=master) ![](https://img.shields.io/github/license/ObservedObserver/showme)", + "description": "![](https://img.shields.io/github/license/Kanaries/Rath)", "scripts": { - "build": "yarn workspace graphic-walker build && yarn workspace frontend build", - "test": "yarn workspace frontend test", + "build": "yarn workspace rath-client build", + "test": "yarn workspace rath-client test", "ui": "npm run test && npm run build && node start.js", - "devfront": "yarn workspace frontend start", + "devfront": "yarn workspace rath-client start", "devback": "yarn workspace backend dev", "count": "cloc --exclude-dir=$(tr '\n' ',' < .clocignore) ." }, "repository": { "type": "git", - "url": "git+https://github.com/ObservedObserver/showme.git" + "url": "git+https://github.com/Kanaries/Rath.git" }, "keywords": [ "insights", @@ -22,7 +22,7 @@ ], "author": "Observed Observer", "bugs": { - "url": "https://github.com/ObservedObserver/showme/issues" + "url": "https://github.com/Kanaries/Rath/issues" }, "workspaces": { "packages": [ @@ -42,7 +42,7 @@ "@types/react-dom": "^17.x", "vega-scenegraph": "4.10.1-kanaries-patch" }, - "homepage": "https://github.com/ObservedObserver/showme#readme", + "homepage": "https://github.com/Kanaries/Rath#readme", "devDependencies": { "concurrently": "^4.1.2", "yarn": "^1.19.0" diff --git a/packages/connectors/app-config.default.json b/packages/olap-connector/app-config.default.json similarity index 100% rename from packages/connectors/app-config.default.json rename to packages/olap-connector/app-config.default.json diff --git a/packages/connectors/app-config.json b/packages/olap-connector/app-config.json similarity index 100% rename from packages/connectors/app-config.json rename to packages/olap-connector/app-config.json diff --git a/packages/connectors/app/constants.ts b/packages/olap-connector/app/constants.ts similarity index 100% rename from packages/connectors/app/constants.ts rename to packages/olap-connector/app/constants.ts diff --git a/packages/connectors/app/controllers/clickHouseProxy.ts b/packages/olap-connector/app/controllers/clickHouseProxy.ts similarity index 100% rename from packages/connectors/app/controllers/clickHouseProxy.ts rename to packages/olap-connector/app/controllers/clickHouseProxy.ts diff --git a/packages/connectors/app/controllers/connection.ts b/packages/olap-connector/app/controllers/connection.ts similarity index 100% rename from packages/connectors/app/controllers/connection.ts rename to packages/olap-connector/app/controllers/connection.ts diff --git a/packages/connectors/app/controllers/index.ts b/packages/olap-connector/app/controllers/index.ts similarity index 100% rename from packages/connectors/app/controllers/index.ts rename to packages/olap-connector/app/controllers/index.ts diff --git a/packages/connectors/app/interfaces.ts b/packages/olap-connector/app/interfaces.ts similarity index 100% rename from packages/connectors/app/interfaces.ts rename to packages/olap-connector/app/interfaces.ts diff --git a/packages/connectors/app/main.ts b/packages/olap-connector/app/main.ts similarity index 100% rename from packages/connectors/app/main.ts rename to packages/olap-connector/app/main.ts diff --git a/packages/connectors/app/router.ts b/packages/olap-connector/app/router.ts similarity index 100% rename from packages/connectors/app/router.ts rename to packages/olap-connector/app/router.ts diff --git a/packages/connectors/app/services/chmeta.ts b/packages/olap-connector/app/services/chmeta.ts similarity index 100% rename from packages/connectors/app/services/chmeta.ts rename to packages/olap-connector/app/services/chmeta.ts diff --git a/packages/connectors/app/services/chparser.ts b/packages/olap-connector/app/services/chparser.ts similarity index 100% rename from packages/connectors/app/services/chparser.ts rename to packages/olap-connector/app/services/chparser.ts diff --git a/packages/connectors/app/store.ts b/packages/olap-connector/app/store.ts similarity index 100% rename from packages/connectors/app/store.ts rename to packages/olap-connector/app/store.ts diff --git a/packages/connectors/app/utils.ts b/packages/olap-connector/app/utils.ts similarity index 100% rename from packages/connectors/app/utils.ts rename to packages/olap-connector/app/utils.ts diff --git a/packages/connectors/https.sh b/packages/olap-connector/https.sh similarity index 100% rename from packages/connectors/https.sh rename to packages/olap-connector/https.sh diff --git a/packages/connectors/package.json b/packages/olap-connector/package.json similarity index 95% rename from packages/connectors/package.json rename to packages/olap-connector/package.json index 0726df12..3b658cef 100644 --- a/packages/connectors/package.json +++ b/packages/olap-connector/package.json @@ -1,5 +1,5 @@ { - "name": "connectors", + "name": "olap-connector", "version": "1.0.0", "description": "", "scripts": { diff --git a/packages/connectors/security/req.cnf b/packages/olap-connector/security/req.cnf similarity index 100% rename from packages/connectors/security/req.cnf rename to packages/olap-connector/security/req.cnf diff --git a/packages/connectors/tsconfig.json b/packages/olap-connector/tsconfig.json similarity index 100% rename from packages/connectors/tsconfig.json rename to packages/olap-connector/tsconfig.json diff --git a/packages/rath-client/public/locales/en-US.json b/packages/rath-client/public/locales/en-US.json index 047f7b9d..0e9e3187 100644 --- a/packages/rath-client/public/locales/en-US.json +++ b/packages/rath-client/public/locales/en-US.json @@ -40,16 +40,16 @@ }, "menu": { "dataSource": "DataSource", - "editor": "Manual Exploration⭐️", + "editor": "Manual Exploration", "vizPipe": "interactive Pipeline", "support": "Support", "megaAuto": "Mega-auto Exploration", - "semiAuto": "Semi-auto Exploration⭐️", + "semiAuto": "Semi-auto Exploration", "devCollection": "Insiders(α)", "dashBoardDesigner": "Dashboard Designer", - "painter": "Data Painter🚀", + "painter": "Data Painter", "collection": "Collection", - "dashboard": "Dashboard📋" + "dashboard": "Dashboard" }, "preference": { "title": "preference", @@ -79,8 +79,8 @@ "analysis": { "start": "Start Analysis", "checkResult": "Check Existed Results", - "pattern": "Data Copilot", - "manual": "Manual Explore" + "pattern": "Semi-auto Exploration", + "manual": "Manual Exploration" }, "importStorage": { "title": "Import History Storage", @@ -123,6 +123,7 @@ "databaseName": "Database", "schemaName": "Schema", "tableName": "Table", + "editName": "Edit Name", "preview": "Data Preview", "query": "SQL Query", "connectorConfig": "Connector Settings", diff --git a/packages/rath-client/public/locales/zh-CN.json b/packages/rath-client/public/locales/zh-CN.json index 48ea5ac3..eb98d615 100644 --- a/packages/rath-client/public/locales/zh-CN.json +++ b/packages/rath-client/public/locales/zh-CN.json @@ -40,15 +40,15 @@ }, "menu": { "dataSource": "数据源", - "editor": "自助分析⭐️", + "editor": "自助分析", "support": "帮助", "megaAuto": "全自动分析", - "semiAuto": "半自动探索⭐️", + "semiAuto": "半自动探索", "devCollection": "实验特性(α)", "dashBoardDesigner": "智能报表", - "painter": "数据绘板🚀", + "painter": "数据绘板", "collection": "收藏夹", - "dashboard": "报表📋" + "dashboard": "报表" }, "preference": { "title": "个人偏好", @@ -78,7 +78,7 @@ "analysis": { "start": "开始分析", "checkResult": "查看已有结果", - "pattern": "半自动化分析", + "pattern": "半自动化探索", "manual": "自助分析" }, "importStorage": { @@ -119,9 +119,10 @@ "charset": "字符集编码", "databaseType": "选择数据库", "connectUri": "连接 URI", - "databaseName": "Database", + "databaseName": "数据库", "schemaName": "Schema", - "tableName": "Table", + "tableName": "表", + "editName": "编辑名称", "preview": "数据预览", "query": "SQL 查询", "connectorConfig": "连接器设置", diff --git a/packages/rath-client/src/App.tsx b/packages/rath-client/src/App.tsx index e34947f3..80458371 100644 --- a/packages/rath-client/src/App.tsx +++ b/packages/rath-client/src/App.tsx @@ -18,59 +18,56 @@ import Painter from './pages/painter'; import Collection from './pages/collection'; import Dashboard from './pages/dashboard'; - function App() { - const { langStore, commonStore } = useGlobalStore(); - const { appKey, navMode } = commonStore; + const { langStore, commonStore } = useGlobalStore(); + const { appKey, navMode } = commonStore; - useEffect(() => { - initRathWorker(commonStore.computationEngine); - return () => { - destroyRathWorker(); - }; - }, [commonStore]); + useEffect(() => { + initRathWorker(commonStore.computationEngine); + return () => { + destroyRathWorker(); + }; + }, [commonStore]); - if (!langStore.loaded) { + if (!langStore.loaded) { + return ( +
+ +
+ ); + } return ( -
- -
-) - } - return ( -
-
-
- -
-
-
- +
+
+
+ +
+
+
+ +
+ {appKey === PIVOT_KEYS.dataSource && } + {appKey === PIVOT_KEYS.editor && } + {appKey === PIVOT_KEYS.support && } + {appKey === PIVOT_KEYS.megaAuto && } + {appKey === PIVOT_KEYS.semiAuto && } + {appKey === PIVOT_KEYS.painter && } + {appKey === PIVOT_KEYS.dashBoardDesigner && } + {appKey === PIVOT_KEYS.collection && } + {appKey === PIVOT_KEYS.dashboard && } + +
+
- {appKey === PIVOT_KEYS.dataSource && ( - - )} - {appKey === PIVOT_KEYS.editor && } - {appKey === PIVOT_KEYS.support && } - {appKey === PIVOT_KEYS.megaAuto && } - {appKey === PIVOT_KEYS.semiAuto && } - {appKey === PIVOT_KEYS.painter && } - {appKey === PIVOT_KEYS.dashBoardDesigner && } - {appKey === PIVOT_KEYS.collection && } - {appKey === PIVOT_KEYS.dashboard && } - -
-
-
- ) + ); } const OBApp = observer(App); export default function WrappedApp(): JSX.Element { - return ( - - - - ); + return ( + + + + ); } diff --git a/packages/rath-client/src/components/dbGraph/index.tsx b/packages/rath-client/src/components/dbGraph/index.tsx index de8b6977..a92ac05d 100644 --- a/packages/rath-client/src/components/dbGraph/index.tsx +++ b/packages/rath-client/src/components/dbGraph/index.tsx @@ -69,27 +69,27 @@ export const DBBox = styled.span` background-color: #f3f3f3; } `; -const Output = styled.div({ - flexGrow: 1, - flexShrink: 1, - overflow: 'hidden', - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - border: '1px solid', - borderTop: 'none', - marginBottom: '1em', - - '> span': { - flexGrow: 1, - flexShrink: 1, - padding: '0 1em', - overflow: 'hidden', +const Output = styled.div` + flex-grow: 1; + flex-shrink: 1; + overflow: hidden; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + border: 1px solid; + border-top: none; + margin-bottom: 1em; + + > span { + flex-grow: 1; + flex-shrink: 1; + padding: 0 1em; + overflow: hidden; // textOverflow: 'ellipsis', - maxWidth: '30vw', - }, -}); + max-width: 30vw; + } +` const STROKE_RADIUS = 12; diff --git a/packages/rath-client/src/components/fieldFilter/index.tsx b/packages/rath-client/src/components/fieldFilter/index.tsx index 7d525a12..4904ea48 100644 --- a/packages/rath-client/src/components/fieldFilter/index.tsx +++ b/packages/rath-client/src/components/fieldFilter/index.tsx @@ -10,7 +10,6 @@ import { useGlobalStore } from '../../store'; import RangeSelection from './rangeSelection'; import SetSelection from './setSelection'; - interface FieldFilterProps { fid: string; } diff --git a/packages/rath-client/src/components/react-vega.tsx b/packages/rath-client/src/components/react-vega.tsx index 9f492198..9cb0f269 100644 --- a/packages/rath-client/src/components/react-vega.tsx +++ b/packages/rath-client/src/components/react-vega.tsx @@ -5,87 +5,93 @@ import embed, { vega } from 'vega-embed'; import { EDITOR_URL } from '../constants'; interface ReactVegaProps { - dataSource: any[]; - spec: any; - actions?: boolean; - signalHandler?: { - [key: string]: (name: any, value: any, view: View) => void - } + dataSource: any[]; + spec: any; + actions?: boolean; + signalHandler?: { + [key: string]: (name: any, value: any, view: View) => void; + }; } -const ReactVega: React.FC = props => { - const { spec, dataSource, signalHandler = {}, actions } = props - const container = useRef(null); - const viewRef = useRef(); - useEffect(() => { - if (container.current) { - const sspec = { - ...spec, - data: { - ...spec.data, +const ReactVega: React.FC = (props) => { + const { spec, dataSource, signalHandler = {}, actions } = props; + const container = useRef(null); + const viewRef = useRef(); + useEffect(() => { + if (container.current) { + const sspec = { + ...spec, + data: { + ...spec.data, + }, + }; + if (spec.data) { + sspec.data = { + ...spec.data, + }; + } + sspec.data.values = dataSource; + embed(container.current, sspec, { + editorUrl: EDITOR_URL, + timeFormatLocale: intl.get('time_format') as any, + actions, + }).then((res) => { + const view = res.view; + viewRef.current = view; + for (let key in signalHandler) { + try { + view.addSignalListener(key, (n, v) => signalHandler[key](n, v, view)); + } catch (error) { + console.warn(error); + } + } + }); } - }; - if (spec.data) { - sspec.data = { - ...spec.data - } - } - sspec.data.values = dataSource; - embed(container.current, sspec, { - editorUrl: EDITOR_URL, - timeFormatLocale: intl.get('time_format') as any, - actions - }).then(res => { - const view = res.view; - viewRef.current = view; - for (let key in signalHandler) { - try { - view.addSignalListener(key, (n, v) => signalHandler[key](n, v, view)); - } catch (error) { - console.warn(error); - } - } - }) - } - return () => { - if (viewRef.current) { - viewRef.current.finalize(); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [spec, actions]) + return () => { + if (viewRef.current) { + viewRef.current.finalize(); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [spec, actions]); - useEffect(() => { - if (viewRef.current && signalHandler) { - for (let key in signalHandler) { - try { - viewRef.current.addSignalListener(key, (n, v) => signalHandler[key](n, v, viewRef.current!)); - } catch (error) { - console.warn(error); - } - } - } - return () => { - if (viewRef.current && signalHandler) { - for (let key in signalHandler) { - try { - viewRef.current.removeSignalListener(key, (n, v) => signalHandler[key](n, v, viewRef.current!)); - } catch (error) { - console.warn(error); - } + useEffect(() => { + if (viewRef.current && signalHandler) { + for (let key in signalHandler) { + try { + viewRef.current.addSignalListener(key, (n, v) => signalHandler[key](n, v, viewRef.current!)); + } catch (error) { + console.warn(error); + } + } } - } - } - }, [signalHandler]) + return () => { + if (viewRef.current && signalHandler) { + for (let key in signalHandler) { + try { + viewRef.current.removeSignalListener(key, (n, v) => signalHandler[key](n, v, viewRef.current!)); + } catch (error) { + console.warn(error); + } + } + } + }; + }, [signalHandler]); - useEffect(() => { - if (viewRef.current) { - viewRef.current.change('dataSource', vega.changeset().remove(() => true).insert(dataSource)); - viewRef.current.resize(); - viewRef.current.runAsync(); - } - }, [dataSource]) - return
-} + useEffect(() => { + if (viewRef.current) { + viewRef.current.change( + 'dataSource', + vega + .changeset() + .remove(() => true) + .insert(dataSource) + ); + viewRef.current.resize(); + viewRef.current.runAsync(); + } + }, [dataSource]); + return
; +}; export default ReactVega; diff --git a/packages/rath-client/src/components/simpleTick.tsx b/packages/rath-client/src/components/simpleTick.tsx index 8bd53819..ebbd4833 100644 --- a/packages/rath-client/src/components/simpleTick.tsx +++ b/packages/rath-client/src/components/simpleTick.tsx @@ -3,58 +3,58 @@ import { IRow } from 'visual-insights'; import ReactVega from './react-vega'; interface SimpleTickProps { - x: string; - y: string; - threshold: number; - dataSource: IRow[] + x: string; + y: string; + threshold: number; + dataSource: IRow[]; } -const SimpleTick: React.FC = props => { - const { x, y, threshold, dataSource = [] } = props; - const spec = useMemo(() => { - return { - width: 180, - height: 200, - data: { - name: 'dataSource' - }, - transform: [ - { calculate: threshold.toString(), as: 'threshold' } - ], - layer: [ - { - mark: 'point', - encoding: { - x: { - field: x, - type: 'nominal' +const SimpleTick: React.FC = (props) => { + const { x, y, threshold, dataSource = [] } = props; + const spec = useMemo(() => { + return { + width: 180, + height: 200, + data: { + name: 'dataSource', }, - y: { - field: y, - type: 'quantitative', - scale: { - domain: [0, 1] - } - } - } - }, - { - mark: 'rule', - encoding: { - y: { - field: 'threshold', - type: 'quantitative' - }, - color: { - value: 'red' - } - } - } - ] - } - }, [x, y, threshold]) - return
- -
; -} + transform: [{ calculate: threshold.toString(), as: 'threshold' }], + layer: [ + { + mark: 'point', + encoding: { + x: { + field: x, + type: 'nominal', + }, + y: { + field: y, + type: 'quantitative', + scale: { + domain: [0, 1], + }, + }, + }, + }, + { + mark: 'rule', + encoding: { + y: { + field: 'threshold', + type: 'quantitative', + }, + color: { + value: 'red', + }, + }, + }, + ], + }; + }, [x, y, threshold]); + return ( +
+ +
+ ); +}; -export default SimpleTick; \ No newline at end of file +export default SimpleTick; diff --git a/packages/rath-client/src/constants.ts b/packages/rath-client/src/constants.ts index 99bd7310..7bbcc02f 100644 --- a/packages/rath-client/src/constants.ts +++ b/packages/rath-client/src/constants.ts @@ -1,4 +1,4 @@ -import { IECStatus } from "./interfaces"; +import { IECStatus } from './interfaces'; export const RATH_INDEX_COLUMN_KEY = '__rath_index_col_key__'; @@ -12,30 +12,30 @@ export const PIVOT_KEYS = { painter: 'painter', collection: 'collection', dashboard: 'dashboard', - } as const; +} as const; export const COMPUTATION_ENGINE = { - clickhouse: 'clickhouse', - webworker: 'webworker' -} + clickhouse: 'clickhouse', + webworker: 'webworker', +}; export const EXPLORE_MODE = { - first: 'first', - familiar: 'familiar', - comprehensive: 'comprehensive', - manual: 'manual' -} + first: 'first', + familiar: 'familiar', + comprehensive: 'comprehensive', + manual: 'manual', +}; export const DEMO_DATA_REQUEST_TIMEOUT = 1000 * 10; export const ENGINE_CONNECTION_STAGES: Array<{ stage: number; name: IECStatus; description?: string }> = [ - { stage: 0, name: 'client', description: 'client module importetd.' }, - { stage: 1, name: 'proxy', description: 'database proxy connector lanuched.' }, - { stage: 2, name: 'engine', description: 'clickhouse connected.' } + { stage: 0, name: 'client', description: 'client module importetd.' }, + { stage: 1, name: 'proxy', description: 'database proxy connector lanuched.' }, + { stage: 2, name: 'engine', description: 'clickhouse connected.' }, ]; -export const RESULT_STORAGE_SPLITOR = '\n===RATH_STORAGE_SPLITOR===\n' +export const RESULT_STORAGE_SPLITOR = '\n===RATH_STORAGE_SPLITOR===\n'; -export const STORAGE_FILE_SUFFIX = 'krs' +export const STORAGE_FILE_SUFFIX = 'krs'; -export const EDITOR_URL = 'https://kanaries.cn/vega-editor/' \ No newline at end of file +export const EDITOR_URL = 'https://kanaries.cn/vega-editor/'; diff --git a/packages/rath-client/src/dev/services.ts b/packages/rath-client/src/dev/services.ts index 2f9ed104..f9edf29f 100644 --- a/packages/rath-client/src/dev/services.ts +++ b/packages/rath-client/src/dev/services.ts @@ -1,16 +1,16 @@ -import { IRow } from "visual-insights"; -import { IRawField } from "../interfaces"; -import { workerService } from "../services"; +import { IRow } from 'visual-insights'; +import { IRawField } from '../interfaces'; +import { workerService } from '../services'; /* eslint import/no-webpack-loader-syntax:0 */ // @ts-ignore // eslint-disable-next-line import ExpandDateTimeWorker from './workers/dateTimeExpand.worker.js?worker'; -import { dateTimeExpand, doTest } from './workers/engine/dateTimeExpand' -import { checkExpandEnv } from './workers/engine/checkExpandEnv' +import { dateTimeExpand, doTest } from './workers/engine/dateTimeExpand'; +import { checkExpandEnv } from './workers/engine/checkExpandEnv'; interface ExpandDateTimeProps { - dataSource: IRow[]; - fields: IRawField[]; + dataSource: IRow[]; + fields: IRawField[]; } const ExpandEnv = checkExpandEnv(); @@ -18,26 +18,24 @@ const ExpandEnv = checkExpandEnv(); /** * @deprecated use generic fields extension API */ -export async function expandDateTimeService (props: ExpandDateTimeProps): Promise { - if (ExpandEnv === 'debug') { - doTest() - let res = dateTimeExpand(props) as ExpandDateTimeProps - return res - } - else { - try { - const worker = new ExpandDateTimeWorker() - const result = await workerService(worker, props) - worker.terminate(); - if (result.success) { - return result.data - } - else { - throw new Error(`[ExpandDateTimeWorker]: ${result.message}`) - } - } catch (error) { - console.error(`[ExpandDateTimeWorker]: ${error}`) - throw error; +export async function expandDateTimeService(props: ExpandDateTimeProps): Promise { + if (ExpandEnv === 'debug') { + doTest(); + let res = dateTimeExpand(props) as ExpandDateTimeProps; + return res; + } else { + try { + const worker = new ExpandDateTimeWorker(); + const result = await workerService(worker, props); + worker.terminate(); + if (result.success) { + return result.data; + } else { + throw new Error(`[ExpandDateTimeWorker]: ${result.message}`); + } + } catch (error) { + console.error(`[ExpandDateTimeWorker]: ${error}`); + throw error; + } } - } -} \ No newline at end of file +} diff --git a/packages/rath-client/src/dev/workers/engine/checkExpandEnv.ts b/packages/rath-client/src/dev/workers/engine/checkExpandEnv.ts index 544a0b68..c56e27f0 100644 --- a/packages/rath-client/src/dev/workers/engine/checkExpandEnv.ts +++ b/packages/rath-client/src/dev/workers/engine/checkExpandEnv.ts @@ -1,12 +1,11 @@ export function checkExpandEnv(): string { - if (typeof window === 'object') { - const url = new URL(window.location.href).searchParams.get('expand'); - if(url) { - (window as any).ExpandEnv = url - return url + if (typeof window === 'object') { + const url = new URL(window.location.href).searchParams.get('expand'); + if (url) { + (window as any).ExpandEnv = url; + return url; + } else return ''; } - else return '' - } - if (process.env.EXPAND_ENV) return process.env.EXPAND_ENV - else return '' -} \ No newline at end of file + if (process.env.EXPAND_ENV) return process.env.EXPAND_ENV; + else return ''; +} diff --git a/packages/rath-client/src/global.ts b/packages/rath-client/src/global.ts index e0cbce75..d6845b99 100644 --- a/packages/rath-client/src/global.ts +++ b/packages/rath-client/src/global.ts @@ -3,23 +3,23 @@ export type Aggregator = 'sum' | 'mean' | 'count'; export type OperatorType = 'sum' | 'mean' | 'count'; export enum IDataSourceType { - FILE = 'file', - RESTFUL = 'restful', - DATABASE = 'database', - DEMO = 'demo', - OLAP = 'olap', - LOCAL = 'local', - AIRTABLE = 'airtable' + FILE = 'file', + RESTFUL = 'restful', + DATABASE = 'database', + DEMO = 'demo', + OLAP = 'olap', + LOCAL = 'local', + AIRTABLE = 'airtable', } export const globalRef: { - baseVisSpec: any + baseVisSpec: any; } = { - baseVisSpec: null -} + baseVisSpec: null, +}; export const AGGREGATION_LIST: Array<{ key: Aggregator; text: string }> = [ - { key: "sum", text: "Sum" }, - { key: "count", text: "Count" }, - { key: "mean", text: "Mean" }, -]; \ No newline at end of file + { key: 'sum', text: 'Sum' }, + { key: 'count', text: 'Count' }, + { key: 'mean', text: 'Mean' }, +]; diff --git a/packages/rath-client/src/hooks/index.ts b/packages/rath-client/src/hooks/index.ts index c996157c..ee38f29f 100644 --- a/packages/rath-client/src/hooks/index.ts +++ b/packages/rath-client/src/hooks/index.ts @@ -1,17 +1,17 @@ import { useState, useCallback, useMemo } from 'react'; import produce, { Draft } from 'immer'; -import intl from "react-intl-universal"; +import intl from 'react-intl-universal'; import { CleanMethod } from '../interfaces'; /** * @param S type of the composed state */ -export type StateUpdater = (draftState: Draft) => void +export type StateUpdater = (draftState: Draft) => void; /** - * + * * @param initState * useComposeState helps you manage several state together, provided with immutable state change api - * + * * example: * ```js * const [state, setState] = useComposeState({foo: 12, bar: { foo: 20}}) @@ -21,30 +21,33 @@ export type StateUpdater = (draftState: Draft) => void * ``` */ export function useComposeState(initState: S): [S, (stateUpdater: StateUpdater) => void] { - const [state, setState] = useState(initState) - const updateState = useCallback((stateUpdater: StateUpdater) => { - setState(state => { - const nextState = produce(state, draftState => stateUpdater(draftState)) - return nextState - }) - }, [setState]) - return [state, updateState] + const [state, setState] = useState(initState); + const updateState = useCallback( + (stateUpdater: StateUpdater) => { + setState((state) => { + const nextState = produce(state, (draftState) => stateUpdater(draftState)); + return nextState; + }); + }, + [setState] + ); + return [state, updateState]; } export const cleanMethodList: Array<{ key: CleanMethod; text: string }> = [ - { key: 'dropNull', text: 'drop null records' }, - { key: 'useMode', text: 'replace null with mode' }, - { key: 'simpleClean', text: 'simple cleaning' }, - { key: 'none', text: 'none' } -] + { key: 'dropNull', text: 'drop null records' }, + { key: 'useMode', text: 'replace null with mode' }, + { key: 'simpleClean', text: 'simple cleaning' }, + { key: 'none', text: 'none' }, +]; export const useCleanMethodList = function (): typeof cleanMethodList { - return useMemo(() => { - return cleanMethodList.map((m) => { - return { - key: m.key, - text: intl.get(`dataSource.methods.${m.key}`), - }; - }); - }, []) -} + return useMemo(() => { + return cleanMethodList.map((m) => { + return { + key: m.key, + text: intl.get(`dataSource.methods.${m.key}`), + }; + }); + }, []); +}; diff --git a/packages/rath-client/src/index.css b/packages/rath-client/src/index.css index 82b99680..5a46894e 100644 --- a/packages/rath-client/src/index.css +++ b/packages/rath-client/src/index.css @@ -1,15 +1,14 @@ /* @import '~office-ui-fabric-core/dist/css/fabric.css'; */ body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - background-color: rgb(250, 250, 250) + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: rgb(250, 250, 250) } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",monospace; } diff --git a/packages/rath-client/src/index.tsx b/packages/rath-client/src/index.tsx index 57e19b5c..8bf1b545 100644 --- a/packages/rath-client/src/index.tsx +++ b/packages/rath-client/src/index.tsx @@ -1,13 +1,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { initializeIcons } from '@fluentui/font-icons-mdl2' -import 'office-ui-fabric-core/dist/css/fabric.css' +import { initializeIcons } from '@fluentui/font-icons-mdl2'; +import 'office-ui-fabric-core/dist/css/fabric.css'; import './index.css'; import App from './App'; -initializeIcons() +initializeIcons(); -ReactDOM.render( - , - document.getElementById('root') -); +ReactDOM.render(, document.getElementById('root')); diff --git a/packages/rath-client/src/loggers/dataImport.ts b/packages/rath-client/src/loggers/dataImport.ts index a20cace6..db929bee 100644 --- a/packages/rath-client/src/loggers/dataImport.ts +++ b/packages/rath-client/src/loggers/dataImport.ts @@ -1,7 +1,7 @@ -import { IMuteFieldBase, IRow } from "../interfaces" +import { IMuteFieldBase, IRow } from '../interfaces'; const DATA_SOURCE_LOGGER_URL = - 'https://1423108296428281.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/Rath/dataSourceLogger/' + 'https://1423108296428281.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/Rath/dataSourceLogger/'; interface IDataImportInfo { dataType: string; @@ -11,19 +11,19 @@ interface IDataImportInfo { info?: any; size: number; } -export async function logDataImport (props: IDataImportInfo) { +export async function logDataImport(props: IDataImportInfo) { if (process.env.NODE_ENV === 'production') { try { - const res = await fetch(DATA_SOURCE_LOGGER_URL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(props), - }) - await res.json() + const res = await fetch(DATA_SOURCE_LOGGER_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(props), + }); + await res.json(); } catch (error) { - console.error(error) + console.error(error); } } else { // eslint-disable-next-line no-console @@ -31,23 +31,25 @@ export async function logDataImport (props: IDataImportInfo) { } } -export async function dataBackup (file: File) { - if (process.env.NODE_ENV === 'production') { - const data = new FormData(); - data.append('file', file); - fetch("https://kanaries.cn/api/ce/uploadDataset", { - method: 'POST', - credentials: 'include', - body: data - }).then(res => res.json()) - .then(res => { - // eslint-disable-next-line no-console - console.log(res) - }).catch(err => { - console.warn(err) - }) - } else { - // eslint-disable-next-line no-console - console.log(file) - } -} \ No newline at end of file +export async function dataBackup(file: File) { + if (process.env.NODE_ENV === 'production') { + const data = new FormData(); + data.append('file', file); + fetch('https://kanaries.cn/api/ce/uploadDataset', { + method: 'POST', + credentials: 'include', + body: data, + }) + .then((res) => res.json()) + .then((res) => { + // eslint-disable-next-line no-console + console.log(res); + }) + .catch((err) => { + console.warn(err); + }); + } else { + // eslint-disable-next-line no-console + console.log(file); + } +} diff --git a/packages/rath-client/src/pages/dataSource/advice/index.tsx b/packages/rath-client/src/pages/dataSource/advice/index.tsx index e76f15f2..0d5f3e10 100644 --- a/packages/rath-client/src/pages/dataSource/advice/index.tsx +++ b/packages/rath-client/src/pages/dataSource/advice/index.tsx @@ -5,6 +5,7 @@ import styled from 'styled-components'; import intl from 'react-intl-universal'; import { useGlobalStore } from '../../../store'; import { IDataPrepProgressTag } from '../../../interfaces'; +import { useActionModes } from '../baseActions/mainActionButton'; const AdviceContainer = styled.div` .row{ @@ -14,14 +15,14 @@ const AdviceContainer = styled.div` ` -interface AdviceProps { - onForceAnalysis?: () => void; -} - -const Advice: React.FC = props => { - const { onForceAnalysis } = props; +const Advice: React.FC = props => { const { dataSourceStore } = useGlobalStore(); const { measures, cleanedData, hasOriginalDimensionInData, groupMeanLimitCountsLog, dataPrepProgressTag } = dataSourceStore; + const { startMode } = useActionModes(); + + const onForceAnalysis = () => { + startMode.onClick && startMode.onClick(); + } return { diff --git a/packages/rath-client/src/pages/dataSource/baseActions/mainActionButton.tsx b/packages/rath-client/src/pages/dataSource/baseActions/mainActionButton.tsx new file mode 100644 index 00000000..a9482227 --- /dev/null +++ b/packages/rath-client/src/pages/dataSource/baseActions/mainActionButton.tsx @@ -0,0 +1,94 @@ +import { IContextualMenuItem, IContextualMenuProps, PrimaryButton } from '@fluentui/react'; +import { observer, useObserver } from 'mobx-react-lite'; +import React, { useCallback, useMemo } from 'react'; +import intl from 'react-intl-universal'; +import { EXPLORE_MODE, PIVOT_KEYS } from '../../../constants'; +import { useGlobalStore } from '../../../store'; + +export const useActionModes = function () { + const { dataSourceStore, commonStore, ltsPipeLineStore, megaAutoStore } = useGlobalStore(); + const { exploreMode, taskMode } = commonStore; + const { staisfyAnalysisCondition, fieldMetas } = dataSourceStore; + const startMegaAutoAnalysis = useCallback(() => { + ltsPipeLineStore.startTask(taskMode).then(() => { + megaAutoStore.emitViewChangeTransaction(0); + }); + commonStore.setAppKey(PIVOT_KEYS.megaAuto); + }, [ltsPipeLineStore, megaAutoStore, commonStore, taskMode]); + + const onCheckResults = useCallback(() => { + megaAutoStore.emitViewChangeTransaction(0); + commonStore.setAppKey(PIVOT_KEYS.megaAuto); + }, [megaAutoStore, commonStore]); + + const startSemiAutoAnalysis = useCallback(() => { + commonStore.setAppKey(PIVOT_KEYS.semiAuto); + }, [commonStore]); + const hasResults = megaAutoStore.insightSpaces.length > 0; + + const analysisOptions: IContextualMenuProps = useMemo(() => { + return { + items: [ + { + key: 'function.analysis.start', + text: intl.get('function.analysis.start'), + onClick: startMegaAutoAnalysis, + }, + { + key: 'function.analysis.checkResult', + text: intl.get('function.analysis.checkResult'), + onClick: onCheckResults, + }, + { + key: 'function.analysis.pattern', + text: intl.get('function.analysis.pattern'), + onClick: startSemiAutoAnalysis, + }, + { + key: 'function.analysis.manual', + text: intl.get('function.analysis.manual'), + onClick: () => { + commonStore.setAppKey(PIVOT_KEYS.editor); + }, + }, + ], + }; + }, [startMegaAutoAnalysis, onCheckResults, startSemiAutoAnalysis, commonStore]); + const startMode = useMemo(() => { + if (exploreMode === EXPLORE_MODE.first || fieldMetas.length > 25) { + return analysisOptions.items[2]; + } + if (exploreMode === EXPLORE_MODE.manual) { + return analysisOptions.items[3]; + } + if (hasResults) { + return analysisOptions.items[1]; + } + return analysisOptions.items[0]; + }, [hasResults, exploreMode, analysisOptions, fieldMetas]); + + return useObserver(() => ({ + startMode, + analysisOptions, + staisfyAnalysisCondition, + })); +}; + +const MainActionButton: React.FC = () => { + const { staisfyAnalysisCondition, startMode, analysisOptions } = useActionModes(); + + return ( + { + startMode.onClick && startMode.onClick(); + }} + /> + ); +}; + +export default observer(MainActionButton); diff --git a/packages/rath-client/src/pages/dataSource/dataTable/headerCell.tsx b/packages/rath-client/src/pages/dataSource/dataTable/headerCell.tsx index 70c62890..91f0a9ac 100644 --- a/packages/rath-client/src/pages/dataSource/dataTable/headerCell.tsx +++ b/packages/rath-client/src/pages/dataSource/dataTable/headerCell.tsx @@ -1,30 +1,31 @@ import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import intl from 'react-intl-universal'; +import { runInAction } from 'mobx'; import { IAnalyticType, ISemanticType } from 'visual-insights'; import { Callout, IconButton, PrimaryButton, TextField } from '@fluentui/react'; import { useId } from '@fluentui/react-hooks'; import DistributionChart from '../metaView/distChart'; -import DropdownSelect from '../../../components/dropDownSelect' +import DropdownSelect from '../../../components/dropDownSelect'; import { FieldExtSuggestion, IFieldMeta, IRawField } from '../../../interfaces'; import { LiveContainer } from '../metaView/metaList'; import FieldExtSuggestions from '../../../components/fieldExtend/suggestions'; import { getGlobalStore } from '../../../store'; - +import { PIVOT_KEYS } from '../../../constants'; const HeaderCellContainer = styled.div<{ isPreview: boolean }>` .bottom-bar { position: absolute; display: flex; justify-content: space-between; - height: ${({ isPreview }) => isPreview ? '2.4em' : '4px'}; + height: ${({ isPreview }) => (isPreview ? '2.4em' : '4px')}; font-size: 0.9rem; line-height: 2.4em; - border-radius: ${({ isPreview }) => isPreview ? '0' : '0px 0px 2px 2px'}; + border-radius: ${({ isPreview }) => (isPreview ? '0' : '0px 0px 2px 2px')}; left: 0px; right: 0px; top: 0px; - margin: 0px ${({ isPreview }) => isPreview ? '0px' : '1px'}; + margin: 0px ${({ isPreview }) => (isPreview ? '0px' : '1px')}; padding: 0 0.8em; color: #fff; font-weight: 600; @@ -34,12 +35,11 @@ const HeaderCellContainer = styled.div<{ isPreview: boolean }>` align-items: center; } } - padding-top: ${({ isPreview }) => isPreview ? '2.2em' : '0'}; - .info-container{ + padding-top: ${({ isPreview }) => (isPreview ? '2.2em' : '0')}; + .info-container { min-height: 50px; } - .viz-container{ - + .viz-container { } .dim { background-color: #1890ff; @@ -53,10 +53,10 @@ const HeaderCellContainer = styled.div<{ isPreview: boolean }>` .preview { background-color: #eaa300; } - .header-row{ + .header-row { display: flex; flex-wrap: nowrap; - .header{ + .header { margin-top: 0px; margin-bottom: 0px; font-size: 18px; @@ -68,45 +68,48 @@ const HeaderCellContainer = styled.div<{ isPreview: boolean }>` white-space: nowrap; text-overflow: ellipsis; } - .edit-icon{ + .edit-icon { flex-shrink: 0; flex-grow: 0; } } - .checkbox-container{ + .checkbox-container { display: flex; align-items: center; margin-top: 2px; - label{ + label { margin-right: 6px; } } `; function getClassName(type: 'dimension' | 'measure', disable: boolean) { - if (disable) return 'disable' - return type === "dimension" ? "dim" : "mea" + if (disable) return 'disable'; + return type === 'dimension' ? 'dim' : 'mea'; } interface HeaderCellProps { name: string; code: string; disable: boolean; - onChange?: (fid: string, propKey: keyof IRawField, value: any) => void + onChange?: (fid: string, propKey: keyof IRawField, value: any) => void; meta: IFieldMeta | null; extSuggestions: FieldExtSuggestion[]; isExt: boolean; isPreview: boolean; } -interface IOption { key: T; text: string } +interface IOption { + key: T; + text: string; +} const DataTypeOptions: IOption[] = [ { key: 'nominal', text: 'nominal' }, { key: 'ordinal', text: 'ordinal' }, { key: 'quantitative', text: 'quantitative' }, - { key: 'temporal', text: 'temporal' } -] + { key: 'temporal', text: 'temporal' }, +]; function useBIFieldTypeOptions(): IOption[] { const dimensionLabel = intl.get('meta.dimension'); @@ -114,14 +117,14 @@ function useBIFieldTypeOptions(): IOption[] { const options = useMemo[]>(() => { return [ { key: 'dimension', text: dimensionLabel }, - { key: 'measure', text: measureLabel } - ] + { key: 'measure', text: measureLabel }, + ]; }, [dimensionLabel, measureLabel]); return options; } -const HeaderCell: React.FC = props => { - const { dataSourceStore } = getGlobalStore(); +const HeaderCell: React.FC = (props) => { + const { dataSourceStore, commonStore, semiAutoStore } = getGlobalStore(); const { name, code, meta, disable, isPreview, onChange, extSuggestions, isExt } = props; const [showNameEditor, setShowNameEditor] = useState(false); const [headerName, setHeaderName] = useState(name); @@ -131,26 +134,44 @@ const HeaderCell: React.FC = props => { useEffect(() => { setHeaderName(name); - }, [name]) + }, [name]); return (

- { - meta && meta.geoRole !== 'none' && - } + {meta && meta.geoRole !== 'none' && ( + + )} {name}

{isPreview || ( <>
- { - setShowNameEditor(true) + setShowNameEditor(true); }} /> + {meta && ( + { + runInAction(() => { + commonStore.setAppKey(PIVOT_KEYS.semiAuto); + semiAutoStore.clearMainView(); + semiAutoStore.updateMainView({ + fields: [meta], + imp: meta.features.entropy, + }); + }); + }} + /> + )}
{extSuggestions.length > 0 && ( = props => { }} > -
- {extSuggestions.length} -
+
{extSuggestions.length}
)} {canDelete && ( @@ -180,37 +199,46 @@ const HeaderCell: React.FC = props => { )}
- { - showNameEditor && { setShowNameEditor(false); }} - + onDismiss={() => { + setShowNameEditor(false); + }} >

{intl.get('dataSource.table.edit')}

- { - setHeaderName(`${val}`); - }} /> + { + setHeaderName(`${val}`); + }} + />
{ - onChange && onChange(code, 'name', headerName) + onChange && onChange(code, 'name', headerName); setShowNameEditor(false); }} />
- } + )} {meta && ( - { - if (onChange) { - onChange(code, 'semanticType', e.target.value as ISemanticType) - } - }}> + { + if (onChange) { + onChange(code, 'semanticType', e.target.value as ISemanticType); + } + }} + > {DataTypeOptions.map((op) => ( )} { - { - if (onChange) { - // FIXME: 弱约束问题 - onChange(code, 'analyticType', e.target.value as IAnalyticType); - } - }}> - { - optionsOfBIFieldType.map(op => ) - } + { + if (onChange) { + // FIXME: 弱约束问题 + onChange(code, 'analyticType', e.target.value as IAnalyticType); + } + }} + > + {optionsOfBIFieldType.map((op) => ( + + ))} }
- { - onChange && onChange(code, 'disable', !e.target.checked) - }} /> + { + onChange && onChange(code, 'disable', !e.target.checked); + }} + />
- {meta && } + {meta && ( + + )}
{/* { onChange && onChange(code, 'disable', !isChecked) }} /> */} {/* {meta && } */} - -
+ +
{isPreview ? ( <> preview @@ -276,10 +319,12 @@ const HeaderCell: React.FC = props => { />
- ) : ''} + ) : ( + '' + )}
); -} +}; export default HeaderCell; diff --git a/packages/rath-client/src/pages/dataSource/index.tsx b/packages/rath-client/src/pages/dataSource/index.tsx index 87c5d81e..fb4afa70 100644 --- a/packages/rath-client/src/pages/dataSource/index.tsx +++ b/packages/rath-client/src/pages/dataSource/index.tsx @@ -1,12 +1,10 @@ -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect } from 'react'; import intl from 'react-intl-universal'; import { PrimaryButton, Stack, DefaultButton, Dropdown, - IContextualMenuProps, - IContextualMenuItem, IconButton, CommandButton, ProgressIndicator, @@ -14,7 +12,6 @@ import { } from '@fluentui/react'; import { observer } from 'mobx-react-lite'; import { useGlobalStore } from '../../store'; -import { EXPLORE_MODE, PIVOT_KEYS } from '../../constants'; import { CleanMethod, IDataPrepProgressTag, IDataPreviewMode, IMuteFieldBase, IRow } from '../../interfaces'; import { Card } from '../../components/card'; import { useCleanMethodList } from '../../hooks'; @@ -28,13 +25,14 @@ import AnalysisSettings from './settings'; import FastSelection from './fastSelection'; import ProfilingView from './profilingView'; import LaTiaoConsole from './LaTiaoConsole'; +import MainActionButton from './baseActions/mainActionButton'; const MARGIN_LEFT = { marginLeft: '1em' }; interface DataSourceBoardProps {} const DataSourceBoard: React.FC = (props) => { - const { dataSourceStore, commonStore, ltsPipeLineStore, megaAutoStore } = useGlobalStore(); + const { dataSourceStore, commonStore } = useGlobalStore(); const { cleanedData, @@ -44,12 +42,9 @@ const DataSourceBoard: React.FC = (props) => { loading, showDataImportSelection, dataPreviewMode, - staisfyAnalysisCondition, dataPrepProgressTag, } = dataSourceStore; - const { exploreMode, taskMode } = commonStore; - useEffect(() => { // 注意!不要对useEffect加依赖rawData,因为这里是初始加载的判断。 if (rawData && rawData.length === 0) { @@ -77,22 +72,6 @@ const DataSourceBoard: React.FC = (props) => { [dataSourceStore] ); - const onV1EngineStart = useCallback(() => { - ltsPipeLineStore.startTask(taskMode).then(() => { - megaAutoStore.emitViewChangeTransaction(0); - }); - commonStore.setAppKey(PIVOT_KEYS.megaAuto); - }, [ltsPipeLineStore, megaAutoStore, commonStore, taskMode]); - - const onCheckResults = useCallback(() => { - megaAutoStore.emitViewChangeTransaction(0); - commonStore.setAppKey(PIVOT_KEYS.megaAuto); - }, [megaAutoStore, commonStore]); - - const onBuildKnowledge = useCallback(() => { - commonStore.setAppKey(PIVOT_KEYS.semiAuto); - }, [commonStore]); - const onSelectPannelClose = useCallback(() => { dataSourceStore.setShowDataImportSelection(false); }, [dataSourceStore]); @@ -124,50 +103,6 @@ const DataSourceBoard: React.FC = (props) => { [dataSourceStore] ); - const analysisOptions: IContextualMenuProps = useMemo(() => { - return { - items: [ - { - key: 'function.analysis.start', - text: intl.get('function.analysis.start'), - onClick: onV1EngineStart, - }, - { - key: 'function.analysis.checkResult', - text: intl.get('function.analysis.checkResult'), - onClick: onCheckResults, - }, - { - key: 'function.analysis.pattern', - text: intl.get('function.analysis.pattern'), - onClick: onBuildKnowledge, - }, - { - key: 'function.analysis.manual', - text: intl.get('function.analysis.manual'), - onClick: () => { - commonStore.setAppKey(PIVOT_KEYS.editor); - }, - }, - ], - }; - }, [onV1EngineStart, onCheckResults, onBuildKnowledge, commonStore]); - - const hasResults = megaAutoStore.insightSpaces.length > 0; - - const startMode = useMemo(() => { - if (exploreMode === EXPLORE_MODE.first) { - return analysisOptions.items[2]; - } - if (exploreMode === EXPLORE_MODE.manual) { - return analysisOptions.items[3]; - } - if (hasResults) { - return analysisOptions.items[1]; - } - return analysisOptions.items[0]; - }, [hasResults, exploreMode, analysisOptions]); - const exportData = useCallback(() => { const ds = dataSourceStore.exportDataAsDSService(); const content = JSON.stringify(ds); @@ -195,16 +130,7 @@ const DataSourceBoard: React.FC = (props) => { - { - startMode.onClick && startMode.onClick(); - }} - /> + {dataImportButton(intl.get('dataSource.importData.buttonName'), rawData)} = (props) => { setLoadingAnimation={toggleLoadingAnimation} /> - {rawData.length > 0 && ( - { - startMode.onClick && startMode.onClick(); - }} - /> - )} + {rawData.length > 0 && } {dataPrepProgressTag !== IDataPrepProgressTag.none && } ` overflow: hidden; position: relative; @@ -21,14 +33,14 @@ const MetaItemContainer = styled.div<{ focus: boolean; isPreview: boolean }>` position: absolute; display: flex; justify-content: space-between; - height: ${({ isPreview }) => isPreview ? '2.4em' : '4px'}; + height: ${({ isPreview }) => (isPreview ? '2.4em' : '4px')}; font-size: 0.9rem; line-height: 2.4em; border-radius: 0px 0px 2px 2px; left: 0px; right: 0px; top: 0px; - margin: 0px ${({ isPreview }) => isPreview ? '0px' : '1px'}; + margin: 0px ${({ isPreview }) => (isPreview ? '0px' : '1px')}; padding: 0 0.8em; color: #fff; font-weight: 600; @@ -48,9 +60,9 @@ const MetaItemContainer = styled.div<{ focus: boolean; isPreview: boolean }>` background-color: #9e9e9e; } .preview { - background-color: #eaa300; + background-color: #faad14; } - h1{ + h1 { font-weight: 500; font-size: 26px; color: #333; @@ -61,7 +73,7 @@ const MetaItemContainer = styled.div<{ focus: boolean; isPreview: boolean }>` color: rgb(89, 89, 89); } padding: 1em; - padding-top: ${({ isPreview }) => isPreview ? '2.2em' : '1em'}; + padding-top: ${({ isPreview }) => (isPreview ? '2.2em' : '1em')}; margin: 1em; box-shadow: 0 1.6px 3.6px 0 rgb(0 0 0 / 13%), 0 0.3px 0.9px 0 rgb(0 0 0 / 11%); border-radius: 8px; @@ -77,7 +89,7 @@ const MetaItemContainer = styled.div<{ focus: boolean; isPreview: boolean }>` flex-grow: 0; } - animation: ${({ focus }) => focus ? 'outline 2s linear' : ''}; + animation: ${({ focus }) => (focus ? 'outline 2s linear' : '')}; @keyframes outline { from { @@ -99,7 +111,7 @@ const MetaItemContainer = styled.div<{ focus: boolean; isPreview: boolean }>` right: 0; top: 0; transform: scale(1.4); - + & * { background: none; color: #c50f1f; @@ -109,7 +121,7 @@ const MetaItemContainer = styled.div<{ focus: boolean; isPreview: boolean }>` display: flex; align-items: center; } -` +`; const IndicatorCard = styled.div` padding: 0em 1em; @@ -179,10 +191,21 @@ interface MetaItemProps { onChange?: (fid: string, propKey: keyof IRawField, value: any) => void; } - -const MetaItem: React.FC = props => { - const { colKey, colName, semanticType, analyticType, dist, disable, onChange, focus, extSuggestions, isPreview, isExt } = props; - const { dataSourceStore } = getGlobalStore(); +const MetaItem: React.FC = (props) => { + const { + colKey, + colName, + semanticType, + analyticType, + dist, + disable, + onChange, + focus, + extSuggestions, + isPreview, + isExt, + } = props; + const { dataSourceStore, semiAutoStore, commonStore } = getGlobalStore(); const [editing, setEditing] = React.useState(false); const [editingName, setEditingName] = React.useState(colName); useEffect(() => { @@ -191,13 +214,13 @@ const MetaItem: React.FC = props => { const ANALYTIC_TYPE_CHOICES_LANG: IChoiceGroupOption[] = ANALYTIC_TYPE_CHOICES.map((ch) => ({ ...ch, - text: intl.get(`common.analyticType.${ch.key}`) - })) + text: intl.get(`common.analyticType.${ch.key}`), + })); const SEMANTIC_TYPE_CHOICES_LANG: IChoiceGroupOption[] = SEMANTIC_TYPE_CHOICES.map((ch) => ({ ...ch, - text: intl.get(`common.semanticType.${ch.key}`) - })) + text: intl.get(`common.semanticType.${ch.key}`), + })); const containerRef = useRef(null); const expandBtnRef = useRef(null); @@ -221,40 +244,63 @@ const MetaItem: React.FC = props => { }, [isPreview]); const canDelete = !isPreview && isExt; - - return -
- {isPreview ? ( - <> - preview -
- dataSourceStore.settleExtField(colKey)} - iconProps={{ - iconName: 'CompletedSolid', - style: { - color: '#0027b4', - }, - }} - /> - dataSourceStore.deleteExtField(colKey)} - iconProps={{ - iconName: 'Delete', - style: { - color: '#c50f1f', - }, - }} - /> -
- - ) : ''} -
-
+ + return ( + +
+ {isPreview && ( + <> + {intl.get('dataSource.preview')} +
+ dataSourceStore.settleExtField(colKey)} + iconProps={{ + iconName: 'CompletedSolid', + style: { + color: '#003a8c', + }, + }} + /> + dataSourceStore.deleteExtField(colKey)} + iconProps={{ + iconName: 'Delete', + style: { + color: '#c50f1f', + }, + }} + /> +
+ + )} +
+
+ + + { + runInAction(() => { + commonStore.setAppKey(PIVOT_KEYS.semiAuto); + semiAutoStore.initMainViewWithSingleField(colKey); + }); + }} + /> + {extSuggestions.length > 0 && ( + + +
{extSuggestions.length}
+
+ )} +
+
+
{!editing && (

{colName}

{ setEditing(true); @@ -262,11 +308,14 @@ const MetaItem: React.FC = props => { />
)} - { - editing && - { - setEditingName(val || ''); - }} /> + {editing && ( + + { + setEditingName(val || ''); + }} + /> = props => { }} /> - } -
-
Column ID: {colKey}
- -
- - -
{intl.get('dataSource.meta.uniqueValue')}
-
{dist.length}
-
-
- { - onChange && option && onChange(colKey, 'analyticType', option.key) - }} - /> -
-
- { - onChange && option && onChange(colKey, 'semanticType', option.key) - }} - /> -
-
- { - onChange && onChange(colKey, 'disable', !checked) - }} - /> -
-
- - {extSuggestions.length > 0 && ( - - -
- {extSuggestions.length} -
-
)}
-
- {canDelete && ( -
- dataSourceStore.deleteExtField(colKey)} +
Column ID: {colKey}
+ +
+ + +
{intl.get('dataSource.meta.uniqueValue')}
+
{dist.length}
+
+
+ { + onChange && option && onChange(colKey, 'analyticType', option.key); + }} + /> +
+
+ { + onChange && option && onChange(colKey, 'semanticType', option.key); + }} + /> +
+
+ { + onChange && onChange(colKey, 'disable', !checked); + }} + /> +
+
+
- )} - -} + {canDelete && ( +
+ dataSourceStore.deleteExtField(colKey)} + /> +
+ )} + + ); +}; interface MetaListProps { metas: IFieldMetaWithExtSuggestions[]; onlyExt: boolean; focusIdx: number; - onChange?: (fid: string, propKey: keyof IRawField, value: any) => void + onChange?: (fid: string, propKey: keyof IRawField, value: any) => void; } -const MetaList: React.FC = props => { +const MetaList: React.FC = (props) => { const { metas, onChange, focusIdx, onlyExt } = props; - return - { - metas.map((m, i) => ( + return ( + + {metas.map((m, i) => !onlyExt || m.extSuggestions.length > 0 ? ( = props => { isPreview={m.stage === 'preview'} /> ) : null - )) - } - -} + )} + + ); +}; export default MetaList; diff --git a/packages/rath-client/src/pages/dataSource/profilingView/metaDetail.tsx b/packages/rath-client/src/pages/dataSource/profilingView/metaDetail.tsx index 87d880c3..e44b0d59 100644 --- a/packages/rath-client/src/pages/dataSource/profilingView/metaDetail.tsx +++ b/packages/rath-client/src/pages/dataSource/profilingView/metaDetail.tsx @@ -1,4 +1,4 @@ -import { Dropdown, IDropdownOption, PrimaryButton, Stack } from '@fluentui/react'; +import { DefaultButton, Dropdown, IDropdownOption, Stack } from '@fluentui/react'; import { applyFilters, IFilter, IRow } from '@kanaries/loa'; import { runInAction } from 'mobx'; import { observer } from 'mobx-react-lite'; @@ -10,13 +10,13 @@ import { PIVOT_KEYS } from '../../../constants'; import { IFieldMeta } from '../../../interfaces'; import { computeFieldFeatures } from '../../../lib/meta/fieldMeta'; import { useGlobalStore } from '../../../store'; +import FieldFilter from '../../../components/fieldFilter'; import { ANALYTIC_TYPE_CHOICES, SEMANTIC_TYPE_CHOICES } from '../config'; import DetailTable from './detailTable'; import FullDistViz from './fullDistViz'; import StatTable from './statTable'; import { patchFilterTemporalRange } from './utils'; - const DetailContainer = styled.div` flex-grow: 1; flex-shrink: 1; @@ -137,10 +137,10 @@ const MetaDetail: React.FC = (props) => { semanticType={field.semanticType} onSelect={onSelectionChange} /> - - + { runInAction(() => { commonStore.setAppKey(PIVOT_KEYS.semiAuto); @@ -166,6 +166,7 @@ const MetaDetail: React.FC = (props) => { option && dataSourceStore.updateFieldInfo(field.fid, 'semanticType', option.key); }} /> +
diff --git a/packages/rath-client/src/pages/dataSource/selection/olap.tsx b/packages/rath-client/src/pages/dataSource/selection/olap.tsx index 37acb35d..8e5b9119 100644 --- a/packages/rath-client/src/pages/dataSource/selection/olap.tsx +++ b/packages/rath-client/src/pages/dataSource/selection/olap.tsx @@ -8,13 +8,13 @@ import { logDataImport } from '../../../loggers/dataImport'; import { notify } from '../../../components/error'; const StackTokens = { - childrenGap: 20 + childrenGap: 1 } -// const PROTOCOL_LIST: IDropdownOption[] = [ -// { text: 'https', key: 'https' }, -// { text: 'http', key: 'http' } -// ] +const PROTOCOL_LIST: IDropdownOption[] = [ + { text: 'https', key: 'https' }, + { text: 'http', key: 'http' } +] interface OLAPDataProps { onClose: () => void; onDataLoaded: (fields: IMuteFieldBase[], dataSource: IRow[]) => void; @@ -42,7 +42,7 @@ const OLAPData: React.FC = props => { clickHouseStore.loadSampleData() .then(({ fieldMetas, data}) => { logDataImport({ - dataType: 'AirTable', + dataType: 'OLAP', fields: fieldMetas, dataSource: data.slice(0, 10), size: data.length @@ -63,9 +63,9 @@ const OLAPData: React.FC = props => { clickHouseStore.getDefaultConfig() .catch((err) => { notify({ - title: 'Clickhouse Config Init Error', - type: 'error', - content: `${err}\n It may be casued by a failure of start of clickhouse connector.` + title: 'Failed to load OLAP Config from server', + type: 'warning', + content: `${err}\n using default config instead.` }) }) .finally(() => { @@ -87,23 +87,32 @@ const OLAPData: React.FC = props => { return
- {/* */} - { + clickHouseStore.setProxyConfig('protocol', option?.key as string) + }} + /> + { clickHouseStore.setProxyConfig('host', v); }} /> - { clickHouseStore.setProxyConfig('port', v); }} /> - { + clickHouseStore.setConfig('protocol', option?.key as string) + }} + /> + { clickHouseStore.setConfig('host', v); }} /> - { clickHouseStore.setConfig('port', v); }} /> { success: true; @@ -205,8 +205,8 @@ export async function rathEngineServerService(props: MessageServerProps) { } } -export async function footmanEngineService( - props: IFootmanProps, +export async function loaEngineService( + props: ILoaProps, mode: 'server' | 'local' = 'local' ): Promise { try { @@ -231,8 +231,8 @@ export async function footmanEngineService( throw new Error(`[result.fail] ${result.message}`); } } else { - const worker = new FootmanWorker(); - const result = await workerService(worker, props); + const worker = new LoaWorker(); + const result = await workerService(worker, props); worker.terminate(); if (result.success) { return result.data; diff --git a/packages/rath-client/src/store/clickhouseStore.ts b/packages/rath-client/src/store/clickhouseStore.ts index d2689717..347ebe07 100644 --- a/packages/rath-client/src/store/clickhouseStore.ts +++ b/packages/rath-client/src/store/clickhouseStore.ts @@ -101,11 +101,7 @@ export class ClickHouseStore { runInAction(() => { this.connectStatus = 'client' }) - notify({ - title: 'CK connection fail', - type: 'error', - content: `clickhouse connection fail.\n${error}` - }) + throw new Error('OLAP Service Connection Error'); } } public async loadDBList() { diff --git a/packages/rath-client/src/store/semiAutomation/mainStore.ts b/packages/rath-client/src/store/semiAutomation/mainStore.ts index d8933c1d..5a31c551 100644 --- a/packages/rath-client/src/store/semiAutomation/mainStore.ts +++ b/packages/rath-client/src/store/semiAutomation/mainStore.ts @@ -5,11 +5,10 @@ import { Specification } from "visual-insights"; import { IFieldMeta, IResizeMode, IVegaSubset } from "../../interfaces"; import { distVis } from "../../queries/distVis"; import { labDistVis } from "../../queries/labdistVis"; -import { footmanEngineService } from "../../services/index"; +import { loaEngineService } from "../../services/index"; import { DataSourceStore } from "../dataSourceStore"; import { IAssoViews, IMainVizSetting, IRenderViewKey, ISetting, makeInitAssoViews } from "./localTypes"; - const RENDER_BATCH_SIZE = 5; export class SemiAutomationStore { @@ -98,6 +97,16 @@ export class SemiAutomationStore { this.mainView = null; this.compareView = null; } + public initMainViewWithSingleField (fid: string) { + const field = this.fieldMetas.find(f => f.fid === fid); + if (field) { + this.clearMainView() + this.updateMainView({ + fields: [field], + imp: field.features.entropy + }) + } + } public updateAutoAssoConfig (akey: IRenderViewKey, value: boolean) { this.autoAsso[akey] = value; } @@ -191,7 +200,7 @@ export class SemiAutomationStore { this.featViews.computing = true const { fieldMetas, dataSource, mainView } = this; try { - const res = await footmanEngineService({ + const res = await loaEngineService({ dataSource, fields: fieldMetas, task: 'featureSelection', @@ -211,7 +220,7 @@ export class SemiAutomationStore { this.pattViews.computing = true const { fieldMetas, dataSource, mainView } = this; try { - const res = await footmanEngineService({ + const res = await loaEngineService({ dataSource, fields: fieldMetas, task: 'patterns', @@ -231,7 +240,7 @@ export class SemiAutomationStore { this.pattViews.computing = true; const { dataSource, fieldMetas } = this; try { - const res = await footmanEngineService({ + const res = await loaEngineService({ dataSource, fields: fieldMetas, task: 'univar' @@ -251,7 +260,7 @@ export class SemiAutomationStore { this.filterViews.computing = true; const { fieldMetas, dataSource, mainView } = this; try { - const res = await footmanEngineService({ + const res = await loaEngineService({ dataSource, fields: fieldMetas, task: 'filterSelection', @@ -391,7 +400,7 @@ export class SemiAutomationStore { this.featViews.computing = true const { fieldMetas, dataSource } = this; try { - const res = await footmanEngineService<{ features: IFieldMeta[] }>({ + const res = await loaEngineService<{ features: IFieldMeta[] }>({ dataSource, fields: fieldMetas, task: 'comparison', diff --git a/packages/rath-client/src/workers/loa/service.ts b/packages/rath-client/src/workers/loa/service.ts index 78e729fb..9d9a3bfc 100644 --- a/packages/rath-client/src/workers/loa/service.ts +++ b/packages/rath-client/src/workers/loa/service.ts @@ -1,13 +1,13 @@ import { NextVICore, IPattern } from '@kanaries/loa' import { IFieldMeta, IRow } from "../../interfaces"; -export interface IFootmanProps { +export interface ILoaProps { task: 'univar' | 'patterns' | 'featureSelection' | 'comparison' | 'filterSelection'; props?: any; dataSource: IRow[]; fields: IFieldMeta[]; } -export function serviceHandler(reqProps: IFootmanProps) { +export function serviceHandler(reqProps: ILoaProps) { const { task, props, dataSource, fields } = reqProps try { if (task === 'univar') return univarService(dataSource, fields, props); @@ -16,7 +16,7 @@ export function serviceHandler(reqProps: IFootmanProps) { if (task === 'comparison') return featureForComparison(dataSource, fields, props); if (task === 'filterSelection') return filterSelection(dataSource, fields, props); } catch (error: any) { - throw new Error(`[footman][${task}]${error}\n${error.stack}`) + throw new Error(`[loa engine][${task}]${error}\n${error.stack}`) } }