Passed
Push — master ( 5fd33b...e0a73e )
by dai
01:40
created

PlaceSchedule.get_schedule()   F

Complexity

Conditions 14

Size

Total Lines 65
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 50
nop 9
dl 0
loc 65
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 -*-
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2
from __future__ import absolute_import, unicode_literals
3
4
import six.moves.urllib.parse as urlparse
5
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):
0 ignored issues
show
Coding Style introduced by
This class should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
16
    schedule_url = None
17
    payload = None
18
19
    def get_schedule(self,
0 ignored issues
show
best-practice introduced by
Too many arguments (8/5)
Loading history...
Comprehensibility introduced by
This function exceeds the maximum number of variables (17/15).
Loading history...
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
        返回一个生成器,生成器一旦报错则将退出:https://codeday.me/bug/20180125/124136.html
30
        除了解析异常其他报错均不抛出
31
        '''
32
        self.schedule_url = self.school_url['PLACE_SCHEDULE_URL'] + \
33
            urlparse.quote(self.account.encode('gb2312'))
34
35
        if not self._update_payload(**kwargs):
0 ignored issues
show
unused-code introduced by
Too many nested blocks (6/5)
Loading history...
36
            yield {'error': "获取教学场地课表失败"}
37
        else:
38
            # 遍历校区
39
            for campus in self.payload['campus_list']:
40
                if self._is_skip(campus["name"], campus_list, filter_name_list=filter_campus_list):
41
                    continue
42
                if not self._update_payload(campus, **kwargs):
43
                    continue
44
45
                # 遍历楼号
46
                for building in self.payload['building_list']:
47
                    kwargs['building'] = building
48
                    if self._is_skip(building["name"], building_list, filter_name_list=filter_building_list):
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (109/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
49
                        continue
50
                    if not self._update_payload(campus, **kwargs):
51
                        continue
52
53
                    # 遍历教室类别
54
                    for classroom_type in self.payload['classroom_type_list']:
55
                        kwargs['classroom_type'] = classroom_type
56
                        if self._is_skip(classroom_type["name"], classroom_type_list, filter_name_list=filter_classroom_type_list):
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (131/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
57
                            continue
58
                        if not self._update_payload(campus, **kwargs):
59
                            continue
60
                        # 遍历教室名称
61
                        for classroom_name in self.payload['classroom_name_list']:
62
                            kwargs['classroom_name'] = classroom_name
63
                            if self._is_skip(classroom_name["name"], classroom_name_list):
64
                                continue
65
                            try:
66
                                res = self._get_api(campus, **kwargs)
67
                            except ScheduleException:
68
                                continue
69
70
                            schedule = ScheduleParse(
71
                                res.content.decode('GB18030'),
72
                                self.time_list,
73
                                ScheduleType.CLASS
74
                            ).get_schedule_dict()
75
76
                            data = {
77
                                "campus": campus["name"],
78
                                "building": building["name"],
79
                                "classroom_type": classroom_type["name"],
80
                                "classroom_name": classroom_name["name"]
81
                            }
82
                            data.update(schedule)
83
                            yield data
84
85
    def _is_skip(self, name, name_list, filter_name_list=None):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
86
        if (name_list and name not in name_list) or (filter_name_list and name in filter_name_list):
87
            return True
88
89
    def _get_api(self, campus=None, **kwargs):
90
        """ 请求函数 """
91
        if self.payload and campus:
92
            building = kwargs.pop('building', None)
93
            classroom_type = kwargs.pop('classroom_type', None)
94
            classroom_name = kwargs.pop('classroom_name', None)
95
            data = {
96
                "Button1": "",
97
                "xq": self.payload['schedule_term'],
98
                "xn": self.payload['schedule_year'],
99
                "ddlXq": campus["value"],
100
                'ddllh': building["value"] if building else '',
101
                "ddlJslb": classroom_type["value"].encode('gb2312') if classroom_type else '',
102
                "ddlJsmc": classroom_name["value"].encode('gb2312') if classroom_name else '',
103
                "__VIEWSTATE": self.payload['view_state'],
104
            }
105
            _request = self._post
106
        else:
107
            data = ""
108
            _request = self._get
109
110
        try:
111
            res = _request(self.schedule_url, data=data, **kwargs)
112
            if res.status_code != 200:
113
                raise RequestException
114
        except RequestException:
115
            raise ScheduleException(self.code, '获取教学场地课表失败')
116
        return res
117
118
    def _update_payload(self, *args, **kwargs):
119
        # 更新提交参数 payload
120
        try:
121
            res = self._get_api(*args, **kwargs)
122
        except ScheduleException:
123
            return None
124
        self.payload = self._get_payload(res.text)
125
        return True
126
127
    @staticmethod
128
    def _get_payload(html):
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (16/15).
Loading history...
129
        pre_soup = BeautifulSoup(html, "html.parser")
130
        view_state = pre_soup.find(attrs={"name": "__VIEWSTATE"})['value']
131
        searchbox = pre_soup.find("div", {"class": "searchbox"})
132
133
        schedule_year = searchbox.find("select", {"id": "xn"}).find(
134
            "option", {"selected": "selected"}).text
135
        schedule_term = searchbox.find("select", {"id": "xq"}).find(
136
            "option", {"selected": "selected"}).text
137
138
        campus = searchbox.find(id='ddlXq').find_all('option')
139
        buildings = searchbox.find(id='ddllh').find_all('option')
140
        types = searchbox.find(id='ddlJslb').find_all('option')
141
        names = searchbox.find(id='ddlJsmc').find_all('option')
142
143
        campus_list = [{"name": v.text, "value": v['value']} for v in campus]
144
        building_list = [{"name": v.text, "value": v['value']} for v in buildings]
145
        type_list = [{"name": v.text, "value": v['value']} for v in types]
146
        name_list = [{"name": v.text, "value": v['value']} for v in names]
147
148
        payload = {
149
            'view_state': view_state,
150
            'schedule_term': schedule_term,
151
            'schedule_year': schedule_year,
152
            'campus_list': campus_list,
153
            'building_list': building_list,
154
            'classroom_type_list': type_list,
155
            'classroom_name_list': name_list,
156
        }
157
        return payload
158