62 lines
2.2 KiB
TypeScript
62 lines
2.2 KiB
TypeScript
// xuchao: 数学每周检测——从已点亮数学课的题池 seeded 抽 10 题
|
|
import { and, eq } from "drizzle-orm";
|
|
import { db } from "@/db";
|
|
import { mathPoints, mathProgress, mathWeeklyTest } from "@/db/schema";
|
|
import { extractQuiz, type QuizQ } from "@/lib/quiz-parse";
|
|
import { todayStr, weekKey } from "@/lib/dates";
|
|
|
|
export const currentMathWeekKey = () => weekKey(todayStr());
|
|
|
|
// xuchao: 确定性随机(同 seed 每次得到相同组卷,渲染与判分一致)
|
|
function hashStr(s: string): number {
|
|
let h = 5381;
|
|
for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) >>> 0;
|
|
return h >>> 0;
|
|
}
|
|
function mulberry32(a: number) {
|
|
return () => {
|
|
a |= 0;
|
|
a = (a + 0x6d2b79f5) | 0;
|
|
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
};
|
|
}
|
|
function seededSample<T>(arr: T[], seed: string, n: number): T[] {
|
|
const rnd = mulberry32(hashStr(seed));
|
|
const pool = [...arr];
|
|
for (let i = pool.length - 1; i > 0; i--) {
|
|
const j = Math.floor(rnd() * (i + 1));
|
|
[pool[i], pool[j]] = [pool[j], pool[i]];
|
|
}
|
|
return pool.slice(0, n);
|
|
}
|
|
|
|
// xuchao: 从已点亮数学课组卷;题池不足 5 题时用计算模块兜底
|
|
export async function buildMathWeeklyQuestions(childId: number, wk: string): Promise<QuizQ[]> {
|
|
const lit = await db
|
|
.select({ pointId: mathProgress.pointId })
|
|
.from(mathProgress)
|
|
.where(and(eq(mathProgress.childUserId, childId), eq(mathProgress.passed, true)));
|
|
const litIds = new Set(lit.map((r) => r.pointId));
|
|
|
|
const all = await db.select().from(mathPoints);
|
|
let pool: QuizQ[] = [];
|
|
for (const p of all) {
|
|
if (litIds.has(p.pointId)) pool.push(...extractQuiz(p.md));
|
|
}
|
|
if (pool.length < 5) {
|
|
for (const p of all) if (p.module === "calc") pool.push(...extractQuiz(p.md));
|
|
}
|
|
return seededSample(pool, `${childId}:math:${wk}`, 10);
|
|
}
|
|
|
|
export async function getMathWeeklyRecord(childId: number, wk: string) {
|
|
const [row] = await db
|
|
.select()
|
|
.from(mathWeeklyTest)
|
|
.where(and(eq(mathWeeklyTest.childUserId, childId), eq(mathWeeklyTest.weekKey, wk)))
|
|
.limit(1);
|
|
return row ?? null;
|
|
}
|