feat:考试记录导出功能

This commit is contained in:
zty 2024-06-04 16:25:22 +08:00
parent cf38fbd5e3
commit 8b747cb05f
4 changed files with 59 additions and 60 deletions

View File

@ -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

View File

@ -5,7 +5,7 @@ from openpyxl.styles import Font, Fill
import json import json
import os import os
def export_question(questions): def export_question(questions:object):
''' '''
params: serializer questions params: serializer questions
return: xlsx path return: xlsx path
@ -24,4 +24,33 @@ def export_question(questions):
if not os.path.exists(full_path): if not os.path.exists(full_path):
os.makedirs(full_path) os.makedirs(full_path)
wb.save(full_path+filename) 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)

View File

@ -1,7 +1,7 @@
from django.shortcuts import render from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import ListModelMixin, DestroyModelMixin, RetrieveModelMixin 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.models import Question, Questioncat, PaperQuestion
from apps.exam.serializers import (QuestionSerializer, QuestioncatSerializer, PaperSerializer, ExamDetailSerializer, ExamRecordDetailSerializer, ExamListSerializer, from apps.exam.serializers import (QuestionSerializer, QuestioncatSerializer, PaperSerializer, ExamDetailSerializer, ExamRecordDetailSerializer, ExamListSerializer,
ExamCreateUpdateSerializer, ExamListSerializer, ExamRecordSubmitSerializer, PaperDetailSerializer, PaperCreateUpdateSerializer, AnswerDetailOutSerializer, ExamRecordListSerializer) 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 utils.queryset import get_child_queryset2
from apps.system.permission import has_permission from apps.system.permission import has_permission
from apps.exam.parse_word import interpret_text from apps.exam.parse_word import interpret_text
from .export import export_question
import os import os
import shutil import shutil
from django.db.models import Q from django.db.models import Q
@ -65,22 +64,10 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
perms_map = {'get': '*', 'post':'question', 'put':'question', 'delete':'question'} perms_map = {'get': '*', 'post':'question', 'put':'question', 'delete':'question'}
queryset = Question.objects.all() queryset = Question.objects.all()
serializer_class = QuestionSerializer serializer_class = QuestionSerializer
filterset_fields = ['level', 'type', 'year'] filterset_fields = ['level', 'type', 'year', 'questioncat']
search_fields = ['name', 'options', 'resolution'] 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) @action(methods=['post'], detail=False, url_name='enable_question', perms_map={'post': 'question'}, serializer_class=Serializer)
def enable(self, request): def enable(self, request):
""" """
@ -93,7 +80,7 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
Question.objects.filter(pk__in=ids).update(enabled=True) Question.objects.filter(pk__in=ids).update(enabled=True)
return Response(status=200) 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): def deletes(self, request):
""" """
批量删除 批量删除
@ -104,8 +91,7 @@ class QuestionViewSet(CreateUpdateCustomMixin, ModelViewSet):
return Response() return Response()
return Response({'error':'权限不足'}) return Response({'error':'权限不足'})
@action(methods=['get'], detail=False, @action(methods=['get'], detail=False, perms_map={'get':'export_question'})
url_path='export', url_name='export_question', perms_map=[{'*':'export_question'}])
def export(self, request): def export(self, request):
""" """
导出题目 导出题目
@ -294,8 +280,7 @@ class PaperViewSet(ModelViewSet):
return Response() return Response()
@action(methods=['put'], detail=True, url_path='clone', url_name='clone_paper', @action(methods=['put'], detail=True, url_path='clone', url_name='clone_paper', perms_map={'put':'clone_paper'})
perms_map=[{'put':'clone_paper'}])
def clone(self, request, pk=None): def clone(self, request, pk=None):
''' '''
克隆试卷 克隆试卷
@ -492,7 +477,7 @@ class ExamViewSet(CreateUpdateCustomMixin, ModelViewSet):
instance.delete(soft=False) instance.delete(soft=False)
return Response(status=204) 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 @transaction.atomic
def start(self, request, *args, **kwargs): def start(self, request, *args, **kwargs):
""" """
@ -564,7 +549,8 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
if has_permission('ctc_manager', self.request.user): if has_permission('ctc_manager', self.request.user):
return qs 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: else:
return qs.filter(create_by=self.request.user) return qs.filter(create_by=self.request.user)
@ -577,11 +563,9 @@ class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, G
def perform_destroy(self, instance): # 考试记录物理删除 def perform_destroy(self, instance): # 考试记录物理删除
instance.delete(soft=False) 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): def clear(self, request, pk=None):
""" """
清除七日前未提交的考试记录
清除七日前未提交的考试记录 清除七日前未提交的考试记录
""" """
now = timezone.now 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) ExamRecord.objects.filter(create_time__lte=days7_ago, is_submited=False).delete(soft=False)
return Response(status=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 @transaction.atomic
def submit(self, request, pk=None): def submit(self, request, pk=None):
''' '''

View File

@ -348,8 +348,11 @@ class UserExamViewset(ImpMixin, ModelViewSet):
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据 for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
if row[0] is not None: if row[0] is not None:
dept = Organization.objects.get(name=row[3]) dept = Organization.objects.get(name=row[3])
if not dept: user_depts = get_child_queryset2(request.user.dept).order_by('sort')
return Response({'msg': '部门不存在'}) 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 = { serializer_data = {
'name': row[1], 'name': row[1],
'username':row[2], 'username':row[2],
@ -510,6 +513,7 @@ class UserViewSet(PageOrNot, ModelViewSet):
while sheet['b'+str(i)].value: while sheet['b'+str(i)].value:
name = sheet['b'+str(i)].value name = sheet['b'+str(i)].value
email = sheet['e'+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(): if not User.objects.filter(username=email).exists():
user = User.objects.create(name=name, user = User.objects.create(name=name,
username=email, username=email,
@ -517,7 +521,7 @@ class UserViewSet(PageOrNot, ModelViewSet):
dept=dept) dept=dept)
else: else:
user = User.objects.get(username=email) user = User.objects.get(username=email)
dept = Organization.objects.get(name=sheet['j'+str(i)].value)
if sheet['f'+str(i)].value: if sheet['f'+str(i)].value:
user.roles.add(role1) user.roles.add(role1)
if sheet['g'+str(i)].value: if sheet['g'+str(i)].value: