forked from ppuz2f57w/wlhdl
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
16 KiB
477 lines
16 KiB
import os
|
|
import json
|
|
import csv
|
|
from abc import ABC, abstractmethod
|
|
from datetime import date
|
|
from typing import List, Optional, Dict
|
|
from model.student import Student
|
|
|
|
class IStudentDAL(ABC):
|
|
"""学生信息数据访问层接口"""
|
|
|
|
@abstractmethod
|
|
def add_student(self, student: Student) -> bool:
|
|
"""添加学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def delete_student_by_id_card(self, id_card: str) -> bool:
|
|
"""根据身份证号删除学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def delete_student_by_stu_id(self, stu_id: str) -> bool:
|
|
"""根据学号删除学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def update_student(self, id_card: str, updated_student: Student) -> bool:
|
|
"""更新学生信息"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def find_student_by_id_card(self, id_card: str) -> Optional[Student]:
|
|
"""根据身份证号查找学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def find_student_by_stu_id(self, stu_id: str) -> Optional[Student]:
|
|
"""根据学号查找学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def find_students_by_name(self, name: str) -> List[Student]:
|
|
"""根据姓名查找学生(模糊匹配)"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def find_students_by_class_name(self, class_name: str) -> List[Student]:
|
|
"""根据班级名称查找学生(模糊匹配)"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def find_students_by_major(self, major: str) -> List[Student]:
|
|
"""根据专业查找学生(模糊匹配)"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_all_students(self) -> List[Student]:
|
|
"""获取所有学生信息"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_student_count(self) -> int:
|
|
"""获取学生总数"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_students_by_height_range(self, min_height: int, max_height: int) -> List[Student]:
|
|
"""按身高范围查找学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_students_by_enrollment_year(self, year: int) -> List[Student]:
|
|
"""按入学年份查找学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_students_by_age_range(self, min_age: int, max_age: int) -> List[Student]:
|
|
"""按年龄范围查找学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_students_by_major_group(self) -> Dict[str, List[Student]]:
|
|
"""按专业分组学生"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_average_height(self) -> float:
|
|
"""计算平均身高"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_average_weight(self) -> float:
|
|
"""计算平均体重"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_gender_ratio(self) -> Dict[Optional[bool], float]:
|
|
"""计算性别比例"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def clear_all_students(self) -> None:
|
|
"""清空所有学生信息"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def check_student_exists_by_id_card(self, id_card: str) -> bool:
|
|
"""检查身份证号是否存在"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def check_student_exists_by_stu_id(self, stu_id: str) -> bool:
|
|
"""检查学号是否存在"""
|
|
pass
|
|
|
|
|
|
class JsonStudentDAL(IStudentDAL):
|
|
"""JSON文件存储实现"""
|
|
|
|
def __init__(self, file_path: str):
|
|
self.file_path = file_path
|
|
self.__ensure_file_exists()
|
|
|
|
def __ensure_file_exists(self):
|
|
"""确保JSON文件存在"""
|
|
if not os.path.exists(self.file_path):
|
|
with open(self.file_path, 'w', encoding='utf-8') as file:
|
|
# noinspection PyTypeChecker
|
|
json.dump([], file)
|
|
|
|
def __load_students(self) -> List[Student]:
|
|
"""从JSON文件加载学生列表"""
|
|
try:
|
|
with open(self.file_path, 'r', encoding='utf-8') as file:
|
|
student_dicts = json.load(file)
|
|
return [Student.from_dict(sd) for sd in student_dicts]
|
|
except json.JSONDecodeError:
|
|
return []
|
|
|
|
def __save_students(self, students: List[Student]) -> None:
|
|
"""将学生列表保存到JSON文件"""
|
|
student_dicts = [s.to_dict() for s in students]
|
|
with open(self.file_path, 'w', encoding='utf-8') as file:
|
|
json.dump(student_dicts, file, ensure_ascii=False, indent=4)
|
|
|
|
# 实现接口方法
|
|
def add_student(self, student: Student) -> bool:
|
|
students = self.__load_students()
|
|
if any(s.id_card == student.id_card for s in students):
|
|
return False
|
|
if any(s.stu_id == student.stu_id for s in students):
|
|
return False
|
|
students.append(student)
|
|
self.__save_students(students)
|
|
return True
|
|
|
|
def delete_student_by_id_card(self, id_card: str) -> bool:
|
|
students = self.__load_students()
|
|
original_count = len(students)
|
|
students = [s for s in students if s.id_card != id_card]
|
|
if len(students) < original_count:
|
|
self.__save_students(students)
|
|
return True
|
|
return False
|
|
|
|
def delete_student_by_stu_id(self, stu_id: str) -> bool:
|
|
students = self.__load_students()
|
|
original_count = len(students)
|
|
students = [s for s in students if s.stu_id != stu_id]
|
|
if len(students) < original_count:
|
|
self.__save_students(students)
|
|
return True
|
|
return False
|
|
|
|
def update_student(self, id_card: str, updated_student: Student) -> bool:
|
|
students = self.__load_students()
|
|
for i, s in enumerate(students):
|
|
if s.id_card == id_card:
|
|
students[i] = updated_student
|
|
self.__save_students(students)
|
|
return True
|
|
return False
|
|
|
|
def find_student_by_id_card(self, id_card: str) -> Optional[Student]:
|
|
students = self.__load_students()
|
|
for s in students:
|
|
if s.id_card == id_card:
|
|
return s
|
|
return None
|
|
|
|
def find_student_by_stu_id(self, stu_id: str) -> Optional[Student]:
|
|
students = self.__load_students()
|
|
for s in students:
|
|
if s.stu_id == stu_id:
|
|
return s
|
|
return None
|
|
|
|
def find_students_by_name(self, name: str) -> List[Student]:
|
|
students = self.__load_students()
|
|
return [s for s in students if name.lower() in s.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 s.class_name and class_name.lower() in s.class_name.lower()]
|
|
|
|
def find_students_by_major(self, major: str) -> List[Student]:
|
|
students = self.__load_students()
|
|
return [s for s in students if s.major and major.lower() in s.major.lower()]
|
|
|
|
def get_all_students(self) -> List[Student]:
|
|
return self.__load_students()
|
|
|
|
def get_student_count(self) -> int:
|
|
return len(self.__load_students())
|
|
|
|
def get_students_by_height_range(self, min_height: int, max_height: int) -> List[Student]:
|
|
students = self.__load_students()
|
|
return [s for s in students if s.height and min_height <= s.height <= max_height]
|
|
|
|
def get_students_by_enrollment_year(self, year: int) -> List[Student]:
|
|
students = self.__load_students()
|
|
return [s for s in students if s.enrollment_date and s.enrollment_date.year == year]
|
|
|
|
def get_students_by_age_range(self, min_age: int, max_age: int) -> List[Student]:
|
|
students = self.__load_students()
|
|
today = date.today()
|
|
result = []
|
|
for s in students:
|
|
if s.birth_date:
|
|
age = today.year - s.birth_date.year
|
|
if (today.month, today.day) < (s.birth_date.month, s.birth_date.day):
|
|
age -= 1
|
|
if min_age <= age <= max_age:
|
|
result.append(s)
|
|
return result
|
|
|
|
def get_students_by_major_group(self) -> Dict[str, List[Student]]:
|
|
students = self.__load_students()
|
|
groups = {}
|
|
for s in students:
|
|
if s.major:
|
|
if s.major not in groups:
|
|
groups[s.major] = []
|
|
groups[s.major].append(s)
|
|
return groups
|
|
|
|
def get_average_height(self) -> float:
|
|
students = self.__load_students()
|
|
heights = [s.height for s in students if s.height is not None]
|
|
if not heights:
|
|
return 0.0
|
|
return sum(heights) / len(heights)
|
|
|
|
def get_average_weight(self) -> float:
|
|
students = self.__load_students()
|
|
weights = [s.weight for s in students if s.weight is not None]
|
|
if not weights:
|
|
return 0.0
|
|
return sum(weights) / len(weights)
|
|
|
|
def get_gender_ratio(self) -> Dict[Optional[bool], float]:
|
|
students = self.__load_students()
|
|
genders = [s.gender for s in students if s.gender is not None]
|
|
total = len(genders)
|
|
if total == 0:
|
|
return {True: 0.0, False: 0.0}
|
|
|
|
male_count = sum(1 for g in genders if g)
|
|
female_count = total - male_count
|
|
return {
|
|
True: male_count / total * 100,
|
|
False: female_count / total * 100
|
|
}
|
|
|
|
def clear_all_students(self) -> None:
|
|
self.__save_students([])
|
|
|
|
def check_student_exists_by_id_card(self, id_card: str) -> bool:
|
|
return self.find_student_by_id_card(id_card) is not None
|
|
|
|
def check_student_exists_by_stu_id(self, stu_id: str) -> bool:
|
|
return self.find_student_by_stu_id(stu_id) is not None
|
|
|
|
|
|
class CsvStudentDAL(IStudentDAL):
|
|
"""CSV文件存储实现"""
|
|
|
|
def __init__(self, file_path: str):
|
|
self.file_path = file_path
|
|
self.__ensure_file_exists()
|
|
|
|
def __ensure_file_exists(self):
|
|
"""确保CSV文件存在"""
|
|
if not os.path.exists(self.file_path):
|
|
with open(self.file_path, 'w', encoding='utf-8', newline='') as file:
|
|
writer = csv.writer(file)
|
|
writer.writerow([
|
|
'name', 'id_card', 'stu_id', 'gender', 'height', 'weight',
|
|
'enrollment_date', 'class_name', 'major', 'email', 'phone'
|
|
])
|
|
|
|
def __load_students(self) -> List[Student]:
|
|
"""从CSV文件加载学生列表"""
|
|
students = []
|
|
try:
|
|
with open(self.file_path, 'r', encoding='utf-8', newline='') as file:
|
|
reader = csv.DictReader(file)
|
|
for row in reader:
|
|
# 转换数据类型
|
|
if row['gender']:
|
|
row['gender'] = True if row['gender'] == 'True' else False
|
|
|
|
if row['height']:
|
|
row['height'] = int(row['height'])
|
|
|
|
if row['weight']:
|
|
row['weight'] = float(row['weight'])
|
|
|
|
if row['enrollment_date']:
|
|
row['enrollment_date'] = date.fromisoformat(row['enrollment_date'])
|
|
|
|
students.append(Student.from_dict(row))
|
|
except (FileNotFoundError, csv.Error, ValueError):
|
|
pass
|
|
return students
|
|
|
|
def __save_students(self, students: List[Student]) -> None:
|
|
"""将学生列表保存到CSV文件"""
|
|
with open(self.file_path, 'w', encoding='utf-8', newline='') as file:
|
|
fieldnames = [
|
|
'name', 'id_card', 'stu_id', 'gender', 'height', 'weight',
|
|
'enrollment_date', 'class_name', 'major', 'email', 'phone'
|
|
]
|
|
writer = csv.DictWriter(file, fieldnames=fieldnames)
|
|
writer.writeheader()
|
|
for student in students:
|
|
writer.writerow(student.to_dict())
|
|
|
|
# 实现接口方法
|
|
def add_student(self, student: Student) -> bool:
|
|
students = self.__load_students()
|
|
if any(s.id_card == student.id_card for s in students):
|
|
return False
|
|
if any(s.stu_id == student.stu_id for s in students):
|
|
return False
|
|
students.append(student)
|
|
self.__save_students(students)
|
|
return True
|
|
|
|
def delete_student_by_id_card(self, id_card: str) -> bool:
|
|
students = self.__load_students()
|
|
original_count = len(students)
|
|
students = [s for s in students if s.id_card != id_card]
|
|
if len(students) < original_count:
|
|
self.__save_students(students)
|
|
return True
|
|
return False
|
|
|
|
def delete_student_by_stu_id(self, stu_id: str) -> bool:
|
|
students = self.__load_students()
|
|
original_count = len(students)
|
|
students = [s for s in students if s.stu_id != stu_id]
|
|
if len(students) < original_count:
|
|
self.__save_students(students)
|
|
return True
|
|
return False
|
|
|
|
def update_student(self, id_card: str, updated_student: Student) -> bool:
|
|
students = self.__load_students()
|
|
for i, s in enumerate(students):
|
|
if s.id_card == id_card:
|
|
students[i] = updated_student
|
|
self.__save_students(students)
|
|
return True
|
|
return False
|
|
|
|
def find_student_by_id_card(self, id_card: str) -> Optional[Student]:
|
|
students = self.__load_students()
|
|
for s in students:
|
|
if s.id_card == id_card:
|
|
return s
|
|
return None
|
|
|
|
def find_student_by_stu_id(self, stu_id: str) -> Optional[Student]:
|
|
students = self.__load_students()
|
|
for s in students:
|
|
if s.stu_id == stu_id:
|
|
return s
|
|
return None
|
|
|
|
def find_students_by_name(self, name: str) -> List[Student]:
|
|
students = self.__load_students()
|
|
return [s for s in students if name.lower() in s.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 s.class_name and class_name.lower() in s.class_name.lower()]
|
|
|
|
def find_students_by_major(self, major: str) -> List[Student]:
|
|
students = self.__load_students()
|
|
return [s for s in students if s.major and major.lower() in s.major.lower()]
|
|
|
|
def get_all_students(self) -> List[Student]:
|
|
return self.__load_students()
|
|
|
|
def get_student_count(self) -> int:
|
|
return len(self.__load_students())
|
|
|
|
def get_students_by_height_range(self, min_height: int, max_height: int) -> List[Student]:
|
|
students = self.__load_students()
|
|
return [s for s in students if s.height and min_height <= s.height <= max_height]
|
|
|
|
def get_students_by_enrollment_year(self, year: int) -> List[Student]:
|
|
students = self.__load_students()
|
|
return [s for s in students if s.enrollment_date and s.enrollment_date.year == year]
|
|
|
|
def get_students_by_age_range(self, min_age: int, max_age: int) -> List[Student]:
|
|
students = self.__load_students()
|
|
today = date.today()
|
|
result = []
|
|
for s in students:
|
|
if s.birth_date:
|
|
age = today.year - s.birth_date.year
|
|
if (today.month, today.day) < (s.birth_date.month, s.birth_date.day):
|
|
age -= 1
|
|
if min_age <= age <= max_age:
|
|
result.append(s)
|
|
return result
|
|
|
|
def get_students_by_major_group(self) -> Dict[str, List[Student]]:
|
|
students = self.__load_students()
|
|
groups = {}
|
|
for s in students:
|
|
if s.major:
|
|
if s.major not in groups:
|
|
groups[s.major] = []
|
|
groups[s.major].append(s)
|
|
return groups
|
|
|
|
def get_average_height(self) -> float:
|
|
students = self.__load_students()
|
|
heights = [s.height for s in students if s.height is not None]
|
|
if not heights:
|
|
return 0.0
|
|
return sum(heights) / len(heights)
|
|
|
|
def get_average_weight(self) -> float:
|
|
students = self.__load_students()
|
|
weights = [s.weight for s in students if s.weight is not None]
|
|
if not weights:
|
|
return 0.0
|
|
return sum(weights) / len(weights)
|
|
|
|
def get_gender_ratio(self) -> Dict[Optional[bool], float]:
|
|
students = self.__load_students()
|
|
genders = [s.gender for s in students if s.gender is not None]
|
|
total = len(genders)
|
|
if total == 0:
|
|
return {True: 0.0, False: 0.0}
|
|
|
|
male_count = sum(1 for g in genders if g)
|
|
female_count = total - male_count
|
|
return {
|
|
True: male_count / total * 100,
|
|
False: female_count / total * 100
|
|
}
|
|
|
|
def clear_all_students(self) -> None:
|
|
self.__save_students([])
|
|
|
|
def check_student_exists_by_id_card(self, id_card: str) -> bool:
|
|
return self.find_student_by_id_card(id_card) is not None
|
|
|
|
def check_student_exists_by_stu_id(self, stu_id: str) -> bool:
|
|
return self.find_student_by_stu_id(stu_id) is not None
|