Update output format to include credOverTime (#1791)

As requested by @s-ben, we map now include cred over time for all
contributions, not just contributors. Based on discussion with @Beanow,
we made it an optional field so that we can optionally filter to save
space instead.

I was initially concerned that we wouldn't be able to compute
credOverTime for non-user nodes in CredRank, which is why I left it out.
However, informed by discussions with @mZargham, I'm less concerned
because PageRank (and thus CredRank) is a linear operator on the seed
vector. So, if we want to compute the "cred over time" for individual
contributions in CredRank, we can do so by constructing time-specific
seed vectors (which flow only to activity minting cred in the specified
interval), and the sum of contributions time-scoped cred will be equal
to the non-time-scoped cred. It's good that we'll still have the epoch
nodes for users, as that will allow us to model sponsorship cred flow
dynamics.

cc @wchargin for CredRank considerations.

Test plan: Unit tests updated, `yarn test` passes.
This commit is contained in:
Dandelion Mané 2020-05-15 11:12:15 -07:00 committed by GitHub
parent ec56ce79c8
commit 91ca897d99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 2275 additions and 6 deletions

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@ export type CredFlow = {|+forwards: number, +backwards: number|};
export type Output = OutputV1;
export const COMPAT_INFO = {
type: "sourcecred/analysis/output",
version: "0.1.0",
version: "0.2.0",
};
/**
@ -33,6 +33,10 @@ export const COMPAT_INFO = {
export type OutputNode = {|
+address: $ReadOnlyArray<string>,
+cred: number,
// Full cred over time (aligned with output interval boundaries).
// It's optional because it inflates the output size a lot -- we'll
// want to filter out low-cred nodes for large projects
+credOverTime: ?$ReadOnlyArray<number>,
+minted: number,
+timestamp: TimestampMs | null,
// Description comes from the underlying Graph node, so it's determined by the
@ -50,6 +54,8 @@ export type OutputV1 = {|
// Ordered by address
+orderedNodes: $ReadOnlyArray<OutputNode>,
+plugins: $ReadOnlyArray<PluginDeclaration>,
// Interval endpoints, aligned with credOverTime
+intervalEndpoints: $ReadOnlyArray<TimestampMs>,
|};
export function fromTimelineCredAndPlugins(
@ -58,23 +64,26 @@ export function fromTimelineCredAndPlugins(
): Output {
const {graph, weights} = tc.weightedGraph();
const nodeEvaluator = nodeWeightEvaluator(weights);
const intervalEndpoints = tc.intervals().map((x) => x.endTimeMs);
const orderedNodes = Array.from(graph.nodes()).map(
({description, address, timestampMs}) => {
const cred = NullUtil.get(tc.credNode(address)).total;
const {cred, total} = NullUtil.get(tc.credNode(address));
// In TimelineCred, a node with a null timestamp will never mint cred, because we don't
// know what period to mint it in.
// When we transition to CredRank, we should remove this check.
const minted = timestampMs == null ? 0 : nodeEvaluator(address);
return {
address: NodeAddress.toParts(address),
cred,
cred: total,
// todo: add optional filtering to reduce the data size
credOverTime: cred,
minted,
description,
timestamp: timestampMs,
};
}
);
return {orderedNodes, plugins};
return {orderedNodes, plugins, intervalEndpoints};
}
/**
@ -115,7 +124,7 @@ export type OutputV2 = {|
+orderedNodes: $ReadOnlyArray<OutputNode>,
+plugins: $ReadOnlyArray<PluginDeclaration>,
+contributors: $ReadOnlyArray<Contributor>,
+intervalEnd: $ReadOnlyArray<TimestampMs>,
+intervalEndpoints: $ReadOnlyArray<TimestampMs>,
|};
/**

View File

@ -122,5 +122,15 @@ describe("src/analysis/output", () => {
expect(cred).toEqual(credNode.total);
}
});
it("by default, all nodes have cred over time", () => {
const {output, timelineCred} = example();
for (const {address, credOverTime} of output.orderedNodes) {
const credNode = timelineCred.credNode(NodeAddress.fromParts(address));
if (credNode == null) {
throw new Error("Can't find node");
}
expect(credOverTime).toEqual(credNode.cred);
}
});
});
});