Denormalize betAmount and betOutcome fields on comments (#838)

* Create and use `betAmount` and `betOutcome` fields on comments

* Be robust to ridiculous bet IDs on dev
This commit is contained in:
Marshall Polaris 2022-09-04 14:28:45 -07:00 committed by GitHub
parent a15230e7ab
commit 6ef2beed8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 15 deletions

View File

@ -23,10 +23,16 @@ export type Comment<T extends AnyCommentType = AnyCommentType> = {
type OnContract = { type OnContract = {
commentType: 'contract' commentType: 'contract'
contractId: string contractId: string
contractSlug: string
contractQuestion: string
answerOutcome?: string answerOutcome?: string
betId?: string betId?: string
// denormalized from contract
contractSlug: string
contractQuestion: string
// denormalized from bet
betAmount?: number
betOutcome?: string
} }
type OnGroup = { type OnGroup = {

View File

@ -63,11 +63,15 @@ export const onCreateCommentOnContract = functions
.doc(comment.betId) .doc(comment.betId)
.get() .get()
bet = betSnapshot.data() as Bet bet = betSnapshot.data() as Bet
answer = answer =
contract.outcomeType === 'FREE_RESPONSE' && contract.answers contract.outcomeType === 'FREE_RESPONSE' && contract.answers
? contract.answers.find((answer) => answer.id === bet?.outcome) ? contract.answers.find((answer) => answer.id === bet?.outcome)
: undefined : undefined
await change.ref.update({
betOutcome: bet.outcome,
betAmount: bet.amount,
})
} }
const comments = await getValues<ContractComment>( const comments = await getValues<ContractComment>(

View File

@ -0,0 +1,69 @@
// Filling in the bet-based fields on comments.
import * as admin from 'firebase-admin'
import { zip } from 'lodash'
import { initAdmin } from './script-init'
import {
DocumentCorrespondence,
findDiffs,
describeDiff,
applyDiff,
} from './denormalize'
import { log } from '../utils'
import { Transaction } from 'firebase-admin/firestore'
initAdmin()
const firestore = admin.firestore()
async function getBetComments(transaction: Transaction) {
const allComments = await transaction.get(
firestore.collectionGroup('comments')
)
const betComments = allComments.docs.filter((d) => d.get('betId'))
log(`Found ${betComments.length} comments associated with bets.`)
return betComments
}
async function denormalize() {
let hasMore = true
while (hasMore) {
hasMore = await admin.firestore().runTransaction(async (trans) => {
const betComments = await getBetComments(trans)
const bets = await Promise.all(
betComments.map((doc) =>
trans.get(
firestore
.collection('contracts')
.doc(doc.get('contractId'))
.collection('bets')
.doc(doc.get('betId'))
)
)
)
log(`Found ${bets.length} bets associated with comments.`)
const mapping = zip(bets, betComments)
.map(([bet, comment]): DocumentCorrespondence => {
return [bet!, [comment!]] // eslint-disable-line
})
.filter(([bet, _]) => bet.exists) // dev DB has some invalid bet IDs
const amountDiffs = findDiffs(mapping, 'amount', 'betAmount')
const outcomeDiffs = findDiffs(mapping, 'outcome', 'betOutcome')
log(`Found ${amountDiffs.length} comments with mismatched amounts.`)
log(`Found ${outcomeDiffs.length} comments with mismatched outcomes.`)
const diffs = amountDiffs.concat(outcomeDiffs)
diffs.slice(0, 500).forEach((d) => {
log(describeDiff(d))
applyDiff(trans, d)
})
if (diffs.length > 500) {
console.log(`Applying first 500 because of Firestore limit...`)
}
return diffs.length > 500
})
}
}
if (require.main === module) {
denormalize().catch((e) => console.error(e))
}

View File

@ -125,15 +125,12 @@ export function FeedComment(props: {
} = props } = props
const { text, content, userUsername, userName, userAvatarUrl, createdTime } = const { text, content, userUsername, userName, userAvatarUrl, createdTime } =
comment comment
let betOutcome: string | undefined, const betOutcome = comment.betOutcome
bought: string | undefined, let bought: string | undefined
money: string | undefined let money: string | undefined
if (comment.betAmount != null) {
const matchedBet = betsBySameUser.find((bet) => bet.id === comment.betId) bought = comment.betAmount >= 0 ? 'bought' : 'sold'
if (matchedBet) { money = formatMoney(Math.abs(comment.betAmount))
betOutcome = matchedBet.outcome
bought = matchedBet.amount >= 0 ? 'bought' : 'sold'
money = formatMoney(Math.abs(matchedBet.amount))
} }
const [highlighted, setHighlighted] = useState(false) const [highlighted, setHighlighted] = useState(false)
@ -148,7 +145,7 @@ export function FeedComment(props: {
const { userPosition, outcome } = getBettorsLargestPositionBeforeTime( const { userPosition, outcome } = getBettorsLargestPositionBeforeTime(
contract, contract,
comment.createdTime, comment.createdTime,
matchedBet ? [] : betsBySameUser comment.betId ? [] : betsBySameUser
) )
return ( return (
@ -175,7 +172,7 @@ export function FeedComment(props: {
username={userUsername} username={userUsername}
name={userName} name={userName}
/>{' '} />{' '}
{!matchedBet && {!comment.betId != null &&
userPosition > 0 && userPosition > 0 &&
contract.outcomeType !== 'NUMERIC' && ( contract.outcomeType !== 'NUMERIC' && (
<> <>
@ -194,7 +191,6 @@ export function FeedComment(props: {
of{' '} of{' '}
<OutcomeLabel <OutcomeLabel
outcome={betOutcome ? betOutcome : ''} outcome={betOutcome ? betOutcome : ''}
value={(matchedBet as any).value}
contract={contract} contract={contract}
truncate="short" truncate="short"
/> />