feat(布置作业): 能布置「画流程图」「先画再写」,看板分清卡在画图还是写代码
Deploy / deploy (push) Canceled after 0s

布置时多一个「做什么」:写代码 / 先画再写 / 画流程图(只能选打开了画流程图的题)。
- 先画再写:一道题图画到 A/S、代码也交对才算做完;学生画一道写一道、全画完再写都行,
  打开题目自动停在该做的那一步(图没画对停画布,画对了换到布置的语言)
- 也能分开布置:先一节画流程图,看板上「用这几道题写代码」再开一节;学生那边把今天
  几节课的要求合起来,还没画完的照样看得到要画
- 看板格子是一条进度:图 1/6 → 空心「图 ✓」→ 代码 ✗ → ✓,先写代码没画图的写「缺图」;
  卡在画图上的点开讲那张图(要改的框标红 + AI 的哪里错/怎么改),按走对几组归堆
- 写代码的课不再拿画对的流程图顶替做完;数据统计里画流程图的课数流程图提交

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-11-02 21:50:48 +08:00
1 parent 71323d9b63
commit 38a331c2b7
33 files changed
+4987 -209

No files matched your search

+26 -5
View File
@@ -7,6 +7,7 @@
*/
import {
FLOWCHART_PASS_GRADES,
NO_CLASS,
STUDENT_ROLES,
type ProblemLanguage,
@@ -537,6 +538,7 @@ async function lessonPlans(range: StatisticsRange) {
day: schema.classLesson.day,
problemIds: schema.classLesson.problemIds,
language: schema.classLesson.language,
mode: schema.classLesson.mode,
createdAt: schema.classLesson.createdAt,
})
.from(schema.classLesson)
@@ -560,6 +562,7 @@ async function assignedLessons(plans: LessonPlan[], range: StatisticsRange) {
class_name: plan.className,
day: plan.day,
problem_id: problemId,
mode: plan.mode,
})),
),
)
@@ -571,7 +574,7 @@ async function assignedLessons(plans: LessonPlan[], range: StatisticsRange) {
UNJUDGED_RESULTS.map((value) => sql`${value}`),
sql`, `,
)
const plan = sql`(select * from jsonb_to_recordset(${planRows}::jsonb) as p(k int, class_name text, day text, problem_id int))`
const plan = sql`(select * from jsonb_to_recordset(${planRows}::jsonb) as p(k int, class_name text, day text, problem_id int, mode text))`
const [counts, doneByLesson] = await Promise.all([
db.execute<{
k: number
@@ -584,22 +587,37 @@ async function assignedLessons(plans: LessonPlan[], range: StatisticsRange) {
last: string
}>(sql`
with p as ${plan},
-- 这节课要写代码就数代码提交,要画流程图就数流程图(画到 A / S 算对、还在评算没判完),
-- 先画再写两种都数。同一天先布置画流程图、再布置同样的题写代码,两节各数各的
s as (
select p.k, s.user_id, s.problem_id, s.result, s.create_time
select p.k, s.user_id, s.problem_id, s.result in (${accepted}) as ok, s.result in (${judging}) as pending, s.create_time
from ${schema.submission} s
join ${schema.user} u on u.id = s.user_id
join p on p.class_name = u.class_name and p.problem_id = s.problem_id
and p.day = to_char(${localTime(sql`s.create_time`)}, 'YYYY-MM-DD')
where s.contest_id is null
where s.contest_id is null and p.mode <> 'flowchart'
and s.create_time <= ${range.end}
${range.start ? sql`and s.create_time >= ${range.start}` : sql``}
and u.admin_type = 'Regular User' and u.is_disabled = false
union all
select p.k, f.user_id, f.problem_id, (f.status = 2 and coalesce(f.ai_grade in (${sql.join(
FLOWCHART_PASS_GRADES.map((grade) => sql`${grade}`),
sql`, `,
)}), false)) as ok, f.status in (0, 1) as pending, f.create_time
from ${schema.flowchartSubmission} f
join ${schema.user} u on u.id = f.user_id
join p on p.class_name = u.class_name and p.problem_id = f.problem_id
and p.day = to_char(${localTime(sql`f.create_time`)}, 'YYYY-MM-DD')
where p.mode <> 'code'
and f.create_time <= ${range.end}
${range.start ? sql`and f.create_time >= ${range.start}` : sql``}
and u.admin_type = 'Regular User' and u.is_disabled = false
)
select k, problem_id,
count(distinct user_id)::int as users,
count(*)::int as total,
(count(*) filter (where result in (${accepted})))::int as accepted,
(count(*) filter (where result in (${judging})))::int as judging,
(count(*) filter (where ok))::int as accepted,
(count(*) filter (where pending))::int as judging,
min(create_time) as first,
max(create_time) as last
from s
@@ -624,6 +642,7 @@ async function assignedLessons(plans: LessonPlan[], range: StatisticsRange) {
className: lesson.className,
day: lesson.day,
problemIds: lesson.problemIds,
mode: lesson.mode,
start: first,
end: last,
users: total?.users ?? 0,
@@ -868,6 +887,7 @@ submissionStatisticsRoutes.get('/submissions/statistics/lessons', requireTeacher
.map((row) => row!.displayId)
.slice(0, STATISTICS_MAX_PROBLEMS),
assigned: false,
mode: null,
doneCount: null,
fromProblemSet: lesson.from_ps * 2 > lesson.total,
// 次数和正确率只算这节课的题:点进去筛的就是这几道,两边的数才对得上
@@ -901,6 +921,7 @@ submissionStatisticsRoutes.get('/submissions/statistics/lessons', requireTeacher
.map((row) => row!.displayId)
.slice(0, STATISTICS_MAX_PROBLEMS),
assigned: true,
mode: lesson.mode,
doneCount: lesson.doneCount,
fromProblemSet: false,
total: lesson.total,