feat:考试记录导出功能
This commit is contained in:
parent
cf38fbd5e3
commit
8b747cb05f
|
@ -1,29 +0,0 @@
|
|||
from openpyxl.workbook import Workbook
|
||||
from django.conf import settings
|
||||
from datetime import datetime
|
||||
from openpyxl.styles import Font, Fill
|
||||
import json
|
||||
|
||||
BASE_DIR = settings.BASE_DIR
|
||||
|
||||
|
||||
def export_question(questions):
|
||||
'''
|
||||
params: serializer questions
|
||||
return: xlsx path
|
||||
'''
|
||||
wb = Workbook()
|
||||
ws1 = wb.active
|
||||
ws1.title = '题目表'
|
||||
ws1.append(['分类','题型', '题干', '选项', '正确答案', '解析'])
|
||||
row = ws1.row_dimensions[1]
|
||||
row.font = Font(bold=True)
|
||||
for i in questions:
|
||||
# options=''
|
||||
# for key in i.options:
|
||||
# pass
|
||||
ws1.append([i.questioncat.name, i.type, i.name, json.dumps(i.options, ensure_ascii=False), ''.join(sorted(i.right)), i.resolution])
|
||||
filename = 'questions' + datetime.now().strftime("%Y%m%d%H%M%S") +'.xlsx'
|
||||
path = '/media/export/' + filename
|
||||
wb.save((BASE_DIR + path).replace('\\', '/'))
|
||||
return path
|
|
@ -5,7 +5,7 @@ from openpyxl.styles import Font, Fill
|
|||
import json
|
||||
import os
|
||||
|
||||
def export_question(questions):
|
||||
def export_question(questions:object):
|
||||
'''
|
||||
params: serializer questions
|
||||
return: xlsx path
|
||||
|
@ -24,4 +24,33 @@ def export_question(questions):
|
|||
if not os.path.exists(full_path):
|
||||
os.makedirs(full_path)
|
||||
wb.save(full_path+filename)
|
||||
return path + filename
|
||||
return path + filename
|
||||
|
||||
|
||||
def export_record(records:object):
|
||||
'''
|
||||
params: serializer records
|
||||
return: xlsx path
|
||||
'''
|
||||
wb = Workbook()
|
||||
ws1 = wb.active
|
||||
ws1.title = '答题记录表'
|
||||
ws1.append(['类型', '用户', '是否通过', '得分', '总分', '耗时(时分秒)', '答题时间', '部门'])
|
||||
row = ws1.row_dimensions[1]
|
||||
row.font = Font(bold=True)
|
||||
for i in records:
|
||||
tookformat = get_took_format(i.took)
|
||||
ws1.append([i.type, i.create_by.name, i.is_pass, i.score, i.total_score, str(tookformat), str(i.start_time), i.belong_dept.name])
|
||||
filename = 'records' + datetime.now().strftime("%Y%m%d%H%M%S") +'.xlsx'
|
||||
path = '/media/temp/'
|
||||
full_path = settings.BASE_DIR + '/media/temp/'
|
||||
if not os.path.exists(full_path):
|
||||
os.makedirs(full_path)
|
||||
wb.save(full_path+filename)
|
||||
return path + filename
|
||||
|
||||
|
||||
def get_took_format(took:int):
|
||||
m, s = divmod(took, 60)
|
||||
h, m = divmod(m, 60)
|
||||
return "%02d:%02d:%02d" % (h, m, s)
|
|
@ -1,7 +1,7 @@
|
|||
from django.shortcuts import render
|
||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||
from rest_framework.mixins import ListModelMixin, DestroyModelMixin, RetrieveModelMixin
|
||||
from apps.exam.exports import export_question
|
||||
from apps.exam.exports import export_question, export_record
|
||||
from apps.exam.models import Question, Questioncat, PaperQuestion
|
||||
from apps.exam.serializers import (QuestionSerializer, QuestioncatSerializer, PaperSerializer, ExamDetailSerializer, ExamRecordDetailSerializer, ExamListSerializer,
|
||||
ExamCreateUpdateSerializer, ExamListSerializer, ExamRecordSubmitSerializer, PaperDetailSerializer, PaperCreateUpdateSerializer, AnswerDetailOutSerializer, ExamRecordListSerializer)
|
||||
|
@ -23,7 +23,6 @@ from apps.edu.serializers import CertificateSerializer
|
|||
from utils.queryset import get_child_queryset2
|
||||
from apps.system.permission import has_permission
|
||||
from apps.exam.parse_word import interpret_text
|
||||
from .export import export_question
|
||||
import os
|
||||
import shutil
|
||||
from django.db.models import Q
|
||||
|
@ -65,22 +64,10 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
|
|||
perms_map = {'get': '*', 'post':'question', 'put':'question', 'delete':'question'}
|
||||
queryset = Question.objects.all()
|
||||
serializer_class = QuestionSerializer
|
||||
filterset_fields = ['level', 'type', 'year']
|
||||
filterset_fields = ['level', 'type', 'year', 'questioncat']
|
||||
search_fields = ['name', 'options', 'resolution']
|
||||
|
||||
|
||||
@action(methods=['get'], detail=False,
|
||||
url_path='export', url_name='export_question', perms_map=[{'get': '*'}], serializer_class=Serializer)
|
||||
def export_question(self, request):
|
||||
"""
|
||||
导出题目
|
||||
|
||||
导出题目
|
||||
"""
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
path = export_question(queryset)
|
||||
return Response({'path': path})
|
||||
|
||||
@action(methods=['post'], detail=False, url_name='enable_question', perms_map={'post': 'question'}, serializer_class=Serializer)
|
||||
def enable(self, request):
|
||||
"""
|
||||
|
@ -93,7 +80,7 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
|
|||
Question.objects.filter(pk__in=ids).update(enabled=True)
|
||||
return Response(status=200)
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map=[{'*':'question_delete'}])
|
||||
@action(methods=['post'], detail=False, perms_map={'*':'question_delete'})
|
||||
def deletes(self, request):
|
||||
"""
|
||||
批量删除
|
||||
|
@ -104,8 +91,7 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
|
|||
return Response()
|
||||
return Response({'error':'权限不足'})
|
||||
|
||||
@action(methods=['get'], detail=False,
|
||||
url_path='export', url_name='export_question', perms_map=[{'*':'export_question'}])
|
||||
@action(methods=['get'], detail=False, perms_map={'get':'export_question'})
|
||||
def export(self, request):
|
||||
"""
|
||||
导出题目
|
||||
|
@ -294,8 +280,7 @@ class PaperViewSet(ModelViewSet):
|
|||
return Response()
|
||||
|
||||
|
||||
@action(methods=['put'], detail=True, url_path='clone', url_name='clone_paper',
|
||||
perms_map=[{'put':'clone_paper'}])
|
||||
@action(methods=['put'], detail=True, url_path='clone', url_name='clone_paper', perms_map={'put':'clone_paper'})
|
||||
def clone(self, request, pk=None):
|
||||
'''
|
||||
克隆试卷
|
||||
|
@ -492,7 +477,7 @@ class ExamViewSet(CreateUpdateCustomMixin, ModelViewSet):
|
|||
instance.delete(soft=False)
|
||||
return Response(status=204)
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map=[{'post': '*'}], serializer_class=Serializer, permission_classes = [IsAuthenticated])
|
||||
@action(methods=['post'], detail=True, perms_map={'post': '*'}, serializer_class=Serializer, permission_classes = [IsAuthenticated])
|
||||
@transaction.atomic
|
||||
def start(self, request, *args, **kwargs):
|
||||
"""
|
||||
|
@ -564,7 +549,8 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
|
|||
if has_permission('ctc_manager', self.request.user):
|
||||
return qs
|
||||
# 如果是部门管理员,只能看到自己部门下的考试记录
|
||||
# return qs.filter(belong_dept__in=get_child_queryset2(self.request.user.dept))
|
||||
elif has_permission('exam_manager', self.request.user):
|
||||
return qs.filter(belong_dept__in=get_child_queryset2(self.request.user.dept))
|
||||
# 如果是普通员工,只能看到自己考试记录
|
||||
else:
|
||||
return qs.filter(create_by=self.request.user)
|
||||
|
@ -577,11 +563,9 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
|
|||
def perform_destroy(self, instance): # 考试记录物理删除
|
||||
instance.delete(soft=False)
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map=[{'post': '*'}], serializer_class=Serializer, permission_classes = [IsAuthenticated])
|
||||
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=Serializer, permission_classes = [IsAuthenticated])
|
||||
def clear(self, request, pk=None):
|
||||
"""
|
||||
清除七日前未提交的考试记录
|
||||
|
||||
清除七日前未提交的考试记录
|
||||
"""
|
||||
now = timezone.now
|
||||
|
@ -589,8 +573,19 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
|
|||
ExamRecord.objects.filter(create_time__lte=days7_ago, is_submited=False).delete(soft=False)
|
||||
return Response(status=False)
|
||||
|
||||
|
||||
@action(methods=['get'], detail=False,
|
||||
url_path='export', url_name='export_record', perms_map={'*': 'export_ansrecord'}, serializer_class=Serializer)
|
||||
def export(self, request):
|
||||
"""
|
||||
导出答题记录
|
||||
"""
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
path = export_record(queryset)
|
||||
return Response({'path': path})
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map=[{'post': '*'}], serializer_class=ExamRecordSubmitSerializer, permission_classes = [IsAuthenticated])
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post': '*'}, serializer_class=ExamRecordSubmitSerializer, permission_classes = [IsAuthenticated])
|
||||
@transaction.atomic
|
||||
def submit(self, request, pk=None):
|
||||
'''
|
||||
|
|
|
@ -348,8 +348,11 @@ class UserExamViewset(ImpMixin, ModelViewSet):
|
|||
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
|
||||
if row[0] is not None:
|
||||
dept = Organization.objects.get(name=row[3])
|
||||
if not dept:
|
||||
return Response({'msg': '部门不存在'})
|
||||
user_depts = get_child_queryset2(request.user.dept).order_by('sort')
|
||||
serializer = OrganizationSerializer(user_depts, many=True)
|
||||
depts = any(i.id==dept.id for i in serializer.data)
|
||||
if depts is False:
|
||||
return Response({'msg': f'本公司下不存在此部门{row[3]}'})
|
||||
serializer_data = {
|
||||
'name': row[1],
|
||||
'username':row[2],
|
||||
|
@ -510,6 +513,7 @@ class UserViewSet(PageOrNot, ModelViewSet):
|
|||
while sheet['b'+str(i)].value:
|
||||
name = sheet['b'+str(i)].value
|
||||
email = sheet['e'+str(i)].value
|
||||
dept = Organization.objects.get(name=sheet['j'+str(i)].value)
|
||||
if not User.objects.filter(username=email).exists():
|
||||
user = User.objects.create(name=name,
|
||||
username=email,
|
||||
|
@ -517,7 +521,7 @@ class UserViewSet(PageOrNot, ModelViewSet):
|
|||
dept=dept)
|
||||
else:
|
||||
user = User.objects.get(username=email)
|
||||
dept = Organization.objects.get(name=sheet['j'+str(i)].value)
|
||||
|
||||
if sheet['f'+str(i)].value:
|
||||
user.roles.add(role1)
|
||||
if sheet['g'+str(i)].value:
|
||||
|
|
Loading…
Reference in New Issue