gp 6 months ago
parent 739784a75b
commit 97b34a0763

3
.idea/.gitignore vendored

@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.11" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.11 (pythonProject1)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
<component name="PyCharmProfessionalAdvertiser">
<option name="shown" value="true" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/DjangoExam.iml" filepath="$PROJECT_DIR$/.idea/DjangoExam.iml" />
</modules>
</component>
</project>

@ -0,0 +1,2 @@
import pymysql
pymysql.install_as_MySQLdb()

@ -0,0 +1,16 @@
"""
ASGI config for DjangoExam project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DjangoExam.settings')
application = get_asgi_application()

@ -0,0 +1,136 @@
"""
Django settings for DjangoExam project.
Generated by 'django-admin startproject' using Django 3.2.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-$z1j^bnfbn$epv=k&o@s#7)((t3=k%5^1=x+=^)rj1gcw43tjp'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'exam', #添加此项
]
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 = 'DjangoExam.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, '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',
],
},
},
]
WSGI_APPLICATION = 'DjangoExam.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'djangoexam', # 数据库名称
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'USER': 'root', # 数据库用户名
'PASSWORD': '12345678', # 数据库密码
}
}
# Password validation
# https://docs.djangoproject.com/en/3.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/3.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'), # 添加此项
]
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

@ -0,0 +1,31 @@
"""DjangoExam URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf.urls import url
from exam import views
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^$',views.index),#默认访问首页
url('index/',views.index,name='index'),
url('studentLogin/',views.studentLogin,name='studentLogin'),#学生登录
url('startExam/',views.startExam,name='startExam'),#开始考试
url('calculateGrade/',views.calculateGrade,name='calculateGrade'),#考试评分
path('stulogout/',views.stulogout,name='stulogout'), # 学生退出登录
path('userfile/',views.userfile,name='userfile'), # 个人信息
path('examinfo/',views.examinfo,name='examinfo'), # 考试信息
]

@ -0,0 +1,16 @@
"""
WSGI config for DjangoExam project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DjangoExam.settings')
application = get_wsgi_application()

@ -0,0 +1,11 @@
from django.contrib import admin
# Register your models here.
# 修改名称
admin.site.site_header='在线考试系统后台'
admin.site.site_title='在线考试系统'
from exam.models import Academy,Major, Course,Student,QuestionBank,TestPaper,Record
admin.site.register([Academy,Major,Course,Student,QuestionBank,TestPaper,Record])

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ExamConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'exam'

@ -0,0 +1,118 @@
# Generated by Django 3.2 on 2021-10-22 08:27
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Academy',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='序号')),
('name', models.CharField(max_length=20, verbose_name='学院')),
],
options={
'verbose_name': '学院',
'verbose_name_plural': '学院',
},
),
migrations.CreateModel(
name='Course',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='序号')),
('course_id', models.CharField(max_length=10, verbose_name='课程号')),
('course_name', models.CharField(max_length=30, verbose_name='课程名称')),
],
options={
'verbose_name': '课程',
'verbose_name_plural': '课程',
},
),
migrations.CreateModel(
name='Major',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='序号')),
('major', models.CharField(max_length=30, verbose_name='专业')),
('academy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.academy', verbose_name='学院')),
],
options={
'verbose_name': '专业',
'verbose_name_plural': '专业',
},
),
migrations.CreateModel(
name='QuestionBank',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='序号')),
('title', models.TextField(verbose_name='题目')),
('qtype', models.CharField(choices=[('单选', '单选'), ('多选', '多选'), ('判断', '判断')], max_length=40, verbose_name='题目类型')),
('a', models.CharField(max_length=40, verbose_name='A选项')),
('b', models.CharField(max_length=40, verbose_name='B选项')),
('c', models.CharField(max_length=40, verbose_name='C选项')),
('d', models.CharField(max_length=40, verbose_name='D选项')),
('answer', models.CharField(choices=[('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D')], max_length=4, verbose_name='答案')),
('difficulty', models.CharField(choices=[('easy', '简单'), ('middle', '中等'), ('difficult', '')], max_length=10, verbose_name='难度')),
('score', models.IntegerField(verbose_name='分值')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.course', verbose_name='科目')),
('major', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.major', verbose_name='专业')),
],
options={
'verbose_name': '题库',
'verbose_name_plural': '题库',
},
),
migrations.CreateModel(
name='TestPaper',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='序号')),
('title', models.CharField(max_length=40, unique=True, verbose_name='题目')),
('time', models.IntegerField(help_text='单位是分钟', verbose_name='考试时长')),
('examtime', models.DateTimeField(verbose_name='上次考试时间')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.course', verbose_name='科目')),
('major', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.major', verbose_name='考卷适合专业')),
('pid', models.ManyToManyField(to='exam.QuestionBank')),
],
options={
'verbose_name': '试卷',
'verbose_name_plural': '试卷',
},
),
migrations.CreateModel(
name='Student',
fields=[
('sid', models.CharField(max_length=12, primary_key=True, serialize=False, verbose_name='学号')),
('name', models.CharField(max_length=20, unique=True, verbose_name='姓名')),
('sex', models.BooleanField(choices=[(0, ''), (1, '')], verbose_name='性别')),
('age', models.IntegerField(verbose_name='年龄')),
('sclass', models.CharField(help_text='例如: 17-03', max_length=20, verbose_name='班级')),
('email', models.EmailField(default=None, max_length=254, verbose_name='邮箱')),
('pwd', models.CharField(max_length=20, verbose_name='密码')),
('academy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.academy', verbose_name='学院')),
('major', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='exam.major', verbose_name='专业')),
],
options={
'verbose_name': '学生',
'verbose_name_plural': '学生信息表',
},
),
migrations.CreateModel(
name='Record',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='序号')),
('grade', models.FloatField(verbose_name='成绩')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stu_course', to='exam.course', verbose_name='考试科目')),
('sid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stu_xuehao', to='exam.student', verbose_name='学号')),
],
options={
'verbose_name': '学生成绩',
'verbose_name_plural': '学生成绩',
},
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-10-26 02:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('exam', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='record',
name='rtime',
field=models.DateTimeField(blank=True, null=True, verbose_name='考试时间'),
),
]

