Skip to content

Commit

Permalink
Timegraph: Support for the tree view on the left of the chart
Browse files Browse the repository at this point in the history
The chart is updated when collapsing or expanding entries in the tree.
Also, fixed the entries alignment with the chart's rows.

Signed-off-by: soukaina <soukaina.moussaoui@polymtl.ca>
  • Loading branch information
smouss authored and MatthewKhouzam committed Sep 4, 2020
1 parent ef686a1 commit 387096e
Show file tree
Hide file tree
Showing 6 changed files with 407 additions and 382 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client';
import { TimeGraphEntry, TimeGraphRow, TimeGraphModel, TimeGraphState } from 'tsp-typescript-client/lib/models/timegraph';
import { TimelineChart } from 'timeline-chart/lib/time-graph-model';
import { QueryHelper } from 'tsp-typescript-client/lib/models/query/query-helper';
import { EntryHeader } from 'tsp-typescript-client/lib/models/entry';

export class TspDataProvider {

Expand All @@ -27,31 +26,20 @@ export class TspDataProvider {
this.totalRange = 0;
}

async getData(viewRange?: TimelineChart.TimeGraphRange, resolution?: number): Promise<TimelineChart.TimeGraphModel> {
// QueryHelper.timeQuery(QueryHelper.splitRangeIntoEqualParts(1332170682440133097, 1332170682540133097, 1120));
const resourcesTreeParameters = QueryHelper.timeQuery([0, 1]);
const treeResponse = (await this.client.fetchTimeGraphTree<TimeGraphEntry, EntryHeader>(
this.traceUUID,
this.outputId,
resourcesTreeParameters)).getModel();
this.timeGraphEntries = treeResponse.model.entries;
async getData(ids: number[], entries: TimeGraphEntry[], viewRange?: TimelineChart.TimeGraphRange, resolution?: number): Promise<TimelineChart.TimeGraphModel> {
this.timeGraphEntries = [...entries];
this.totalRange = this.timeGraphEntries[0].endTime - this.timeGraphEntries[0].startTime; // 1332170682540133097 - starttime
const selectedItems = new Array<number>();
this.timeGraphEntries.forEach(timeGraphEntry => {
selectedItems.push(timeGraphEntry.id);
});

let statesParameters = QueryHelper.selectionTimeQuery(QueryHelper.splitRangeIntoEqualParts(1332170682440133097, 1332170682540133097, 1120), selectedItems);
let statesParameters = QueryHelper.selectionTimeQuery(QueryHelper.splitRangeIntoEqualParts(1332170682440133097, 1332170682540133097, 1120), ids);
if (viewRange && resolution) {
const start = viewRange.start + this.timeGraphEntries[0].startTime;
const end = viewRange.end + this.timeGraphEntries[0].startTime;
statesParameters = QueryHelper.selectionTimeQuery(QueryHelper.splitRangeIntoEqualParts(Math.trunc(start), Math.trunc(end), resolution), selectedItems);
statesParameters = QueryHelper.selectionTimeQuery(QueryHelper.splitRangeIntoEqualParts(Math.trunc(start), Math.trunc(end), resolution), ids);
}
const stateResponse = (await this.client.fetchTimeGraphStates<TimeGraphModel>(this.traceUUID,
this.outputId, statesParameters)).getModel();

this.timeGraphRows = stateResponse.model.rows;
this.timeGraphRowsOrdering();
this.timeGraphRowsOrdering(ids);

// the start time which is normalized to logical 0 in timeline chart.
const chartStart = this.timeGraphEntries[0].startTime;
Expand Down Expand Up @@ -85,12 +73,15 @@ export class TspDataProvider {
};
}

private timeGraphRowsOrdering() {
private timeGraphRowsOrdering(orderedIds: number[]) {
const newTimeGraphRows: TimeGraphRow[] = [];
this.timeGraphEntries.forEach(entry => {
const timeGraphRow = this.timeGraphRows.find(row => (row as any).entryID === entry.id);
orderedIds.forEach(id => {
const timeGraphRow = this.timeGraphRows.find(row => (row as any).entryID === id);
if (timeGraphRow) {
newTimeGraphRows.push(timeGraphRow);
} else {
const emptyRow: any = {states: [{value: 0, startTime: 0, duration: 0, label: '', tags: 0}], entryID: id};
newTimeGraphRows.push(emptyRow);
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ import { StyleProvider } from './data-providers/style-provider';
import { TspDataProvider } from './data-providers/tsp-data-provider';
import { ReactTimeGraphContainer } from './utils/timegraph-container-component';
import { OutputElementStyle } from 'tsp-typescript-client/lib/models/styles';
import { EntryTree } from './utils/filtrer-tree/entry-tree';
import { listToTree, getAllExpandedNodeIds } from './utils/filtrer-tree/utils';

type TimegraphOutputProps = AbstractOutputProps & {
addWidgetResizeHandler: (handler: () => void) => void;
};

type TimegraohOutputState = AbstractOutputState & {
type TimegraphOutputState = AbstractOutputState & {
timegraphTree: TimeGraphEntry[];
collapsedNodes: number[];
};

export class TimegraphOutputComponent extends AbstractTreeOutputComponent<TimegraphOutputProps, TimegraohOutputState> {
export class TimegraphOutputComponent extends AbstractTreeOutputComponent<TimegraphOutputProps, TimegraphOutputState> {
private totalHeight = 0;
private rowController: TimeGraphRowController;
private chartLayer: TimeGraphChart;
Expand All @@ -43,27 +46,29 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
super(props);
this.state = {
outputStatus: ResponseStatus.RUNNING,
timegraphTree: []
timegraphTree: [],
collapsedNodes: []
};
this.onToggleCollapse = this.onToggleCollapse.bind(this);
this.tspDataProvider = new TspDataProvider(this.props.tspClient, this.props.traceId, this.props.outputDescriptor.id);
this.rowController = new TimeGraphRowController(this.props.style.rowHeight, this.totalHeight);
this.horizontalContainer = React.createRef();
const providers: TimeGraphChartProviders = {
dataProvider: async (range: TimelineChart.TimeGraphRange, resolution: number) => this.fetchTimegraphData(range, resolution),
rowElementStyleProvider: (model: TimelineChart.TimeGraphRowElementModel) => this.getElementStyle(model),
rowStyleProvider: (row: TimelineChart.TimeGraphRowModel) => ({
backgroundColor: 0x979797,// 0xaaaaff,
backgroundOpacity: row.selected ? 0.1 : 0,
lineColor: 0xdddddd, // hasStates ? 0xdddddd : 0xaa4444, // row.data && row.data.hasStates
lineThickness: 1, // hasStates ? 1 : 3 // row.data && row.data.hasStates
})
backgroundColor: 0x979797,// 0xaaaaff,
backgroundOpacity: row.selected ? 0.1 : 0,
lineColor: 0xdddddd, // hasStates ? 0xdddddd : 0xaa4444, // row.data && row.data.hasStates
lineThickness: 1, // hasStates ? 1 : 3 // row.data && row.data.hasStates
})
};
this.chartLayer = new TimeGraphChart('timeGraphChart', providers, this.rowController);
this.vscrollLayer = new TimeGraphVerticalScrollbar('timeGraphVerticalScrollbar', this.rowController);

this.rowController.onVerticalOffsetChangedHandler(()=>{
this.rowController.onVerticalOffsetChangedHandler(() => {
if (this.treeRef.current) {
this.treeRef.current.scrollTop=this.rowController.verticalOffset;
this.treeRef.current.scrollTop = this.rowController.verticalOffset;
}
});

Expand All @@ -82,15 +87,15 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr

synchronizeTreeScroll(): void {
if (this.treeRef.current) {
this.rowController.verticalOffset=this.treeRef.current.scrollTop;
this.rowController.verticalOffset = this.treeRef.current.scrollTop;
}
}

async componentDidMount(): Promise<void> {
this.waitAnalysisCompletion();
}

async componentDidUpdate(_prevProps: TimegraphOutputProps, _prevState: TimegraohOutputState): Promise<void> {
async componentDidUpdate(_prevProps: TimegraphOutputProps, _prevState: TimegraphOutputState): Promise<void> {
if (this.state.outputStatus !== ResponseStatus.COMPLETED || !this.state.timegraphTree.length) {
const treeParameters = QueryHelper.timeQuery([0, 1]);
const treeResponse = (await this.props.tspClient.fetchTimeGraphTree<TimeGraphEntry, EntryHeader>(this.props.traceId,
Expand All @@ -105,19 +110,33 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
timegraphTree: treeResponse.model.entries,
styleModel: styleResponse.model
});
this.chartLayer.updateChart();
}

if (this.state.collapsedNodes !== _prevState.collapsedNodes) {
this.chartLayer.updateChart();
}
}

private onToggleCollapse(id: number) {
let newList = [...this.state.collapsedNodes];
const exist = this.state.collapsedNodes.find(expandId => expandId === id);
if (exist !== undefined) {
newList = newList.filter(collapsed => id !== collapsed);
} else {
newList = newList.concat(id);
}
this.setState({ collapsedNodes: newList });
}

renderTree(): React.ReactNode {
return <React.Fragment>
{this.state.timegraphTree.map((entry, i) => {
if (entry.parentId !== -1) {
return <p style={{ height: this.props.style.rowHeight, margin: 0 }} key={i}>
{entry.labels[0] + '\n'}
</p>;
}
})}
</React.Fragment>;
return <EntryTree
collapsedNodes={this.state.collapsedNodes}
showFilter={false}
entries={this.state.timegraphTree}
showCheckboxes={false}
onToggleCollapse={this.onToggleCollapse}
/>;
}

renderChart(): React.ReactNode {
Expand Down Expand Up @@ -197,13 +216,15 @@ export class TimegraphOutputComponent extends AbstractTreeOutputComponent<Timegr
}

private async fetchTimegraphData(range: TimelineChart.TimeGraphRange, resolution: number) {
const treeNodes = listToTree(this.state.timegraphTree);
const orderedTreeIds = getAllExpandedNodeIds(treeNodes, this.state.collapsedNodes);
const length = range.end - range.start;
const overlap = ((length * 5) - length) / 2;
const start = range.start - overlap > 0 ? range.start - overlap : 0;
const end = range.end + overlap < this.props.unitController.absoluteRange ? range.end + overlap : this.props.unitController.absoluteRange;
const newRange: TimelineChart.TimeGraphRange = { start, end };
const newResolution: number = resolution * 0.8;
const timeGraphData: TimelineChart.TimeGraphModel = await this.tspDataProvider.getData(newRange, this.props.style.chartWidth);
const timeGraphData: TimelineChart.TimeGraphModel = await this.tspDataProvider.getData(orderedTreeIds, this.state.timegraphTree, newRange, this.props.style.chartWidth);
if (timeGraphData && this.selectedElement) {
for (const row of timeGraphData.rows) {
const selEl = row.states.find(el => !!this.selectedElement && el.id === this.selectedElement.id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ export class EntryTree extends React.Component<EntryTreeProps> {
super(props);
}

shouldComponentUpdate = (nextProps: EntryTreeProps): boolean => (this.props.checkedSeries !== nextProps.checkedSeries || this.props.entries !== nextProps.entries);
shouldComponentUpdate = (nextProps: EntryTreeProps): boolean =>
(this.props.checkedSeries !== nextProps.checkedSeries || this.props.entries !== nextProps.entries || this.props.collapsedNodes !== nextProps.collapsedNodes);

render(): JSX.Element {
return <FilterTree
nodes = { listToTree(this.props.entries) }
nodes={listToTree(this.props.entries)}
{...this.props}
/>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps> {
<div
data-level={this.props.level}
key={this.props.node.id}
style={{display: 'flex', flexDirection: 'row', padding: '5px 8px'}}
style={{display: 'flex', flexDirection: 'row', position: 'relative', transform: 'translateY(+20%)', top:'50%',
paddingRight: '8px', paddingLeft: '8px', height: 20, whiteSpace: 'nowrap'}}
>
{ this.isLeaf()
? <span style={{paddingLeft:this.props.padding}}></span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,14 @@ export const listToTree = (list: Entry[]): TreeNode[] => {
});
return rootNodes;
};

export const getAllExpandedNodeIds = (nodes: TreeNode[],collapsedNodes: number[]): number[] => {
const visibleIds: number[] = [];
nodes.forEach((node: TreeNode) => {
visibleIds.push(node.id);
if (node.children.length && !collapsedNodes.includes(node.id)) {
visibleIds.push(...getAllExpandedNodeIds(node.children, collapsedNodes));
}
});
return visibleIds;
};
Loading

0 comments on commit 387096e

Please sign in to comment.