PlaceSchedule.get_schedule()   F
last analyzed

Complexity

Conditions 14

Size

Total Lines 66
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 38
nop 9
dl 0
loc 66
rs 3.6
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like school_api.client.api.place_schedule.PlaceSchedule.get_schedule() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
# -*- coding: utf-8 -*-
2
from __future__ import absolute_import, unicode_literals
3
4
import six.moves.urllib.parse as urlparse
5
import time
6
from bs4 import BeautifulSoup
7
from requests import RequestException
8
9
from school_api.client.api.base import BaseSchoolApi
10
from school_api.client.api.utils.schedule_parse import ScheduleParse
11
from school_api.client.utils import ScheduleType
12
from school_api.exceptions import ScheduleException
13
14
15
class PlaceSchedule(BaseSchoolApi):
16
    schedule_url = None
17
    payload = None
18
19
    def get_schedule(self,
20
                     campus_list=None,
21
                     building_list=None,
22
                     classroom_type_list=None,
23
                     classroom_name_list=None,
24
                     filter_campus_list=None,
25
                     filter_building_list=None,
26
                     filter_classroom_type_list=None,
27
                     **kwargs):
28
        '''
29
        课表信息 获取入口
30
        生成器一旦报错则将退出:https://codeday.me/bug/20180125/124136.html
31
        除了解析异常其他报错均不抛出
32
33
        :param campus_list: 校区列表
34
        :param building_list: 楼号列表
35
        :param classroom_type_list: 教室类别列表
36
        :param classroom_name_list: 教室名称列表
37
38
        :param filter_campus_list: 排除校区列表
39
        :param filter_building_list: 排除楼号列表
40
        :param filter_classroom_type_list: 排除教室类别列表
41
        :param kwargs: requests 参数
42
        :yield: 生成器
43
        '''
44
45
        self.schedule_url = self.school_url['PLACE_SCHEDULE_URL'] + \
46
                            urlparse.quote(self.user.account.encode('gb2312'))
47
48
        if not self._update_payload(**kwargs):
49
            yield {'error': "获取教学场地课表失败"}
50
        else:
51
            # 遍历校区
52
            for campus in self.payload['campus_list']:
53
                if self._is_skip(campus["name"], campus_list, filter_name_list=filter_campus_list):
54
                    continue
55
                if not self._update_payload(campus, **kwargs):
56
                    continue
57
58
                # 遍历楼号
59
                for building in self.payload['building_list']:
60
                    kwargs['building'] = building
61
                    if self._is_skip(building["name"], building_list, filter_name_list=filter_building_list):
62
                        continue
63
                    if not self._update_payload(campus, **kwargs):
64
                        continue
65
66
                    # 遍历教室类别
67
                    for classroom_type in self.payload['classroom_type_list']:
68
                        kwargs['classroom_type'] = classroom_type
69
                        if self._is_skip(classroom_type["name"], classroom_type_list,
70
                                         filter_name_list=filter_classroom_type_list):
71
                            continue
72
                        if not self._update_payload(campus, **kwargs):
73
                            continue
74
75
                        # 遍历教室名称
76
                        for classroom_name in self.payload['classroom_name_list']:
77
                            if self._is_skip(classroom_name["name"], classroom_name_list):
78
                                continue
79
80
                            kwargs['classroom_name'] = classroom_name
81
                            # 请求接口获取课表数据
82
                            data = self._get_result(campus, **kwargs)
83
                            if data:
84
                                yield data
85
86
    def _get_result(self, campus, **kwargs):
87
        """ 处理请求结果,并返回 """
88
        try:
89
            res = self._get_api(campus, **kwargs)
90
        except ScheduleException:
91
            return
92
93
        schedule = ScheduleParse(res.text, self.time_list, ScheduleType.CLASS).get_schedule_dict()
94
95
        data = {
96
            "campus": campus["name"],
97
            "building": kwargs['building']["name"],
98
            "classroom_type": kwargs['classroom_type']["name"],
99
            "classroom_name": kwargs['classroom_name']["name"]
100
        }
101
        data.update(schedule)
102
        return data
103
104
    def _get_api(self, campus=None, **kwargs):
105
        """ 请求函数 """
106
        if self.payload and campus:
107
            building = kwargs.pop('building', None)
108
            classroom_type = kwargs.pop('classroom_type', None)
109
            classroom_name = kwargs.pop('classroom_name', None)
110
            data = {
111
                "Button1": "",
112
                "xq": self.payload['schedule_term'],
113
                "xn": self.payload['schedule_year'],
114
                "ddlXq": campus["value"],
115
                'ddllh': building["value"] if building else '',
116
                "ddlJslb": classroom_type["value"].encode('gb2312') if classroom_type else '',
117
                "ddlJsmc": classroom_name["value"].encode('gb2312') if classroom_name else '',
118
                "__VIEWSTATE": self.payload['view_state'],
119
            }
120
            _request = self._post
121
        else:
122
            data = ""
123
            _request = self._get
124
125
        try:
126
            res = _request(self.schedule_url, data=data, **kwargs)
127
        except RequestException:
128
            raise ScheduleException(self.school_code, '获取教学场地课表失败')
129
        return res
130
131
    def _update_payload(self, *args, **kwargs):
132
        # 更新提交参数 payload
133
        try:
134
            res = self._get_api(*args, **kwargs)
135
        except ScheduleException:
136
            return None
137
138
        try:
139
            self.payload = self._get_payload(res.text)
140
        except AttributeError:
141
            return None
142
        except Exception:
143
            time.sleep(2)
144
            return None
145
        return True
146
147
    @staticmethod
148
    def _get_payload(html):
149
        ''' 获取课表提交参数 '''
150
        pre_soup = BeautifulSoup(html, "html.parser")
151
        view_state = pre_soup.find(attrs={"name": "__VIEWSTATE"})['value']
152
        searchbox = pre_soup.find("div", {"class": "searchbox"})
153
154
        schedule_year = searchbox.find("select", {"id": "xn"}).find(
155
            "option", {"selected": "selected"}).text
156
        schedule_term = searchbox.find("select", {"id": "xq"}).find(
157
            "option", {"selected": "selected"}).text
158
159
        campuses = searchbox.find(id='ddlXq').find_all('option')
160
        buildings = searchbox.find(id='ddllh').find_all('option')
161
        types = searchbox.find(id='ddlJslb').find_all('option')
162
        names = searchbox.find(id='ddlJsmc').find_all('option')
163
164
        campuses = [{"name": v.text, "value": v['value']} for v in campuses]
165
        buildings = [{"name": v.text, "value": v['value']} for v in buildings]
166
        types = [{"name": v.text, "value": v['value']} for v in types]
167
        names = [{"name": v.text, "value": v['value']} for v in names]
168
169
        payload = {
170
            'view_state': view_state,
171
            'schedule_term': schedule_term,
172
            'schedule_year': schedule_year,
173
            'campus_list': campuses,
174
            'building_list': buildings,
175
            'classroom_type_list': types,
176
            'classroom_name_list': names,
177
        }
178
        return payload
179
180
    @staticmethod
181
    def _is_skip(name, name_list, filter_name_list=None):
182
        ''' 检查是否跳过 '''
183
        if (name_list and name not in name_list) or (filter_name_list and name in filter_name_list):
184
            return True
185
        return None
186