Compare commits

..

3 Commits
main ... DAL

@ -0,0 +1,90 @@
from datetime import date
class Student:
def __init__(self, sid: str, name: str, height: str, birth_date: date | str, enrollment_date: date | str, class_name: str):
self.sid = sid
self.name = name.strip()
self.height = int(height) # 转换为整数类型
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._validation_errors = []
self._validate_height()
self._validate_date()
self._validate_name()
def _validate_height(self) -> None:
if not (50 <= self.height <= 250):
self._validation_errors.append(f"身高{self.height}cm超出合理范围")
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():
self._validation_errors.append("姓名长度需在2-20个字符之间")
def _validate_date(self) -> None:
today = date.today()
if self.birth_date > today:
self._validation_errors.append('出生日期不能在未来')
if self.enrollment_date > today:
self._validation_errors.append('入学日期不能在未来')
if self.birth_date > self.enrollment_date:
self._validation_errors.append('入学日期不能早于出生日期')
@property
def is_valid(self) -> bool:
return len(self._validation_errors) == 0
def get_errors(self) -> list[str]:
return self._validation_errors.copy()
def __eq__(self, other) -> bool:
if not isinstance(other, Student):
return NotImplemented
return (self.sid == other.sid and
self.name == other.name and
self.height == other.height and
self.birth_date == other.birth_date and
self.enrollment_date == other.enrollment_date and
self.class_name == other.class_name
)
@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'])
return cls(
sid=data['sid'],
name=data['name'].strip(),
height=data['height'],
birth_date=birth_date,
enrollment_date=enrollment_date,
class_name=data['class_name']
)
def to_dict(self):
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
}
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")"
)

@ -0,0 +1,290 @@
import json
import os, sys,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
self._ensure_file_exists()
def _ensure_file_exists(self):
if not os.path.exists(self.file_path):
with open(self.file_path, mode='w', encoding='utf-8') as file:
json.dump([], file)
def save_students(self, students: List[Student]) -> None:
student_dicts = [student.to_dict() for student in students]
with open(self.file_path, mode='w', encoding='utf-8') as file:
json.dump(student_dicts, file, ensure_ascii=False, indent=4)
def load_students(self) -> List[Student]:
with open(self.file_path, mode='r', encoding='utf-8') as file:
student_dicts = json.load(file)
return [Student.from_dict(student_dict) for student_dict in student_dicts]
def add_student(self, student: Student) -> bool:
students = self.load_students()
for existing_student in students:
if existing_student.sid == student.sid:
return False
students.append(student)
self.save_students(students)
return True
def find_student_by_sid(self, sid: str) -> Optional[Student]:
students = self.load_students()
for student in students:
if student.sid == sid:
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]:
students = self.load_students()
return [s for s in students if class_name.lower() in s.class_name.lower()]
def get_all_students(self) -> List[Student]:
return self.load_students()
def update_student(self, sid: str, updated_student: Student) -> bool:
students = self.load_students()
for i, student in enumerate(students):
if student.sid == sid:
students[i] = updated_student
self.save_students(students)
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:
students = self.load_students()
for i, student in enumerate(students):
if student.sid == sid:
if name is not None:
students[i].name = name
if height is not None:
students[i].height = height
if birth_date is not None:
students[i].birth_date = birth_date
if enrollment_date is not None:
students[i].enrollment_date = enrollment_date
if class_name is not None:
students[i].class_name = class_name
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]
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:
return self.find_student_by_sid(sid) is not None
def export_to_json(self, file_path):
students = self.load_students()
student_dicts = [student.to_dict() for student in students]
with open(file_path, mode='w', encoding='utf-8') as file:
json.dump(student_dicts, file, ensure_ascii=False, indent=4)
def import_from_json(self, file_path):
try:
with open(file_path, mode='r', encoding='utf-8') as file:
student_dicts = json.load(file)
students = [Student.from_dict(student_dict) for student_dict in student_dicts]
self.save_students(students)
except FileNotFoundError:
print(f"文件 {file_path} 未找到。")
except json.JSONDecodeError:
print(f"文件 {file_path} 不是有效的 JSON 文件。")
except ValueError as e:
print(f"数据转换错误: {e}")
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())
def import_from_csv(self, file_path):
try:
students = []
with open(file_path, mode='r', encoding='utf-8') as file:
reader = csv.DictReader(file)
for row in reader:
row['birth_date'] = date.fromisoformat(row['birth_date'])
row['enrollment_date'] = date.fromisoformat(row['enrollment_date'])
student = Student.from_dict(row)
students.append(student)
self.save_students(students)
except FileNotFoundError:
print(f"文件 {file_path} 未找到。")
except ValueError as e:
print(f"日期格式错误: {e}")
def get_average_height(self):
students = self.load_students()
if not students:
return 0
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):
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):
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):
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])
def backup_data(self, backup_path):
try:
with open(self.file_path, mode='r', encoding='utf-8') as src_file, \
open(backup_path, mode='w', encoding='utf-8') as dst_file:
dst_file.write(src_file.read())
print(f"数据备份到 {backup_path} 成功。")
except Exception as e:
print(f"数据备份失败: {e}")
def restore_data(self, backup_path):
try:
with open(backup_path, mode='r', encoding='utf-8') as src_file, \
open(self.file_path, mode='w', encoding='utf-8') as dst_file:
dst_file.write(src_file.read())
print(f"数据从 {backup_path} 恢复成功。")
except FileNotFoundError:
print(f"备份文件 {backup_path} 未找到。")
except Exception as e:
print(f"数据恢复失败: {e}")

