學生選課系統


題目要求

利用規范化目錄結構完成一個學生選課系統。
角色:學生、管理員。
功能分析:
用戶登錄之后就可以直接判斷用戶身份,是學生還是管理員。
學生登錄之后有以下幾個功能:
查看所有課程。
選擇課程。
查看所選課程。
退出程序。
管理員登錄之后有以下幾個功能:
創建課程(需要記錄日志)。
創建學生賬號(需要記錄日志)。
查看所有課程。
查看所有學生。
查看所有學生的選課情況。
退出程序。
課程屬性:課程名,價格,周期,老師。
學生屬性:姓名,所選課程。
管理員屬性:姓名。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM