将word格式的题库转为txt格式,导入至sqlite3中,使用Express.js做服务端提供json格式数据,使用React做前端获取服务端数据。本文为第一部分,实现导入数据和服务端提供数据API接口。
数据导入
源格式
源题库为word格式,题型分别为单选、多选和判断题。
转为txt格式
为便于读取,word格式另存为txt
格式,文件名为questions.txt
。
建立数据库
我使用的IPython来建立数据库:1
2
3
4
5
6
7
8
9
10
11
12
13
14import sqlite3
conn=sqlite3.connect('mydb.db')
c=conn.cursor()
c.execute('''create table (
id int primary key, // 主键
description text, // 题目
answer text, // 答案
A text, //选择项A。判断题时,A为正确。
B text, //选择项B。判断题时,B为错误。
C text, //选择项C
D text, //选择项D
E text, //选择项E。默认最多5个选择项。
)''')
c.commit()
从txt文件中提取试题信息
txt
题库中,每道题都以阿拉伯数字+.
开始,形如1.
,选择题题干中正确答案在全角括号中,形如(ABCD)
,判断题题干行中会有×
或√
符合,据此提取数据。代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56import re // 导入正则表达式模块
p1=re.compile("\d+.") // 判断是否为新的题干的正则表达式
p2=re.compile("((.+))") // 提取正确答案的正则表达式
f=open('questions.txt','r') // 打开文本文件
lines=f.readlines() // 读取全部文本
questions = [] // 建立空题库,在遍历文本中追加
description='' // 初始化题目
answer='' // 初始化答案
A='' // 初始化选项
B=''
C=''
D=''
E=''
for line in lines:
if p1.match(s): // 该行以数字+.开始,遇到一道新题
if description != '': // 确定题目非空
questions.append([description,answer,answerA, // 新题追加到题库中,
answerB,answerC,answerD,answerE,remark])
question='' // 然后清空各字段
answer=''
answerA=''
answerB=''
answerC=''
answerD=''
answerE=''
remark=''
if '×' in s or '√' in line: // 这是一道判断题吗
description=s[:s.find('(')] // 提取题目表述
answerA='√'
answerB='×'
remark='2' // 为便于排序,备注中判断题标记为2
if '×' in s: // 答案为×
answer='B'
elif '√' in s:
answer='A'
continue // 判断题没有选择项,所以直接跳到下一个循环
else: // 不是判断,那就是选择题了
description=s[:s.find('(')+1]+s[s.find(')'):] // 提取题目描述
answer=p2.search(s).group(1).strip()
if len(answer)>1:
remark='1' // 多选题标记为1
else:
remark='0' // 单选题标记为0
else: // 该行不是以数字+.开始,是选择项
answers = s.split() // 选择项之间以空格分开
for an in answers:
if an.startswith('A'): // 选项A
answerA=an
elif an.startswith('B'): // 选项B
answerB=an
elif an.startswith('C'): // 选项C
answerC=an
elif an.startswith('D'): // 选项D
answerD=an
elif an.startswith('E'): // 选项E
answerE=an
试题信息导入数据库
现在,所有题目都在questions
数组中,可以插入到数据库中了:1
2
3
4
5
6
7
8conn = sqlite3.connect('mydb.db') // 连接数据库
c=conn.cursor() // 获取游标
i=1 // 计数器,做ID赋值用
for q in questions:
// 执行插入
c.execute("insert into question(id,description,answer,A,B,C,D,E,remark) values(%d,'%s','%s','%s','%s','%s','%s','%s','%s')"%(i,q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7]))
i=i+1
conn.commit() // 提交
使用express.js建立服务端
新建目录express-sqlite3
:1
$ mkdir express-sqlite3 ; cd express-sqlite3
建立新文件package.json
,输入以下内容:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
"name": "express-sqlite3",
"version": "1.0.0",
"description": "",
"main": "restapi.js",
"dependencies": {
"express": "^4.13.1",
"sqlite3": "https://github.com/mapbox/node-sqlite3/tarball/master"
},
"devDependencies": {},
"scripts": {
"start": "node restapi.js"
},
"author": "",
"license": "ISC"
}
执行npm install
安装包。
新建db
目录,将上一步生成的mydb.db
文件拷贝至此目录内。
新建restapi.js
文件,输入以下内容:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('db/mydb.db');
var express = require('express');
var restapi = express();
restapi.get('/data', function(req, res){
results = [];
db.all("SELECT * from question order by remark,description", function(err, rows){
rows.map((row)=>{
results.push({"id":row.id,
"description":row.description,
"answer":row.answer,
"A":row.A,
"B":row.B,
"C":row.C,
"D":row.D,
"E":row.E,
"remark":row.remark,})
});
res.json(results);
});
});
restapi.listen(3000); // 在3000端口监听
console.log("Submit GET to http://localhost:3000/data");
在命令行执行:1
2
3
4npm start
> express-sqlite3@1.0.0 start /home/fanzhh/projects/express-sqlite3
> node restapi.js
Submit GET to http://localhost:3000/data
此时在浏览器输入地址http://localhost:3000/data
,页面显示如下:
建立React项目
首先安装create-react-app
,如果你已安装,请略过。1
$ npm install -g create-react-app
然后新建项目,我们项目的名字为frontend
:1
$ create-react-app frontend
安装过程需要几分钟:
从服务器获取json
数据我们需要用jquery
,安装1
npm install query
界面设计
我们预想的操作界面是这样的(原谅我粗狂的画风^^):
每道题在一个
修改App.js
修改frontend/src/App.js
文件。
导入
1 | import React, { Component } from 'react'; |
题目描述部件
1 | class DescriptionBar extends Component { |
答案选择部件
1 | class SelectionsBar extends Component { |
提交部件
1 | class SubmitBar extends Component { |
问题部件
问题部件是题目描述和答案选择的父部件。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class QuestionBar extends Component {
render() {
return (
<div>
<DescriptionBar description={this.props.question.description} /> // 题目描述部件
<SelectionsBar // 选择部件
id={this.props.question.id} // 传递属性值
answer={this.props.question.answer}
answerA={this.props.question.A}
answerB={this.props.question.B}
answerC={this.props.question.C}
answerD={this.props.question.D}
answerE={this.props.question.E}
remark={this.props.question.remark}
onChange={this.props.onChange}
/>
{this.props.answered ? (this.props.question.answer===this.props.answer.answer? ('') : (<p style={{"color":"red"}}>正确答案:{this.props.question.answer}</p>) ) : ('')} // 如果当前已经检查,且回答与正确答案不符,则以红色显示正确的答案。
</div>
)
}
}
整体App部件
1 | class App extends Component { |
OK。
Github
这个项目我放在github上了,地址在这儿。
演示地址
点击这儿可以查看heroku
上的演示(题库数据量较大,加载大概需要十几秒钟)。