From 8b747cb05f0c555ccff747e1437392ba0c0bd062 Mon Sep 17 00:00:00 2001 From: zty Date: Tue, 4 Jun 2024 16:25:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E8=80=83=E8=AF=95=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/apps/exam/export.py | 29 ----------------------- server/apps/exam/exports.py | 33 ++++++++++++++++++++++++-- server/apps/exam/views.py | 47 +++++++++++++++++-------------------- server/apps/system/views.py | 10 +++++--- 4 files changed, 59 insertions(+), 60 deletions(-) delete mode 100644 server/apps/exam/export.py diff --git a/server/apps/exam/export.py b/server/apps/exam/export.py deleted file mode 100644 index 133e9dd..0000000 --- a/server/apps/exam/export.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/server/apps/exam/exports.py b/server/apps/exam/exports.py index e509192..9558373 100644 --- a/server/apps/exam/exports.py +++ b/server/apps/exam/exports.py @@ -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 \ No newline at end of file + 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) \ No newline at end of file diff --git a/server/apps/exam/views.py b/server/apps/exam/views.py index f6d667c..bd2a2bb 100644 --- a/server/apps/exam/views.py +++ b/server/apps/exam/views.py @@ -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): ''' diff --git a/server/apps/system/views.py b/server/apps/system/views.py index 1b8b0c8..8d84cfc 100644 --- a/server/apps/system/views.py +++ b/server/apps/system/views.py @@ -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: