hide `filterTimelineCred` (#1357)

TimelineCred computation is implemented as follows:
- Compute Distribution
- Filter it down to specified node types
- Wrap the filtered results into a TimelineCred

I want to change how the filtering works. The new filtering logic will
depend on logic we've already implemented in TimelineCred; therefore
filtering should be done on the TimelineCred object and not separately.
Specifically, I want to be able to filter down to the highest-scored
nodes by type (dependent on the type).

As a first step, I've refactored the interface to TimelineCred so that
the filtering is an implementation detail, i.e. the TimelineCred
constructor doesn't expect objects defined in `filterTimelineCred`.

Test plan: `yarn test` passes after a snapshot update.
This commit is contained in:
Dandelion Mané 2019-09-08 00:20:34 +02:00 committed by GitHub
parent 5996dd710a
commit f31a92874b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 33 additions and 63 deletions

View File

@ -1,6 +1,5 @@
// @flow
import {toObject, fromObject} from "../../util/map";
import {type Interval} from "./interval";
import {NodeAddress, type NodeAddressT} from "../../core/graph";
import {type FullTimelineCred} from "./distributionToCred";
@ -47,20 +46,3 @@ export function filterTimelineCred(
}
return {intervals, addressToCred};
}
export type FilteredTimelineCredJSON = {|
+intervals: $ReadOnlyArray<Interval>,
+addressToCred: {[NodeAddressT]: $ReadOnlyArray<number>},
|};
export function filteredTimelineCredToJSON(
x: FilteredTimelineCred
): FilteredTimelineCredJSON {
return {intervals: x.intervals, addressToCred: toObject(x.addressToCred)};
}
export function filteredTimelineCredFromJSON(
x: FilteredTimelineCredJSON
): FilteredTimelineCred {
return {intervals: x.intervals, addressToCred: fromObject(x.addressToCred)};
}

View File

@ -1,11 +1,7 @@
// @flow
import {NodeAddress} from "../../core/graph";
import {
filterTimelineCred,
filteredTimelineCredToJSON,
filteredTimelineCredFromJSON,
} from "./filterTimelineCred";
import {filterTimelineCred} from "./filterTimelineCred";
describe("src/analysis/timeline/filterTimelineCred", () => {
const na = (...parts) => NodeAddress.fromParts(parts);
@ -38,17 +34,4 @@ describe("src/analysis/timeline/filterTimelineCred", () => {
);
});
});
it("JSON serialization", () => {
const i0 = {startTimeMs: 0, endTimeMs: 10};
const i1 = {startTimeMs: 10, endTimeMs: 20};
const intervals = [i0, i1];
const fc = {
intervals,
addressToCred: new Map().set(na("foo"), [1, 4]).set(na("bar"), [2, 5]),
};
const json = filteredTimelineCredToJSON(fc);
const fc_ = filteredTimelineCredFromJSON(json);
expect(fc).toEqual(fc_);
});
});

View File

@ -3,6 +3,7 @@
import {sum} from "d3-array";
import sortBy from "lodash.sortby";
import * as NullUtil from "../../util/null";
import * as MapUtil from "../../util/map";
import {toCompat, fromCompat, type Compatible} from "../../util/compat";
import {type Interval} from "./interval";
import {timelinePagerank} from "./timelinePagerank";
@ -24,9 +25,6 @@ import {type NodeAndEdgeTypes} from "../types";
import {
filterTimelineCred,
type FilteredTimelineCred,
filteredTimelineCredToJSON,
filteredTimelineCredFromJSON,
type FilteredTimelineCredJSON,
} from "./filterTimelineCred";
export type {Interval} from "./interval";
@ -100,18 +98,21 @@ export type TimelineCredConfig = {|
*/
export class TimelineCred {
_graph: Graph;
_cred: FilteredTimelineCred;
_intervals: $ReadOnlyArray<Interval>;
_addressToCred: Map<NodeAddressT, $ReadOnlyArray<number>>;
_params: TimelineCredParameters;
_config: TimelineCredConfig;
constructor(
graph: Graph,
cred: FilteredTimelineCred,
intervals: $ReadOnlyArray<Interval>,
addressToCred: Map<NodeAddressT, $ReadOnlyArray<number>>,
params: TimelineCredParameters,
config: TimelineCredConfig
) {
this._graph = graph;
this._cred = cred;
this._intervals = intervals;
this._addressToCred = addressToCred;
this._params = params;
this._config = config;
}
@ -142,7 +143,7 @@ export class TimelineCred {
* Return all the intervals in the timeline.
*/
intervals(): $ReadOnlyArray<Interval> {
return this._cred.intervals;
return this._intervals;
}
/**
@ -154,7 +155,7 @@ export class TimelineCred {
* filtered results; if so, it will return undefined.
*/
credNode(a: NodeAddressT): ?CredNode {
const cred = this._cred.addressToCred.get(a);
const cred = this._addressToCred.get(a);
if (cred === undefined) {
return undefined;
}
@ -169,7 +170,7 @@ export class TimelineCred {
*/
credSortedNodes(prefix: NodeAddressT): $ReadOnlyArray<CredNode> {
const match = (a) => NodeAddress.hasPrefix(a, prefix);
const addresses = Array.from(this._cred.addressToCred.keys()).filter(match);
const addresses = Array.from(this._addressToCred.keys()).filter(match);
const credNodes = addresses.map((a) => this.credNode(a));
return sortBy(credNodes, (x: CredNode) => -x.total);
}
@ -177,7 +178,8 @@ export class TimelineCred {
toJSON(): TimelineCredJSON {
const rawJSON = {
graphJSON: this._graph.toJSON(),
credJSON: filteredTimelineCredToJSON(this._cred),
intervalsJSON: this._intervals,
credJSON: MapUtil.toObject(this._addressToCred),
paramsJSON: paramsToJSON(this._params),
configJSON: this._config,
};
@ -186,11 +188,11 @@ export class TimelineCred {
static fromJSON(j: TimelineCredJSON): TimelineCred {
const json = fromCompat(COMPAT_INFO, j);
const {graphJSON, credJSON, paramsJSON, configJSON} = json;
const {graphJSON, intervalsJSON, credJSON, paramsJSON, configJSON} = json;
const cred = MapUtil.fromObject(credJSON);
const graph = Graph.fromJSON(graphJSON);
const cred = filteredTimelineCredFromJSON(credJSON);
const params = paramsFromJSON(paramsJSON);
return new TimelineCred(graph, cred, params, configJSON);
return new TimelineCred(graph, intervalsJSON, cred, params, configJSON);
}
static async compute(
@ -199,7 +201,13 @@ export class TimelineCred {
config: TimelineCredConfig
): Promise<TimelineCred> {
const ftc = await _computeTimelineCred(graph, params, config);
return new TimelineCred(graph, ftc, params, config);
return new TimelineCred(
graph,
ftc.intervals,
ftc.addressToCred,
params,
config
);
}
}
@ -235,7 +243,8 @@ export opaque type TimelineCredJSON = Compatible<{|
+graphJSON: GraphJSON,
+paramsJSON: ParamsJSON,
+configJSON: TimelineCredConfig,
+credJSON: FilteredTimelineCredJSON,
+credJSON: {[string]: $ReadOnlyArray<number>},
+intervalsJSON: $ReadOnlyArray<Interval>,
|}>;
type ParamsJSON = {|

View File

@ -5,7 +5,6 @@ import sortBy from "lodash.sortby";
import {utcWeek} from "d3-time";
import {NodeAddress, Graph} from "../../core/graph";
import {TimelineCred, type TimelineCredConfig} from "./timelineCred";
import {type FilteredTimelineCred} from "./filterTimelineCred";
import {defaultWeights} from "../weights";
describe("src/analysis/timeline/timelineCred", () => {
@ -45,12 +44,14 @@ describe("src/analysis/timeline/timelineCred", () => {
const scores = intervals.map((_unuesd, i) => generator(i));
addressToCred.set(address, scores);
}
const filteredTimelineCred: FilteredTimelineCred = {
const params = {alpha: 0.05, intervalDecay: 0.5, weights: defaultWeights()};
return new TimelineCred(
graph,
intervals,
addressToCred,
};
const params = {alpha: 0.05, intervalDecay: 0.5, weights: defaultWeights()};
return new TimelineCred(graph, filteredTimelineCred, params, credConfig());
params,
credConfig()
);
}
it("JSON serialization works", () => {

View File

@ -10,7 +10,6 @@ import {
TimelineCred,
type TimelineCredConfig,
} from "../analysis/timeline/timelineCred";
import {type FilteredTimelineCred} from "../analysis/timeline/filterTimelineCred";
import {defaultWeights} from "../analysis/weights";
export default class TimelineCredViewInspectiontest extends React.Component<{|
@ -51,17 +50,13 @@ export default class TimelineCredViewInspectiontest extends React.Component<{|
const scores = intervals.map((_unuesd, i) => generator(i));
addressToCred.set(address, scores);
}
const filteredTimelineCred: FilteredTimelineCred = {
intervals,
addressToCred,
};
const params = {alpha: 0.05, intervalDecay: 0.5, weights: defaultWeights()};
const config: TimelineCredConfig = {
scoreNodePrefix: NodeAddress.empty,
filterNodePrefixes: [NodeAddress.empty],
types: {nodeTypes: [], edgeTypes: []},
};
return new TimelineCred(graph, filteredTimelineCred, params, config);
return new TimelineCred(graph, intervals, addressToCred, params, config);
}
render() {