c1785 1 month ago
parent 36813aaa18
commit 4d4bd8e2e2

@ -14,13 +14,18 @@ class StudentBLL:
if not student.is_valid:
raise ValueError(f"学生数据校验不通过。错误信息:{student.get_errors()}")
return self.dal.add_student(student)
# 其他方法同理
def delete_student(self, sid: str) ->bool:
def delete_student(self, sid: str) -> bool:
if not self.dal.check_student_exists(sid):
raise ValueError(f"学生 ID {sid} 不存在。")
return self.dal.delete_student(sid)
def delete_student_by_id_number(self, id_number: str) -> bool:
student = self.dal.find_student_by_id_number(id_number)
if not student:
raise ValueError(f"身份证号为 {id_number} 的学生不存在。")
return self.dal.delete_student_by_id_number(id_number)
def update_student(self, sid: str, student: Student) -> bool:
if not self.dal.check_student_exists(sid):
raise ValueError(f"学生 ID {sid} 不存在。")
@ -30,20 +35,39 @@ class StudentBLL:
def update_student_partial(self, sid: str, name: Optional[str] = None, height: Optional[int] = None,
birth_date: Optional[date] = None, enrollment_date: Optional[date] = None,
class_name: Optional[str] = None) -> bool:
class_name: Optional[str] = None, major: Optional[str] = None,
phone: Optional[str] = None, email: Optional[str] = None) -> bool:
if not self.dal.check_student_exists(sid):
raise ValueError(f"学生 ID {sid} 不存在。")
return self.dal.update_student_partial(sid, name, height, birth_date, enrollment_date, class_name)
return self.dal.update_student_partial(sid, name, height, birth_date, enrollment_date,
class_name, major, phone, email)
def get_student_by_id(self, sid: str) -> Optional[Student]:
return self.dal.find_student_by_sid(sid)
def find_student_by_id_number(self, id_number: str) -> Optional[Student]:
return self.dal.find_student_by_id_number(id_number)
def get_students_by_name(self, name: str) -> List[Student]:
return self.dal.find_students_by_name(name)
def get_students_by_class(self, class_name: str) -> List[Student]:
return self.dal.find_students_by_class_name(class_name)
def get_students_by_major(self, major: str) -> List[Student]:
return self.dal.find_students_by_major(major)
def get_all_students(self) -> List[Student]:
return self.dal.get_all_students()
def count_students_by_major(self):
"""统计各专业学生人数"""
return self.dal.count_students_by_major()
def calculate_average_height_weight(self, group_by='major'):
"""计算平均身高/体重(按班级或专业)"""
return self.dal.calculate_average_height_weight(group_by)
def calculate_gender_ratio(self):
"""统计性别比例"""
return self.dal.calculate_gender_ratio()

