Compare commits

...

33 Commits

Author SHA1 Message Date
32e1af6bf2 更新 requirements.txt 2023-12-31 00:23:03 +08:00
b5dd4b6d7d 1 2023-12-30 22:44:36 +08:00
cf2ff28e58 更新 login.html 2023-12-30 21:31:47 +08:00
58c4b85e6a 删除 requirements.txt 2023-12-29 17:31:25 +08:00
b6fc41994e 创建 ReceiveExcelTest.py 2023-12-29 17:31:23 +08:00
1a08ba460c 创建 StudentSignInTest.py 2023-12-29 17:25:11 +08:00
692631e395 创建 TeacherSignInTest.py 2023-12-29 17:25:09 +08:00
1174b225a7 创建 TestGetHtml.py 2023-12-29 17:25:05 +08:00
ded28f6a1e 创建 TestLogout.py 2023-12-29 16:31:56 +08:00
4e64fb2422 创建 TestGetMenu.py 2023-12-29 16:31:55 +08:00
eb074e5dd5 更新 database_manager.py 2023-12-29 16:07:22 +08:00
6ef3faf149 创建 TestLogin.py 2023-12-29 16:07:20 +08:00
9b519f4650 更新 login.html 2023-12-29 16:04:57 +08:00
d6bef5812b 删除 init.py 2023-12-29 16:04:54 +08:00
8e8fccbb1a 创建 app.py 2023-12-29 16:04:52 +08:00
237f0acf8d 删除 app.py 2023-12-29 16:04:50 +08:00
44ce96c7d9 更新 views.py 2023-12-29 16:04:42 +08:00
dae0ce21be 更新 announcement.html 2023-12-29 15:19:49 +08:00
a94dc7b415 更新 app.py 2023-12-29 15:06:04 +08:00
ca44cf0eaa 创建 requirements.txt 2023-12-29 15:05:23 +08:00
18c3968e59 更新 database_manager.py 2023-12-29 15:02:57 +08:00
5ce9ee1b1e 更新 mysql.sql 2023-12-29 14:47:07 +08:00
2596680c3a 更新 views.py 2023-12-29 14:47:05 +08:00
ff030444e4 更新 mysql.sql 2023-12-29 14:39:51 +08:00
e14688471a 更新 mysql.sql 2023-12-29 14:31:29 +08:00
c554d2d7af 创建 parse_table.py 2023-12-29 14:27:59 +08:00
9296b70ac3 更新 mysql.sql 2023-12-29 14:27:57 +08:00
cff2b66bc0 更新 template.xlsx 2023-12-29 14:27:54 +08:00
9d68073dd5 更新 database_manager.py 2023-12-29 14:27:52 +08:00
894d075d55 更新 views.py 2023-12-29 14:27:49 +08:00
8021f15e58 更新 import-class.html 2023-12-29 14:27:46 +08:00
b5461dbff4 更新 attendance-teacher.html 2023-12-29 14:27:44 +08:00
aab814a23a 更新 get_teacher_attendance_table.js 2023-12-29 14:27:38 +08:00
28 changed files with 860 additions and 261 deletions

View File

@@ -0,0 +1,2 @@
import views

View File

File diff suppressed because one or more lines are too long

View File

