一個簡單的多用戶交互系統的實現

需求以下:建立管理員、教師、學員這三個視圖,實現一個簡單的課程操做交互python

具體實現以下:linux

Homework:

├─bin
│──────start.py #程序的入口

├─conf
│──────config.py #程序用到的文件的路徑以及其餘關係映射信息

├─core #
│──────logger.py#記錄日誌的邏輯
│──────main.py#實現三類用戶的登錄邏輯,並利用反射調用不一樣類的具體方法
│──────manager.py#實現了管理員類的各個功能
│──────my_pickle.py#實現了將不一樣對象dump、load進文件的方法以及修改已經被dump的文件的方法
│──────other_logics.py#其餘額外功能的邏輯
│──────school.py#裏面的classes類用來建立與班級名相同名字的文件
│──────student.py#實現學生類的功能邏輯
│──────teacher.py#實現講師類的功能邏輯

└─db
│──classes_obj#存放各個班級對象的信息
│──logs.log#存放日誌信息
│──teacher_obj#存放講師對象信息
│──userinfo#存放登錄用戶信息

└─studentinfo#裏面的文件是與班級同名的文件,各個文件裏面存的是相應班級裏的學員信息
│────────python_s9git

  代碼以下:json

from os import getcwd,path
from sys import path as sys_path
sys_path.insert(0,path.dirname(getcwd()))

from core import main


if __name__ == '__main__':
    main.main()
start.py
import os

PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#userinfo文件的路徑
USERINFO = os.path.join(PATH,'db','userinfo')
#schoolinfo的路徑
SCHOOLINFO = os.path.join(PATH,'db','school_obj')
#teacher_obj的路徑
TEACHER_OBJ = os.path.join(PATH,'db','teacher_obj')
#classes_obj的路徑
CLASSES_OBJ = os.path.join(PATH,'db','classes_obj')
#course_obj的路徑
COURSE_OBJ = os.path.join(PATH,'db','course_obj')
#studentinfo的路徑
STUDENTINFO = os.path.join(PATH,'db','studentinfo')
#日誌文件的路徑
LOGGER = os.path.join(PATH,'db','logs.log')
#用戶選擇視圖輸入項與身份信息的鍵值對
USER_DICT = {'1':'Manager','2':'Teacher','3':'Student'}
#學校與課程對應關係字典
school_class = {'Beijing':['python','linux'],'Shanghai':['go']}
config.py
import logging
from conf import config

def logger_file():
    #生成logger對象
    whw_logger = logging.getLogger('logs.log')
    whw_logger.setLevel(logging.INFO)
    #生成handler對象
    whw_fh = logging.FileHandler(config.LOGGER)
    whw_fh.setLevel(logging.INFO)
    #生成Formatter對象
    file_formatter = logging.Formatter(' %(asctime)s - %(name)s - %(levelname)s - %(message)s ')
    #把formatter對象綁定到handler對象中
    whw_fh.setFormatter(file_formatter)
    # 把handler對象綁定到logger對象中
    whw_logger.addHandler(whw_fh)

    return whw_logger

def write_log(msg):
    log_obj = logger_file()
    log_obj.info(msg)
    log_obj.handlers.pop()
logger.py
import sys
import os
from core.manager import Manager
from core.teacher import Teacher
from core.student import Student
from conf import config
from core import my_pickle
from .other_logics import file_name,show_classes,show_student_score


