diff --git a/worker/confidence.js b/worker/confidence.js deleted file mode 100644 index d56b4b83..00000000 --- a/worker/confidence.js +++ /dev/null @@ -1,25 +0,0 @@ -// https://en.wikipedia.org/wiki/Normal_distribution#Quantile_function -// const z = 1.281551565545 // 80% confidence -// const z = 1.644853626951 // 90% confidence -// const z = 1.959963984540 // 95% confidence -const z = 3.090232306168 // 98% confidence - -function confidence (s, n) { - if (n === 0) { - return 0 - } - - const p = s / n - const left = p + 1 / (2 * n) * z * z - const right = z * Math.sqrt(p * (1 - p) / n + z * z / (4 * n * n)) - const under = 1 + 1 / n * z * z - - return (left - right) / under -} - -console.log(confidence(process.argv[2], process.argv[3])) - -/* - Need to describe how they'll earn - If we trust upvotes how can we use that to determine the best -*/ diff --git a/worker/earn.js b/worker/earn.js index e6b3da17..7ad9db1c 100644 --- a/worker/earn.js +++ b/worker/earn.js @@ -1,5 +1,10 @@ const serialize = require('../api/resolvers/serial') +const ITEM_EACH_REWARD = 3.0 +const UPVOTE_EACH_REWARD = 6.0 +const TOP_ITEMS = 21 +const EARLY_MULTIPLIER_MAX = 100.0 + // TODO: use a weekly trust measure or make trust decay function earn ({ models }) { return async function ({ name }) { @@ -14,18 +19,63 @@ function earn ({ models }) { OR ("ItemAct".act = 'VOTE' AND "Item"."userId" = "ItemAct"."userId")) AND "ItemAct".created_at > now_utc() - INTERVAL '1 day'` - // calculate the total trust - const { sum: { trust } } = await models.user.aggregate({ - sum: { - trust: true - } - }) + /* + How earnings work: + 1/3: top 21 posts over last 36 hours, scored on a relative basis + 1/3: top 21 comments over last 36 hours, scored on a relative basis + 1/3: top upvoters of top posts/comments, scored on: + - their trust + - how much they tipped + - how early they upvoted it + - how the post/comment scored + */ // get earners { id, earnings } const earners = await models.$queryRaw(` - SELECT id, FLOOR(${sum} * (trust/${trust}) * 1000) as earnings - FROM users - WHERE trust > 0`) + WITH item_ratios AS ( + SELECT *, + "weightedVotes"/(sum("weightedVotes") OVER (PARTITION BY "parentId" IS NULL)) AS ratio + FROM ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY "parentId" IS NULL ORDER BY "weightedVotes" desc) AS r + FROM + "Item" + WHERE created_at >= now_utc() - interval '36 hours' + ) x + WHERE x.r <= ${TOP_ITEMS} + ), + upvoters AS ( + SELECT "ItemAct"."userId", item_ratios.id, item_ratios.ratio, item_ratios."parentId", + sum("ItemAct".sats) as tipped, min("ItemAct".created_at) as acted_at + FROM item_ratios + JOIN "ItemAct" on "ItemAct"."itemId" = item_ratios.id + WHERE act IN ('VOTE','TIP') + AND "ItemAct"."userId" <> item_ratios."userId" + GROUP BY "ItemAct"."userId", item_ratios.id, item_ratios.ratio, item_ratios."parentId" + ), + upvoter_ratios AS ( + SELECT "userId", sum(early_multiplier*tipped_ratio*ratio*users.trust) as upvoting_score, + "parentId" IS NULL as "isPost" + FROM ( + SELECT *, + ${EARLY_MULTIPLIER_MAX}/(ROW_NUMBER() OVER (partition by id order by acted_at asc)) AS early_multiplier, + tipped::float/(sum(tipped) OVER (partition by id)) tipped_ratio + FROM upvoters + ) u + JOIN users on "userId" = users.id + GROUP BY "userId", "parentId" IS NULL + ) + SELECT "userId" as id, FLOOR(sum(proportion)*${sum}*1000) as earnings + FROM ( + SELECT "userId", + upvoting_score/(sum(upvoting_score) OVER (PARTITION BY "isPost"))/${UPVOTE_EACH_REWARD} as proportion + FROM upvoter_ratios + UNION ALL + SELECT "userId", ratio/${ITEM_EACH_REWARD} as proportion + FROM item_ratios + ) a + GROUP BY "userId" + HAVING FLOOR(sum(proportion)*${sum}) >= 1`) // for each earner, serialize earnings // we do this for each earner because we don't need to serialize @@ -41,47 +91,4 @@ function earn ({ models }) { } } -// earn historical ... TODO: delete after announcement -function earnHistorical ({ models }) { - return async function ({ name }) { - console.log('running', name) - - // compute how much sn earned today - const [{ sum }] = await models.$queryRaw` - SELECT sum("ItemAct".sats) - FROM "ItemAct" - JOIN "Item" on "ItemAct"."itemId" = "Item".id - WHERE ("ItemAct".act in ('BOOST', 'STREAM') - OR ("ItemAct".act = 'VOTE' AND "Item"."userId" = "ItemAct"."userId"))` - - // add in the job sats that weren't recorded from jobs - const fullSum = 200000 + sum - - // calculate the total trust - const { sum: { trust } } = await models.user.aggregate({ - sum: { - trust: true - } - }) - - // get earners { id, earnings } - const earners = await models.$queryRaw(` - SELECT id, FLOOR(${fullSum} * (trust/${trust}) * 1000) as earnings - FROM users - WHERE trust > 0`) - - // for each earner, serialize earnings - // we do this for each earner because we don't need to serialize - // all earner updates together - earners.forEach(async earner => { - if (earner.earnings > 0) { - await serialize(models, - models.$executeRaw`SELECT earn(${earner.id}, ${earner.earnings})`) - } - }) - - console.log('done', name) - } -} - -module.exports = { earn, earnHistorical } +module.exports = { earn } diff --git a/worker/index.js b/worker/index.js index efd4daef..3399acd5 100644 --- a/worker/index.js +++ b/worker/index.js @@ -6,7 +6,7 @@ const { checkInvoice, checkWithdrawal } = require('./wallet') const { repin } = require('./repin') const { trust } = require('./trust') const { auction } = require('./auction') -const { earn, earnHistorical } = require('./earn') +const { earn } = require('./earn') const { ApolloClient, HttpLink, InMemoryCache } = require('@apollo/client') const { indexItem, indexAllItems } = require('./search') const fetch = require('cross-fetch') @@ -45,7 +45,6 @@ async function work () { await boss.work('indexAllItems', indexAllItems(args)) await boss.work('auction', auction(args)) await boss.work('earn', earn(args)) - await boss.work('earnHistorical', earnHistorical(args)) console.log('working jobs') }