mirror of
https://github.com/xodio/xod.git
synced 2026-02-20 02:01:20 +01:00
feat(xod-client): show user-friendly table log source labels
This commit is contained in:
@@ -33,7 +33,12 @@ import {
|
||||
|
||||
import * as EAT from '../editor/actionTypes';
|
||||
|
||||
import { UPLOAD_MSG_TYPE, LOG_TAB_TYPE, SESSION_TYPE, NEW_SHEET } from './constants';
|
||||
import {
|
||||
UPLOAD_MSG_TYPE,
|
||||
LOG_TAB_TYPE,
|
||||
SESSION_TYPE,
|
||||
NEW_SHEET,
|
||||
} from './constants';
|
||||
import * as MSG from './messages';
|
||||
import { STATUS } from '../utils/constants';
|
||||
import { isXodErrorMessage } from './debugProtocol';
|
||||
@@ -140,10 +145,15 @@ const addMessagesOrIncrementSkippedLines = R.curry(
|
||||
|
||||
const updateInteractiveNodeValues = R.curry((messageList, state) => {
|
||||
const MapToRekey = R.prop('nodeIdsMap', state);
|
||||
const tableLogNodeIds = R.compose(
|
||||
R.unnest,
|
||||
R.values,
|
||||
R.prop('tableLogSources')
|
||||
)(state);
|
||||
// All node ids that is not represented in `tableLogNodeIds` is `watch` nodes
|
||||
const [tableLogMessages, watchNodeMessages] = R.compose(
|
||||
R.map(R.fromPairs),
|
||||
R.partition(R.compose(isAmong(state.tableLogNodeIds), R.nth(0))),
|
||||
R.partition(R.compose(isAmong(tableLogNodeIds), R.nth(0))),
|
||||
R.toPairs,
|
||||
renameKeys(MapToRekey),
|
||||
R.groupBy(R.prop('nodeId')),
|
||||
@@ -203,6 +213,14 @@ const updateInteractiveNodeValues = R.curry((messageList, state) => {
|
||||
)(state);
|
||||
});
|
||||
|
||||
const updateTableLogSources = R.curry((patchPath, tableLogNodeIds, state_) =>
|
||||
R.over(
|
||||
R.lensPath(['tableLogSources', patchPath]),
|
||||
R.compose(R.uniq, R.concat(tableLogNodeIds), R.defaultTo([])),
|
||||
state_
|
||||
)
|
||||
);
|
||||
|
||||
/* eslint-disable no-bitwise */
|
||||
const filterPinKeysByBitmask = R.curry((pinKeys, bitmask) =>
|
||||
R.addIndex(R.filter)((val, idx) => bitmask & (1 << idx), pinKeys)
|
||||
@@ -522,7 +540,10 @@ export default (state = initialState, action) => {
|
||||
R.assoc('isSkippingNewSerialLogLines', false),
|
||||
R.assoc('numberOfSkippedSerialLogLines', 0),
|
||||
R.assoc('nodeIdsMap', invertedNodeIdsMap),
|
||||
R.assoc('tableLogNodeIds', action.payload.tableLogNodeIds),
|
||||
updateTableLogSources(
|
||||
action.payload.patchPath,
|
||||
action.payload.tableLogNodeIds
|
||||
),
|
||||
R.assoc('nodePinKeysMap', action.payload.nodePinKeysMap),
|
||||
rekeyAndAssocPinsAffectedByErrorRaisers(
|
||||
invertedNodeIdsMap,
|
||||
@@ -648,7 +669,11 @@ export default (state = initialState, action) => {
|
||||
R.assoc('isPreparingSimulation', false),
|
||||
R.assoc('isOutdated', false),
|
||||
R.assoc('uploadProgress', null),
|
||||
R.assoc('tableLogNodeIds', action.payload.tableLogNodeIds),
|
||||
updateTableLogSources(
|
||||
action.payload.patchPath,
|
||||
action.payload.tableLogNodeIds
|
||||
),
|
||||
|
||||
R.assoc('nodeIdsMap', invertedNodeIdsMap),
|
||||
R.assoc('nodePinKeysMap', action.payload.nodePinKeysMap),
|
||||
R.assoc('globals', action.payload.globals),
|
||||
|
||||
@@ -11,6 +11,7 @@ import { DEBUGGER_TAB_ID } from '../editor/constants';
|
||||
import { SESSION_TYPE } from './constants';
|
||||
|
||||
import { createMemoizedSelector } from '../utils/selectorTools';
|
||||
import { getTableLogSourceLabels } from './utils';
|
||||
|
||||
export const getDebuggerState = R.prop('debugger');
|
||||
|
||||
@@ -308,7 +309,19 @@ export const getTableLogValues = R.compose(
|
||||
getDebuggerState
|
||||
);
|
||||
|
||||
export const getTableLogSources = R.compose(R.keys, getTableLogValues);
|
||||
const getTableLogSourcesRaw = R.compose(
|
||||
R.prop('tableLogSources'),
|
||||
getDebuggerState
|
||||
);
|
||||
|
||||
// :: State -> [{ nodeId: NodeId, label: String }]
|
||||
export const getTableLogSources = state =>
|
||||
R.compose(
|
||||
R.unnest,
|
||||
R.values,
|
||||
R.mapObjIndexed(getTableLogSourceLabels(state.project)),
|
||||
getTableLogSourcesRaw
|
||||
)(state);
|
||||
|
||||
export const getTableLogsByNodeId = R.curry((nodeId, state) =>
|
||||
R.compose(R.pathOr([], ['tableLogValues', nodeId]), getDebuggerState)(state)
|
||||
|
||||
@@ -39,7 +39,10 @@ export default {
|
||||
currentStage: LOG_TAB_TYPE.COMPILER,
|
||||
nodeIdsMap: {},
|
||||
watchNodeValues: {},
|
||||
tableLogNodeIds: [],
|
||||
tableLogSources: {
|
||||
// PatchPath : [NodeId]
|
||||
// where PatchPath is a path of root patch that was compiled
|
||||
},
|
||||
tableLogValues: {
|
||||
// NodeId : [ /* Experiments history */ [[String]] ]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as R from 'ramda';
|
||||
import * as XP from 'xod-project';
|
||||
import { foldMaybe } from 'xod-func-tools';
|
||||
import { foldMaybe, mapIndexed } from 'xod-func-tools';
|
||||
|
||||
import { UPLOAD_MSG_TYPE } from './constants';
|
||||
|
||||
@@ -38,3 +38,93 @@ export const getTetheringInetNodeId = R.curry(
|
||||
);
|
||||
|
||||
export const isErrorMessage = R.propEq('type', UPLOAD_MSG_TYPE.ERROR);
|
||||
|
||||
// :: Project -> [NodeId] -> PatchPath -> [{ nodeId: NodeId, label: String }]
|
||||
export const getTableLogSourceLabels = R.curry(
|
||||
(project, sourceNodeIds, rootPatchPath) =>
|
||||
R.compose(
|
||||
R.map(
|
||||
R.applySpec({
|
||||
nodeId: R.prop('nodeId'),
|
||||
label: R.compose(
|
||||
R.join(' > '),
|
||||
R.concat([rootPatchPath]),
|
||||
R.pluck('finalLabel'),
|
||||
R.prop('chunks')
|
||||
),
|
||||
})
|
||||
),
|
||||
R.reduce((acc, nextSource) => {
|
||||
const reducedChunks = R.pluck('chunks', acc);
|
||||
const result = R.over(
|
||||
R.lensProp('chunks'),
|
||||
mapIndexed((chunk, idx) =>
|
||||
R.compose(
|
||||
R.ifElse(
|
||||
R.equals(0),
|
||||
() => R.assoc('finalLabel', chunk.label, chunk),
|
||||
num =>
|
||||
R.assoc('finalLabel', `${chunk.label} #${num + 1}`, chunk)
|
||||
),
|
||||
R.length,
|
||||
R.uniqBy(R.both(R.prop('label'), R.prop('nodeId'))),
|
||||
R.filter(
|
||||
rc =>
|
||||
rc.nodeId !== chunk.nodeId &&
|
||||
rc.label === chunk.label &&
|
||||
rc.parent === chunk.parent
|
||||
),
|
||||
R.reject(R.isNil),
|
||||
R.pluck(idx)
|
||||
)(reducedChunks)
|
||||
),
|
||||
nextSource
|
||||
);
|
||||
return [...acc, result];
|
||||
}, []),
|
||||
R.map(nodeId => {
|
||||
// Convert chained NodeId (`a~b~c`) into [NodeId] (['a', 'b', 'c'])
|
||||
const splittedNodeId = R.split('~', nodeId);
|
||||
return R.compose(
|
||||
foldMaybe(
|
||||
{
|
||||
nodeId,
|
||||
chunks: [
|
||||
{
|
||||
nodeId,
|
||||
label: [`DELETED (${nodeId})`],
|
||||
},
|
||||
],
|
||||
},
|
||||
R.applySpec({
|
||||
nodeId: R.always(nodeId),
|
||||
chunks: R.identity,
|
||||
})
|
||||
),
|
||||
R.map(
|
||||
mapIndexed((chunk, idx) =>
|
||||
R.compose(
|
||||
R.assoc('parent', R.__, chunk),
|
||||
R.join('~'),
|
||||
R.concat([rootPatchPath]),
|
||||
R.take(idx)
|
||||
)(splittedNodeId)
|
||||
)
|
||||
),
|
||||
XP.mapNestedNodes(
|
||||
node => {
|
||||
const nodeType = XP.getNodeType(node);
|
||||
const label = XP.getNodeLabel(node);
|
||||
return {
|
||||
nodeType,
|
||||
nodeId: XP.getNodeId(node),
|
||||
label: label.length ? label : XP.getBaseName(nodeType),
|
||||
};
|
||||
},
|
||||
project,
|
||||
rootPatchPath
|
||||
)
|
||||
)(splittedNodeId);
|
||||
})
|
||||
)(sourceNodeIds)
|
||||
);
|
||||
|
||||
@@ -83,7 +83,7 @@ class TableLog extends React.Component {
|
||||
|
||||
onSaveLogClicked() {
|
||||
const tsv = gridToTsv(this.getSheet());
|
||||
const defaultFilename = `log-${this.props.nodeId}-${
|
||||
const defaultFilename = `log-${this.getNodeLabel()}-${
|
||||
this.props.sheetIndex
|
||||
}.txt`;
|
||||
|
||||
@@ -116,6 +116,13 @@ class TableLog extends React.Component {
|
||||
return this.props.document.length;
|
||||
}
|
||||
|
||||
getNodeLabel() {
|
||||
return R.compose(
|
||||
R.propOr('unknown', 'label'),
|
||||
R.find(R.propEq('nodeId', this.props.nodeId))
|
||||
)(this.props.sources);
|
||||
}
|
||||
|
||||
isEmptySheet() {
|
||||
return this.getSheet().length === 0;
|
||||
}
|
||||
@@ -136,9 +143,9 @@ class TableLog extends React.Component {
|
||||
disabled={hasNoSources}
|
||||
>
|
||||
{hasNoSources ? <option>No logs</option> : null}
|
||||
{this.props.sources.map(nodeId => (
|
||||
{this.props.sources.map(({ nodeId, label }) => (
|
||||
<option key={nodeId} value={nodeId}>
|
||||
{nodeId}
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -202,7 +209,7 @@ TableLog.propTypes = {
|
||||
// from parent
|
||||
tabId: PropTypes.string.isRequired,
|
||||
// connect
|
||||
sources: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
sources: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
document: PropTypes.array.isRequired,
|
||||
nodeId: PropTypes.string.isRequired,
|
||||
sheetIndex: PropTypes.number.isRequired,
|
||||
|
||||
263
packages/xod-client/test/tableLogSources.spec.js
Normal file
263
packages/xod-client/test/tableLogSources.spec.js
Normal file
@@ -0,0 +1,263 @@
|
||||
import * as R from 'ramda';
|
||||
import { assert } from 'chai';
|
||||
import { defaultizeProject } from 'xod-project/test/helpers';
|
||||
|
||||
import { getTableLogSourceLabels } from '../src/debugger/utils';
|
||||
|
||||
describe('generate labels for table log sources', () => {
|
||||
const assertSameSources = (project, rootPatchPath, expectedLabelsForIds) => {
|
||||
const ids = R.keys(expectedLabelsForIds);
|
||||
const result = getTableLogSourceLabels(project, ids, rootPatchPath);
|
||||
const expectedResult = R.compose(
|
||||
R.map(
|
||||
R.applySpec({
|
||||
nodeId: R.nth(0),
|
||||
label: R.nth(1),
|
||||
})
|
||||
),
|
||||
R.toPairs
|
||||
)(expectedLabelsForIds);
|
||||
assert.deepEqual(result, expectedResult);
|
||||
};
|
||||
|
||||
it('should contain `DELETED (nodeId)` for a source that not exists in the patch', () => {
|
||||
const project = defaultizeProject({
|
||||
patches: {
|
||||
'@/main': {},
|
||||
'xod/debug/table-log': {},
|
||||
},
|
||||
});
|
||||
|
||||
assertSameSources(project, '@/main', {
|
||||
'a~n~t': '@/main > DELETED (a~n~t)',
|
||||
t: '@/main > DELETED (t)',
|
||||
});
|
||||
});
|
||||
it('should contain node type base names if the node is unlabeled', () => {
|
||||
const project = defaultizeProject({
|
||||
patches: {
|
||||
'@/main': {
|
||||
nodes: {
|
||||
a: {
|
||||
type: '@/nested-1',
|
||||
},
|
||||
b: {
|
||||
type: '@/nested-2',
|
||||
},
|
||||
t: {
|
||||
type: 'xod/debug/table-log',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-1': {
|
||||
nodes: {
|
||||
n: {
|
||||
type: '@/nested-2',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-2': {
|
||||
nodes: {
|
||||
t: {
|
||||
type: 'xod/debug/table-log',
|
||||
},
|
||||
},
|
||||
},
|
||||
'xod/debug/table-log': {},
|
||||
},
|
||||
});
|
||||
|
||||
assertSameSources(project, '@/main', {
|
||||
'a~n~t': '@/main > nested-1 > nested-2 > table-log',
|
||||
'b~t': '@/main > nested-2 > table-log',
|
||||
t: '@/main > table-log',
|
||||
});
|
||||
});
|
||||
it('should contain node labels if it is set', () => {
|
||||
const project = defaultizeProject({
|
||||
patches: {
|
||||
'@/main': {
|
||||
nodes: {
|
||||
a: {
|
||||
type: '@/nested-1',
|
||||
label: 'Nested One',
|
||||
},
|
||||
b: {
|
||||
type: '@/nested-2',
|
||||
label: 'Nested Two',
|
||||
},
|
||||
c: {
|
||||
type: '@/nested-label',
|
||||
label: 'Nested Label',
|
||||
},
|
||||
t: {
|
||||
type: 'xod/debug/table-log',
|
||||
label: 'My Table Log',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-1': {
|
||||
nodes: {
|
||||
n: {
|
||||
type: '@/nested-2',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-2': {
|
||||
nodes: {
|
||||
t: {
|
||||
type: 'xod/debug/table-log',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-label': {
|
||||
nodes: {
|
||||
n: {
|
||||
type: '@/nested-2',
|
||||
label: 'Log',
|
||||
},
|
||||
},
|
||||
},
|
||||
'xod/debug/table-log': {},
|
||||
},
|
||||
});
|
||||
|
||||
assertSameSources(project, '@/main', {
|
||||
'a~n~t': '@/main > Nested One > nested-2 > table-log',
|
||||
'b~t': '@/main > Nested Two > table-log',
|
||||
'c~n~t': '@/main > Nested Label > Log > table-log',
|
||||
t: '@/main > My Table Log',
|
||||
});
|
||||
});
|
||||
it('should add numbers to the duplicates on a correct nesting level', () => {
|
||||
const project = defaultizeProject({
|
||||
patches: {
|
||||
'@/main': {
|
||||
nodes: {
|
||||
a: {
|
||||
type: '@/nested-1',
|
||||
label: 'Nested',
|
||||
},
|
||||
a2: {
|
||||
type: '@/nested-1',
|
||||
label: 'Nested',
|
||||
},
|
||||
a3: {
|
||||
type: '@/nested-1',
|
||||
},
|
||||
b: {
|
||||
type: '@/nested-label',
|
||||
},
|
||||
b2: {
|
||||
type: '@/nested-label',
|
||||
},
|
||||
d: {
|
||||
type: '@/nested-double',
|
||||
},
|
||||
d2: {
|
||||
type: '@/nested-double',
|
||||
},
|
||||
d3: {
|
||||
type: '@/nested-double',
|
||||
label: 'Double Inside',
|
||||
},
|
||||
nc: {
|
||||
type: '@/nested-complex',
|
||||
},
|
||||
nc2: {
|
||||
type: '@/nested-complex',
|
||||
},
|
||||
t: {
|
||||
type: 'xod/debug/table-log',
|
||||
label: 'My Table Log',
|
||||
},
|
||||
t2: {
|
||||
type: 'xod/debug/table-log',
|
||||
label: 'My Table Log',
|
||||
},
|
||||
t3: {
|
||||
type: 'xod/debug/table-log',
|
||||
label: 'My Table Log',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-1': {
|
||||
nodes: {
|
||||
n: {
|
||||
type: '@/nested-2',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-2': {
|
||||
nodes: {
|
||||
t: {
|
||||
type: 'xod/debug/table-log',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-label': {
|
||||
nodes: {
|
||||
n: {
|
||||
type: '@/nested-2',
|
||||
label: 'Log',
|
||||
},
|
||||
},
|
||||
},
|
||||
'@/nested-double': {
|
||||
nodes: {
|
||||
t: { type: 'xod/debug/table-log' },
|
||||
t2: { type: 'xod/debug/table-log' },
|
||||
},
|
||||
},
|
||||
'@/nested-complex': {
|
||||
nodes: {
|
||||
n: { type: '@/nested-1' },
|
||||
n2: { type: '@/nested-1' },
|
||||
nn: { type: '@/nested-2' },
|
||||
nn2: { type: '@/nested-2' },
|
||||
nd: { type: '@/nested-double' },
|
||||
nd2: { type: '@/nested-double' },
|
||||
},
|
||||
},
|
||||
'xod/debug/table-log': {},
|
||||
},
|
||||
});
|
||||
|
||||
assertSameSources(project, '@/main', {
|
||||
'a~n~t': '@/main > Nested > nested-2 > table-log',
|
||||
'a2~n~t': '@/main > Nested #2 > nested-2 > table-log',
|
||||
'a3~n~t': '@/main > nested-1 > nested-2 > table-log',
|
||||
'b~n~t': '@/main > nested-label > Log > table-log',
|
||||
'b2~n~t': '@/main > nested-label #2 > Log > table-log',
|
||||
'd~t': '@/main > nested-double > table-log',
|
||||
'd~t2': '@/main > nested-double > table-log #2',
|
||||
'd2~t': '@/main > nested-double #2 > table-log',
|
||||
'd2~t2': '@/main > nested-double #2 > table-log #2',
|
||||
'd3~t': '@/main > Double Inside > table-log',
|
||||
'd3~t2': '@/main > Double Inside > table-log #2',
|
||||
'nc~n~n~t': '@/main > nested-complex > nested-1 > nested-2 > table-log',
|
||||
'nc~n2~n~t':
|
||||
'@/main > nested-complex > nested-1 #2 > nested-2 > table-log',
|
||||
'nc~nn~t': '@/main > nested-complex > nested-2 > table-log',
|
||||
'nc~nn2~t': '@/main > nested-complex > nested-2 #2 > table-log',
|
||||
'nc~nd~t': '@/main > nested-complex > nested-double > table-log',
|
||||
'nc~nd~t2': '@/main > nested-complex > nested-double > table-log #2',
|
||||
'nc~nd2~t': '@/main > nested-complex > nested-double #2 > table-log',
|
||||
'nc~nd2~t2': '@/main > nested-complex > nested-double #2 > table-log #2',
|
||||
'nc2~n~n~t':
|
||||
'@/main > nested-complex #2 > nested-1 > nested-2 > table-log',
|
||||
'nc2~n2~n~t':
|
||||
'@/main > nested-complex #2 > nested-1 #2 > nested-2 > table-log',
|
||||
'nc2~nn~t': '@/main > nested-complex #2 > nested-2 > table-log',
|
||||
'nc2~nn2~t': '@/main > nested-complex #2 > nested-2 #2 > table-log',
|
||||
'nc2~nd~t': '@/main > nested-complex #2 > nested-double > table-log',
|
||||
'nc2~nd~t2': '@/main > nested-complex #2 > nested-double > table-log #2',
|
||||
'nc2~nd2~t': '@/main > nested-complex #2 > nested-double #2 > table-log',
|
||||
'nc2~nd2~t2':
|
||||
'@/main > nested-complex #2 > nested-double #2 > table-log #2',
|
||||
t: '@/main > My Table Log',
|
||||
t2: '@/main > My Table Log #2',
|
||||
t3: '@/main > My Table Log #3',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user