增加一行

z'yzy
员卓豪 1 month ago
parent 2ad1efbadf
commit 0507891046

@ -1,259 +0,0 @@
import csv
import json
import sqlite3
from abc import ABC, abstractmethod
from datetime import date
from typing import List, Optional, Dict, Any, Union
class Student:
def __init__(self, name: str, id_card: str, stu_id: str, gender: Optional[bool] = None,
height: Optional[int] = None, weight: Optional[float] = None,
enrollment_date: Optional[Union[date, str]] = None,
class_name: Optional[str] = None, major: Optional[str] = None):
self.name = name
self.id_card = id_card
self.stu_id = stu_id
self.gender = gender
self.height = height
self.weight = weight
self.enrollment_date = date.fromisoformat(enrollment_date) if isinstance(enrollment_date,
str) else enrollment_date
self.class_name = class_name
self.major = major
self.age, self.birthday = self._calculate_age_and_birthday()
self._validation_errors = self._validate_all()
def _calculate_age_and_birthday(self) -> tuple[int, date]:
birth_year, birth_month, birth_day = map(int, (self.id_card[6:10], self.id_card[10:12], self.id_card[12:14]))
birthday = date(birth_year, birth_month, birth_day)
today = date.today()
age = today.year - birth_year - ((today.month, today.day) < (birth_month, birth_day))
return age, birthday
def _validate_all(self) -> List[str]:
errors = []
if len(self.id_card) != 18 or not self.id_card[:17].isdigit():
errors.append("身份证号必须为18位数字")
if not self.stu_id:
errors.append("学号不能为空")
if not (2 <= len(self.name) <= 20) or not self.name.isalpha():
errors.append("姓名需为2-20个字母")
if self.enrollment_date and self.enrollment_date < self.birthday:
errors.append("入学日期不能早于出生日期")
if self.height and not 50 <= self.height <= 250:
errors.append("身高需在50-250cm之间")
if self.weight and not 5 <= self.weight <= 300:
errors.append("体重需在5-300kg之间")
return errors
@property
def is_valid(self) -> bool:
return not self._validation_errors
def to_dict(self) -> Dict[str, Any]:
return {
'name': self.name, 'id_card': self.id_card, 'stu_id': self.stu_id,
'gender': self.gender, 'height': self.height, 'weight': self.weight,
'enrollment_date': self.enrollment_date.isoformat() if self.enrollment_date else None,
'class_name': self.class_name, 'major': self.major,
'age': self.age, 'birthday': self.birthday.isoformat()
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Student':
return cls(**data)
class IStudentDAL(ABC):
@abstractmethod
def get_by_id(self, id_card: str) -> Optional[Student]: ...
@abstractmethod
def get_by_stu_id(self, stu_id: str) -> Optional[Student]: ...
@abstractmethod
def get_all(self) -> List[Student]: ...
@abstractmethod
def add(self, student: Student) -> bool: ...
@abstractmethod
def delete(self, id_card: str = None, stu_id: str = None) -> bool: ...
@abstractmethod
def update(self, student: Student) -> bool: ...
class BaseDAL(IStudentDAL):
def __init__(self, file_path: str):
self.file_path = file_path
self._init_storage()
def _init_storage(self): pass
def get_by_stu_id(self, stu_id: str) -> Optional[Student]:
return next((s for s in self.get_all() if s.stu_id == stu_id), None)
class CSVStudentDAL(BaseDAL):
def _init_storage(self):
with open(self.file_path, 'a') as f: pass # Ensure file exists
def get_all(self) -> List[Student]:
with open(self.file_path, 'r') as f:
return [Student.from_dict(row) for row in csv.DictReader(f)]
def add(self, student: Student) -> bool:
if self.get_by_id(student.id_card) or self.get_by_stu_id(student.stu_id):
return False
with open(self.file_path, 'a', newline='') as f:
writer = csv.DictWriter(f, fieldnames=student.to_dict().keys())
if f.tell() == 0: writer.writeheader()
writer.writerow(student.to_dict())
return True
def delete(self, id_card: str = None, stu_id: str = None) -> bool:
students = [s for s in self.get_all()
if not ((id_card and s.id_card == id_card) or (stu_id and s.stu_id == stu_id))]
with open(self.file_path, 'w', newline='') as f:
if students:
writer = csv.DictWriter(f, fieldnames=students[0].to_dict().keys())
writer.writeheader()
writer.writerows(s.to_dict() for s in students)
return len(students) < len(self.get_all())
class JSONStudentDAL(BaseDAL):
def _init_storage(self):
with open(self.file_path, 'a') as f: pass
def get_all(self) -> List[Student]:
with open(self.file_path, 'r') as f:
return [Student.from_dict(row) for row in json.load(f)]
def add(self, student: Student) -> bool:
data = self.get_all()
if any(s.id_card == student.id_card or s.stu_id == student.stu_id for s in data):
return False
data.append(student)
self._save_all(data)
return True
def _save_all(self, data: List[Student]):
with open(self.file_path, 'w') as f:
json.dump([s.to_dict() for s in data], f, indent=2)
class SQLiteStudentDAL(BaseDAL):
def _init_storage(self):
with sqlite3.connect(self.file_path) as conn:
conn.execute('''
CREATE TABLE IF NOT EXISTS students (
id_card TEXT PRIMARY KEY, stu_id TEXT UNIQUE, name TEXT,
gender BOOLEAN, height INTEGER, weight REAL,
enrollment_date TEXT, class_name TEXT, major TEXT,
age INTEGER, birthday TEXT)''')
def get_all(self) -> List[Student]:
with sqlite3.connect(self.file_path) as conn:
rows = conn.execute("SELECT * FROM students").fetchall()
return [Student.from_dict(dict(zip(('id_card', 'stu_id', 'name', 'gender', 'height',
'weight', 'enrollment_date', 'class_name', 'major',
'age', 'birthday'), row))) for row in rows]
def add(self, student: Student) -> bool:
try:
with sqlite3.connect(self.file_path) as conn:
conn.execute('''
INSERT INTO students VALUES (?,?,?,?,?,?,?,?,?,?,?)''',
(student.id_card, student.stu_id, student.name, student.gender,
student.height, student.weight,
student.enrollment_date.isoformat() if student.enrollment_date else None,
student.class_name, student.major, student.age, student.birthday.isoformat()))
return True
except sqlite3.IntegrityError:
return False
class StudentService:
def __init__(self, dal: IStudentDAL):
self.dal = dal
def add_student(self, student: Student) -> bool:
if not student.is_valid:
raise ValueError(f"Invalid student: {', '.join(student._validation_errors)}")
return self.dal.add(student)
def get_all(self) -> List[Student]:
return self.dal.get_all()
def query(self, **kwargs) -> List[Student]:
return [s for s in self.get_all()
if all(getattr(s, k, None) == v for k, v in kwargs.items() if v)]
def get_stats(self, field: str) -> Dict[str, int]:
return {v: sum(1 for s in self.get_all() if getattr(s, field) == v)
for v in set(getattr(s, field) for s in self.get_all())}
class StudentCLI:
def __init__(self, service: StudentService):
self.service = service
def run(self):
while True:
print("\n1. Add 2. List 3. Query 4. Stats 0. Exit")
choice = input("Choice: ")
if choice == "1":
self._add()
elif choice == "2":
self._list()
elif choice == "3":
self._query()
elif choice == "4":
self._stats()
elif choice == "0":
break
def _add(self):
student = Student(
name=input("Name: "),
id_card=input("ID Card: "),
stu_id=input("Student ID: "),
gender=input("Gender (True/False): ").lower() == 'true',
height=int(input("Height: ")) if input("Height (optional): ") else None,
weight=float(input("Weight: ")) if input("Weight (optional): ") else None
)
try:
if self.service.add_student(student):
print("Added successfully!")
except ValueError as e:
print(f"Error: {e}")
def _list(self):
for student in self.service.get_all():
print(student.to_dict())
def _query(self):
field = input("Field to query (name/class_name/major): ")
value = input(f"{field} value: ")
for student in self.service.query(**{field: value}):
print(student.to_dict())
def _stats(self):
field = input("Field for stats (class_name/major): ")
for k, v in self.service.get_stats(field).items():
print(f"{k}: {v}")
if __name__ == "__main__":
storage = input("Storage (csv/json/sqlite): ").lower()
dal = {
'csv': CSVStudentDAL('students.csv'),
'json': JSONStudentDAL('students.json'),
'sqlite': SQLiteStudentDAL('students.db')
}.get(storage, SQLiteStudentDAL('students.db'))
service = StudentService(dal)
StudentCLI(service).run()
Loading…
Cancel
Save