From 975448ec69fb8756dbc2f55f383242d1de5a9340 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 23 May 2018 15:10:44 -0600 Subject: [PATCH] patches to reliability computation based on settings --- .../coral-admin/src/containers/UserDetail.js | 8 +++ client/coral-admin/src/graphql/index.js | 29 +++++++--- graph/resolvers/index.js | 2 + graph/resolvers/karma_threshold.js | 6 +++ graph/resolvers/settings.js | 10 +++- graph/typeDefs.graphql | 26 +++++++++ services/karma.js | 15 +++--- test/server/services/karma.js | 53 +++++++++++++++++++ 8 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 graph/resolvers/karma_threshold.js create mode 100644 test/server/services/karma.js diff --git a/client/coral-admin/src/containers/UserDetail.js b/client/coral-admin/src/containers/UserDetail.js index 4a6a93e84..666e6c8f2 100644 --- a/client/coral-admin/src/containers/UserDetail.js +++ b/client/coral-admin/src/containers/UserDetail.js @@ -228,6 +228,14 @@ export const withUserDetailQuery = withQuery( } ${getSlotFragmentSpreads(slots, 'user')} } + settings { + karma { + comment { + reliable + unreliable + } + } + } me { id } diff --git a/client/coral-admin/src/graphql/index.js b/client/coral-admin/src/graphql/index.js index 2b5b5d8c0..bb4b35b74 100644 --- a/client/coral-admin/src/graphql/index.js +++ b/client/coral-admin/src/graphql/index.js @@ -24,14 +24,23 @@ const userRoleFragment = gql` } `; -const toBoolean = value => { - if (value === 0) { - return null; - } else if (value > 0) { +/** + * calculateReliability will determine the reliability of a karma score based on + * the settings for the karma type. + * + * @param {Number} karma - the current karma value/score for the given user + * @param {Object} thresholds - the karma thresholds to base the karma computation on + */ +const calculateReliability = (karma, { reliable, unreliable }) => { + if (karma >= reliable) { return true; - } else { + } + + if (karma <= unreliable) { return false; } + + return null; }; export default { @@ -312,7 +321,10 @@ export default { user: { reliable: { commenter: { - $set: toBoolean(prev.user.reliable.commenterScore - 1), + $set: calculateReliability( + prev.user.reliable.commenterScore - 1, + prev.settings.karma.comment + ), }, commenterScore: { $apply: count => count - 1, @@ -328,7 +340,10 @@ export default { user: { reliable: { commenter: { - $set: toBoolean(prev.user.reliable.commenterScore + 1), + $set: calculateReliability( + prev.user.reliable.commenterScore + 1, + prev.settings.karma.comment + ), }, commenterScore: { $apply: count => count + 1, diff --git a/graph/resolvers/index.js b/graph/resolvers/index.js index a8765f743..4bc9d9624 100644 --- a/graph/resolvers/index.js +++ b/graph/resolvers/index.js @@ -15,6 +15,7 @@ const DontAgreeActionSummary = require('./dont_agree_action_summary'); const FlagAction = require('./flag_action'); const FlagActionSummary = require('./flag_action_summary'); const GenericUserError = require('./generic_user_error'); +const KarmaThreshold = require('./karma_threshold'); const LocalUserProfile = require('./local_user_profile'); const RootMutation = require('./root_mutation'); const RootQuery = require('./root_query'); @@ -48,6 +49,7 @@ let resolvers = { FlagAction, FlagActionSummary, GenericUserError, + KarmaThreshold, LocalUserProfile, RootMutation, RootQuery, diff --git a/graph/resolvers/karma_threshold.js b/graph/resolvers/karma_threshold.js new file mode 100644 index 000000000..693b1042c --- /dev/null +++ b/graph/resolvers/karma_threshold.js @@ -0,0 +1,6 @@ +const { property } = require('lodash'); + +module.exports = { + reliable: property('RELIABLE'), + unreliable: property('UNRELIABLE'), +}; diff --git a/graph/resolvers/settings.js b/graph/resolvers/settings.js index 6ad1aa455..11e21068b 100644 --- a/graph/resolvers/settings.js +++ b/graph/resolvers/settings.js @@ -1,8 +1,13 @@ const { VIEW_PROTECTED_SETTINGS } = require('../../perms/constants'); - const { decorateWithPermissionCheck } = require('./util'); -const Settings = {}; +const Settings = { + karma: ( + settings, + args, + { connectors: { services: { Karma: { THRESHOLDS } } } } + ) => THRESHOLDS, +}; // PROTECTED_SETTINGS are the settings keys that must be protected for only some // eyes. @@ -11,6 +16,7 @@ const PROTECTED_SETTINGS = { autoCloseStream: [VIEW_PROTECTED_SETTINGS], wordlist: [VIEW_PROTECTED_SETTINGS], domains: [VIEW_PROTECTED_SETTINGS], + karma: [VIEW_PROTECTED_SETTINGS], }; // decorate the fields on the settings resolver with a permission check. diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 1c323e6f4..1f3016a18 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -801,6 +801,29 @@ type Domains { whitelist: [String!]! } +# KarmaThreshold defines the bounds for which a User will become unreliable or +# reliable based on their karma score. If the score is equal or less than the +# unreliable value, they are unreliable. If the score is equal or more than the +# reliable value, they are reliable. If they are neither reliable or unreliable +# then they are neutral. +type KarmaThreshold { + reliable: Int! + unreliable: Int! +} + +# KarmaThresholds contains the currently set thresholds for triggering Trust +# beheviour. +type KarmaThresholds { + + # flag represents karma settings in relation to how well a User's flagging + # ability aligns with the moderation decicions made by moderators. + flag: KarmaThreshold! + + # comment represents the karma setting in relation to how well a User's + # comments are moderated. + comment: KarmaThreshold! +} + # Settings stores the global settings for a given installation. type Settings { @@ -873,6 +896,9 @@ type Settings { # domains will return a given list of domains. domains: Domains + + # karma contains the currently set thresholds for triggering Trust beheviour. + karma: KarmaThresholds } ################################################################################ diff --git a/services/karma.js b/services/karma.js index 72533d328..e255e3b14 100644 --- a/services/karma.js +++ b/services/karma.js @@ -115,18 +115,14 @@ class KarmaService { /** * Inspects the reliability of a property and returns it if known. * @param {String} name - name of the property - * @param {Object} trust - object possibly containing the propertys + * @param {Object} trust - object possibly containing the properties */ static isReliable(name, trust) { - if (trust && trust[name]) { - if (trust[name].karma > THRESHOLDS[name].RELIABLE) { - return true; - } else if (trust[name].karma <= THRESHOLDS[name].UNRELIABLE) { - return false; - } - } else if (THRESHOLDS[name].RELIABLE < 0) { + const karma = get(trust, [name, 'karma'], 0); + + if (karma >= THRESHOLDS[name].RELIABLE) { return true; - } else if (THRESHOLDS[name].UNRELIABLE > 0) { + } else if (karma <= THRESHOLDS[name].UNRELIABLE) { return false; } @@ -171,3 +167,4 @@ class KarmaService { } module.exports = KarmaService; +module.exports.THRESHOLDS = THRESHOLDS; diff --git a/test/server/services/karma.js b/test/server/services/karma.js new file mode 100644 index 000000000..d725d8308 --- /dev/null +++ b/test/server/services/karma.js @@ -0,0 +1,53 @@ +const chai = require('chai'); +const { expect } = chai; +const { merge } = require('lodash'); +const Karma = require('../../../services/karma'); + +const thresholdsBackup = {}; +const thresholdsOverride = { + comment: { + RELIABLE: 1, + UNRELIABLE: -1, + }, + flag: { + RELIABLE: 1, + UNRELIABLE: -1, + }, +}; + +describe('services.Karma', () => { + before(() => { + // Backup the existing thresholds. + merge(thresholdsBackup, Karma.THRESHOLDS); + + // Configure the thresholds to a known value. + merge(Karma.THRESHOLDS, thresholdsOverride); + + expect(Karma.THRESHOLDS).to.deep.equal(thresholdsOverride); + }); + + after(() => { + // Restore the thresholds. + merge(Karma.THRESHOLDS, thresholdsBackup); + + expect(Karma.THRESHOLDS).to.deep.equal(thresholdsBackup); + }); + + describe('#isReliable', () => { + it('neutral', () => { + expect(Karma.isReliable('comment', {})).to.be.null; + expect(Karma.isReliable('comment', { comment: {} })).to.be.null; + expect(Karma.isReliable('comment', { comment: { karma: 0 } })).to.be.null; + }); + it('unreliable', () => { + expect(Karma.isReliable('comment', { comment: { karma: -1 } })).to.be + .false; + expect(Karma.isReliable('comment', { comment: { karma: -2 } })).to.be + .false; + }); + it('reliable', () => { + expect(Karma.isReliable('comment', { comment: { karma: 1 } })).to.be.true; + expect(Karma.isReliable('comment', { comment: { karma: 2 } })).to.be.true; + }); + }); +});