python 选课系统(面向对象)

时间:2024-04-15 14:38:01

项目需求:

利用规范化目录结构完成一个学生选课系统。

  • **角色:**学生、管理员。

  • 功能分析:

    • 用户登录之后就可以直接判断用户身份,是学生还是管理员。
    • 学生登录之后有以下几个功能:
      1. 查看所有课程。
      2. 选择课程。
      3. 查看所选课程。
      4. 退出程序。
    • 管理员登录之后有以下几个功能:
      1. 创建课程(需要记录日志)。
      2. 创建学生账号(需要记录日志)。
      3. 查看所有课程。
      4. 查看所有学生。
      5. 查看所有学生的选课情况。
      6. 退出程序。
  • 课程属性:课程名,价格,周期,老师。

  • 学生属性:姓名,所选课程。

  • 管理员属性:姓名。

流程图: https://www.processon.com/view/link/5d283960e4b0878e40af9dd8

目录结构:

具体代码:

start.py

import sys
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_DIR)
from core import src
from core.src import Course
from core.src import Student

if __name__ == \'__main__\':
    src.main()
View Code

settings.py

import os
import logging
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
USERINFO_PATH = os.path.join(BASE_DIR, \'db\',\'user_info\')
COURSE_PATH = os.path.join(BASE_DIR, \'db\',\'Course\')
STUDENT_PATH = os.path.join(BASE_DIR, \'db\',\'Student\')
LOGGING_PATH = os.path.join(BASE_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,
        },
    },
}
View Code

src.py

import os
import sys
import pickle
from conf import settings
from lib import common


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)
                    print(f"{num}: {obj.name}  {obj.price}  {obj.period}")
                except EOFError:
                    break

    def exit(self):
        sys.exit(f"\033[0;32m感谢{self.name}使用选课系统!\033[0m")

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:
            chice_num = input("\033[0;32m请输入要选择的课程序号:\033[0m").strip()
            with open(settings.COURSE_PATH, mode="rb") as f:
                for i in range(int(chice_num)):
                    obj = pickle.load(f)
                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):
        with open(settings.STUDENT_PATH, mode="rb") as f1:
            while 1:
                try:
                    obj = pickle.load(f1)
                    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():
    count = 1
    while count < 4:
        username = input("请输入用户名: ").strip()
        password = input("请输入密码: ").strip()
        pwd_md5 = common.hashlib_md5(password)
        with open(settings.USERINFO_PATH, encoding="utf-8") as f1:
            for line in f1:
                if not line.strip():continue
                user, pwd, identify = line.strip().split("|")
                if user == username and pwd == pwd_md5:
                    return {"username": user, "identify": identify, "auth": True}
            else:
                print("用户名或密码错误,请重新输入")
        count += 1
    return {"username": username, "identify": None, "auth": 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"]:
        if hasattr(sys.modules[__name__], dict_auth["identify"]):
            cls = getattr(sys.modules[__name__], dict_auth["identify"])
        obj = cls.get_obj(dict_auth["username"])
        while 1:
            for num, option in enumerate(cls.operate_lst, 0):
                print(f"{num+1}: {option[0]}")
            choice_num = int(input(\'\033[0;32m 请输入选项:\033[0m\').strip())
            getattr(obj, cls.operate_lst[choice_num - 1][1])()

    else:
        print("三次验证失败,系统自动退出")
        return False
View Code

common.py

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)
    logger = logging.getLogger()
    return logger
View Code