@ -1,117 +1,10 @@
import json
import os, sys,csv
import os
import csv
from datetime import date
from typing import List, Optional
from model.Student import Student
import csv
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
sys.path.append(parent_dir)
from model.Student import Student
class StudentDALJSON:
def __init__(self, file_path="students.json"):
self.file_path = file_path
def get_all_students(self):
try:
with open(self.file_path, 'r', encoding='utf-8') as file:
student_dicts = json.load(file)
return [Student.from_dict(s) for s in student_dicts]
except (FileNotFoundError, json.JSONDecodeError):
return []
def save_students(self, students):
student_dicts = [student.to_dict() for student in students]
with open(self.file_path, 'w', encoding='utf-8') as file:
json.dump(student_dicts, file, ensure_ascii=False, indent=4)
# 新增导出为JSON
def export_to_json(self, file_path=None):
if not file_path:
file_path = self.file_path
students = self.get_all_students()
if not students:
print("没有数据可导出")
return
try:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump([s.to_dict() for s in students], f, ensure_ascii=False, indent=4)
print(f"数据已导出到 {file_path}")
except Exception as e:
print(f"导出失败: {e}")
# 新增导出为CSV
def export_to_csv(self, file_path="students.csv"):
students = self.get_all_students()
if not students:
print("没有数据可导出")
return
try:
with open(file_path, 'w', encoding='utf-8-sig', newline='') as f:
writer = csv.DictWriter(
f,
fieldnames=[
'student_id', 'name', 'gender', 'age', 'class_name',
'major', 'phone', 'email', 'id_number',
'height', 'weight', 'birth_date', 'enrollment_date'
]
)
writer.writeheader()
for student in students:
data = student.to_dict()
# 将日期对象转换为字符串
data['birth_date'] = data['birth_date'].isoformat()
data['enrollment_date'] = data['enrollment_date'].isoformat()
writer.writerow(data)
print(f"数据已导出到 {file_path}")
except Exception as e:
print(f"导出失败: {e}")
# 新增从JSON导入
def import_from_json(self, file_path):
try:
with open(file_path, 'r', encoding='utf-8') as f:
student_dicts = json.load(f)
students = []
for data in student_dicts:
# 转换日期字符串为日期对象
data['birth_date'] = date.fromisoformat(data['birth_date'])
data['enrollment_date'] = date.fromisoformat(data['enrollment_date'])
students.append(Student.from_dict(data))
self.save_students(students)
print(f"{file_path} 导入成功")
except FileNotFoundError:
print(f"文件不存在: {file_path}")
except json.JSONDecodeError:
print(f"JSON格式错误: {file_path}")
except Exception as e:
print(f"导入失败: {e}")
# 新增从CSV导入
def import_from_csv(self, file_path):
try:
with open(file_path, 'r', encoding='utf-8-sig') as f:
reader = csv.DictReader(f)
student_dicts = list(reader)
students = []
for data in student_dicts:
# 转换数值类型
data['age'] = int(data['age'])
data['height'] = float(data['height'])
data['weight'] = float(data['weight'])
# 转换日期类型
data['birth_date'] = date.fromisoformat(data['birth_date'])
data['enrollment_date'] = date.fromisoformat(data['enrollment_date'])
students.append(Student.from_dict(data))
self.save_students(students)
print(f"{file_path} 导入成功")
except FileNotFoundError:
print(f"文件不存在: {file_path}")
except Exception as e:
print(f"导入失败: {e}")
class StudentDAL:
def __init__(self, file_path: str):
self.file_path = file_path
@ -148,13 +41,24 @@ class StudentDAL:
return student
return None
def find_students_by_name(self, name: str) ->List[Student]:
def find_student_by_id_number(self, id_number: str) -> Optional[Student]:
students = self.load_students()
for student in students:
if student.id_number == id_number:
return student
return None
def find_students_by_name(self, name: str) -> List[Student]:
students = self.load_students()
return [student for student in students if name.lower() in student.name.lower()]
def find_students_by_class_name(self, class_name: str) ->List[Student]:
def find_students_by_class_name(self, class_name: str) -> List[Student]:
students = self.load_students()
return [s for s in students if class_name.lower() in s.class_name.lower()]
return [student for student in students if class_name.lower() in student.class_name.lower()]
def find_students_by_major(self, major: str) -> List[Student]:
students = self.load_students()
return [student for student in students if major.lower() in student.major.lower()]
def get_all_students(self) -> List[Student]:
return self.load_students()
@ -168,7 +72,10 @@ class StudentDAL:
return True
return False
def update_student_partial(self, sid:str, name:Optional[str]=None, height:Optional[float]=None, birth_date:Optional[date]=None, enrollment_date:Optional[date]=None, class_name:Optional[str]=None) ->bool:
def update_student_partial(self, sid: str, name: Optional[str] = None, height: Optional[int] = None,
birth_date: Optional[date] = None, enrollment_date: Optional[date] = None,
class_name: Optional[str] = None, major: Optional[str] = None,
phone: Optional[str] = None, email: Optional[str] = None) -> bool:
students = self.load_students()
for i, student in enumerate(students):
if student.sid == sid:
@ -182,12 +89,17 @@ class StudentDAL:
students[i].enrollment_date = enrollment_date
if class_name is not None:
students[i].class_name = class_name
if major is not None:
students[i].major = major
if phone is not None:
students[i].phone = phone
if email is not None:
students[i].email = email
self.save_students(students)
return True
return False
def delete_student(self, sid: str) -> bool:
"""根据学生 ID 删除学生"""
students = self.load_students()
initial_length = len(students)
students = [student for student in students if student.sid != sid]
@ -196,13 +108,22 @@ class StudentDAL:
return True
return False
def delete_student_by_id_number(self, id_number: str) -> bool:
students = self.load_students()
initial_length = len(students)
students = [student for student in students if student.id_number != id_number]
if len(students) < initial_length:
self.save_students(students)
return True
return False
def clear_all_students(self) -> None:
self.save_students([])
def get_student_count(self) -> int:
return len(self.load_students())
def check_student_exists(self, sid: str) ->bool:
def check_student_exists(self, sid: str) -> bool:
return self.find_student_by_sid(sid) is not None
def export_to_json(self, file_path):
@ -226,12 +147,28 @@ class StudentDAL:
def export_to_csv(self, file_path):
students = self.load_students()
with open(file_path, mode='w', encoding='utf-8', newline='') as file:
fieldnames = ['sid', 'name', 'height', 'birth_date', 'enrollment_date', 'class_name']
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for student in students:
writer.writerow(student.to_dict())
if not students:
print("没有数据可导出")
return
try:
with open(file_path, 'w', encoding='utf-8-sig', newline='') as f:
writer = csv.DictWriter(
f,
fieldnames=[
'sid', 'name', 'gender', 'height', 'weight', 'birth_date', 'enrollment_date',
'class_name', 'id_number', 'phone', 'email', 'major'
]
)
writer.writeheader()
for student in students:
data = student.to_dict()
# 将日期对象转换为字符串
data['birth_date'] = data['birth_date'].isoformat()
data['enrollment_date'] = data['enrollment_date'].isoformat()
writer.writerow(data)
print(f"数据已导出到 {file_path}")
except Exception as e:
print(f"导出失败: {e}")
def import_from_csv(self, file_path):
try:
@ -253,18 +190,18 @@ class StudentDAL:
students = self.load_students()
if not students:
return 0
total_height = sum(student.height for student in students)
total_height = sum([student.height for student in students])
return total_height / len(students)
def count_students_by_height_range(self, min_height, max_height):
def count_students_by_height_range(self, min_height: int, max_height: int):
students = self.load_students()
return len([student for student in students if min_height <= student.height <= max_height])
def count_students_by_enrollment_year(self, year):
def count_students_by_enrollment_year(self, year: int):
students = self.load_students()
return len([student for student in students if student.enrollment_date.year == year])
def count_students_by_age_range(self, min_age, max_age):
def count_students_by_age_range(self, min_age: int, max_age: int):
today = date.today()
students = self.load_students()
return len([student for student in students if min_age <= today.year - student.birth_date.year <= max_age])
@ -287,4 +224,52 @@ class StudentDAL:
except FileNotFoundError:
print(f"备份文件 {backup_path} 未找到。")
except Exception as e:
print(f"数据恢复失败: {e}")
print(f"数据恢复失败: {e}")
def count_students_by_major(self):
"""统计各专业学生人数"""
students = self.load_students()
major_count = {}
for student in students:
major = student.major
if major in major_count:
major_count[major] += 1
else:
major_count[major] = 1
return major_count
def calculate_average_height_weight(self, group_by='major'):
"""计算平均身高/体重(按班级或专业)"""
students = self.load_students()
group_info = {}
for student in students:
group_key = getattr(student, group_by)
if group_key not in group_info:
group_info[group_key] = {'height_sum': 0, 'weight_sum': 0, 'count': 0}
group_info[group_key]['height_sum'] += student.height
group_info[group_key]['weight_sum'] += student.weight
group_info[group_key]['count'] += 1
result = {}
for group, info in group_info.items():
average_height = info['height_sum'] / info['count']
average_weight = info['weight_sum'] / info['count']
result[group] = {'average_height': average_height, 'average_weight': average_weight}
return result
def calculate_gender_ratio(self):
"""统计性别比例"""
students = self.load_students()
male_count = 0
female_count = 0
for student in students:
if student.gender == '':
male_count += 1
elif student.gender == '':
female_count += 1
total_count = male_count + female_count
if total_count == 0:
return {'male_ratio': 0, 'female_ratio': 0}
male_ratio = male_count / total_count
female_ratio = female_count / total_count
return {'male_ratio': male_ratio, 'female_ratio': female_ratio}

