正式考试完成
This commit is contained in:
parent
94ee6f844f
commit
27123fb3ec
|
@ -133,3 +133,11 @@ export function exportTest(query) {
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function exportwTest(id,data) {
|
||||||
|
return request({
|
||||||
|
url: `/examtest/examtest/${id}/exportw/`,
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
|
@ -18,7 +18,7 @@
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="listQuery.type"
|
v-model="listQuery.type"
|
||||||
placeholder="模考类型"
|
placeholder="考试类型"
|
||||||
clearable
|
clearable
|
||||||
style="width: 200px"
|
style="width: 200px"
|
||||||
class="filter-item"
|
class="filter-item"
|
||||||
|
@ -85,6 +85,9 @@
|
||||||
<el-table-column align="left" label="押题卷">
|
<el-table-column align="left" label="押题卷">
|
||||||
<template slot-scope="scope">{{ scope.row.paper_name }}</template>
|
<template slot-scope="scope">{{ scope.row.paper_name }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="所属考试">
|
||||||
|
<template slot-scope="scope">{{ scope.row.exam_name }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column align="left" label="得分" sortable='custom' prop="score">
|
<el-table-column align="left" label="得分" sortable='custom' prop="score">
|
||||||
<template slot-scope="scope">{{ scope.row.score }}</template>
|
<template slot-scope="scope">{{ scope.row.score }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
@ -94,14 +97,20 @@
|
||||||
<el-table-column align="left" label="答题时间">
|
<el-table-column align="left" label="答题时间">
|
||||||
<template slot-scope="scope">{{ scope.row.start_time }}</template>
|
<template slot-scope="scope">{{ scope.row.start_time }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="操作">
|
<el-table-column align="center" label="操作" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
|
v-if="scope.row.type=='正式考试'"
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
@click="handleDetail(scope)"
|
@click="handleExport(scope)"
|
||||||
icon="el-icon-more"
|
>生成Word</el-button>
|
||||||
></el-button>
|
<el-button
|
||||||
|
v-if="scope.row.type=='正式考试'"
|
||||||
|
type="warning"
|
||||||
|
size="small"
|
||||||
|
@click="handleExport2(scope)"
|
||||||
|
>重新生成</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
@ -116,7 +125,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getExamTestlist, exportTest } from "@/api/examtest";
|
import { getExamTestlist, exportTest, exportwTest } from "@/api/examtest";
|
||||||
import checkPermission from "@/utils/permission";
|
import checkPermission from "@/utils/permission";
|
||||||
import Pagination from "@/components/Pagination";
|
import Pagination from "@/components/Pagination";
|
||||||
|
|
||||||
|
@ -138,6 +147,7 @@ export default {
|
||||||
typeOptions: [
|
typeOptions: [
|
||||||
{ key: "自助模考", label: "自助模考", value: "自助模考" },
|
{ key: "自助模考", label: "自助模考", value: "自助模考" },
|
||||||
{ key: "押卷模考", label: "押卷模考", value: "押卷模考"},
|
{ key: "押卷模考", label: "押卷模考", value: "押卷模考"},
|
||||||
|
{ key: "正式考试", label: "正式考试", value: "正式考试"},
|
||||||
],
|
],
|
||||||
passOptions: [
|
passOptions: [
|
||||||
{ key: true, label: "通过", value: true },
|
{ key: true, label: "通过", value: true },
|
||||||
|
@ -186,10 +196,19 @@ export default {
|
||||||
value:'setTimeRange',
|
value:'setTimeRange',
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getList();
|
this.getQuery();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checkPermission,
|
checkPermission,
|
||||||
|
getQuery() {
|
||||||
|
if(this.$route.params.exam){
|
||||||
|
this.listQuery.exam = this.$route.params.exam;
|
||||||
|
this.getList()
|
||||||
|
}else{
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
getList() {
|
getList() {
|
||||||
this.listLoading = true;
|
this.listLoading = true;
|
||||||
getExamTestlist(this.listQuery).then(response => {
|
getExamTestlist(this.listQuery).then(response => {
|
||||||
|
@ -210,9 +229,17 @@ export default {
|
||||||
this.value = []
|
this.value = []
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
handleDetail(scope) {
|
handleExport(scope) {
|
||||||
// this.dialogVisible = true
|
const loading = this.$loading({text: '正在生成word...',});
|
||||||
// this.question = scope.row
|
exportwTest(scope.row.id).then(res=>{
|
||||||
|
loading.close()
|
||||||
|
}).catch(e=>{loading.close()})
|
||||||
|
},
|
||||||
|
handleExport2(scope) {
|
||||||
|
const loading = this.$loading({text: '正在重新生成word...',});
|
||||||
|
exportwTest(scope.row.id, {anew:true}).then(res=>{
|
||||||
|
loading.close()
|
||||||
|
}).catch(e=>{loading.close()})
|
||||||
},
|
},
|
||||||
exportTest() {
|
exportTest() {
|
||||||
const loading = this.$loading();
|
const loading = this.$loading();
|
||||||
|
|
|
@ -242,6 +242,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleView(scope){
|
handleView(scope){
|
||||||
|
this.$router.push({name:'ExamTest', params:{exam:scope.row.id}})
|
||||||
|
|
||||||
},
|
},
|
||||||
async confirmexam(form) {
|
async confirmexam(form) {
|
||||||
|
|
|
@ -69,9 +69,9 @@ App({
|
||||||
globalData: {
|
globalData: {
|
||||||
userInfo: {},
|
userInfo: {},
|
||||||
userinfo: {}, // 服务器传回的消费者信息
|
userinfo: {}, // 服务器传回的消费者信息
|
||||||
//host: 'https://apitest.ahctc.cn',
|
host: 'https://apitest.ahctc.cn',
|
||||||
mediahost: 'https://apitest.ahctc.cn',
|
mediahost: 'https://apitest.ahctc.cn',
|
||||||
host: 'http://127.0.0.1:8000',
|
//host: 'http://127.0.0.1:8000',
|
||||||
//mediahost: 'http://127.0.0.1:8000',
|
//mediahost: 'http://127.0.0.1:8000',
|
||||||
token : '',
|
token : '',
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@
|
||||||
"pages/material/video",
|
"pages/material/video",
|
||||||
"pages/qtest/form",
|
"pages/qtest/form",
|
||||||
"pages/main/start",
|
"pages/main/start",
|
||||||
"pages/exam/index"
|
"pages/exam/index",
|
||||||
|
"pages/exam/note"
|
||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"backgroundTextStyle": "light",
|
"backgroundTextStyle": "light",
|
||||||
|
|
|
@ -34,10 +34,14 @@ Page({
|
||||||
})
|
})
|
||||||
let exam = res.data.exam
|
let exam = res.data.exam
|
||||||
api.request(`/examtest/exam/${exam}/init/`,'POST', data).then(res=>{
|
api.request(`/examtest/exam/${exam}/init/`,'POST', data).then(res=>{
|
||||||
console.log(res.data)
|
wx.hideLoading()
|
||||||
wx.hideLoading({
|
try {
|
||||||
success: (res) => {},
|
wx.setStorageSync('examtest', res.data)
|
||||||
})
|
} catch (e) { }
|
||||||
|
wx.navigateTo({
|
||||||
|
url: '/pages/exam/note',
|
||||||
|
})
|
||||||
|
|
||||||
}).catch(e=>{
|
}).catch(e=>{
|
||||||
wx.hideLoading({
|
wx.hideLoading({
|
||||||
success: (res) => {},
|
success: (res) => {},
|
||||||
|
@ -55,7 +59,11 @@ Page({
|
||||||
* 生命周期函数--监听页面加载
|
* 生命周期函数--监听页面加载
|
||||||
*/
|
*/
|
||||||
onLoad: function (options) {
|
onLoad: function (options) {
|
||||||
|
wx.showLoading({
|
||||||
|
title: '获取信息...',
|
||||||
|
})
|
||||||
api.request('/crm/consumer/info/','GET').then(res=>{
|
api.request('/crm/consumer/info/','GET').then(res=>{
|
||||||
|
wx.hideLoading()
|
||||||
getApp().globalData.userinfo = res.data.userinfo
|
getApp().globalData.userinfo = res.data.userinfo
|
||||||
this.setData(
|
this.setData(
|
||||||
{
|
{
|
||||||
|
@ -65,7 +73,7 @@ Page({
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
}).catch(e=>{wx.hideLoading()})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,9 +32,8 @@
|
||||||
<label class="weui-label">部门</label>
|
<label class="weui-label">部门</label>
|
||||||
</view>
|
</view>
|
||||||
<view class="weui-cell__bd">
|
<view class="weui-cell__bd">
|
||||||
<span wx:if="{{userinfo.deptname}}">{{userinfo.deptname}}</span>
|
<!-- <span wx:if="{{userinfo.deptname}}">{{userinfo.deptname}}</span> -->
|
||||||
<input wx:else class="weui-input" placeholder="填写您的部门" placeholder-class="weui-input__placeholder" bindinput="deptInput" >
|
<input class="weui-input" placeholder="填写您的部门" placeholder-class="weui-input__placeholder" bindinput="deptInput" value="{{form.deptname}}">
|
||||||
{{userinfo.deptname}}
|
|
||||||
</input>
|
</input>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
// pages/exam/note.js
|
||||||
|
Page({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面的初始数据
|
||||||
|
*/
|
||||||
|
data: {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生命周期函数--监听页面加载
|
||||||
|
*/
|
||||||
|
onLoad: function (options) {
|
||||||
|
try {
|
||||||
|
var value = wx.getStorageSync('examtest')
|
||||||
|
if (value) {
|
||||||
|
let examtest = value
|
||||||
|
delete examtest['questions']
|
||||||
|
this.setData(examtest)
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生命周期函数--监听页面初次渲染完成
|
||||||
|
*/
|
||||||
|
onReady: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生命周期函数--监听页面显示
|
||||||
|
*/
|
||||||
|
onShow: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生命周期函数--监听页面隐藏
|
||||||
|
*/
|
||||||
|
onHide: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生命周期函数--监听页面卸载
|
||||||
|
*/
|
||||||
|
onUnload: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面相关事件处理函数--监听用户下拉动作
|
||||||
|
*/
|
||||||
|
onPullDownRefresh: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面上拉触底事件的处理函数
|
||||||
|
*/
|
||||||
|
onReachBottom: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户点击右上角分享
|
||||||
|
*/
|
||||||
|
onShareAppMessage: function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
startTest: function () {
|
||||||
|
wx.reLaunch({
|
||||||
|
url: '/pages/test/test',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"usingComponents": {},
|
||||||
|
"navigationBarTitleText": "考试须知"
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<view class="page">
|
||||||
|
<view class="page__bd">
|
||||||
|
<view class="weui-article">
|
||||||
|
<view class="weui-article__h1" style="text-align:center">正式考试须知</view>
|
||||||
|
<view class="weui-article__h2">1.考试信息</view>
|
||||||
|
<view>名称:<span style="color:blue">{{exam_.name}}</span></view>
|
||||||
|
<view>监考人:<span style="color:blue">{{exam_.proctor_name}}/{{exam_.proctor_phone}}</span></view>
|
||||||
|
<view class="weui-article__h2">2.试卷信息</view>
|
||||||
|
<view>答卷时长:<span style="color:red">{{limit}}分钟</span></view>
|
||||||
|
<view wx:if="{{danxuan_count>0}}">{{danxuan_count}}道单选题,每题{{danxuan_score}}分</view>
|
||||||
|
<view wx:if="{{duoxuan_count>0}}">{{duoxuan_count}}道多选题,每题{{duoxuan_score}}分</view>
|
||||||
|
<view wx:if="{{panduan_count>0}}">{{panduan_count}}道判断题,每题{{panduan_score}}分</view>
|
||||||
|
<view >多选题漏选每个正确选项得1分,错选0分</view>
|
||||||
|
<view >满分{{total_score}};<span style="color:blue">{{pass_score}}</span>以上通过</view>
|
||||||
|
<view class="weui-article__h2">3.答题须知</view>
|
||||||
|
<view style="color:red">进入答题后请不要后退或返回桌面</view>
|
||||||
|
<view>用户可点击上一题/下一题进行切换答题</view>
|
||||||
|
<view>可点击答题卡复查</view>
|
||||||
|
<view>请合理安排时间答题,可提前交卷,超时会自动提交</view>
|
||||||
|
</view>
|
||||||
|
<a class="weui-btn weui-btn_primary" bindtap="startTest">开始考试</a>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
|
@ -0,0 +1 @@
|
||||||
|
/* pages/exam/note.wxss */
|
|
@ -18,7 +18,12 @@ Page({
|
||||||
onLoad: function (options) {
|
onLoad: function (options) {
|
||||||
var that = this
|
var that = this
|
||||||
var query = {'examtest':options.id}
|
var query = {'examtest':options.id}
|
||||||
|
wx.showLoading({
|
||||||
|
title: '加载中...',
|
||||||
|
mask:true
|
||||||
|
})
|
||||||
api.request('/examtest/answerdetail/', 'GET', query).then(res => {
|
api.request('/examtest/answerdetail/', 'GET', query).then(res => {
|
||||||
|
wx.hideLoading()
|
||||||
for (var i = 0; i < res.data.length; i++) {
|
for (var i = 0; i < res.data.length; i++) {
|
||||||
if(res.data[i].img){
|
if(res.data[i].img){
|
||||||
res.data[i].img = getApp().globalData.mediahost + res.data[i].img
|
res.data[i].img = getApp().globalData.mediahost + res.data[i].img
|
||||||
|
@ -29,7 +34,7 @@ Page({
|
||||||
that.setData({
|
that.setData({
|
||||||
tmtotal:res.data.length
|
tmtotal:res.data.length
|
||||||
})
|
})
|
||||||
})
|
}).catch(e=>{wx.hideLoading()})
|
||||||
try {
|
try {
|
||||||
const res = wx.getSystemInfoSync()
|
const res = wx.getSystemInfoSync()
|
||||||
that.setData({
|
that.setData({
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// pages/lianxi/index.js
|
// pages/lianxi/index.js
|
||||||
const api = require("../../utils/request.js");
|
const api = require("../../utils/request.js");
|
||||||
|
var util = require('../../utils/util.js')
|
||||||
Page({
|
Page({
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,6 +37,9 @@ Page({
|
||||||
getList: function () {
|
getList: function () {
|
||||||
var that = this
|
var that = this
|
||||||
api.request('/examtest/examtest/self/', 'GET', that.data.query).then(res => {
|
api.request('/examtest/examtest/self/', 'GET', that.data.query).then(res => {
|
||||||
|
for(var i=0;i<res.data.results.length;i++){
|
||||||
|
res.data.results[i].took = util.formatSecond(res.data.results[i].took)
|
||||||
|
}
|
||||||
if (that.data.query.page == 1) {
|
if (that.data.query.page == 1) {
|
||||||
that.data.results = res.data.results
|
that.data.results = res.data.results
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
<view class="weui-media-box__bd weui-media-box__bd_in-appmsg">
|
<view class="weui-media-box__bd weui-media-box__bd_in-appmsg">
|
||||||
<view class="weui-media-box__title">{{item.name}}</view>
|
<view class="weui-media-box__title">{{item.name}}</view>
|
||||||
<view class="weui-media-box__desc">
|
<view class="weui-media-box__desc">
|
||||||
<span style="font-weight:bold;color:darkblue">{{item.type}}</span>
|
<span style="font-weight:bold;color:brown;" wx:if="{{item.type=='正式考试'}}">{{item.type}}</span>
|
||||||
|
<span style="font-weight:bold;color:darkblue" wx:else>{{item.type}}</span>
|
||||||
<span>-</span>
|
<span>-</span>
|
||||||
<span style="font-weight:bold;color:green;" wx:if="{{item.is_pass}}">通过</span>
|
<span style="font-weight:bold;color:green;" wx:if="{{item.is_pass}}">通过</span>
|
||||||
<span style="font-weight:bold;color:red;" wx:else>未通过</span>
|
<span style="font-weight:bold;color:red;" wx:else>未通过</span>
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
<span style="font-weight:bold">{{item.total_score}})</span>
|
<span style="font-weight:bold">{{item.total_score}})</span>
|
||||||
</view>
|
</view>
|
||||||
<view class="weui-media-box__desc">
|
<view class="weui-media-box__desc">
|
||||||
耗时:{{item.took}}s
|
耗时:{{item.took}}
|
||||||
开始答题:{{item.start_time}}
|
开始答题:{{item.start_time}}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
|
@ -150,6 +150,9 @@ Page({
|
||||||
for(var i=0;i<tm_current.user_answer.length;i++){
|
for(var i=0;i<tm_current.user_answer.length;i++){
|
||||||
if(tm_current.right.indexOf(tm_current.user_answer[i])!=-1){
|
if(tm_current.right.indexOf(tm_current.user_answer[i])!=-1){
|
||||||
score = score + 1
|
score = score + 1
|
||||||
|
if(score==2){
|
||||||
|
break;
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
score = 0
|
score = 0
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -2,8 +2,13 @@ from openpyxl.workbook import Workbook
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from openpyxl.styles import Font, Fill
|
from openpyxl.styles import Font, Fill
|
||||||
|
from rest_framework.serializers import Serializer
|
||||||
|
from .models import ExamTest, AnswerDetail
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from docxtpl import DocxTemplate
|
||||||
|
from io import BytesIO
|
||||||
|
from .serializers import ExamTestDetailSerializer
|
||||||
|
import os
|
||||||
BASE_DIR = settings.BASE_DIR
|
BASE_DIR = settings.BASE_DIR
|
||||||
|
|
||||||
def export_test(tests):
|
def export_test(tests):
|
||||||
|
@ -23,3 +28,24 @@ def export_test(tests):
|
||||||
path = '/media/export/' + filename
|
path = '/media/export/' + filename
|
||||||
wb.save((BASE_DIR + path).replace('\\', '/'))
|
wb.save((BASE_DIR + path).replace('\\', '/'))
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
def exportw_test(obj, bool):
|
||||||
|
"""
|
||||||
|
导出个人考试记录word版本
|
||||||
|
"""
|
||||||
|
filename = obj.exam.name + '-' + obj.consumer.name + '-' + obj.consumer.username + '.doc'
|
||||||
|
path = '/media/export/' + filename
|
||||||
|
fullpath = BASE_DIR + path
|
||||||
|
if bool or (not os.path.exists(fullpath)):
|
||||||
|
data = ExamTestDetailSerializer(instance=obj).data
|
||||||
|
#开始生成word
|
||||||
|
doc = DocxTemplate(BASE_DIR + "/tmp/examtest.docx")
|
||||||
|
doc.render(data)
|
||||||
|
doc.save(fullpath)
|
||||||
|
# output = BytesIO()
|
||||||
|
# doc.save(output)
|
||||||
|
# output.seek(0)
|
||||||
|
# res = HttpResponse(content_type='application/msword')
|
||||||
|
# res['Content-Disposition'] = 'attachment;filename='+filename+'.docx'
|
||||||
|
# res.write(output.getvalue())
|
||||||
|
return path
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 3.0.4 on 2021-03-21 01:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('question', '0012_question_img'),
|
||||||
|
('examtest', '0025_auto_20210314_1716'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='answerdetail',
|
||||||
|
name='examtest',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answerdetail_examtest', to='examtest.ExamTest'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='examtest',
|
||||||
|
name='detail',
|
||||||
|
field=models.ManyToManyField(through='examtest.AnswerDetail', to='question.Question', verbose_name='答题记录'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -20,7 +20,8 @@ class Exam(CommonModel):
|
||||||
proctor_phone = models.CharField('监考人联系方式', max_length=100)
|
proctor_phone = models.CharField('监考人联系方式', max_length=100)
|
||||||
create_admin = models.ForeignKey(UserProfile, on_delete=models.SET_NULL, null=True, blank=True, related_name='exam_create_admin')
|
create_admin = models.ForeignKey(UserProfile, on_delete=models.SET_NULL, null=True, blank=True, related_name='exam_create_admin')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class ExamTest(CommonModel):
|
class ExamTest(CommonModel):
|
||||||
|
@ -43,7 +44,7 @@ class ExamTest(CommonModel):
|
||||||
took = models.IntegerField(default=0, verbose_name='耗时(秒)')
|
took = models.IntegerField(default=0, verbose_name='耗时(秒)')
|
||||||
start_time = models.DateTimeField(verbose_name='开始答题时间')
|
start_time = models.DateTimeField(verbose_name='开始答题时间')
|
||||||
end_time = models.DateTimeField(verbose_name='结束答题时间')
|
end_time = models.DateTimeField(verbose_name='结束答题时间')
|
||||||
detail = models.ManyToManyField(Question, related_name='答题记录', through='AnswerDetail')
|
detail = models.ManyToManyField(Question, verbose_name='答题记录', through='AnswerDetail')
|
||||||
is_pass = models.BooleanField(default=True, verbose_name='是否通过')
|
is_pass = models.BooleanField(default=True, verbose_name='是否通过')
|
||||||
|
|
||||||
exam = models.ForeignKey(Exam, verbose_name='关联的正式考试', null=True, blank=True, related_name='examtest_exam', on_delete= models.SET_NULL)
|
exam = models.ForeignKey(Exam, verbose_name='关联的正式考试', null=True, blank=True, related_name='examtest_exam', on_delete= models.SET_NULL)
|
||||||
|
@ -53,7 +54,7 @@ class ExamTest(CommonModel):
|
||||||
|
|
||||||
|
|
||||||
class AnswerDetail(SoftCommonModel):
|
class AnswerDetail(SoftCommonModel):
|
||||||
examtest = models.ForeignKey(ExamTest, on_delete=models.CASCADE)
|
examtest = models.ForeignKey(ExamTest, on_delete=models.CASCADE, related_name='answerdetail_examtest')
|
||||||
question = models.ForeignKey(Question, on_delete=models.CASCADE)
|
question = models.ForeignKey(Question, on_delete=models.CASCADE)
|
||||||
user_answer = JSONField(null=True,blank=True)
|
user_answer = JSONField(null=True,blank=True)
|
||||||
score = models.FloatField(default=0, verbose_name='本题得分')
|
score = models.FloatField(default=0, verbose_name='本题得分')
|
||||||
|
|
|
@ -61,6 +61,7 @@ class ExamTestListSerializer(serializers.ModelSerializer):
|
||||||
end_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
|
end_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
|
||||||
workscope_name = serializers.StringRelatedField(source='workscope', read_only=True)
|
workscope_name = serializers.StringRelatedField(source='workscope', read_only=True)
|
||||||
paper_name = serializers.StringRelatedField(source='paper', read_only=True)
|
paper_name = serializers.StringRelatedField(source='paper', read_only=True)
|
||||||
|
exam_name = serializers.StringRelatedField(source='exam', read_only=True)
|
||||||
consumer_name = serializers.SerializerMethodField()
|
consumer_name = serializers.SerializerMethodField()
|
||||||
took_format = serializers.SerializerMethodField()
|
took_format = serializers.SerializerMethodField()
|
||||||
consumer_company_name = serializers.SerializerMethodField()
|
consumer_company_name = serializers.SerializerMethodField()
|
||||||
|
@ -83,7 +84,7 @@ class ExamTestListSerializer(serializers.ModelSerializer):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def setup_eager_loading(queryset):
|
def setup_eager_loading(queryset):
|
||||||
""" Perform necessary eager loading of data. """
|
""" Perform necessary eager loading of data. """
|
||||||
queryset = queryset.select_related('consumer','paper', 'workscope')
|
queryset = queryset.select_related('consumer','paper', 'workscope', 'exam')
|
||||||
return queryset
|
return queryset
|
||||||
from question.serializers import QuestionSerializer
|
from question.serializers import QuestionSerializer
|
||||||
class AnswerDetailSerializer(serializers.ModelSerializer):
|
class AnswerDetailSerializer(serializers.ModelSerializer):
|
||||||
|
@ -174,3 +175,44 @@ class ExamListSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Exam
|
model = Exam
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
class ExamSimpleSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Exam
|
||||||
|
exclude = ('create_admin',)
|
||||||
|
|
||||||
|
class ConsumerSimpleSerializer(serializers.ModelSerializer):
|
||||||
|
comanyname = serializers.StringRelatedField(source='company', read_only=True)
|
||||||
|
class Meta:
|
||||||
|
model = Consumer
|
||||||
|
fields = ['id', 'name', 'ID_number1', 'comanyname', 'deptname', 'username']
|
||||||
|
class ExamTestDetailSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
考试详情序列化(导出用)
|
||||||
|
"""
|
||||||
|
workscope_name = serializers.StringRelatedField(source='workscope', read_only=True)
|
||||||
|
paper_name = serializers.StringRelatedField(source='paper', )
|
||||||
|
took_format = serializers.SerializerMethodField()
|
||||||
|
detail_ = serializers.SerializerMethodField()
|
||||||
|
exam_ = ExamSimpleSerializer(source='exam', read_only=True)
|
||||||
|
consumer_ = ConsumerSimpleSerializer(source='consumer', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ExamTest
|
||||||
|
exclude = ('detail',)
|
||||||
|
|
||||||
|
def get_took_format(self, obj):
|
||||||
|
m, s = divmod(obj.took, 60)
|
||||||
|
h, m = divmod(m, 60)
|
||||||
|
return "%02d:%02d:%02d" % (h, m, s)
|
||||||
|
|
||||||
|
def get_detail_(self, obj):
|
||||||
|
objs = obj.answerdetail_examtest.order_by('question__type')
|
||||||
|
return AnswerDetailSerializer(instance=objs, many=True).data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setup_eager_loading(queryset):
|
||||||
|
""" Perform necessary eager loading of data. """
|
||||||
|
queryset = queryset.select_related('consumer','paper', 'workscope', 'exam')
|
||||||
|
queryset = queryset.prefetch_related('answerdetail_examtest')
|
||||||
|
return queryset
|
|
@ -2,6 +2,7 @@ from datetime import datetime
|
||||||
|
|
||||||
from django.db.models import Avg
|
from django.db.models import Avg
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
from django.utils.translation import get_language_from_request
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from openpyxl import Workbook, load_workbook
|
from openpyxl import Workbook, load_workbook
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
@ -10,6 +11,7 @@ from rest_framework.filters import OrderingFilter, SearchFilter
|
||||||
from rest_framework.generics import GenericAPIView
|
from rest_framework.generics import GenericAPIView
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.serializers import Serializer
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
|
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
|
||||||
|
@ -20,14 +22,14 @@ from server import settings
|
||||||
from utils.custom import CommonPagination
|
from utils.custom import CommonPagination
|
||||||
from utils.mixins import OptimizationMixin
|
from utils.mixins import OptimizationMixin
|
||||||
|
|
||||||
from .exports import export_test
|
from .exports import export_test, exportw_test
|
||||||
from .models import AnswerDetail, Banner, ExamTest, Exam
|
from .models import AnswerDetail, Banner, ExamTest, Exam
|
||||||
from .models_paper import Paper, PaperQuestions, TestRule, WorkScope
|
from .models_paper import Paper, PaperQuestions, TestRule, WorkScope
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
AnswerDetailCreateSerializer, AnswerDetailSerializer, BannerSerializer,
|
AnswerDetailCreateSerializer, AnswerDetailSerializer, BannerSerializer,
|
||||||
ExamTestListSerializer, MoniTestSerializer, PaperDetailSerializer,
|
ExamTestListSerializer, MoniTestSerializer, PaperDetailSerializer,
|
||||||
PaperQuestionsCreateSerializer, PaperSerializer, TestRuleSerializer,
|
PaperQuestionsCreateSerializer, PaperSerializer, TestRuleSerializer,
|
||||||
WorkScopeSerializer, ExamCreateUpdateSerializer, ExamListSerializer)
|
WorkScopeSerializer, ExamCreateUpdateSerializer, ExamListSerializer, ExamTestDetailSerializer)
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
@ -77,8 +79,6 @@ class ExamViewSet(ModelViewSet):
|
||||||
"""
|
"""
|
||||||
参加考试
|
参加考试
|
||||||
"""
|
"""
|
||||||
if request.user.role.name in ['游客', '注册用户']:
|
|
||||||
return Response({'error':'无权参加考试,请联系老师'})
|
|
||||||
if request.data.get('deptname', None):
|
if request.data.get('deptname', None):
|
||||||
request.user.deptname = request.data['deptname']
|
request.user.deptname = request.data['deptname']
|
||||||
request.user.save()
|
request.user.save()
|
||||||
|
@ -87,6 +87,8 @@ class ExamViewSet(ModelViewSet):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
try:
|
try:
|
||||||
exam = Exam.objects.get(code=code, opentime__lt=now, closetime__gt=now)
|
exam = Exam.objects.get(code=code, opentime__lt=now, closetime__gt=now)
|
||||||
|
if ExamTest.objects.filter(exam=exam, consumer=request.user).exists():
|
||||||
|
return Response({'error':'您已参加过该场考试'})
|
||||||
return Response({'exam':exam.pk})
|
return Response({'exam':exam.pk})
|
||||||
except:
|
except:
|
||||||
return Response({'error':'考试编号不存在'})
|
return Response({'error':'考试编号不存在'})
|
||||||
|
@ -103,7 +105,8 @@ class ExamViewSet(ModelViewSet):
|
||||||
if workscope.name in ['医学Ⅲ类', '非医学Ⅲ类']:
|
if workscope.name in ['医学Ⅲ类', '非医学Ⅲ类']:
|
||||||
ret['name'] = obj.name
|
ret['name'] = obj.name
|
||||||
ret['type'] = '正式考试' # 正式考试
|
ret['type'] = '正式考试' # 正式考试
|
||||||
ret['exam'] = ExamListSerializer(instance=obj).data
|
ret['exam'] = kwargs['pk']
|
||||||
|
ret['exam_'] = ExamListSerializer(instance=obj).data
|
||||||
ret['workscope'] = workscope.id
|
ret['workscope'] = workscope.id
|
||||||
ret['limit'] = 60
|
ret['limit'] = 60
|
||||||
ret['total_score'] = 120
|
ret['total_score'] = 120
|
||||||
|
@ -347,7 +350,7 @@ class ExamTestViewSet(ModelViewSet):
|
||||||
ordering = ['-create_time']
|
ordering = ['-create_time']
|
||||||
search_fields = ('consumer__name', 'consumer__company__name')
|
search_fields = ('consumer__name', 'consumer__company__name')
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||||
filterset_fields = ['type','is_pass']
|
filterset_fields = ['type','is_pass', 'exam']
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
assert self.queryset is not None, (
|
assert self.queryset is not None, (
|
||||||
|
@ -432,6 +435,16 @@ class ExamTestViewSet(ModelViewSet):
|
||||||
path = export_test(serializer.data)
|
path = export_test(serializer.data)
|
||||||
return Response({'path': path})
|
return Response({'path': path})
|
||||||
|
|
||||||
|
@action(methods=['post'], detail = True ,perms_map=[{'post':'export_test'}])
|
||||||
|
def exportw(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if 'anew' in request.data and request.data['anew']:
|
||||||
|
# 是否需要重新生成
|
||||||
|
path = exportw_test(obj, True)
|
||||||
|
else:
|
||||||
|
path = exportw_test(obj, False)
|
||||||
|
# return res
|
||||||
|
|
||||||
class PaperViewSet(ModelViewSet):
|
class PaperViewSet(ModelViewSet):
|
||||||
"""
|
"""
|
||||||
押题卷增删改查
|
押题卷增删改查
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue