<repo_name>marui0608/Exam<file_sep>/apps/account/forms.py
from captcha.fields import CaptchaField
from django import forms


class RegisterForm(forms.Form):
    '''
    注册验证表单
    '''
    nick_name = forms.CharField(required=True, min_length=1)
    email = forms.EmailField(required=True)
    password = forms.CharField(required=True, min_length=5)
    # 验证码
    captcha = CaptchaField(error_messages={'invalid': '验证码错误'})


class ForgetPwdForm(forms.Form):
    '''忘记密码'''
    email = forms.EmailField(required=True)
    captcha = CaptchaField(error_messages={'invalid': '验证码错误'})


class LoginForm(forms.Form):
    """
    登陆验证码
    """
    username = forms.CharField(required=True)
    password = forms.CharField(required=True, min_length=5)


class ModifyPwdForm(forms.Form):
    '''重置密码'''
    password1 = forms.CharField(required=True, min_length=5)
    password2 = forms.CharField(required=True, min_length=5)
<file_sep>/apps/competition/urls.py
from django.urls import path

from apps.competition.views import *

app_name = 'set'
urlpatterns = [
    # 录入、配置题库首页
    path('set_index/', set_index, name='set_index'),
    # 录制题库
    path('set_bank/', set_bank, name='set_bank'),
    # 模板下载
    path('template_download/', template_download, name='template_download'),
    # 配置考试
    path('set_test/', set_test, name='set_test'),
    path('type/', type, name='type'),
    # 考试信息
    path('test_info/<id>', test_info, name='test_info'),
    # 开始考试
    path('test_start/<id>', test_start, name='test_start'),
    # 考试结果
    path('result/<uid>/<gid>', result, name='result'),
]
<file_sep>/apps/account/views.py
import datetime
import json
import random
import re

import requests
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.hashers import make_password
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views.generic.base import View

from Exam.settings import APIKEY
from apps.account.models import UserProfile, EmailVerifyRecord, VerifyCode
from utils import weibo
from utils.email_send import send_register_eamil
from utils.yunpian import YunPian
from .forms import RegisterForm, LoginForm, ForgetPwdForm, ModifyPwdForm


# 激活用户功能视图
class ActiveUserView(View):
    def get(self, request, active_code):
        # 查询邮箱验证记录是否存在
        all_record = EmailVerifyRecord.objects.filter(code=active_code)
        if all_record:
            for record in all_record:
                # 获取到对应的邮箱
                email = record.email
                # 查找到邮箱对应的user
                user = UserProfile.objects.get(email=email)
                user.is_active = True
                user.save()
                # 激活成功跳转到登录页面
                return render(request, "login.html", )
            # 验证码不对的时候跳转到激活失败页面
            else:
                return render(request, 'active_fail.html')
        else:
            return render(request, "register.html", {"msg": "您的激活链接无效"})


# 邮箱注册视图
class RegisterView(View):

    def get(self, request):
        register_form = RegisterForm()
        return render(request, 'register.html', {'register_form': register_form})

    def post(self, request):
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():

            nick_name = request.POST.get('nick_name', None)
            user_name = request.POST.get('email', None)
            pass_word = request.POST.get('password', None)
            # 实例化一个user_profile对象
            user_profile = UserProfile()
            user_profile.nick_name = nick_name
            user_profile.username = user_name
            user_profile.email = user_name
            user_profile.is_active = False
            # 对保存到数据库的密码加密
            user_profile.password = <PASSWORD>(pass_<PASSWORD>)
            user_profile.save()
            send_register_eamil(user_name, 'register')
            return render(request, 'send_success.html')
        else:
            return render(request, 'register.html', {'register_form': register_form})


# 手机号注册视图
class ForCodeView(View):
    def get(self, request):
        return render(request, 'register.html')

    def post(self, request):
        mobile = request.POST.get('mobile', None)
        password = request.POST.get('password', None)
        if mobile:
            # 验证是否为有效手机号
            mobile_pat = re.compile('^(13\d|14[5|7]|15\d|166|17[3|6|7]|18\d)\d{8}$')
            res = re.search(mobile_pat, mobile)
            if res:
                if password:
                    # 生成手机验证码
                    c = random.randint(100000, 999999)
                    code = VerifyCode.objects.create(code=str(c), mobile=mobile)
                    code.save()
                    code = VerifyCode.objects.filter(code=str(c)).first()
                    yunpian = YunPian(APIKEY)
                    sms_status = yunpian.send_sms(code=code, mobile=mobile)
                    reg = UserProfile.objects.create(username=mobile, mobile=mobile, password=(<PASSWORD>(password)))
                    reg.save()
                    msg = '发送成功，请查收!'
                    return HttpResponse(msg)
                else:
                    msg = '请输入账户密码!'
                    return HttpResponse(msg)
            else:
                msg = '请输入有效手机号码!'
                return HttpResponse(msg)
        else:
            msg = '手机号不能为空！'
            return HttpResponse(msg)