@@ -9,10 +9,10 @@ layui.use(['laypage', 'element', 'jquery', 'dropdown'], function () {
response.data.forEach(function (item) { response.data.forEach(function (item) {
var $row = $('<tr></tr>'); var $row = $('<tr></tr>');
$row.append(`<td>${item.course_id}</td>`);
$row.append(`<td>${item.course_name}</td>`); $row.append(`<td>${item.course_name}</td>`);
$row.append(`<td>${item.course_code}</td>`); $row.append(`<td>${item.class_name}</td>`);
$row.append(`<td>${item.credits}</td>`); $row.append(`<td>${item.major}</td>`);
$row.append(`<td>${item.class_name + item.major}</td>`);
$row.append(`<td><div class="layui-btn-container"> $row.append(`<td><div class="layui-btn-container">
<button type="button" class="layui-btn btn-sign-in" <button type="button" class="layui-btn btn-sign-in"
data-course-id="${item.course_id}" data-course-id="${item.course_id}"

View File

@@ -1,11 +1,11 @@
$.get('/api/menu', function (menuItems) { $.get('/api/menu', function (menuItems) {
var menuList = $('.layui-nav.layui-nav-tree'); var menuList = $('.layui-nav.layui-nav-tree');
menuList.empty(); menuList.empty();
menuItems.forEach(function (item) { menuItems.forEach(function (item) {
var href = item.path || 'javascript:void(0);'; // 提供一个默认值 var href = item.path || 'javascript:void(0);'; // 提供一个默认值
menuList.append('<li class="layui-nav-item"><a href="' + href + '">' + item.name + '</a></li>'); menuList.append('<li class="layui-nav-item"><a href="' + href + '">' + item.name + '</a></li>');
});
layui.element.render('nav', 'test');
}); });
layui.element.render('nav', 'test');
});

View File

@@ -7,6 +7,47 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<link href="static/css/layui.css" rel="stylesheet"/> <link href="static/css/layui.css" rel="stylesheet"/>
<style>
/* 容器样式 */
.layui-container {
padding: 20px;
background-color: #f7f7f7;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
/* 标题样式 */
.layui-header {
margin-bottom: 20px;
font-size: 2em; /* 更大的标题字体 */
color: #333;
border-bottom: 2px solid #e2e2e2;
padding-bottom: 10px;
}
/* 课程信息样式 */
.layui-field-box p {
font-size: 1.2em; /* 更大的文字 */
margin: 10px 0; /* 调整段落间距 */
padding: 10px; /* 内边距 */
background-color: #fff; /* 背景色 */
border-radius: 4px; /* 轻微的圆角 */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); /* 细微的阴影 */
}
/* 字段集合样式调整 */
fieldset.layui-elem-field {
border-color: #ddd; /* 更淡的边框颜色 */
}
legend {
font-size: 1.4em; /* Legend文字大小 */
color: #333; /* 文字颜色 */
}
</style>
</head> </head>
<body> <body>
@@ -21,7 +62,7 @@
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png" src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
class="layui-nav-img" class="layui-nav-img"
/> />
{{ session.nickname }} {{ session.name }}
</a> </a>
<dl class="layui-nav-child"> <dl class="layui-nav-child">
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile --> <dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->

View File

@@ -91,32 +91,11 @@
</div> </div>
</div> </div>
<div class="layui-body"> <div class="layui-body">
<div> <div class="layui-container">
<!-- 动态显示当前时间的标题 --> <div class="layui-row">
<h2 id="attendance-reminder">签到提醒 </h2> </div>
<h2 id="current-time">当前时间</h2> <div id="course-list" class="layui-row">
<table class="layui-table calendar-table" id="calendar"> <!-- 课程列表将在这里生成 -->
<!-- 日历的头部 -->
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<!-- 日历的主体部分 -->
<tbody id="calendar-body">
<!-- 动态生成日历的日期部分 -->
</tbody>
</table>
<!-- 提醒内容 -->
<div class="reminder">
<span><span class="color-box" style="background-color: green;"></span>绿色代表当月已签到</span><br>
<span><span class="color-box" style="background-color: #FFA07A;"></span>颜色代表本月需要签到</span>
</div> </div>
</div> </div>
</div> </div>
@@ -127,59 +106,34 @@
<script src="/static/js/menu.js"></script> <script src="/static/js/menu.js"></script>
<script src="/static/js/logout.js"></script> <script src="/static/js/logout.js"></script>
<script> <script>
layui.use(['layer'], function () { document.addEventListener("DOMContentLoaded", function () {
var layer = layui.layer; // 模拟从后端获取的课程数据
let courses = [
{name: "线性代数", time: "14:00", description: "线性代数是数学的一个基础分支,关注线性方程组、向量空间、矩阵理论及线性变换等概念。本课程将带领学生探索向量和矩阵的运算,理解行列式、特征值和特征向量的计算,以及线性空间和子空间的概念。透过一系列的应用实例,如图像处理和机器学习,学生将学会如何将理论应用于实际问题中,为进一步学习高等数学和工程应用打下坚实的基础。", room: "101教室"},
{name: "数据结构", time: "15:00", description: "数据结构是计算机科学中的一个重要领域,涉及组织、管理和存储数据的方式,以便高效地访问和修改。本课程覆盖了从基本的数据结构如数组、链表、栈和队列,到更高级的结构如树、图、散列表和堆。通过理论学习与实际编程相结合的方式,学生将掌握如何选择合适的数据结构解决特定问题,以及对各种数据结构进行效率分析和比较。", room: "102教室"},
{name: "计算机网络", time: "16:00", description: "计算机网络是研究计算机之间的连接方式及其通信协议的科学。本课程提供计算机网络的综合介绍包括网络架构、协议、网络通信理论、实际应用等内容。学生将了解网络层次结构包括物理层、数据链路层、网络层、传输层和应用层。课程重点讲解TCP/IP模型以及它在网络通信中的关键作用。通过本课程的学习学生将获得设计、实施、管理和维护网络的基本知识和技能。", room: "103教室"}
];
// 假设的已签到日期数据 // 获取课程列表容器
var signedDays = [1, 5, 9]; // 这里仅为示例,实际应从服务器获取 let courseListDiv = document.getElementById('course-list');
var requiredSignDays = [2, 6, 15, 23];
// 动态生成日历 // 遍历课程数组,为每门课创建一个卡片
function generateCalendar() { courses.forEach(course => {
var today = new Date(); let courseDiv = document.createElement('div');
var currentMonth = today.getMonth(); courseDiv.className = 'layui-col-md4';
var currentYear = today.getFullYear(); courseDiv.innerHTML = `<div class="course-card layui-elem-field">
<legend>${course.name}</legend>
var firstDay = new Date(currentYear, currentMonth, 1).getDay(); <div class="layui-field-box">
var daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate(); <p><strong>时间:</strong> ${course.time}</p>
<p><strong>教室:</strong> ${course.room}</p>
var calendarHtml = ''; <p><strong>描述:</strong> ${course.description}</p>
var day = 1; <p><strong>剩余时间:</strong> <span class="countdown">计算中...</span></p>
</div>
for (var i = 0; i < 6; i++) { </div>`;
calendarHtml += '<tr>'; courseListDiv.appendChild(courseDiv);
for (var j = 0; j < 7; j++) { });
if (i === 0 && j < firstDay) {
calendarHtml += '<td></td>';
} else if (day > daysInMonth) {
break;
} else {
var classes = '';
if (signedDays.includes(day)) classes += 'signed '; // 已签到
if (requiredSignDays.includes(day)) classes += 'required-sign'; // 需要签到
calendarHtml += `<td class="${classes}">${day}</td>`;
day++;
}
}
calendarHtml += '</tr>';
if (day > daysInMonth) {
break;
}
}
document.getElementById('calendar-body').innerHTML = calendarHtml;
}
function updateTime() {
var now = new Date();
var timeStr = now.getFullYear() + '年' + (now.getMonth() + 1) + '月' + now.getDate() + '日';
document.getElementById('current-time').innerText = '当前时间: ' + timeStr;
}
// 页面加载时生成日历
generateCalendar();
updateTime();
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@@ -55,10 +55,10 @@
<!-- 表头 --> <!-- 表头 -->
<thead> <thead>
<tr> <tr>
<th>课程名称</th>
<th>课程代码</th> <th>课程代码</th>
<th>学分</th> <th>课程名称</th>
<th>班级专业</th> <th>班级</th>
<th>专业</th>
<th>签到</th> <th>签到</th>
</tr> </tr>
</thead> </thead>

View File

@@ -7,6 +7,40 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<link href="static/css/layui.css" rel="stylesheet"> <link href="static/css/layui.css" rel="stylesheet">
<style>
#sign-in-reminder {
max-width: 600px; /* 限制最大宽度 */
margin: 50px auto; /* 上下保留空间,左右自动以居中显示 */
padding: 20px; /* 内边距 */
border-radius: 8px; /* 轻微的圆角 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 给予阴影以突出层次 */
background-color: #f7f7f7; /* 浅灰色背景 */
}
#title {
text-align: center; /* 标题居中 */
color: #333; /* 标题颜色 */
font-size: 24px; /* 字号大小 */
}
#course-info {
text-align: center; /* 课程信息文本居中 */
margin-bottom: 20px; /* 与下方元素保持距离 */
}
.layui-btn {
width: 100%; /* 使按钮宽度填满容器 */
background-color: #009688; /* 设定一个现代感的按钮颜色 */
color: white; /* 文字颜色为白色 */
border-radius: 4px; /* 轻微的圆角 */
padding: 10px 0; /* 上下填充,增加按钮触摸面积 */
text-align: center; /* 文字水平居中 */
display: block; /* 转换为块级元素以应用宽度 */
line-height: 1.5; /* 调整行高以垂直居中文字 */
font-size: 16px; /* 设定文字大小 */
}
</style>
</head> </head>
<body> <body>
@@ -44,7 +78,7 @@
</div> </div>
<div class="layui-body"> <div class="layui-body">
<div id="sign-in-reminder" class="layui-container"> <div id="sign-in-reminder" class="layui-container">
<blockquote class="layui-elem-quote layui-text" id="title"> <blockquote class="layui-card-header" id="title">
课程签到 课程签到
</blockquote> </blockquote>
<div class="layui-text" id="course-info"></div> <div class="layui-text" id="course-info"></div>
@@ -70,7 +104,7 @@
if (response.msg === "ok") { if (response.msg === "ok") {
// 如果后端返回课程名 // 如果后端返回课程名
$("#course-info").text("课程:" + response.data.course_name + "。在上课时间内,请及时签到!"); $("#course-info").text("课程:" + response.data.course_name + "。在上课时间内,请及时签到!");
// 启用签到按钮 // 启用签到按钮
$("#sign-in-btn").prop('disabled', false); $("#sign-in-btn").prop('disabled', false);
courseData = response.data; courseData = response.data;
@@ -86,7 +120,7 @@
$("#sign-in-btn").click(function () { $("#sign-in-btn").click(function () {
if (!$(this).prop('disabled')) { if (!$(this).prop('disabled')) {
// 发送签到请求到后端 // 发送签到请求到后端
$.post("/api/student-sign-in",courseData,function (response) { $.post("/api/student-sign-in", courseData, function (response) {
// 处理签到后的响应 // 处理签到后的响应
if (response.msg === 'ok') { if (response.msg === 'ok') {
layer.msg('签到成功!'); layer.msg('签到成功!');

View File

@@ -9,9 +9,10 @@
<link href="static/css/layui.css" rel="stylesheet"/> <link href="static/css/layui.css" rel="stylesheet"/>
</head> </head>
<body> <body>
<div class="layui-layout layui-layout-admin"> <div class="layui-layout layui-layout-admin">
<div class="layui-header"> <div class="layui-header layui-bg-black">
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div> <div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
<!-- 头部区域可配合layui 已有的水平导航) --> <!-- 头部区域可配合layui 已有的水平导航) -->
<ul class="layui-nav layui-layout-right"> <ul class="layui-nav layui-layout-right">
@@ -56,33 +57,33 @@
layui.use(['table', 'jquery'], function () { layui.use(['table', 'jquery'], function () {
var table = layui.table; var table = layui.table;
var $ = layui.jquery; var $ = layui.jquery;
// 发起GET请求获取数据 // 发起GET请求获取数据
$.get('/api/get-course-type', function (res) { $.get('/api/get-course-type', function (res) {
// 假设返回的res是一个对象包含必修和选修的课程名数组 // 假设返回的res是一个对象包含必修和选修的课程名数组
// 处理返回的数据,转换为表格能接受的格式 // 处理返回的数据,转换为表格能接受的格式
var tableData = []; var tableData = [];
res['必修'].forEach(function (course) { res['必修'].forEach(function (course) {
tableData.push({course_type: '必修', course_name: course}); tableData.push({course_type: '必修', course_name: course});
}); });
res['选修'].forEach(function (course) { res['选修'].forEach(function (course) {
tableData.push({course_type: '选修', course_name: course}); tableData.push({course_type: '选修', course_name: course});
}); });
// 渲染表格 // 渲染表格
table.render({ table.render({
elem: '#courseTable', elem: '#courseTable',
cols: [[ // 设置表头 cols: [[ // 设置表头
{field: 'course_type', title: '课程类型', sort: true}, {field: 'course_type', title: '课程类型', sort: true},
{field: 'course_name', title: '课程名称', sort: true} {field: 'course_name', title: '课程名称', sort: true}
]], ]],
data: tableData // 使用处理后的数据 data: tableData // 使用处理后的数据
});
}); });
}); });
});
</script> </script>
</body> </body>
</html> </html>

View File

@@ -7,6 +7,34 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<link href="static/css/layui.css" rel="stylesheet"/> <link href="static/css/layui.css" rel="stylesheet"/>
<style>
.custom-table {
border-collapse: collapse;
width: 100%;
border: 1px solid #ddd; /* 轻灰色边框 */
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 轻微的阴影 */
}
.custom-table th, .custom-table td {
text-align: left;
padding: 12px 15px;
border-bottom: 1px solid #ddd; /* 每行之间的分割线 */
}
.custom-table th {
background-color: #f2f2f2; /* 轻灰色背景 */
color: #333; /* 深色文字 */
}
.custom-table tr:hover {
background-color: #f5f5f5; /* 鼠标悬浮时的背景色 */
}
.custom-table td {
color: #555; /* 内容的文字颜色 */
}
</style>
</head> </head>
<body> <body>
@@ -45,27 +73,21 @@
<div class="layui-body"> <div class="layui-body">
<!-- 内容主体区域 --> <!-- 内容主体区域 -->
<div style="padding: 15px;"> <div style="padding: 15px;">
<blockquote class="layui-elem-quote layui-text"> <table class="layui-table custom-table">
课程信息 <!-- 略去colgroup中课程代码的列 -->
</blockquote>
<table class="layui-table">
<colgroup> <colgroup>
<col width="200">
<col width="150"> <col width="150">
<col width="150"> <col width="200">
<col width="150">
<col width="100">
<col>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
<th>课程名称</th> <th>课程名称</th>
<th>课程代码</th>
<th>学分</th> <th>学分</th>
<th>课程描述</th> <th>课程描述</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- 动态生成的课程信息将填充在这里 -->
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -77,20 +99,22 @@
<script src="static/layui.js"></script> <script src="static/layui.js"></script>
<script src="/static/js/menu.js"></script> <script src="/static/js/menu.js"></script>
<script src="/static/js/logout.js"></script> <script src="/static/js/logout.js"></script>
<script> <script>
$.get('/api/get-course-info', function (courses) { $.get('/api/get-course-info', function (courses) {
var tbody = $('.layui-table tbody'); var tbody = $('.layui-table tbody');
tbody.empty(); // 清空表格现有内容 tbody.empty(); // 清空表格现有内容
courses.forEach(function (course) { courses.forEach(function (course) {
var row = '<tr>' + var row = '<tr>' +
'<td>' + course.course_name + '</td>' + '<td>' + course.course_name + '</td>' +
'<td>' + course.course_code + '</td>' + // 移除课程代码的数据
'<td>' + course.credits + '</td>' + '<td>' + course.credits + '</td>' +
'<td>' + course.description + '</td>' + '<td>' + course.description + '</td>' +
'</tr>'; '</tr>';
tbody.append(row); // 将新行添加到表格中 tbody.append(row); // 将新行添加到表格中
}); });
}); }
);
</script> </script>
</body> </body>
</html> </html>

View File

@@ -29,7 +29,7 @@
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png" src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
class="layui-nav-img" class="layui-nav-img"
/> />
{{ session.nickname }} {{ session.name }}
</a> </a>
<dl class="layui-nav-child"> <dl class="layui-nav-child">
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile --> <dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->

View File

@@ -9,11 +9,22 @@
<body class="layui-padding-3"> <body class="layui-padding-3">
<style> <style>
.demo-login-container{width: 320px; margin: 21px auto 0;} .demo-login-container {
.demo-login-other .layui-icon{position: relative; display: inline-block; margin: 0 2px; top: 2px; font-size: 26px;} width: 320px;
margin: 21px auto 0;
}
.demo-login-other .layui-icon {
position: relative;
display: inline-block;
margin: 0 2px;
top: 2px;
font-size: 26px;
}
</style> </style>
<form class="layui-form" id="loginForm"> <form class="layui-form" id="loginForm">
<h1 style="text-align:center; color: #16baaa;">网上上课点名系统</h1> <h1 style="text-align:center; color: #1E90FF; font-family: 'Arial', sans-serif; text-shadow: 2px 2px 4px;">
网上上课点名系统</h1>
<div class="demo-login-container"> <div class="demo-login-container">
<div class="layui-form-item"> <div class="layui-form-item">
<div class="layui-input-wrap"> <div class="layui-input-wrap">
@@ -39,13 +50,17 @@
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<!-- 登录按钮 --> <!-- 登录按钮 -->
<button class="layui-btn layui-btn-fluid" type="submit" lay-submit lay-filter="login" id="btnLogin">登录</button> <button class="layui-btn layui-btn-fluid" type="submit" lay-submit lay-filter="login" id="btnLogin"
style="background-color: #4CAF50; color: white;">登录
</button>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<!-- 注册按钮 --> <!-- 注册按钮 -->
<button type="button" class="layui-btn layui-btn-fluid" id="btnRegister">注册</button> <button type="button" class="layui-btn layui-btn-fluid" id="btnRegister"
style="background-color: #1E90FF; color: white;">注册
</button>
</div> </div>
</div> </div>
</form> </form>
@@ -64,6 +79,7 @@
data: data.field, // 表单数据 data: data.field, // 表单数据
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
console.log(response)
if (response.success) { if (response.success) {
window.location.href = '/attendance-reminder'; // 或者你的成功页面 window.location.href = '/attendance-reminder'; // 或者你的成功页面
} else { } else {

View File

@@ -18,7 +18,7 @@
} }
</style> </style>
<h1 style="text-align:center; color: #16baaa;">注册</h1> <h1 style="text-align:center; color: #1E90FF;">注册</h1>
<form class="layui-form"> <form class="layui-form">
<div class="demo-reg-container"> <div class="demo-reg-container">
@@ -60,7 +60,8 @@
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<button class="layui-btn layui-btn-fluid" type="submit" lay-submit lay-filter="btnRegister">注册</button> <button class="layui-btn layui-btn-fluid" type="submit" lay-submit lay-filter="btnRegister" style="background-color: #1E90FF; color: white;">注册</button>
</div> </div>
</div> </div>
</form> </form>

76
app/templates/test.html Normal file
View File

@@ -0,0 +1,76 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>签到提醒仪表板</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layui-src/dist/css/layui.css" media="all">
<style>
.course-card {
padding: 10px;
border: 1px solid #DDD;
border-radius: 5px;
margin: 10px 0;
background-color: #FFF;
}
.course-card .layui-field-box {
padding: 10px;
}
.course-card button {
width: 100%;
text-align: center;
}
</style>
</head>
<body>
<div class="layui-container">
<div class="layui-row">
<div class="layui-col-xs12">
<h1>签到提醒仪表板</h1>
</div>
</div>
<div id="course-list" class="layui-row">
<!-- 课程列表将在这里生成 -->
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/layui-src/dist/layui.all.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
// 模拟从后端获取的课程数据
let courses = [
{name: "数学", time: "14:00", description: "关于几何和代数的深入学习", teacher: "张老师", room: "101教室"},
{name: "物理", time: "15:00", description: "探索物质世界的基本规律", teacher: "李老师", room: "102教室"},
{name: "化学", time: "16:00", description: "化合物与元素的神秘世界", teacher: "王老师", room: "103教室"}
];
// 获取课程列表容器
let courseListDiv = document.getElementById('course-list');
// 遍历课程数组,为每门课创建一个卡片
courses.forEach(course => {
let courseDiv = document.createElement('div');
courseDiv.className = 'layui-col-md4';
courseDiv.innerHTML = `<div class="course-card layui-elem-field">
<legend>${course.name} - ${course.teacher}</legend>
<div class="layui-field-box">
<p><strong>时间:</strong> ${course.time}</p>
<p><strong>教室:</strong> ${course.room}</p>
<p><strong>描述:</strong> ${course.description}</p>
<p><strong>剩余时间:</strong> <span class="countdown">计算中...</span></p>
<button class="layui-btn layui-btn-normal">签到</button>
</div>
</div>`;
courseListDiv.appendChild(courseDiv);
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,43 @@
import unittest
import os
from app.views import *
from werkzeug.datastructures import FileStorage # 用于创建测试文件
class ReceiveExcelTestCase(unittest.TestCase):
def setUp(self):
app.testing = True # 设置 Flask 应用为测试模式
self.client = app.test_client()
# 创建一个测试用的Excel文件或使用一个真实的Excel文件路径
self.test_file_path = 'template.xlsx'
def test_receive_excel_no_file(self):
# 测试没有文件时的情况
response = self.client.post('/api/receive-excel')
self.assertEqual(response.status_code, 400)
self.assertEqual(response.get_json(), {"error": "No file part"})
def test_receive_excel_with_file(self):
# 模拟已登录用户
with self.client.session_transaction() as sess:
sess['number'] = 'X202301000001'
# 使用 FileStorage 创建测试文件对象
data = {
'file': (open(self.test_file_path, 'rb'), self.test_file_path)
}
# 发送 POST 请求到 /api/receive-excel
response = self.client.post('/api/receive-excel', data=data, content_type='multipart/form-data')
# 验证返回的状态码和响应
self.assertEqual(response.status_code, 200)
self.assertEqual(response.get_json(), {"message": "File successfully processed"})
def tearDown(self):
# 清理测试用的文件或数据
if os.path.exists(self.test_file_path):
os.remove(self.test_file_path)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,39 @@
import unittest
from app.views import *
from unittest.mock import patch
from flask import session
class StudentSignInTestCase(unittest.TestCase):
def setUp(self):
# 设置 Flask 测试模式
app.testing = True
self.client = app.test_client()
@patch('db.database_manager.DatabaseManager.update_sign_in_info')
def test_student_sign_in(self, mock_update_sign_in_info):
# 模拟已登录用户
with self.client.session_transaction() as sess:
sess['number'] = 'X202301000001'
# 模拟数据库操作
mock_update_sign_in_info.return_value = 1 # 假设成功更新签到信息
# 发送 POST 请求到/api/student-sign-in
response = self.client.post('/api/student-sign-in', data={
'course_name': '计算机导论',
'course_id': '2',
'student_number': 'X202301000001',
})
# 验证返回的状态码和JSON数据
self.assertEqual(response.status_code, 200)
json_data = response.get_json()
self.assertEqual(json_data, {"msg": "ok", "data": "签到成功!"})
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,37 @@
import unittest
from app.views import *
from unittest.mock import patch
from flask import session
class TeacherSignInTestCase(unittest.TestCase):
def setUp(self):
# 设置 Flask 测试模式
app.testing = True
self.client = app.test_client()
@patch('db.database_manager.DatabaseManager.update_sign_in_info')
def test_teacher_sign_in(self, mock_teacher_sign_in):
# 模拟已登录用户
with self.client.session_transaction() as sess:
sess['number'] = 'X202301000001'
# 模拟数据库操作的返回值
mock_teacher_sign_in.return_value = {"msg": "当前班级专业没有学生'"}
# 发送 POST 请求到/api/teacher-sign-in
response = self.client.post('/api/teacher-sign-in', data={
'course_id': '123',
'course_name': 'Test Course',
'class_name': 'TestClass',
'major_id': '456'
})
# 验证返回的状态码和数据
self.assertEqual(response.status_code, 200)
json_data = response.get_json()
self.assertEqual(json_data, {"msg": "当前班级专业没有学生"})
if __name__ == '__main__':
unittest.main()

73
app/tests/TestGetHtml.py Normal file
View File

@@ -0,0 +1,73 @@
import unittest
from app.views import *
class HtmlTestCase(unittest.TestCase):
def setUp(self):
app.testing = True # 设置 Flask 应用为测试模式
self.client = app.test_client() # 创建一个测试客户端
def test_get_course_info(self):
# 测试 GET 请求
response = self.client.get('/course-info')
# 验证响应状态码
self.assertEqual(response.status_code, 200)
# 验证响应的Content-Type头部值
self.assertIn('text/html', response.content_type)
def test_get_attendance(self):
# 测试 GET 请求
response = self.client.get('/attendance')
# 验证响应状态码
self.assertEqual(response.status_code, 200)
# 验证响应的Content-Type头部值
self.assertIn('text/html', response.content_type)
def test_get_announcement(self):
# 测试 GET 请求
response = self.client.get('/announcement')
# 验证响应状态码
self.assertEqual(response.status_code, 200)
# 验证响应的Content-Type头部值
self.assertIn('text/html', response.content_type)
def test_get_attendance_reminder(self):
# 测试 GET 请求
response = self.client.get('/attendance-reminder')
# 验证响应状态码
self.assertEqual(response.status_code, 200)
# 验证响应的Content-Type头部值
self.assertIn('text/html', response.content_type)
def test_get_course_category(self):
# 测试 GET 请求
response = self.client.get('/course-category')
# 验证响应状态码
self.assertEqual(response.status_code, 200)
# 验证响应的Content-Type头部值
self.assertIn('text/html', response.content_type)
def test_get_attendance_teacher(self):
# 测试 GET 请求
response = self.client.get('/attendance-teacher')
# 验证响应状态码
self.assertEqual(response.status_code, 200)
# 验证响应的Content-Type头部值
self.assertIn('text/html', response.content_type)
if __name__ == '__main__':
unittest.main()

44
app/tests/TestGetMenu.py Normal file
View File

@@ -0,0 +1,44 @@
import unittest
from app.views import *
from flask import session
from unittest.mock import patch # 如果需要模拟数据库方法
class MenuAPITestCase(unittest.TestCase):
def setUp(self):
app.testing = True # 设置 Flask 应用为测试模式
self.client = app.test_client() # 创建一个测试客户端
self.expected_menu = [
{"name": "课程类别", "path": "/course-category"},
{"name": "课程信息", "path": "/course-info"},
{"name": "课程签到", "path": "/attendance-teacher"},
{"name": "签到提醒", "path": "/attendance-reminder"}
]
def test_menu_no_login(self):
# 测试未登录情况下请求API
response = self.client.get('/api/menu')
self.assertEqual(response.status_code, 401)
self.assertEqual(response.get_json(), [])
def test_login(self):
with self.client:
self.client.post('/login', data=dict(
number='G0001',
password='1'
))
# 确认登录后session中有数据
self.assertIn('role', session)
# 执行logout
response = self.client.get('/api/menu')
print(response.get_json())
# 确认响应是重定向到登录页面
self.assertEqual(response.status_code, 200)
self.assertEqual(self.expected_menu,response.get_json())
if __name__ == '__main__':
unittest.main()

45
app/tests/TestLogin.py Normal file
View File

@@ -0,0 +1,45 @@
import unittest
from app.views import *
from db.database_manager import DatabaseManager # 确保你可以导入DatabaseManager
class LoginTestCase(unittest.TestCase):
def setUp(self):
# 设置 Flask 测试模式
app.testing = True
self.client = app.test_client()
def test_login_get(self):
# 测试 GET 请求返回登录页面
response = self.client.get('/login')
self.assertEqual(response.status_code, 200)
self.assertIn('text/html', response.content_type)
def test_successful_login_post(self):
# 测试有效的登录 POST 请求
with self.client:
response = self.client.post('/login', data={
'number': 'G0001',
'password': '1'
})
# 根据你的应用逻辑调整断言
self.assertEqual(response.status_code, 200)
def test_invalid_login_post(self):
# 测试无效的登录 POST 请求
response = self.client.post('/login', data={
'number': 'admin',
'password': 'admin'
})
# 将返回的数据解析为JSON
json_data = response.get_json()
# 确认JSON响应中的值
self.assertEqual(response.status_code, 200)
self.assertFalse(json_data['success'])
self.assertEqual(json_data['message'], "无效的用户名或密码")
# 运行测试
if __name__ == '__main__':
unittest.main()

34
app/tests/TestLogout.py Normal file
View File

@@ -0,0 +1,34 @@
import unittest
from app.views import *
class LogoutTestCase(unittest.TestCase):
def setUp(self):
# 设置 Flask 测试模式
app.testing = True
self.client = app.test_client()
def test_logout(self):
# 先登录,确保 session 是设置的
with self.client:
self.client.post('/login', data=dict(
number='G0001',
password='1'
))
# 确认登录后session中有数据
self.assertIn('number', session)
# 执行logout
response = self.client.get('/logout')
# 确认 session 被清空
self.assertNotIn('number', session)
# 确认响应是重定向到登录页面
self.assertEqual(response.status_code, 302)
self.assertTrue(response.location.endswith('/login'))
if __name__ == '__main__':
unittest.main()

View File

@@ -8,6 +8,7 @@ from datetime import datetime
# 应用内部模块 # 应用内部模块
from utils.time_utils import check_now_time from utils.time_utils import check_now_time
from utils.parse_table import parse_table
from utils.allowed_files import allowed_excel from utils.allowed_files import allowed_excel
from db.connection import MySQLPool from db.connection import MySQLPool
from db.database_manager import DatabaseManager from db.database_manager import DatabaseManager
@@ -16,7 +17,6 @@ from models.Teacher import Teacher
from models.User import User from models.User import User
from config import SECRET_KEY, LOGGING_CONFIG, FILE_PATH from config import SECRET_KEY, LOGGING_CONFIG, FILE_PATH
app = Flask(__name__, static_folder='static') app = Flask(__name__, static_folder='static')
app.secret_key = SECRET_KEY # 从配置文件设置 app.secret_key = SECRET_KEY # 从配置文件设置
logging.basicConfig(**LOGGING_CONFIG) logging.basicConfig(**LOGGING_CONFIG)
@@ -89,7 +89,7 @@ def login():
db_manager = DatabaseManager() db_manager = DatabaseManager()
result = db_manager.valid_login(number, password) # 获取验证结果 result = db_manager.valid_login(number, password) # 获取验证结果
print(result)
# 确保用户已验证且活跃(未被禁用) # 确保用户已验证且活跃(未被禁用)
if result['valid'] and result['status'] == 1: if result['valid'] and result['status'] == 1:
# 登录成功 # 登录成功
@@ -97,7 +97,7 @@ def login():
session['role'] = check_identity(number) session['role'] = check_identity(number)
session['name'] = result['name'] session['name'] = result['name']
return jsonify(success=True, message="登录成功") return jsonify(success=True, message="登录成功")
elif not result['status']: elif not result.get('status'):
# 用户被禁用的情况 # 用户被禁用的情况
return jsonify(success=False, message="账户已被禁用") return jsonify(success=False, message="账户已被禁用")
else: else:
@@ -276,6 +276,7 @@ def download_template():
@app.route('/api/receive-excel', methods=['POST']) @app.route('/api/receive-excel', methods=['POST'])
def receive_excel(): def receive_excel():
number = session.get('number')
# 检查是否有文件在请求中 # 检查是否有文件在请求中
if 'file' not in request.files: if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400 return jsonify({"error": "No file part"}), 400
@@ -290,9 +291,10 @@ def receive_excel():
if file and allowed_excel(file.filename): if file and allowed_excel(file.filename):
try: try:
# 使用 openpyxl 读取文件内容 # 使用 openpyxl 读取文件内容
workbook = openpyxl.load_workbook(file) data = parse_table(file, number)
# TODO: 在这里处理你的Excel文件例如读取数据 db_manager = DatabaseManager()
print(workbook) result = db_manager.insert_into_class_student(data)
print(result)
return jsonify({"message": "File successfully processed"}), 200 return jsonify({"message": "File successfully processed"}), 200
except Exception as e: except Exception as e:
return jsonify({"error": str(e)}), 500 return jsonify({"error": str(e)}), 500
@@ -381,6 +383,7 @@ def student_get_today_courses():
@app.route('/api/teacher-sign-in', methods=['POST']) @app.route('/api/teacher-sign-in', methods=['POST'])
def teacher_sign_in(): def teacher_sign_in():
teacher_number = session.get('number')
course_id = request.form['course_id'] course_id = request.form['course_id']
course_name = request.form['course_name'] course_name = request.form['course_name']
class_name = request.form['class_name'] class_name = request.form['class_name']
@@ -392,7 +395,7 @@ def teacher_sign_in():
print(f"course_id: {course_id},course_name: {course_name},class_name: {class_name},major_id: {major_id}") print(f"course_id: {course_id},course_name: {course_name},class_name: {class_name},major_id: {major_id}")
db_manager = DatabaseManager() db_manager = DatabaseManager()
data = db_manager.teacher_sign_in(course_id, course_name, class_name, major_id,date,status) data = db_manager.teacher_sign_in(course_id, course_name, class_name, major_id, date, status, teacher_number)
return data return data

View File

@@ -77,7 +77,7 @@ class DatabaseManager:
return {'valid': True, 'status': status, 'name': name} return {'valid': True, 'status': status, 'name': name}
# 密码不匹配或用户不存在,返回登录失败 # 密码不匹配或用户不存在,返回登录失败
return {'valid': False} return {'valid': False, 'status': True}
def get_menu(self, role): def get_menu(self, role):
sql = "SELECT menu_name,path FROM menu_items WHERE role=%s ORDER BY `order`" sql = "SELECT menu_name,path FROM menu_items WHERE role=%s ORDER BY `order`"
@@ -111,8 +111,34 @@ WHERE
""" """
# 执行查询并返回结果 # 执行查询并返回结果
result = self.fetch(sql, (teacher_number,)) class_student_sql = """SELECT course_id, course_name,class_name,major,major_id,major FROM class_student where teacher_number = %s;"""
return result result1 = self.fetch(sql, (teacher_number,))
result2 = self.fetch(class_student_sql, (teacher_number))
data = []
# 遍历第一个结果集
if result1:
for row in result1:
data.append({
"course_id": row['course_id'],
"course_name": row['course_name'],
"class_name": row['class_name'],
"major_id": row['major_id'],
"major": row['major']
})
# 遍历第二个结果集
if result2:
for row in result2:
data.append({
"course_id": row['course_id'],
"course_name": row['course_name'],
"class_name": row['class_name'],
"major_id": row['major_id'],
"major": row['major']
})
# 返回包含两个结果集信息的data列表
return data
def get_course_type(self): def get_course_type(self):
sql = "SELECT course_name, course_type FROM course" sql = "SELECT course_name, course_type FROM course"
@@ -152,13 +178,16 @@ WHERE
class_name = self.fetch(sql_student, (student_number,))[0]['class_name'] class_name = self.fetch(sql_student, (student_number,))[0]['class_name']
# 使用class_name和day_of_week从schedule表获取course_id # 使用class_name和day_of_week从schedule表获取course_id
sql_schedule = "SELECT course_id, FROM schedule WHERE day_of_week = %s AND class_name = %s AND period_id = %s;" sql_schedule = "SELECT course_id FROM schedule WHERE day_of_week = %s AND class_name = %s AND period_id = %s;"
course_id = self.fetch(sql_schedule, (day_of_week, class_name, period_id)) course_id = self.fetch(sql_schedule, (day_of_week, class_name, period_id))
print(f"course_id: {course_id}")
id = course_id[0]['course_id']
print(f"id: {id}")
# 对于每一个course_id从course表中查询course_name # 对于每一个course_id从course表中查询course_name
sql_course = "SELECT course_name FROM course WHERE course_id = %s;" sql_course = "SELECT course_name FROM course WHERE course_id = %s;"
course_name = self.fetch(sql_course, (course_id['course_id'],)) course_name = self.fetch(sql_course, (id,))[0]['course_name']
data = {"course_name": course_name, "course_id": course_id} print(f"course_name: {course_name}")
data = {"course_name": course_name, "course_id": id}
return data return data
def update_sign_in_info(self, student_number, course_id, course_name, date, status): def update_sign_in_info(self, student_number, course_id, course_name, date, status):
@@ -209,25 +238,62 @@ WHERE
data.append({"course_name": course_name, "time": time}) data.append({"course_name": course_name, "time": time})
return data return data
def teacher_sign_in(self, course_id, course_name, class_name, major_id, date, status): def teacher_sign_in(self, course_id, course_name, class_name, major_id, date, status, teacher_number):
class_student_sql = "SELECT student_number FROM class_student WHERE teacher_number = %s AND major_id = %s AND class_name = %s"
class_student_result = self.fetch(class_student_sql, (teacher_number, major_id, class_name))
student_sql = "SELECT student_number FROM student WHERE class_name = %s AND major_id = %s;" student_sql = "SELECT student_number FROM student WHERE class_name = %s AND major_id = %s;"
student_sql_result = self.fetch(student_sql, (class_name, major_id)) student_sql_result = self.fetch(student_sql, (class_name, major_id))
values_list = []
# 检查是否有学生编号被返回 # 检查是否有学生编号被返回
if student_sql_result: if student_sql_result:
values_list = []
for student in student_sql_result: for student in student_sql_result:
# 对于每个学生编号,创建一条记录的值元组 # 对于每个学生编号,创建一条记录的值元组
student_number = student['student_number'] student_number = student['student_number']
values_list.append((student_number, course_id, course_name, date, status)) values_list.append((student_number, course_id, course_name, date, status))
# 构建一次性插入多条记录的SQL语句 if class_student_result:
attendance_sql = "INSERT INTO attendance_record (student_number, course_id, course_name, date, status) VALUES (%s, %s, %s, %s, %s)" for class_student in class_student_result:
# 注意这里变量名已经修改为class_student
student_number = class_student['student_number']
values_list.append((student_number, course_id, course_name, date, status))
# 使用executemany一次性插入多条记录 # 如果没有任何学生编号,返回相应的消息
result = self.executemany(attendance_sql, values_list) if not values_list:
print(result)
return {"msg": "班级签到成功,签到人数:" + str(result)}
else:
# 如果没有学生编号,可能需要处理错误或记录日志
return {"msg": "当前班级专业没有学生"} return {"msg": "当前班级专业没有学生"}
# 构建一次性插入多条记录的SQL语句
attendance_sql = "INSERT INTO attendance_record (student_number, course_id, course_name, date, status) VALUES (%s, %s, %s, %s, %s)"
# 使用executemany一次性插入多条记录
result = self.executemany(attendance_sql, values_list)
print(f"Inserted rows: {result}")
return {"msg": "班级签到成功,签到人数:" + str(result)}
def insert_into_class_student(self, class_students):
try:
values_list = [
(
cs['teacher_number'],
cs['class_name'],
cs['student_name'],
cs['student_number'],
cs['course_id'],
cs['course_name'],
cs['major_id'],
cs['major']
)
for cs in class_students
]
sql = """INSERT INTO class_student
(teacher_number, class_name, student_name, student_number, course_id, course_name, major_id, major)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)"""
result = self.executemany(sql, values_list)
return {"inserted_rows": result}
except Exception as e:
# 这里应根据实际情况记录日志或返回错误信息
print(f"An error occurred: {e}")
return {"error": str(e)}

Binary file not shown.

212
mysql.sql
View File

@@ -1,18 +1,3 @@
CREATE TABLE user
(
user_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
number VARCHAR(15) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
status BOOLEAN NOT NULL
);
INSERT INTO user (name, number, password, identity, status)
VALUES ('学生', '1', '$2b$12$okY88GrzlUHb/Ox1ENwtqeBUnE0bgMOCPy.UKmFaTnu3El7EYX8Em', 'student', TRUE),
('老师', '2', '$2b$12$okY88GrzlUHb/Ox1ENwtqeBUnE0bgMOCPy.UKmFaTnu3El7EYX8Em', 'teacher', TRUE);
CREATE TABLE menu_items CREATE TABLE menu_items
( (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
@@ -27,11 +12,8 @@ INSERT INTO menu_items (menu_name, role, path, `order`)
VALUES ('课程信息', 'student', '/course-info', 1), VALUES ('课程信息', 'student', '/course-info', 1),
('课程签到', 'student', '/attendance', 2), ('课程签到', 'student', '/attendance', 2),
('公告信息', 'student', '/announcement', 3), ('公告信息', 'student', '/announcement', 3),
('签到提醒', 'student', '/attendance-reminder', 4); ('签到提醒', 'student', '/attendance-reminder', 4),
('课程类别', 'teacher', '/course-category', 1),
INSERT INTO menu_items (menu_name, role, path, `order`)
VALUES ('课程类别', 'teacher', '/course-category', 1),
('课程信息', 'teacher', '/course-info', 2), ('课程信息', 'teacher', '/course-info', 2),
('课程签到', 'teacher', '/attendance-teacher', 3), ('课程签到', 'teacher', '/attendance-teacher', 3),
('签到提醒', 'teacher', '/attendance-reminder', 4); ('签到提醒', 'teacher', '/attendance-reminder', 4);
@@ -79,7 +61,6 @@ VALUES ('大学计算机基础', 'CF001', '必修', 3, '介绍计算机基础知
('离散数学导论', 'IDTM01', '必修', 3, '介绍离散数学的基础知识和应用'), ('离散数学导论', 'IDTM01', '必修', 3, '介绍离散数学的基础知识和应用'),
('计算机网络', 'CN002', '必修', 4, '学习计算机网络的基础理论和协议'); ('计算机网络', 'CN002', '必修', 4, '学习计算机网络的基础理论和协议');
CREATE TABLE attendance_record CREATE TABLE attendance_record
( (
record_id INT AUTO_INCREMENT PRIMARY KEY, record_id INT AUTO_INCREMENT PRIMARY KEY,
@@ -90,6 +71,61 @@ CREATE TABLE attendance_record
status VARCHAR(20) status VARCHAR(20)
); );
CREATE TABLE time_period
(
period_id INT AUTO_INCREMENT UNIQUE,
period_name VARCHAR(10),
start_time TIME,
end_time TIME,
PRIMARY KEY (period_id)
);
INSERT INTO time_period (period_name, start_time, end_time)
VALUES ('一、二节', '08:00:00', '09:30:00'),
('三、四节', '10:00:00', '11:30:00'),
('五、六节', '14:30:00', '16:00:00'),
('七、八节', '16:30:00', '18:00:00');
CREATE TABLE class_student (
id INT AUTO_INCREMENT PRIMARY KEY,
teacher_number VARCHAR(20) NOT NULL,
class_name VARCHAR(20) NOT NULL , -- 班级
student_name VARCHAR(50) NOT NULL , -- 姓名
student_number VARCHAR(20) NOT NULL UNIQUE, -- 学号
course_id INT NOT NULL , -- 课程ID
course_name VARCHAR(100) NOT NULL , -- 课程名称
major_id VARCHAR(20) NOT NULL,
major VARCHAR(255) NOT NULL
);
CREATE TABLE user
(
user_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
number VARCHAR(15) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
status BOOLEAN NOT NULL
);
INSERT INTO user (name, number, password, status) VALUES
('教师1','G0001','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('教师2','G0002','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('教师3','G0003','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('教师4','G0004','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生1','X202301000001','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生2','X202301000002','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生3','X202301000003','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生4','X202301000004','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生5','X202301000005','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生6','X202301000006','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生7','X202301000007','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生8','X202301000008','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生9','X202301000009','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生10','X2023010000010','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生11','X2023010000011','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
('学生12','X2023010000012','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE);
CREATE TABLE major CREATE TABLE major
( (
@@ -117,27 +153,20 @@ CREATE TABLE student
FOREIGN KEY (major_id) REFERENCES major (major_id) FOREIGN KEY (major_id) REFERENCES major (major_id)
); );
INSERT INTO student (user_id, student_number, student_name, class_id) INSERT INTO student (user_id, student_number, student_name, class_name,major_id)
VALUES (5,'王伟', 202300001000, 1), VALUES
(6,'李娜', 202300001001, 1), (5,'X202301000001','学生1','2023级01班','000'),
(7,'张伟', 202300001002, 1), (6,'X202301000002','学生2','2023级01班','000'),
(8,'刘洋', 202300001003, 1), (7,'X202301000003','学生3','2023级01班','000'),
(9,'陈敏', 202300001004, 1), (8,'X202301000004','学生4','2023级01班','000'),
(10,'杨静', 202300001005, 2), (9,'X202302000005','学生5','2023级02班','000'),
(11,'赵媛媛', 202300001006, 2), (10,'X202302000006','学生6','2023级02班','000'),
(12,'黄进', 202300001007, 2), (11,'X202302000007','学生7','2023级02班','000'),
(13,'周杰', 202300001008, 2), (12,'X202302000008','学生8','2023级02班','000'),
(14,'吴琳', 202300001009, 2), (13,'X202303000009','学生9','2023级03班','001'),
(15,'徐涛', 202300001010, 3), (14,'X202303000010','学生10','2023级03班','001'),
(16,'孙怡', 202300001011, 3), (15,'X202303000011','学生11','2023级03班','001'),
(17,'朱元璋', 202300001012, 3), (16,'X202303000012','学生12','2023级03班','001');
(18,'马云', 202300001013, 3),
(19,'胡雪', 202300001014, 3),
(20,'郭敬明', 202300001015, 4),
(21,'林芳', 202300001016, 4),
(22,'段誉', 202300001017, 4),
(23,'曹操', 202300001018, 4),
(24,'刘备', 202300001019, 4);
CREATE TABLE teacher CREATE TABLE teacher
( (
@@ -149,11 +178,11 @@ CREATE TABLE teacher
FOREIGN KEY (user_id) REFERENCES user (user_id) FOREIGN KEY (user_id) REFERENCES user (user_id)
); );
INSERT INTO teacher (name, user_id) INSERT INTO teacher (name, user_id,teacher_number)
VALUES ('教师1', 1), VALUES ('教师1', 1,'G0001'),
('教师2', 2), ('教师2', 2,'G0002'),
('教师3', 3), ('教师3', 3,'G0003'),
('教师4', 4); ('教师4', 4,'G0004');
CREATE TABLE major_course CREATE TABLE major_course
( (
@@ -188,11 +217,14 @@ CREATE TABLE teacher_class_course
); );
INSERT INTO teacher_class_course(teacher_number, course_id, class_name,major_id) VALUES INSERT INTO teacher_class_course(teacher_number, course_id, class_name,major_id) VALUES
('G0000',1,'2023级01班','000'), ('G0001',1,'2023级01班','000'),
('G0000',3,'2023级01班','000'), ('G0001',3,'2023级01班','000'),
('G0001',2,'2023级01','000'), ('G0002',2,'2023级02','000'),
('G0001',4,'2023级01','000'); ('G0002',4,'2023级02','000'),
('G0001',1,'2023级02班','000'),
('G0001',3,'2023级02班','000'),
('G0002',2,'2023级03班','001'),
('G0002',4,'2023级03班','001');
CREATE TABLE schedule CREATE TABLE schedule
( (
@@ -210,41 +242,45 @@ CREATE TABLE schedule
INSERT INTO schedule (day_of_week, period_id, teacher_number, class_name, course_id) VALUES INSERT INTO schedule (day_of_week, period_id, teacher_number, class_name, course_id) VALUES
(1,1,'G0000','2023级01班',1), (1,1,'G0001','2023级01班',1),
(1,2,'G0000','2023级01班',3), (1,2,'G0001','2023级01班',3),
(1,3,'G0001','2023级01班',2), (1,3,'G0002','2023级01班',2),
(1,4,'G0001','2023级01班',4), (1,4,'G0002','2023级01班',4),
(2,1,'G0001','2023级01班',4), (2,1,'G0001','2023级01班',1),
(2,2,'G0001','2023级01班',2), (2,2,'G0001','2023级01班',3),
(2,3,'G0000','2023级01班',1), (2,3,'G0002','2023级01班',2),
(2,4,'G0000','2023级01班',3), (2,4,'G0002','2023级01班',4),
(3,1,'G0000','2023级01班',1), (3,1,'G0001','2023级01班',1),
(3,2,'G0000','2023级01班',3), (3,2,'G0001','2023级01班',3),
(3,3,'G0001','2023级01班',2), (3,3,'G0002','2023级01班',2),
(3,4,'G0001','2023级01班',4), (3,4,'G0002','2023级01班',4),
(4,1,'G0001','2023级01班',4), (4,1,'G0001','2023级01班',1),
(4,2,'G0001','2023级01班',2), (4,2,'G0001','2023级01班',3),
(4,3,'G0000','2023级01班',1), (4,3,'G0002','2023级01班',2),
(4,4,'G0000','2023级01班',3), (4,4,'G0002','2023级01班',4),
(5,1,'G0000','2023级01班',1), (5,1,'G0001','2023级01班',1),
(5,2,'G0000','2023级01班',3), (5,2,'G0001','2023级01班',3),
(5,3,'G0001','2023级01班',2), (5,3,'G0002','2023级01班',2),
(5,4,'G0001','2023级01班',4); (5,4,'G0002','2023级01班',4),
(1,1,'G0002','2023级03班',2),
CREATE TABLE time_period (1,2,'G0002','2023级03班',4),
( (1,3,'G0001','2023级02班',1),
period_id INT AUTO_INCREMENT UNIQUE, (1,4,'G0001','2023级02班',3),
period_name VARCHAR(10), (2,1,'G0002','2023级02班',2),
start_time TIME, (2,2,'G0002','2023级02班',4),
end_time TIME, (2,3,'G0001','2023级02班',1),
PRIMARY KEY (period_id) (2,4,'G0001','2023级02班',3),
); (3,1,'G0002','2023级03班',2),
(3,2,'G0002','2023级03班',4),
INSERT INTO time_period (period_name, start_time, end_time) (3,3,'G0001','2023级02班',1),
VALUES ('一、二节', '08:00:00', '09:30:00'), (3,4,'G0001','2023级02班',3),
('三、四节', '10:00:00', '11:30:00'), (4,1,'G0002','2023级02班',2),
('五、六节', '14:30:00', '16:00:00'), (4,2,'G0002','2023级02班',4),
('七、八节', '16:30:00', '18:00:00'); (4,3,'G0001','2023级02班',1),
(4,4,'G0001','2023级02班',3),
(5,1,'G0002','2023级03班',2),
(5,2,'G0002','2023级03班',4),
(5,3,'G0001','2023级02班',1),
(5,4,'G0001','2023级02班',3);

Binary file not shown.

30
utils/parse_table.py Normal file
View File

@@ -0,0 +1,30 @@
from openpyxl import load_workbook
def parse_table(file_path, number):
# 加载Excel工作簿
work_book = load_workbook(file_path)
# 选择工作簿中的第一个工作表
sheet = work_book.active
# 创建一个列表来存储所有行的数据
data = []
# 从第二行开始遍历(假设第一行是标题行)
for row in sheet.iter_rows(min_row=2, values_only=True):
# 创建一个字典来存储每行的数据
row_data = {
"teacher_number": number,
"class_name": row[0], # 班级
"student_name": row[1], # 姓名
"student_number": row[2], # 学号
"course_id": row[3], # 课程id
"course_name": row[4], # 课程
"major_id": row[5], # 专业代码
"major": row[6]
}
data.append(row_data)
# 返回解析后的数据
return data