@ -1,10 +1 @@
[
{
"sid": "110309230907041",
"name": "cgt",
"height": 180,
"birth_date": "2002-01-25",
"enrollment_date": "2023-09-01",
"class_name": "cs"
}
]
[]

@ -1,129 +1,9 @@
from datetime import date
from ui.studentui import StudentUI
def main():
"""主函数"""
ui = StudentUI()
while True:
ui.display_menu()
choice = ui.get_input("请输入你的选择: ")
if choice == '1':
ui.add_student()
elif choice == '2':
sid = ui.get_input("请输入要删除的学生学号: ")
try:
if ui.bll.delete_student(sid):
print("学生信息删除成功!")
else:
print("学生信息删除失败。")
except ValueError as e:
print(e)
elif choice == '3':
sid = ui.get_input("请输入要更新的学生学号: ")
student = ui.bll.get_student_by_id(sid)
if student:
# 获取更新信息
name = ui.get_input(f"请输入新的姓名(当前:{student.name}),不修改请直接回车: ")
height = ui.get_input(f"请输入新的身高(当前:{student.height}),不修改请直接回车: ")
birth_date = ui.get_input(f"请输入新的出生日期(当前:{student.birth_date}),不修改请直接回车: ")
enrollment_date = ui.get_input(f"请输入新的入学日期(当前:{student.enrollment_date}),不修改请直接回车: ")
class_name = ui.get_input(f"请输入新的班级(当前:{student.class_name}),不修改请直接回车: ")
if name:
student.name = name
if height:
student.height = int(height)
if birth_date:
student.birth_date = date.fromisoformat(birth_date)
if enrollment_date:
student.enrollment_date = date.fromisoformat(enrollment_date)
if class_name:
student.class_name = class_name
try:
if ui.bll.update_student(sid, student):
print("学生信息更新成功!")
else:
print("学生信息更新失败。")
except ValueError as e:
print(e)
else:
print(f"学生 ID {sid} 不存在。")
elif choice == '4':
ui.display_query_menu()
query_choice = ui.get_input("请输入你的查询选择: ")
if query_choice == '1':
students = ui.bll.get_all_students()
for student in students:
print(student)
elif query_choice == '2':
sid = ui.get_input("请输入要查询的学生学号: ")
student = ui.bll.get_student_by_id(sid)
if student:
print(student)
else:
print(f"学生 ID {sid} 不存在。")
elif query_choice == '3':
# 这里和按学号查询逻辑一样
sid = ui.get_input("请输入要查询的学生学号: ")
student = ui.bll.get_student_by_id(sid)
if student:
print(student)
else:
print(f"学生 ID {sid} 不存在。")
elif query_choice == '4':
name = ui.get_input("请输入要查询的学生姓名: ")
students = ui.bll.get_students_by_name(name)
for student in students:
print(student)
elif query_choice == '5':
class_name = ui.get_input("请输入要查询的班级: ")
students = ui.bll.get_students_by_class(class_name)
for student in students:
print(student)
elif query_choice == '0':
continue
elif choice == '5':
ui.display_stats_menu()
stats_choice = ui.get_input("请输入你的统计选择: ")
if stats_choice == '1':
count = ui.bll.dal.get_student_count()
print(f"学生总数为: {count}")
elif stats_choice == '2':
average_height = ui.bll.dal.get_average_height()
print(f"学生平均身高为: {average_height:.2f} cm")
elif stats_choice == '3':
min_height = int(ui.get_input("请输入最小身高: "))
max_height = int(ui.get_input("请输入最大身高: "))
count = ui.bll.dal.count_students_by_height_range(min_height, max_height)
print(f"身高在 {min_height} - {max_height} cm 之间的学生数量为: {count}")
elif stats_choice == '4':
year = int(ui.get_input("请输入入学年份: "))
count = ui.bll.dal.count_students_by_enrollment_year(year)
print(f"{year} 年入学的学生数量为: {count}")
elif stats_choice == '5':
min_age = int(ui.get_input("请输入最小年龄: "))
max_age = int(ui.get_input("请输入最大年龄: "))
count = ui.bll.dal.count_students_by_age_range(min_age, max_age)
print(f"年龄在 {min_age} - {max_age} 岁之间的学生数量为: {count}")
elif stats_choice == '0':
continue
elif choice == '6':
ui.display_import_export_menu()
import_export_choice = ui.get_input("请输入你的导入导出选择: ")
if import_export_choice != '0':
ui.handle_import_export(import_export_choice)
else:
continue
elif choice == '7':
try:
ui.bll.dal.clear_all_students()
print("所有学生信息已清空!")
except Exception as e:
print(f"清空学生信息失败: {e}")
elif choice == '0':
break
ui.run()
if __name__ == '__main__':
main()
if __name__ == "__main__":
main()

@ -1,27 +1,46 @@
from datetime import date
import re
class Student:
def __init__(self, sid: str, name: str, height: str, birth_date: date | str, enrollment_date: date | str, class_name: str):
def __init__(self, sid: str, name: str, gender: str, height: int, weight: float, birth_date: date | str,
enrollment_date: date | str, class_name: str, id_number: str, phone: str, email: str, major: str):
self.sid = sid
self.name = name.strip()
self.height = int(height) # 转换为整数类型
self.gender = gender
self.height = height
self.weight = weight
self.birth_date = birth_date if isinstance(birth_date, date) else date.fromisoformat(birth_date)
self.enrollment_date = enrollment_date if isinstance(enrollment_date, date) else date.fromisoformat(enrollment_date)
self.class_name = class_name
self.id_number = id_number
self.phone = phone
self.email = email
self.major = major
self._validation_errors = []
self._validate_height()
self._validate_weight()
self._validate_date()
self._validate_name()
self._validate_id_number()
self._validate_gender()
def _validate_height(self) -> None:
if not (50 <= self.height <= 250):
self._validation_errors.append(f"身高{self.height}cm超出合理范围")
def _validate_weight(self) -> None:
if not (5 <= self.weight <= 300):
self._validation_errors.append(f"体重{self.weight}kg超出合理范围")
def _validate_name(self) -> None:
if not (2 <= len(self.name) <= 20) or not self.name.isprintable():
self._validation_errors.append("姓名长度需在2-20个字符之间且为可打印字符")
if not self.name.isprintable():
if not (2 <= len(self.name) <= 20):
self._validation_errors.append("姓名长度需在2-20个字符之间")
if not all('\u4e00' <= char <= '\u9fff' or char.isascii() for char in self.name):
self._validation_errors.append("姓名只能包含中文、英文或数字")
def _validate_id_number(self) -> None:
if not re.match(r'^\d{17}[\dXx]$', self.id_number):
self._validation_errors.append("身份证号格式不正确")
def _validate_date(self) -> None:
today = date.today()
@ -29,8 +48,12 @@ class Student:
self._validation_errors.append('出生日期不能在未来')
if self.enrollment_date > today:
self._validation_errors.append('入学日期不能在未来')
if self.birth_date > self.enrollment_date:
self._validation_errors.append('入学日期不能早于出生日期')
if (self.enrollment_date.year - self.birth_date.year) < 15:
self._validation_errors.append('入学年龄应至少15岁')
def _validate_gender(self) -> None:
if self.gender not in ['', '']:
self._validation_errors.append('性别只能是""""')
@property
def is_valid(self) -> bool:
@ -44,47 +67,66 @@ class Student:
return NotImplemented
return (self.sid == other.sid and
self.name == other.name and
self.gender == other.gender and
self.height == other.height and
self.weight == other.weight and
self.birth_date == other.birth_date and
self.enrollment_date == other.enrollment_date and
self.class_name == other.class_name
)
self.class_name == other.class_name and
self.id_number == other.id_number and
self.phone == other.phone and
self.email == other.email and
self.major == other.major)
@classmethod
def from_dict(cls, data: dict) -> 'Student':
birth_date = data['birth_date'] if isinstance(data['birth_date'], date) else date.fromisoformat(
data['birth_date'])
enrollment_date = data['enrollment_date'] if isinstance(data['enrollment_date'], date) else date.fromisoformat(
data['enrollment_date'])
birth_date = data['birth_date'] if isinstance(data['birth_date'], date) else date.fromisoformat(data['birth_date'])
enrollment_date = data['enrollment_date'] if isinstance(data['enrollment_date'], date) else date.fromisoformat(data['enrollment_date'])
return cls(
sid=data['sid'],
name=data['name'].strip(),
name=data['name'],
gender=data['gender'],
height=data['height'],
weight=data['weight'],
birth_date=birth_date,
enrollment_date=enrollment_date,
class_name=data['class_name']
class_name=data['class_name'],
id_number=data['id_number'],
phone=data['phone'],
email=data['email'],
major=data['major']
)
def to_dict(self):
def to_dict(self) -> dict:
return {
'sid': self.sid,
'name': self.name,
'height': self.height,
'birth_date': self.birth_date.isoformat(),
'enrollment_date': self.enrollment_date.isoformat(),
'class_name': self.class_name
}
'sid': self.sid,
'name': self.name,
'gender': self.gender,
'height': self.height,
'weight': self.weight,
'birth_date': self.birth_date.isoformat(),
'enrollment_date': self.enrollment_date.isoformat(),
'class_name': self.class_name,
'id_number': self.id_number,
'phone': self.phone,
'email': self.email,
'major': self.major
}
def __repr__(self) -> str:
return (
f"{self.__class__.__name__}("
f"sid='{self.sid}', "
f"name='{self.name}',"
f"height={self.height},"
f"birth_date=date({self.birth_date.year},{self.birth_date.month},{self.birth_date.day}), "
f"enrollment_date=date({self.enrollment_date.year},{self.enrollment_date.month},{self.enrollment_date.day}), "
f"class_name='{self.class_name}'"
f"name='{self.name}', "
f"gender='{self.gender}', "
f"height={self.height}, "
f"weight={self.weight}, "
f"birth_date=date({self.birth_date.year}, {self.birth_date.month}, {self.birth_date.day}), "
f"enrollment_date=date({self.enrollment_date.year}, {self.enrollment_date.month}, {self.enrollment_date.day}), "
f"class_name='{self.class_name}', "
f"id_number='{self.id_number}', "
f"phone='{self.phone}', "
f"email='{self.email}', "
f"major='{self.major}'"
f")"
)
)

@ -4,19 +4,18 @@ from datetime import date
from bll.student_bll import StudentBLL
from dal.student_dal_json import StudentDAL
from model.Student import Student
from dal.student_dal_json import StudentDALJSON # 确保导入
class StudentUI:
def __init__(self):
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
file_path = os.path.join(parent_dir, "data", "student.json")
data_dir = os.path.join(parent_dir, "data")
os.makedirs(data_dir, exist_ok=True)
file_path = os.path.join(data_dir, "students.json")
dal = StudentDAL(file_path)
self.bll = StudentBLL(dal)
self.file_path = file_path
def display_menu(self):
"""显示主菜单"""
os.system('cls' if os.name == 'nt' else 'clear')
@ -33,6 +32,17 @@ class StudentUI:
print("0. 退出系统")
print("*" * 50)
def display_delete_menu(self):
"""显示删除子菜单"""
os.system('cls' if os.name == 'nt' else 'clear')
print("*" * 50)
print(" 学生信息管理系统 - 删除菜单 ")
print("*" * 50)
print("1. 按学号删除学生信息")
print("2. 按身份证号删除学生信息")
print("0. 返回上一级")
print("*" * 50)
def display_query_menu(self):
"""显示查询子菜单"""
os.system('cls' if os.name == 'nt' else 'clear')
@ -44,6 +54,7 @@ class StudentUI:
print("3. 按学号查询")
print("4. 按姓名查询")
print("5. 按班级查询")
print("6. 按专业查询")
print("0. 返回上一级")
print("*" * 50)
@ -58,6 +69,10 @@ class StudentUI:
print("3. 按身高范围统计")
print("4. 按入学年份统计")
print("5. 按年龄范围统计")
print("6. 统计各专业学生人数")
print("7. 计算平均身高/体重(按班级)")
print("8. 计算平均身高/体重(按专业)")
print("9. 统计性别比例")
print("0. 返回上一级")
print("*" * 50)
@ -71,6 +86,8 @@ class StudentUI:
print("2. 从JSON导入数据")
print("3. 导出数据到CSV")
print("4. 从CSV导入数据")
print("5. 备份数据")
print("6. 恢复数据")
print("0. 返回上一级")
print("*" * 50)
@ -89,62 +106,94 @@ class StudentUI:
print("*" * 50)
name = self.get_input("请输入姓名: ")
while not (2 <= len(name) <= 20):
print("姓名长度需在2-20个字符之间")
name = self.get_input("请输入姓名: ")
gender = self.get_input("请输入性别(男/女): ")
while gender not in ['', '']:
print("性别只能是''''")
gender = self.get_input("请输入性别(男/女): ")
id_number = self.get_input("请输入身份证号: ")
while not re.match(r'^\d{17}[\dXx]$', id_number):
print("身份证号格式不正确,请输入 18 位身份证号。")
print("身份证号格式不正确,请输入18位身份证号。")
id_number = self.get_input("请输入身份证号: ")
while True:
student_id = self.get_input("请输入学号: ")
if re.match(r'^\d{10,15}$', student_id): # 假设学号是10 - 15位数字
break
print("学号格式不正确请输入10到15位数字")
gender = self.get_input("请输入性别(男/女): ")
if re.match(r'^\d{10,15}$', student_id): # 假设学号是10-15位数字
if not self.bll.dal.check_student_exists(student_id):
break
print("学号已存在,请重新输入。")
else:
print("学号格式不正确请输入10到15位数字")
while True:
try:
height = float(self.get_input("请输入身高(cm): "))
if height <= 0 or height > 250:
raise ValueError
break
if 50 <= height <= 250:
break
print("身高必须在50到250cm之间请重新输入。")
except ValueError:
print("身高必须为0到250之间的有效数值,请重新输入")
print("身高必须为有效数值,请重新输入")
while True:
try:
weight = float(self.get_input("请输入体重(kg): "))
if weight <= 0 or weight > 500:
raise ValueError
break
if 5 <= weight <= 300:
break
print("体重必须在5到300kg之间请重新输入。")
except ValueError:
print("体重必须为0到500之间的有效数值,请重新输入")
print("体重必须为有效数值,请重新输入")
# 从身份证号中提取出生日期
birth_date_str = id_number[6:14]
birth_date = date(int(birth_date_str[:4]), int(birth_date_str[4:6]), int(birth_date_str[6:]))
# 修改后的日期输入验证
# 电话号码验证
while True:
phone = self.get_input("请输入电话号码: ")
if re.match(r'^1[3-9]\d{9}$', phone):
break
print("电话号码格式不正确请输入11位数字")
# 邮箱验证
while True:
email = self.get_input("请输入邮箱地址: ")
if re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', email):
break
print("邮箱格式不正确,请重新输入")
# 入学日期验证
while True:
enrollment_date = self.get_input("请输入入学日期(YYYY-MM-DD): ")
try:
enrollment_date_obj = date.fromisoformat(enrollment_date)
break
if enrollment_date_obj >= birth_date:
break
print("入学日期不能早于出生日期,请重新输入。")
except ValueError:
print("日期格式错误,请使用 YYYY-MM-DD 格式例如2023-09-01")
print("日期格式错误,请使用YYYY-MM-DD格式例如2023-09-01")
class_name = self.get_input("请输入班级: ")
major = self.get_input("请输入专业: ")
# 创建 Student 对象
# 创建Student对象
try:
student = Student(
sid=student_id,
name=name,
gender=gender,
height=height,
birth_date=birth_date, # 使用从身份证提取的出生日期
weight=weight,
birth_date=birth_date,
enrollment_date=enrollment_date_obj,
class_name=class_name
class_name=class_name,
id_number=id_number,
phone=phone,
email=email,
major=major
)
if self.bll.add_student(student):
print("学生信息添加成功!")
@ -153,20 +202,333 @@ class StudentUI:
except ValueError as e:
print(f"添加学生失败: {e}")
def handle_delete(self, choice):
"""处理删除操作"""
if choice == '1':
sid = self.get_input("请输入要删除的学生学号: ")
try:
if self.bll.delete_student(sid):
print("学生信息删除成功!")
else:
print("学生信息删除失败。")
except ValueError as e:
print(e)
elif choice == '2':
id_number = self.get_input("请输入要删除的学生身份证号: ")
try:
if self.bll.delete_student_by_id_number(id_number):
print("学生信息删除成功!")
else:
print("学生信息删除失败。")
except ValueError as e:
print(e)
elif choice == '0':
return
else:
print("无效的选择,请重新输入。")
def update_student(self):
"""更新学生信息"""
os.system('cls' if os.name == 'nt' else 'clear')
print("*" * 50)
print(" 更新学生信息 ")
print("*" * 50)
sid = self.get_input("请输入要更新的学生学号: ")
student = self.bll.get_student_by_id(sid)
if student:
print(f"当前学生信息: {student}")
# 获取更新信息
name = self.get_input(f"请输入新的姓名(当前:{student.name}),不修改请直接回车: ")
gender = self.get_input(f"请输入新的性别(当前:{student.gender}),不修改请直接回车: ")
height = self.get_input(f"请输入新的身高(当前:{student.height}),不修改请直接回车: ")
weight = self.get_input(f"请输入新的体重(当前:{student.weight}),不修改请直接回车: ")
birth_date = self.get_input(f"请输入新的出生日期(当前:{student.birth_date}),不修改请直接回车: ")
enrollment_date = self.get_input(f"请输入新的入学日期(当前:{student.enrollment_date}),不修改请直接回车: ")
class_name = self.get_input(f"请输入新的班级(当前:{student.class_name}),不修改请直接回车: ")
major = self.get_input(f"请输入新的专业(当前:{student.major}),不修改请直接回车: ")
phone = self.get_input(f"请输入新的电话号码(当前:{student.phone}),不修改请直接回车: ")
email = self.get_input(f"请输入新的邮箱地址(当前:{student.email}),不修改请直接回车: ")
# 更新学生信息
update_fields = {}
if name:
update_fields['name'] = name
if gender:
update_fields['gender'] = gender
if height:
update_fields['height'] = int(height)
if weight:
update_fields['weight'] = float(weight)
if birth_date:
update_fields['birth_date'] = date.fromisoformat(birth_date)
if enrollment_date:
update_fields['enrollment_date'] = date.fromisoformat(enrollment_date)
if class_name:
update_fields['class_name'] = class_name
if major:
update_fields['major'] = major
if phone:
update_fields['phone'] = phone
if email:
update_fields['email'] = email
if update_fields:
try:
if self.bll.update_student_partial(sid, **update_fields):
print("学生信息更新成功!")
else:
print("学生信息更新失败。")
except ValueError as e:
print(e)
else:
print("未输入任何更新信息,操作取消。")
else:
print(f"学生 ID {sid} 不存在。")
def handle_query(self, choice):
"""处理查询操作"""
if choice == '1':
students = self.bll.get_all_students()
if not students:
print("没有找到学生信息。")
return
print("\n所有学生信息:")
print(f"{'学号':<15}{'姓名':<10}{'性别':<5}{'年龄':<5}{'班级':<15}{'专业':<15}{'身高(cm)':<10}{'体重(kg)':<10}")
print("-" * 80)
today = date.today()
for student in students:
age = today.year - student.birth_date.year
if today < date(today.year, student.birth_date.month, student.birth_date.day):
age -= 1
print(f"{student.sid:<15}{student.name:<10}{student.gender:<5}{age:<5}{student.class_name:<15}{student.major:<15}{student.height:<10}{student.weight:<10}")
elif choice == '2':
id_number = self.get_input("请输入身份证号: ")
student = self.bll.find_student_by_id_number(id_number)
if student:
print("\n找到学生信息:")
print(f"学号: {student.sid}")
print(f"姓名: {student.name}")
print(f"性别: {student.gender}")
print(f"年龄: {date.today().year - student.birth_date.year}")
print(f"班级: {student.class_name}")
print(f"专业: {student.major}")
print(f"身高: {student.height} cm")
print(f"体重: {student.weight} kg")
print(f"身份证号: {student.id_number}")
print(f"电话号码: {student.phone}")
print(f"邮箱地址: {student.email}")
print(f"出生日期: {student.birth_date}")
print(f"入学日期: {student.enrollment_date}")
else:
print(f"未找到身份证号为 {id_number} 的学生。")
elif choice == '3':
sid = self.get_input("请输入学号: ")
student = self.bll.get_student_by_id(sid)
if student:
print("\n找到学生信息:")
print(f"学号: {student.sid}")
print(f"姓名: {student.name}")
print(f"性别: {student.gender}")
print(f"年龄: {date.today().year - student.birth_date.year}")
print(f"班级: {student.class_name}")
print(f"专业: {student.major}")
print(f"身高: {student.height} cm")
print(f"体重: {student.weight} kg")
print(f"身份证号: {student.id_number}")
print(f"电话号码: {student.phone}")
print(f"邮箱地址: {student.email}")
print(f"出生日期: {student.birth_date}")
print(f"入学日期: {student.enrollment_date}")
else:
print(f"未找到学号为 {sid} 的学生。")
elif choice == '4':
name = self.get_input("请输入姓名: ")
students = self.bll.get_students_by_name(name)
if not students:
print(f"未找到姓名包含 '{name}' 的学生。")
return
print(f"\n找到 {len(students)} 名学生:")
print(f"{'学号':<15}{'姓名':<10}{'性别':<5}{'年龄':<5}{'班级':<15}{'专业':<15}{'身高(cm)':<10}{'体重(kg)':<10}")
print("-" * 80)
today = date.today()
for student in students:
age = today.year - student.birth_date.year
if today < date(today.year, student.birth_date.month, student.birth_date.day):
age -= 1
print(f"{student.sid:<15}{student.name:<10}{student.gender:<5}{age:<5}{student.class_name:<15}{student.major:<15}{student.height:<10}{student.weight:<10}")
elif choice == '5':
class_name = self.get_input("请输入班级: ")
students = self.bll.get_students_by_class(class_name)
if not students:
print(f"未找到班级包含 '{class_name}' 的学生。")
return
print(f"\n找到 {len(students)} 名学生:")
print(f"{'学号':<15}{'姓名':<10}{'性别':<5}{'年龄':<5}{'班级':<15}{'专业':<15}{'身高(cm)':<10}{'体重(kg)':<10}")
print("-" * 80)
today = date.today()
for student in students:
age = today.year - student.birth_date.year
if today < date(today.year, student.birth_date.month, student.birth_date.day):
age -= 1
print(f"{student.sid:<15}{student.name:<10}{student.gender:<5}{age:<5}{student.class_name:<15}{student.major:<15}{student.height:<10}{student.weight:<10}")
elif choice == '6':
major = self.get_input("请输入专业: ")
students = self.bll.get_students_by_major(major)
if not students:
print(f"未找到专业包含 '{major}' 的学生。")
return
print(f"\n找到 {len(students)} 名学生:")
print(f"{'学号':<15}{'姓名':<10}{'性别':<5}{'年龄':<5}{'班级':<15}{'专业':<15}{'身高(cm)':<10}{'体重(kg)':<10}")
print("-" * 80)
today = date.today()
for student in students:
age = today.year - student.birth_date.year
if today < date(today.year, student.birth_date.month, student.birth_date.day):
age -= 1
print(f"{student.sid:<15}{student.name:<10}{student.gender:<5}{age:<5}{student.class_name:<15}{student.major:<15}{student.height:<10}{student.weight:<10}")
elif choice == '0':
return
else:
print("无效的选择,请重新输入。")
def handle_stats(self, choice):
"""处理统计分析操作"""
if choice == '1':
count = self.bll.dal.get_student_count()
print(f"学生总数为: {count}")
elif choice == '2':
average_height = self.bll.dal.get_average_height()
print(f"学生平均身高为: {average_height:.2f} cm")
elif choice == '3':
min_height = int(self.get_input("请输入最小身高: "))
max_height = int(self.get_input("请输入最大身高: "))
count = self.bll.dal.count_students_by_height_range(min_height, max_height)
print(f"身高在 {min_height} - {max_height} cm 之间的学生数量为: {count}")
elif choice == '4':
year = int(self.get_input("请输入入学年份: "))
count = self.bll.dal.count_students_by_enrollment_year(year)
print(f"{year} 年入学的学生数量为: {count}")
elif choice == '5':
min_age = int(self.get_input("请输入最小年龄: "))
max_age = int(self.get_input("请输入最大年龄: "))
count = self.bll.dal.count_students_by_age_range(min_age, max_age)
print(f"年龄在 {min_age} - {max_age} 岁之间的学生数量为: {count}")
elif choice == '6':
major_counts = self.bll.count_students_by_major()
print("\n各专业学生人数统计:")
print(f"{'专业':<20}{'人数'}")
print("-" * 30)
for major, count in major_counts.items():
print(f"{major:<20}{count}")
elif choice == '7':
print("\n按班级统计平均身高/体重:")
class_stats = self.bll.calculate_average_height_weight('class_name')
print(f"{'班级':<20}{'平均身高(cm)':<15}{'平均体重(kg)'}")
print("-" * 45)
for class_name, stats in class_stats.items():
print(f"{class_name:<20}{stats['average_height']:<15.2f}{stats['average_weight']:.2f}")
elif choice == '8':
print("\n按专业统计平均身高/体重:")
major_stats = self.bll.calculate_average_height_weight('major')
print(f"{'专业':<20}{'平均身高(cm)':<15}{'平均体重(kg)'}")
print("-" * 45)
for major, stats in major_stats.items():
print(f"{major:<20}{stats['average_height']:<15.2f}{stats['average_weight']:.2f}")
elif choice == '9':
gender_ratio = self.bll.calculate_gender_ratio()
print("\n性别比例统计:")
print(f"男生比例: {gender_ratio['male_ratio']:.2%}")
print(f"女生比例: {gender_ratio['female_ratio']:.2%}")
elif choice == '0':
return
else:
print("无效的选择,请重新输入。")
def handle_import_export(self, choice):
"""处理数据导入导出操作"""
if choice == '1':
file_path = self.get_input("请输入要导出的 JSON 文件路径: ")
file_path = self.get_input("请输入要导出的JSON文件路径: ")
self.bll.dal.export_to_json(file_path)
print("数据导出到 JSON 成功!")
print("数据导出到JSON成功")
elif choice == '2':
file_path = self.get_input("请输入要导入的 JSON 文件路径: ")
file_path = self.get_input("请输入要导入的JSON文件路径: ")
self.bll.dal.import_from_json(file_path)
print("数据从 JSON 导入成功!")
print("数据从JSON导入成功")
elif choice == '3':
file_path = self.get_input("请输入要导出的 CSV 文件路径: ")
file_path = self.get_input("请输入要导出的CSV文件路径: ")
self.bll.dal.export_to_csv(file_path)
print("数据导出到 CSV 成功!")
print("数据导出到CSV成功")
elif choice == '4':
file_path = self.get_input("请输入要导入的 CSV 文件路径: ")
file_path = self.get_input("请输入要导入的CSV文件路径: ")
self.bll.dal.import_from_csv(file_path)
print("数据从 CSV 导入成功!")
print("数据从CSV导入成功")
elif choice == '5':
backup_path = self.get_input("请输入备份文件路径: ")
self.bll.dal.backup_data(backup_path)
elif choice == '6':
backup_path = self.get_input("请输入恢复文件路径: ")
self.bll.dal.restore_data(backup_path)
elif choice == '0':
return
else:
print("无效的选择,请重新输入。")
def clear_all_students(self):
"""清空所有学生信息"""
os.system('cls' if os.name == 'nt' else 'clear')
print("*" * 50)
print(" 警告:清空所有学生信息 ")
print("*" * 50)
confirm = self.get_input("此操作将删除所有学生信息,且无法恢复。确定要继续吗?(y/n): ")
if confirm.lower() == 'y':
try:
self.bll.dal.clear_all_students()
print("所有学生信息已清空!")
except Exception as e:
print(f"清空学生信息失败: {e}")
def run(self):
"""运行主程序"""
while True:
self.display_menu()
choice = self.get_input("请输入你的选择: ")
if choice == '1':
self.add_student()
elif choice == '2':
while True:
self.display_delete_menu()
delete_choice = self.get_input("请输入你的删除选择: ")
if delete_choice == '0':
break
self.handle_delete(delete_choice)
elif choice == '3':
self.update_student()
elif choice == '4':
while True:
self.display_query_menu()
query_choice = self.get_input("请输入你的查询选择: ")
if query_choice == '0':
break
self.handle_query(query_choice)
elif choice == '5':
while True:
self.display_stats_menu()
stats_choice = self.get_input("请输入你的统计选择: ")
if stats_choice == '0':
break
self.handle_stats(stats_choice)
elif choice == '6':
while True:
self.display_import_export_menu()
import_export_choice = self.get_input("请输入你的导入导出选择: ")
if import_export_choice == '0':
break
self.handle_import_export(import_export_choice)
elif choice == '7':
self.clear_all_students()
elif choice == '0':
print("感谢使用学生信息管理系统,再见!")
break
else:
print("无效的选择,请重新输入。")
input("\n按Enter键继续...")
Loading…
Cancel
Save