@ -0,0 +1,114 @@
from django.db import models
# 学院表
class Academy(models.Model):
id = models.AutoField('序号',primary_key=True)
name = models.CharField('学院',max_length=20)
# 修改显示的表的名字
class Meta:
verbose_name = '学院'
verbose_name_plural = '学院'
def __str__(self):
return self.name
# 专业表
class Major(models.Model):
id = models.AutoField('序号',primary_key=True)
academy = models.ForeignKey(Academy,on_delete=models.CASCADE,verbose_name='学院')
major = models.CharField('专业',max_length=30)
# 修改显示的表的名字
class Meta:
verbose_name = '专业'
verbose_name_plural = '专业'
def __str__(self):
return self.major
# 课程表
class Course(models.Model):
id = models.AutoField('序号',primary_key=True)
course_id = models.CharField('课程号',max_length=10)
course_name = models.CharField('课程名称',max_length=30)
class Meta:
verbose_name = '课程'
verbose_name_plural = '课程'
def __str__(self):
return self.course_name
# 学生表
class Student(models.Model):
sid = models.CharField('学号',max_length=12,primary_key=True)
name = models.CharField('姓名',max_length=20,unique=True)
sex = models.BooleanField('性别',choices=((0,''),(1,'')))
age = models.IntegerField('年龄')
academy = models.ForeignKey(Academy,on_delete=models.CASCADE,verbose_name='学院')
major = models.ForeignKey(Major,on_delete=models.CASCADE,verbose_name='专业')
sclass = models.CharField('班级',max_length=20,help_text='例如: 17-03')
email = models.EmailField('邮箱',default=None) # 默认为空 唯一值
pwd = models.CharField('密码',max_length=20)
# 修改显示的表的名字
class Meta:
verbose_name = '学生'
verbose_name_plural = '学生信息表'
def __str__(self):
return self.sid
# 题库表
class QuestionBank(models.Model):
id = models.AutoField('序号',primary_key=True)
major = models.ForeignKey(Major,on_delete=models.CASCADE,verbose_name='专业')
course = models.ForeignKey(Course,on_delete=models.CASCADE,verbose_name='科目')
title = models.TextField('题目')
qtype = models.CharField('题目类型',choices=(('单选','单选'),('多选','多选'),('判断','判断')),max_length=40)
a = models.CharField('A选项',max_length=40)
b = models.CharField('B选项',max_length=40)
c = models.CharField('C选项',max_length=40)
d = models.CharField('D选项',max_length=40)
answer = models.CharField('答案',choices=(('A','A'),('B','B'),('C','C'),('D','D')),max_length=4)
difficulty = models.CharField('难度',choices=(('easy','简单'),('middle','中等'),('difficult','')),max_length=10)
score = models.IntegerField('分值')
class Meta:
# 选择这个表之后显示的名字
verbose_name = '题库'
# 显示的表名
verbose_name_plural = '题库'
def __str__(self):
return '<%s:%s>' % (self.course, self.title)
# 试卷表
class TestPaper(models.Model):
id = models.AutoField('序号',primary_key=True)
title = models.CharField('题目',max_length=40,unique=True)
pid = models.ManyToManyField(QuestionBank)
course = models.ForeignKey(Course,on_delete=models.CASCADE,verbose_name='科目')
major = models.ForeignKey(Major,on_delete=models.CASCADE,verbose_name='考卷适合专业')
time = models.IntegerField('考试时长',help_text='单位是分钟')
examtime = models.DateTimeField('上次考试时间')
class Meta:
# 选择这个表之后显示的名字
verbose_name = '试卷'
verbose_name_plural = '试卷'
# # 学生成绩表
class Record(models.Model):
id = models.AutoField('序号',primary_key=True)
sid = models.ForeignKey(Student,on_delete=models.CASCADE,verbose_name='学号',related_name='stu_xuehao')
course = models.ForeignKey(Course,on_delete=models.CASCADE,verbose_name='考试科目',related_name='stu_course')
grade = models.FloatField('成绩')
rtime = models.DateTimeField('考试时间',blank=True,null=True)
class Meta:
verbose_name = '学生成绩'
verbose_name_plural = '学生成绩'
def __str__(self):
return '<%s:%s>' % (self.sid,self.grade)

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,20 @@
from django.urls import path
from . import views
app_name = 'exam'
urlpatterns = [
# path('',views.index,name='index'),
# path('stulogout/',views.stulogout,name='stulogout'), # 学生退出登录
# path('userfile/',views.userfile,name='userfile'), # 个人信息
# path('examinfo/',views.examinfo,name='examinfo'), # 个人信息
# path('tealogout/',views.tealogout,name='tealogout'), # 学生退出登录
# path('emailsend/',views.email_send,name='emailsend'), # 发送验证码
# path('sendemail/',views.send_my_email,name='sendemail'), # 发送验证码
# path('studentLogin/',views.studentLogin,name='studentLogin'), # 学生登录
# path('teacherLogin/',views.teacherLogin,name='teacherLogin'), # 老师登录
# path('startExam/',views.startExam,name='startExam'), # 开始考试
# path('calculateGrade/',views.calculateGrade,name='calculateGrade'), # 交卷计算成绩
# path('showGrade/',views.showGrade,name='showGrade'), # 成绩统计
]

