基于Express实现Web端树莓派云课堂习题上传功能

序言

一个在线教育平台,习题上传功能是不可缺少的。这里基于Nodejs的Express框架,接入MongoDB数据库,实现习题的创建,作答和统计,并使用Echart展示数据。

前期设计

流程设计

一个简单的习题功能,包括了教师的习题创建、学生的习题作答和教师的习题统计数据查看的三部分功能。

页面设计

教师课程页面的习题创建和查看入口

教师创建新习题页面,包括选择题和填空题

教师习题数据统计页面,使用柱状图进行数据的展示,并且可以查看具体的学生名单

学生的习题作答界面

前端脚本

习题上传脚本

主要需要动态修改题目数量、填入题目内容与答案、将填入好的习题数据发送至服务端的功能

$(document).ready(() => {
    // 添加选择题
    $("#addChoice").click(function() { // 点击增加选择题
        $("#choice").append('\
        // 选择题的具体HTML代码
        ');

		// jQ动态创建的DOM元素,需要通过父元素指定来绑定事件
        $("#choice").on("click", ".delete", function() { 
            $(this).parent().remove();
        });

    });
});

$(document).ready(() => {
    $("#addCompletion").click(function() { // 点击增加填空题
        $("#completion").append('\
       // 填空题的具体HTML码
        ')
    })

	// 同样绑定移除填空题的事件
    $("#completion").on("click", ".delete", function() {
        $(this).parent().remove();
    });
})

$(document).ready(() => {
    var choice = [];
    var completion = [];
    $("#submit").click(() => {
        $("#choice >li").each(function() {
            // 利用jQ选择题,获得填入的选择题信息
        });

        $("#completion >li").each(function() {
            // 利用jQ选择题,获得填入的填空题信息
        });
        
        // 将习题数据Post发出
        $.post("process_upload", {test: JSON.stringify(test)}, (data) => {
            // ...
        }, "json");
    })
})

习题作答脚本

只需要利用jQ Ajax功能获得题目并展示,以及将作答数据Post回服务端

$(document).ready(() => {
    $.get("/tests?id=" + id, (data) => { // 获得JSON格式的试题数据
        data.choice.forEach((item, index, array) => {
            $("#choice").append('\
           		// 选择题的具体HTML代码
            ')
        });
        data.completion.forEach((item, index, array) => {
            $("#completion").append('\
                // 填空题的具体HTML代码
            ')
        });
    }, "json");
});

$(document).ready(() => {
    $("#submit").click(() => {
        // 利用jQ获得作答的数据,并且Post给服务端
    });
});

习题数据查看脚本

$(document).ready(() => {
    $.get("/tests?id=" + id, (data) => {
        // 同样获得习题数据并展示
    }, "json");
});

// 绘图
function plant(choiceArr, completionArr) {
    choiceArr.forEach((element, index, array) => {
        // 对每道选择题数据进行绘图
    });

    completionArr.forEach((element, index, array) => {
         // 对每道填空题数据进行绘图
    });
}

后端处理

习题数据

每一份习题在数据库中存储的形式:

_idclassnamenamechoicecompletion
习题的唯一id(自动生成)习题发布的班级习题名所有选择题(数列)所有填空题(数列)

每一道选择题的存储形式:

stem A B C D answer answers
题干 A选项内容 B选项内容 C选项内容 D选项内容 标准答案 选择四个选项的学生名单

每一道填空题的存储形式:

stem answer answers
题干标准答案 回答正确学生名单、回答错误学生名单以及错误答案

路由配置

添加了一个tests.js路由文件,用来处理与试题相关的请求。
由于试题数据需要登录才能查看,并且需要分身份发送不同的内容,
几乎所有的响应都包含对是否已在session中登录和对身份是否符合的判断(在以下代码示例中略),
如果不符合,跳转至登录界面或者403界面。

// 直接返回试题原数据
router.get('/', function(req, res) {
    db.find("test", {"_id": ObjectId(req.query.id)}, (data) => {
        // 调用自己编写的调用数据库的函数,获得试题信息并返回
        // 如果身份是学生,只截取
    });
})

// 试题查看页面
router.get('/paper', function(req, res) {
    db.find("test", {"_id": ObjectId(req.query.id)}, (data) => {
        // 判断习题是否存在
        // 习题若存在,如果session的identity是教师,发送教师的页面;如果是学生发送学生的页面
    });
})

// 试题页面
router.get('/add', function(req, res) {
    // 如果session的identity是教师,发送创建试题页面
})

// 上传新试题
router.post('/process_upload', function(req, res) {
   	// 将获得的新习题数据存入数据库
})

// 提交完成的试题
router.post('/process_submit', function(req, res) {
    // 从数据库中取出相应的习题数据,将学生的作答插入数据中,再更新数据库
})

// 读取习题列表
router.get('/list', function(req, res) {
    // 获得所有习题的列表
})

结语

这样便完成了一个基础的习题上传功能,在接下来还需要做功能的添加和优化,例如

  • 增加题型
  • 设置试题允许作答的起止时间、得分分布等
  • 增加教师对学生作答内容的反馈功能
  • 增加教师对习题的删除、锁定、修改等等动能
  • 增加学生对习题的结果回查、排名查看
  • 在从数据库取原作答情况,和更新新作答情况之间给数据库加Pessimistic Lock,以免学生同时提交作答导致数据丢失
  • 模块化从试题数据库取试题所有数据、仅取题目、仅取学生作答情况和得分、仅取某一个学生的所有作答等功能
  • 以及其他功能和优化