创建题目分类/题目表
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 apps.system.models import CommonAModel
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
|
||||
# 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/'
|
||||
HTML_BASE_URL = 'exam/'
|
||||
router = routers.DefaultRouter()
|
||||
router.register('questioncat', QuestioncatViewSet, basename='questioncat')
|
||||
router.register('question', QuestionViewSet, basename="question")
|
||||
urlpatterns = [
|
||||
]
|
||||
path(API_BASE_URL, include(router.urls))
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,169 @@
|
|||
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.
|
||||
|
||||
|
||||
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 *
|
||||
DEBUG = True
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'cma',
|
||||
'USER': 'postgres',
|
||||
'PASSWORD': 'zctest1234',
|
||||
'HOST': '47.95.0.242',
|
||||
'PORT': '5432',
|
||||
},
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.postgresql',
|
||||
# 'NAME': 'cma',
|
||||
# 'USER': 'cma',
|
||||
# 'PASSWORD': 'cma123',
|
||||
# 'HOST': '172.16.80.102',
|
||||
# # 'HOST': '1.203.161.102',
|
||||
# 'PORT': '5432',
|
||||
# }
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.postgresql',
|
||||
# 'NAME': 'cma',
|
||||
# 'USER': 'postgres',
|
||||
# 'PASSWORD': 'zctest1234',
|
||||
# 'HOST': '47.95.0.242',
|
||||
# 'PORT': '5432',
|
||||
# },
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'cma',
|
||||
'USER': 'cma',
|
||||
'PASSWORD': 'cma123',
|
||||
'HOST': '172.16.80.102',
|
||||
# 'HOST': '1.203.161.102',
|
||||
'PORT': '5432',
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue