Persist Cred Explorer weights in LocalStore (#493)

The implementation is similar to the LocalStore usage in
`credExplorer/app.js`. Had to make a spurious refactor from Map to
Object because ES6 maps don't stringify by default, and I didn't feel
like writing a custom JSON serializer.

Test plan:
Didn't add unit tests, although at some point we should come up with a
nice LocalStore mock and test LocalStore code. I did, however, manually
try it out and verify that it works :)

Paired with @wchargin
This commit is contained in:
Dandelion Mané 2018-07-06 13:59:18 -07:00 committed by GitHub
parent 50792a7088
commit 95fca7db17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 75 additions and 59 deletions

View File

@ -11,6 +11,7 @@ import {
import {type EdgeEvaluator} from "../../core/attribution/pagerank";
import {byEdgeType, byNodeType} from "./edgeWeights";
import LocalStore from "./LocalStore";
// Hacks...
import * as GithubNode from "../../plugins/github/nodes";
@ -22,37 +23,45 @@ type Props = {
onChange: (EdgeEvaluator) => void,
};
// The key should be an EdgeAddressT, but a Flow bug prevents this.
type EdgeWeights = {[string]: UserEdgeWeight};
type UserEdgeWeight = {|+logWeight: number, +directionality: number|};
const EDGE_WEIGHTS_KEY = "edgeWeights";
const defaultEdgeWeights = (): EdgeWeights => ({
[(GithubEdge._Prefix.authors: string)]: {logWeight: 0, directionality: 0.5},
[(GithubEdge._Prefix.mergedAs: string)]: {logWeight: 0, directionality: 0.5},
[(GithubEdge._Prefix.references: string)]: {
logWeight: 0,
directionality: 0.5,
},
[(GithubEdge._Prefix.hasParent: string)]: {logWeight: 0, directionality: 0.5},
[(GitEdge._Prefix.hasTree: string)]: {logWeight: 0, directionality: 0.5},
[(GitEdge._Prefix.hasParent: string)]: {logWeight: 0, directionality: 0.5},
[(GitEdge._Prefix.includes: string)]: {logWeight: 0, directionality: 0.5},
[(GitEdge._Prefix.becomes: string)]: {logWeight: 0, directionality: 0.5},
[(GitEdge._Prefix.hasContents: string)]: {logWeight: 0, directionality: 0.5},
});
// The key should be a NodeAddressT, but a Flow bug prevents this.
type NodeWeights = {[string]: UserNodeWeight};
type UserNodeWeight = number /* in log space */;
const defaultEdgeWeights = (): Map<EdgeAddressT, UserEdgeWeight> =>
new Map()
.set(GithubEdge._Prefix.authors, {logWeight: 0, directionality: 0.5})
.set(GithubEdge._Prefix.mergedAs, {logWeight: 0, directionality: 0.5})
.set(GithubEdge._Prefix.references, {logWeight: 0, directionality: 0.5})
.set(GithubEdge._Prefix.hasParent, {logWeight: 0, directionality: 0.5})
.set(GitEdge._Prefix.hasTree, {logWeight: 0, directionality: 0.5})
.set(GitEdge._Prefix.hasParent, {logWeight: 0, directionality: 0.5})
.set(GitEdge._Prefix.includes, {logWeight: 0, directionality: 0.5})
.set(GitEdge._Prefix.becomes, {logWeight: 0, directionality: 0.5})
.set(GitEdge._Prefix.hasContents, {logWeight: 0, directionality: 0.5});
const defaultNodeWeights = (): Map<NodeAddressT, UserNodeWeight> =>
new Map()
.set(GithubNode._Prefix.repo, 0)
.set(GithubNode._Prefix.issue, 0)
.set(GithubNode._Prefix.pull, 0)
.set(GithubNode._Prefix.review, 0)
.set(GithubNode._Prefix.comment, 0)
.set(GithubNode._Prefix.userlike, 0)
.set(GitNode._Prefix.blob, 0)
.set(GitNode._Prefix.commit, 0)
.set(GitNode._Prefix.tree, 0)
.set(GitNode._Prefix.treeEntry, 0);
const NODE_WEIGHTS_KEY = "nodeWeights";
const defaultNodeWeights = (): NodeWeights => ({
[(GithubNode._Prefix.repo: string)]: 0,
[(GithubNode._Prefix.issue: string)]: 0,
[(GithubNode._Prefix.pull: string)]: 0,
[(GithubNode._Prefix.review: string)]: 0,
[(GithubNode._Prefix.comment: string)]: 0,
[(GithubNode._Prefix.userlike: string)]: 0,
[(GitNode._Prefix.blob: string)]: 0,
[(GitNode._Prefix.commit: string)]: 0,
[(GitNode._Prefix.tree: string)]: 0,
[(GitNode._Prefix.treeEntry: string)]: 0,
});
type State = {
edgeWeights: Map<EdgeAddressT, UserEdgeWeight>,
nodeWeights: Map<NodeAddressT, UserNodeWeight>,
edgeWeights: EdgeWeights,
nodeWeights: NodeWeights,
};
export class WeightConfig extends React.Component<Props, State> {
@ -63,6 +72,17 @@ export class WeightConfig extends React.Component<Props, State> {
nodeWeights: defaultNodeWeights(),
};
}
componentDidMount() {
this.setState(
(state) => ({
edgeWeights: LocalStore.get(EDGE_WEIGHTS_KEY, state.edgeWeights),
nodeWeights: LocalStore.get(NODE_WEIGHTS_KEY, state.nodeWeights),
}),
() => this.fire()
);
}
render() {
return (
<div
@ -84,34 +104,32 @@ export class WeightConfig extends React.Component<Props, State> {
);
}
componentDidMount() {
this.fire();
}
fire() {
const {edgeWeights, nodeWeights} = this.state;
const edgePrefixes = Array.from(edgeWeights.entries()).map(
([prefix, {logWeight, directionality}]) => ({
prefix,
weight: 2 ** logWeight,
directionality,
})
);
const nodePrefixes = Array.from(nodeWeights.entries()).map(
([prefix, logWeight]) => ({prefix, weight: 2 ** logWeight})
);
LocalStore.set(EDGE_WEIGHTS_KEY, edgeWeights);
LocalStore.set(NODE_WEIGHTS_KEY, nodeWeights);
const edgePrefixes = Object.keys(edgeWeights).map((key) => {
const {logWeight, directionality} = edgeWeights[key];
const prefix: EdgeAddressT = (key: any);
return {prefix, weight: 2 ** logWeight, directionality};
});
const nodePrefixes = Object.keys(nodeWeights).map((key) => ({
prefix: ((key: any): NodeAddressT),
weight: 2 ** nodeWeights[key],
}));
const edgeEvaluator = byNodeType(byEdgeType(edgePrefixes), nodePrefixes);
this.props.onChange(edgeEvaluator);
}
}
class EdgeConfig extends React.Component<{
edgeWeights: Map<EdgeAddressT, UserEdgeWeight>,
onChange: (Map<EdgeAddressT, UserEdgeWeight>) => void,
edgeWeights: EdgeWeights,
onChange: (EdgeWeights) => void,
}> {
render() {
const controls = [];
for (const [key, currentValue] of this.props.edgeWeights.entries()) {
for (const key of Object.keys(this.props.edgeWeights)) {
const {logWeight} = this.props.edgeWeights[key];
controls.push(
<label style={{display: "block"}} key={key}>
<input
@ -119,20 +137,18 @@ class EdgeConfig extends React.Component<{
min={-10}
max={10}
step={0.1}
value={currentValue.logWeight}
value={logWeight}
onChange={(e) => {
const value: number = e.target.valueAsNumber;
const edgeWeights = new Map(this.props.edgeWeights);
const oldValue = edgeWeights.get(key);
if (oldValue == null) {
throw new Error(key);
}
edgeWeights.set(key, {...oldValue, logWeight: value});
const edgeWeights = {
...this.props.edgeWeights,
[key]: {...this.props.edgeWeights[key], logWeight: value},
};
this.props.onChange(edgeWeights);
}}
/>{" "}
{formatNumber(currentValue.logWeight)}{" "}
{JSON.stringify(EdgeAddress.toParts(key))}
{formatNumber(logWeight)}{" "}
{JSON.stringify(EdgeAddress.toParts((key: any)))}
</label>
);
}
@ -146,12 +162,13 @@ class EdgeConfig extends React.Component<{
}
class NodeConfig extends React.Component<{
nodeWeights: Map<NodeAddressT, UserNodeWeight>,
onChange: (Map<NodeAddressT, UserNodeWeight>) => void,
nodeWeights: NodeWeights,
onChange: (NodeWeights) => void,
}> {
render() {
const controls = [];
for (const [key, currentValue] of this.props.nodeWeights.entries()) {
for (const key of Object.keys(this.props.nodeWeights)) {
const currentValue = this.props.nodeWeights[key];
controls.push(
<label style={{display: "block"}} key={key}>
<input
@ -162,13 +179,12 @@ class NodeConfig extends React.Component<{
value={currentValue}
onChange={(e) => {
const value: number = e.target.valueAsNumber;
const nodeWeights = new Map(this.props.nodeWeights);
nodeWeights.set(key, value);
const nodeWeights = {...this.props.nodeWeights, [key]: value};
this.props.onChange(nodeWeights);
}}
/>{" "}
{formatNumber(currentValue)}{" "}
{JSON.stringify(NodeAddress.toParts(key))}
{JSON.stringify(NodeAddress.toParts((key: any)))}
</label>
);
}