Skip to content
This repository has been archived by the owner on Jun 29, 2022. It is now read-only.

Commit

Permalink
chore: comments + linting for change-analysis + postinstall fifinet (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
BryanPan342 authored Jul 14, 2021
1 parent 767c7d4 commit 028ed56
Show file tree
Hide file tree
Showing 62 changed files with 3,214 additions and 2,732 deletions.
3 changes: 2 additions & 1 deletion packages/change-analysis-models/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"build": "npm run lint && npm run compile",
"compile": "npx tsc",
"lint": "eslint . --ext .ts",
"test": "jest"
"test": "jest",
"postinstall": "npm --prefix node_modules/fifinet run build && mv node_modules/fifinet/dist/* node_modules/fifinet"
},
"repository": {
"type": "git",
Expand Down
10 changes: 0 additions & 10 deletions packages/change-analysis/.eslintrc

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import { AggCharacteristicValue, Aggregation, getAllCharacteristics } from "cdk-change-analyzer-models";
import { AggCharacteristicValue, Aggregation } from 'cdk-change-analyzer-models';

export type AggDescriptionCreator =
export type AggDescriptionCreator =
(characteristics: Record<string, AggCharacteristicValue>) => {
describedCharacteristics?: string[],
descriptions?: string[]
describedCharacteristics?: string[],
descriptions?: string[]
};

export function addAggDescriptions<T>(
aggRoots: Aggregation<T>[], descriptionCreators: AggDescriptionCreator[]
aggRoots: Aggregation<T>[], descriptionCreators: AggDescriptionCreator[],
): Aggregation<T>[]{
const aggStack: Aggregation<T>[] = [...aggRoots];
const aggStack: Aggregation<T>[] = [...aggRoots];

while(aggStack.length){
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const agg = aggStack.pop()!;
aggStack.push(...agg?.subAggs ?? []);
if(!agg.descriptions)
agg.descriptions = [];
const undescribedCharacteristics = new Set(Object.keys(agg.characteristics));
agg.descriptions.push(...descriptionCreators.flatMap(creator => {
const { descriptions, describedCharacteristics } = creator(agg.characteristics);
describedCharacteristics?.forEach(c => undescribedCharacteristics.delete(c));
return descriptions ?? [];
}));
while(aggStack.length){
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const agg = aggStack.pop()!;
aggStack.push(...agg?.subAggs ?? []);
if(!agg.descriptions)
agg.descriptions = [];
const undescribedCharacteristics = new Set(Object.keys(agg.characteristics));
agg.descriptions.push(...descriptionCreators.flatMap(creator => {
const { descriptions, describedCharacteristics } = creator(agg.characteristics);
describedCharacteristics?.forEach(c => undescribedCharacteristics.delete(c));
return descriptions ?? [];
}));

agg.descriptions.push(...[...undescribedCharacteristics].map(c => `${c}: ${agg.characteristics[c]}`));
}
agg.descriptions.push(...[...undescribedCharacteristics].map(c => `${c}: ${agg.characteristics[c]}`));
}

return aggRoots;
return aggRoots;
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { CompOpAggCharacteristics, AggCharacteristicValue } from "cdk-change-analyzer-models";
import { AggModule } from "./aggregation-module";
import { CompOpAggCharacteristics, AggCharacteristicValue } from 'cdk-change-analyzer-models';
import { AggModule } from './aggregation-module';

type CharacteristicRequirements = { [c in CompOpAggCharacteristics]?: AggCharacteristicValue };

export type AggModuleTreeNode<T> = {
module: AggModule<T>,
submodules?: AggModuleTreeNode<T>[],
requiredCharacteristics?: CharacteristicRequirements,
module: AggModule<T>,
submodules?: AggModuleTreeNode<T>[],
requiredCharacteristics?: CharacteristicRequirements,

/**
/**
* 'disableOnNoExtraInfo' is true if the module should be used
* only when it offers detailing information.
* only when it offers detailing information.
*
* For example, for a TreeNode such as the following:
* {
Expand All @@ -24,14 +24,14 @@ export type AggModuleTreeNode<T> = {
* even if module B only creates one Aggregation. However, since 'disableOnNoExtraInfo' is true,
* if module B does not offer any further grouping (aka has only one resulting Aggregation) it
* will be discarded, along with its subgroups.
*
*
*/
disableOnNoExtraInfo?: boolean,
disableOnNoExtraInfo?: boolean,

/**
* 'disableSubmoduleCollapse' is true if a single Aggregation output of this Node
/**
* 'disableSubmoduleCollapse' is true if a single Aggregation output of this Node
* should be merged with parent module's Aggregation. Note: with disableOnNoExtraInfo
*
*
* For example, for a TreeNode such as the following:
* {
* module: A (this module groups entities by characteristic 'a'),
Expand All @@ -48,29 +48,29 @@ export type AggModuleTreeNode<T> = {
* However, since module B has `disableSubmoduleCollapse: true` associated, module C's Aggregation will not
* be collapsed and, hence, for the same scenario, there will be groups with characteristics 'a' and 'b',
* which each have a subgroup with characteristic 'c'.
*
*
*
*
*/
disableSubmoduleCollapse?: boolean,
disableSubmoduleCollapse?: boolean,

/**
/**
* 'forceSubmoduleCollapse' is true if a module's resulting Aggregations should be collapsed with the parent module
*
*
* For example, for a TreeNode such as the following:
* {
* module: A (this module groups entities by characteristic 'a'),
* forceSubmoduleCollapse: true,
* submodules: [{
* module: B (this module groups entities by characteristic 'b'),
*
*
* }]
* }
* By default, only if there is a single 'b' characteristic value (module B results in a single Aggregations),
* will the Aggregation resulting from B be merged with the source Aggregation coming from module A. However, since
* 'forceSubmoduleCollapse' is true in module A, if there are multiple 'b' characteristic values (module B results
* in multiple Aggregations) they will all be collapsed with module A's Aggregations. The resulting Aggregations will be as many as
* the B Aggregations found and will have both 'b' and 'a' characteristics.
*
*
*/
forceSubmoduleCollapse?: boolean,
forceSubmoduleCollapse?: boolean,
}
49 changes: 24 additions & 25 deletions packages/change-analysis/aggregations/aggregation-module.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,47 @@
import { groupArrayBy } from "cdk-change-analyzer-models";
import { Aggregation } from "cdk-change-analyzer-models";
import { Aggregation, groupArrayBy } from 'cdk-change-analyzer-models';

/**
* Defines an Aggregation
*/
export abstract class AggModule<T> {
constructor(
public readonly label: string
){}
constructor(
public readonly label: string,
){}

public abstract extractGroups(entities: Set<T>): Aggregation<T>[];
public abstract extractGroups(entities: Set<T>): Aggregation<T>[];
}

/**
* Defines an Aggregation module that groups entities based on
* an indexable value (obtained using indexValueGetter)
*/
export class EqualityAggModule<T> extends AggModule<T> {
constructor(
label: string,
public readonly indexValueGetter: (e: T) => string | number | boolean | undefined
){super(label);}
constructor(
label: string,
public readonly indexValueGetter: (e: T) => string | number | boolean | undefined,
){super(label);}

extractGroups(entities: Set<T>): Aggregation<T>[] {
return [...groupArrayBy([...entities], this.indexValueGetter)].map(([key, ops]) => ({
entities: new Set(ops),
characteristics: {
[this.label]: key
}
}));
}
extractGroups(entities: Set<T>): Aggregation<T>[] {
return [...groupArrayBy([...entities], this.indexValueGetter)].map(([key, ops]) => ({
entities: new Set(ops),
characteristics: {
[this.label]: key,
},
}));
}
}

/**
* Defines an Aggregation module that groups entities based on
* similarity between each pair of entities (obtained using similarityChecker)
*/
export class SimilarityAggModule<T> extends AggModule<T> {
constructor(
label: string,
public readonly similarityChecker: (e: T) => boolean
){super(label);}
constructor(
label: string,
public readonly similarityChecker: (e: T) => boolean,
){super(label);}

extractGroups(): Aggregation<T>[] {
throw Error("Similarity Agg Module does not have an implementation yet");
}
extractGroups(): Aggregation<T>[] {
throw Error('Similarity Agg Module does not have an implementation yet');
}
}
138 changes: 71 additions & 67 deletions packages/change-analysis/aggregations/aggregations-extractor.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,86 @@
import { setsEqual } from "cdk-change-analyzer-models";
import { AggModuleTreeNode } from "./aggregation-module-tree-node";
import { AggCharacteristicValue, Aggregation } from "cdk-change-analyzer-models";
import { AggCharacteristicValue, Aggregation, setsEqual } from 'cdk-change-analyzer-models';
import { AggModuleTreeNode } from './aggregation-module-tree-node';

export class ModuleTreeAggsExtractor {

public static extract<T>(
moduleNode: AggModuleTreeNode<T>,
entities: T[],
): Aggregation<T>[] {
return this.extractTreeRoot(moduleNode, new Set(entities));
}
public static extract<T>(
moduleNode: AggModuleTreeNode<T>,
entities: T[],
): Aggregation<T>[] {
return this.extractTreeRoot(moduleNode, new Set(entities));
}

private static extractTreeRoot<T>(
moduleNode: AggModuleTreeNode<T>,
entities: Set<T>,
characteristics: Record<string, AggCharacteristicValue> = {}
): Aggregation<T>[] {
if(moduleNode.requiredCharacteristics){
const isValid = Object.entries(moduleNode.requiredCharacteristics).every(([c, v]) => {
return characteristics[c] === v;
});
if(!isValid) return [];
}
private static extractTreeRoot<T>(
moduleNode: AggModuleTreeNode<T>,
entities: Set<T>,
characteristics: Record<string, AggCharacteristicValue> = {},
): Aggregation<T>[] {
if(moduleNode.requiredCharacteristics){
const isValid = Object.entries(moduleNode.requiredCharacteristics).every(([c, v]) => {
return characteristics[c] === v;
});
if(!isValid) return [];
}

const directAggs = moduleNode.module.extractGroups(entities);
const directAggs = moduleNode.module.extractGroups(entities);

if(moduleNode.disableOnNoExtraInfo && directAggs.length <= 1)
return [];
if(moduleNode.disableOnNoExtraInfo && directAggs.length <= 1)
return [];

const finalAggs = directAggs.flatMap(g => {
const gs = ModuleTreeAggsExtractor.findAggsIntersections<T>(
moduleNode.submodules?.flatMap(
sm => ModuleTreeAggsExtractor.extractTreeRoot(sm, g.entities, {...characteristics, ...g.characteristics})
) ?? []
);
const finalAggs = directAggs.flatMap(g => {
const gs = ModuleTreeAggsExtractor.findAggsIntersections<T>(
moduleNode.submodules?.flatMap(
sm => ModuleTreeAggsExtractor.extractTreeRoot(sm, g.entities, {...characteristics, ...g.characteristics}),
) ?? [],
);

if(moduleNode.forceSubmoduleCollapse
|| (!moduleNode.disableSubmoduleCollapse && gs.length === 1 && setsEqual(g.entities, gs[0].entities))){
return gs.map(gSubGroup => ({...gSubGroup, characteristics: {...g.characteristics, ...gSubGroup.characteristics} }));
} else if(gs && gs.length) {
g.subAggs = gs;
gs.forEach(gSubGroup => gSubGroup.parentAgg = g);
}

return [g];
}
if(moduleNode.forceSubmoduleCollapse ||
(!moduleNode.disableSubmoduleCollapse && gs.length === 1&& setsEqual(g.entities, gs[0].entities))) {
return gs.map(gSubGroup =>
({
...gSubGroup,
characteristics: {...g.characteristics, ...gSubGroup.characteristics},
}),
);
} else if(gs && gs.length) {
g.subAggs = gs;
gs.forEach(gSubGroup => gSubGroup.parentAgg = g);
}

return finalAggs;
}
return [g];
},
);

return finalAggs;
}

private static findAggsIntersections<T>(groups: Aggregation<T>[]): Aggregation<T>[]{
const resultingIntersections: Record<string, Aggregation<T>> = {};
const indexGetter = (originalAggs: number[]) => originalAggs.join(',');
private static findAggsIntersections<T>(groups: Aggregation<T>[]): Aggregation<T>[]{
const resultingIntersections: Record<string, Aggregation<T>> = {};
const indexGetter = (originalAggs: number[]) => originalAggs.join(',');

for(let agg = 0; agg< groups.length; agg++){ // for each original Agg
for(const operation of [...groups[agg].entities]){ // each operation in Agg
const aggsContainingOperation: number[] = [agg];
for(let agg2 = agg+1; agg2 < groups.length; agg2++){ // check all other original Aggs for same operation
if(groups[agg2].entities.has(operation)){
aggsContainingOperation.push(agg2); // add it to the intersection identifier
groups[agg2].entities.delete(operation);
}
}
if(aggsContainingOperation.length > 1){
const intersectionIndex = indexGetter(aggsContainingOperation);
if(!resultingIntersections[intersectionIndex]){
resultingIntersections[intersectionIndex] = {
entities: new Set([operation]),
characteristics: Object.assign({}, ...aggsContainingOperation.map(i => groups[i].characteristics))
};
} else {
resultingIntersections[intersectionIndex].entities.add(operation);
}
groups[agg].entities.delete(operation);
}
}
for(let agg = 0; agg< groups.length; agg++){ // for each original Agg
for(const operation of [...groups[agg].entities]){ // each operation in Agg
const aggsContainingOperation: number[] = [agg];
for(let agg2 = agg+1; agg2 < groups.length; agg2++){ // check all other original Aggs for same operation
if(groups[agg2].entities.has(operation)){
aggsContainingOperation.push(agg2); // add it to the intersection identifier
groups[agg2].entities.delete(operation);
}
}
if(aggsContainingOperation.length > 1){
const intersectionIndex = indexGetter(aggsContainingOperation);
if(!resultingIntersections[intersectionIndex]){
resultingIntersections[intersectionIndex] = {
entities: new Set([operation]),
characteristics: Object.assign({}, ...aggsContainingOperation.map(i => groups[i].characteristics)),
};
} else {
resultingIntersections[intersectionIndex].entities.add(operation);
}
groups[agg].entities.delete(operation);
}
return [...groups, ...Object.values(resultingIntersections)].filter(agg => agg.entities.size > 0);
}
}
return [...groups, ...Object.values(resultingIntersections)].filter(agg => agg.entities.size > 0);
}
}
Loading

0 comments on commit 028ed56

Please sign in to comment.