Example #1
0
    def __init__(self,
                 urls=None,
                 user_config_path='../conf/user_config.ini',
                 *args, **kwargs):
        '''
        :param urls:
        :param user_config_path:
        :param args:
        :param kwargs: 目前仍支持从settings中读取设备,后续考虑移除
        '''
        self._logger = LogHandler("Loginer")
        self._S = requests.session()
        self._user_config_path = user_config_path
        self._user_info_from_settings = kwargs.get('user_info')
        self._cfg = get_cfg(self._user_config_path)
        self._urls = urls

        self.headers = {
            'Connection': 'keep-alive',
            'sec-ch-ua': '"Google Chrome";v="87", "\\"Not;A\\\\Brand";v="99", "Chromium";v="87"',
            'Accept': '*/*',
            'X-Requested-With': 'XMLHttpRequest',
            'sec-ch-ua-mobile': '?0',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Origin': 'https://onestop.ucas.ac.cn',
            'Sec-Fetch-Site': 'same-origin',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Dest': 'empty',
            'Referer': 'https://onestop.ucas.ac.cn/',
            'Accept-Language': 'zh-CN,zh;q=0.9',
        }
Example #2
0
 def __init__(self,
              urls=None,
              user_config_path='../conf/user_config.ini',
              *args,
              **kwargs):
     super().__init__(urls, user_config_path, *args, **kwargs)
     self._logger = LogHandler('GradeObserver')
Example #3
0
    def __init__(self,
                 urls=None,
                 user_config_path='../conf/user_config.ini',
                 assess_msgs=[],
                 *args,
                 **kwargs):
        super().__init__(urls, user_config_path, *args, **kwargs)
        self._logger = LogHandler('Assesser')
        self._assess_msgs = assess_msgs

        self._id_pattern = re.compile('/evaluate/.*?/(?P<id>.*?)$')
        self._course_assess_url = None  # 动态获取课程评估地址
Example #4
0
    def __init__(self,
                 urls=None,
                 user_config_path='../conf/user_config.ini',
                 *args,
                 **kwargs):
        super().__init__(urls, user_config_path, *args, **kwargs)
        self._logger = LogHandler("Downloader")
        self._resource_path_from_settings = kwargs.get('resource_path')
        self._filter_list = kwargs.get('filter_list')

        self._update_sources = []
        self._l_course_info = []
        self._d_source_info = {}
        self._cur_course_info = None

        self._collection_id_pattern = re.compile(
            "value='(?P<collection_id>.*?)';")  # 获取collection id 信息正则
        self._dir_pattern = re.compile(
            "value='/group/[0-9]*/(?P<dir>.*?)';")  # 获取文件夹目录信息正则
Example #5
0
    def __init__(self,
                 welcome_msg,
                 record_path='../conf/record.ini',
                 *args,
                 **kwargs):
        self._logger = LogHandler("Init")
        self._welcome_msg = welcome_msg

        self._record_path = record_path
        self._cfg = get_cfg(config_path=self._record_path)

        self._name_of_update_section = 'update_info'
        self._name_of_update_time = 'last_update_time'  # 记录上次更新的时间
        self._name_of_tag = 'tag'  # 当前版本号

        # update api info
        self.__update_info_api = "https://api.github.com/repos/GentleCP/UCAS-Helper"
        self.__latest_tag_api = "https://api.github.com/repos/GentleCP/UCAS-Helper/tags"

        self._wifiLoginer = WifiLoginer(accounts_path=settings.ACCOUNTS_PATH)
        self._downloader = Downloader(
            user_info=settings.USER_INFO,  # 未来删除
            urls=settings.URLS,
            user_config_path=settings.USER_CONFIG_PATH,
            resource_path=settings.SOURCE_DIR,  # 未来删除
            filter_list=settings.FILTER_LIST)

        self._assesser = Assesser(
            user_info=settings.USER_INFO,  # 未来删除
            user_config_path=settings.USER_CONFIG_PATH,
            urls=settings.URLS,
            assess_msgs=settings.ASSESS_MSG)
        self._gradeObserver = GradeObserver(
            user_config_path=settings.USER_CONFIG_PATH,
            user_info=settings.USER_INFO,  # 未来删除
            urls=settings.URLS)
Example #6
0
class GradeObserver(Loginer):
    """
    课程成绩查看器
    """
    def __init__(self,
                 urls=None,
                 user_config_path='../conf/user_config.ini',
                 *args,
                 **kwargs):
        super().__init__(urls, user_config_path, *args, **kwargs)
        self._logger = LogHandler('GradeObserver')

    def _show_grade(self):
        try:
            res = self._S.get(self._urls['grade_url']['http'],
                              headers=self.headers,
                              timeout=5)
        except requests.Timeout:
            res = self._S.get(self._urls['grade_url']['https'],
                              headers=self.headers)

        bs4obj = BeautifulSoup(res.text, 'html.parser')
        thead = bs4obj.find('thead')
        pd = PrettyTable()
        pd.field_names = [x.string for x in thead.find_all('th')]

        tbody = bs4obj.find('tbody')
        for tr in tbody.find_all('tr'):
            # tr:每一门课程信息
            pd.add_row([x.string.strip() for x in tr.find_all('td')])
        self._logger.info('成绩查询结果如下')
        print(pd)

    def run(self):
        self.login()
        self._show_grade()
Example #7
0
class Init(object):
    """
    用于检查一切配置信息是否合理正确
    """
    def __init__(self,
                 welcome_msg,
                 record_path='../conf/record.ini',
                 *args,
                 **kwargs):
        self._logger = LogHandler("Init")
        self._welcome_msg = welcome_msg

        self._record_path = record_path
        self._cfg = get_cfg(config_path=self._record_path)

        self._name_of_update_section = 'update_info'
        self._name_of_update_time = 'last_update_time'  # 记录上次更新的时间
        self._name_of_tag = 'tag'  # 当前版本号

        # update api info
        self.__update_info_api = "https://api.github.com/repos/GentleCP/UCAS-Helper"
        self.__latest_tag_api = "https://api.github.com/repos/GentleCP/UCAS-Helper/tags"

        self._wifiLoginer = WifiLoginer(accounts_path=settings.ACCOUNTS_PATH)
        self._downloader = Downloader(
            user_info=settings.USER_INFO,  # 未来删除
            urls=settings.URLS,
            user_config_path=settings.USER_CONFIG_PATH,
            resource_path=settings.SOURCE_DIR,  # 未来删除
            filter_list=settings.FILTER_LIST)

        self._assesser = Assesser(
            user_info=settings.USER_INFO,  # 未来删除
            user_config_path=settings.USER_CONFIG_PATH,
            urls=settings.URLS,
            assess_msgs=settings.ASSESS_MSG)
        self._gradeObserver = GradeObserver(
            user_config_path=settings.USER_CONFIG_PATH,
            user_info=settings.USER_INFO,  # 未来删除
            urls=settings.URLS)

    def __get_tag(self):
        '''
        从配置文件或在线获取当前版本号
        :return: tag, e.g. v2.3.1
        '''
        if not self._cfg.has_section(self._name_of_update_section):
            self._cfg.add_section(self._name_of_update_section)
        try:
            local_tag = self._cfg.get(self._name_of_update_section,
                                      self._name_of_tag)
        except configparser.NoOptionError:
            self._logger.info('getting latest tag')
            return json.loads(requests.get(
                self.__latest_tag_api).text)[0].get('name')
        else:
            if not local_tag:
                self._logger.info('getting latest tag')
                return json.loads(requests.get(
                    self.__latest_tag_api).text)[0].get('name')
            else:
                return local_tag

    def _show_welcome(self):
        '''
        :return:
        '''
        tag = self.__get_tag()
        print(self._welcome_msg.format(tag=tag))

    def __check_update(self):
        '''
        check the latest code from github repo api, if detect the new version, update the demo
        :return: {}, if need update, return True and latest_update_time, else return False
        '''
        self._logger.info("Checking update...")

        try:
            latest_update_time = requests.get(
                self.__update_info_api).json()["updated_at"]
            latest_tag = json.loads(requests.get(
                self.__latest_tag_api).text)[0].get('name')
        except Exception:
            self._logger.error("checking update faild.")
            return {'need_update': False}
        try:
            last_update_time = self._cfg.get(self._name_of_update_section,
                                             self._name_of_update_time)
        except (configparser.NoSectionError, configparser.NoOptionError) as e:
            self._logger.info("Available updates detected, start updating...")
            return {
                'need_update': True,
                'latest_update_time': latest_update_time,
                'latest_tag': latest_tag
            }
        else:
            if latest_update_time == last_update_time:
                # already up to date
                self._logger.info("Already up to date.")
                return {'need_update': False}
            else:
                self._logger.info(
                    "Available updates detected, start updating...")
                return {
                    'need_update': True,
                    'latest_update_time': latest_update_time,
                    'latest_tag': latest_tag
                }

    def _do_update(self):
        if not settings.ALLOW_AUTO_UPDATE:
            # not allow update
            return
        check_update_res = self.__check_update()
        if check_update_res['need_update']:
            # need to update
            try:
                os.system(
                    "git stash && git fetch --all && git merge && git stash pop"
                )
            except KeyboardInterrupt:
                # update interrupt, nothing to do.
                self._logger.error("Update Interrupt by user.")
            else:
                # update complete, update the local update time.
                if not self._cfg.has_section(self._name_of_update_section):
                    self._cfg.add_section(self._name_of_update_section)

                # update latest_update_time
                self._cfg.set(self._name_of_update_section,
                              self._name_of_update_time,
                              check_update_res['latest_update_time'])
                # update latest_tag
                self._cfg.set(self._name_of_update_section, self._name_of_tag,
                              check_update_res['latest_tag'])
                self._cfg.write(open(self._record_path, 'w'))
                self._logger.info("Update compelte.")

    def _cmd(self):
        while True:
            time.sleep(0.1)
            option = input("输入你的操作:")
            if option == 'q':
                print("欢迎使用,下次再会~")
                exit(ExitStatus.OK)

            elif not (option.isdigit() and 1 <= int(option) <= 5):
                self._logger.error("非法操作,请重新输入")
            else:
                option = int(option)
                if option == 1:
                    try:
                        self._downloader.run()
                    except BackToMain:
                        pass

                elif option == 2:
                    try:
                        self._wifiLoginer.login()
                    except WifiError:
                        pass

                elif option == 3:
                    try:
                        self._wifiLoginer.logout()
                    except WifiError:
                        pass

                elif option == 4:
                    self._assesser.run()

                elif option == 5:
                    self._gradeObserver.run()

    def run(self):
        self._show_welcome()
        self._do_update()
        self._cmd()
