zoukankan      html  css  js  c++  java
  • 一个简单的多用户交互系统的实现

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

    具体实现如下:

    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( '33[1;32m请选择用户视图:
    33[0m',
                   '1:管理员视图
    ',
                   '2:讲师视图
    ',
                   '3:学生视图
    '
            )
    
            choice = input('请选择相应视图的编号:')
            if not choice.isdigit() or int(choice)<=0 or int(choice)>3:
                print('33[1;31m请输入正确的编号!33[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('33[1;32m%s 用户 %s 登陆成功!33[0m' % (user_identity,username))
                            #以字典形式返回用户名 身份
                            return {'username':username,'identity':identity}
                    else:
                        print('33[1;31m抱歉,输入有误,登陆失败!33[0m')
    
    
    def main():
        '''
        打印欢迎信息
        调用login()——得到一个返回值:用户的姓名与身份
        打印用户身份的功能菜单
        如果用户想要调用任何方法,应该通过角色的对象调用,跳转到对应对象的方法里
        :return:
        '''
        print('33[0;35m欢迎进入学生选课系统!33[0m')
        ret = login()
        if ret:
            while 1:
                #管理员登陆
                if ret['identity'] == 'Manager':
                    print('33[0;32m******************33[0m')
                    role_class = getattr(sys.modules[__name__],ret['identity'])
                    #将类实例化成相应的对象
                    obj = role_class(ret['username'])
    
                    while 1:
                        print('33[0;32m******************33[0m')
                        for i,v in enumerate(role_class.menu,1):
                            print(i,v[0])
                        #避免输入不合法,利用异常处理
    
                        choice = input('33[0;32m请输入您要进行的操作编号:33[0m')
                        if not choice.isdigit():
                            print('33[1;31m请输入数字!33[0m')
                            break
                        choice_int = int(choice)
                        if choice_int < 1 or choice_int > 9:
                            print('33[1;31m抱歉,请输入正确的编号!33[0m')
                            break
                        ##进行第二次的反射,后面加了括号直接执行了
                        getattr(obj,role_class.menu[choice_int-1][1])()
                #讲师登陆
                if ret['identity'] == 'Teacher':
                    print('33[0;32m******************33[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('33[0;32m******************33[0m')
                        for i,v in enumerate(teacher_class.menu,1):
                            print(i,v[0])
                        choice = input('33[0;32m请输入您要进行的操作编号:33[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('33[1;31m抱歉,请输入正确的编号!33[0m')
                #学生登录——需要输入班级
                if ret['identity'] == 'Student':
                    global stu_status
                    stu_status = False
                    global stu_class
                    print('33[0;32m******************33[0m')
                    class_input = input('请输入您所在的班级:').strip()
                    #判断输入的班级是否存在
                    if class_input not in file_name(config.STUDENTINFO):
                        print('33[1;31m抱歉,请输入正确的班级!33[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('33[0;32m******************33[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('33[1;31m请输入正确的操作编号!33[0m')
                        else:
                            print('33[1;31m抱歉您不在这个班级!33[0m')
            else:
                print('33[1;31m请输入正确的用户编号33[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('
    %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('33[1;31m该讲师已经存在!33[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('33[0;32m讲师创建成功!33[0m')
            write_log('创建了讲师:%s' % teacher_name)
    
    
        def create_classes(self):
            #注意,新建的班级名字不能是已经存在的
            classes_names = file_name(config.STUDENTINFO)
            print('33[0;32m已经存在的班级如下:33[0m')
            for i,v in enumerate(classes_names,1):
                print('%s: %s' % (i,v))
            class_name = input('请输入您要新建的班级名称:')
            if class_name in classes_names:
                print('33[1;31m该班级已经存在!33[0m')
                return
            school_name = input('请输入所属的学校:(Beijing|Shanghai)')
            if school_name not in 'Beijing|Shanghai'.split('|'):
                print('33[1;31m请输入正确的学校!33[0m')
                return
            course_name = input('请输入课程名称:(python|linux|go)')
            if course_name not in 'python|linux|go'.split('|'):
                print('33[1;31m请输入正确的课程!33[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('33[1;31m您填写的学校与课程的关系不存在!33[0m')
                return
    
    
        def create_student(self):
            student_name = input('请输入学生姓名:')
            student_pass = input('请输入学生密码:')
            #列举出所有存在的班级
            classes_names = file_name(config.STUDENTINFO)
            print('33[0;32m已经存在的班级如下:33[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('33[1;31m输入错误,创建学生失败!33[0m')   #for...else 语句
    
    
        def bound_class_teacher(self):
            global a
            global status
            status = False
            #显示既有的班级
            classes_names =file_name(config.STUDENTINFO)
            print('33[0;32m已经存在的班级如下:33[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('33[1;31m抱歉,没有这个班级!33[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('33[1;31m本教师已经与该课程有了绑定关系,请勿重复绑定!33[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('33[1;31m录入有误,绑定失败!33[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('33[1;32m您所教授的班级为:33[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('33[1;31m抱歉,您没有教这个班级!33[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('33[1;31m抱歉,您没有教这个班级!33[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('33[1;32m修改成功!
    33[0m')
                s_status = False
            else:
                print('33[1;31m录入有误,操作失败!33[0m')
    teacher.py

    演示如下:

  • 相关阅读:
    UVA247 电话圈 Calling Circles
    Python开发之路
    Day 8-模块
    Homework
    Day 7- 装饰器
    Day 6-文件操作的其他方法 迭代器 生成器
    数据-进制
    Day 5-变量与高阶函数
    Day 4-集合、百分号拼接及函数
    Day 3-Python列表、元祖、词典
  • 原文地址:https://www.cnblogs.com/paulwhw/p/9032015.html
Copyright © 2011-2022 走看看