学生选课系统
题目要求
利用规范化目录结构完成一个学生选课系统。
角色:学生、管理员。
功能分析:
用户登录之后就可以直接判断用户身份,是学生还是管理员。
学生登录之后有以下几个功能:
查看所有课程。
选择课程。
查看所选课程。
退出程序。
管理员登录之后有以下几个功能:
创建课程(需要记录日志)。
创建学生账号(需要记录日志)。
查看所有课程。
查看所有学生。
查看所有学生的选课情况。
退出程序。
课程属性:课程名,价格,周期,老师。
学生属性:姓名,所选课程。
管理员属性:姓名。
start
import os
import sys
BATH_DIR = os.path.dirname(os.path.dirname(file))
sys.path.append(BATH_DIR)
添加搜索路径 确定当前工程的搜索路径 定位到 老师版本学生选课系统,可千万不能变成每周大作业了 为了查找具体的路径
from core import src
from core.src import Course
from core.src import Student
if name == \’main\’:
src.main()
src
import sys
import os
import pickle
from lib import common
from conf import settings
class Base: #
def show_courses(self):
with open(settings.COURSE_PATH, mode=\'rb\') as f:
num = 0
while 1:
try:
num += 1
obj = pickle.load(f) # # obj获取每行的课程 每行课程是一个对象
print(f\'{num}: {obj.name} {obj.price} {obj.period}\')# 打印对应的课程以及序列号
\'\'\'
1: Python周末班 12000 6个月
2: python脱产班 23000 6个月
3: 大数据 13000 4个月
4: java 12000 5个月
5: 测试 12000 6个月
6: 算法工程师 36000 6个月
7: 前端 12000 4个月
8: 平面设计 2345 六个月
\'\'\'
except EOFError: # 读完之后直接停止,防止循环读内容报错
break
def exit(self): # 使用这个函数退出,并且打印退出提示信息
sys.exit(f\'\033[0;32m感谢{self.name}使用选课系统!\033[0m\')
# sys.exit所用是退出并且打印内容,是sys模块里面的一个功能
class Student(Base):
operate_lst = [(\’查看可选课程\’, \’show_courses\’),
(\’选择课程\’, \’select_course\’),
(\’查看所选课程\’, \’show_selected_course\’),
(\’退出\’, \’exit\’)] # 静态属性
def __init__(self, name):
self.name = name # 定义两个属性
self.courses = [] # 空列表,为了后面添加课程
def select_course(self):
"""选择课程"""
self.show_courses()# 先把课程打印出来 调用父类方法
try:
choice_num = input(\'\033[0;32m请输入要选择的课程序号:\033[0m\').strip()
# 输入要添加的课程序号
with open(settings.COURSE_PATH, mode=\'rb\') as f:
for i in range(int(choice_num)):
# 根据输入的序号确定循环次数最终锁定选择的课程对象
# 比如 range(5) 循环 5 次
obj = pickle.load(f) ## 拿出最后一个 最后被覆盖 # load读出来 用obj接收所选的课程对象
self.courses.append(obj) # 将课程添加到里面
print(f\'\033[0;32m您已经成功添加了{obj.name}课程\033[0m\')
# 把所选的课程添加到学生对象所对应的课程列表中
except Exception:
print(\'输入有误....\')
def show_selected_course(self):
"""查看所选课程"""
print(f\'\033[0;32m您已报名如下课程\033[0m\')
for obj_course in self.courses:
# 循环此学生对象的列表,然后把所有课程的打印,每个课程也是一个对象
print(f\'\033[0;32m课程名:{obj_course.name},课程价格:{obj_course.price} 课程周期:{obj_course.period}\033[0m\')
def exit(self):
"""退出之前要将学生选择的课程添加上"""
with open(settings.STUDENT_PATH, mode=\'rb\') as f1, \
open(f\'{settings.STUDENT_PATH}_bak\', mode=\'wb\') as f2:
\'\'\'打开两个文件,读旧文件,写新文件\'\'\'
while 1: # 循环读内容
try:
obj = pickle.load(f1)
pickle.dump(self if obj.name == self.name else obj, f2)
\'\'\' 把相应的对象名修改成现在的,其他的对象名直接写入
\'\'\'
except EOFError:
break
os.remove(settings.STUDENT_PATH)
os.rename(f\'{settings.STUDENT_PATH}_bak\', settings.STUDENT_PATH) # 改名字
super().exit()
@classmethod
def get_obj(cls, username):
"""此方法是区别学生与管理员登录,学生登录就会去文件中取对象,管理员登录则直接userinfo获取管理员用户名密码"""
with open(settings.STUDENT_PATH, mode=\'rb\') as f1:
while 1:
try:
obj = pickle.load(f1)
print(obj)
if username == obj.name:
return obj
except EOFError:
break
class Manager(Base):
operate_lst = [(\’创建课程\’, \’create_course\’),
(\’创建学生\’, \’create_student\’),
(\’查看可选课程\’, \’show_courses\’),
(\’查看所有学生\’, \’show_students\’),
(\’查看所有学生选课情况\’, \’show_students_courses\’),
(\’退出\’, \’exit\’)]
def __init__(self, name):
self.name = name
def create_course(self):
"""创建课程"""
course = getattr(sys.modules[__name__], \'Course\')
name, price, period = input(\'请依次输入课程名,价格以及课程周期,以|分割\').strip().split(\'|\')
obj = course(name, price, period)
with open(settings.COURSE_PATH, mode=\'ab\') as f1:
pickle.dump(obj, f1)
logger = common.record_logger()
logger.info(f\'成功创建{name}课程\')
def create_student(self):
"""创建学生"""
student_username = input(\'\033[0;32m 请输入学生姓名:\033[0m\').strip()
student_password = input(\'\033[0;32m 请输入学生密码:\033[0m\').strip()
student_pwd_md5 = common.hashlib_md5(student_password)
with open(settings.USERINFO_PATH, encoding=\'utf-8\', mode=\'a\') as f1:
f1.write(f\'\n{student_username}|{student_pwd_md5}|Student\')
with open(settings.STUDENT_PATH, mode=\'ab\') as f:
obj = getattr(sys.modules[__name__], \'Student\')(student_username)
pickle.dump(obj, f)
logger = common.record_logger()
logger.info(f\'成功您已成功创建学生账号:{student_username},初始密码:{student_password}\')
def show_students(self):
"""查看所有学生"""
with open(settings.STUDENT_PATH, mode=\'rb\') as f1:
while 1:
try:
obj = pickle.load(f1)
print(obj.name)
except EOFError:
break
def show_students_courses(self):
"""查看所有学生选课情况"""
with open(settings.STUDENT_PATH, mode=\'rb\') as f1:
while 1:
try:
obj = pickle.load(f1)
print(f\'\033[0;32m学生:{obj.name},所选课程:\
{["%s-%s-%s" %(course.name,course.price,course.period) for course in obj.courses]}\033[0m\')
except EOFError:
break
def exit(self):
"""退出"""
super().exit()
@classmethod
def get_obj(cls, username):
return Manager(username) # 实例化的过程
class Course:
def init(self, name, price, period):
self.name = name
self.price = price
self.period = period
self.teacher = None
def login():
“””登陆逻辑,此处是用了单次登陆验证,你也可以根据自己的需求改成三次登陆失败才返回False”””
count = 1
while count < 4:
username = input(\’请输入用户名:\’).strip()
password = input(\’请输入密码:\’).strip()
pwd_md5 = common.hashlib_md5(password) # 这一步是加密
with open(settings.USERINFO_PATH,mode=\’r\’, encoding=\’utf-8\’) as f1:
for line in f1: # 一行一行读取
if not line.strip(): # 如果是空字符串,不空就是 True 继续执行
continue # 继续走 for
user, pwd, identify = line.strip().split(\’|\’) #去掉结尾\n 分割之后是列表
if user == username and pwd == pwd_md5:
return {\’username\’: user, \’identify\’: identify, \’auth\’: True} # 成功了 这个状态就是True
else:
print(\’用户名或者密码错误,请重新输入\’)
count += 1
return {\’username\’: username, \’identify\’: None, \’auth\’: False} # 失败了就把状态改为 False
def main():
print(\’\033[0;32m欢迎访问选课系统,请先登录\033[0m\’)
dict_auth = login()
print(f”\033[0;32m登陆成功,欢迎{dict_auth[\’username\’]},您的身份是{dict_auth[\’identify\’]}\033[0m”)
if dict_auth[\’auth\’]:
\’\’\’根据不同的身份,进行相应的操作,登录成功状态就改为 True\’\’\’
if hasattr(sys.modules[name], dict_auth[\’identify\’]): # 必须找到这个类,才能调用后续的方法
\’\’\’反射到本模块,找到 identify 对应的类 反射 判断对象是否存在,存在就是返回 True 否则 False\’\’\’
cls = getattr(sys.modules[name], dict_auth[\’identify\’]) # 得到类名地址
\’\’\’
如果是管理者登录:
obj = cls(dict_auth[\’username\’])
如果是学生登录:
从Student文件中 读取该学生对象并返回:
打开文件,读取文件: pickle.load(f) dic_auth[\’username\’] == obj.name
obj = 文件中读取的学生对象
\’\’\’
obj = cls.get_obj(dict_auth[\’username\’]) # 管理员与学生都定义了此方法,鸭子类型.
while 1:
for num, option in enumerate(cls.operate_lst, 0): # 类名. 这个万能的点 # num 将元组解构
\’\’\’
operate_lst = [(\’创建课程\’, \’create_course\’),
(\’创建学生\’, \’create_student\’),
(\’查看可选课程\’, \’show_courses\’),
(\’查看所有学生\’, \’show_students\’),
(\’查看所有学生选课情况\’, \’show_students_courses\’),
(\’退出\’, \’exit\’)]
\’\’\’
print(f\'{num+1}: {option[0]}\’)
\’\’\’
输出是这个东东
1: 创建课程
2: 创建学生
3: 查看可选课程
4: 查看所有学生
5: 查看所有学生选课情况
6: 退出
num 的作用就是解构,坤坤说这些都是旧的知识点,我的妈呀,难受,竟然都不知道
\'\'\'
choice_num = int(input(\'\033[0;32m 请输入选项:\033[0m\').strip())
getattr(obj, cls.operate_lst[choice_num - 1][1])() # getattr(obj,\'create_course\')() 已经调用这个函数了
else:
print(\'三次验证失败,系统自动退出\')
return False
settings
import os
import logging.config
BATH_DIR = os.path.dirname(os.path.dirname(file))
USERINFO_PATH = os.path.join(BATH_DIR, \’db\’, \’user_info\’)
COURSE_PATH = os.path.join(BATH_DIR, \’db\’, \’Course\’)
STUDENT_PATH = os.path.join(BATH_DIR, \’db\’, \’Student\’)
LOGGING_PATH = os.path.join(BATH_DIR, \’log\’, \’admin.log\’)
SIMPLE_FORMAT = \'[%(asctime)s] %(message)s\’
LOGGING_DIC = {
\’version\’: 1,
\’disable_existing_loggers\’: False,
\'formatters\': {
\'simple\': {
\'format\': SIMPLE_FORMAT,
},
},
\'filters\': {},
\'handlers\': {
# 打印到终端的日志
\'stream\': {
\'level\': \'DEBUG\',
\'class\': \'logging.StreamHandler\', # 打印到屏幕
\'formatter\': \'simple\'
},
# 打印到文件的日志,收集info及以上的日志
\'file\': {
\'level\': \'DEBUG\',
\'class\': \'logging.handlers.RotatingFileHandler\', # 保存到文件
\'formatter\': \'simple\',
\'filename\': LOGGING_PATH, # 日志文件
\'maxBytes\': 1024 * 1024 * 5, # 日志大小 5M
\'backupCount\': 5,
\'encoding\': \'utf-8\', # 日志文件的编码,再也不用担心中文log乱码了
},
},
\'loggers\': {
# logging.getLogger(__name__)拿到的logger配置
\'\': {
\'handlers\': [\'stream\', \'file\'],
\'level\': \'DEBUG\',
\'propagate\': True,
},
},
}
common
import hashlib
import logging.config
from conf.settings import LOGGING_DIC
def hashlib_md5(password):
“””密码加密”””
ret = hashlib.md5()
ret.update(password.encode(\’utf-8\’))
return ret.hexdigest()
def record_logger():
“””记录日志”””
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger() # 生成一个log实例
return logger