Skip to content

About

把课程笔记变成选择题和填空题的期末刷题站 · 纯前端 + 零依赖 Node 后端

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

期末刷题站

把课程笔记粘进来,自动变成选择题和填空题,按笔记顺序刷。给同学之间互相分享题集用的。

在线试用:https://finals-quiz.app.workbuddy.host/?sharecode=LKWARVTOI6

期末刷题站 观猹徽章

纯前端 + 零第三方依赖的 Node 后端,npm start 就能跑,不装数据库。

它做什么

笔记直接粘进去(也支持拖入 .docx / .pdf / .md / .txt,文件在浏览器里解析、不上传),按原文顺序出题:

  • 选择题:笔记里已经有成型的选择题(题干 + A. 选项 + 答案字母),原样收进题库
  • 填空题:句子里的考点被 **加粗** 标出,或结构明显(X 是指 Y、X 包括 Y)时,把考点挖空
  • 题型由分区决定:「一、单选题」那一节里不会冒出多选题,多选题只出现在「二、多选题」到「三、填空题」之间
  • 多选题没写正确项 = 全选:卷子上「下列属于…的是」后面列了 A~E 却没标答案的,按「列出的都对」出题
  • 名词解释:只保留冒号前的名词当题干(药物警戒:… → 题干「药物警戒」),你默写整段解释,按关键内容给分
  • 简答题:题干就是那个标题,答案是下面那串要点,你一条条打字答,答中几条算几条
  • 背诵卡是「标题 + 答案一起背」:名词解释显示术语和解释,简答显示标题和全部要点,简答/多选还能列出所有正确项
  • 挖空只挖短语:答案 ≤16 字、不以虚词开头、不挨着顿号;切不出好空就往前挪, 把前半句末尾的名词短语挖掉(胃酸分泌增多时,____ → ____时,弱酸性药物的吸收增强),题干永远完整
  • 只挖考点本身:空后面的半句会留在题干里,题目始终是完整的一句话(「…包括 A、B,用药期间需监测血压」不会变成只剩「…包括____」)
  • 识别不出结构的句子也不丢,兜底转成填空题

两步导入:先原卷(可选)→ 再笔记

导入页是上下两张步骤卡,顺序是固定的,第一步可以跳过:

第一步 · 导入原卷(可选)  一次选十几个文件 → 照抄入库 = 真题库
        ↓  这一库题决定「选择题从哪来」
第二步 · 导入笔记(必做)  每条笔记先去翻真题库
                          翻到 → 出原卷那道题(原题干 / 原选项 / 原答案)
                          翻不到 → 按笔记挖空出填空题,不给选项

顶部有一条流程条把这件事写在最显眼的位置(1 导入原卷 [可选] → 2 导入笔记 [必做]),每张步骤卡标题上也标着「可选 / 必做」;第一步做完,流程条自己会亮起来,卡片底部那块提示会变成「真题库:N 道原卷题,来自 M 个章节」。

两步填同一个课程名:原卷和笔记按课程名归进同一个题集,第二步才找得到第一步的真题。课程名会从文件名里猜(临床药物治疗学概论 第三章.docx → 课程「临床药物治疗学概论」、章节「第三章」),纯章节名不瞎猜,留给你填。

多文件:两个文件框都是 multiple,一次可以选十几个(一章一个文件很常见)。逐个读、边读边显示「正在读取 3/12…」,一个文件坏了不影响其他文件;读进来的文件列在输入框上方(文件名 + 字数 + 逐个 ✕ 移除 + 合计)。原卷会按各自文件名的章节分别归档,合成的真题库按章节混排;同一道题在几份卷子里重复出现只留一份。

两条道走的是完全不同的解析器:

笔记 原卷
干什么 从叙述里挖空出题,改写、切短语 把卷子上的题原样照抄,题干一个字不改
题干 从长句里截出考点 卷子上怎么写就怎么存
答案 解析器推出来的(可能猜错) 卷子上印的那个:字母写在括号里(( C ))、括号后(( )A)、甚至直接把答案写成词((合理用药))都认
选择题 不产出,一律挖成填空 有,选项就是卷子印的那几个;括号里直接写了词的,自动降成填空
适合 自己整理的笔记 老师发的复习卷、往年真题

原卷模式下,答案的三种写法都认:

药物治疗的核心问题是( C )      ← 字母印在括号里
药物治疗的核心问题是(     )C   ← 字母印在括号后面,可隔着半句话
药物治疗的核心问题是(合理用药)  ← 干脆把答案写成词 → 自动收成填空题

( ) 空着、旁边又没写答案的题会被跳过而不是瞎猜——宁可少一道,不留错题。选项一行写多个、首项漏了 A.、选项跨行接续,这些排版花活都认。多选题里列了 A~E 就算多选,不会因为只有单个答案就被扔掉。

括号里写词时只认含汉字的短括号:(ADR)、(AUC) 这种缩写,(4分)、(1) 这种分值题号,(见第三章)、(包括中药) 这种补充说明,都不会被当成答案抢走。

这里不内置任何专业的原卷题库——用户来自各个专业,预置一份药学的卷子对别人只是噪音。要真题就自己那份文件导进来,解析器只认排版、不认学科。

选择题只认卷子原题,不自己编

这条是硬规矩:选择题的题干和选项只能来自你上传的卷子。系统拼出来的「正确答案 + 从题库里抓几个词当干扰项」那种题,一律不许出。

于是笔记解析变成了两路:

笔记句子 ──► 先在题库里翻原卷真题
               ├─ 翻到了(相似度 ≥ 0.45 或题干被这句话整个包住)
               │    └─► 出卷子那道题:题干、A/B/C/D 选项、标准答案,一个字不改
               │        笔记这句话退到「讲解」位置,答完显示「笔记里怎么说」
               │        题库里已经有这道题 → 只补讲解,不重复出一条
               └─ 没翻到
                    └─► 按笔记挖空出填空(多个答案就按顿号分隔考),不给任何选项

所以先导入原卷、再解析笔记是最顺的顺序(也就是页面上那两步):真题已经在库里,笔记一解析就能对上,题库不会出现两道同考点的题,更不会出现机器编的假选择题。跳过第一步也能用,只是笔记里所有内容都按填空考。

命中判断用二元组相似度(判分那套 dice),再加一条「题干被笔记句包住就算命中」——卷子的题干常常只是笔记句的前半截:

卷子:药物协同作用包括( )
笔记:药物协同作用包括相加作用 增强作用 增敏作用
      → 判为同一题,出卷子那道,笔记那句当讲解

拿一份 302 句的精简笔记实测:224 道原卷题里 131 道(58%)能对上笔记,阈值附近的样本抽查都是对的。剩下对不上的,说明卷子考了笔记没写,正好暴露复习盲区。

笔记里本来就印着 A/B/C/D 的成题(很多资料就是这么发的)不算编造,照旧出选择题——选项是原文写的,系统没动手。

笔记里这样写,效果最好:

所选药物对患者具备**安全、有效、经济、适当**四个要素
→ 所选药物对患者具备____四个要素        (填空,答案是这四个词,顿号分隔填)

**安全**是药物治疗的首要前提
→ ____是药物治疗的首要前提              (加粗顶在句首也认,考的就是加粗的词)

生物半衰期与血药浓度无关,又称之为恒比消除
→ 生物半衰期与血药浓度无关,又称之为____(没有加粗也能出,答案是「恒比消除」)

功能

页面 说明
导入原卷(第一步,可选) 一次选十几个文件或整份粘进来,把试卷(题干 + A/B/C/D 选项 + 答案字母)原样收进题库,不动一个字、不挖空、不改写;解析完直接入库,不占用笔记那步的预览表
导入笔记(第二步,必做) 粘贴笔记或拖文件(可多选),自动出题;卷子考过的出原题,没考过的挖空出填空。保存为本地题集;也可导出/导入题库 JSON、导出 Markdown
补充导入(随时) 题集建好后又拿到新卷子/新笔记,在导入页第三张卡补进去:卷子和笔记可以混着选 —— 认得出带选项的选择题就当试卷照抄,认不出就当笔记挖空;跟题集重复的题标「已有」、默认不勾、勾了也跳过(判重看 id,也看原文句子,换个词挖空也算同一道)
刷题 单选/多选/填空混着来,答完立刻判分并给解析;分组往下推,做对的题按间隔复习排期不再天天重来;出错了点「✎ 改题」教它一次,以后都按你的版本出
背知识点 先看知识卡片(一次一张,考点高亮),看满 20 张再进入自测;自测也有「形式」可选:全部考填空(默认)/ 全部给选项 / 混合,卷子上有选项的题才会给选项。背完一轮的结算页可以直接「继续刷题」,范围跟着刚才背的一致
错题本 只收做错过、还没订正的题:答对一次就移出,再答错又回来;简答「部分正确」不算订正。想看已经订正但还没扎实的,切到「未掌握(含已订正)」
收藏夹 刷题卡右上角「☆ 收藏」,把值得反复记的题单独收起来;可按当前/全部题集看、按收藏时间或掌握度排序、一键「刷收藏的题」。收藏记在题目上(q.fav 时间戳),跟着题集导出、导入、云同步走
我的批注 答完题看到答案时,在反馈卡片里写一句自己的话(易错点、口诀、老师强调的);下次再遇到这道题、选完答案,批注跟正确答案一起显示。批注记在题目上(q.memo),错题本和收藏夹里也能看能改
数据统计 正确率、按科目/题型分布、最近 7 天练习曲线
题集广场 把题集分享给所有人,按「专业 / 科目」打标签,别人免登录直接取来刷
冲刺报告(付费) 按你本机的答题记录算出考前 72 小时该刷什么:薄弱章节 Top 5(按「还能补回多少分」排,不是按章节顺序)、每章必刷的题干、切成三天的量;一次都没碰过的章节单独提醒。需要登录并购买(¥9.90 买断)才能看

其他细节:

  • 账号可选。不登录就是游客,全部功能可用,数据存在本机浏览器;登录后题集和练习记录存服务端,换设备接着刷
  • 出题后自动判分,填空题答完会把答案填回空格并高亮;判定卡片会滚进视口,桌面端也不会错过答案
  • 出题出错了可以直接教它改。刷题卡片和背知识点卡片右上角都有「✎ 改题」→ 说清「这题该怎么考 / 题干(选中的词一键挖成 ____)/ 答案」→ 保存;在哪一页改的都一样,改完刷题和背知识点都按这一版出。改法记在这道题上:当前卡片立刻换版,刷新、换设备同步、导出 JSON 都带着;改坏了点「恢复系统原样」退回解析器原来的出法
  • 选择题的干扰项也能自己改。改单选的「③ 选项」区会把选项一条条铺开,想改哪条直接改文字,能加能删,勾哪个哪个就是答案(单选点新的自动取消旧的,多选可以勾好几个);也可以反过来在答案框里写顿号分隔的答案,选项跟着变。勾了不止一个却设成单选、只勾一个却设成多选,保存会拦下来提示你;只剩一个选项时自动降级成填空,不会留一道没有干扰项的选择题
  • 原卷来的题和笔记来的题分得清。导入预览里每题都标着章节(章节 · 第七章)和选项,两种来源的题可以存进同一个题集,互不干扰
  • 分组往下刷,不从头再来。「再来一组」会接着抽下一批:默认「间隔复习」——答错的下组必来,答对一次隔 1 组、连对 2 次隔 3 组、连对 3 次隔 7 组(已掌握的题更久);抽题方式也能切成「一路推新题」(这场答对的不再出现)或「只刷没掌握的」(掌握度到 90% 才放过)。排期存在本机,关掉页面明天再来也认
  • 刷新不丢进度。答到一半按了 F5、关掉页面明天再开,回来还是那道题:已答的题不重问、不重复计数,写了一半的答案草稿也在。恢复出来的那道题标着「已恢复」,旁边有「重新开始」可以扔掉这次进度重抽。进度只存本机(换设备不串),换题集 / 题目被删会自动作废,不会拼出半截会话
  • 批注只在答完之后出现。写批注的入口就在反馈卡片里(「✎ 写一条」),答完这道题才能看到自己写的东西 —— 答题前露出来等于提前泄题。批注跟着题目走,导出 JSON / 导入 / 云同步都带着;错题本和收藏夹的列表里也能直接改
  • 改完题这道题回到「没答过」。以前改完还停在原先那次「答错了」的回顾里,看不出改动、也没法按新版重答。现在保存改法(或点「恢复系统原样」)之后,卡片立刻回到初见:输入框 / 选项回来、反馈清空,重新答一次按新版判分;结账页只算重答那一次,不会变成两道题。只清「正停着的这道」——从别处改别的题,不会动这场刷题的记录
  • 答错的题可以点「← 上一题」回看答案,回顾只读、不重复计数
  • 深色/浅色主题、手机窄屏都有适配
  • 页面最下面是版本号 + 「★ 给我 Star」按钮,点了直接去 GitHub 仓库
  • 第一个登录的账号自动成为站长,可在统计页看到站点使用情况
  • 每页最下面有一行版本号(如 v1.15.0),和 package.json、GET /api/version 是同一个数,用来确认线上跑的是哪一版

更新日志

从 v1.2.0 到 v1.21.1 的完整变更记在 CHANGELOG.md:24 个版本、按时间倒序、每版写清「新增了什么 / 修好了什么」,末尾列着还没做的边界。

按功能(而不是按版本)整理的总览在 FEATURES.md:19 条、带编号,01-12 与封面图上的十二张卡片一一对应。

跑起来

需要 Node 18+。

npm start              # 默认 3000 端口
PORT=8080 npm start    # 换端口

打开 http://localhost:3000。

没有 Node 也能用:直接双击 index.html,出题、刷题、背知识点、错题本、统计全部照常,只是没有登录和云同步(页面显示「离线模式」)。

登录支持三种方式:观猹 OAuth 一键登录、WorkBuddy OAuth 一键登录(这两个都需要从已注册的线上域名打开,本地 localhost 用不了)、昵称 + 密码(本地可用)。

测试

27 套脚本、1183 项断言,覆盖后端接口、浏览器交互、题库还原、原卷分区、原卷原样录入(含老版 .doc 转 docx)、真题优先(笔记命中原卷)、加粗识别、广场、作答与回看、用户改题(含选项编辑、改完重答)、题目批注、背诵形式、收藏夹、补充导入、分组推进与间隔复习、刷新续答、观猹登录:

node test/api.test.js       # 56 项,起临时端口 + 临时数据目录,跑完整接口链路
node test/browser.test.js   # 63 项
node test/core.test.js      # 92 项,含逐页切换、知识卡片、页脚版本号、Star 链接、观猹徽章、右键菜单
node test/bank.test.js      # 31 项,题库还原、分区题型、挖空粒度
node test/bold.test.js      # 49 项,加粗识别、兜底不丢句、挖空边界
node test/plaza.test.js     # 64 项,广场接口 + 前端
node test/review.test.js    # 51 项,填空判分、答完不自动跳、回看、反馈滚动
node test/paper.test.js     # 38 项,原卷分区题型、多选全选、名词解释、简答要点、手打判分
node test/fix.test.js       # 49 项,改题型/题干/答案、挖空按钮、持久化、恢复原样、降级、背诵卡、背知识点页改完同步到刷题
node test/opts.test.js      # 50 项,选择题选项铺开、勾选即答案、改文字、加删、单选互斥、只剩一个降级、填空收起选项区
node test/paperdoc.test.js  # 126 项,原卷原样录入:分区、答案括号内/后/写成词、选项排版花活、缺答案跳过、两步导入、多份原卷合成、名词解释/简答、不内置某专业题库、选中文件后真正读进列表
node test/preferpaper.test.js # 47 项,真题优先:笔记命中原卷就用原题、没考过挖成填空、不自造干扰项、已有题只补讲解
node test/batch.test.js     # 43 项,再来一组接着往下、答对跳过、间隔复习排期、冷却不卡死
node test/resume.test.js    # 30 项,刷新续答:进度与题目、看答案时刷新、草稿、结账页、换题集作废、重新开始
node test/fav.test.js       # 40 项,收藏:刷题卡收藏/取消、回顾态、收藏夹列表与排序、范围只刷收藏、清空、跟题集走
node test/sup.test.js       # 49 项,补充导入:自动分辨卷子/笔记、已有题去重、失败提示、老版 .doc 转 docx
node test/memo.test.js      # 46 项,批注:答题前不露、答完跟正确答案一起显示、就地增改删、错题本/收藏夹可改、导出导入都带着
node test/refix.test.js     # 32 项,改完题回到初见:清掉本次作答、重新判分、结账不翻倍、不误伤别的题
node test/reciteform.test.js # 49 项,背诵形式:全部考填空/全部给选项/混合、单选多选判定、手打题永远手打
node test/watcha.test.js    # 51 项,观猹登录:授权地址参数、PKCE 校验、state 拦截与重放、账号复用、错误分支
node test/watchaui.test.js   # 19 项,观猹登录前端:入口显隐、发起登录、回调换号、不抢 WorkBuddy 的回调

前端几套用 happy-dom 跑,需先 npm i -D happy-dom(或全局装好再把 NODE_PATH 指过去)。

项目结构

index.html   页面结构
styles.css   样式(深浅色)
app.js       全部前端逻辑:出题、判分、刷题、背诵、统计、云同步、题库还原、原卷照抄
server.js    后端,只用 Node 自带模块(http / fs / crypto)
pay.js       爱猹收(观猹支付)接入层:查权益 / 扣额度,失败一律关闭
watcha-oauth.js  观猹 OAuth2 接入层:授权码 + PKCE、换 token、取用户信息
vendor/      WorkBuddy 登录 SDK(本地托管,不走外网 CDN)
data/        运行时生成的数据(users.json / cloud.json / stats.json / plaza.json),不进版本库
test/        测试脚本

数据结构就是几个 JSON 文件,没有数据库。data/ 在 .gitignore 里,部署后自动生成。

支付配置放在 data/pay.config.json(同样不进版本库),里面是服务端 API Key 和权益 ID; 也可以用环境变量 WATCHA_PAY_API_KEY / WATCHA_PAY_ENTITLEMENT_ID / WATCHA_PAY_BASE_URL 覆盖。 密钥只放服务端,前端只跟后端自己的三个接口对话,永远拿不到 Key。

观猹登录的配置放在 data/watcha-oauth.config.json(同样不进版本库):clientId、redirectUri、 sharecode、scope(默认 read)、baseUrl,公开客户端还可以不填 clientSecret。 也可以用环境变量 WATCHA_OAUTH_CLIENT_ID / WATCHA_OAUTH_CLIENT_SECRET / WATCHA_OAUTH_REDIRECT_URI / WATCHA_OAUTH_SHARECODE / WATCHA_OAUTH_SCOPE / WATCHA_OAUTH_BASE_URL 覆盖。 redirectUri 必须和观猹后台登记的回调地址同一个源;线上有分享码门禁,实际的 redirect_uri 会带上当前页的分享码(前端把当前地址交给服务端,服务端只认同源的地址)。

⚠️ 发版前先挪一下 data/。发版会把整个目录打包上传,data/ 里除了运行数据,还可能有 本地快照和各种令牌。直接发布会用本地 stats.json 覆盖线上的真实访问数据,也可能把不该上服务器的 文件带上去。稳妥做法:发版前把 data/ 挪到目录外,只放一份 pay.config.json 和一份 watcha-oauth.config.json 进去,发完再挪回来。

已知限制

  • 笔记解析是规则驱动,不是模型。写得越结构化,出的题越好;纯口语流水账的笔记出题质量会下降
  • 单次导入的题集上限 400 题
  • 拖文件支持 .docx / .doc / .pdf / .md / .txt / .csv。老版 .doc(Word 97-2003)会在网页里转成 .docx 再解析:按 OLE 复合文档 + piece table 把正文抠出来,包成一个最小 docx 交给同一套解析器,转换结果可在文件列表点「存 .docx」下载。只保正文文字与段落,样式、图片、表格、页眉页脚不带;nFib < 0x00C1 的 Word 6/95 文档走 fcMin..fcMac 的老路,只认单字节字符集
  • 原卷解析同样靠规则。卷子排版越规整(题型大标题 + 标号题干 + 选项带字母 + 答案写在括号里)越准;( ) 里确实没印答案的题会被跳过,需要自己补进题库
  • 笔记和原卷的对应靠字面相似度(不是模型)。换个说法、同义改写就未必对得上——对不上的不会被硬凑成选择题,只会老实出填空题
  • 括号里写的词当答案有个前提:这一行得有题号(1. / 2、),或者它前面没有正在收的题。否则一行没标号的选项(药品(包括中药)说明书)容易被误当成一道填空题
  • 填空题区里,括号空着、答案也没写在旁边的题会被跳过(卷子本身没给答案,抄下来也没法判分)
  • 广场没有点赞/评论,只有分享、取用、删除
  • 登录接了观猹 OAuth、WorkBuddy OAuth 和昵称密码,微信/QQ 需要备案域名与资质,暂未接

About

把课程笔记变成选择题和填空题的期末刷题站 · 纯前端 + 零依赖 Node 后端

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages