mirror of
https://github.com/status-im/sourcecred.git
synced 2025-01-14 22:54:46 +00:00
Create a weights
module with types and utils (#787)
This commit creates a central `weights` module that defines all of the weight-related types, and provides some utilities for dealing with them. This way users of weight-concepts do not need to depend on a lot of random modules just to get the relevant types. The utility methods are implicitly defined a few places in the codebase: now we can avoid re-writing them, and test them more thoroughly. Test plan: Unit tests pass.
This commit is contained in:
parent
d77c76082d
commit
b40c1d078d
@ -1,22 +1,18 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import * as NullUtil from "../../util/null";
|
||||||
|
|
||||||
import {type EdgeEvaluator} from "../../core/attribution/pagerank";
|
import {type EdgeEvaluator} from "../../core/attribution/pagerank";
|
||||||
import {weightsToEdgeEvaluator} from "./weights/weightsToEdgeEvaluator";
|
import {weightsToEdgeEvaluator} from "./weights/weightsToEdgeEvaluator";
|
||||||
|
import type {StaticPluginAdapter} from "../adapters/pluginAdapter";
|
||||||
import type {StaticAdapterSet} from "../adapters/adapterSet";
|
import type {StaticAdapterSet} from "../adapters/adapterSet";
|
||||||
import {
|
import {
|
||||||
type WeightedTypes,
|
type WeightedTypes,
|
||||||
PluginWeightConfig,
|
defaultWeightsForAdapter,
|
||||||
} from "./weights/PluginWeightConfig";
|
combineWeights,
|
||||||
import {
|
} from "./weights/weights";
|
||||||
type WeightedNodeType,
|
import {PluginWeightConfig} from "./weights/PluginWeightConfig";
|
||||||
defaultWeightedNodeType,
|
|
||||||
} from "./weights/NodeTypeConfig";
|
|
||||||
import {
|
|
||||||
type WeightedEdgeType,
|
|
||||||
defaultWeightedEdgeType,
|
|
||||||
} from "./weights/EdgeTypeConfig";
|
|
||||||
import {FALLBACK_NAME} from "../adapters/fallbackAdapter";
|
import {FALLBACK_NAME} from "../adapters/fallbackAdapter";
|
||||||
|
|
||||||
type Props = {|
|
type Props = {|
|
||||||
@ -88,23 +84,16 @@ export class WeightConfig extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fire() {
|
fire() {
|
||||||
let nodeWeights: WeightedNodeType[] = [];
|
const weights = combineWeights(
|
||||||
let edgeWeights: WeightedEdgeType[] = [];
|
this.props.adapters
|
||||||
for (const adapter of this.props.adapters.adapters()) {
|
.adapters()
|
||||||
const weights = this.state.pluginNameToWeights.get(adapter.name());
|
.map((adapter: StaticPluginAdapter) =>
|
||||||
const newNodeWeights =
|
NullUtil.orElse(
|
||||||
weights == null
|
this.state.pluginNameToWeights.get(adapter.name()),
|
||||||
? adapter.nodeTypes().map(defaultWeightedNodeType)
|
defaultWeightsForAdapter(adapter)
|
||||||
: weights.nodes;
|
)
|
||||||
const newEdgeWeights =
|
)
|
||||||
weights == null
|
);
|
||||||
? adapter.edgeTypes().map(defaultWeightedEdgeType)
|
|
||||||
: weights.edges;
|
|
||||||
nodeWeights = nodeWeights.concat(newNodeWeights);
|
|
||||||
edgeWeights = edgeWeights.concat(newEdgeWeights);
|
|
||||||
}
|
|
||||||
|
|
||||||
const weights = {nodes: nodeWeights, edges: edgeWeights};
|
|
||||||
const edgeEvaluator = weightsToEdgeEvaluator(weights);
|
const edgeEvaluator = weightsToEdgeEvaluator(weights);
|
||||||
this.props.onChange(edgeEvaluator);
|
this.props.onChange(edgeEvaluator);
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,8 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {WeightSlider, type Props as WeightSliderProps} from "./WeightSlider";
|
import {WeightSlider, type Props as WeightSliderProps} from "./WeightSlider";
|
||||||
import {type EdgeType} from "../../adapters/pluginAdapter";
|
|
||||||
|
|
||||||
export type WeightedEdgeType = {|
|
import type {WeightedEdgeType} from "./weights";
|
||||||
+type: EdgeType,
|
|
||||||
+forwardWeight: number,
|
|
||||||
+backwardWeight: number,
|
|
||||||
|};
|
|
||||||
|
|
||||||
export function defaultWeightedEdgeType(type: EdgeType): WeightedEdgeType {
|
|
||||||
return {
|
|
||||||
type,
|
|
||||||
forwardWeight: type.defaultForwardWeight,
|
|
||||||
backwardWeight: type.defaultBackwardWeight,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EdgeTypeConfig extends React.Component<{
|
export class EdgeTypeConfig extends React.Component<{
|
||||||
+weightedType: WeightedEdgeType,
|
+weightedType: WeightedEdgeType,
|
||||||
|
@ -4,23 +4,12 @@ import React from "react";
|
|||||||
import {shallow} from "enzyme";
|
import {shallow} from "enzyme";
|
||||||
|
|
||||||
import {WeightSlider} from "./WeightSlider";
|
import {WeightSlider} from "./WeightSlider";
|
||||||
import {
|
import {EdgeTypeConfig, EdgeWeightSlider} from "./EdgeTypeConfig";
|
||||||
defaultWeightedEdgeType,
|
|
||||||
EdgeTypeConfig,
|
|
||||||
EdgeWeightSlider,
|
|
||||||
} from "./EdgeTypeConfig";
|
|
||||||
import {assemblesEdgeType} from "../../adapters/demoAdapters";
|
import {assemblesEdgeType} from "../../adapters/demoAdapters";
|
||||||
|
|
||||||
require("../../testUtil").configureEnzyme();
|
require("../../testUtil").configureEnzyme();
|
||||||
|
|
||||||
describe("app/credExplorer/weights/EdgeTypeConfig", () => {
|
describe("app/credExplorer/weights/EdgeTypeConfig", () => {
|
||||||
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("EdgeTypeConfig", () => {
|
describe("EdgeTypeConfig", () => {
|
||||||
function example() {
|
function example() {
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
|
@ -2,16 +2,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {WeightSlider} from "./WeightSlider";
|
import {WeightSlider} from "./WeightSlider";
|
||||||
import {type NodeType} from "../../adapters/pluginAdapter";
|
import type {WeightedNodeType} from "./weights";
|
||||||
|
|
||||||
export type WeightedNodeType = {|+type: NodeType, +weight: number|};
|
|
||||||
|
|
||||||
export function defaultWeightedNodeType(type: NodeType): WeightedNodeType {
|
|
||||||
return {
|
|
||||||
type,
|
|
||||||
weight: type.defaultWeight,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NodeTypeConfig extends React.Component<{
|
export class NodeTypeConfig extends React.Component<{
|
||||||
+weightedType: WeightedNodeType,
|
+weightedType: WeightedNodeType,
|
||||||
|
@ -4,18 +4,12 @@ import React from "react";
|
|||||||
import {shallow} from "enzyme";
|
import {shallow} from "enzyme";
|
||||||
|
|
||||||
import {WeightSlider} from "./WeightSlider";
|
import {WeightSlider} from "./WeightSlider";
|
||||||
import {defaultWeightedNodeType, NodeTypeConfig} from "./NodeTypeConfig";
|
import {NodeTypeConfig} from "./NodeTypeConfig";
|
||||||
import {inserterNodeType} from "../../adapters/demoAdapters";
|
import {inserterNodeType} from "../../adapters/demoAdapters";
|
||||||
|
|
||||||
require("../../testUtil").configureEnzyme();
|
require("../../testUtil").configureEnzyme();
|
||||||
|
|
||||||
describe("app/credExplorer/weights/NodeTypeConfig", () => {
|
describe("app/credExplorer/weights/NodeTypeConfig", () => {
|
||||||
describe("defaultWeightedNodeType", () => {
|
|
||||||
it("sets default weight as specified in type", () => {
|
|
||||||
const wnt = defaultWeightedNodeType(inserterNodeType);
|
|
||||||
expect(wnt.weight).toEqual(wnt.type.defaultWeight);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe("NodeTypeConfig", () => {
|
describe("NodeTypeConfig", () => {
|
||||||
function example() {
|
function example() {
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
|
@ -2,23 +2,17 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import deepEqual from "lodash.isequal";
|
import deepEqual from "lodash.isequal";
|
||||||
import {
|
import {NodeTypeConfig} from "./NodeTypeConfig";
|
||||||
NodeTypeConfig,
|
import {EdgeTypeConfig} from "./EdgeTypeConfig";
|
||||||
defaultWeightedNodeType,
|
|
||||||
type WeightedNodeType,
|
|
||||||
} from "./NodeTypeConfig";
|
|
||||||
import {
|
|
||||||
EdgeTypeConfig,
|
|
||||||
defaultWeightedEdgeType,
|
|
||||||
type WeightedEdgeType,
|
|
||||||
} from "./EdgeTypeConfig";
|
|
||||||
import {StaticPluginAdapter} from "../../adapters/pluginAdapter";
|
import {StaticPluginAdapter} from "../../adapters/pluginAdapter";
|
||||||
import {styledVariable} from "./EdgeTypeConfig";
|
import {styledVariable} from "./EdgeTypeConfig";
|
||||||
|
import {
|
||||||
export type WeightedTypes = {|
|
type WeightedTypes,
|
||||||
+nodes: $ReadOnlyArray<WeightedNodeType>,
|
type WeightedEdgeType,
|
||||||
+edges: $ReadOnlyArray<WeightedEdgeType>,
|
type WeightedNodeType,
|
||||||
|};
|
defaultWeightedNodeType,
|
||||||
|
defaultWeightedEdgeType,
|
||||||
|
} from "./weights";
|
||||||
|
|
||||||
export type Props = {|
|
export type Props = {|
|
||||||
+adapter: StaticPluginAdapter,
|
+adapter: StaticPluginAdapter,
|
||||||
|
@ -4,8 +4,13 @@ import React from "react";
|
|||||||
import {shallow} from "enzyme";
|
import {shallow} from "enzyme";
|
||||||
import {PluginWeightConfig} from "./PluginWeightConfig";
|
import {PluginWeightConfig} from "./PluginWeightConfig";
|
||||||
import {FactorioStaticAdapter} from "../../adapters/demoAdapters";
|
import {FactorioStaticAdapter} from "../../adapters/demoAdapters";
|
||||||
import {NodeTypeConfig, defaultWeightedNodeType} from "./NodeTypeConfig";
|
import {NodeTypeConfig} from "./NodeTypeConfig";
|
||||||
import {EdgeTypeConfig, defaultWeightedEdgeType} from "./EdgeTypeConfig";
|
import {EdgeTypeConfig} from "./EdgeTypeConfig";
|
||||||
|
import {
|
||||||
|
defaultWeightsForAdapter,
|
||||||
|
defaultWeightedNodeType,
|
||||||
|
defaultWeightedEdgeType,
|
||||||
|
} from "./weights";
|
||||||
|
|
||||||
require("../../testUtil").configureEnzyme();
|
require("../../testUtil").configureEnzyme();
|
||||||
|
|
||||||
@ -21,10 +26,7 @@ describe("src/app/credExplorer/weights/PluginWeightConfig", () => {
|
|||||||
}
|
}
|
||||||
it("fires plugin's default weights on mount", () => {
|
it("fires plugin's default weights on mount", () => {
|
||||||
const {onChange, adapter} = example();
|
const {onChange, adapter} = example();
|
||||||
const expected = {
|
const expected = defaultWeightsForAdapter(adapter);
|
||||||
nodes: adapter.nodeTypes().map(defaultWeightedNodeType),
|
|
||||||
edges: adapter.edgeTypes().map(defaultWeightedEdgeType),
|
|
||||||
};
|
|
||||||
expect(onChange).toHaveBeenCalledWith(expected);
|
expect(onChange).toHaveBeenCalledWith(expected);
|
||||||
});
|
});
|
||||||
it("renders a NodeTypeConfig for each node type", () => {
|
it("renders a NodeTypeConfig for each node type", () => {
|
||||||
|
74
src/app/credExplorer/weights/weights.js
Normal file
74
src/app/credExplorer/weights/weights.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import {NodeAddress, EdgeAddress} from "../../../core/graph";
|
||||||
|
import type {NodeType, EdgeType} from "../../adapters/pluginAdapter";
|
||||||
|
import type {StaticPluginAdapter} from "../../adapters/pluginAdapter";
|
||||||
|
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 = {|
|
||||||
|
+nodes: $ReadOnlyArray<WeightedNodeType>,
|
||||||
|
+edges: $ReadOnlyArray<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: StaticPluginAdapter
|
||||||
|
): WeightedTypes {
|
||||||
|
return {
|
||||||
|
nodes: adapter.nodeTypes().map(defaultWeightedNodeType),
|
||||||
|
edges: adapter.edgeTypes().map(defaultWeightedEdgeType),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function combineWeights(
|
||||||
|
ws: $ReadOnlyArray<WeightedTypes>
|
||||||
|
): WeightedTypes {
|
||||||
|
const seenPrefixes = new Set();
|
||||||
|
const nodes = [].concat(...ws.map((x) => x.nodes));
|
||||||
|
for (const {
|
||||||
|
type: {prefix},
|
||||||
|
} of nodes) {
|
||||||
|
if (seenPrefixes.has(prefix)) {
|
||||||
|
throw new Error(`Duplicate prefix: ${NodeAddress.toString(prefix)}`);
|
||||||
|
}
|
||||||
|
seenPrefixes.add(prefix);
|
||||||
|
}
|
||||||
|
const edges = [].concat(...ws.map((x) => x.edges));
|
||||||
|
for (const {
|
||||||
|
type: {prefix},
|
||||||
|
} of edges) {
|
||||||
|
if (seenPrefixes.has(prefix)) {
|
||||||
|
throw new Error(`Duplicate prefix: ${EdgeAddress.toString(prefix)}`);
|
||||||
|
}
|
||||||
|
seenPrefixes.add(prefix);
|
||||||
|
}
|
||||||
|
return {nodes, edges};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultWeightsForAdapterSet(
|
||||||
|
adapters: StaticAdapterSet
|
||||||
|
): WeightedTypes {
|
||||||
|
return combineWeights(adapters.adapters().map(defaultWeightsForAdapter));
|
||||||
|
}
|
93
src/app/credExplorer/weights/weights.test.js
Normal file
93
src/app/credExplorer/weights/weights.test.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import {
|
||||||
|
defaultWeightedNodeType,
|
||||||
|
defaultWeightedEdgeType,
|
||||||
|
defaultWeightsForAdapter,
|
||||||
|
combineWeights,
|
||||||
|
defaultWeightsForAdapterSet,
|
||||||
|
} from "./weights";
|
||||||
|
import {
|
||||||
|
inserterNodeType,
|
||||||
|
machineNodeType,
|
||||||
|
assemblesEdgeType,
|
||||||
|
transportsEdgeType,
|
||||||
|
FactorioStaticAdapter,
|
||||||
|
staticAdapterSet,
|
||||||
|
} from "../../adapters/demoAdapters";
|
||||||
|
|
||||||
|
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: adapter.nodeTypes().map(defaultWeightedNodeType),
|
||||||
|
edges: adapter.edgeTypes().map(defaultWeightedEdgeType),
|
||||||
|
};
|
||||||
|
expect(defaultWeightsForAdapter(adapter)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("combineWeights", () => {
|
||||||
|
const defaultWeights = () =>
|
||||||
|
defaultWeightsForAdapter(new FactorioStaticAdapter());
|
||||||
|
const emptyWeights = () => ({nodes: [], edges: []});
|
||||||
|
it("successfully combines WeightedTypes", () => {
|
||||||
|
const weights1 = {
|
||||||
|
nodes: [defaultWeightedNodeType(inserterNodeType)],
|
||||||
|
edges: [defaultWeightedEdgeType(assemblesEdgeType)],
|
||||||
|
};
|
||||||
|
const weights2 = {
|
||||||
|
nodes: [defaultWeightedNodeType(machineNodeType)],
|
||||||
|
edges: [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: [],
|
||||||
|
edges: [defaultWeightedEdgeType(assemblesEdgeType)],
|
||||||
|
};
|
||||||
|
expect(() => combineWeights([weights, weights])).toThrowError(
|
||||||
|
"Duplicate prefix"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("errors on duplicate node prefix", () => {
|
||||||
|
const weights = {
|
||||||
|
nodes: [defaultWeightedNodeType(inserterNodeType)],
|
||||||
|
edges: [],
|
||||||
|
};
|
||||||
|
expect(() => combineWeights([weights, weights])).toThrowError(
|
||||||
|
"Duplicate prefix"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("defaultWeightsForAdapterSet", () => {
|
||||||
|
it("works on a demo adapter set", () => {
|
||||||
|
expect(defaultWeightsForAdapterSet(staticAdapterSet())).toEqual(
|
||||||
|
combineWeights(
|
||||||
|
staticAdapterSet()
|
||||||
|
.adapters()
|
||||||
|
.map(defaultWeightsForAdapter)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import type {Edge} from "../../../core/graph";
|
import type {Edge} from "../../../core/graph";
|
||||||
import type {WeightedTypes} from "./PluginWeightConfig";
|
import type {WeightedTypes} from "./weights";
|
||||||
import type {EdgeEvaluator} from "../../../core/attribution/pagerank";
|
import type {EdgeEvaluator} from "../../../core/attribution/pagerank";
|
||||||
import {NodeTrie, EdgeTrie} from "../../../core/trie";
|
import {NodeTrie, EdgeTrie} from "../../../core/trie";
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user