Compare commits

...

45 Commits

Author SHA1 Message Date
dbeeb4d25a 更新 requirements.txt 2023-12-31 00:22:29 +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
c0d0e43e73 创建 get_teacher_attendance_table.js 2023-12-29 11:34:34 +08:00
3e79a22956 更新 attendance-teacher.html 2023-12-29 11:34:31 +08:00
ca55d9d2fd 更新 views.py 2023-12-29 11:34:26 +08:00
e655b72a5c 更新 database_manager.py 2023-12-29 11:34:24 +08:00
e75cce4dc9 更新 mysql.sql 2023-12-29 11:34:18 +08:00
a2be2f6378 更新 announcement.html 2023-12-29 10:12:56 +08:00
b07880eb7d 创建 time_utils.py 2023-12-29 01:58:24 +08:00
9048134f55 创建 allowed_files.py 2023-12-29 01:58:22 +08:00
dc2aedb06b 创建 __init__.py 2023-12-29 01:58:19 +08:00
74d63734bc 更新 database_manager.py 2023-12-29 01:58:17 +08:00
70c0c7925d 更新 views.py 2023-12-29 01:58:15 +08:00
1dddf07b54 删除 time_utils.py 2023-12-29 01:58:12 +08:00
88f5713f16 删除 allowed_files.py 2023-12-29 01:58:10 +08:00
7075531fc6 更新 announcement.html 2023-12-29 01:58:02 +08:00
22 changed files with 728 additions and 171 deletions

View File

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

View File

@@ -0,0 +1,60 @@
layui.use(['laypage', 'element', 'jquery', 'dropdown'], function () {
var laypage = layui.laypage;
var $ = layui.jquery;
function renderTable(page) {
$.get('/api/get-teacher-attendance-table?page=' + page, function (response) {
var $tbody = $('#attendanceTable tbody');
$tbody.empty(); // 清空表格内容
response.data.forEach(function (item) {
var $row = $('<tr></tr>');
$row.append(`<td>${item.course_id}</td>`);
$row.append(`<td>${item.course_name}</td>`);
$row.append(`<td>${item.class_name}</td>`);
$row.append(`<td>${item.major}</td>`);
$row.append(`<td><div class="layui-btn-container">
<button type="button" class="layui-btn btn-sign-in"
data-course-id="${item.course_id}"
data-course-name="${item.course_name}"
data-class-name = "${item.class_name}"
data-major-id = "${item.major_id}"
>签到</button>
</div>
</td>`)
$tbody.append($row);
});
laypage.render({
elem: 'pagination',
count: response.count,
limit: 10,
curr: page,
jump: function (obj, first) {
if (!first) {
renderTable(obj.curr);
}
}
});
});
}
// 为动态生成的按钮添加点击事件
$(document).on('click', '.btn-sign-in', function () {
var courseId = $(this).data('course-id'); // 获取课程ID
var courseName = $(this).data('course-name'); // 获取课程名
var className = $(this).data('class-name'); // 获取课程名
var majorId = $(this).data('major-id'); // 获取课程名
// console.log(courseId,courseName,className,majorId)
// 向后端发送POST请求
$.post('/api/teacher-sign-in', {
course_id: courseId,
course_name: courseName,
class_name: className,
major_id:majorId
}, function (response) {
layer.msg(response.msg);
});
});
renderTable(1); // 初始加载第一页
});

View File

@@ -21,7 +21,7 @@
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
class="layui-nav-img"
/>
{{ session.nickname }}
{{ session.name }}
</a>
<dl class="layui-nav-child">
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
@@ -72,10 +72,15 @@
$(document).ready(function () {
// 请求后端获取课程信息
$.get('/api/get-today-courses', function (data) {
data.forEach(function (course) {
// 为每个课程创建一个段落<p>并添加到容器中,并设置样式使字体更大且更加好看
$('#courses-container').append('<p style="font-size: 1.2em; margin: 5px 0;">课程: ' + course.name + ' - ' + course.time + '</p>');
});
if (data.msg == "ok") {
data.data.forEach(function (course) {
// 为每个课程创建一个段落<p>并添加到容器中,并设置样式使字体更大且更加好看
$('#courses-container').append('<p style="font-size: 1.2em; margin: 5px 0;">课程: ' + course.course_name + ' <br> 时间: ' + course.time + '</p>');
});
} else {
$('#courses-container').append(data.msg)
}
});
});
</script>

View File

@@ -55,10 +55,10 @@
<!-- 表头 -->
<thead>
<tr>
<th>课程名称</th>
<th>课程代码</th>
<th>学分</th>
<th>班级专业</th>
<th>课程名称</th>
<th>班级</th>
<th>专业</th>
<th>签到</th>
</tr>
</thead>
@@ -74,50 +74,8 @@
<script src="/static/layui.js"></script>
<script src="/static/js/menu.js"></script>
<script src="/static/js/logout.js"></script>
<script src="/static/js/get_teacher_attendance_table.js"></script>
<script>
layui.use(['laypage', 'element', 'jquery', 'dropdown'], function () {
var laypage = layui.laypage;
var $ = layui.jquery;
function renderTable(page) {
// 发送GET请求到服务器端点
$.get('/api/get-teacher-attendance-table?page=' + page, function (response) {
var $tbody = $('#attendanceTable tbody');
$tbody.empty(); // 清空表格内容
// 遍历返回的数据数组
response.data.forEach(function (item) {
// 创建新的行并填充数据
var $row = $('<tr></tr>');
$row.append(`<td>${item.course_name}</td>`);
$row.append(`<td>${item.course_code}</td>`);
$row.append(`<td>${item.credits}</td>`);
$row.append(`<td>${item.class_name + item.major}</td>`);
$row.append(`<td><div class="layui-btn-container">
<button type="button" class="layui-btn">签到</button>
</div>
</td>`)
$tbody.append($row);
});
// 渲染分页控件
laypage.render({
elem: 'pagination', // 分页容器的id
count: response.count, // 总条数
limit: 10, // 每页显示条数
curr: page, // 当前页
jump: function (obj, first) {
if (!first) { // 首次不执行
renderTable(obj.curr); // 根据页码加载数据
}
}
});
});
}
// 初始加载第一页
renderTable(1);
});
</script>
</body>
</html>

View File

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

View File

@@ -64,6 +64,7 @@
data: data.field, // 表单数据
dataType: 'json',
success: function (response) {
console.log(response)
if (response.success) {
window.location.href = '/attendance-reminder'; // 或者你的成功页面
} else {

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

@@ -1,19 +1,21 @@
import os
import openpyxl as openpyxl
import logging
# 第三方库
import openpyxl
from flask import Flask, redirect, url_for, render_template, session, jsonify, request, send_file
from datetime import datetime
# 应用内部模块
from utils.time_utils import check_now_time
from utils.parse_table import parse_table
from utils.allowed_files import allowed_excel
from db.connection import MySQLPool
from config import SECRET_KEY
from db.database_manager import DatabaseManager
from models.Student import Student
from models.Teacher import Teacher
from models.User import User
import logging
from config import LOGGING_CONFIG
from config import FILE_PATH
from datetime import datetime
import datetime
from config import SECRET_KEY, LOGGING_CONFIG, FILE_PATH
app = Flask(__name__, static_folder='static')
app.secret_key = SECRET_KEY # 从配置文件设置
@@ -87,7 +89,7 @@ def login():
db_manager = DatabaseManager()
result = db_manager.valid_login(number, password) # 获取验证结果
print(result)
# 确保用户已验证且活跃(未被禁用)
if result['valid'] and result['status'] == 1:
# 登录成功
@@ -95,7 +97,7 @@ def login():
session['role'] = check_identity(number)
session['name'] = result['name']
return jsonify(success=True, message="登录成功")
elif not result['status']:
elif not result.get('status'):
# 用户被禁用的情况
return jsonify(success=False, message="账户已被禁用")
else:
@@ -245,6 +247,7 @@ def get_current_teacher_courses():
# 获取所有课程数据
db_manager = DatabaseManager()
all_course_data = db_manager.get_current_teacher_courses(number)
print(all_course_data)
logging.info(f"all_course_data: {all_course_data}")
# 计算分页的起始和结束索引
start = (page - 1) * limit
@@ -273,6 +276,7 @@ def download_template():
@app.route('/api/receive-excel', methods=['POST'])
def receive_excel():
number = session.get('number')
# 检查是否有文件在请求中
if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400
@@ -287,9 +291,10 @@ def receive_excel():
if file and allowed_excel(file.filename):
try:
# 使用 openpyxl 读取文件内容
workbook = openpyxl.load_workbook(file)
# TODO: 在这里处理你的Excel文件例如读取数据
print(workbook)
data = parse_table(file, number)
db_manager = DatabaseManager()
result = db_manager.insert_into_class_student(data)
print(result)
return jsonify({"message": "File successfully processed"}), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
@@ -315,7 +320,8 @@ def get_course_name():
return jsonify({"msg": "用户未登录或编号不可用", "data": None})
# 获取今天是星期几
day_of_week = datetime.date.today().weekday() + 1
now = datetime.now()
day_of_week = now.weekday() + 1
# 如果是周末
if not (1 <= day_of_week <= 5):
@@ -358,7 +364,8 @@ def student_get_today_courses():
return jsonify({"msg": "用户未登录或编号不可用", "data": None})
# 获取今天是星期几
day_of_week = datetime.date.today().weekday() + 1
now = datetime.now() # 获取当前时间
day_of_week = now.weekday() + 1
# 如果是周末
if not (1 <= day_of_week <= 5):
@@ -373,5 +380,24 @@ def student_get_today_courses():
'data': data
})
@app.route('/api/teacher-sign-in', methods=['POST'])
def teacher_sign_in():
teacher_number = session.get('number')
course_id = request.form['course_id']
course_name = request.form['course_name']
class_name = request.form['class_name']
major_id = request.form['major_id']
now = datetime.now()
date = now.strftime("%Y年%m月%d%H:%M")
status = "出勤"
print(f"course_id: {course_id},course_name: {course_name},class_name: {class_name},major_id: {major_id}")
db_manager = DatabaseManager()
data = db_manager.teacher_sign_in(course_id, course_name, class_name, major_id, date, status, teacher_number)
return data
if __name__ == '__main__':
app.run(debug=True)