@ -0,0 +1,172 @@
import os
import re
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")
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')
print("*" * 50)
print(" 学生信息管理系统 - 主菜单 ")
print("*" * 50)
print("1. 添加学生信息")
print("2. 删除学生信息")
print("3. 更新学生信息")
print("4. 查询学生信息")
print("5. 统计分析")
print("6. 数据导入导出")
print("7. 清空所有学生信息")
print("0. 退出系统")
print("*" * 50)
def display_query_menu(self):
"""显示查询子菜单"""
os.system('cls' if os.name == 'nt' else 'clear')
print("*" * 50)
print(" 学生信息管理系统 - 查询菜单 ")
print("*" * 50)
print("1. 查询所有学生")
print("2. 按身份证号查询")
print("3. 按学号查询")
print("4. 按姓名查询")
print("5. 按班级查询")
print("0. 返回上一级")
print("*" * 50)
def display_stats_menu(self):
"""显示统计分析子菜单"""
os.system('cls' if os.name == 'nt' else 'clear')
print("*" * 50)
print(" 学生信息管理系统 - 统计分析菜单 ")
print("*" * 50)
print("1. 学生总数")
print("2. 平均身高")
print("3. 按身高范围统计")
print("4. 按入学年份统计")
print("5. 按年龄范围统计")
print("0. 返回上一级")
print("*" * 50)
def display_import_export_menu(self):
"""显示数据导入导出子菜单"""
os.system('cls' if os.name == 'nt' else 'clear')
print("*" * 50)
print(" 学生信息管理系统 - 数据导入导出菜单 ")
print("*" * 50)
print("1. 导出数据到JSON")
print("2. 从JSON导入数据")
print("3. 导出数据到CSV")
print("4. 从CSV导入数据")
print("0. 返回上一级")
print("*" * 50)
def get_input(self, prompt: str) -> str:
"""获取用户输入,添加异常处理"""
try:
return input(prompt).strip()
except EOFError:
return "0" # 按Ctrl+D视为退出
def add_student(self):
"""添加学生信息"""
os.system('cls' if os.name == 'nt' else 'clear')
print("*" * 50)
print(" 添加学生信息 ")
print("*" * 50)
name = self.get_input("请输入姓名: ")
id_number = self.get_input("请输入身份证号: ")
while not re.match(r'^\d{17}[\dXx]$', id_number):
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("请输入性别(男/女): ")
while True:
try:
height = float(self.get_input("请输入身高(cm): "))
if height <= 0 or height > 250:
raise ValueError
break
except ValueError:
print("身高必须为0到250之间的有效数值请重新输入")
while True:
try:
weight = float(self.get_input("请输入体重(kg): "))
if weight <= 0 or weight > 500:
raise ValueError
break
except ValueError:
print("体重必须为0到500之间的有效数值请重新输入")
# 从身份证号中提取出生日期
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:
enrollment_date = self.get_input("请输入入学日期(YYYY-MM-DD): ")
try:
enrollment_date_obj = date.fromisoformat(enrollment_date)
break
except ValueError:
print("日期格式错误,请使用 YYYY-MM-DD 格式例如2023-09-01")
class_name = self.get_input("请输入班级: ")
major = self.get_input("请输入专业: ")
# 创建 Student 对象
try:
student = Student(
sid=student_id,
name=name,
height=height,
birth_date=birth_date, # 使用从身份证提取的出生日期
enrollment_date=enrollment_date_obj,
class_name=class_name
)
if self.bll.add_student(student):
print("学生信息添加成功!")
else:
print("学生信息添加失败。")
except ValueError as e:
print(f"添加学生失败: {e}")
def handle_import_export(self, choice):
if choice == '1':
file_path = self.get_input("请输入要导出的 JSON 文件路径: ")
self.bll.dal.export_to_json(file_path)
print("数据导出到 JSON 成功!")
elif choice == '2':
file_path = self.get_input("请输入要导入的 JSON 文件路径: ")
self.bll.dal.import_from_json(file_path)
print("数据从 JSON 导入成功!")
elif choice == '3':
file_path = self.get_input("请输入要导出的 CSV 文件路径: ")
self.bll.dal.export_to_csv(file_path)
print("数据导出到 CSV 成功!")
elif choice == '4':
file_path = self.get_input("请输入要导入的 CSV 文件路径: ")
self.bll.dal.import_from_csv(file_path)
print("数据从 CSV 导入成功!")
Loading…
Cancel
Save