题库系统设计

如果是教育领域,题库应该是很常见的系统。按用途,可以分为在线考试系统和组卷系统。后者主要是k12的老师在用,因为涉及公式系统,排版系统,比前者要复杂的多。网上涉及的题库系统主要是第一种,是一种在线考试系统,其难度比k12老师专用的组卷系统容易太多。

本篇文章,主要是讲述的是k12的题库设计,最终的目标是组卷,并提供最终的试卷打印系统。

这个系统从大的方向来分,主要分为录入-存储-组卷三大块,其中每大块又分为各个子模块。

  • 录入

    • 标准化录入
      • 模板录入(word,excel等)录入
      • 在线格式化录入
    • 非标准化录入
      • 第三方word录入
        • 题目格式解析
        • 公式识别
        • 图片处理
      • 扫描数据录入
        • OCR识别
        • 试题结构解析
  • 存储

    • 标准化格式处理
    • 标签系统
    • 检索系统
  • 组卷

    • 输出Word
    • 输出pdf
    • 输出Tex

试题录入

试题的录入,就是生成结构化试题的过程,大概得流程如下:

录入流程

试题的录入包含了模板录入和第三方来源录入。模板录入相对简单多了,因为本身结构就固定了,在录入公式和图片的时候,公式可以直接采用Tex,图片可以是先就上传到服务器。这样可以一步就生成结构化数据,进入下一个环节。

但对于收集来的非标准的word,就不那么好处理了。对于不同的文档内容,通常是需要程序员写不同的正则表达式来解析,但这种方案始终需要通过代码来解决,无法将工作交给运营甚至老师来处理。为了彻底让开发者和使用者分离,我们还专门做了一个规则库。在这个规则库选择各种规则,拼凑成一个解析模板来解析整个word,就如同我们所熟知的,活字印刷术。

为此,我们提供了一个规则管理界面,运营或老师可以选择不同的匹配规则,这些规则组成的规则模板就组成了我们后端常见的lexerparser, 经过解析后就可以转换成一个标准的json对象。

存储

如果试题只是单纯的文本,那存储就非常简单了。但对于导入非标word,格式就五花八门了。比如下面的这个化学试题:

这里面有文本,也有图片,还有公式。它这里的公式用的mathtype 6.0. mathtype是一个第三方的数学公式插件,它能在Office文档中启用编辑,并生成一个带有公式矢量图的ole对象插入到文档中。注意的是,mathtype是收费软件,需要用特定的软件展示,比如我们经常在网上看到这样的内容。

可以看到mathtype根本就没解析,

除了mathtype,Office也自带了一个公式编辑器Office MathML(OMML), 但在介绍OMML之前,先介绍下 MathMl.

MathML全称为数学标记语言(Mathematical Markup Language),是一种基于XML的标准,用来在互联网上书写数学符号和公式的置标语言。MathML可以直接从HTML5使用MathML标签。 当您希望在网页中显示更多简单的数学符号时,它非常有用,并且由于其简单性和与HTML的相似性 ,因此非常易于使用。 比如勾股定理的mathml表示如下:

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 2.0//EN""http://www.w3.org/TR/MathML2/dtd/mathml2.dtd">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<msup>
    <mi>a</mi>
    <mn>2</mn>
</msup>
<mo>+</mo>
<msup>
    <mi>b</mi>
    <mn>2</mn>
</msup>
<mo>=</mo>
<msup>
    <mi>c</mi>
    <mn>2</mn>
</msup>
</math>

office用的是Office Open Xml, 它不是标准的xml,所以实现起来有些差异。一个n的额平分的OMML如下:

<m:oMathPara><!-- mathematical block container used as a paragraph -->
  <m:oMath><!-- mathematical inline formula -->
    <m:f><!-- a fraction -->
      <m:num><m:r><m:t>π</m:t></m:r></m:num>
      <m:den><m:r><m:t>2</m:t></m:r></m:den>
    </m:f>
  </m:oMath>
</m:oMathPara>

以上几种公式格式或多或少都有缺陷,比如mathtype不开源,无法在网页解析,mathml和omml无法在pdf使用,而且在涉及文本和公式混排的时候不能很好处理,为此我们需要一种通用的,能嵌入文本的格式,为此,我们最终选择了LaTeX,

LaTeX

  • 将数学公式写在 $ $ 之间,代表的是插入行内数学公式(通常称为行内模式)。
  • 将数学公式写在 $$ $$ 之间,会使公式独立成一行并强制居中(通常称为独立模式)。
  • 字体颜色:{\color{色调}表达式}
  • 背景颜色:{\color{文字色调}\colorbox{背景色调}{表达式(可以打中文)}}
  • latex跟word一样插入的图片是本地图片,如果要引入网络图片,需要\write18命令,如\write18{wget http://www.abc.com/path/to/image.png}

我们在项目中使用到的三者之间转换关系是:OMML -> MathML -> LaTex. 这些都有开源的包来转换。

但要注意的是,mathtype是收费软件,它虽然提供了c#的mathtype-sdk来解析ole文件,但是得掏钱。而且本人也不是c#技术栈的。

好在mathtype的官方文档里有介绍OLE的编码规则How MTEF is stored in files and objects.在这篇文档里MTEF 4 MTEF 5的规范都有。

那接下来,就是个痛苦又繁琐的工作了,根据文档里的锚文本的标记,在embeddings里找到对应的oleObject1.bin文件,解析它并将其转成LaTex格式。

题库包含了七种基本题型:单选题、多选题、不定项选择题、填空题、判断题、问答题、理解题七种基本题型。题目还分为单题和组题,组题就是一个题干包含了多个问题。我们将每道题转换成了一个json对象,这个对象里凡是涉及富文本的,都是LaTeX格式。此外方便搜索和组合,我们还要对此打上不同的标签,比如学科,年级,教材版本,XX中高考原题,竞赛题,难度等不同的标签。

组卷

组件系统就是在题库里搜出合适的题目,组合成一张试卷,供下载,打印。

我们的试题的富文本内容本身就存的LaTeX格式,我们只需要安装某个宏包的格式组装好就好了,比如LaTeX exam,网上也有很多布局精美的宏。当然,你也可以自己定义一个。然后将组合后的tex文件转换成pdf即可。我们可以部署TeXLive来将LaTeX的源文件tex转换成pdf,也可以利用Pandoc将pdf转成word等文件,这两个都是跨平台的开源软件。 当然,如果我们要生成html,注意 Pandoc生成html的时候需要引入mathjax.