Organize weights by plugin (#773)
This commit adds PluginWeightConfig, which is responsible for adding all the weights for an individual plugin. The top-level WeightConfig now creates multiple PluginWeightConfigs. It also takes responsibility for hiding the FallbackPlugin. Test plan: The PluginWeightConfig is tested (and fairly simple). The top-level WeightConfig is not yet tested (#604), so I manually tested that the weights in the app still function.
This commit is contained in:
parent
846363465d
commit
2779770af5
|
@ -1,6 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
- Organize weight config by plugin (#773)
|
||||
- Configure edge forward/backward weights separately (#749)
|
||||
- Combine "load graph" and "run pagerank" into one button (#759)
|
||||
- Store GitHub data compressed at rest, reducing space usage by 6–8× (#750)
|
||||
|
|
|
@ -9,6 +9,7 @@ import CheckedLocalStore from "../checkedLocalStore";
|
|||
import BrowserLocalStore from "../browserLocalStore";
|
||||
|
||||
import {type EdgeEvaluator} from "../../core/attribution/pagerank";
|
||||
import {defaultStaticAdapters} from "../adapters/defaultPlugins";
|
||||
import {PagerankTable} from "./pagerankTable/Table";
|
||||
import {WeightConfig} from "./WeightConfig";
|
||||
import RepositorySelect from "./RepositorySelect";
|
||||
|
@ -118,6 +119,7 @@ export function createApp(
|
|||
</button>
|
||||
<WeightConfig
|
||||
onChange={(edgeEvaluator) => this.setState({edgeEvaluator})}
|
||||
adapters={defaultStaticAdapters()}
|
||||
/>
|
||||
<LoadingIndicator appState={this.state.appState} />
|
||||
{pagerankTable}
|
||||
|
|
|
@ -1,30 +1,31 @@
|
|||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import sortBy from "lodash.sortby";
|
||||
|
||||
import {type EdgeEvaluator} from "../../core/attribution/pagerank";
|
||||
import {byEdgeType, byNodeType} from "./edgeWeights";
|
||||
import {defaultStaticAdapters} from "../adapters/defaultPlugins";
|
||||
import type {StaticAdapterSet} from "../adapters/adapterSet";
|
||||
import {
|
||||
type WeightedTypes,
|
||||
PluginWeightConfig,
|
||||
} from "./weights/PluginWeightConfig";
|
||||
import {
|
||||
NodeTypeConfig,
|
||||
defaultWeightedNodeType,
|
||||
type WeightedNodeType,
|
||||
defaultWeightedNodeType,
|
||||
} from "./weights/NodeTypeConfig";
|
||||
import {
|
||||
EdgeTypeConfig,
|
||||
defaultWeightedEdgeType,
|
||||
type WeightedEdgeType,
|
||||
defaultWeightedEdgeType,
|
||||
} from "./weights/EdgeTypeConfig";
|
||||
import {styledVariable} from "./weights/EdgeTypeConfig";
|
||||
import {FALLBACK_NAME} from "../adapters/fallbackAdapter";
|
||||
|
||||
type Props = {|
|
||||
+adapters: StaticAdapterSet,
|
||||
+onChange: (EdgeEvaluator) => void,
|
||||
|};
|
||||
|
||||
type State = {
|
||||
edgeWeights: $ReadOnlyArray<WeightedEdgeType>,
|
||||
nodeWeights: $ReadOnlyArray<WeightedNodeType>,
|
||||
pluginNameToWeights: Map<string, WeightedTypes>,
|
||||
expanded: boolean,
|
||||
};
|
||||
|
||||
|
@ -32,12 +33,7 @@ export class WeightConfig extends React.Component<Props, State> {
|
|||
constructor(props: Props): void {
|
||||
super(props);
|
||||
this.state = {
|
||||
edgeWeights: defaultStaticAdapters()
|
||||
.edgeTypes()
|
||||
.map(defaultWeightedEdgeType),
|
||||
nodeWeights: defaultStaticAdapters()
|
||||
.nodeTypes()
|
||||
.map(defaultWeightedNodeType),
|
||||
pluginNameToWeights: new Map(),
|
||||
expanded: false,
|
||||
};
|
||||
}
|
||||
|
@ -65,26 +61,49 @@ export class WeightConfig extends React.Component<Props, State> {
|
|||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<EdgeConfig
|
||||
edgeWeights={this.state.edgeWeights}
|
||||
onChange={(ew) =>
|
||||
this.setState({edgeWeights: ew}, () => this.fire())
|
||||
}
|
||||
/>
|
||||
<NodeConfig
|
||||
nodeWeights={this.state.nodeWeights}
|
||||
onChange={(nw) =>
|
||||
this.setState({nodeWeights: nw}, () => this.fire())
|
||||
}
|
||||
/>
|
||||
{this.pluginWeightConfigs()}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
pluginWeightConfigs() {
|
||||
return this.props.adapters
|
||||
.adapters()
|
||||
.filter((x) => x.name() !== FALLBACK_NAME)
|
||||
.map((adapter) => {
|
||||
const onChange = (weightedTypes) => {
|
||||
this.state.pluginNameToWeights.set(adapter.name(), weightedTypes);
|
||||
this.fire();
|
||||
};
|
||||
return (
|
||||
<PluginWeightConfig
|
||||
key={adapter.name()}
|
||||
adapter={adapter}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fire() {
|
||||
const {edgeWeights, nodeWeights} = this.state;
|
||||
let nodeWeights: WeightedNodeType[] = [];
|
||||
let edgeWeights: WeightedEdgeType[] = [];
|
||||
for (const adapter of this.props.adapters.adapters()) {
|
||||
const weights = this.state.pluginNameToWeights.get(adapter.name());
|
||||
const newNodeWeights =
|
||||
weights == null
|
||||
? adapter.nodeTypes().map(defaultWeightedNodeType)
|
||||
: weights.nodes;
|
||||
const newEdgeWeights =
|
||||
weights == null
|
||||
? adapter.edgeTypes().map(defaultWeightedEdgeType)
|
||||
: weights.edges;
|
||||
nodeWeights = nodeWeights.concat(newNodeWeights);
|
||||
edgeWeights = edgeWeights.concat(newEdgeWeights);
|
||||
}
|
||||
|
||||
const edgePrefixes = edgeWeights.map(
|
||||
({type, forwardWeight, backwardWeight}) => ({
|
||||
prefix: type.prefix,
|
||||
|
@ -100,71 +119,3 @@ export class WeightConfig extends React.Component<Props, State> {
|
|||
this.props.onChange(edgeEvaluator);
|
||||
}
|
||||
}
|
||||
|
||||
class EdgeConfig extends React.Component<{
|
||||
edgeWeights: $ReadOnlyArray<WeightedEdgeType>,
|
||||
onChange: ($ReadOnlyArray<WeightedEdgeType>) => void,
|
||||
}> {
|
||||
_renderWeightControls() {
|
||||
return sortBy(this.props.edgeWeights, ({type}) => type.prefix).map(
|
||||
(weightedEdgeType) => {
|
||||
const onChange = (value) => {
|
||||
const edgeWeights = this.props.edgeWeights.filter(
|
||||
(x) => x.type.prefix !== weightedEdgeType.type.prefix
|
||||
);
|
||||
edgeWeights.push(value);
|
||||
this.props.onChange(edgeWeights);
|
||||
};
|
||||
return (
|
||||
<EdgeTypeConfig
|
||||
key={weightedEdgeType.type.prefix}
|
||||
weightedType={weightedEdgeType}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Edge weights</h2>
|
||||
<p>
|
||||
Flow cred from {styledVariable("β")} to {styledVariable("α")} when:
|
||||
</p>
|
||||
{this._renderWeightControls()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NodeConfig extends React.Component<{
|
||||
nodeWeights: $ReadOnlyArray<WeightedNodeType>,
|
||||
onChange: ($ReadOnlyArray<WeightedNodeType>) => void,
|
||||
}> {
|
||||
_renderControls() {
|
||||
return sortBy(this.props.nodeWeights, ({type}) => type.prefix).map(
|
||||
(weightedType) => {
|
||||
const onChange = (newType) => {
|
||||
const nodeWeights = this.props.nodeWeights.filter(
|
||||
(x) => x.type.prefix !== weightedType.type.prefix
|
||||
);
|
||||
nodeWeights.push(newType);
|
||||
this.props.onChange(nodeWeights);
|
||||
};
|
||||
return (
|
||||
<NodeTypeConfig weightedType={weightedType} onChange={onChange} />
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Node weights</h2>
|
||||
{this._renderControls()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import deepEqual from "lodash.isequal";
|
||||
import {
|
||||
NodeTypeConfig,
|
||||
defaultWeightedNodeType,
|
||||
type WeightedNodeType,
|
||||
} from "./NodeTypeConfig";
|
||||
import {
|
||||
EdgeTypeConfig,
|
||||
defaultWeightedEdgeType,
|
||||
type WeightedEdgeType,
|
||||
} from "./EdgeTypeConfig";
|
||||
import {StaticPluginAdapter} from "../../adapters/pluginAdapter";
|
||||
import {styledVariable} from "./EdgeTypeConfig";
|
||||
|
||||
export type WeightedTypes = {|
|
||||
+nodes: $ReadOnlyArray<WeightedNodeType>,
|
||||
+edges: $ReadOnlyArray<WeightedEdgeType>,
|
||||
|};
|
||||
|
||||
export type Props = {|
|
||||
+adapter: StaticPluginAdapter,
|
||||
+onChange: (WeightedTypes) => void,
|
||||
|};
|
||||
export type State = {|nodes: WeightedNodeType[], edges: WeightedEdgeType[]|};
|
||||
|
||||
export class PluginWeightConfig extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const nodes = this.props.adapter.nodeTypes().map(defaultWeightedNodeType);
|
||||
const edges = this.props.adapter.edgeTypes().map(defaultWeightedEdgeType);
|
||||
this.state = {nodes, edges};
|
||||
}
|
||||
|
||||
fire() {
|
||||
this.props.onChange({nodes: this.state.nodes, edges: this.state.edges});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fire();
|
||||
}
|
||||
|
||||
_renderNodeWeightControls() {
|
||||
return this.state.nodes.map((wnt: WeightedNodeType) => {
|
||||
const onChange = (newType: WeightedNodeType) => {
|
||||
const index = this.state.nodes.findIndex((x) =>
|
||||
deepEqual(x.type, wnt.type)
|
||||
);
|
||||
const newNodes = this.state.nodes.slice();
|
||||
newNodes[index] = newType;
|
||||
this.setState({nodes: newNodes}, () => this.fire());
|
||||
};
|
||||
return (
|
||||
<NodeTypeConfig
|
||||
key={wnt.type.prefix}
|
||||
weightedType={wnt}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_renderEdgeWeightControls() {
|
||||
return this.state.edges.map((wnt: WeightedEdgeType) => {
|
||||
const onChange = (newType: WeightedEdgeType) => {
|
||||
const index = this.state.edges.findIndex((x) =>
|
||||
deepEqual(x.type, wnt.type)
|
||||
);
|
||||
const newEdges = this.state.edges.slice();
|
||||
newEdges[index] = newType;
|
||||
this.setState({edges: newEdges}, () => this.fire());
|
||||
};
|
||||
return (
|
||||
<EdgeTypeConfig
|
||||
key={wnt.type.prefix}
|
||||
weightedType={wnt}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h3>{this.props.adapter.name()}</h3>
|
||||
<h4 style={{marginBottom: "0.3em"}}>Node weights</h4>
|
||||
{this._renderNodeWeightControls()}
|
||||
<h4 style={{marginBottom: "0.3em"}}>Edge weights</h4>
|
||||
<p style={{marginBottom: "0.6em", marginTop: "0.6em"}}>
|
||||
Flow cred from {styledVariable("β")} to {styledVariable("α")} when:
|
||||
</p>
|
||||
{this._renderEdgeWeightControls()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import {shallow} from "enzyme";
|
||||
import {PluginWeightConfig} from "./PluginWeightConfig";
|
||||
import {FactorioStaticAdapter} from "../../adapters/demoAdapters";
|
||||
import {NodeTypeConfig, defaultWeightedNodeType} from "./NodeTypeConfig";
|
||||
import {EdgeTypeConfig, defaultWeightedEdgeType} from "./EdgeTypeConfig";
|
||||
|
||||
require("../../testUtil").configureEnzyme();
|
||||
|
||||
describe("src/app/credExplorer/weights/PluginWeightConfig", () => {
|
||||
describe("PluginWeightConfig", () => {
|
||||
function example() {
|
||||
const onChange = jest.fn();
|
||||
const adapter = new FactorioStaticAdapter();
|
||||
const el = shallow(
|
||||
<PluginWeightConfig adapter={adapter} onChange={onChange} />
|
||||
);
|
||||
return {el, onChange, adapter};
|
||||
}
|
||||
it("fires plugin's default weights on mount", () => {
|
||||
const {onChange, adapter} = example();
|
||||
const expected = {
|
||||
nodes: adapter.nodeTypes().map(defaultWeightedNodeType),
|
||||
edges: adapter.edgeTypes().map(defaultWeightedEdgeType),
|
||||
};
|
||||
expect(onChange).toHaveBeenCalledWith(expected);
|
||||
});
|
||||
it("renders a NodeTypeConfig for each node type", () => {
|
||||
const {el, adapter} = example();
|
||||
const ntc = el.find(NodeTypeConfig);
|
||||
const nodeTypes = adapter.nodeTypes();
|
||||
for (let i = 0; i < nodeTypes.length; i++) {
|
||||
const weightedType = defaultWeightedNodeType(nodeTypes[i]);
|
||||
expect(ntc.at(i).props().weightedType).toEqual(weightedType);
|
||||
}
|
||||
});
|
||||
it("renders a EdgeTypeConfig for each edge type", () => {
|
||||
const {el, adapter} = example();
|
||||
const ntc = el.find(EdgeTypeConfig);
|
||||
const edgeTypes = adapter.edgeTypes();
|
||||
for (let i = 0; i < edgeTypes.length; i++) {
|
||||
const weightedType = defaultWeightedEdgeType(edgeTypes[i]);
|
||||
expect(ntc.at(i).props().weightedType).toEqual(weightedType);
|
||||
}
|
||||
});
|
||||
it("NodeTypeConfig onChange wired properly", () => {
|
||||
const {el, adapter, onChange} = example();
|
||||
const ntc = el.find(NodeTypeConfig).at(0);
|
||||
|
||||
const nodes = adapter.nodeTypes().map(defaultWeightedNodeType);
|
||||
const newWeightedType = {...nodes[0], weight: 707};
|
||||
const newNodes = [newWeightedType, ...nodes.slice(1)];
|
||||
const expected = {
|
||||
nodes: newNodes,
|
||||
edges: adapter.edgeTypes().map(defaultWeightedEdgeType),
|
||||
};
|
||||
ntc.props().onChange(newWeightedType);
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
expect(onChange.mock.calls[1][0]).toEqual(expected);
|
||||
});
|
||||
it("EdgeTypeConfig onChange wired properly", () => {
|
||||
const {el, adapter, onChange} = example();
|
||||
const ntc = el.find(EdgeTypeConfig).at(0);
|
||||
const edges = adapter.edgeTypes().map(defaultWeightedEdgeType);
|
||||
const newWeightedType = {...edges[0], weight: 707};
|
||||
const newEdges = [newWeightedType, ...edges.slice(1)];
|
||||
const expected = {
|
||||
nodes: adapter.nodeTypes().map(defaultWeightedNodeType),
|
||||
edges: newEdges,
|
||||
};
|
||||
ntc.props().onChange(newWeightedType);
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
expect(onChange.mock.calls[1][0]).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue