Move weight logic to analysis (#966)

There's a folder called `app/credExplorer/weights` which contains the
type specification for weights (for PageRank configurability), and also
contains frontend code for specifying those weights. This commit creates
a `weights` module under `analysis` which will contain just the logic
for specifying and using the weights, without any frontend
consideration.

It's mostly a port of the existing logic in `credExplorer/weights`, with
the caveat that app adapter related concepts have been removed, in favor
of referencing the declaration instead.

We then remove the duplicated logic and re-route imports.

Test plan: `yarn test`
This commit is contained in:
Dandelion Mané 2018-10-31 13:27:03 -07:00 committed by GitHub
parent 86a5b532f8
commit f3ddb84cbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 193 additions and 168 deletions

57
src/analysis/weights.js Normal file
View File

@ -0,0 +1,57 @@
// @flow
import * as MapUtil from "../util/map";
import {type NodeAddressT, type EdgeAddressT} from "../core/graph";
import type {EdgeType, NodeType} from "./types";
import type {PluginDeclaration} from "./pluginDeclaration";
export type WeightedNodeType = {|+type: NodeType, +weight: number|};
export type WeightedEdgeType = {|
+type: EdgeType,
+forwardWeight: number,
+backwardWeight: number,
|};
export type WeightedTypes = {|
// Map from the weighted type's prefix to the type
+nodes: Map<NodeAddressT, WeightedNodeType>,
+edges: Map<EdgeAddressT, WeightedEdgeType>,
|};
export function defaultWeightedNodeType(type: NodeType): WeightedNodeType {
return {
type,
weight: type.defaultWeight,
};
}
export function defaultWeightedEdgeType(type: EdgeType): WeightedEdgeType {
return {
type,
forwardWeight: type.defaultForwardWeight,
backwardWeight: type.defaultBackwardWeight,
};
}
export function defaultWeightsForDeclaration(
declaration: PluginDeclaration
): WeightedTypes {
return {
nodes: new Map(
declaration.nodeTypes.map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(
declaration.edgeTypes.map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
}
export function combineWeights(
ws: $ReadOnlyArray<WeightedTypes>
): WeightedTypes {
return {
nodes: MapUtil.merge(ws.map((x) => x.nodes)),
edges: MapUtil.merge(ws.map((x) => x.edges)),
};
}

View File

@ -0,0 +1,106 @@
// @flow
import {
defaultWeightedNodeType,
defaultWeightedEdgeType,
defaultWeightsForDeclaration,
combineWeights,
} from "./weights";
import {
inserterNodeType,
machineNodeType,
assemblesEdgeType,
transportsEdgeType,
declaration,
} from "../plugins/demo/declaration";
describe("analysis/weights", () => {
describe("defaultWeightedNodeType", () => {
it("sets default weight as specified in type", () => {
const wnt = defaultWeightedNodeType(inserterNodeType);
expect(wnt.weight).toEqual(wnt.type.defaultWeight);
});
});
describe("defaultWeightedEdgeType", () => {
it("sets default weights as specified in the type", () => {
const wet = defaultWeightedEdgeType(assemblesEdgeType);
expect(wet.forwardWeight).toEqual(wet.type.defaultForwardWeight);
expect(wet.backwardWeight).toEqual(wet.type.defaultBackwardWeight);
});
});
describe("defaultWeightsForDeclaration", () => {
it("works on the demo declaration", () => {
const expected = {
nodes: new Map(
declaration.nodeTypes.map((x) => [
x.prefix,
defaultWeightedNodeType(x),
])
),
edges: new Map(
declaration.edgeTypes.map((x) => [
x.prefix,
defaultWeightedEdgeType(x),
])
),
};
expect(defaultWeightsForDeclaration(declaration)).toEqual(expected);
});
});
describe("combineWeights", () => {
const defaultWeights = () => defaultWeightsForDeclaration(declaration);
const emptyWeights = () => ({nodes: new Map(), edges: new Map()});
it("successfully combines WeightedTypes", () => {
const weights1 = {
nodes: new Map().set(
inserterNodeType.prefix,
defaultWeightedNodeType(inserterNodeType)
),
edges: new Map().set(
assemblesEdgeType.prefix,
defaultWeightedEdgeType(assemblesEdgeType)
),
};
const weights2 = {
nodes: new Map().set(
machineNodeType.prefix,
defaultWeightedNodeType(machineNodeType)
),
edges: new Map().set(
transportsEdgeType.prefix,
defaultWeightedEdgeType(transportsEdgeType)
),
};
expect(combineWeights([weights1, weights2])).toEqual(defaultWeights());
});
it("treats empty weights as an identity", () => {
expect(
combineWeights([emptyWeights(), defaultWeights(), emptyWeights()])
).toEqual(defaultWeights());
});
it("errors on duplicate edge prefix", () => {
const weights = {
nodes: new Map(),
edges: new Map().set(
assemblesEdgeType.prefix,
defaultWeightedEdgeType(assemblesEdgeType)
),
};
expect(() => combineWeights([weights, weights])).toThrowError(
"duplicate key"
);
});
it("errors on duplicate node prefix", () => {
const weights = {
nodes: new Map().set(
inserterNodeType.prefix,
defaultWeightedNodeType(inserterNodeType)
),
edges: new Map(),
};
expect(() => combineWeights([weights, weights])).toThrowError(
"duplicate key"
);
});
});
});

View File

@ -10,10 +10,8 @@ import Link from "../Link";
import {defaultStaticAdapters} from "../adapters/defaultPlugins";
import {PagerankTable} from "./pagerankTable/Table";
import {
type WeightedTypes,
defaultWeightsForAdapterSet,
} from "./weights/weights";
import type {WeightedTypes} from "../../analysis/weights";
import {defaultWeightsForAdapterSet} from "./weights/weights";
import RepositorySelect from "./RepositorySelect";
import {Prefix as GithubPrefix} from "../../plugins/github/nodes";
import {

View File

@ -9,7 +9,7 @@ import type {PagerankNodeDecomposition} from "../../../analysis/pagerankNodeDeco
import {DynamicAdapterSet} from "../../adapters/adapterSet";
import type {DynamicAppAdapter} from "../../adapters/appAdapter";
import {FALLBACK_NAME} from "../../adapters/fallbackAdapter";
import {type WeightedTypes} from "../weights/weights";
import type {WeightedTypes} from "../../../analysis/weights";
import {WeightConfig} from "../weights/WeightConfig";
import {NodeRowList} from "./Node";

View File

@ -13,7 +13,7 @@ import {
} from "../../analysis/pagerank";
import {StaticAdapterSet, DynamicAdapterSet} from "../adapters/adapterSet";
import type {WeightedTypes} from "./weights/weights";
import type {WeightedTypes} from "../../analysis/weights";
import {weightsToEdgeEvaluator} from "./weights/weightsToEdgeEvaluator";
/*

View File

@ -11,10 +11,8 @@ import {Graph, NodeAddress} from "../../core/graph";
import {Assets} from "../assets";
import {makeRepoId, type RepoId} from "../../core/repoId";
import {type EdgeEvaluator} from "../../analysis/pagerank";
import {
type WeightedTypes,
defaultWeightsForAdapterSet,
} from "./weights/weights";
import type {WeightedTypes} from "../../analysis/weights";
import {defaultWeightsForAdapterSet} from "./weights/weights";
import {StaticAdapterSet, DynamicAdapterSet} from "../adapters/adapterSet";
import type {
PagerankNodeDecomposition,

View File

@ -3,7 +3,7 @@
import React from "react";
import {WeightSlider, type Props as WeightSliderProps} from "./WeightSlider";
import type {WeightedEdgeType} from "./weights";
import type {WeightedEdgeType} from "../../../analysis/weights";
export class EdgeTypeConfig extends React.Component<{
+weightedType: WeightedEdgeType,

View File

@ -2,7 +2,7 @@
import React from "react";
import {WeightSlider} from "./WeightSlider";
import type {WeightedNodeType} from "./weights";
import type {WeightedNodeType} from "../../../analysis/weights";
export class NodeTypeConfig extends React.Component<{
+weightedType: WeightedNodeType,

View File

@ -7,11 +7,11 @@ import {NodeTypeConfig} from "./NodeTypeConfig";
import {EdgeTypeConfig} from "./EdgeTypeConfig";
import {StaticAppAdapter} from "../../adapters/appAdapter";
import {styledVariable} from "./EdgeTypeConfig";
import {
type WeightedTypes,
type WeightedEdgeType,
type WeightedNodeType,
} from "./weights";
import type {
WeightedTypes,
WeightedEdgeType,
WeightedNodeType,
} from "../../../analysis/weights";
export type Props = {|
+adapter: StaticAppAdapter,

View File

@ -15,11 +15,11 @@ import {
import {NodeTypeConfig} from "./NodeTypeConfig";
import {EdgeTypeConfig} from "./EdgeTypeConfig";
import {
defaultWeightsForAdapter,
type WeightedTypes,
defaultWeightedNodeType,
defaultWeightedEdgeType,
type WeightedTypes,
} from "./weights";
} from "../../../analysis/weights";
import {defaultWeightsForAdapter} from "./weights";
require("../../testUtil").configureEnzyme();

View File

@ -5,7 +5,7 @@ import * as NullUtil from "../../../util/null";
import * as MapUtil from "../../../util/map";
import type {StaticAdapterSet} from "../../adapters/adapterSet";
import type {WeightedTypes} from "./weights";
import type {WeightedTypes} from "../../../analysis/weights";
import {PluginWeightConfig} from "./PluginWeightConfig";
import {FALLBACK_NAME} from "../../adapters/fallbackAdapter";

View File

@ -1,64 +1,17 @@
// @flow
import * as MapUtil from "../../../util/map";
import {type NodeAddressT, type EdgeAddressT} from "../../../core/graph";
import type {EdgeType, NodeType} from "../../../analysis/types";
import {
type WeightedTypes,
combineWeights,
defaultWeightsForDeclaration,
} from "../../../analysis/weights";
import type {StaticAppAdapter} from "../../adapters/appAdapter";
import type {StaticAdapterSet} from "../../adapters/adapterSet";
export type WeightedNodeType = {|+type: NodeType, +weight: number|};
export type WeightedEdgeType = {|
+type: EdgeType,
+forwardWeight: number,
+backwardWeight: number,
|};
export type WeightedTypes = {|
// Map from the weighted type's prefix to the type
+nodes: Map<NodeAddressT, WeightedNodeType>,
+edges: Map<EdgeAddressT, WeightedEdgeType>,
|};
export function defaultWeightedNodeType(type: NodeType): WeightedNodeType {
return {
type,
weight: type.defaultWeight,
};
}
export function defaultWeightedEdgeType(type: EdgeType): WeightedEdgeType {
return {
type,
forwardWeight: type.defaultForwardWeight,
backwardWeight: type.defaultBackwardWeight,
};
}
export function defaultWeightsForAdapter(
adapter: StaticAppAdapter
): WeightedTypes {
return {
nodes: new Map(
adapter
.declaration()
.nodeTypes.map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(
adapter
.declaration()
.edgeTypes.map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
}
export function combineWeights(
ws: $ReadOnlyArray<WeightedTypes>
): WeightedTypes {
return {
nodes: MapUtil.merge(ws.map((x) => x.nodes)),
edges: MapUtil.merge(ws.map((x) => x.edges)),
};
return defaultWeightsForDeclaration(adapter.declaration());
}
export function defaultWeightsForAdapterSet(

View File

@ -1,112 +1,25 @@
// @flow
import {defaultWeightsForAdapter, defaultWeightsForAdapterSet} from "./weights";
import {
defaultWeightedNodeType,
defaultWeightedEdgeType,
defaultWeightsForAdapter,
defaultWeightsForDeclaration,
combineWeights,
defaultWeightsForAdapterSet,
} from "./weights";
import {
inserterNodeType,
machineNodeType,
assemblesEdgeType,
transportsEdgeType,
} from "../../../plugins/demo/declaration";
} from "../../../analysis/weights";
import {declaration} from "../../../plugins/demo/declaration";
import {
FactorioStaticAdapter,
staticAdapterSet,
} from "../../../plugins/demo/appAdapter";
describe("app/credExplorer/weights/weights", () => {
describe("defaultWeightedNodeType", () => {
it("sets default weight as specified in type", () => {
const wnt = defaultWeightedNodeType(inserterNodeType);
expect(wnt.weight).toEqual(wnt.type.defaultWeight);
});
});
describe("defaultWeightedEdgeType", () => {
it("sets default weights as specified in the type", () => {
const wet = defaultWeightedEdgeType(assemblesEdgeType);
expect(wet.forwardWeight).toEqual(wet.type.defaultForwardWeight);
expect(wet.backwardWeight).toEqual(wet.type.defaultBackwardWeight);
});
});
describe("defaultWeightsForAdapter", () => {
it("works on the demo adapter", () => {
const adapter = new FactorioStaticAdapter();
const expected = {
nodes: new Map(
adapter
.declaration()
.nodeTypes.map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(
adapter
.declaration()
.edgeTypes.map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
const expected = defaultWeightsForDeclaration(declaration);
expect(defaultWeightsForAdapter(adapter)).toEqual(expected);
});
});
describe("combineWeights", () => {
const defaultWeights = () =>
defaultWeightsForAdapter(new FactorioStaticAdapter());
const emptyWeights = () => ({nodes: new Map(), edges: new Map()});
it("successfully combines WeightedTypes", () => {
const weights1 = {
nodes: new Map().set(
inserterNodeType.prefix,
defaultWeightedNodeType(inserterNodeType)
),
edges: new Map().set(
assemblesEdgeType.prefix,
defaultWeightedEdgeType(assemblesEdgeType)
),
};
const weights2 = {
nodes: new Map().set(
machineNodeType.prefix,
defaultWeightedNodeType(machineNodeType)
),
edges: new Map().set(
transportsEdgeType.prefix,
defaultWeightedEdgeType(transportsEdgeType)
),
};
expect(combineWeights([weights1, weights2])).toEqual(defaultWeights());
});
it("treats empty weights as an identity", () => {
expect(
combineWeights([emptyWeights(), defaultWeights(), emptyWeights()])
).toEqual(defaultWeights());
});
it("errors on duplicate edge prefix", () => {
const weights = {
nodes: new Map(),
edges: new Map().set(
assemblesEdgeType.prefix,
defaultWeightedEdgeType(assemblesEdgeType)
),
};
expect(() => combineWeights([weights, weights])).toThrowError(
"duplicate key"
);
});
it("errors on duplicate node prefix", () => {
const weights = {
nodes: new Map().set(
inserterNodeType.prefix,
defaultWeightedNodeType(inserterNodeType)
),
edges: new Map(),
};
expect(() => combineWeights([weights, weights])).toThrowError(
"duplicate key"
);
});
});
describe("defaultWeightsForAdapterSet", () => {
it("works on a demo adapter set", () => {
expect(defaultWeightsForAdapterSet(staticAdapterSet())).toEqual(

View File

@ -1,7 +1,7 @@
// @flow
import type {Edge} from "../../../core/graph";
import type {WeightedTypes} from "./weights";
import type {WeightedTypes} from "../../../analysis/weights";
import type {EdgeEvaluator} from "../../../analysis/pagerank";
import {NodeTrie, EdgeTrie} from "../../../core/trie";