# -*- coding: utf-8 -*-
# @Time    : 18-10-7 上午11:45
# @Author  : Felix Wang

'''
选课系统开发 系统登录需要有三类用户:学生、管理员、讲师,针对不同用户提供不
同功能:
学生用户 :对于学生用户来说,登陆之后有三个功能
1、查看所有课程
2、选择课程
3、查看所选课程
4、删除已选课程
管理员用户:管理员用户除了可以做一些查看功能之外,还有很多创建工作。
1、创建课程
2、创建学生学生账号
3、查看所有课程
4、查看所有学生
5、查看所有学生的选课情况
讲师用户:
1、查看所有课程
2、选择所教课程
3、查看所教课程
4、删除所教课程
'''
from hashlib import md5
import shelve  # 用来存储对象
import logging  # 记录日志

STUDENTOBJ = 'stu_obj'
ADMINOBJ = 'admin_obj'
TEACHEROBJ = 'teacher_obj'
COURSEOBJ = 'course_obj'


# 用来存储操作日志
def my_log(logfile, partName, level):
    """
    :param logfile: 日志文件名
    :param partName: 项目名称,模块名称或者其他日志使用者
    :param level: 日志等级
    :return: logger object

    # CRITICAL = 50
    # FATAL = CRITICAL
    # ERROR = 40
    # WARNING = 30
    # WARN = WARNING
    # INFO = 20
    # DEBUG = 10
    # NOTSET = 0
    """
    # 多文件日志处理
    # 创建⼀个操作⽇志的对象logger(依赖FileHandler)
    file_handler = logging.FileHandler(logfile, 'a', encoding='utf-8')
    # 设置日志文件内容的格式
    file_handler.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s"))
    logger = logging.Logger(partName, level=level)
    logger.addHandler(file_handler)
    return logger


# 用来将密码加密
def encryption(pwd):
    """
    :param pwd: 密码
    :return: 密码加密后得到的md5值
    """
    flag = bytes('@#$%', encoding='utf8')
    if type(pwd) == type(str()):
        byte1 = flag + bytes(pwd, encoding='utf8') + flag
    # print(str(byte,'utf8'))
    elif type(pwd) == type(bytes()):
        byte1 = flag + pwd + flag
    else:
        raise Exception('必须是字符串或者字节')
    return md5(byte1).hexdigest()


# 将对象存储
def saveObj(filename, obj):
    """
    :param filename: 对象存储的文件名
    :param obj: 对象
    :return:
    """
    with shelve.open(filename) as f:
        if hasattr(obj, 'userName'):
            f[obj.userName] = obj
        elif hasattr(obj, 'name'):
            f[obj.name] = obj


# 获取对象
def getObj(filename, keyvalue=None):
    """
    :param filename: 对象存储的文件名
    :param keyvalue: 对象存的键
    :return: 如果keyvalue为None,以字典形式返回文件中所有对象,如果keyvalue不为None,返回文件中对应的对象,如果不存在,返回None
    """
    if keyvalue == None:
        dic = {}
        with shelve.open(filename) as f:
            for key in f:
                dic[key] = f[key]
        return dic
    else:
        with shelve.open(filename) as f:
            if keyvalue in f:
                return f[keyvalue]
            else:
                return None


class Person:
    def __init__(self, userName, pwd):
        self.userName = userName
        self.__pwd = encryption(pwd)

    def login(self, userName, pwd):
        if self.userName == userName and self.__pwd == encryption(pwd):
            return True
        return False

    def showAllCourses(self):
        return getObj(COURSEOBJ)

    def __repr__(self):
        return self.userName


class Student(Person):
    def __init__(self, userName, pwd):
        super().__init__(userName, pwd)
        self.courses = []

    def __repr__(self):
        return '学生用户名:{}'.format(self.userName)

    # 查看选择的课程
    def showCourses(self):
        return self.courses

    # 选择课程
    def selectCourse(self, course):
        self.courses.append(course)

    # 删除课程
    def removeCourse(self, course):
        self.courses.remove(course)


