Skip to content

Commit

Permalink
start adding topN again
Browse files Browse the repository at this point in the history
  • Loading branch information
sgratzl committed Dec 27, 2018
1 parent 4ac544f commit 6f8b2b4
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 49 deletions.
1 change: 1 addition & 0 deletions demo/aggregate.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
}
const b = LineUpJS.builder(arr);
b.deriveColumns()
b.aggregationStrategy('group+top+item')
b.ranking(LineUpJS.buildRanking()
.supportTypes()
.group()
Expand Down
7 changes: 6 additions & 1 deletion src/builder/DataBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Column, IColumnDesc} from '../model';
import {DataProvider, LocalDataProvider, deriveColors, deriveColumnDescriptions, IDataProviderOptions, ILocalDataProviderOptions} from '../provider';
import {DataProvider, LocalDataProvider, deriveColors, deriveColumnDescriptions, IDataProviderOptions, ILocalDataProviderOptions, IAggregationStrategy} from '../provider';
import {LineUp, Taggle} from '../ui';
import ColumnBuilder from './column/ColumnBuilder';
import LineUpBuilder from './LineUpBuilder';
Expand Down Expand Up @@ -34,6 +34,11 @@ export default class DataBuilder extends LineUpBuilder {
return this;
}

aggregationStrategy(strategy: IAggregationStrategy) {
this.providerOptions.aggregationStrategy = strategy;
return this;
}

/**
* allow just a single selection
*/
Expand Down
50 changes: 38 additions & 12 deletions src/provider/ADataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {AEventDispatcher, debounce, ISequence, OrderedSet, IDebounceContext, IEv
import {Column, Ranking, AggregateGroupColumn, createAggregateDesc, IAggregateGroupColumnDesc, isSupportType, EDirtyReason, RankColumn, createRankDesc, createSelectionDesc, IColumnDesc, IDataRow, IGroup, IndicesArray, IOrderedGroup, ISelectionColumnDesc, EAggregationState, IColumnDump, IRankingDump} from '../model';
import {models} from '../model/models';
import {forEachIndices, everyIndices, toGroupID, unifyParents} from '../model/internal';
import {IDataProvider, IDataProviderDump, IDataProviderOptions, SCHEMA_REF, IExportOptions} from './interfaces';
import {IDataProvider, IDataProviderDump, IDataProviderOptions, SCHEMA_REF, IExportOptions, IAggregationStrategy} from './interfaces';
import {exportRanking, map2Object, object2Map} from './utils';
import {IRenderTasks} from '../renderer';

Expand Down Expand Up @@ -201,7 +201,7 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider {
*/
private readonly selection = new OrderedSet<number>();

//Map<ranking.id@group.name, showTopN>
//Map<ranking.id@group.name, -1=expand,0=collapse,N=topN>
private readonly aggregations = new Map<string, number>(); // not part of = show all

private uid = 0;
Expand All @@ -212,13 +212,21 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider {
readonly columnTypes: {[columnType: string]: typeof Column};

protected readonly multiSelections: boolean;
private readonly aggregationStrategy: IAggregationStrategy;
private showTopN: number;

constructor(options: Partial<IDataProviderOptions> = {}) {
super();
this.columnTypes = Object.assign(models(), options.columnTypes || {});
this.multiSelections = options.singleSelection !== true;
this.showTopN = options.showTopN != null ? options.showTopN : 0;
const o: Readonly<IDataProviderOptions> = Object.assign({
columnTypes: {},
singleSelection: false,
showTopN: 10,
aggregationStrategy: 'item'
}, options);
this.columnTypes = Object.assign(models(), o.columnTypes);
this.multiSelections = o.singleSelection !== true;
this.showTopN = o.showTopN;
this.aggregationStrategy = o.aggregationStrategy;
}

/**
Expand Down Expand Up @@ -697,6 +705,10 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider {
return n < 0 ? EAggregationState.EXPAND : (n === 0 ? EAggregationState.COLLAPSE : EAggregationState.EXPAND_TOP_N);
}

setAggregated(ranking: Ranking, group: IGroup, value: boolean) {
return this.setAggregationState(ranking, group, value ? EAggregationState.COLLAPSE : EAggregationState.EXPAND);
}

setAggregationState(ranking: Ranking, group: IGroup, value: EAggregationState) {
this.setTopNAggregated(ranking, group, value === EAggregationState.COLLAPSE ? 0 : (value === EAggregationState.EXPAND_TOP_N ? this.showTopN : -1));
}
Expand Down Expand Up @@ -724,20 +736,34 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider {
}
}

getAggregationStrategy() {
return this.aggregationStrategy;
}

private initAggregateState(ranking: Ranking, groups: IGroup[]) {
// by default show top N
let initial = -1;
switch(this.aggregationStrategy) {
case 'group':
initial = 0;
break;
case 'item':
case 'group+item':
case 'group+item+top':
initial = -1;
break;
case 'group+top+item':
initial = this.showTopN;
break;
}

for (const group of groups) {
const key = `${ranking.id}@${toGroupID(group)}`;
if (!this.aggregations.has(key)) {
this.aggregations.set(key, this.showTopN);
if (!this.aggregations.has(key) && initial >= 0) {
this.aggregations.set(key, initial);
}
}
}

setAggregated(ranking: Ranking, group: IGroup, value: boolean) {
return this.setTopNAggregated(ranking, group, value ? 0 : -1);
}

setTopNAggregated(ranking: Ranking, group: IGroup, value: number) {
this.unaggregateParents(ranking, group);
const key = `${ranking.id}@${toGroupID(group)}`;
Expand Down
10 changes: 10 additions & 0 deletions src/provider/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {IAbortAblePromise} from 'lineupengine';
export {ABORTED} from 'lineupengine';
export declare type IAbortAblePromise<T> = IAbortAblePromise<T>;

export declare type IAggregationStrategy = 'group' | 'item' | 'group+item' | 'group+top+item' | 'group+item+top';

export interface IDataProviderOptions {
columnTypes: {[columnType: string]: typeof Column};

Expand All @@ -20,6 +22,12 @@ export interface IDataProviderOptions {
* @default 10
*/
showTopN: number;

/**
* aggregation strategy to show upon grouping, see also showTopN
* @default 'item'
*/
aggregationStrategy: IAggregationStrategy;
}

export interface IDataProvider extends AEventDispatcher {
Expand Down Expand Up @@ -66,6 +74,8 @@ export interface IDataProvider extends AEventDispatcher {

getColumns(): IColumnDesc[];

getAggregationStrategy(): IAggregationStrategy;

isAggregated(ranking: Ranking, group: IGroup): boolean;

setAggregationState(ranking: Ranking, group: IGroup, state: EAggregationState): void;
Expand Down
5 changes: 1 addition & 4 deletions src/styles/_vars.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ $lu_drag_over: rgba(151, 151, 151, 0.8) !default;

$lu_header_background: #6d6c6c !default;

$lu_body_font: 10pt 'Helvetica Neue',
Helvetica,
Arial,
sans-serif !default;
$lu_body_font: 10pt 'Helvetica Neue', Helvetica, Arial, sans-serif !default;
$lu_body_text_hover_color: darkblue !default;

$lu_slope_width: 200px !default;
Expand Down
64 changes: 32 additions & 32 deletions src/ui/EngineRanking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import {ACellTableSection, GridStyleManager, IAbortAblePromise, ICellRenderConte
import {ILineUpFlags} from '../config';
import {HOVER_DELAY_SHOW_DETAIL} from '../constants';
import {AEventDispatcher, clear, debounce, IEventContext, IEventHandler, IEventListener} from '../internal';
import {Column, IGroupData, IGroupItem, IOrderedGroup, isGroup, isMultiLevelColumn, Ranking, StackColumn, IGroupParent} from '../model';
import {Column, IGroupData, IGroupItem, IOrderedGroup, isGroup, isMultiLevelColumn, Ranking, StackColumn, IGroupParent, defaultGroup} from '../model';
import {IImposer, IRenderCallback, IRenderContext} from '../renderer';
import {CANVAS_HEIGHT, COLUMN_PADDING, cssClass, engineCssClass} from '../styles';
import {lineupAnimation} from './animation';
import {IRankingBodyContext, IRankingHeaderContextContainer} from './interfaces';
import MultiLevelRenderColumn from './MultiLevelRenderColumn';
import RenderColumn, {IRenderers} from './RenderColumn';
import SelectionManager from './SelectionManager';
import {groupRoots, isOrderedGroup, toRowMeta} from '../model/internal';
import {groupRoots, toRowMeta} from '../model/internal';

export interface IEngineRankingContext extends IRankingHeaderContextContainer, IRenderContext {
createRenderer(c: Column, imposer?: IImposer): IRenderers;
Expand Down Expand Up @@ -833,6 +833,9 @@ export default class EngineRanking extends ACellTableSection<RenderColumn> imple
groupData(): (IGroupItem | IGroupData)[] {
const groups = this.ranking.getGroups();
const provider = this.ctx.provider;
const strategy = provider.getAggregationStrategy();
const alwaysShowGroup = strategy === 'group+item' || strategy === 'group+item+top' || strategy === 'group+top+item';

const r = <(IGroupItem | IGroupData)[]>[];

if (groups.length === 0) {
Expand All @@ -847,48 +850,45 @@ export default class EngineRanking extends ACellTableSection<RenderColumn> imple
});
};

if (groups.length === 1 && groups[0].name === defaultGroup.name) {
const group = groups[0];
const l = group.order.length;
for (let i = 0; i < l; ++i) {
pushItem(group, group.order[i], i);
}
return r;
}

const roots = groupRoots(groups);

const pushGroup = (group: IOrderedGroup | Readonly<IGroupParent>) => {
const n = provider.getTopNAggregated(this.ranking, group);
if (n < 0) {
// expand
const gparent = <IGroupParent>group;
if (Array.isArray(gparent.subGroups) && gparent.subGroups.length > 0) {
for (const g of gparent.subGroups) {
pushGroup(<IOrderedGroup | Readonly<IGroupParent>>g);
}
} else if (isOrderedGroup(group)) {
const l = group.order.length;
for (let i = 0; i < l; ++i) {
pushItem(group, group.order[i], i);
}

// all are IOrderedGroup since propagated
const ordered = <IOrderedGroup>group;
const gparent = <IGroupParent>group;

if (n === 0 || alwaysShowGroup) {
r.push(ordered);
}

if (n !== 0 && Array.isArray(gparent.subGroups) && gparent.subGroups.length > 0) {
for (const g of gparent.subGroups) {
pushGroup(<IOrderedGroup | Readonly<IGroupParent>>g);
}
} else if (isOrderedGroup(group)) {
// collapse
r.push(group);
return;
}

const l = n < 0 ? ordered.order.length : Math.min(n, ordered.order.length);
for (let i = 0; i < l; ++i) {
pushItem(ordered, ordered.order[i], i);
}
// should not happen since propagated
};

for (const root of roots) {
pushGroup(root);
}

// for (const group of groups) {
// const length = group.order.length;

// const n = provider.getTopNAggregated(this.ranking, group);

// // always the group for stratified datasets
// // if (n >= 0) {
// r.push(Object.assign(group, {meta: <IGroupMeta>(n === 0 ? `first last` : (n > 0 ? 'first top' : `first`))}));
// // }

// const slice = Math.min(n >= 0 ? n : Number.POSITIVE_INFINITY, length);


// }
return r;
}

Expand Down

0 comments on commit 6f8b2b4

Please sign in to comment.