Canonicalise certain transition pairs, handle arbitrary consecutive transitions

Transition pairs joined,left and left,joined are now transformed into single meta-transitions "joined_and_left" and "left_and_joined" respectively. These are described as "joined and left", "left and rejoined".

Treat consecutive sequences of transitions as repetitions, and handle any arbitrary repetitions of transitions:

...,joined,left,joined,left,joined,left,...

is canonicalised into
...,joined_and_left, joined_and_left, joined_and_left,...

which is truncated and described as
... , joined and left 3 times, ...

This also works if there are multiple consecutive sequences separated by other transitions:

..., banned, banned, banned, joined, unbanned, unbanned, unbanned,...

becomes
... was banned 3 times, joined, was unbanned 3 times ...
pull/21833/head
lukebarnard 2017-01-16 14:49:07 +01:00
parent fb68fff536
commit 82d6805a71
1 changed files with 78 additions and 35 deletions

View File

@ -88,62 +88,105 @@ module.exports = React.createClass({
} }
}, },
_getDescriptionForTransition(t, plural) { _getDescriptionForTransition(t, plural, repeats) {
let beConjugated = plural ? "were" : "was"; let beConjugated = plural ? "were" : "was";
let invitation = plural ? "invitations" : "an invitation"; let invitation = plural ? "invitations" : "an invitation";
switch (t) { let res = null;
case 'joined': return "joined"; let map = {
case 'left': return "left"; "joined": "joined",
case 'invite_reject': return "rejected " + invitation; "left": "left",
case 'invite_withdrawal': return "withdrew " + invitation; "joined_and_left": "joined and left",
case 'invited': return beConjugated + " invited"; "left_and_joined": "left and rejoined",
case 'banned': return beConjugated + " banned"; "invite_reject": "rejected " + invitation,
case 'unbanned': return beConjugated + " unbanned"; "invite_withdrawal": "withdrew " + invitation,
case 'kicked': return beConjugated + " kicked"; "invited": beConjugated + " invited",
"banned": beConjugated + " banned",
"unbanned": beConjugated + " unbanned",
"kicked": beConjugated + " kicked",
};
if (Object.keys(map).includes(t)) {
res = map[t] + (repeats > 1 ? " " + repeats + " times" : "" );
} }
return null; return res;
},
_getCanonicalTransitions: function(transitions) {
let modMap = {
'joined' : {
'after' : 'left',
'newTransition' : 'joined_and_left',
},
'left' : {
'after' : 'joined',
'newTransition' : 'left_and_joined',
},
// $currentTransition : {
// 'after' : $nextTransition,
// 'newTransition' : 'new_transition_type',
// },
};
const res = [];
for (let i = 0; i < transitions.length; i++) {
let t = transitions[i];
let t2 = transitions[i + 1];
let transition = t;
if (i < transitions.length - 1 && modMap[t] && modMap[t].after === t2) {
transition = modMap[t].newTransition;
i++;
}
res.push(transition);
}
return res;
},
_getTruncatedTransitions: function(transitions) {
let res = [];
for (let i = 0; i < transitions.length; i++) {
if (res.length > 0 && res[res.length - 1].transitionType === transitions[i]) {
res[res.length - 1].repeats += 1;
} else {
res.push({
transitionType: transitions[i],
repeats: 1,
});
}
}
// returns [{
// transitionType: "joined_and_left"
// repeats: 123
// }, ... ]
return res;
}, },
_renderSummary: function(eventAggregates) { _renderSummary: function(eventAggregates) {
let summaries = Object.keys(eventAggregates).map((transitions) => { let summaries = Object.keys(eventAggregates).map((transitions) => {
let nameList = this._renderNameList(eventAggregates[transitions]); let nameList = this._renderNameList(eventAggregates[transitions]);
let plural = eventAggregates[transitions].length > 1;
let repeats = 1; let repeats = 1;
let repeatExtra = 0; let repeatExtra = 0;
let splitTransitions = transitions.split(','); let splitTransitions = transitions.split(',');
let describedTransitions = splitTransitions;
let plural = eventAggregates[transitions].length > 1;
for (let modulus = 1; modulus <= 2; modulus++) { // Some pairs of transitions are common and are repeated a lot, so canonicalise them into "pair" transitions
// Sequences that are repeating through modulus transitions will be truncated let canonicalTransitions = this._getCanonicalTransitions(splitTransitions);
if (this._isRepeatedSequence(describedTransitions, modulus)) { // Remove consecutive repetitions of the same transition (like 5 consecutive 'join_and_leave's)
// Extra repeating sequence on the end that should be treated separately let truncatedTransitions = this._getTruncatedTransitions(canonicalTransitions);
// so as to avoid j,l,j,l,j => "... joined and left 2.5 times"
repeatExtra = describedTransitions.length % modulus;
repeats = (describedTransitions.length - repeatExtra) / modulus; let descs = truncatedTransitions.map((t) => {
describedTransitions = describedTransitions.slice(0, modulus); return this._getDescriptionForTransition(t.transitionType, plural, t.repeats);
break;
}
}
let numberOfTimes = repeats > 1 ? " " + repeats + " times" : "";
let descs = describedTransitions.map((t) => {
return this._getDescriptionForTransition(t, plural);
});
let afterRepeatDescs = splitTransitions.slice(splitTransitions.length - repeatExtra).map((t) => {
return this._getDescriptionForTransition(t, plural);
}); });
let desc = this._renderCommaSeparatedList(descs); let desc = this._renderCommaSeparatedList(descs);
let afterRepeatDesc = this._renderCommaSeparatedList(afterRepeatDescs);
return nameList + " " + desc + numberOfTimes + (afterRepeatDesc ? " and then " + afterRepeatDesc : ""); return nameList + " " + desc;
}); });
if (!summaries) { if (!summaries) {