创建题目分类/题目表
This commit is contained in:
		
							parent
							
								
									d4a8c9dfa0
								
							
						
					
					
						commit
						cc30c9babf
					
				|  | @ -0,0 +1,27 @@ | ||||||
|  | from openpyxl.workbook import Workbook | ||||||
|  | from django.conf import settings | ||||||
|  | from datetime import datetime | ||||||
|  | from openpyxl.styles import Font, Fill | ||||||
|  | import json | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | 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: | ||||||
|  |         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/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 | ||||||
|  | @ -1,3 +1,47 @@ | ||||||
| from django.db import models | from django.db import models | ||||||
|  | from apps.system.models import CommonAModel | ||||||
|  | from django.contrib.postgres.fields import JSONField | ||||||
| 
 | 
 | ||||||
| # Create your models here. | # Create your models here. | ||||||
|  | class Questioncat(CommonAModel): | ||||||
|  |     name = models.CharField(max_length=200, verbose_name='名称') | ||||||
|  |     parent = models.ForeignKey('self', verbose_name='父', null=True, blank=True, on_delete=models.CASCADE, related_name='questioncat_parent') | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = '题目分类' | ||||||
|  |         verbose_name_plural = verbose_name | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.name | ||||||
|  |      | ||||||
|  |     @property | ||||||
|  |     def tmtotal(self): | ||||||
|  |         return self.questioncat.count() | ||||||
|  |      | ||||||
|  | 
 | ||||||
|  | class Question(CommonAModel): | ||||||
|  |     type_choices = ( | ||||||
|  |         ('单选', '单选'), | ||||||
|  |         ('多选', '多选'), | ||||||
|  |         ('判断', '判断'), | ||||||
|  |     ) | ||||||
|  |     level_choices = ( | ||||||
|  |         ('低', '低'), | ||||||
|  |         ('中', '中'), | ||||||
|  |         ('高', '高'), | ||||||
|  |     ) | ||||||
|  |     name = models.TextField(verbose_name='题干') | ||||||
|  |     img = models.CharField(max_length=1000, null=True, blank=True, verbose_name='题干图片') | ||||||
|  |     type = models.CharField(max_length=50, default='单选', choices=type_choices, verbose_name='题型') | ||||||
|  |     level = models.CharField(max_length=50, default='低', choices=level_choices, verbose_name='难度') | ||||||
|  |     questioncat = models.ForeignKey(Questioncat, blank=True, null=True, on_delete=models.SET_NULL, verbose_name='所属题库', related_name='questioncat') | ||||||
|  |     options = JSONField(verbose_name='选项') | ||||||
|  |     right = JSONField(verbose_name='正确答案') | ||||||
|  |     resolution = models.TextField(verbose_name='解析', blank=True) | ||||||
|  |     enabled = models.BooleanField('是否启用', default=False) | ||||||
|  |     year = models.IntegerField('真题年份', null=True, blank=True) | ||||||
|  |     class Meta: | ||||||
|  |         verbose_name = '题目' | ||||||
|  |         verbose_name_plural = verbose_name | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.name | ||||||
|  | @ -0,0 +1,15 @@ | ||||||
|  | from rest_framework.serializers import ModelSerializer | ||||||
|  | 
 | ||||||
|  | from apps.exam.models import Question, Questioncat | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class QuestioncatSerializer(ModelSerializer): | ||||||
|  |     class Meta: | ||||||
|  |         model = Questioncat | ||||||
|  |         fields = '__all__' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class QuestionSerializer(ModelSerializer): | ||||||
|  |     class Meta: | ||||||
|  |         model = Question | ||||||
|  |         fields = '__all__' | ||||||
|  | @ -1,6 +1,13 @@ | ||||||
| from django.urls import path | from django.urls import path, include | ||||||
|  | from rest_framework import routers | ||||||
|  | 
 | ||||||
|  | from apps.exam.views import QuestionViewSet, QuestioncatViewSet | ||||||
| 
 | 
 | ||||||
| API_BASE_URL = 'api/exam/' | API_BASE_URL = 'api/exam/' | ||||||
| HTML_BASE_URL = 'exam/' | HTML_BASE_URL = 'exam/' | ||||||
|  | router = routers.DefaultRouter() | ||||||
|  | router.register('questioncat', QuestioncatViewSet, basename='questioncat') | ||||||
|  | router.register('question', QuestionViewSet, basename="question") | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  |     path(API_BASE_URL, include(router.urls)) | ||||||
| ] | ] | ||||||
|  | @ -1,3 +1,169 @@ | ||||||
| from django.shortcuts import render | from django.shortcuts import render | ||||||
|  | from rest_framework.viewsets import ModelViewSet | ||||||
|  | from apps.exam.exports import export_question | ||||||
|  | from apps.exam.models import Question, Questioncat | ||||||
|  | from apps.exam.serializers import QuestionSerializer, QuestioncatSerializer | ||||||
|  | from rest_framework.decorators import action | ||||||
|  | from rest_framework.response import Response | ||||||
|  | from rest_framework.permissions import IsAuthenticated | ||||||
|  | from openpyxl import Workbook, load_workbook | ||||||
|  | from django.conf import settings | ||||||
| 
 | 
 | ||||||
