python3 Flask 多人答题(完整项目带源码与使用)
TopQB答題系統
2020/01/05
@pingfan
功能:
?? ?1、多人同時答題系統
?? ?2、在線查看個人得分與答題情況(解析)
?? ?3、載入題庫,隨機抽取題目支持【單選題,多選題,判斷題】
?? ?4、自定義題目數量與題目分值,題目都是隨機但數量不變總分不變
?? ?5、在線實時查看總體得分情況,可導出excel
?? ?6、適應場景: 各類考試答題等
?? ?7、提示:?
?? ??? ?1、不需要外網,任意內網(局域網)機器運行均可,也可單機運行使用
?? ??? ?2、無毒,請添加信任
?? ??? ?3、系統要求: 64位win7、win10(當然linux是最好的)
使用說明:
?? ?1、題庫模板位于【static】->【qb.xlsx】題目索引值不可重復
?? ?2、運行app.exe
?? ?3、查看本機ip地址
?? ?4、訪問:
?? ??? ?1、答題地址:
?? ??? ??? ?http://【本機ip】:9999
?? ??? ??? ?如:http://192.168.1.2:9999
?? ??? ?2、設置地址:
?? ??? ??? ?http://【本機ip】:9999/config
?? ??? ?3、得分地址:
?? ??? ??? ?http://【本機ip】:9999/results
?? ??? ?4、管理地址:
?? ??? ??? ?http://【本機ip】:9999/admin
?? ?5、答題前請設置題目數量,題目得分
?? ?6、實時查看總體得分情況刷新成績頁面即可
==========================================================================================
管理頁面
設置頁面
成績匯總頁面
答題頁面
解析頁面
==========================================================================================
小小flask項目,就幾個路由系統,沒使用藍圖。
完整目錄結構
不使用數據庫,輕應用讀取excel中的題目緩存到內存中
需要一個excel.xlsx(2007以上的題庫模板)放置到目錄的static文件夾中命名為【qb.xlsx】(讀取的是第一個工作表)
==========================================================================================
只依賴flask,提升并發的(協程)gevent庫,以及pfExcel(基于openpyxl的封裝)用于讀取excel中的數據(前面的博客有提到)
前端沒有使用任何框架,js都是原生
以下是依賴庫的安裝
# 安裝flask pip install flask# 安裝gevent pip install gevent# 安裝openpyxl pip install openpyxl部署就是一個(pyinstaller庫編譯app.py)exe執行文件
# 安裝pyinstaller pip install pyinstaller # 編譯app.pypyinstaller -F -i 你的logo文件.ico app.py以下是pyExcel.py源碼
?
以下是qb.py源碼
""" 用于操作題庫數據 """ import socket from random import shufflefrom pfExcel import ExcelWork# 問題設置 QUESTION_CONFIG = {'radioNum': 1,'checkboxNum': 1,'trueOrFalseNum': 1,'radioResult': 1,'checkboxResult': 1,'trueOrFalseResult': 1 }def loadQb(qbPath):"""加載題庫數據:param qbPath: list: 題庫地址:return: list: [[單選題,], [多選題,], [判斷題,]]"""# 單選題列表radioList = []# 多選題列表checkboxList = []# 判斷題列表trueOrFalseList = []# 讀取excelexcel = ExcelWork(qbPath)# 獲取所有數據for index, _ in enumerate(excel.getColumn(1)):# 每行數據: ['索引', '題型', '題目', '選項A', '選項B', '選項C', '選項D', '選項E', '選項F', '答案']rowList = excel.getRow(index + 1)[0:10]# 根據題型劃分if rowList[1] == '單選題':radioList.append(rowList)elif rowList[1] == '多選題':checkboxList.append(rowList)elif rowList[1] == '判斷題':trueOrFalseList.append(rowList)return [radioList, checkboxList, trueOrFalseList]def randomQbData(qbData):"""隨機題庫數據直接改變原始數據:param qbData: list: [[單選題,], [多選題,], [判斷題,]]:return:"""for tempData in qbData:# 打亂列表數據的順序shuffle(tempData)def getUserQbData(qbData):"""獲取用戶題庫數據并保存:param qbData: list: [[單選題,], [多選題,], [判斷題,]]:return: list: [[題],]"""radioList = qbData[0][:int(QUESTION_CONFIG['radioNum'])]checkboxList = qbData[1][:int(QUESTION_CONFIG['checkboxNum'])]trueOrFalseList = qbData[2][:int(QUESTION_CONFIG['trueOrFalseNum'])]return radioList + checkboxList + trueOrFalseListdef listToStr(dataList):"""將列表中的值提取出來轉為字符串:param dataList: list:return: str"""tempStr = ''for temp in dataList:tempStr = tempStr + str(temp)return tempStrdef checkQbData(userQbData):"""檢查答案與用戶作答數據進行比對為列表添加結果:param userQbData: list: [[索引, 題型, 題目, 選項A, 選項B, 選項C, 選項D, 選項E, 選項F, 答案, 用戶作答],]:return: list: [[索引, 題型, 題目, 選項A, 選項B, 選項C, 選項D, 選項E, 選項F, 答案, 用戶作答, 正確|錯誤],]"""for qb in userQbData:# 比對結果if qb[9] == qb[10]:qb.append('正確')else:qb.append('錯誤')def getUserResult(userQbData):"""獲取用戶成績:param userQbData: list: [[索引, 題型, 題目, 選項A, 選項B, 選項C, 選項D, 選項E, 選項F, 答案, 用戶作答, 正確|錯誤],]:return: float: 用戶成績"""result = 0for temp in userQbData:if temp[-1] == '正確':if temp[1] == '單選題':result = result + float(QUESTION_CONFIG['radioResult'])elif temp[1] == '多選題':result = result + float(QUESTION_CONFIG['checkboxResult'])elif temp[1] == '判斷題':result = result + float(QUESTION_CONFIG['trueOrFalseResult'])return resultdef getIp():"""查詢本機ip地址:return: str: 本機ip"""s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)s.connect(('8.8.8.8', 80))ip = s.getsockname()[0]s.close()return ip以下是app.py源碼
from gevent import monkeymonkey.patch_all() # 猴子補丁from gevent import pywsgi from copy import deepcopy import webbrowserfrom flask import Flask, request, render_template, redirect from qb import loadQb, randomQbData, getUserQbData, listToStr, checkQbData, getUserResult, getIp, QUESTION_CONFIG# app實例 app = Flask(__name__)""" 緩存 QB_DATA緩存excel表格題庫數據 USER_QB_DATA緩存用戶隨機的題庫數據 """ # 題庫數據緩存[[單選題,], [多選題,], [判斷題,]] QB_DATA = loadQb('static/qb.xlsx') # 用戶題庫數據緩存 {ip: [單選題, 多選題, 判斷題],} USER_QB_DATA = {} # 成績緩存[[name, class, result],] RESULT_DATA = []# 打開管理頁面 webbrowser.open('http://127.0.0.1:9999/admin')@app.route('/', methods=['GET', 'POST']) def question():"""答題|解析:return:"""if request.method == 'GET':# 深拷貝數據,保證原始數據不被修改data = deepcopy(QB_DATA)# 隨機題庫數據randomQbData(data)# 獲取用戶題庫數據userQbData = getUserQbData(data)# 緩存用戶題庫數據 {ip: [[單選題],[多選題],[判斷題],],}USER_QB_DATA[request.remote_addr] = userQbData# 組合數據cont = {'data': userQbData}return render_template('question.html', **cont)else:# 獲取用戶name = request.form.get('name')# 獲取班級qcls = request.form.get('qcls')# 獲取用戶題庫緩存數據userQbData = USER_QB_DATA[request.remote_addr]# 清除用戶緩存del USER_QB_DATA[request.remote_addr]# 用戶題庫緩存數據后追加用戶答題結果for qb in userQbData:qb.append(listToStr(request.form.getlist(str(qb[0]))))# 檢查答題結果checkQbData(userQbData)# 獲取成績result = getUserResult(userQbData)# 添加成績緩存RESULT_DATA.append([name, qcls, result])# 組合數據cont = {'data': userQbData,'name': name,'qcls': qcls,'result': result}return render_template('result.html', **cont)@app.route('/admin') def admin():cont = {'url': f'http://{getIp()}:9999'}return render_template('admin.html', **cont)@app.route('/results') def results():"""成績:return:"""cont = {'data': RESULT_DATA}return render_template('results.html', **cont)@app.route('/config', methods=['GET', 'POST']) def config():"""設置:return:"""if request.method == 'GET':cont = {'data': QUESTION_CONFIG}return render_template('config.html', **cont)else:data = request.form.to_dict()for k, v in data.items():QUESTION_CONFIG[k] = vreturn redirect('/config')if __name__ == '__main__':print('-----答題系統服務已啟動-----\n')print('-----后臺管理-----\n')print('http://127.0.0.1:9999/admin\n')print('#' * 30)server = pywsgi.WSGIServer(('0.0.0.0', 9999), app)server.serve_forever()==========================================================================================
前端頁面放置到templates文件夾中
以下是admin.html頁面源碼
<!DOCTYPE html> <html lang="zh"> <head><meta charset="UTF-8"><title>答題系統</title><style>* {margin: 0;padding: 0;}ul {list-style: none;}.main {width: 800px;margin: 0 auto;text-align: center;overflow: hidden;}.title {margin-top: 30px;font-size: 30px;font-weight: bold;}.info {margin-top: 30px;}.info li {margin-top: 10px;}.url{font-weight: bold;margin-top: 30px;}</style> </head> <body> <div class="main"><div class="title">答題系統</div><div class="url">答題地址:{{ url }}</div><ul class="info"><li><a href="/config" target="_blank">設置</a></li><li><a href="/results" target="_blank">成績</a></li></ul> </div> </body> </html>以下是config.html源碼
<!DOCTYPE html> <html lang="zh"> <head><meta charset="UTF-8"><title>答題系統</title><style>* {margin: 0;padding: 0;}ul {list-style: none;}.main {width: 800px;margin: 0 auto;text-align: center;overflow: hidden;}.title {margin-top: 30px;font-size: 30px;font-weight: bold;}.info {margin-top: 30px;}.info li {margin-top: 10px;}.sub {margin: 20px;}</style> </head> <body> <div class="main"><div class="title">答題系統</div><form action="/config" method="post"><ul class="info"><li><label>單選題數量:<input type="text" name="radioNum" value="{{ data['radioNum'] }}"></label></li><li><label>多選題數量:<input type="text" name="checkboxNum" value="{{ data['checkboxNum'] }}"></label></li><li><label>判斷題數量:<input type="text" name="trueOrFalseNum" value="{{ data['trueOrFalseNum'] }}"></label></li><li><label>單選題分值:<input type="text" name="radioResult" value="{{ data['radioResult'] }}"></label></li><li><label>多選題分值:<input type="text" name="checkboxResult" value="{{ data['checkboxResult'] }}"></label></li><li><label>判斷題分值:<input type="text" name="trueOrFalseResult"value="{{ data['trueOrFalseResult'] }}"></label></li></ul><div class="sub"><input type="submit" value="設置"></div></form> </div> </body> </html>以下是results.html源碼
<!DOCTYPE html> <html lang="zh"> <head><meta charset="UTF-8"><title>答題系統</title><style>* {margin: 0;padding: 0;}ul {list-style: none;}.main {width: 800px;margin: 0 auto;text-align: center;overflow: hidden;}.title {margin-top: 30px;font-size: 30px;font-weight: bold;}.get {margin-top: 20px;}.result{width: 400px;margin: 20px auto;}</style> </head> <body> <div class="main"><div class="title">答題系統</div><div class="get"><button onclick="copyTable()">導出</button></div><table class="result"><tr><td>姓名</td><td>班級</td><td>成績</td></tr>{% for tempList in data %}<tr>{% for temp in tempList %}<td>{{ temp }}</td>{% endfor %}</tr>{% endfor %}</table> </div> <script>//將table中的數據放置粘貼板function copyTable() {//獲取table數據let data = '';let tb = document.getElementsByTagName("table")[0];let rows = tb.rows;for (let i = 0; i < rows.length; i++) {let cells = rows[i].cells;for (let j = 0; j < cells.length; j++) {data = data + cells[j].textContent + '\t';}data = data.slice(0, -1) + '\n';}data = data.slice(0, -1);//將數據添加至粘貼板let textArea = document.createElement("textarea");textArea.value = data;document.body.appendChild(textArea);textArea.select();try {document.execCommand('copy')} catch (err) {alert('導出失敗')}document.body.removeChild(textArea);alert('數據已導出找個表格粘貼吧~')} </script> </body> </html>以下是question.html源碼
<!DOCTYPE html> <html lang="zh"> <head><meta charset="UTF-8"><title>答題系統</title><style>* {margin: 0;padding: 0;}ul {list-style: none;}.main {width: 800px;margin: 0 auto;text-align: center;overflow: hidden;}.title {margin-top: 30px;font-size: 30px;font-weight: bold;}.info {margin-top: 30px;}.info li {margin-top: 10px;}.question {width: 400px;margin: 30px auto;text-align: left;}.questionType {font-size: 18px;font-weight: bold;}.questionTitle {margin-top: 10px;font-size: 18px;}.option {margin-top: 10px;}.sub {margin: 20px;}</style> </head> <body> <div class="main"><div class="title">答題系統</div><form action="/" method="post"><ul class="info"><li><label>姓名:<input type="text" name="name"></label></li><li><label>班級:<input type="text" name="qcls"></label></li></ul>{% for temp in data %}{# 判斷題型 #}{% if temp[1] != '多選題' %}{# ['索引', '題型', '題目', '選項A', '選項B', '選項C', '選項D', '選項E', '選項F', '答案'] #}<ul class="question"><li class="questionType">{{ temp[1] }}</li><li class="questionTitle">{{ temp[2] }}</li>{% if temp[3] !=None %}<li class="option"><label><input type="radio" name="{{ temp[0] }}" value="A">{{ temp[3] }}</label></li>{% endif %}{% if temp[4] !=None %}<li class="option"><label><input type="radio" name="{{ temp[0] }}" value="B">{{ temp[4] }}</label></li>{% endif %}{% if temp[5] !=None %}<li class="option"><label><input type="radio" name="{{ temp[0] }}" value="C">{{ temp[5] }}</label></li>{% endif %}{% if temp[6] !=None %}<li class="option"><label><input type="radio" name="{{ temp[0] }}" value="D">{{ temp[6] }}</label></li>{% endif %}{% if temp[7] !=None %}<li class="option"><label><input type="radio" name="{{ temp[0] }}" value="E">{{ temp[7] }}</label></li>{% endif %}{% if temp[8] !=None %}<li class="option"><label><input type="radio" name="{{ temp[0] }}" value="F">{{ temp[8] }}</label></li>{% endif %}</ul>{% else %}<ul class="question"><li class="questionType">{{ temp[1] }}</li><li class="questionTitle">{{ temp[2] }}</li>{% if temp[3] !=None %}<li class="option"><label><input type="checkbox" name="{{ temp[0] }}" value="A">{{ temp[3] }}</label></li>{% endif %}{% if temp[4] !=None %}<li class="option"><label><input type="checkbox" name="{{ temp[0] }}" value="B">{{ temp[4] }}</label></li>{% endif %}{% if temp[5] !=None %}<li class="option"><label><input type="checkbox" name="{{ temp[0] }}" value="C">{{ temp[5] }}</label></li>{% endif %}{% if temp[6] !=None %}<li class="option"><label><input type="checkbox" name="{{ temp[0] }}" value="D">{{ temp[6] }}</label></li>{% endif %}{% if temp[7] !=None %}<li class="option"><label><input type="checkbox" name="{{ temp[0] }}" value="E">{{ temp[7] }}</label></li>{% endif %}{% if temp[8] !=None %}<li class="option"><label><input type="checkbox" name="{{ temp[0] }}" value="F">{{ temp[8] }}</label></li>{% endif %}</ul>{% endif %}{% endfor %}<div class="sub"><input type="submit" value="提交"></div></form> </div> </body> </html>以下是result.html源碼
<!DOCTYPE html> <html lang="zh"> <head><meta charset="UTF-8"><title>答題系統</title><style>* {margin: 0;padding: 0;}ul {list-style: none;}.main {width: 800px;margin: 0 auto;text-align: center;overflow: hidden;}.title {margin-top: 30px;font-size: 30px;font-weight: bold;}.info {margin-top: 30px;}.info li {margin-top: 10px;}.result {font-size: 18px;font-weight: bold;}.question {width: 400px;margin: 30px auto;text-align: left;padding: 10px;}.questionType {font-size: 18px;font-weight: bold;}.questionTitle {margin-top: 10px;font-size: 18px;}.option {margin-top: 10px;}</style> </head> <body> <div class="main"><div class="title">答題系統</div><ul class="info"><li><label class="result">得分:{{ result }}</label></li><li><label>姓名:{{ name }}</label></li><li><label>班級:{{ qcls }}</label></li></ul>{% for temp in data %}{# 判斷題型 #}{# ['索引', '題型', '題目', '選項A', '選項B', '選項C', '選項D', '選項E', '選項F', '答案', '用戶作答', '正確|錯誤'] #}<ul class="question"><li class="questionType">{{ temp[1] }}</li><li class="questionTitle">{{ temp[2] }}</li>{% if temp[3] !=None %}<li class="option"><label>{{ temp[3] }}</label></li>{% endif %}{% if temp[4] !=None %}<li class="option"><label>{{ temp[4] }}</label></li>{% endif %}{% if temp[5] !=None %}<li class="option"><label>{{ temp[5] }}</label></li>{% endif %}{% if temp[6] !=None %}<li class="option"><label>{{ temp[6] }}</label></li>{% endif %}{% if temp[7] !=None %}<li class="option"><label>{{ temp[7] }}</label></li>{% endif %}{% if temp[8] !=None %}<li class="option"><label>{{ temp[8] }}</label></li>{% endif %}<li class="option"><label>答案是:{{ temp[9] }}</label></li><li class="option"><label>您選擇的是:{{ temp[10] }}</label></li><li class="option"><label class="color">判定結果:{{ temp[11] }}</label></li></ul>{% endfor %} </div> <script>let a = document.getElementsByClassName('color')for (let i = 0; i < a.length; i++) {//父類let p = a[i].parentNode.parentNodeif (a[i].innerText === '判定結果:錯誤') {p.style.backgroundColor = 'LightCoral'} else {p.style.backgroundColor = 'SpringGreen'}} </script> </body> </html>將源碼按照文件名保存,運行編譯都可以(進行過真實環境測試,效果還可以,寫的啰嗦求大佬指正,互相學習!
當然也可以直接拿來用,用的便捷用的完成要求啦,麻煩點個贊哈~~
以下是pyinstaller編譯后的目錄 9.9M多一點點,夠小了沒有通過pyinstaller把靜態文件與模板打包到exe中(其實是可以的但不利于直接修改模板題庫)
templates目錄
總結
以上是生活随笔為你收集整理的python3 Flask 多人答题(完整项目带源码与使用)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何将图片压缩到指定大小?压缩图片大小的
- 下一篇: 自律的程序员生活是什么样的?