@ -0,0 +1,124 @@
from datetime import datetime
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.urls import reverse
from exam import models
# Create your views here.
# 学生登录
def studentLogin(request):
if request.method == 'POST':
# 获取表单信息
sid = request.POST.get('sid')
password = request.POST.get('password')
print("sid", sid, "password", password)
# 通过学号获取该学生实体
student = models.Student.objects.get(sid=sid)
print(student)
if password == student.pwd: # 登录成功
request.session['username']=sid #user的值发送给session里的username
request.session['is_login']=True #认证为真
# 查询考试信息
paper = models.TestPaper.objects.filter(major=student.major)
# 查询成绩信息
grade = models.Record.objects.filter(sid=student.sid)
# 渲染index模板
return render(request, 'index.html', {'student': student, 'paper': paper, 'grade': grade})
else:
return render(request,'login.html',{'message':'密码不正确'})
elif request.method == 'GET':
return render(request, 'login.html')
else:
return HttpResponse("请使用GET或POST请求数据")
# 首页
def index(request):
if request.session.get('is_login',None): #若session认证为真
username = request.session.get('username',None)
print(username )
student = models.Student.objects.get(sid=username)
# 查询考试信息
paper = models.TestPaper.objects.filter(major=student.major)
return render(request, 'index.html',{'student': student,'paper': paper})
else:
return render(request, 'index.html')
def userfile(request):
if request.session.get('is_login',None): #若session认证为真
username = request.session.get('username',None)
print(username )
student = models.Student.objects.get(sid=username)
# 查询考试信息
paper = models.TestPaper.objects.filter(major=student.major)
return render(request, 'userfile.html',{'student': student})
#学生退出登录
def stulogout(request):
# logout(request)
request.session.clear()
url = reverse('index')
return redirect(url)
# 考试信息
def startExam(request):
sid = request.GET.get('sid')
title = request.GET.get('title') # 试卷名字 唯一
subject1 = request.GET.get('subject') # 考试科目
# 获取学生信息
student = models.Student.objects.get(sid=sid)
# 试卷信息
paper = models.TestPaper.objects.filter(title=title,course__course_name=subject1)
context = {
'student': student,
'paper': paper,
'title': title,
'subject':subject1,
'count': paper.count() # 数据表中数据的条数
}
return render(request, 'exam.html', context=context)
def examinfo(request):
if request.session.get('is_login',None): #若session认证为真
username = request.session.get('username',None)
student = models.Student.objects.get(sid=username)
# 查询成绩信息
grade = models.Record.objects.filter(sid=student.sid)
return render(request, 'examinfo.html',{'student': student,'grade': grade})
else:
return render(request, 'examinfo.html')
# 计算考试成绩
def calculateGrade(request):
if request.method == 'POST':
sid = request.POST.get('sid')
subject1 = request.POST.get('subject')
student = models.Student.objects.get(sid=sid)
paper = models.TestPaper.objects.filter(major=student.major)
grade = models.Record.objects.filter(sid=student.sid)
course = models.Course.objects.filter(course_name=subject1).first()
now = datetime.now()
# 计算考试成绩
questions = models.TestPaper.objects.filter(course__course_name=subject1).\
values('pid').values('pid__id','pid__answer','pid__score')
stu_grade = 0 # 初始化一个成绩
for p in questions:
qid = str(p['pid__id'])
stu_ans = request.POST.get(qid)
cor_ans = p['pid__answer']
if stu_ans == cor_ans:
stu_grade += p['pid__score']
models.Record.objects.create(sid_id=sid, course_id=course.id, grade=stu_grade,rtime=now)
context = {
'student': student,
'paper': paper,
'grade': grade
}
return render(request, 'index.html', context=context)