def login():
    '''
    登陸函數,應該先到conf.config中先讀取userinfo的文件路徑。
    再讀取userinfo文件中的信息,對用戶名與密碼進行查驗
    登錄成功後查看這我的的身份來肯定進入哪個視圖
    :return:
    '''
    while 1:
        print( '\033[1;32m請選擇用戶視圖:\n\033[0m',
               '1:管理員視圖\n',
               '2:講師視圖\n',
               '3:學生視圖\n'
        )

        choice = input('請選擇相應視圖的編號:')
        if not choice.isdigit() or int(choice)<=0 or int(choice)>3:
            print('\033[1;31m請輸入正確的編號!\033[0m')
            continue

        else:
            username = input('用戶名:')
            password = input('密 碼:')
            user_identity = config.USER_DICT[choice]
            #打開userinfo文件...
            with open(config.USERINFO,'r',encoding='utf-8') as f:
                for line in f:
                    user_name,pass_word,identity = line.strip().split('|')
                    #必須是用戶名 密碼  身份 三個所有一致才能登錄成功
                    if user_name == username and password == pass_word and user_identity == identity:
                        print('\033[1;32m%s 用戶 %s 登錄成功!\033[0m' % (user_identity,username))
                        #以字典形式返回用戶名 身份
                        return {'username':username,'identity':identity}
                else:
                    print('\033[1;31m抱歉,輸入有誤,登錄失敗!\033[0m')


def main():
    '''
    打印歡迎信息
    調用login()——獲得一個返回值:用戶的姓名與身份
    打印用戶身份的功能菜單
    若是用戶想要調用任何方法,應該經過角色的對象調用,跳轉到對應對象的方法裏
    :return:
    '''
    print('\033[0;35m歡迎進入學生選課系統!\033[0m')
    ret = login()
    if ret:
        while 1:
            #管理員登錄
            if ret['identity'] == 'Manager':
                print('\033[0;32m******************\033[0m')
                role_class = getattr(sys.modules[__name__],ret['identity'])
                #將類實例化成相應的對象
                obj = role_class(ret['username'])

                while 1:
                    print('\033[0;32m******************\033[0m')
                    for i,v in enumerate(role_class.menu,1):
                        print(i,v[0])
                    #避免輸入不合法,利用異常處理

                    choice = input('\033[0;32m請輸入您要進行的操做編號:\033[0m')
                    if not choice.isdigit():
                        print('\033[1;31m請輸入數字!\033[0m')
                        break
                    choice_int = int(choice)
                    if choice_int < 1 or choice_int > 9:
                        print('\033[1;31m抱歉,請輸入正確的編號!\033[0m')
                        break
                    ##進行第二次的反射,後面加了括號直接執行了
                    getattr(obj,role_class.menu[choice_int-1][1])()
            #講師登錄
            if ret['identity'] == 'Teacher':
                print('\033[0;32m******************\033[0m')
                global teacher_school
                #反射:從本模塊找到Teacher類
                teacher_class = getattr(sys.modules[__name__],ret['identity'])
                pk_teacher = my_pickle.MyPickle(config.TEACHER_OBJ)
                ob_teacher = pk_teacher.loaditer()
                for i in ob_teacher:
                    if i.name == ret['username']:
                        teacher_school = i.school
                #利用找到的Teacher類與用戶輸入的name、school實例化講師對象
                teacher_obj = teacher_class(ret['username'],teacher_school)
                while 1:
                    print('\033[0;32m******************\033[0m')
                    for i,v in enumerate(teacher_class.menu,1):
                        print(i,v[0])
                    choice = input('\033[0;32m請輸入您要進行的操做編號:\033[0m')
                    if choice.isdigit() and  0 < int(choice) < 5:
                        choice_int = int(choice)
                        #第二次反射,後面加了括號,直接執行就行
                        getattr(teacher_obj,teacher_class.menu[choice_int-1][1])()
                    else:
                        print('\033[1;31m抱歉,請輸入正確的編號!\033[0m')
            #學生登陸——須要輸入班級
            if ret['identity'] == 'Student':
                global stu_status
                stu_status = False
                global stu_class
                print('\033[0;32m******************\033[0m')
                class_input = input('請輸入您所在的班級:').strip()
                #判斷輸入的班級是否存在
                if class_input not in file_name(config.STUDENTINFO):
                    print('\033[1;31m抱歉,請輸入正確的班級!\033[0m')

                else:
                    #判斷輸入的班級有沒有登錄的學生name
                    pk_student = my_pickle.MyPickle(os.path.join(config.STUDENTINFO,class_input))
                    obj_student = pk_student.loaditer()
                    for i in obj_student:
                        #找到了這個學生對象
                        if i.name == ret['username']:
                            #將這個班級賦值給global變量stu_class
                            stu_class = i.clas
                            stu_status = True
                    #說明成功找到了該學生的對象
                    if stu_status == True:
                        # 第一次反射
                        student_class = getattr(sys.modules[__name__], ret['identity'])
                        #實例化
                        student_obj = student_class(ret['username'],stu_class)
                        while 1:
                            print('\033[0;32m******************\033[0m')
                            for i,v in enumerate(student_obj.menu,1):
                                print(i,v[0])
                            choice = input('請輸入您要進行的操做編號:')
                            if choice.isdigit() and ( 0 < int(choice) < 3):
                                if choice == '1':
                                    show_student_score(ret['username'],class_input)
                                elif choice == '2':
                                    exit()
                            else:
                                print('\033[1;31m請輸入正確的操做編號!\033[0m')
                    else:
                        print('\033[1;31m抱歉您不在這個班級!\033[0m')
        else:
            print('\033[1;31m請輸入正確的用戶編號\033[0m')
main.py
#首先,以管理員的身份登陸
#登陸後 應該實例化一個對應身份的對象manager_obj = manager(name)
#管理員對象能夠調用全部的方法
from conf import config
from core import teacher
from core import my_pickle
from core import student
from core import school
from .other_logics import file_name
from .logger import logger_file
from .logger import write_log
import os


#管理員類
class Manager:
    menu =[
        ('建立講師帳號','create_teacher'),('建立學生帳號','create_student'),
        ('建立班級', 'create_classes'), ('查看學校','show_school'),
        ('查看課程','show_courses'),('查看講師','show_teachers'),
        ('查看班級','show_classes'),('爲班級指定老師','bound_class_teacher'),
        ('退出','exit')
          ]



    def __init__(self,name):
        self.name = name
        #至關於拿到了一個對象,這個對象裏面只存了文件的名字
        self.teacher_pickle_obj = my_pickle.MyPickle(config.TEACHER_OBJ)  #拿到 mypickle_obj
        self.school_pickle_obj = my_pickle.MyPickle(config.SCHOOLINFO)
        self.classes_pickle_obj = my_pickle.MyPickle(config.CLASSES_OBJ)
        self.course_pickle_obj = my_pickle.MyPickle(config.COURSE_OBJ)

    def exit(self):
        exit()

    #往文件裏面添加內容的時候寫成統一的方法
    #由於它既沒有引用類的屬性,也沒有引用對象的屬性,因此把它作成非綁定方法
    @staticmethod
    def userinfo_handler(content):
        with open(config.USERINFO,'a') as f:
            f.write('\n%s' % content)


    def show(self,pickle_obj):
        #反射
        pick_obj = getattr(self,pickle_obj)
        #執行一個生成器函數就拿到一個生成器對象,這裏拿到的每個值,都是生成器返回的對象
        load_g = pick_obj.loaditer()
        for course_obj in load_g:
            for i in course_obj.__dict__:####
                print('%s: %s'%(i,course_obj.__dict__[i]))
            print('*' * 20)

    def show_school(self):
        print('校區信息以下:')
        for i,v in enumerate(config.school_class,1):
            print('%s: %s'% (i,v))

    def show_teachers(self):
        self.show('teacher_pickle_obj')

    def show_courses(self):
        print('課程信息以下:')
        for i,v in enumerate(config.school_class):
            print('學校:%s-->課程:%s' % (v,config.school_class[v]))

    def show_classes(self):
        self.show('classes_pickle_obj')


    def create_teacher(self):
        l = []
        with open(config.USERINFO,'r') as f:
            for line in f:
                username,v,b = line.strip().split('|')
                l.append(username)
        teacher_name = input('請輸入老師的姓名:')
        if teacher_name in l:
            print('\033[1;31m該講師已經存在!\033[0m')
            return
        teacher_pass = input('請輸入老師的密碼:')
        self.show_school()
        teacher_school = input('請輸入所屬的學校:(Beijing|Shanghai)')
        #存入文件
        content = '%s|%s|Teacher' % (teacher_name,teacher_pass)
        Manager.userinfo_handler(content)
        #根據輸入的姓名與學校值實例化一個老師對象
        teacher1 = teacher.Teacher(teacher_name,teacher_school)###實例化
        self.teacher_pickle_obj.dump(teacher1)
        print('\033[0;32m講師建立成功!\033[0m')
        write_log('建立了講師:%s' % teacher_name)


    def create_classes(self):
        #注意,新建的班級名字不能是已經存在的
        classes_names = file_name(config.STUDENTINFO)
        print('\033[0;32m已經存在的班級以下:\033[0m')
        for i,v in enumerate(classes_names,1):
            print('%s: %s' % (i,v))
        class_name = input('請輸入您要新建的班級名稱:')
        if class_name in classes_names:
            print('\033[1;31m該班級已經存在!\033[0m')
            return
        school_name = input('請輸入所屬的學校:(Beijing|Shanghai)')
        if school_name not in 'Beijing|Shanghai'.split('|'):
            print('\033[1;31m請輸入正確的學校!\033[0m')
            return
        course_name = input('請輸入課程名稱:(python|linux|go)')
        if course_name not in 'python|linux|go'.split('|'):
            print('\033[1;31m請輸入正確的課程!\033[0m')
            return
        #判斷 Beijing只能有python與linux,Shanghai只能有go:
        if (school_name == 'Beijing' and (course_name =='python' or course_name == 'linux')) or (school_name == 'Shanghai' and course_name == 'go'):
            #若是符合條件的話新建一個路徑
            student_path = os.path.join(config.STUDENTINFO,class_name)
            #利用上面的路徑建立一個空文件
            open(student_path,'w').close()
            class_obj = school.Classes(school_name,class_name,course_name)
            #利用pickle dump實例化的對象,這一點json作不到!
            self.classes_pickle_obj.dump(class_obj)#######
            print('課程  %s 建立成功!' % course_name)
            write_log('建立了課程:%s' % course_name)
        else:
            print('\033[1;31m您填寫的學校與課程的關係不存在!\033[0m')
            return


    def create_student(self):
        student_name = input('請輸入學生姓名:')
        student_pass = input('請輸入學生密碼:')
        #列舉出全部存在的班級
        classes_names = file_name(config.STUDENTINFO)
        print('\033[0;32m已經存在的班級以下:\033[0m')
        for i, v in enumerate(classes_names, 1):
            print('%s: %s' % (i, v))

        student_class = input('請輸入學生所在的班級:')
        class_g = self.classes_pickle_obj.loaditer()
        for clas in class_g:
            #這裏有既有班級名卡控,不用作判斷了
            if clas.name == student_class:
                #先把用戶名、密碼、角色寫入userinfo文件中
                content = '%s|%s|Student' % (student_name,student_pass)
                Manager.userinfo_handler(content)
                #實例化
                stu_obj = student.Student(student_name,clas)
                student_path = os.path.join(config.STUDENTINFO,student_class)
                my_pickle.MyPickle(student_path).dump(stu_obj)
                print('學生 %s 建立成功!' % student_name)
                write_log('學生 %s 建立成功!' % student_name)
                return
        else:
            print('\033[1;31m輸入錯誤,建立學生失敗!\033[0m')   #for...else 語句


    def bound_class_teacher(self):
        global a
        global status
        status = False
        #顯示既有的班級
        classes_names =file_name(config.STUDENTINFO)
        print('\033[0;32m已經存在的班級以下:\033[0m')
        for i, v in enumerate(classes_names, 1):
            print('%s: %s' % (i, v))
        class_name = input('請輸入要指定的班級:')
        #判斷一下輸入的班級是否存在
        if class_name not in classes_names:
            print('\033[1;31m抱歉,沒有這個班級!\033[0m')
            return
        #顯示既有講師
        print('全部講師信息爲:')
        self.show_teachers()
        teacher_name = input('請輸入須要指定的講師:')
        #利用teacher_obj文件實例化出一個可迭代的對象
        teach_g = self.teacher_pickle_obj.loaditer()
        #遍歷這個可迭代的對象
        for teacher_obj in teach_g:
            # 前提是姓名是惟一的,找到了對應的老師!
            if teacher_obj.name == teacher_name:
                if class_name in teacher_obj.classes:
                    print('\033[1;31m本教師已經與該課程有了綁定關係,請勿重複綁定!\033[0m')
                    return
                #將符合要求的對象賦值給一個global變量,待後面處理
                teacher_obj.classes.append(class_name)
                a = teacher_obj
                #判斷成功修改
                status = True
        #必須等遍歷完文件關閉後才能再進行edit操做
        if status == True:
            file1 = my_pickle.MyPickle(config.TEACHER_OBJ)
            file1.edit(a)
            print(a.name,a.classes)
            print('綁定成功!')
            write_log('班級%s綁定了講師:%s' % (class_name,teacher_name))
            a = None
            status = False
        else:
            print('\033[1;31m錄入有誤,綁定失敗!\033[0m')
manager.py
import pickle
import os

class MyPickle:
    def __init__(self,filename):
        #只是用文件名來實例化對象,並無打開文件
        self.filename = filename

    #每次須要dump一個文件的時候須要先打開一個文件
    def dump(self,obj):
        with open(self.filename,'ab') as f:
            # 利用pickle dump實例化的對象,這一點json作不到!
            pickle.dump(obj,f)

    #每次須要load文件的時候,須要轉格式
    def loaditer(self):
        with open(self.filename,'rb') as f:
            while 1:
                try:
                    #不能把全部的文件都同時讀出來,須要每讀一個文件再作一次操做
                    obj = pickle.load(f)
                    yield obj
                except:
                    break
        def close_file():
            f.close()


    #"修改"已經被dump的文件
    def edit(self,obj):
        #利用原文件.bak這個路徑名實例化一個新的對象,而後利用dump跟loaditer方法將知足條件的信息寫進這個bak文件中,最後replace~~
        f_temp = MyPickle(self.filename+'.bak')
        with open(self.filename,'rb+') as f:
            for i in self.loaditer():
                if i.name == obj.name:
                    f_temp.dump(obj)
                else:
                    f_temp.dump(i)

        os.replace(self.filename+'.bak',self.filename)
my_pickle.py
import os
from conf import config
from .my_pickle import MyPickle

# 查找studentinfo文件夾下文件列表的方法
def file_name(file_dir):
    for root, dirs, files in os.walk(file_dir):
        return files


# 顯示etudentinfo文件夾下全部文件名的方法
def show_classes():
    for root, dirs, files in os.walk(config.STUDENTINFO):
        for i, v in enumerate(files, 1):
            print('%s : %s' % (i, v))


# 由於學生須要班級來實例化,而班級是一個個的文件,因此顯示學生成績的方法寫在函數裏
def show_student_score(name, file):
    class_path = os.path.join(config.STUDENTINFO, file)
    pk_stu = MyPickle(class_path)
    obj_stu = pk_stu.loaditer()
    for i in obj_stu:
        if i.name == name:
            print('%s 的成績爲:%s' % (name, i.score))
            return
other_logics.py
class Classes:
    def __init__(self,school,name,course):
        self.school = school
        self.name = name #班級名稱,,
        self.course = course  #科目 python go linux
        #self.student_path = student_path  #學生信息文件的絕對路徑
school.py
class Student:
    menu = [('查看本身的成績:','show_my_score'),
            ('退出','exit')
        ]

    def __init__(self,name,clas):
        self.name = name
        self.clas = clas
        self.score = ''
student.py
from core import my_pickle
from conf import config
import os

# class Classes:
#     def __init__(self,school_name,class_name,class_kind):
#         self.school_name = school_name #分校
#         self.class_name = class_name #班級名 python_s11
#         self.class_kind = class_kind #班級科目 python go linux
#         self.student = ['student_obj']


class Course:
    def __init__(self,course_name,course_period,course_price):
        self.course_name = course_name
        self.course_period = course_period#週期
        self.course_price = course_price


class Teacher:
    menu = [
        ('查看本人所授班級','show_classes'),
        ('查看所授班級學生','show_class_students'),
        ('修改學生成績','reverse_grade'),
        ('退出','exit')
    ]

    def __init__(self,name,school):
        self.name = name
        self.school = school
        self.classes = []

    def exit(self):
        exit()


    def show_classes(self):
        print('\033[1;32m您所教授的班級爲:\033[0m')
        global classes_list
        #利用teacher_obj文件實例化一個MyPickle對象
        teacher_file = my_pickle.MyPickle(config.TEACHER_OBJ)
        #利用loaditer獲得一個可迭代的對象
        teacher_obj = teacher_file.loaditer()
        #遍歷~
        for i in teacher_obj:
            #找到對應的講師
            if i.name == self.name:
                #班級列表賦值給classes_list
                classes_list = i.classes
        #顯示這個classes_list列表,就是這個老師教的班級
        for i,v in enumerate(classes_list,1):
            print('%s: %s' % (i,v))
        return


    def show_class_students(self):
        clas = input('請輸入您要查找學生所在的班級:').strip()
        global classes_list1
        # 利用teacher_obj文件實例化一個MyPickle對象
        teacher_file = my_pickle.MyPickle(config.TEACHER_OBJ)
        # 利用loaditer獲得一個可迭代的對象
        teacher_obj = teacher_file.loaditer()
        # 遍歷~
        for i in teacher_obj:
            # 找到對應的講師
            if i.name == self.name:
                # 班級列表賦值給classes_list
                classes_list1 = i.classes
        #判斷輸入的班級是否在老師所教班級的列表裏
        if clas in classes_list1:
            #獲得這個班級文件的路徑
            class_path = os.path.join(config.STUDENTINFO,clas)
            #利用這個班級文件路徑再實例化一個MyPickle對象...
            pk_class = my_pickle.MyPickle(class_path)
            class_obj = pk_class.loaditer()
            for i in class_obj:
                print('學生姓名:%s;學生成績:%s' % (i.name,i.score))

        else:
            print('\033[1;31m抱歉,您沒有教這個班級!\033[0m')


    def reverse_grade(self):
        self.show_classes()
        clas = input('請輸入您要修改的學生所在的班級:').strip()
        global classes_list1
        global stu1
        global s_status
        s_status = False
        # 利用teacher_obj文件實例化一個MyPickle對象
        teacher_file = my_pickle.MyPickle(config.TEACHER_OBJ)
        # 利用loaditer獲得一個可迭代的對象
        teacher_obj = teacher_file.loaditer()
        # 遍歷~
        for i in teacher_obj:
            # 找到對應的講師
            if i.name == self.name:
                # 班級列表賦值給classes_list
                classes_list1 = i.classes
        # 判斷輸入的班級是否在老師所教班級的列表裏
        if clas in classes_list1:
            # 獲得這個班級文件的路徑
            class_path = os.path.join(config.STUDENTINFO, clas)
            # 利用這個班級文件路徑再實例化一個MyPickle對象...
            pk_class = my_pickle.MyPickle(class_path)
            class_obj = pk_class.loaditer()
            choice_stu = input('請輸入您要修改爲績的學生:').strip()
            score = input('請輸入該學生修改後的成績:').strip()
            for i in class_obj:
                if i.name == choice_stu:
                    i.score = score
                    #將這個學生對象賦值給global變量stu1
                    stu1 = i
                    s_status = True

        else:
            print('\033[1;31m抱歉,您沒有教這個班級!\033[0m')
            return

        if s_status == True:
            stu_file = my_pickle.MyPickle(os.path.join(config.STUDENTINFO,clas))
            stu_file.edit(stu1)
            print('學生:%s;成績:%s' % (stu1.name,stu1.score))
            print('\033[1;32m修改爲功!\n\033[0m')
            s_status = False
        else:
            print('\033[1;31m錄入有誤,操做失敗!\033[0m')
teacher.py

 

演示以下:app

相關文章
相關標籤/搜索