export type VocabularyRiskTestType = 'chinese-to-english' | 'audio-to-english' | 'multiple-choice'; export const DEFAULT_VOCABULARY_FULL_CORRECT_WORD_THRESHOLD = 50; export const VOCABULARY_FULL_CORRECT_RISK_FLAG = 'large_batch_all_correct'; export const INVALIDATING_VOCABULARY_RISK_FLAGS: string[] = [ 'invalid_origin', 'invalid_answer_proof', 'invalid_question_token', 'invalid_option_token', 'invalid_submitted_at', 'submitted_at_out_of_sync', 'attempt_expired', 'too_fast', 'too_slow', 'missing_choice_interaction', 'missing_input_value_trace', 'input_value_mismatch', 'uniform_answer_intervals', 'repeated_whole_second_intervals', VOCABULARY_FULL_CORRECT_RISK_FLAG ]; const parsePositiveThreshold = (value: unknown): number | undefined => { const parsed = Number(value); return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined; }; export const getVocabularyFullCorrectWordThreshold = (): number => { return parsePositiveThreshold(process.env.VOCABULARY_FULL_CORRECT_WORD_THRESHOLD) ?? DEFAULT_VOCABULARY_FULL_CORRECT_WORD_THRESHOLD; }; export const analyzeVocabularyAttemptResultRisk = ({ testType, totalWords, correctWords, threshold = getVocabularyFullCorrectWordThreshold() }: { testType: VocabularyRiskTestType | string; totalWords: number; correctWords: number; threshold?: number; }): string[] => { const effectiveThreshold = parsePositiveThreshold(threshold) ?? DEFAULT_VOCABULARY_FULL_CORRECT_WORD_THRESHOLD; const safeTotalWords = Number.isFinite(totalWords) ? totalWords : 0; const safeCorrectWords = Number.isFinite(correctWords) ? correctWords : 0; if ( testType !== 'multiple-choice' && safeTotalWords > effectiveThreshold && safeTotalWords === safeCorrectWords ) { return [VOCABULARY_FULL_CORRECT_RISK_FLAG]; } return []; };