View File

@@ -1,3 +1,5 @@
from utils.time_utils import check_now_time
from utils.time_utils import get_time_by_ids
from db.connection import MySQLPool
import bcrypt
@@ -29,6 +31,22 @@ class DatabaseManager:
cursor.close()
conn.close()
def executemany(self, query, params_list):
conn = self.pool.get_connection()
try:
cursor = conn.cursor()
cursor.executemany(query, params_list) # 使用executemany代替execute
conn.commit() # 提交事务
return cursor.rowcount # 返回受影响的行数
except Exception as e:
# 可以在这里添加错误处理逻辑,例如打印错误日志、回滚事务等
print("An error occurred: ", e)
conn.rollback()
return None
finally:
cursor.close()
conn.close()
def user_exists(self, phone_number):
sql = "SELECT 1 FROM user WHERE number=%s LIMIT 1"
result = self.fetch(sql, (phone_number,))
@@ -59,7 +77,7 @@ class DatabaseManager:
return {'valid': True, 'status': status, 'name': name}
# 密码不匹配或用户不存在,返回登录失败
return {'valid': False}
return {'valid': False, 'status': True}
def get_menu(self, role):
sql = "SELECT menu_name,path FROM menu_items WHERE role=%s ORDER BY `order`"
@@ -75,11 +93,13 @@ class DatabaseManager:
# 使用INNER JOIN连接teacher_class_course表和course表
sql = """
SELECT
c.course_id,
c.course_name,
c.course_code,
c.credits,
tcc.class_name,
m.major
m.major,
m.major_id
FROM
teacher_class_course tcc
JOIN
@@ -91,8 +111,34 @@ WHERE
"""
# 执行查询并返回结果
result = self.fetch(sql, (teacher_number,))
return result
class_student_sql = """SELECT course_id, course_name,class_name,major,major_id,major FROM class_student where teacher_number = %s;"""
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):
sql = "SELECT course_name, course_type FROM course"
@@ -126,18 +172,22 @@ WHERE
return self.execute(sql, data)
def get_course_name(self, student_number, day_of_week, period_id):
print(f"student_number: {student_number}, day_of_week: {day_of_week}, period_id: {period_id}")
# 从student表获取class_name
sql_student = "SELECT class_name FROM student WHERE student_number = %s;"
class_name = self.fetch(sql_student, (student_number,))[0]['class_name']
# 使用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))
print(f"course_id: {course_id}")
id = course_id[0]['course_id']
print(f"id: {id}")
# 对于每一个course_id从course表中查询course_name
sql_course = "SELECT course_name FROM course WHERE course_id = %s;"
course_name = self.fetch(sql_course, (course_id['course_id'],))
data = {"course_name": course_name, "course_id": course_id}
course_name = self.fetch(sql_course, (id,))[0]['course_name']
print(f"course_name: {course_name}")
data = {"course_name": course_name, "course_id": id}
return data
def update_sign_in_info(self, student_number, course_id, course_name, date, status):
@@ -149,7 +199,7 @@ WHERE
result = self.execute(sql, val)
return result
def get_class_courses(self,student_number):
def get_class_courses(self, student_number):
# 1. 查询学生的主修专业
major_sql = "SELECT major_id FROM student WHERE student_number = %s;"
major_result = self.fetch(major_sql, student_number)
@@ -175,13 +225,75 @@ WHERE
class_name = self.fetch(sql_student, (student_number,))[0]['class_name']
# 使用class_name和day_of_week从schedule表获取course_id
sql_schedule = "SELECT course_id, FROM schedule WHERE day_of_week = %s AND class_name = %s;"
course_id = self.fetch(sql_schedule, (day_of_week, class_name))
print(course_id)
# 对于每一个course_id从course表中查询course_name
sql_course = "SELECT course_name FROM course WHERE course_id = %s;"
course_name = self.fetch(sql_course, (course_id['course_id'],))
data = {"course_name": course_name, "course_id": course_id}
sql_schedule = "SELECT course_id,period_id FROM schedule WHERE day_of_week = %s AND class_name = %s;"
schedule_results = self.fetch(sql_schedule, (day_of_week, class_name))
print(schedule_results)
data = []
for i in schedule_results:
course_id = i['course_id']
sql_course = "SELECT course_name FROM course WHERE course_id = %s;"
course_name = self.fetch(sql_course, (course_id))[0]['course_name']
id = i['period_id']
time = get_time_by_ids(id)
data.append({"course_name": course_name, "time": time})
return data
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_result = self.fetch(student_sql, (class_name, major_id))
values_list = []
# 检查是否有学生编号被返回
if student_sql_result:
for student in student_sql_result:
# 对于每个学生编号,创建一条记录的值元组
student_number = student['student_number']
values_list.append((student_number, course_id, course_name, date, status))
if class_student_result:
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))
# 如果没有任何学生编号,返回相应的消息
if not values_list:
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.

210
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
(
id INT AUTO_INCREMENT PRIMARY KEY,
@@ -27,11 +12,8 @@ INSERT INTO menu_items (menu_name, role, path, `order`)
VALUES ('课程信息', 'student', '/course-info', 1),
('课程签到', 'student', '/attendance', 2),
('公告信息', 'student', '/announcement', 3),
('签到提醒', 'student', '/attendance-reminder', 4);
INSERT INTO menu_items (menu_name, role, path, `order`)
VALUES ('课程类别', 'teacher', '/course-category', 1),
('签到提醒', 'student', '/attendance-reminder', 4),
('课程类别', 'teacher', '/course-category', 1),
('课程信息', 'teacher', '/course-info', 2),
('课程签到', 'teacher', '/attendance-teacher', 3),
('签到提醒', 'teacher', '/attendance-reminder', 4);
@@ -79,7 +61,6 @@ VALUES ('大学计算机基础', 'CF001', '必修', 3, '介绍计算机基础知
('离散数学导论', 'IDTM01', '必修', 3, '介绍离散数学的基础知识和应用'),
('计算机网络', 'CN002', '必修', 4, '学习计算机网络的基础理论和协议');
CREATE TABLE attendance_record
(
record_id INT AUTO_INCREMENT PRIMARY KEY,
@@ -90,6 +71,61 @@ CREATE TABLE attendance_record
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
(
@@ -117,27 +153,20 @@ CREATE TABLE student
FOREIGN KEY (major_id) REFERENCES major (major_id)
);
INSERT INTO student (user_id, student_number, student_name, class_id)
VALUES (5,'王伟', 202300001000, 1),
(6,'李娜', 202300001001, 1),
(7,'张伟', 202300001002, 1),
(8,'刘洋', 202300001003, 1),
(9,'陈敏', 202300001004, 1),
(10,'杨静', 202300001005, 2),
(11,'赵媛媛', 202300001006, 2),
(12,'黄进', 202300001007, 2),
(13,'周杰', 202300001008, 2),
(14,'吴琳', 202300001009, 2),
(15,'徐涛', 202300001010, 3),
(16,'孙怡', 202300001011, 3),
(17,'朱元璋', 202300001012, 3),
(18,'马云', 202300001013, 3),
(19,'胡雪', 202300001014, 3),
(20,'郭敬明', 202300001015, 4),
(21,'林芳', 202300001016, 4),
(22,'段誉', 202300001017, 4),
(23,'曹操', 202300001018, 4),
(24,'刘备', 202300001019, 4);
INSERT INTO student (user_id, student_number, student_name, class_name,major_id)
VALUES
(5,'X202301000001','学生1','2023级01班','000'),
(6,'X202301000002','学生2','2023级01班','000'),
(7,'X202301000003','学生3','2023级01班','000'),
(8,'X202301000004','学生4','2023级01班','000'),
(9,'X202302000005','学生5','2023级02班','000'),
(10,'X202302000006','学生6','2023级02班','000'),
(11,'X202302000007','学生7','2023级02班','000'),
(12,'X202302000008','学生8','2023级02班','000'),
(13,'X202303000009','学生9','2023级03班','001'),
(14,'X202303000010','学生10','2023级03班','001'),
(15,'X202303000011','学生11','2023级03班','001'),
(16,'X202303000012','学生12','2023级03班','001');
CREATE TABLE teacher
(
@@ -149,11 +178,11 @@ CREATE TABLE teacher
FOREIGN KEY (user_id) REFERENCES user (user_id)
);
INSERT INTO teacher (name, user_id)
VALUES ('教师1', 1),
('教师2', 2),
('教师3', 3),
('教师4', 4);
INSERT INTO teacher (name, user_id,teacher_number)
VALUES ('教师1', 1,'G0001'),
('教师2', 2,'G0002'),
('教师3', 3,'G0003'),
('教师4', 4,'G0004');
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
('G0000',1,'2023级01班','000'),
('G0000',3,'2023级01班','000'),
('G0001',2,'2023级01','000'),
('G0001',4,'2023级01','000');
('G0001',1,'2023级01班','000'),
('G0001',3,'2023级01班','000'),
('G0002',2,'2023级02','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
(
@@ -210,39 +242,45 @@ CREATE TABLE schedule
INSERT INTO schedule (day_of_week, period_id, teacher_number, class_name, course_id) VALUES
(1,1,'G0000','2023级01班',1),
(1,2,'G0000','2023级01班',3),
(1,3,'G0001','2023级01班',2),
(1,4,'G0001','2023级01班',4),
(2,1,'G0001','2023级01班',4),
(2,2,'G0001','2023级01班',2),
(2,3,'G0000','2023级01班',1),
(2,4,'G0000','2023级01班',3),
(3,1,'G0000','2023级01班',1),
(3,2,'G0000','2023级01班',3),
(3,3,'G0001','2023级01班',2),
(3,4,'G0001','2023级01班',4),
(4,1,'G0001','2023级01班',4),
(4,2,'G0001','2023级01班',2),
(4,3,'G0000','2023级01班',1),
(4,4,'G0000','2023级01班',3),
(5,1,'G0000','2023级01班',1),
(5,2,'G0000','2023级01班',3),
(5,3,'G0001','2023级01班',2),
(5,4,'G0001','2023级01班',4);
(1,1,'G0001','2023级01班',1),
(1,2,'G0001','2023级01班',3),
(1,3,'G0002','2023级01班',2),
(1,4,'G0002','2023级01班',4),
(2,1,'G0001','2023级01班',1),
(2,2,'G0001','2023级01班',3),
(2,3,'G0002','2023级01班',2),
(2,4,'G0002','2023级01班',4),
(3,1,'G0001','2023级01班',1),
(3,2,'G0001','2023级01班',3),
(3,3,'G0002','2023级01班',2),
(3,4,'G0002','2023级01班',4),
(4,1,'G0001','2023级01班',1),
(4,2,'G0001','2023级01班',3),
(4,3,'G0002','2023级01班',2),
(4,4,'G0002','2023级01班',4),
(5,1,'G0001','2023级01班',1),
(5,2,'G0001','2023级01班',3),
(5,3,'G0002','2023级01班',2),
(5,4,'G0002','2023级01班',4),
(1,1,'G0002','2023级03班',2),
(1,2,'G0002','2023级03班',4),
(1,3,'G0001','2023级02班',1),
(1,4,'G0001','2023级02班',3),
(2,1,'G0002','2023级02班',2),
(2,2,'G0002','2023级02班',4),
(2,3,'G0001','2023级02班',1),
(2,4,'G0001','2023级02班',3),
(3,1,'G0002','2023级03班',2),
(3,2,'G0002','2023级03班',4),
(3,3,'G0001','2023级02班',1),
(3,4,'G0001','2023级02班',3),
(4,1,'G0002','2023级02班',2),
(4,2,'G0002','2023级02班',4),
(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);
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');

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

View File

@@ -23,3 +23,13 @@ def check_now_time():
# 如果当前时间不在任何一个时间段内
return None, "当前不在任何课程时间段内"
def get_time_by_ids(id):
# 获取对应的时间信息并添加到结果列表
period = time_periods.get(id)
if period: # 确保id是有效的
return period["start_time"][0:5] + "-" + period["end_time"][0:5]
else:
return None