| # Create your views here. | # Create your views here. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class QuestioncatViewSet(ModelViewSet): | ||||||
|  |     perms_map = {'*': '*'} | ||||||
|  |     queryset = Questioncat.objects.all() | ||||||
|  |     serializer_class = QuestioncatSerializer | ||||||
|  |     filterset_fields = ['parent'] | ||||||
|  |     search_fields = ['name'] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class QuestionViewSet(ModelViewSet): | ||||||
|  |     perms_map = {'*': '*'} | ||||||
|  |     queryset = Question.objects.all() | ||||||
|  |     serializer_class = QuestionSerializer | ||||||
|  |     filterset_fields = ['level', 'type', 'year'] | ||||||
|  |     search_fields = ['name', 'options', 'resolution'] | ||||||
|  | 
 | ||||||
|  |     @action(methods=['get'], detail=False, | ||||||
|  |             url_path='export', url_name='export_question', perms_map=[{'get': '*'}]) | ||||||
|  |     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', permission_classes=[IsAuthenticated]) | ||||||
|  |     def enable(self, request): | ||||||
|  |         ids = request.data.get('ids', None) | ||||||
|  |         if ids: | ||||||
|  |             Question.objects.filter(pk__in=ids).update(enabled=True) | ||||||
|  |             return Response(status=200) | ||||||
|  | 
 | ||||||
|  |     @action(methods=['post'], detail=False, | ||||||
|  |             url_path='import', url_name='import_question', perms_map=[{'post': '*'}]) | ||||||
|  |     def import_question(self, request): | ||||||
|  |         """ | ||||||
|  |         导入题目 | ||||||
|  |         """ | ||||||
|  |         xlsxpath = request.data['path'] | ||||||
|  |         fullpath = settings.BASE_DIR + xlsxpath | ||||||
|  |         wb = load_workbook(fullpath) | ||||||
|  |         sheet = wb.worksheets[0] | ||||||
|  |         qlist = ['A', 'B', 'C', 'D', 'E', 'F'] | ||||||
|  |         leveldict = {'低': '低', '中': '中', '高': '高'} | ||||||
|  |         notinlist = [] | ||||||
|  |         # 验证文件内容 | ||||||
|  |         if sheet['a2'].value != '题目类型': | ||||||
|  |             return Response({"error": "类型列错误!"}) | ||||||
|  |         if sheet['b2'].value != '分类': | ||||||
|  |             return Response({"error": "分类列错误!"}) | ||||||
|  |         if sheet['c2'].value != '题目': | ||||||
|  |             return Response({"error": "题目列错误!"}) | ||||||
|  |         questioncatdict = {} | ||||||
|  |         questioncats = Questioncat.objects.all() | ||||||
|  |         for i in questioncats: | ||||||
|  |             questioncatdict[i.name] = i.id | ||||||
|  |         i = 3 | ||||||
|  |         while sheet['c'+str(i)].value: | ||||||
|  |             type = sheet['a'+str(i)].value.replace(' ', '') | ||||||
|  |             questioncat = sheet['b'+str(i)].value | ||||||
|  |             if questioncat: | ||||||
|  |                 questioncat = questioncat.replace(' ', '') | ||||||
|  |             else: | ||||||
|  |                 return Response(str(i)+'行没有分类', status=400) | ||||||
|  |             name = sheet['c'+str(i)].value | ||||||
|  | 
 | ||||||
|  |             answer = {} | ||||||
|  |             if sheet['d'+str(i)].value: | ||||||
|  |                 answer['A'] = sheet['d'+str(i)].value | ||||||
|  |             if sheet['e'+str(i)].value: | ||||||
|  |                 answer['B'] = sheet['e'+str(i)].value | ||||||
|  |             if sheet['f'+str(i)].value: | ||||||
|  |                 answer['C'] = sheet['f'+str(i)].value | ||||||
|  |             if sheet['g'+str(i)].value: | ||||||
|  |                 answer['D'] = sheet['g'+str(i)].value | ||||||
|  |             if sheet['h'+str(i)].value: | ||||||
|  |                 answer['E'] = sheet['h'+str(i)].value | ||||||
|  |             if sheet['i'+str(i)].value: | ||||||
|  |                 answer['F'] = sheet['i'+str(i)].value | ||||||
|  |             right = sheet['j'+str(i)].value | ||||||
|  |             if right: | ||||||
|  |                 right = right.replace(' ', '') | ||||||
|  |             else: | ||||||
|  |                 return Response(str(i)+'行没有答案', status=400) | ||||||
|  |             resolution = sheet['k'+str(i)].value | ||||||
|  |             level = sheet['l'+str(i)].value | ||||||
|  |             year = sheet['m' + str(i)].value | ||||||
|  |             if level: | ||||||
|  |                 level = level.replace(' ', '') | ||||||
|  |             cateobj = None | ||||||
|  |             if questioncat not in questioncatdict: | ||||||
|  |                 return Response(str(i)+"行不存在分类("+questioncat+")!请先新建", status=400) | ||||||
|  |             else: | ||||||
|  |                 cateobj = Questioncat.objects.get( | ||||||
|  |                     id=questioncatdict[questioncat]) | ||||||
|  |             if type == '单选': | ||||||
|  |                 if Question.objects.filter(type='单选', name=name, year=year, options=answer, questioncat=cateobj).exists(): | ||||||
|  |                     notinlist.append(i) | ||||||
|  |                 else: | ||||||
|  |                     if right in ['A', 'B', 'C', 'D', 'E', 'F']: | ||||||
|  |                         obj = Question() | ||||||
|  |                         obj.type = '单选' | ||||||
|  |                         if cateobj: | ||||||
|  |                             obj.questioncat = cateobj | ||||||
|  |                         obj.name = name | ||||||
|  |                         obj.options = answer | ||||||
|  |                         obj.right = right | ||||||
|  |                         obj.resolution = resolution if resolution else '' | ||||||
|  |                         obj.year = year if year else None | ||||||
|  |                         if level in leveldict: | ||||||
|  |                             obj.level = leveldict[level] | ||||||
|  |                         else: | ||||||
|  |                             obj.level = '低' | ||||||
|  |                         obj.save() | ||||||
|  |             elif type == '多选': | ||||||
|  |                 right = list(right.strip()) | ||||||
|  |                 if Question.objects.filter(type='多选', name=name, year=year, options=answer, questioncat=cateobj).exists(): | ||||||
|  |                     notinlist.append(i) | ||||||
|  |                 else: | ||||||
|  |                     if [False for c in right if c not in qlist]: | ||||||
|  |                         pass | ||||||
|  |                     else: | ||||||
|  |                         obj = Question() | ||||||
|  |                         obj.type = '多选' | ||||||
|  |                         obj.questioncat = cateobj | ||||||
|  |                         obj.name = name | ||||||
|  |                         obj.options = answer | ||||||
|  |                         obj.right = right | ||||||
|  |                         obj.resolution = resolution if resolution else '' | ||||||
|  |                         obj.year = year if year else None | ||||||
|  |                         if level in leveldict: | ||||||
|  |                             obj.level = leveldict[level] | ||||||
|  |                         else: | ||||||
|  |                             obj.level = '低' | ||||||
|  |                         obj.save() | ||||||
|  |             elif type == '判断': | ||||||
|  |                 if right == 'A' or right == '对' or right == '正确': | ||||||
|  |                     right = 'A' | ||||||
|  |                 else: | ||||||
|  |                     right = 'B' | ||||||
|  |                 if Question.objects.filter(type='判断', name=name, is_delete=0,  options={'A': '对', 'B': '错'}, questioncat=cateobj).exists(): | ||||||
|  |                     notinlist.append(i) | ||||||
|  |                 else: | ||||||
|  |                     obj = Question() | ||||||
|  |                     obj.type = '判断' | ||||||
|  |                     obj.questioncat = cateobj | ||||||
|  |                     obj.name = name | ||||||
|  |                     obj.options = {'A': '对', 'B': '错'} | ||||||
|  |                     obj.right = right | ||||||
|  |                     obj.resolution = resolution if resolution else '' | ||||||
|  |                     obj.year = year if year else None | ||||||
|  |                     if level in leveldict: | ||||||
|  |                         obj.level = leveldict[level] | ||||||
|  |                     else: | ||||||
|  |                         obj.level = '低' | ||||||
|  |                     obj.save() | ||||||
|  |             i = i + 1 | ||||||
|  |         return Response(notinlist, status=200) | ||||||
|  |  | ||||||
|  | @ -1,21 +1,21 @@ | ||||||
| from .settings import * | from .settings import * | ||||||
| DEBUG = True | DEBUG = True | ||||||
| DATABASES = { | DATABASES = { | ||||||
|      'default': { |     #  'default': { | ||||||
|          'ENGINE': 'django.db.backends.postgresql', |     #      'ENGINE': 'django.db.backends.postgresql', | ||||||
|          'NAME': 'cma', |     #      'NAME': 'cma', | ||||||
|          'USER': 'postgres', |     #      'USER': 'postgres', | ||||||
|          'PASSWORD': 'zctest1234', |     #      'PASSWORD': 'zctest1234', | ||||||
|          'HOST': '47.95.0.242', |     #      'HOST': '47.95.0.242', | ||||||
|          'PORT': '5432', |     #      'PORT': '5432', | ||||||
|      }, |     #  }, | ||||||
| #    'default': { |    'default': { | ||||||
| #         'ENGINE': 'django.db.backends.postgresql', |         'ENGINE': 'django.db.backends.postgresql', | ||||||
| #         'NAME': 'cma', |         'NAME': 'cma', | ||||||
| #         'USER': 'cma', |         'USER': 'cma', | ||||||
| #         'PASSWORD': 'cma123', |         'PASSWORD': 'cma123', | ||||||
| #         'HOST': '172.16.80.102', |         'HOST': '172.16.80.102', | ||||||
| #         # 'HOST': '1.203.161.102', |         # 'HOST': '1.203.161.102', | ||||||
| #         'PORT': '5432', |         'PORT': '5432', | ||||||
| #     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue