Reviewed-on: #365 Co-authored-by: miteruzo <miteruzo@naver.com> Co-committed-by: miteruzo <miteruzo@naver.com>
このコミットはPull リクエスト #365 でマージされました.
このコミットが含まれているのは:
@@ -0,0 +1,146 @@
|
||||
import { expectedAnswerForQuestion } from '@/lib/gekanator'
|
||||
|
||||
import type {
|
||||
GekanatorAnswerLog,
|
||||
GekanatorAnswerValue,
|
||||
GekanatorQuestion,
|
||||
} from '@/lib/gekanator'
|
||||
import type { Post } from '@/types'
|
||||
|
||||
|
||||
export type RecoveredCandidatePost = {
|
||||
postId: number
|
||||
answerCountAtRecovery: number }
|
||||
|
||||
|
||||
export const candidatePostsFor = ({
|
||||
posts,
|
||||
questions,
|
||||
answers,
|
||||
softenedQuestionIds,
|
||||
rejectedPostIds,
|
||||
recoveredCandidatePosts,
|
||||
}: {
|
||||
posts: Post[]
|
||||
questions: GekanatorQuestion[]
|
||||
answers: GekanatorAnswerLog[]
|
||||
softenedQuestionIds: Set<string>
|
||||
rejectedPostIds: Set<number>
|
||||
recoveredCandidatePosts: Map<number, number>
|
||||
}): Post[] => {
|
||||
const questionById = new Map (questions.map (question => [question.id, question]))
|
||||
|
||||
return posts.filter (post => {
|
||||
if (rejectedPostIds.has (post.id))
|
||||
return false
|
||||
|
||||
const answerCountAtRecovery = recoveredCandidatePosts.get (post.id)
|
||||
|
||||
return answers.every ((answer, index) => {
|
||||
if (answerCountAtRecovery !== undefined && index < answerCountAtRecovery)
|
||||
return true
|
||||
|
||||
if (softenedQuestionIds.has (answer.questionId))
|
||||
return true
|
||||
|
||||
const question = questionById.get (answer.questionId)
|
||||
if (!(question))
|
||||
return true
|
||||
|
||||
switch (answer.answer)
|
||||
{
|
||||
case 'yes':
|
||||
case 'no': {
|
||||
const expected = expectedAnswerForQuestion (question, post)
|
||||
return expected === null || expected === 'unknown' || expected === answer.answer
|
||||
}
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const hardFilteredPostsForAnswer = ({
|
||||
posts,
|
||||
question,
|
||||
answer,
|
||||
}: {
|
||||
posts: Post[]
|
||||
question: GekanatorQuestion
|
||||
answer: GekanatorAnswerValue
|
||||
}): Post[] => {
|
||||
if (answer === 'unknown')
|
||||
return posts
|
||||
|
||||
return posts.filter (post => {
|
||||
const expected = expectedAnswerForQuestion (question, post)
|
||||
return expected === null || expected === 'unknown' || expected === answer
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const concreteAnswerOptions: GekanatorAnswerValue[] = [
|
||||
'yes',
|
||||
'no',
|
||||
'partial',
|
||||
'probably_no']
|
||||
|
||||
|
||||
export const allConcreteAnswerOptionsExhausted = (
|
||||
posts: Post[],
|
||||
question: GekanatorQuestion | null,
|
||||
): boolean => {
|
||||
if (!(question))
|
||||
return false
|
||||
|
||||
return concreteAnswerOptions.every (answer =>
|
||||
hardFilteredPostsForAnswer ({ posts, question, answer }).length === 0)
|
||||
}
|
||||
|
||||
|
||||
const nextRecoveryBatchSize = (recoveryStepCount: number): number =>
|
||||
6 * (2 ** recoveryStepCount)
|
||||
|
||||
|
||||
export const recoverCandidatePosts = ({
|
||||
posts,
|
||||
scores,
|
||||
rejectedPostIds,
|
||||
recoveredCandidatePosts,
|
||||
eligiblePostIds,
|
||||
answerCountAtRecovery,
|
||||
recoveryStepCount,
|
||||
}: {
|
||||
posts: Post[]
|
||||
scores: Map<number, number>
|
||||
rejectedPostIds: Set<number>
|
||||
recoveredCandidatePosts: Map<number, number>
|
||||
eligiblePostIds: Set<number>
|
||||
answerCountAtRecovery: number
|
||||
recoveryStepCount: number
|
||||
}): {
|
||||
recoveredCandidatePosts: Map<number, number>
|
||||
recoveryStepCount: number
|
||||
} | null => {
|
||||
const recovered = new Map (recoveredCandidatePosts)
|
||||
const candidates = posts
|
||||
.filter (post =>
|
||||
!(rejectedPostIds.has (post.id))
|
||||
&& !(eligiblePostIds.has (post.id))
|
||||
&& !(recovered.has (post.id)))
|
||||
.sort ((a, b) =>
|
||||
(scores.get (b.id) ?? Number.NEGATIVE_INFINITY)
|
||||
- (scores.get (a.id) ?? Number.NEGATIVE_INFINITY))
|
||||
.slice (0, nextRecoveryBatchSize (recoveryStepCount))
|
||||
|
||||
if (candidates.length === 0)
|
||||
return null
|
||||
|
||||
candidates.forEach (post => recovered.set (post.id, answerCountAtRecovery))
|
||||
|
||||
return {
|
||||
recoveredCandidatePosts: recovered,
|
||||
recoveryStepCount: recoveryStepCount + 1 }
|
||||
}
|
||||
新しい課題から参照
ユーザをブロックする