# 实现多用户类型登录
class CustomBackend(ModelBackend):
    def authenticate(self, request, username=None, password=<PASSWORD>, **kwargs):
        try:
            # Q查询集获取数据，实现手机号/邮箱/username都可以登录
            user = UserProfile.objects.get(Q(username=username) | Q(email=username) | Q(mobile=username))
            # 符合条件，加密密码，不可以明文存入
            if user.check_password(password):
                return user
        except Exception as e:
            return None


# 用户登录视图
class LoginView(View):

    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):

        login_form = LoginForm(request.POST)

        if login_form.is_valid():

            user_name = request.POST.get('username', None)
            pass_word = request.POST.get('password', None)

            user = authenticate(username=user_name, password=<PASSWORD>)

            if user is not None:
                if user.is_active:
                    login(request, user)
                    return HttpResponseRedirect(reverse('index'))
                else:
                    return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form})

            else:
                return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form})


        else:
            return render(request, 'login.html', {'login_form': login_form})


# 注销功能视图
class LogoutView(View):

    def get(self, request):
        logout(request)
        return HttpResponseRedirect(reverse('index'))


# 找回密码视图
class ForgetPwdView(View):

    def get(self, request):
        forget_form = ForgetPwdForm()
        return render(request, 'forgetpwd.html', {'forget_form': forget_form})

    def post(self, request):
        forget_form = ForgetPwdForm(request.POST)
        if forget_form.is_valid():
            email = request.POST.get('email', None)
            # 发送邮箱
            send_register_eamil(email, 'forget')
            return render(request, 'send_success.html')
        else:
            return render(request, 'forgetpwd.html', {'forget_form': forget_form})


# 邮箱修改密码视图
class ModifyPwdView(View):

    def post(self, request):
        modify_form = ModifyPwdForm(request.POST)
        if modify_form.is_valid():
            pwd1 = request.POST.get("password1", "")
            pwd2 = request.POST.get("password2", "")
            email = request.POST.get("email", "")
            if pwd1 != pwd2:
                return render(request, "password_reset.html", {"email": email, "msg": "密码不一致！"})
            user = UserProfile.objects.get(email=email)
            user.password = <PASSWORD>(<PASSWORD>)
            user.save()

            return render(request, "login.html")
        else:
            email = request.POST.get("email", "")
            return render(request, "password_reset.html", {"email": email, "modify_form": modify_form})


# 重置密码视图
class ResetView(View):
    def get(self, request, active_code):
        all_records = EmailVerifyRecord.objects.filter(code=active_code)
        if all_records:
            for record in all_records:
                email = record.email
                return render(request, "password_reset.html", {"email": email})
        else:
            return render(request, "active_fail.html")
        return render(request, "login.html")


# 跳转微博登录请求视图
def weibo_login(request):
    url = 'https://api.weibo.com/oauth2/' \
          'authorize?client_id=' + weibo.client_id + '&response_type=code&redirect_uri=' + weibo.redirect_uri
    return redirect(url)


# 微博登录后回调函数视图
class Bindemail(View):
    def get(self, request):
        code = request.GET.get('code')
        token = requests.post('https://api.weibo.com/oauth2/access_token?client_id=' + weibo.client_id + '&client_s'
                                                                                                         'ecret=' + weibo.client_secret + '&grant_type=authorization_'
                                                                                                                                          'code&redirect_uri=' + weibo.redirect_uri + '&code=' + code)
        text = json.loads(token.text)
        if token.status_code != 200:
            return redirect('/login/')
        access_token = text['access_token']
        uid = text['uid']
        url = 'https://api.weibo.com/2/users/show.json?access_token=' + access_token + '&uid=' + uid
        info2 = requests.get(url)
        info = info2.text
        info1 = json.loads(info)
        uid = info1['id']
        print(uid)
        name = info1['name']
        user = UserProfile.objects.filter(username=name).first()
        if user:
            login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            return redirect('/')
        a = UserProfile.objects.create(username=str(name), last_login=datetime.datetime.now(), is_active=True,
                                       password=<PASSWORD>))
        login(request, user, backend='django.contrib.auth.backends.ModelBackend')
        a.save()
        return redirect('/')
<file_sep>/apps/utils/weibo.py
# 引导用户授权地址
authorize_url = 'https://api.weibo.com/oauth2/authorize'
# 应用 App Key:
client_id = '2798589275'
# 应用 App Secret
client_secret = '9a4eb7e02c1fc3e50f32c7e1a10f81e1'
# App Key  App Secret可在应用的详情信息中找到

# 回调地址,线上地址
redirect_uri = 'http://127.0.0.1:8000/bindemail'
# 获取用户access_token地址
access_token_url = 'https://api.weibo.com/oauth2/access_token'
# 获取用户信息的 url
info_url = 'https://api.weibo.com/2/users/show.json'
<file_sep>/apps/competition/migrations/0001_initial.py
# Generated by Django 2.2.5 on 2020-08-05 20:02

import datetime
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='BankXlsx',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('bank_name', models.CharField(max_length=20, verbose_name='题库名称')),
                ('bank_type', models.CharField(max_length=10, verbose_name='题库所属分类')),
                ('bank_xlsx', models.CharField(max_length=50, verbose_name='上传文件名称')),
                ('bank_nums', models.IntegerField(verbose_name='题库总数')),
                ('bank_chio', models.IntegerField(verbose_name='单选题数')),
                ('bank_chios', models.IntegerField(verbose_name='多选题数')),
                ('bank_judge', models.IntegerField(verbose_name='判断题数')),
                ('bank_gap', models.IntegerField(verbose_name='填空题数')),
                ('bank_person', models.IntegerField(verbose_name='参与人数')),
                ('bank_bs', models.IntegerField(verbose_name='已出考试')),
            ],
            options={
                'verbose_name': '存储题库xlsx',
                'verbose_name_plural': '存储题库xlsx',
            },
        ),
        migrations.CreateModel(
            name='Classify',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('class_name', models.CharField(max_length=10, verbose_name='类型名')),
                ('class_head', models.ImageField(upload_to='web/static/images', verbose_name='类型头像')),
                ('class_desc', models.TextField(verbose_name='类型描述')),
            ],
            options={
                'verbose_name': '考试分类',
                'verbose_name_plural': '考试分类',
            },
        ),
        migrations.CreateModel(
            name='Game',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('game_name', models.CharField(max_length=50, verbose_name='考试名称')),
                ('game_nums', models.IntegerField(verbose_name='出题数量')),
                ('game_score', models.IntegerField(verbose_name='总分数')),
                ('create_time', models.DateTimeField(default=datetime.datetime.now, verbose_name='开始时间')),
                ('end_time', models.DateTimeField(verbose_name='结束时间')),
                ('time_bar', models.IntegerField(verbose_name='答题时间限制')),
                ('game_rule', models.TextField(verbose_name='考试规则')),
                ('bank_game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.BankXlsx', verbose_name='关联题库表')),
                ('class_game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.Classify', verbose_name='关联分类表')),
            ],
            options={
                'verbose_name': '考试配置',
                'verbose_name_plural': '考试配置',
            },
        ),
        migrations.CreateModel(
            name='Ques_judge',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('ques_type', models.CharField(default='判断题', max_length=5, verbose_name='题类型')),
                ('question', models.TextField(verbose_name='判断题目')),
                ('answer', models.TextField(verbose_name='答案')),
                ('score', models.IntegerField(verbose_name='分值')),
                ('judge_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.BankXlsx', verbose_name='关联题库表')),
            ],
            options={
                'verbose_name': '判断题',
                'verbose_name_plural': '判断题',
            },
        ),
        migrations.CreateModel(
            name='Ques_gap',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('ques_type', models.CharField(default='填空题', max_length=5, verbose_name='题类型')),
                ('question', models.TextField(verbose_name='填空题目')),
                ('answer', models.TextField(verbose_name='答案')),
                ('score', models.IntegerField(verbose_name='分值')),
                ('gap_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.BankXlsx', verbose_name='关联题库表')),
            ],
            options={
                'verbose_name': '填空题',
                'verbose_name_plural': '填空题',
            },
        ),
        migrations.CreateModel(
            name='Ques_choices',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('ques_type', models.CharField(default='多选题', max_length=5, verbose_name='题类型')),
                ('question', models.TextField(verbose_name='多选题目')),
                ('answer', models.TextField(verbose_name='答案')),
                ('choiceA', models.TextField(verbose_name='选项A')),
                ('choiceB', models.TextField(verbose_name='选项B')),
                ('choiceC', models.TextField(verbose_name='选项C')),
                ('choiceD', models.TextField(verbose_name='选项D')),
                ('score', models.IntegerField(verbose_name='分值')),
                ('chois_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.BankXlsx', verbose_name='关联题库表')),
            ],
            options={
                'verbose_name': '多选题',
                'verbose_name_plural': '多选题',
            },
        ),
        migrations.CreateModel(
            name='Ques_choice',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('ques_type', models.CharField(default='单选题', max_length=5, verbose_name='题类型')),
                ('question', models.TextField(verbose_name='单选题目')),
                ('answer', models.TextField(verbose_name='答案')),
                ('choiceA', models.TextField(verbose_name='选项A')),
                ('choiceB', models.TextField(verbose_name='选项B')),
                ('choiceC', models.TextField(verbose_name='选项C')),
                ('choiceD', models.TextField(verbose_name='选项D')),
                ('score', models.IntegerField(verbose_name='分值')),
                ('choi_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.BankXlsx', verbose_name='关联题库表')),
            ],
            options={
                'verbose_name': '单选题',
                'verbose_name_plural': '单选题',
            },
        ),
        migrations.CreateModel(
            name='GameInfo',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('total', models.IntegerField(verbose_name='得分')),
                ('times', models.CharField(max_length=20, verbose_name='用时')),
                ('yes', models.IntegerField(verbose_name='答对数量')),
                ('no', models.IntegerField(verbose_name='答错数量')),
                ('ginfo_game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.Game', verbose_name='关联考试配置表')),
                ('ginfo_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='关联用户表')),
            ],
            options={
                'verbose_name': '考试记录',
                'verbose_name_plural': '考试记录',
            },
        ),
    ]
<file_sep>/apps/competition/views.py
import json
import os
import random
import xlrd

from django.http import StreamingHttpResponse, Http404
from django.shortcuts import render, redirect

from Exam.settings import BACKUP_ROOT
from .models import *


# 首页
def index(request):
    class_datas = Classify.objects.all()
    return render(request, 'web/index.html', {'class_datas': class_datas})


# 考试分类详情
def class_detail(request, str):
    if request.user.is_authenticated:
        type = Classify.objects.get(class_name=str)
        game_datas = Game.objects.filter(class_game_id=type.id)

        return render(request, 'competition/games.html', {'game_datas': game_datas})
    else:
        return render(request, 'login.html')


# 录入、配置题库首页
def set_index(request):
    if request.user.is_authenticated:
        return render(request, 'setgames/index.html')
    else:
        return render(request, 'login.html')


# 录制题库
def set_bank(request):
    back_class = Classify.objects.all()
    if request.user.is_authenticated:
        if request.method == 'POST':
            bank_name = request.POST.get('bank_name', '')
            bank_type = request.POST.get('bank_type')
            bank_template = request.FILES.get('template', None)
            if bank_name and bank_type and bank_template:
                if not bank_template:
                    return render(request, 'err.html')
                if bank_template.name.split('.')[-1] not in ['xls', 'xlsx']:
                    return render(request, 'err.html')
                bank = BankXlsx.objects.filter(bank_name=bank_name)
                xlsx = BankXlsx.objects.filter(bank_xlsx=bank_template)
                if not bank and not xlsx:
                    # 上传题库到backup文件夹
                    bank_repo = open(os.path.join(BACKUP_ROOT, bank_template.name), 'wb+')
                    for chunk in bank_template.chunks():
                        bank_repo.write(chunk)
                    bank_repo.close()

                    # 取出xlsx格式 题库里面的题
                    data = xlrd.open_workbook('backup/{}'.format(bank_template))
                    table = data.sheets()[0]
                    num1 = 0
                    num2 = 0
                    num3 = 0
                    num4 = 0
                    for i in range(1, table.nrows):
                        ti_type = table.row_values(i)[0]
                        if ti_type == '单选题':
                            num1 += 1
                        elif ti_type == '多选题':
                            num2 += 1
                        elif ti_type == '判断题':
                            num3 += 1
                        elif ti_type == '填空题':
                            num4 += 1
                        else:
                            pass

                    # 添加到题库录入表
                    bank_data = BankXlsx(bank_name=bank_name, bank_type=bank_type, bank_xlsx=bank_template,
                                         bank_nums=table.nrows - 1, bank_chio=num1, bank_chios=num2, bank_judge=num3,
                                         bank_gap=num4, bank_person=0, bank_bs=0)
                    bank_data.save()

                    # 添加到各个类型的题表
                    bank1 = BankXlsx.objects.get(bank_name=bank_name)
                    for i in range(1, table.nrows):
                        ti_type = table.row_values(i)[0]
                        if ti_type == '单选题':
                            question = table.row_values(i)[1]
                            answer = table.row_values(i)[2]
                            A = table.row_values(i)[3]
                            B = table.row_values(i)[4]
                            C = table.row_values(i)[5]
                            D = table.row_values(i)[6]
                            if question and answer and A and B and C and D:
                                cun1 = Ques_choice(question=question, answer=answer, choiceA=A, choiceB=B, choiceC=C,
                                                   choiceD=D, score=5, choi_id_id=bank1.id)
                                cun1.save()
                            else:
                                pass
                        elif ti_type == '多选题':
                            question = table.row_values(i)[1]
                            answer = table.row_values(i)[2]
                            A = table.row_values(i)[3]
                            B = table.row_values(i)[4]
                            C = table.row_values(i)[5]
                            D = table.row_values(i)[6]
                            if question and answer and A and B and C and D:
                                cun2 = Ques_choices(question=question, answer=answer, choiceA=A, choiceB=B, choiceC=C,
                                                    choiceD=D, score=5, chois_id_id=bank1.id)
                                cun2.save()
                            else:
                                pass
                        elif ti_type == '判断题':
                            question = table.row_values(i)[1]
                            answer = table.row_values(i)[2]
                            if question and answer:
                                cun3 = Ques_judge(question=question, answer=answer, score=5, judge_id_id=bank1.id)
                                cun3.save()
                            else:
                                pass
                        elif ti_type == '填空题':
                            question = table.row_values(i)[1]
                            answer = table.row_values(i)[2]
                            if question and answer:
                                cun4 = Ques_gap(question=question, answer=answer, score=5, gap_id_id=bank1.id)
                                cun4.save()
                            else:
                                pass
                        else:
                            pass

                else:
                    return render(request, 'setgames/bank.html', {'class_datas': back_class, 'err': '题库名/题库文件已存在'})
            else:
                return render(request, 'setgames/bank.html', {'class_datas': back_class, 'err': '数据不完整'})
        return render(request, 'setgames/bank.html', {'class_datas': back_class})
    else:
        return render(request, 'login.html')


# 题库模板下载
def template_download(request):
    file_name = 'web/static/template/template.xlsx'
    try:
        # StreamingHttpResponse 下载大文件
        response = StreamingHttpResponse(open(file_name, 'rb'))
        # 防止输出文件乱码
        response['content_type'] = "application/octet-stream"  # 指示传输内容的类型
        response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_name)  # 以附件的形式下载保存
        return response
    except Exception:
        raise Http404


# 配置考试
def set_test(request):
    con = ''
    type = request.GET.get('type', '')
    bank = request.GET.get('bank')
    class_datas = Classify.objects.all()
    bank_datas = BankXlsx.objects.all()
    if request.user.is_authenticated:
        if type:
            bank_datas = BankXlsx.objects.filter(bank_type=type)
        if bank:
            bank_info = BankXlsx.objects.get(id=bank)
        else:
            bank_info = []
        if request.method == 'POST':
            name = request.POST.get('name')
            num = request.POST.get('num')
            score = request.POST.get('score')
            starttime = request.POST.get('starttime')
            endtime = request.POST.get('endtime')
            times = request.POST.get('times')
            ruleText = request.POST.get('ruleText', '')
            name_exists = Game.objects.filter(game_name=name)
            if type and bank:
                if name and num and score and starttime and endtime and times:
                    if not name_exists:
                        if int(num) <= bank_info.bank_nums:
                            g = Game(game_name=name, game_nums=num, game_score=score, create_time=starttime, end_time=endtime,
                                     time_bar=times, game_rule=ruleText, class_game_id=Classify.objects.get(class_name=type).id,
                                     bank_game_id=bank_info.id)
                            g.save()
                            bank_info.bank_bs += 1
                            bank_info.save()
                            return redirect('/detail/{}'.format(type))
                        else:
                            con = '数量超出所属题库的题数!'
                    else:
                        con = '该考试名已存在!'
                else:
                    con = '配置信息输入有误!'
            else:
                con = '请选择题库!'
        return render(request, 'setgames/game.html',
                      {'class_datas': class_datas, 'bank_datas': bank_datas, 'type_date': type, 'bank': bank,
                       'bank_info': bank_info, 'con': con})
    else:
        return render(request, 'login.html')


# 考试信息
def test_info(request, id):
    if request.user.is_authenticated:
        game_info = Game.objects.get(id=id)
        exists = GameInfo.objects.filter(ginfo_user_id=request.user.id,ginfo_game_id=game_info.id)
        return render(request, 'competition/index.html', {'game_info': game_info,'exists':exists})
    else:
        return render(request, 'login.html')


starttime1 = []
suiji_list = []
# 开始考试
def test_start(request, id):
    import datetime
    starttime2 = datetime.datetime.now()
    starttime1.append(starttime2)

    num = 0
    data_list = []
    test_data = Game.objects.get(id=id)
    times = test_data.time_bar - 1
    bank_data = test_data.bank_game
    choi_data = Ques_choice.objects.filter(choi_id=bank_data.id).all()
    chois_data = Ques_choices.objects.filter(chois_id=bank_data.id)
    judge_data = Ques_judge.objects.filter(judge_id=bank_data.id)
    gap_data = Ques_gap.objects.filter(gap_id=bank_data.id)
    for data1 in choi_data:
        num += 1
        data_list.append(data1)
    for data2 in chois_data:
        num += 1
        data_list.append(data2)
    for data3 in judge_data:
        num += 1
        data_list.append(data3)
    for data4 in gap_data:
        num += 1
        data_list.append(data4)

    data_list = random.sample(data_list, test_data.game_nums)
    suiji_list.append(data_list)
    if request.user.is_authenticated:
        if request.method == 'POST':
            my_score = 0
            endtime = datetime.datetime.now()
            my_time = endtime - starttime1[0]
            starttime1.clear()
            yes = 0
            for i, data in zip(range(te), suiji_list[0]):
                j = i + 1
                name = request.POST.getlist('{}'.format(j),[])
                if not name:
                    name = ['空']
                print(name)
                if data.ques_type == '多选题':
                    if name == list(data.answer):
                        yes += 1
                        my_score += data.score
                else:
                    if name[0] == data.answer:
                        yes += 1
                        my_score += data.score
            print(my_score)
            suiji_list.clear()

            # 做记录
            user = UserProfile.objects.get(id=request.user.id)
            no = test_data.game_nums - yes
            jilu = GameInfo(total=my_score, times=my_time, yes=yes, no=int(no), ginfo_game_id=test_data.id,
                            ginfo_user_id=user.id)
            jilu.save()
            # 参与考试人数
            person_nums = BankXlsx.objects.get(id=int(test_data.bank_game_id))
            person_nums.bank_person += 1
            person_nums.save()
            return redirect('set:result', user.id, test_data.id)

        return render(request, 'competition/game.html', {
            'test_data': test_data, 'bank_data': suiji_list[0],
            'times': json.dumps(times), 'leng': json.dumps(len(suiji_list[0]))
        })
    else:
        return render(request, 'login.html')


# 考试结果
def result(request, uid, gid):
    count = 0
    user = UserProfile.objects.get(id=uid)
    userinfo = GameInfo.objects.filter(ginfo_user_id=uid, ginfo_game_id=gid)[0]
    rank = GameInfo.objects.order_by('-total')
    rank = GameInfo.objects.filter(ginfo_game_id=gid).order_by('-total')
    for i in rank:
        count += 1
        if request.user.is_authenticated:
            if i.id == userinfo.id:
                print(count)

                return render(request, 'competition/result.html', {'user': user, 'userinfo': userinfo, 'count': count})
        else:
            return render(request, 'login.html')


# 成绩排行
def rank(request):
    game = request.GET.get('test')
    if request.user.is_authenticated:
        if not game:
            order = GameInfo.objects.order_by('-total')
        else:
            game_deatil = Game.objects.get(game_name=game)
            order = GameInfo.objects.filter(ginfo_game_id=game_deatil.id).order_by('-total')
        good = GameInfo.objects.filter(ginfo_user_id=request.user.id).order_by('-total')
        return render(request, 'competition/rank.html', {'game': game, 'order': order, 'good': good})
    else:
        return render(request, 'login.html')
<file_sep>/apps/competition/adminx.py
import xadmin

from .models import *


class ClassifyAdmin(object):
    list_display = ['class_name', 'class_head', 'class_desc']
    search_fields = ['class_name']
    list_filter = ['class_name']


class BankXlsxAdmin(object):
    list_display = ['bank_name', 'bank_type', 'bank_xlsx', 'bank_nums', 'bank_chio', 'bank_chios', 'bank_judge',
                    'bank_gap', 'bank_person', 'bank_bs']
    search_fields = ['bank_name', 'bank_type', 'bank_xlsx', 'bank_nums', 'bank_chio', 'bank_chios', 'bank_judge',
                     'bank_gap', 'bank_person', 'bank_bs']
    list_filter = ['bank_name', 'bank_type', 'bank_xlsx', 'bank_nums', 'bank_chio', 'bank_chios', 'bank_judge',
                   'bank_gap', 'bank_person', 'bank_bs']


class Ques_choiceAdmin(object):
    list_display = ['question', 'answer', 'choiceA', 'choiceB', 'choiceC', 'choiceD', 'score', 'choi_id']
    search_fields = ['question']
    list_filter = ['question', 'score']


class Ques_choicesAdmin(object):
    list_display = ['question', 'answer', 'choiceA', 'choiceB', 'choiceC', 'choiceD', 'score', 'chois_id']
    search_fields = ['question']
    list_filter = ['question', 'score']


class Ques_judgeAdmin(object):
    list_display = ['question', 'answer', 'score', 'judge_id']
    search_fields = ['question']
    list_filter = ['question', 'score']


class Ques_gapAdmin(object):
    list_display = ['question', 'answer', 'score', 'gap_id']
    search_fields = ['question']
    list_filter = ['question', 'score']


class GameAdmin(object):
    list_display = ['game_name', 'game_nums', 'game_score', 'create_time', 'end_time', 'time_bar', 'game_rule',
                    'class_game', 'bank_game']
    search_fields = ['game_name']
    list_filter = ['game_name', 'game_nums', 'game_score', 'time_bar', 'game_rule']


class GameInfoAdmin(object):
    list_display = ['total', 'times', 'ginfo_game', 'ginfo_user']
    search_fields = ['total']
    list_filter = ['total', 'times']


xadmin.site.register(Classify, ClassifyAdmin)
xadmin.site.register(BankXlsx, BankXlsxAdmin)
xadmin.site.register(Ques_choice, Ques_choiceAdmin)
xadmin.site.register(Ques_choices, Ques_choicesAdmin)
xadmin.site.register(Ques_judge, Ques_judgeAdmin)
xadmin.site.register(Ques_gap, Ques_gapAdmin)
xadmin.site.register(Game, GameAdmin)
xadmin.site.register(GameInfo, GameInfoAdmin)
<file_sep>/apps/competition/__init__.py
default_app_config = 'apps.competition.apps.CompetitionConfig'<file_sep>/Exam/settings.py
"""
Django settings for Exam project.

Generated by 'django-admin startproject' using Django 2.2.5.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""

import os
import sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, BASE_DIR)
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '^<KEY>'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

# 重载AUTH_USER_MODEL
AUTH_USER_MODEL = 'account.UserProfile'

# 云片网APIKEY
APIKEY = "<KEY>"
# Application definition

AUTHENTICATION_BACKENDS = (
    'account.views.CustomBackend',
    'social_core.backends.weibo.WeiboOAuth2',
    'django.contrib.auth.backends.ModelBackend',
)

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'apps.account',
    'apps.competition',
    'xadmin',
    'reversion',
    'crispy_forms',
    'social_django',
    'captcha',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'Exam.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'web/templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                # 第三方登陆
                'social_django.context_processors.backends',
                'social_django.context_processors.login_redirect',
            ],
        },
    },
]

WSGI_APPLICATION = 'Exam.wsgi.application'

# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'exam',
        'USER': 'root',
        'PASSWORD': '<PASSWORD>',
        'HOST': '192.168.127.12',
        'PORT': '3306',
    }
}

# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

# 静态文件
STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "web/static"),
    os.path.join(BASE_DIR, "static"),
)

STATIC_ROOT = os.path.join(BASE_DIR,'static1')

# # 默认图片
# MEDIA_URL = '/heads/'
# MEDIA_ROOT = os.path.join(BASE_DIR, 'web/heads')


# 上传文件
BACKUP_URL = '/backup/'
BACKUP_ROOT = os.path.join(BASE_DIR, 'backup')

EMAIL_HOST = "smtp.qq.com"  # SMTP服务器主机
EMAIL_PORT = 25  # 端口
EMAIL_HOST_USER = "<EMAIL>"  # 邮箱地址
EMAIL_HOST_PASSWORD = "<PASSWORD>"  # 密码
EMAIL_USE_TLS = True
EMAIL_FROM = "<EMAIL>"  # 邮箱地址
<file_sep>/apps/account/adminx.py
import xadmin
from xadmin import views
from .models import *

class BaseSettings(object):
    # xadmin基础配置
    enable_themes = True   #开启主题功能
    use_bootswatch = True


class GlobalSettings(object):
    # 设置网站标题和页脚、收起菜单栏
    site_title = '考试系统后台管理页面'
    site_footer = 'EXAM - Powered By A-team-Group'
    menu_style = 'accordion'


class EmailVerifyRecordAdmin(object):
    list_display = ['code', 'email', 'send_type', 'send_time']
    search_fields = ['code', 'email', 'send_type']
    list_filter = ['code', 'email', 'send_type', 'send_time']


xadmin.site.register(views.BaseAdminView,BaseSettings)
xadmin.site.register(views.CommAdminView,GlobalSettings)
xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)





<file_sep>/requirements.txt
certifi==2020.6.20
cffi==1.14.1
chardet==3.0.4
cryptography==3.0
defusedxml==0.6.0
diff-match-patch==20200713
Django==2.2.5
django-crispy-forms==1.9.2
django-filter==2.3.0
django-formtools==2.2
django-import-export==2.3.0
django-ranged-response==0.2.0
django-reversion==3.0.7
django-simple-captcha==0.5.12
et-xmlfile==1.0.1
future==0.18.2
httplib2==0.9.2
idna==2.10
importlib-metadata==1.7.0
jdcal==1.4.1
Markdown==3.2.2
MarkupPy==1.14
mysqlclient==2.0.1
oauthlib==3.1.0
odfpy==1.4.1
openpyxl==3.0.4
Pillow==7.2.0
pycparser==2.20
PyJWT==1.7.1
PyMySQL==0.10.0
python3-openid==3.2.0
pytz==2020.1
PyYAML==5.3.1
requests==2.24.0
requests-oauthlib==1.3.0
six==1.15.0
social-auth-app-django==4.0.0
social-auth-core==3.3.3
sqlparse==0.3.1
tablib==2.0.0
urllib3==1.25.10
xadmin==2.0.1
xlrd==1.2.0
xlwt==1.3.0
zipp==3.1.0
<file_sep>/apps/competition/apps.py
from django.apps import AppConfig


class CompetitionConfig(AppConfig):
    name = 'apps.competition'
    verbose_name = '考试系统'
<file_sep>/apps/competition/models.py
from datetime import datetime

from django.db import models

from apps.account.models import UserProfile


# 考试分类
class Classify(models.Model):
    class_name = models.CharField(u'类型名', max_length=10)
    class_head = models.ImageField(u'类型头像', upload_to='web/static/images')
    class_desc = models.TextField(u'类型描述')

    # meta信息，即后台栏目名
    class Meta:
        verbose_name = "考试分类"
        verbose_name_plural = verbose_name

    def __repr__(self):
        return self.class_name


# 存储题库xlsx表
class BankXlsx(models.Model):
    bank_name = models.CharField(u'题库名称', max_length=20)
    bank_type = models.CharField(u'题库所属分类', max_length=10)
    bank_xlsx = models.CharField(u'上传文件名称', max_length=50)
    bank_nums = models.IntegerField(u'题库总数')
    bank_chio = models.IntegerField(u'单选题数')
    bank_chios = models.IntegerField(u'多选题数')
    bank_judge = models.IntegerField(u'判断题数')
    bank_gap = models.IntegerField(u'填空题数')
    bank_person = models.IntegerField(u'参与人数')
    bank_bs = models.IntegerField(u'已出考试')

    class Meta:
        verbose_name = "存储题库xlsx"
        verbose_name_plural = verbose_name

    def __repr__(self):
        return self.bank_name


# 单选题表
class Ques_choice(models.Model):
    ques_type = models.CharField(u'题类型', default='单选题', max_length=5)
    question = models.TextField(u'单选题目')
    answer = models.TextField(u'答案')
    choiceA = models.TextField(u'选项A')
    choiceB = models.TextField(u'选项B')
    choiceC = models.TextField(u'选项C')
    choiceD = models.TextField(u'选项D')
    score = models.IntegerField(u'分值')
    choi_id = models.ForeignKey(BankXlsx, on_delete=models.CASCADE, verbose_name='关联题库表')

    class Meta:
        verbose_name = "单选题"
        verbose_name_plural = verbose_name

    def __repr__(self):
        return self.question


# 多选题表
class Ques_choices(models.Model):
    ques_type = models.CharField(u'题类型', default='多选题', max_length=5)
    question = models.TextField(u'多选题目')
    answer = models.TextField(u'答案')
    choiceA = models.TextField(u'选项A')
    choiceB = models.TextField(u'选项B')
    choiceC = models.TextField(u'选项C')
    choiceD = models.TextField(u'选项D')
    score = models.IntegerField(u'分值')
    chois_id = models.ForeignKey(BankXlsx, on_delete=models.CASCADE, verbose_name='关联题库表')

    class Meta:
        verbose_name = "多选题"
        verbose_name_plural = verbose_name

    def __repr__(self):
        return self.question


# 判断题
class Ques_judge(models.Model):
    ques_type = models.CharField(u'题类型', default='判断题', max_length=5)
    question = models.TextField(u'判断题目')
    answer = models.TextField(u'答案')
    score = models.IntegerField(u'分值')
    judge_id = models.ForeignKey(BankXlsx, on_delete=models.CASCADE, verbose_name='关联题库表')

    class Meta:
        verbose_name = "判断题"
        verbose_name_plural = verbose_name

    def __repr__(self):
        return self.question


# 填空题
class Ques_gap(models.Model):
    ques_type = models.CharField(u'题类型', default='填空题', max_length=5)
    question = models.TextField(u'填空题目')
    answer = models.TextField(u'答案')
    score = models.IntegerField(u'分值')
    gap_id = models.ForeignKey(BankXlsx, on_delete=models.CASCADE, verbose_name='关联题库表')

    class Meta:
        verbose_name = "填空题"
        verbose_name_plural = verbose_name

    def __repr__(self):
        return self.question


# 考试配置表
class Game(models.Model):
    game_name = models.CharField(u'考试名称', max_length=50)
    game_nums = models.IntegerField(u'出题数量')
    game_score = models.IntegerField(u'总分数')
    create_time = models.DateTimeField(u'开始时间', default=datetime.now)
    end_time = models.DateTimeField(u'结束时间')
    time_bar = models.IntegerField(u'答题时间限制')
    game_rule = models.TextField(u'考试规则')
    class_game = models.ForeignKey(Classify, on_delete=models.CASCADE, verbose_name='关联分类表')
    bank_game = models.ForeignKey(BankXlsx, on_delete=models.CASCADE, verbose_name='关联题库表')

    class Meta:
        verbose_name = "考试配置"
        verbose_name_plural = verbose_name

    def __repr__(self):
        return self.game_name


# 考试记录表
class GameInfo(models.Model):
    total = models.IntegerField(u'得分')
    times = models.CharField(u'用时', max_length=20)
    yes = models.IntegerField(u'答对数量')
    no = models.IntegerField(u'答错数量')
    ginfo_game = models.ForeignKey(Game, on_delete=models.CASCADE, verbose_name='关联考试配置表')
    ginfo_user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name='关联用户表')

    class Meta:
        verbose_name = "考试记录"
        verbose_name_plural = verbose_name
<file_sep>/Exam/urls.py
import xadmin
from django.urls import path, include, re_path
from django.views.decorators.csrf import csrf_exempt

from account.views import RegisterView, ActiveUserView, ForCodeView, LoginView, ForgetPwdView, LogoutView, \
    ResetView, ModifyPwdView, weibo_login, Bindemail
from apps.competition.views import *

urlpatterns = [
    # xadmin后台路由
    path('xadmin/', xadmin.site.urls),
    # 验证码
    path('captcha/', include('captcha.urls')),

    # 首页
    path('', index, name='index'),
    # 邮箱注册路由
    path('register/', RegisterView.as_view(), name='register'),
    # 邮件激活路由
    re_path('active/(?P<active_code>.*)/', ActiveUserView.as_view(), name='user_active'),
    # 手机号注册路由
    path('forcode/', csrf_exempt(ForCodeView.as_view()), name='forcode'),
    # 第三方登录
    path('social/', include('social_django.urls', namespace='social')),
    # 微博第三方登录路由
    path('weibo/', weibo_login, name="weibo"),
    # 微博登录后回调路由
    path("bindemail/", Bindemail.as_view(), name="Bindemail"),

    # 用户登陆路由
    path("login/", LoginView.as_view(), name="login"),
    # 用户注销路由
    path('logout/', LogoutView.as_view(), name="logout"),
    # 用户找回密码路由
    path('forget/', ForgetPwdView.as_view(), name='forget_pwd'),
    # 用户重置密码路由
    re_path('reset/(?P<active_code>.*)/', ResetView.as_view(), name='reset_pwd'),
    # 用户修改密码路由
    path('modify_pwd/', ModifyPwdView.as_view(), name='modify_pwd'),

    # 首页信息
    path('detail/<str>', class_detail, name='detail'),
    #
    path('set/', include('competition.urls', namespace='set')),
    #
    path('rank/', rank, name='rank'),

]