Example #8
0
class Loginer(object):
    """
    登录课程网站
    """
    def __init__(self,
                 urls=None,
                 user_config_path='../conf/user_config.ini',
                 *args, **kwargs):
        '''
        :param urls:
        :param user_config_path:
        :param args:
        :param kwargs: 目前仍支持从settings中读取设备,后续考虑移除
        '''
        self._logger = LogHandler("Loginer")
        self._S = requests.session()
        self._user_config_path = user_config_path
        self._user_info_from_settings = kwargs.get('user_info')
        self._cfg = get_cfg(self._user_config_path)
        self._urls = urls

        self.headers = {
            'Connection': 'keep-alive',
            'sec-ch-ua': '"Google Chrome";v="87", "\\"Not;A\\\\Brand";v="99", "Chromium";v="87"',
            'Accept': '*/*',
            'X-Requested-With': 'XMLHttpRequest',
            'sec-ch-ua-mobile': '?0',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Origin': 'https://onestop.ucas.ac.cn',
            'Sec-Fetch-Site': 'same-origin',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Dest': 'empty',
            'Referer': 'https://onestop.ucas.ac.cn/',
            'Accept-Language': 'zh-CN,zh;q=0.9',
        }


    def _set_user_info(self):
        '''
        set user info from conf/user_config.ini or from settings.py
        :return: None
        '''

        from_settings_warning_msg = ('Note: you are using the user info from settings.py which may remove in the future, '
                                 'I suggest you to save the user info in conf/user_config.ini')
        try:
            username = self._cfg.get('user_info', 'username')
            password = self._cfg.get('user_info', 'password')
        except (configparser.NoSectionError, configparser.NoOptionError) as e:
            self._logger.warning('Can not read user info from {}, try to get it from settings.py'.format(self._user_config_path))
            self._logger.warning(from_settings_warning_msg)
            self._user_info = self._user_info_from_settings
        else:
            if not username or not password:
                # 用户名或密码信息为空
                self._logger.warning(from_settings_warning_msg)
                self._user_info = self._user_info_from_settings
            else:
                self._user_info = {
                    'username': username,
                    'password': password,
                    'remember': 'undefined'
                }


    def __keep_session(self):
        try:
            res = self._S.get(url=self._urls['course_select_url']['http'], headers = self.headers, timeout=5)
        except requests.Timeout:
            res = self._S.get(url=self._urls['course_select_url']['https'], headers = self.headers)
        course_select_url = re.search(r"window.location.href='(?P<course_select_url>.*?)'", res.text).groupdict().get(
            "course_select_url")
        self._S.get(course_select_url,headers=self.headers)


    def login(self):
        self._set_user_info()
        self._S.get(url=self._urls['home_url']['https'], headers=self.headers, verify=False)  # 获取identity
        res = None
        try:
            res = self._S.post(url=self._urls["login_url"]['https'], data=self._user_info, headers=self.headers, timeout=10)
        except (requests.exceptions.ConnectionError,
                requests.exceptions.ConnectTimeout,
                requests.exceptions.ReadTimeout):
            self._logger.error("网络连接失败,请确认你的网络环境后重试!")
            exit(ExitStatus.NETWORK_ERROR)
        if res.status_code != 200:
            self._logger.error('sep登录失败,未知错误,请到github提交issue,等待作者修复.')
            exit(ExitStatus.UNKNOW_ERROR)
        else:
            json_res = res.json()
            if json_res["f"]:
                self._S.get(res.json()["msg"], headers=self.headers)
                self._logger.info("sep登录成功!")
                self.__keep_session()
            else:
                self._logger.error("sep登录失败,请检查你的用户名和密码设置是否正确!")
                exit(ExitStatus.CONFIG_ERROR)
Example #9
0
class Downloader(Loginer):
    def __init__(self,
                 urls=None,
                 user_config_path='../conf/user_config.ini',
                 *args,
                 **kwargs):
        super().__init__(urls, user_config_path, *args, **kwargs)
        self._logger = LogHandler("Downloader")
        self._resource_path_from_settings = kwargs.get('resource_path')
        self._filter_list = kwargs.get('filter_list')

        self._update_sources = []
        self._l_course_info = []
        self._d_source_info = {}
        self._cur_course_info = None

        self._collection_id_pattern = re.compile(
            "value='(?P<collection_id>.*?)';")  # 获取collection id 信息正则
        self._dir_pattern = re.compile(
            "value='/group/[0-9]*/(?P<dir>.*?)';")  # 获取文件夹目录信息正则

    def _set_resource_path(self):
        '''
        set resource path from conf/user_config.ini or from settings.py
        :return: None
        '''

        from_settings_warning_msg = (
            'Note: you are using the resource path from settings.py which may remove in the future, '
            'I suggest you to save the resource path in conf/user_config.ini')
        try:
            resource_path = self._cfg.get('course_info', 'resource_path')
        except (configparser.NoSectionError, configparser.NoOptionError) as e:
            self._logger.warning(
                'Can not read resource path from {}, try to get it from settings.py'
                .format(self._user_config_path))
            self._logger.warning(from_settings_warning_msg)
            self._resource_path = self._resource_path_from_settings
        else:
            if not resource_path:
                self._logger.warning(from_settings_warning_msg)
                self._resource_path = self._resource_path_from_settings
            else:
                self._resource_path = resource_path

    def __update_source_info(self, course_info, bs4obj, dir):
        i = 1
        for e in bs4obj.findAll('a'):
            try:
                if 'course.ucas.ac.cn/access/content/group' in e["href"]:
                    filename = e.find('span', {
                        'class': 'hidden-sm hidden-xs'
                    }).get_text()
                    self._d_source_info[course_info["name"]].append({
                        'id':
                        i,
                        'name':
                        dir + filename,
                        'url':
                        e["href"]
                    })
                    i += 1
            except (KeyError, AttributeError):
                continue

    def _recur_dir(self, course_info, source_url, bs4obj):
        '''
        递归获取文件夹下文件信息
        :param source_url:
        :param bs4obj:
        :return:
        '''
        l_dir_objs = bs4obj.findAll('a', {'title': '文件夹'})
        if len(l_dir_objs) > 1:
            # 存在其他文件夹,添加当前目录资源信息,接着递归文件夹下内容
            cur_dir = self._dir_pattern.findall(
                l_dir_objs[0]["onclick"])[0]  # 获取了课程文件夹信息
            self.__update_source_info(course_info, bs4obj, cur_dir)

            csrf_token = bs4obj.find('input', {
                'name': 'sakai_csrf_token'
            }).get("value")  # 获取token,用于请求文件夹资源

            for e in bs4obj.findAll('a', {'title': '文件夹'})[1:]:  # 第一个是当前目录忽略
                collection_id = self._collection_id_pattern.findall(
                    e["onclick"])[1]  # 获取了课程文件夹信息
                data = {
                    'source': 0,
                    'collectionId': collection_id,
                    'navRoot': '',
                    'criteria': 'title',
                    'sakai_action': 'doNavigate',
                    'rt_action': '',
                    'selectedItemId': '',
                    'itemHidden': 'false',
                    'itemCanRevise': 'false',
                    'sakai_csrf_token': csrf_token
                }
                res = self._S.post(source_url, data=data,
                                   headers=self.headers)  # 获取文件夹下资源信息
                bs4obj = BeautifulSoup(res.text, 'html.parser')
                self._recur_dir(course_info, source_url, bs4obj)

        else:
            # 没有更深层文件夹了,添加资源信息
            cur_dir = self._dir_pattern.findall(
                l_dir_objs[0]["onclick"])[0]  # 获取了课程文件夹信息
            self.__update_source_info(course_info, bs4obj, cur_dir)
            return

    def _set_course_info(self):
        if not self._l_course_info:
            # 减少后续多次请求课程信息耗时
            try:
                res = self._S.get(url=self._urls['course_info_url']['http'],
                                  headers=self.headers,
                                  timeout=5)
            except requests.Timeout:
                res = self._S.get(url=self._urls['course_info_url']['https'],
                                  headers=self.headers)

            bsobj = BeautifulSoup(res.text, "html.parser")
            refresh_url = bsobj.find("noscript").meta.get("content")[
                6:]  # 获取新的定向url
            res = self._S.get(refresh_url, headers=self.headers)
            bsobj = BeautifulSoup(res.text, "html.parser")
            new_course_url = bsobj.find('a', {
                "title": "我的课程 - 查看或加入站点"
            }).get("href")  # 获取到新的课程信息url
            res = self._S.get(new_course_url, headers=self.headers)
            bsobj = BeautifulSoup(res.text, "html.parser")
            course_list = bsobj.findAll('tr')  # 尚未筛选的杂乱信息
            i = 1
            for course in course_list:
                a = course.find('a')
                course_url = a.get("href")
                course_name = a.get_text()
                if "课程名称" not in course_name:
                    self._l_course_info.append({
                        'id': i,
                        'name': course_name,
                        'url': course_url
                    })
                    self._d_source_info.update({course_name: []})  # 为该课程开辟一个位置
                    i += 1

    def _set_source_info(self, course_info):
        '''
        给定一门课(name+url),获取该课的所有课程资源
        :param course:
        :return:
        '''
        if not self._d_source_info[course_info["name"]]:
            # 该门课的资源信息尚未存储到内存
            res = self._S.get(course_info["url"], headers=self.headers)
            bs4obj = BeautifulSoup(res.text, "html.parser")
            source_url = bs4obj.find('a', {
                'title': '资源 - 上传、下载课件,发布文档,网址等信息'
            }).get("href")
            res = self._S.get(source_url, headers=self.headers)  # 获取课程资源页面
            bs4obj = BeautifulSoup(res.text, "lxml")
            self._recur_dir(course_info, source_url, bs4obj)

    def _download_one(self, course_info, source_info):
        '''
        给定一门课,下载该门课指定一个资源
        :return:
        '''
        # 按季度划分课程
        if "秋季" in course_info['name']:
            base_dir = self._resource_path + '/秋季/'
        elif "春季" in course_info['name']:
            base_dir = self._resource_path + '/春季/'
        else:
            base_dir = self._resource_path + '/夏季/'
        if not os.path.exists(base_dir):
            os.mkdir(base_dir)

        course_dir = base_dir + course_info['name']  # 课程目录
        if not os.path.exists(course_dir):
            os.mkdir(course_dir)

        dirs = source_info['name'].split('/')[0:-1]  # 只取目录部分
        if dirs:
            # 存在文件夹,递归检测文件夹
            recur_mkdir(course_dir, dirs)

        file_path = base_dir + course_info["name"] + '/' + source_info[
            'name']  # 文件存储路径
        if not os.path.isfile(file_path):
            # 只下载没有的文件,若存在不下载,节省流量
            self._logger.info("正在下载:{}".format(source_info['name']))
            download_file(url=source_info['url'],
                          session=self._S,
                          file_path=file_path)
            self._update_sources.append("[{}:{}]".format(
                course_info["name"], source_info['name']))  # 记录更新的课程数据

    def _download_course(self, course_info):
        '''
        下载一门课的所有资源
        :param S:
        :param course_info:
        :return:
        '''
        print("\033[1;45m正在同步{}全部资源... \033[0m".format(course_info["name"]))
        for source_info in self._d_source_info[course_info["name"]]:
            self._download_one(course_info, source_info)

    def _download_all(self, season=None):
        for course_info in self._l_course_info:
            if season is None:
                if course_info['name'] not in self._filter_list:
                    self._set_source_info(course_info)
                    self._download_course(course_info)
            else:
                if season in course_info['name'] and course_info[
                        'name'] not in self._filter_list:
                    self._set_source_info(course_info)
                    self._download_course(course_info)
        if self._update_sources:
            self._logger.info("[同步完成] 本次更新资源列表如下:")
            for source in self._update_sources:
                print('\033[1;41m' + source + '\033[0m')

            is_open = input("是否打开资源所在目录(默认n)?(y/n)")
            if is_open == 'y':
                if open_dir(self._resource_path) == 0:
                    self._logger.info("已为您打开资源目录,请根据更新资源列表查询对应文件!")
                else:
                    self._logger.error("打开资源目录失败,请手动开启!")
        else:
            self._logger.info("[同步完成] 本次无更新内容!")
        exit(ExitStatus.OK)

    def __check_option(self, option):
        if option == 'q':
            print("欢迎使用,下次再会~")
            exit(ExitStatus.OK)

        elif option == 'b' and self._cur_course_info:
            self._cur_course_info = None  # 清空
            return True

        elif option == 'd' and not self._cur_course_info:
            self._download_all()
            return False

        elif option == 's' and not self._cur_course_info:
            self._download_all(season='春季')
            return False

        elif option == 'm' and not self._cur_course_info:
            self._download_all(season='夏季')
            return False

        elif option == 'f' and not self._cur_course_info:
            self._download_all(season='秋季')
            return False

        elif option == 'a' and self._cur_course_info:
            self._download_course(course_info=self._cur_course_info)

        elif option.isdigit() and self._cur_course_info:
            # 课程界面操作
            try:
                source_info = self._d_source_info[
                    self._cur_course_info["name"]][int(option) - 1]
            except IndexError:
                self._logger.warning("不存在该序号课程资源,请重新选择!")
            else:
                self._download_one(self._cur_course_info, source_info)

        elif option.isdigit():
            # 主界面操作
            try:
                self._cur_course_info = self._l_course_info[(int(option) - 1)]
            except IndexError:
                self._logger.warning("不存在该序号课程,请重新选择!")
            else:
                self._set_source_info(self._cur_course_info)
                return True

        else:
            self._logger.warning("非法操作,请重新输入")
            return False

    def _cmd(self):
        while True:
            print("\033[1;45m>课程列表:\033[0m", flush=True)
            show(self._l_course_info)
            print("""
***************************************
*       id:显示对应课程的所有资源       *
*       d:一键同步所有资源             *
*       s:同步春季课程资源             *
*       m:同步夏季课程资源             *
*       f:同步秋季课程资源             *
*       q:退出                        *
***************************************
            """)
            option = input("请输入你的操作:")
            if not self.__check_option(option):
                # 不进入下一级界面
                continue
            while True:
                print("\033[1;45m>课程列表>{}:\033[0m".format(
                    self._cur_course_info["name"]))
                show(self._d_source_info[self._cur_course_info["name"]])
                print("""
*********************************
*       id:下载对应id资源         *
*       a:下载所有               *
*       b:返回上一级             *
*       q:退出                  *
********************************
                """)
                option = input("请输入你的操作:")
                if self.__check_option(option):
                    # 接收到返回上级界面信息
                    break

    def run(self):
        self._set_resource_path()
        if check_dir(self._resource_path):
            self._logger.error("资源存储路径非法或不正确,请检查你的resource_path配置是否正确!")
            exit(ExitStatus.CONFIG_ERROR)
        self.login()
        self._set_course_info()  # 添加所有课程信息到内存中
        self._cmd()  # 进入交互界面
Example #10
0
class Assesser(Loginer):
    def __init__(self,
                 urls=None,
                 user_config_path='../conf/user_config.ini',
                 assess_msgs=[],
                 *args,
                 **kwargs):
        super().__init__(urls, user_config_path, *args, **kwargs)
        self._logger = LogHandler('Assesser')
        self._assess_msgs = assess_msgs

        self._id_pattern = re.compile('/evaluate/.*?/(?P<id>.*?)$')
        self._course_assess_url = None  # 动态获取课程评估地址

    def _get_course_ids(self):
        # 获取课程评估url
        try:
            res = self._S.get(url=self._urls['view_url']['http'],
                              headers=self.headers,
                              timeout=5)
        except requests.Timeout:
            res = self._S.get(url=self._urls['view_url']['https'],
                              headers=self.headers)
        bs4obj = BeautifulSoup(res.text, 'html.parser')
        href = bs4obj.find('a', string=re.compile('.*学期$')).get('href')
        self._course_assess_url = self._urls['base_url']['http'] + href
        # 获取课程id
        try:
            res = self._S.get(self._course_assess_url,
                              headers=self.headers,
                              timeout=5)
        except requests.Timeout:
            self._course_assess_url = self._urls['base_url']['https'] + href
            res = self._S.get(self._course_assess_url, headers=self.headers)

        bs4obj = BeautifulSoup(res.text, 'html.parser')
        urls = [
            url.get('href') for url in bs4obj.find_all('a', {'class': 'btn'})
        ]
        course_ids = []
        for url in urls:
            course_ids.append(self._id_pattern.search(url).groupdict()['id'])
        return course_ids

    def __assess_course(self, course_id):
        try:
            res = self._S.get(self._urls['base_evaluateCourse_url']['http'] +
                              course_id,
                              headers=self.headers,
                              timeout=5)
        except requests.Timeout:
            res = self._S.get(self._urls['base_evaluateCourse_url']['https'] +
                              course_id,
                              headers=self.headers)

        s = res.text.split('?s=')[-1].split('"')[0]
        bs4obj = BeautifulSoup(res.text, 'html.parser')
        radios = bs4obj.find_all('input', attrs={'type': 'radio'})
        value = radios[0]['value']
        data = {}
        for radio in radios:
            data[radio['name']] = value
        textareas = bs4obj.find_all('textarea')
        for textarea, asses_msg in zip(textareas, self._assess_msgs[0:-2]):
            # 填写主观评价内容
            item_id = textarea.get('id')
            data[item_id] = asses_msg
        subjectiveRadio = bs4obj.find('input', {
            'class': 'required radio'
        }).get('id')
        subjectiveCheckbox = bs4obj.find(
            'input', {'class', 'required checkbox'}).get('id')
        data['subjectiveRadio'] = subjectiveRadio  # 教室大小合适
        data['subjectiveCheckbox'] = subjectiveCheckbox  # 自己需求和兴趣

        try:
            post_url = self._urls['base_saveCourseEval_url'][
                'http'] + course_id + '?s=' + s
            res = self._S.post(post_url,
                               data=data,
                               headers=self.headers,
                               timeout=5)
        except requests.Timeout:
            post_url = self._urls['base_saveCourseEval_url'][
                'https'] + course_id + '?s=' + s
            res = self._S.post(post_url, data=data, headers=self.headers)
        tmp = BeautifulSoup(res.text, 'html.parser')
        try:
            flag = tmp.find('label', attrs={'id': 'loginSuccess'})
            if flag.string == '保存成功':
                print('\033[1;45m{}评估结果:[success] \033[0m'.format(course_id))
            else:
                print('\033[1;45m{}评估结果:[fail],请手动重新评估该课 \033[0m'.format(
                    course_id))

        except AttributeError:
            print('\033[1;45m{}评估结果:[fail],尝试重新评估 \033[0m'.format(course_id))
            self.__assess_course(course_id)

    def _assess_courses(self, course_ids):
        self._logger.info('开始评估课程')
        time.sleep(2)
        for course_id in course_ids:
            self.__assess_course(course_id)
        self._logger.info('课程评估完毕')

    def _get_teacher_ids(self):
        # 通过课程评估url得到教师评估url
        teacher_assess_url = self._course_assess_url.replace(
            'course', 'teacher')
        res = self._S.get(teacher_assess_url, headers=self.headers)
        bs4obj = BeautifulSoup(res.text, 'html.parser')
        urls = [
            url.get('href') for url in bs4obj.find_all('a', {'class': 'btn'})
        ]
        teacher_ids = []
        for url in urls:
            teacher_ids.append(self._id_pattern.search(url).groupdict()['id'])
        return teacher_ids

    def __assess_teacher(self, teacher_id):
        try:
            res = self._S.get(self._urls['base_evaluateTeacher_url']['http'] +
                              teacher_id,
                              headers=self.headers,
                              timeout=5)
        except requests.Timeout:
            res = self._S.get(self._urls['base_evaluateTeacher_url']['https'] +
                              teacher_id,
                              headers=self.headers)

        bs4obj = BeautifulSoup(res.text, 'lxml')
        radios = bs4obj.find_all('input', attrs={'type': 'radio'})
        value = radios[0]['value']  # 默认全5星好评
        data = {}
        for radio in radios:
            data[radio['name']] = value
        textareas = bs4obj.find_all('textarea')
        for textarea, asses_msg in zip(textareas, self._assess_msgs[-2:]):
            # 填写主观评价内容
            item_id = textarea.get('id')
            data[item_id] = asses_msg
        data['subjectiveCheckbox'] = ''
        data['subjectiveRadio'] = ''
        post_action = bs4obj.find('form', {'id': 'regfrm'})
        try:
            post_url = self._urls['base_url']['http'] + post_action.get(
                'action')
        except requests.Timeout:
            post_url = self._urls['base_url']['https'] + post_action.get(
                'action')
        try:
            res = self._S.post(post_url,
                               data=data,
                               headers=self.headers,
                               timeout=5)
        except requests.Timeout:
            res = self._S.post(post_url, data=data, headers=self.headers)

        tmp = BeautifulSoup(res.text, 'html.parser')
        try:
            flag = tmp.find('label', attrs={'id': 'loginSuccess'})
            if flag.string == '保存成功':
                print('\033[1;45m{}评估结果:[success] \033[0m'.format(teacher_id))
                return
            else:
                print('\033[1;45m{}评估结果:[fail],请手动评估该教师 \033[0m'.format(
                    teacher_id))

        except AttributeError:
            print('\033[1;45m{}评估结果:[fail],尝试重新评估 \033[0m'.format(teacher_id))
            self.__assess_teacher(teacher_id)

    def _assess_teachers(self, teacher_ids):
        self._logger.info('开始评估教师')
        for teacher_id in teacher_ids:
            self.__assess_teacher(teacher_id)
        self._logger.info('教师评估完毕')

    def run(self):
        self.login()
        course_ids = self._get_course_ids()
        self._assess_courses(course_ids)
        teacher_ids = self._get_teacher_ids()
        self._assess_teachers(teacher_ids)