このコミットが含まれているのは:
+74
-23
@@ -1,5 +1,4 @@
|
||||
import { apiPost } from '@/lib/api'
|
||||
import { fetchPosts } from '@/lib/posts'
|
||||
import { apiGet, apiPost } from '@/lib/api'
|
||||
|
||||
import type { Post } from '@/types'
|
||||
|
||||
@@ -13,6 +12,7 @@ export type GekanatorAnswerValue =
|
||||
export type GekanatorAnswerLog = {
|
||||
questionId: string
|
||||
questionText: string
|
||||
questionCondition?: GekanatorQuestionCondition
|
||||
answer: GekanatorAnswerValue
|
||||
originalAnswer: GekanatorAnswerValue }
|
||||
|
||||
@@ -22,10 +22,26 @@ export type GekanatorQuestionKind =
|
||||
| 'title'
|
||||
| 'original_date'
|
||||
|
||||
export type GekanatorQuestionCondition =
|
||||
| { type: 'tag'; key: string }
|
||||
| { type: 'source'; host: string }
|
||||
| { type: 'original-year'; year: number }
|
||||
| { type: 'original-month'; month: number }
|
||||
| { type: 'original-month-day'; monthDay: string }
|
||||
| { type: 'title-length-greater-than'; length: number }
|
||||
| { type: 'title-has-ascii' }
|
||||
|
||||
export type StoredGekanatorQuestion = {
|
||||
id: string
|
||||
text: string
|
||||
kind: GekanatorQuestionKind
|
||||
condition: GekanatorQuestionCondition }
|
||||
|
||||
export type GekanatorQuestion = {
|
||||
id: string
|
||||
text: string
|
||||
kind: GekanatorQuestionKind
|
||||
condition: GekanatorQuestionCondition
|
||||
test: (post: Post) => boolean }
|
||||
|
||||
const countBy = <T extends string | number> (values: T[]): Map<T, number> => {
|
||||
@@ -144,27 +160,49 @@ const questionableTag = (post: Post, key: string): boolean => {
|
||||
}
|
||||
|
||||
|
||||
const questionMatches = (
|
||||
post: Post,
|
||||
condition: GekanatorQuestionCondition,
|
||||
): boolean => {
|
||||
switch (condition.type)
|
||||
{
|
||||
case 'tag':
|
||||
return questionableTag (post, condition.key)
|
||||
case 'source':
|
||||
return hostOf (post) === condition.host
|
||||
case 'original-year':
|
||||
return originalYearOf (post) === condition.year
|
||||
case 'original-month':
|
||||
return originalMonthOf (post) === condition.month
|
||||
case 'original-month-day':
|
||||
return originalMonthDayOf (post) === condition.monthDay
|
||||
case 'title-length-greater-than':
|
||||
return (post.title?.length ?? 0) > condition.length
|
||||
case 'title-has-ascii':
|
||||
return /[A-Za-z0-9]/.test (post.title ?? '')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const restoreGekanatorQuestion = (
|
||||
question: StoredGekanatorQuestion,
|
||||
): GekanatorQuestion => ({
|
||||
...question,
|
||||
test: (post: Post) => questionMatches (post, question.condition) })
|
||||
|
||||
|
||||
export const storeGekanatorQuestion = (
|
||||
question: GekanatorQuestion,
|
||||
): StoredGekanatorQuestion => ({
|
||||
id: question.id,
|
||||
text: question.text,
|
||||
kind: question.kind,
|
||||
condition: question.condition })
|
||||
|
||||
|
||||
export const fetchGekanatorPosts = async (): Promise<Post[]> => {
|
||||
const limit = 200
|
||||
const first = await fetchPosts ({
|
||||
url: '', title: '', tags: '', match: 'all',
|
||||
originalCreatedFrom: '', originalCreatedTo: '',
|
||||
createdFrom: '', createdTo: '', updatedFrom: '', updatedTo: '',
|
||||
page: 1, limit, order: 'original_created_at:desc' })
|
||||
const posts = [...first.posts]
|
||||
const totalPages = Math.ceil (first.count / limit)
|
||||
|
||||
for (let page = 2; page <= totalPages; page++)
|
||||
{
|
||||
const data = await fetchPosts ({
|
||||
url: '', title: '', tags: '', match: 'all',
|
||||
originalCreatedFrom: '', originalCreatedTo: '',
|
||||
createdFrom: '', createdTo: '', updatedFrom: '', updatedTo: '',
|
||||
page, limit, order: 'original_created_at:desc' })
|
||||
posts.push (...data.posts)
|
||||
}
|
||||
|
||||
return posts
|
||||
const data = await apiGet<{ posts: Post[] }> ('/gekanator/posts')
|
||||
return data.posts
|
||||
}
|
||||
|
||||
|
||||
@@ -209,6 +247,7 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
|
||||
id: `tag:${ key }`,
|
||||
text: tagQuestionText (category, label),
|
||||
kind: 'tag' as const,
|
||||
condition: { type: 'tag' as const, key: String (key) },
|
||||
test: (post: Post) => questionableTag (post, String (key)) }
|
||||
})
|
||||
|
||||
@@ -219,6 +258,7 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
|
||||
id: `source:${ host }`,
|
||||
text: `${ host } の投稿を思ひ浮かべてゐる?`,
|
||||
kind: 'source' as const,
|
||||
condition: { type: 'source' as const, host },
|
||||
test: (post: Post) => hostOf (post) === host }))
|
||||
|
||||
const originalYearQuestions = usefulEntries (originalYears)
|
||||
@@ -228,6 +268,7 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
|
||||
id: `original-year:${ year }`,
|
||||
text: `オリジナルの投稿年は ${ year } 年?`,
|
||||
kind: 'original_date' as const,
|
||||
condition: { type: 'original-year' as const, year },
|
||||
test: (post: Post) => originalYearOf (post) === year }))
|
||||
|
||||
const originalMonthQuestions = usefulEntries (originalMonths)
|
||||
@@ -237,6 +278,7 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
|
||||
id: `original-month:${ month }`,
|
||||
text: `オリジナルの投稿月は ${ month } 月?`,
|
||||
kind: 'original_date' as const,
|
||||
condition: { type: 'original-month' as const, month },
|
||||
test: (post: Post) => originalMonthOf (post) === month }))
|
||||
|
||||
const originalMonthDayQuestions = usefulEntries (originalMonthDays)
|
||||
@@ -249,6 +291,7 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
|
||||
id: `original-month-day:${ monthDay }`,
|
||||
text: `オリジナルの投稿日は ${ month } 月 ${ day } 日?`,
|
||||
kind: 'original_date' as const,
|
||||
condition: { type: 'original-month-day' as const, monthDay: String (monthDay) },
|
||||
test: (post: Post) => originalMonthDayOf (post) === monthDay }
|
||||
})
|
||||
|
||||
@@ -257,11 +300,15 @@ export const buildGekanatorQuestions = (posts: Post[]): GekanatorQuestion[] => {
|
||||
id: 'title:long',
|
||||
text: '題名が長めの投稿?',
|
||||
kind: 'title' as const,
|
||||
condition: {
|
||||
type: 'title-length-greater-than' as const,
|
||||
length: titleLengthMedian },
|
||||
test: (post: Post) => (post.title?.length ?? 0) > titleLengthMedian },
|
||||
{
|
||||
id: 'title:ascii',
|
||||
text: '題名に英数字が混じってゐる?',
|
||||
kind: 'title' as const,
|
||||
condition: { type: 'title-has-ascii' as const },
|
||||
test: (post: Post) => /[A-Za-z0-9]/.test (post.title ?? '') }]
|
||||
.filter (question => {
|
||||
const yes = posts.filter (post => question.test (post)).length
|
||||
@@ -294,6 +341,7 @@ export const saveGekanatorGame = async ({
|
||||
answers: answers.map (answer => ({
|
||||
question_id: answer.questionId,
|
||||
question_text: answer.questionText,
|
||||
question_condition: answer.questionCondition ?? null,
|
||||
answer: answer.answer,
|
||||
original_answer: answer.originalAnswer })) })
|
||||
|
||||
@@ -301,10 +349,13 @@ export const saveGekanatorGame = async ({
|
||||
export const saveGekanatorQuestionSuggestion = async ({
|
||||
gekanatorGameId,
|
||||
questionText,
|
||||
answer,
|
||||
}: {
|
||||
gekanatorGameId: number
|
||||
questionText: string
|
||||
answer: GekanatorAnswerValue
|
||||
}): Promise<{ id: number }> =>
|
||||
await apiPost ('/gekanator/question_suggestions', {
|
||||
gekanator_game_id: gekanatorGameId,
|
||||
question_text: questionText })
|
||||
question_text: questionText,
|
||||
answer })
|
||||
|
||||
新しい課題から参照
ユーザをブロックする