一个简单的多用户交互系统的实现

需求如下:创建管理员、教师、学员这三个视图,实现一个简单的课程操作交互

具体实现如下:

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_s9

  代码如下:

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

演示如下:


│────────......

猜你喜欢

转载自www.cnblogs.com/paulwhw/p/9032015.html