Skip to content

Commit

Permalink
different way to compute tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
sgratzl committed Dec 7, 2018
1 parent c489921 commit d70f850
Show file tree
Hide file tree
Showing 22 changed files with 193 additions and 158 deletions.
161 changes: 93 additions & 68 deletions src/provider/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,48 @@ import {IStatistics, ICategoricalStatistics, IDateStatistics, IAdvancedBoxPlotDa
import {IDataRow, INumberColumn, IDateColumn, ISetColumn, IOrderedGroup, IndicesArray} from '../model';
import Column from '../model/Column';
import {ISequence, lazySeq} from '../internal/interable';
import {IAbortAblePromise} from 'lineupengine';
import {IAbortAblePromise, abortAbleResolveNow} from 'lineupengine';

export {IAbortAblePromise} from 'lineupengine';

export interface ITask<T> {
then<U = void>(onfullfilled: (value: T) => U): U | IAbortAblePromise<U>;
}

class TaskNow<T> implements ITask<T> {
constructor(public readonly v: T) {

}

then<U = void>(onfullfilled: (value: T) => U) {
return onfullfilled(this.v);
}
}

function taskNow<T>(v: T) {
return new TaskNow(v);
}

export function tasksAll<T>(tasks: ITask<T>[]): ITask<T[]> {
if (tasks.every((t) => t instanceof TaskNow)) {
return taskNow(tasks.map((d) => (<TaskNow<T>>d).v));
}
return taskNow([]); // FIXME
}

export interface IRenderTasks {
groupRows<T>(col: Column, group: IOrderedGroup, compute: (rows: ISequence<IDataRow>) => T, render: (value: T) => void): IAbortAblePromise<void> | void;
groupExampleRows<T>(col: Column, group: IOrderedGroup, compute: (rows: ISequence<IDataRow>) => T, render: (value: T) => void): IAbortAblePromise<void> | void;

groupBoxPlotStats(col: Column & INumberColumn, group: IOrderedGroup, render: (group: IAdvancedBoxPlotData, summary: IAdvancedBoxPlotData, data: IAdvancedBoxPlotData) => void): IAbortAblePromise<void> | void;
groupNumberStats(col: Column & INumberColumn, group: IOrderedGroup, render: (group: IStatistics, summary: IStatistics, data: IStatistics) => void): IAbortAblePromise<void> | void;
groupCategoricalStats(col: Column & ISetColumn, group: IOrderedGroup, render: (group: ICategoricalStatistics, summary: ICategoricalStatistics, data: ICategoricalStatistics) => void): IAbortAblePromise<void> | void;
groupDateStats(col: Column & IDateColumn, group: IOrderedGroup, render: (group: IDateStatistics, summary: IDateStatistics, data: IDateStatistics) => void): IAbortAblePromise<void> | void;

summaryBoxPlotStats(col: Column & INumberColumn, render: (summary: IAdvancedBoxPlotData, data: IAdvancedBoxPlotData) => void): IAbortAblePromise<void> | void;
summaryNumberStats(col: Column & INumberColumn, render: (summary: IStatistics, data: IStatistics) => void): IAbortAblePromise<void> | void;
summaryCategoricalStats(col: Column & ISetColumn, render: (summary: ICategoricalStatistics, data: ICategoricalStatistics) => void): IAbortAblePromise<void> | void;
summaryDateStats(col: Column & IDateColumn, render: (summary: IDateStatistics, data: IDateStatistics) => void): IAbortAblePromise<void> | void;
groupRows<T>(col: Column, group: IOrderedGroup, compute: (rows: ISequence<IDataRow>) => T): ITask<T>;
groupExampleRows<T>(col: Column, group: IOrderedGroup, compute: (rows: ISequence<IDataRow>) => T): ITask<T>;

groupBoxPlotStats(col: Column & INumberColumn, group: IOrderedGroup): ITask<{group: IAdvancedBoxPlotData, summary: IAdvancedBoxPlotData, data: IAdvancedBoxPlotData}>;
groupNumberStats(col: Column & INumberColumn, group: IOrderedGroup): ITask<{group: IStatistics, summary: IStatistics, data: IStatistics}>;
groupCategoricalStats(col: Column & ISetColumn, group: IOrderedGroup): ITask<{group: ICategoricalStatistics, summary: ICategoricalStatistics, data: ICategoricalStatistics}>;
groupDateStats(col: Column & IDateColumn, group: IOrderedGroup): ITask<{group: IDateStatistics, summary: IDateStatistics, data: IDateStatistics}>;

summaryBoxPlotStats(col: Column & INumberColumn): ITask<{summary: IAdvancedBoxPlotData, data: IAdvancedBoxPlotData}>;
summaryNumberStats(col: Column & INumberColumn): ITask<{summary: IStatistics, data: IStatistics}>;
summaryCategoricalStats(col: Column & ISetColumn): ITask<{summary: ICategoricalStatistics, data: ICategoricalStatistics}>;
summaryDateStats(col: Column & IDateColumn): ITask<{summary: IDateStatistics, data: IDateStatistics}>;
}


Expand All @@ -37,97 +61,98 @@ export class DirectRenderTasks implements IRenderTasks {
this.dataCache.clear();
}

dirtyColumn(col: Column) {
this.dataCache.delete(col.id);
}

private byOrder(indices: IndicesArray) {
return lazySeq(indices).map(this.byIndex);
}

groupRows<T>(_col: Column, group: IOrderedGroup, compute: (rows: ISequence<IDataRow>) => T, render: (value: T) => void) {
render(compute(this.byOrder(group.order)));
groupRows<T>(_col: Column, group: IOrderedGroup, compute: (rows: ISequence<IDataRow>) => T) {
return taskNow(compute(this.byOrder(group.order)));
}

groupExampleRows<T>(_col: Column, group: IOrderedGroup, compute: (rows: ISequence<IDataRow>) => T, render: (value: T) => void) {
render(compute(this.byOrder(group.order.slice(0, 5))));
groupExampleRows<T>(_col: Column, group: IOrderedGroup, compute: (rows: ISequence<IDataRow>) => T) {
return taskNow(compute(this.byOrder(group.order.slice(0, 5))));
}

groupBoxPlotStats(col: Column & INumberColumn, group: IOrderedGroup, render: (group: IAdvancedBoxPlotData & IStatistics, summary: IAdvancedBoxPlotData & IStatistics, data: IAdvancedBoxPlotData & IStatistics) => void) {
this.groupNumberStats(col, group, render);
groupBoxPlotStats(col: Column & INumberColumn, group: IOrderedGroup) {
return this.groupNumberStats(col, group);
}

groupNumberStats(col: Column & INumberColumn, group: IOrderedGroup, render: (group: IAdvancedBoxPlotData & IAdvancedBoxPlotData & IStatistics, summary: IAdvancedBoxPlotData & IStatistics, data: IAdvancedBoxPlotData & IStatistics) => void) {
this.summaryNumberStats(col, (summary, data) => {
render(computeNormalizedStats(this.byOrder(group.order).map((d) => col.getNumber(d))), summary, data);
});
groupNumberStats(col: Column & INumberColumn, group: IOrderedGroup) {
const {summary, data} = this.summaryNumberStatsD(col);
return taskNow({group: computeNormalizedStats(this.byOrder(group.order).map((d) => col.getNumber(d))), summary, data});
}

groupCategoricalStats(col: Column & ISetColumn, group: IOrderedGroup, render: (group: ICategoricalStatistics, summary: ICategoricalStatistics, data: ICategoricalStatistics) => void) {
this.summaryCategoricalStats(col, (summary, data) => {
render(computeHist(this.byOrder(group.order).map((d) => col.getSet(d)), col.categories), summary, data);
});
groupCategoricalStats(col: Column & ISetColumn, group: IOrderedGroup) {
const {summary, data} = this.summaryCategoricalStatsD(col);
return taskNow({group: computeHist(this.byOrder(group.order).map((d) => col.getSet(d)), col.categories), summary, data});
}

groupDateStats(col: Column & IDateColumn, group: IOrderedGroup, render: (group: IDateStatistics, summary: IDateStatistics, data: IDateStatistics) => void) {
this.summaryDateStats(col, (summary, data) => {
render(computeDateStats(this.byOrder(group.order).map((d) => col.getDate(d))), summary, data);
});
groupDateStats(col: Column & IDateColumn, group: IOrderedGroup) {
const {summary, data} = this.summaryDateStatsD(col);
return taskNow({group: computeDateStats(this.byOrder(group.order).map((d) => col.getDate(d)), summary), summary, data});
}

summaryBoxPlotStats(col: Column & INumberColumn, render: (summary: IAdvancedBoxPlotData & IStatistics, data: IAdvancedBoxPlotData & IStatistics) => void) {
this.summaryNumberStats(col, render);
summaryBoxPlotStats(col: Column & INumberColumn) {
return this.summaryNumberStats(col);
}

summaryNumberStats(col: Column & INumberColumn, render: (summary: IAdvancedBoxPlotData & IStatistics, data: IAdvancedBoxPlotData & IStatistics) => void) {
const ranking = col.findMyRanker()!.getOrder();
this.dataNumberStats(col, (data) => {
render(computeNormalizedStats(this.byOrder(ranking).map((d) => col.getNumber(d)), data.hist.length), data);
});
summaryNumberStats(col: Column & INumberColumn) {
return taskNow(this.summaryNumberStatsD(col));
}

summaryCategoricalStats(col: Column & ISetColumn, render: (summary: ICategoricalStatistics, data: ICategoricalStatistics) => void) {
summaryCategoricalStats(col: Column & ISetColumn) {
return taskNow(this.summaryCategoricalStatsD(col));
}

summaryDateStats(col: Column & IDateColumn) {
return taskNow(this.summaryDateStatsD(col));
}

private summaryNumberStatsD(col: Column & INumberColumn) {
const ranking = col.findMyRanker()!.getOrder();
this.dataCategoricalStats(col, (data) => {
render(computeHist(this.byOrder(ranking).map((d) => col.getSet(d)), col.categories), data);
});
const data = this.dataNumberStats(col);
return {summary: computeNormalizedStats(this.byOrder(ranking).map((d) => col.getNumber(d)), data.hist.length), data};
}

summaryDateStats(col: Column & IDateColumn, render: (summary: IDateStatistics, data: IDateStatistics) => void) {
private summaryCategoricalStatsD(col: Column & ISetColumn) {
const ranking = col.findMyRanker()!.getOrder();
this.dataDateStats(col, (data) => {
render(computeDateStats(this.byOrder(ranking).map((d) => col.getDate(d)), data), data);
});
const data = this.dataCategoricalStats(col);
return {summary: computeHist(this.byOrder(ranking).map((d) => col.getSet(d)), col.categories), data};
}

private summaryDateStatsD(col: Column & IDateColumn) {
const ranking = col.findMyRanker()!.getOrder();
const data = this.dataDateStats(col);
return {summary: computeDateStats(this.byOrder(ranking).map((d) => col.getDate(d)), data), data};
}

dataBoxPlotStats(col: Column & INumberColumn, render: (data: IAdvancedBoxPlotData & IStatistics) => void) {
this.dataNumberStats(col, render);
dataBoxPlotStats(col: Column & INumberColumn) {
this.dataNumberStats(col);
}

dataNumberStats(col: Column & INumberColumn, render: (data: IAdvancedBoxPlotData & IStatistics) => void) {
private cached<T>(col: Column, creator: () => T) {
if (this.dataCache.has(col.id)) {
render(this.dataCache.get(col.id)!);
return;
return this.dataCache.get(col.id)!;
}
const s = computeNormalizedStats(lazySeq(this.data).map((d) => col.getNumber(d)));
const s = creator();
this.dataCache.set(col.id, s);
render(s);
return s;
}

dataCategoricalStats(col: Column & ISetColumn, render: (data: ICategoricalStatistics) => void) {
if (this.dataCache.has(col.id)) {
render(this.dataCache.get(col.id)!);
return;
}
const s = computeHist(lazySeq(this.data).map((d) => col.getSet(d)), col.categories);
this.dataCache.set(col.id, s);
render(s);
dataNumberStats(col: Column & INumberColumn) {
return this.cached(col, () => computeNormalizedStats(lazySeq(this.data).map((d) => col.getNumber(d))));
}

dataDateStats(col: Column & IDateColumn, render: (data: IDateStatistics) => void) {
if (this.dataCache.has(col.id)) {
return render(this.dataCache.get(col.id)!);
}
const s = computeDateStats(lazySeq(this.data).map((d) => col.getDate(d)));
this.dataCache.set(col.id, s);
render(s);
dataCategoricalStats(col: Column & ISetColumn) {
return this.cached(col, () => computeHist(lazySeq(this.data).map((d) => col.getSet(d)), col.categories));
}

dataDateStats(col: Column & IDateColumn) {
return this.cached(col, () => computeDateStats(lazySeq(this.data).map((d) => col.getDate(d))));
}

}
4 changes: 1 addition & 3 deletions src/renderer/AAggregatedGroupRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ export abstract class AAggregatedGroupRenderer<T extends Column> implements ICel
return {
template: `<div>${single.template}</div>`,
update: (node: HTMLElement, group: IOrderedGroup, groupMeta: IGroupMeta) => {
return context.tasks.groupRows(col, group, (rows) => {
return this.aggregatedIndex(rows, col);
}, (data) => {
return context.tasks.groupRows(col, group, (rows) => this.aggregatedIndex(rows, col)).then((data) => {
single.update(<HTMLElement>node.firstElementChild!, data.row, data.index, group, groupMeta);
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/ANumbersCellRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export abstract class ANumbersCellRenderer {
template: `<div class="${clazz}">${templateRow}</div>`,
update: (n: HTMLDivElement, group: IOrderedGroup) => {
// render a heatmap
return context.tasks.groupRows(col, group, (rows) => ANumbersCellRenderer.choose(col, rows), (data) => {
return context.tasks.groupRows(col, group, (rows) => ANumbersCellRenderer.choose(col, rows)).then((data) => {
update(n, data.normalized, data.raw, data.row!);
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/ActionRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default class ActionRenderer implements ICellRendererFactory {
ni.onclick = function (event) {
event.preventDefault();
event.stopPropagation();
context.tasks.groupRows(col, group, (r) => r, (rows) => {
context.tasks.groupRows(col, group, (r) => r).then((rows) => {
if (Array.isArray(rows)) {
actions[i].action(group, rows);
}
Expand Down
8 changes: 4 additions & 4 deletions src/renderer/BoxplotCellRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export default class BoxplotCellRenderer implements ICellRendererFactory {
}

return {box, label};
}, (data) => {
}).then((data) => {
// render
n.classList.toggle(cssClass('missing'), data === null);
if (data === null) {
Expand All @@ -130,8 +130,8 @@ export default class BoxplotCellRenderer implements ICellRendererFactory {
return {
template: isMapAbleColumn(col) ? MAPPED_BOXPLOT : BOXPLOT,
update: (n: HTMLElement) => {
return context.tasks.summaryBoxPlotStats(col, (data: IAdvancedBoxPlotData) => {
if (data == null) {
return context.tasks.summaryBoxPlotStats(col).then(({summary}) => {
if (summary == null) {
n.classList.add(cssClass('missing'));
return;
}
Expand All @@ -143,7 +143,7 @@ export default class BoxplotCellRenderer implements ICellRendererFactory {
Array.from(n.querySelectorAll('span')).forEach((d: HTMLElement, i) => d.textContent = range[i]);
}

renderDOMBoxPlot(n, data, data, sort, colorOf(col, null, imposer), isMapAbleColumn(col));
renderDOMBoxPlot(n, summary, summary, sort, colorOf(col, null, imposer), isMapAbleColumn(col));
});
}
};
Expand Down
22 changes: 11 additions & 11 deletions src/renderer/CategoricalCellRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ export default class CategoricalCellRenderer implements ICellRendererFactory {
return {
template: `${template}</div>`,
update: (n: HTMLElement, group: IOrderedGroup) => {
return context.tasks.groupCategoricalStats(col, group, (hist) => {
update(n, hist);
return context.tasks.groupCategoricalStats(col, group).then(({group}) => {
update(n, group);
});
}
};
Expand All @@ -69,12 +69,12 @@ function staticSummary(col: ISetColumn, context: IRenderContext, interactive: bo
return {
template: `${template}</div>`,
update: (n: HTMLElement) => {
return context.tasks.summaryCategoricalStats(col, (hist) => {
n.classList.toggle(cssClass('missing'), !hist);
if (!hist) {
return context.tasks.summaryCategoricalStats(col).then(({summary}) => {
n.classList.toggle(cssClass('missing'), !summary);
if (!summary) {
return;
}
update(n, hist, hist);
update(n, summary, summary);
});
}
};
Expand All @@ -89,14 +89,14 @@ function interactiveSummary(col: HasCategoricalFilter, context: IRenderContext,
if (!filterUpdate) {
filterUpdate = interactiveHist(col, n);
}
return context.tasks.summaryCategoricalStats(col, (hist, gHist) => {
filterUpdate((interactive && gHist) ? gHist.missing : (hist ? hist.missing : 0), col);
return context.tasks.summaryCategoricalStats(col).then(({summary, data}) => {
filterUpdate((interactive && data) ? data.missing : (summary ? summary.missing : 0), col);

n.classList.toggle(cssClass('missing'), !hist);
if (!hist) {
n.classList.toggle(cssClass('missing'), !summary);
if (!summary) {
return;
}
update(n, hist, gHist);
update(n, summary, data);
});
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/CategoricalHeatmapCellRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default class CategoricalHeatmapCellRenderer implements ICellRendererFact
return {
template: `<div class="${cssClass('heatmap')}">${templateRow}</div>`,
update: (n: HTMLElement, group: IOrderedGroup) => {
return context.tasks.groupRows(col, group, (rows) => union(col, rows), (value) => render(n, value));
return context.tasks.groupRows(col, group, (rows) => union(col, rows)).then((value) => render(n, value));
}
};
}
Expand Down
22 changes: 11 additions & 11 deletions src/renderer/CategoricalStackedDistributionlCellRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export default class CategoricalStackedDistributionlCellRenderer implements ICel
return {
template: `${template}</div>`,
update: (n: HTMLElement, group: IOrderedGroup) => {
return context.tasks.groupCategoricalStats(col, group, (hist, gHist) => {
update(n, hist, gHist);
return context.tasks.groupCategoricalStats(col, group).then(({group, summary}) => {
update(n, group, summary);
});
}
};
Expand All @@ -44,12 +44,12 @@ function staticSummary(col: ICategoricalColumn, context: IRenderContext) {
return {
template: `${template}</div>`,
update: (n: HTMLElement) => {
return context.tasks.summaryCategoricalStats(col, (hist) => {
n.classList.toggle(cssClass('missing'), !hist);
if (!hist) {
return context.tasks.summaryCategoricalStats(col).then(({summary}) => {
n.classList.toggle(cssClass('missing'), !summary);
if (!summary) {
return;
}
update(n, hist);
update(n, summary);
});
}
};
Expand All @@ -64,15 +64,15 @@ function interactiveSummary(col: HasCategoricalFilter, context: IRenderContext,
if (!filterUpdate) {
filterUpdate = interactiveHist(col, n);
}
return context.tasks.summaryCategoricalStats(col, (hist, gHist) => {
const missing = interactive && gHist ? gHist.missing : (hist ? hist.missing : 0);
return context.tasks.summaryCategoricalStats(col).then(({summary, data}) => {
const missing = interactive && data ? data.missing : (summary ? summary.missing : 0);
filterUpdate(missing, col);

n.classList.toggle(cssClass('missing'), !hist);
if (!hist) {
n.classList.toggle(cssClass('missing'), !summary);
if (!summary) {
return;
}
update(n, hist);
update(n, summary);
});
}
};
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/DateCellRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ export default class DateCellRenderer implements ICellRendererFactory {
update: (n: HTMLDivElement, group: IOrderedGroup) => {
const isGrouped = col.isGroupedBy() >= 0;
if (isGrouped) {
return context.tasks.groupRows(col, group, (rows) => choose(rows, col.getDateGrouper(), col), (chosen) => {
return context.tasks.groupRows(col, group, (rows) => choose(rows, col.getDateGrouper(), col)).then((chosen) => {
setText(n, chosen.name);
});
}
return context.tasks.groupExampleRows(col, group, (sample) => exampleText(col, sample), (text) => setText(n, text));
return context.tasks.groupExampleRows(col, group, (sample) => exampleText(col, sample)).then((text) => {
setText(n, text);
});
}
};
}
Expand Down
Loading

0 comments on commit d70f850

Please sign in to comment.