@ -0,0 +1,22 @@
#!/usr/bin/.env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DjangoExam.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,331 @@
/*!
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@-ms-viewport {
width: device-width;
}
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
dfn {
font-style: italic;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg {
overflow: hidden;
vertical-align: middle;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,35 @@
<!-- 载入静态文件-->
{% load static %}
<!-- 网站主语言 -->
<html lang="zh-cn">
<head>
<!-- 网站采用的字符编码 -->
<meta charset="utf-8">
<!-- 预留网站标题的位置 -->
<title>{% block title %}{% endblock %}</title>
<!-- 引入bootstrap的css文件 -->
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
</head>
<body>
<!-- 引入导航栏 -->
{% include 'header.html' %}
<!-- 预留具体页面的位置 -->
{% block content %}{% endblock content %}
<!-- 引入注脚 -->
{% include 'footer.html' %}
<!-- bootstrap.js 依赖 jquery.js 和popper.js因此在这里引入 -->
<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
<script src="{% static 'jquery/jquery-3.6.0.js' %}"></script>
<!--
popper.js 采用 cdn 远程引入,意思是你不需要把它下载到本地。
在实际的开发中推荐静态文件尽量都使用 cdn 的形式。
教程采用本地引入是为了让读者了解静态文件本地部署的流程。
-->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1-lts/dist/umd/popper.min.js"></script>
<!-- 引入bootstrap的js文件 -->
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
</body>
</html>

@ -0,0 +1,118 @@
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
在线考试系统
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<div class="container">
<div class="row">
<p class="col-12 mt-4 mb-4">
<a>当前科目:{{ subject }}</a>
<a>当前考试用户:{{ student.name }}</a>
考试时长:
<b class="alt-1"> 01:40 </b>
</p>
</div>
<div class="container">
<form action="/calculateGrade/" method="post">
<input type="hidden" name="sid" value="{{ student.sid }}">
<input type="hidden" name="subject" value="{{ subject }}">
<div class="row">
<b class="bi bi-alarm" id="timer" style="color:red"></b>
<p>&emsp;</p>
<font><input type="submit" name="tijiao" value="交卷"></font>
</div>
<div class="row">
<div class="col-12 mt-4 mb-4">
<h2>单选题</h2>
<p>
<span></span><i class="content_lit">10</i><span>题,</span>
<span>合计</span><i class="content_fs">10</i><span></span>
</p>
</div>
</div>
<div class="container">
{% for paper1 in paper %}
{% for test in paper1.pid.all %}
<div class="row bg-light">
<div class="col-12">
<!-- <div class="card">-->
<!-- <div class="card-body h-10">-->
<div id="{{ forloop.counter }}">
<b>{{ forloop.counter}}.</b><span>({{ test.score }}分)</span>
<b>{{ test.title }}</b>
<ul>
<li class="option">
<input type="radio" class="radioOrCheck" name="{{ test.id }}"
value="A"/>
<label>A.
<p class="ue" style="display: inline;">{{ test.a }}</p>
</label>
</li>
<li class="option">
<input type="radio" class="radioOrCheck" name="{{ test.id }}"
value="B"/>
<label>
B.<p class="ue" style="display: inline;">{{ test.b }}</p>
</label>
</li>
<li class="option">
<input type="radio" class="radioOrCheck" name="{{ test.id }}"
value="C"/>
<label>
C.<p class="ue" style="display: inline;">{{ test.c}}</p>
</label>
</li>
<li class="option">
<input type="radio" class="radioOrCheck" name="{{ test.id }}"
value="D"/>
<label>
D.<p class="ue" style="display: inline;">{{ test.d }}</p>
</label>
</li>
</ul>
<!-- </div>-->
<!-- </div>-->
</div>
</div>
</div>
{% endfor %}
{% endfor %}
</div>
</form>
</div>
<!--nr end-->
<div class="foot"></div>
</div>
<SCRIPT type="text/javascript">
var maxtime = 1000; //一个小时,按秒计算,自己调整!
$(function () {
timer = setInterval("CountDown()", 1000);
})
function CountDown() {
if (maxtime >= 0) {
minutes = Math.floor(maxtime / 60);
seconds = Math.floor(maxtime % 60);
msg = "距离结束还有" + minutes + "分" + seconds + "秒";
{#document.all["timer"].innerHTML = msg;#}
document.getElementById('timer').innerHTML = msg;
{#if (maxtime == 5 * 60)alert("还剩5分钟");#}
--maxtime;
} else{
{#clearInterval(timer);#}
{#alert("时间到,结束!");#}
document.getElementsByClassName('tijiao').click();
}
}
</SCRIPT>
{% endblock content %}

@ -0,0 +1,41 @@
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
在线考试系统
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<div class="container">
<div class="container">
<br>
<h3>考试成绩</h3>
<p></p>
<table class="table">
<thead>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>考试时间</th>
</tr>
</thead>
<tbody>
{% for grade1 in grade %}
<tr class="table">
<td>{{ student.name }}</td>
<td>{{ grade1.course }}</td>
<td>{{ grade1.grade }}</td>
<td>{{ grade1.rtime|date:"Y-m-d H:i:s"}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock content %}

@ -0,0 +1,10 @@
{% load static %}
<!-- Footer -->
<div>
<br><br><br>
</div>
<footer class="py-3 bg-dark fixed-bottom">
<div class="container">
<p class="m-0 text-center text-white">Copyright &copy; DjangoExam 2023</p>
</div>
</footer>

@ -0,0 +1,42 @@
<!-- 定义导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<!-- 导航栏商标 -->
<a class="navbar-brand" href="#">在线考试</a>
<!-- 导航入口 -->
<div>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/index/">首页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/examinfo/">考试记录</a>
</li>
<!-- Django的 if 模板语句 -->
{% if request.session.username %}
<!-- 如果用户已经登录,则显示用户名下拉框 -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ request.session.username }}
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="/userfile/">个人信息</a>
<a class="dropdown-item" href="/stulogout/">退出登录</a>
</div>
</li>
<!-- 如果用户未登录,则显示 “登录” -->
{% else %}
<li class="nav-item">
<a class="nav-link" href="/studentLogin/">登录</a>
</li>
<!-- if 语句在这里结束 -->
{% endif %}
<li class="nav-item">
<a class="nav-link" href="/admin">管理员</a>
</li>
</ul>
</div>
</div>
</nav>

@ -0,0 +1,38 @@
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
在线考试系统
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<div class="container">
<div class="container">
<br>
<h3>考试信息</h3>
<div class="container">
<div class="row mt-4">
{% for paper1 in paper %}
<!-- 文章内容 -->
<div class="col-6 mb-6">
<!-- 卡片容器 -->
<div class="card">
<!-- 标题 -->
<h4 class="card-header">{{ paper1.title }}</h4>
<!-- 摘要 -->
<div class="card-body">
<h4 class="card-title">{{ paper1.course }}</h4>
<p class="card-text">{{ paper1.examtime }}</p>
<a href="/startExam/?sid={{ student.sid }}&title={{ paper1.title }}&subject={{ paper1.course }}" class="card-link">开始考试</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<p></p>
</div>
</div>
{% endblock content %}

@ -0,0 +1,30 @@
{% extends "base.html" %} {% load static %}
{% block title %} 登录 {% endblock title %}
{% block content %}
<div class="container">
<div class="row justify-content-md-center">
<div class="col-4">
<br>
<form method="post" action="/studentLogin/">
<!-- {% csrf_token %}-->
<!-- 账号 -->
<div class="form-group">
<label >学生学号</label>
<input type="text" class="form-control" name="sid" placeholder="输入学号">
</div>
<!-- 密码 -->
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" name="password" placeholder="输入密码">
</div>
<!-- 提交按钮 -->
<button type="submit" class="btn btn-primary">登录</button>
<div class="form-group">
<br>
<br>
</div>
</form>
</div>
</div>
</div>
{% endblock content %}

@ -0,0 +1,55 @@
{% extends "base.html" %} {% load static %}
{% block title %} 用户信息 {% endblock title %}
{% block content %}
<div class="container">
<br>
<div class="row justify-content-md-center">
<div class="col-8">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">个人信息</h3>
</div>
<div class="panel-body">
<table class="table table-borderless">
<tbody>
<tr>
<td>学号</td>
<td>{{ student.sid }}</td>
</tr>
<tr class="table">
<td>姓名</td>
<td>{{ student.name }}</td>
</tr>
<tr class="table">
<td>性别</td>
{% if student.sex%}
<td></td>
{% else %}
<td></td>
{% endif %}
</tr>
<tr class="table">
<td>学院</td>
<td>{{ student.academy }}</td>
</tr>
<tr class="table">
<td>专业</td>
<td>{{ student.major }}</td>
</tr>
<tr class="table">
<td>邮箱地址</td>
<td>{{ student.email }}</td>
</tr>
<tr class="table">
<td>出生日期</td>
<td>{{ student.birth }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
Loading…
Cancel
Save