06b7e49e98
* Store view counts & last viewed time * Schedule updating user recommendations. Compute using tf-idf. * Update contract's lastBetTime and lastCommentTime on new bets and comments. * Remove contract's lastUpdatedTime * Remove folds activity feed * Implement getFeed cloud function * Hook up client to use getFeed * Script to cache viewCounts and lastViewTime * Batched wait all userRecommendations * Cache view script runs on all users * Update user feed each hour and get feed from cache doc. * Delete view cache script * Update feed script * Tweak feed algorithm * Compute recommendation scores from updateUserFeed * Disable lastViewedScore factor * Update lastCommentTime script * Comment out console.log * Fix timeout issue by calling new cloud functions with part of the work. * Listen for contract updates to feed. * Handle new user: use default feed of top markets this week * Track lastUpdatedTime * Tweak logic of calling cloud functions in batches * Tweak cloud function batching
98 lines
2.9 KiB
TypeScript
98 lines
2.9 KiB
TypeScript
import _ from 'lodash'
|
|
import { Contract } from '../../lib/firebase/contracts'
|
|
import { Comment } from '../../lib/firebase/comments'
|
|
import { Bet } from '../../../common/bet'
|
|
|
|
const MAX_ACTIVE_CONTRACTS = 75
|
|
|
|
// This does NOT include comment times, since those aren't part of the contract atm.
|
|
// TODO: Maybe store last activity time directly in the contract?
|
|
// Pros: simplifies this code; cons: harder to tweak "activity" definition later
|
|
function lastActivityTime(contract: Contract) {
|
|
return Math.max(contract.resolutionTime || 0, contract.createdTime)
|
|
}
|
|
|
|
// Types of activity to surface:
|
|
// - Comment on a market
|
|
// - New market created
|
|
// - Market resolved
|
|
// - Bet on market
|
|
export function findActiveContracts(
|
|
allContracts: Contract[],
|
|
recentComments: Comment[],
|
|
recentBets: Bet[],
|
|
seenContracts: { [contractId: string]: number }
|
|
) {
|
|
const idToActivityTime = new Map<string, number>()
|
|
function record(contractId: string, time: number) {
|
|
// Only record if the time is newer
|
|
const oldTime = idToActivityTime.get(contractId)
|
|
idToActivityTime.set(contractId, Math.max(oldTime ?? 0, time))
|
|
}
|
|
|
|
const contractsById = new Map(allContracts.map((c) => [c.id, c]))
|
|
|
|
// Record contract activity.
|
|
for (const contract of allContracts) {
|
|
record(contract.id, lastActivityTime(contract))
|
|
}
|
|
|
|
// Add every contract that had a recent comment, too
|
|
for (const comment of recentComments) {
|
|
const contract = contractsById.get(comment.contractId)
|
|
if (contract) record(contract.id, comment.createdTime)
|
|
}
|
|
|
|
// Add contracts by last bet time.
|
|
const contractBets = _.groupBy(recentBets, (bet) => bet.contractId)
|
|
const contractMostRecentBet = _.mapValues(
|
|
contractBets,
|
|
(bets) => _.maxBy(bets, (bet) => bet.createdTime) as Bet
|
|
)
|
|
for (const bet of Object.values(contractMostRecentBet)) {
|
|
const contract = contractsById.get(bet.contractId)
|
|
if (contract) record(contract.id, bet.createdTime)
|
|
}
|
|
|
|
let activeContracts = allContracts.filter(
|
|
(contract) =>
|
|
contract.visibility === 'public' &&
|
|
!contract.isResolved &&
|
|
(contract.closeTime ?? Infinity) > Date.now()
|
|
)
|
|
activeContracts = _.sortBy(
|
|
activeContracts,
|
|
(c) => -(idToActivityTime.get(c.id) ?? 0)
|
|
)
|
|
|
|
const contractComments = _.groupBy(
|
|
recentComments,
|
|
(comment) => comment.contractId
|
|
)
|
|
const contractMostRecentComment = _.mapValues(
|
|
contractComments,
|
|
(comments) => _.maxBy(comments, (c) => c.createdTime) as Comment
|
|
)
|
|
|
|
const prioritizedContracts = _.sortBy(activeContracts, (c) => {
|
|
const seenTime = seenContracts[c.id]
|
|
if (!seenTime) {
|
|
return 0
|
|
}
|
|
|
|
const lastCommentTime = contractMostRecentComment[c.id]?.createdTime
|
|
if (lastCommentTime && lastCommentTime > seenTime) {
|
|
return 1
|
|
}
|
|
|
|
const lastBetTime = contractMostRecentBet[c.id]?.createdTime
|
|
if (lastBetTime && lastBetTime > seenTime) {
|
|
return 2
|
|
}
|
|
|
|
return seenTime
|
|
})
|
|
|
|
return prioritizedContracts.slice(0, MAX_ACTIVE_CONTRACTS)
|
|
}
|