class Admin(Person):
    # 创建学生
    def createStudent(self, name, pwd):
        newStu = Student(name, pwd)
        saveObj(STUDENTOBJ, newStu)
        return True

    # 创建课程
    def createCourse(self, name):
        course = Course(name)
        saveObj(COURSEOBJ, course)
        return True

    # 查看所有学生
    def showAllStudents(self):
        return getObj(STUDENTOBJ)


class Teacher(Person):
    def __init__(self, userName, pwd):
        super().__init__(userName, pwd)
        self.courses = []

    # 选择要教课程
    def selectCourse(self, course):
        self.courses.append(course)
        c = getObj(COURSEOBJ, course)
        c.setTeacher(self.userName)
        saveObj(COURSEOBJ, c)  # 更新

    # 查看所教的课程
    def showCourses(self):
        return self.courses

    # 删除所教课程
    def removeCourse(self, course):
        self.courses.remove(course)
        c = getObj(COURSEOBJ, course)
        c.setTeacher(None)
        saveObj(COURSEOBJ, c)  # 更新


class Course:
    def __init__(self, name):
        self.name = name
        self.teacher = None

    def setTeacher(self, teacher):
        self.teacher = teacher

    def __repr__(self):
        return '课程名:{},讲师:{}'.format(self.name, self.teacher if self.teacher else '无')

# 主程序
def main():
    print('欢迎来到felix选课系统!')
    while True:
        while True:
            num1 = input('请选择用户类型:1:学生  2:管理员  3:讲师  q:退出\n:')
            if num1 in ['1', '2', '3', 'q', 'Q']:
                break
            else:
                print('输入有误,请重新输入')
        if num1 == '1':
            while True:
                while True:
                    numStu = input('欢迎进入学生系统:请选择要执行的操作(1,登录  2、注册  b、返回上一级)\n:').strip()
                    if numStu in ['1', '2', 'b', 'B']:
                        break
                    else:
                        print('输入有误,请重新输入')
                if numStu == '2':
                    print('正在注册...')
                    while True:
                        userName = input('请输入用户名:')
                        if userName in getObj(STUDENTOBJ).keys():
                            print('用户名已存在,请重新输入')
                            continue
                        pwd = input('请输入密码:')
                        break
                    newStu = Student(userName, pwd)
                    saveObj(STUDENTOBJ, newStu)
                    print('注册成功')
                elif numStu == '1':
                    print('正在登录...')
                    userName = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    stu = getObj(STUDENTOBJ, userName)
                    if stu and stu.login(userName, pwd):
                        print('登录成功')
                        stulog = my_log('操作日志.log', 'stu-' + userName, 2)
                        stulog.log(2, '学生{}登录'.format(userName))
                        while True:
                            print('请选择需要执行的操作:')
                            while True:
                                numlogin = input('1、查看所有课程  2、选择课程  3、查看所选课程  4、删除已选课程  b、返回上一级\n:')
                                if numlogin in ['1', '2', '3', '4', 'b', 'B']:
                                    break
                                else:
                                    print('输入有误,请重新输入')
                            if numlogin == '1':
                                all_courses = stu.showAllCourses()
                                print('当前的全部课程:{}'.format('' if all_courses else '无'))
                                for k, v in all_courses.items():
                                    print(v)
                                stulog.log(2, '查看所有课程')
                            elif numlogin == '2':
                                all_courses = stu.showAllCourses()
                                print('目前有如下课程:{}'.format('' if all_courses else '无'))
                                if all_courses:
                                    for k, v in all_courses.items():
                                        print(v)
                                    else:
                                        print()
                                    while True:
                                        courseName = input('请输入您要选择的课程名:').strip()
                                        if courseName not in all_courses.keys():
                                            print('您输入的课程名不存在,请重新输入')
                                        else:
                                            break
                                    if courseName not in stu.showCourses():
                                        stu.selectCourse(courseName)
                                        saveObj(STUDENTOBJ, stu)  # 更新
                                        print('课程选择成功')
                                        stulog.log(2, '选择了{}课程'.format(courseName))
                                    else:
                                        print('课程已存在,不要重复选择')
                                else:
                                    print('当前没有课程,不能选择')
                            elif numlogin == '3':
                                print('当前已选择的课程有:{}'.format('' if stu.showCourses() else '无'))
                                for course in stu.showCourses():
                                    c = getObj(COURSEOBJ, course)
                                    print(c)
                                stulog.log(2, '查看了已经选择的课程')
                            elif numlogin == '4':
                                print('当前已选课程:{}'.format('' if stu.showCourses() else '无'))
                                if stu.showCourses():
                                    for course in stu.showCourses():
                                        print(course, end='    ')
                                    else:
                                        print()
                                    while True:
                                        delCourseName = input('请输入需要删除的课程名:').strip()
                                        if delCourseName in stu.showCourses():
                                            break
                                        else:
                                            print('您没有选择该课程,请重新输入')
                                    stu.removeCourse(delCourseName)
                                    saveObj(STUDENTOBJ, stu)  # 更新
                                    print('删除成功')
                                    stulog.log(2, '删除了{}课程'.format(delCourseName))
                                else:
                                    print('当前没有选择课程,不能删除')

                                    # /////
                            elif numlogin.upper() == 'B':
                                stulog.log(2, '学生退出')
                                break
                    else:
                        print('账号或密码错误')
                elif numStu.upper() == 'B':
                    break
        elif num1 == '2':
            while True:
                while True:
                    numStu = input('欢迎进入管理员系统:请选择要执行的操作(1,登录  2、注册  b、返回上一级)\n:')
                    if numStu in ['1', '2', 'b', 'B']:
                        break
                    else:
                        print('输入有误,请重新输入')
                if numStu == '2':
                    print('正在注册...')
                    while True:
                        userName = input('请输入用户名:')
                        if userName in getObj(ADMINOBJ).keys():
                            print('用户名已存在,请重新输入')
                            continue
                        pwd = input('请输入密码:')
                        break
                    newAdmin = Admin(userName, pwd)
                    saveObj(ADMINOBJ, newAdmin)
                    print('注册成功')
                elif numStu == '1':
                    print('正在登录...')
                    userName = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    admin = getObj(ADMINOBJ, userName)
                    if admin and admin.login(userName, pwd):
                        print('登录成功')
                        adminlog = my_log('操作日志.log', 'admin-' + userName, 2)
                        adminlog.log(2, '管理员{}登录'.format(userName))
                        while True:
                            print('请选择要执行的操作:')
                            while True:
                                numlogin = input('1、创建课程  2、创建学生学生账号  3、查看所有课程  4、查看所有学生  5、查看所有学生的选课情况  b、返回上一级\n:')
                                if numlogin in ['1', '2', '3', '4', '5', 'b', 'B']:
                                    break
                                else:
                                    print('输入有误,请重新输入')
                            if numlogin == '1':
                                while True:
                                    courseName = input('请输入课程名称:')
                                    if courseName in getObj(COURSEOBJ).keys():
                                        print('课程已存在,请重新输入')
                                        continue
                                    break
                                admin.createCourse(courseName)
                                print('课程创建成功')
                                adminlog.log(2, '创建了{}课程'.format(courseName))
                            elif numlogin == '2':
                                print('正在创建学生账号...')
                                while True:
                                    userName = input('请输入学生用户名:')
                                    if userName in getObj(STUDENTOBJ).keys():
                                        print('用户名已存在,请重新输入')
                                        continue
                                    pwd = input('请输入密码:')
                                    break
                                admin.createStudent(userName, pwd)
                                print('学生账号创建成功')
                                adminlog.log(2, '创建了{}学生'.format(userName))
                            elif numlogin == '3':
                                all_courses = admin.showAllCourses()
                                print('当前的全部课程:{}'.format('' if all_courses else '无'))
                                for k, v in all_courses.items():
                                    print(v)
                                adminlog.log(2, '查看了当前的全部课程')
                            elif numlogin == '4':
                                all_student = admin.showAllStudents()
                                for k, v in all_student.items():
                                    print(v)
                                adminlog.log(2, '查看了当前全部学生')
                            elif numlogin == '5':
                                all_student = admin.showAllStudents()
                                for k, v in all_student.items():
                                    print('学生用户名:{},选择的课程:{}'.format(str(k),
                                                                     str(v.showCourses() if v.showCourses() else '无')))
                                adminlog.log(2, '查看了全部学生的选课信息')
                            elif numlogin.upper() == 'B':
                                adminlog.log(2, '管理员退出')
                                break
                    else:
                        print('账号或密码错误')
                elif numStu.upper() == 'B':
                    break
        elif num1 == '3':
            while True:
                while True:
                    numStu = input('欢迎进入讲师系统:请选择要执行的操作(1,登录  2、注册  b、返回上一级)\n:')
                    if numStu in ['1', '2', 'b', 'B']:
                        break
                    else:
                        print('输入有误,请重新输入')
                if numStu == '2':
                    print('正在注册...')
                    while True:
                        userName = input('请输入用户名:')
                        if userName in getObj(TEACHEROBJ).keys():
                            print('用户名已存在,请重新输入')
                            continue
                        pwd = input('请输入密码:')
                        break
                    newTeacher = Teacher(userName, pwd)
                    saveObj(TEACHEROBJ, newTeacher)
                    print('注册成功')
                elif numStu == '1':
                    print('正在登录...')
                    userName = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    teacher = getObj(TEACHEROBJ, userName)
                    if teacher and teacher.login(userName, pwd):
                        print('登录成功')
                        teacherlog = my_log('操作日志.log', 'teacher-' + userName, 2)
                        teacherlog.log(2, '讲师{}登录'.format(userName))
                        while True:
                            print('请选择需要执行的操作:')
                            while True:
                                numlogin = input('1、查看所有课程  2、选择所教课程  3、查看所教课程  4、删除所教课程  b、返回上一级\n:')
                                if numlogin in ['1', '2', '3', '4', 'b', 'B']:
                                    break
                                else:
                                    print('输入有误,请重新输入')
                            if numlogin == '1':
                                all_courses = teacher.showAllCourses()
                                print('当前的全部课程:{}'.format('' if all_courses else '无'))
                                for k, v in all_courses.items():
                                    print(v)
                                teacherlog.log(2, '查看了所有课程')
                            elif numlogin == '2':
                                all_courses = teacher.showAllCourses()
                                print('目前有如下课程:{}'.format('' if all_courses else '无'))
                                if all_courses:
                                    for k, v in all_courses.items():
                                        print(v)
                                    else:
                                        print()
                                    while True:
                                        courseName = input('请输入您要教的课程名:').strip()
                                        if courseName not in all_courses.keys():
                                            print('您输入的课程名不存在,请重新输入')
                                        else:
                                            break
                                    if courseName not in teacher.showCourses():
                                        teacher.selectCourse(courseName)
                                        saveObj(TEACHEROBJ, teacher)  # 更新
                                        print('课程选择成功')
                                        teacherlog.log(2, '选择了教{}课程'.format(courseName))
                                    else:
                                        print('您已选择该课程,不能重复选择')
                                else:
                                    print('当前没有课程,无法选择')
                            elif numlogin == '3':
                                print('当前已选择的课程有:{}'.format('' if teacher.showCourses() else '无'))
                                for course in teacher.showCourses():
                                    c = getObj(COURSEOBJ, course)
                                    print(c)
                                teacherlog.log(2, '查看了当前选择的课程')
                            elif numlogin == '4':
                                print('当前已选课程:{}'.format('' if teacher.showCourses() else '无'))
                                if teacher.showCourses():
                                    for course in teacher.showCourses():
                                        print(course, end='    ')
                                    else:
                                        print()
                                    while True:
                                        delCourseName = input('请输入需要删除的课程名:').strip()
                                        if delCourseName in teacher.showCourses():
                                            break
                                        else:
                                            print('您没有选择该课程,请重新输入')
                                    teacher.removeCourse(delCourseName)
                                    saveObj(STUDENTOBJ, teacher)  # 更新
                                    print('删除成功')
                                    teacherlog.log(2, '取消了教{}课程'.format(delCourseName))
                                else:
                                    print('当前没有教的课程,不能删除')
                            elif numlogin.upper() == 'B':
                                teacherlog.log(2, '讲师退出')
                                break
                    else:
                        print('账号或密码错误')
                elif numStu.upper() == 'B':
                    break
        elif num1.upper() == 'Q':
            print('感谢使用felix选课系统,欢迎下次使用!')
            break


if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        print('发生未知错误,请联系管理员改进')