Passed
Push — master ( 3ccee6...dfd07f )
by dai
02:07
created

BaseUserClient.get_view_state_from_html()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nop 1
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 inspect
5
import requests
6
7
from school_api.client.utils import get_time_list
8
from school_api.client.api.base import BaseSchoolApi
9
from school_api.client.api.login import Login
10
from school_api.client.api.utils import get_view_state_from_html
11
from school_api.session.memorystorage import MemoryStorage
12
from school_api.utils import to_text, ObjectDict
13
from school_api.config import URL_ENDPOINT, CLASS_TIME, LOGIN_SESSION_SAVE_TIME
14
15
16
def _is_api_endpoint(obj):
17
    return isinstance(obj, BaseSchoolApi)
18
19
20
class BaseSchoolClient(object):
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...
21
22
    def __init__(self, url, **kwargs):
23
24
        class_time_list = kwargs.get('class_time_list') or CLASS_TIME
25
        time_list = get_time_list(class_time_list)
26
27
        self.school = {
28
            'url': url,
29
            'debug': kwargs.get('debug'),
30
            'name': to_text(kwargs.get('name')),
31
            'code': kwargs.get('code'),
32
            'use_ex_handle': kwargs.get('use_ex_handle', True),
33
            'exist_verify': kwargs.get('exist_verify', True),
34
            'lan_url': kwargs.get('lan_url'),
35
            'proxies': kwargs.get('proxies'),
36
            'priority_porxy': kwargs.get('priority_porxy'),
37
            'timeout': kwargs.get('timeout', 10),
38
            'login_url': kwargs.get('login_url_path', '/default2.aspx'),
39
            'url_endpoint': kwargs.get('url_endpoint') or URL_ENDPOINT,
40
            'time_list': time_list
41
        }
42
        storage = kwargs.get('session', MemoryStorage)
43
        self.session = storage(self.school['code'])
44
        self.init_login_view_state(kwargs.get('login_view_state', {}))
45
        self.school = ObjectDict(self.school)
46
47
    def init_login_view_state(self, login_view_state):
48
        """ 初始化  login_view_state"""
49
        for url_key, view_state in login_view_state.items():
50
            self.session.set('login_view:' + url_key, view_state)
51
52
53
class BaseUserClient(object):
0 ignored issues
show
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
54
    """docstring for BaseUserClient"""
55
56
    _proxy = None
57
    login = Login()
58
59
    def __new__(cls, *args):
0 ignored issues
show
Unused Code introduced by
The argument args seems to be unused.
Loading history...
60
        self = super(BaseUserClient, cls).__new__(cls)
61
        api_endpoints = inspect.getmembers(self, _is_api_endpoint)
62
        for name, api in api_endpoints:
63
            api_cls = type(api)
64
            api = api_cls(self)
65
            setattr(self, name, api)
66
        return self
67
68
    def __init__(self, school, account, password, user_type):
69
        self._http = requests.Session()
70
        self._http.headers.update({
71
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) '
72
                          'AppleWebKit/537.36 (KHTML, like Gecko) '
73
                          'Chrome/62.0.3202.89 Safari/537.36',
74
            'Content-Type': 'application/x-www-form-urlencoded',
75
        })
76
        self.account = to_text(account)
77
        self.password = password
78
        self.user_type = user_type
79
        self.school = school.school
80
        self.base_url = self.school.url
81
        self.session = school.session
82
83
        if self.school.priority_porxy:
84
            self.set_proxy()
85
86
    def _request(self, method, url_or_endpoint, **kwargs):
87
        if not url_or_endpoint.startswith(('http://', 'https://')):
88
            url = '{base}{endpoint}'.format(
89
                base=self.base_url,
90
                endpoint=url_or_endpoint
91
            )
92
        else:
93
            url = url_or_endpoint
94
95
        kwargs['timeout'] = kwargs.get('timeout', self.school.timeout)
96
        res = self._http.request(
97
            method=method,
98
            url=url,
99
            proxies=self._proxy,
100
            **kwargs
101
        )
102
        return res
103
104
    def get(self, url, **kwargs):
0 ignored issues
show
Coding Style introduced by
This method 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...
105
        return self._request(
106
            method='GET',
107
            url_or_endpoint=url,
108
            **kwargs
109
        )
110
111
    def post(self, url, **kwargs):
0 ignored issues
show
Coding Style introduced by
This method 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...
112
        return self._request(
113
            method='POST',
114
            url_or_endpoint=url,
115
            **kwargs
116
        )
117
118
    def head(self, url, **kwargs):
0 ignored issues
show
Coding Style introduced by
This method 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...
119
        return self._request(
120
            method='HEAD',
121
            url_or_endpoint=url,
122
            **kwargs
123
        )
124
125
    def set_proxy(self):
126
        """ 设置代理 """
127
        self.school.priority_porxy = True
128
        self.base_url = self.school.lan_url or self.base_url
129
        self._proxy = self.school.proxies
130
131
    def update_headers(self, headers_dict):
0 ignored issues
show
Coding Style introduced by
This method 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...
132
        self._http.headers.update(headers_dict)
133
134
    def get_view_state(self, url_suffix, **kwargs):
0 ignored issues
show
Coding Style introduced by
This method 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...
135
        res = self.get(url_suffix, allow_redirects=False, **kwargs)
136
        if res.status_code != 200:
137
            raise requests.RequestException
138
        return get_view_state_from_html(res.text)
139
140
    def get_login_session_key(self):
141
        ''' 获取缓存登录会话的key '''
142
        url = self.base_url + self.school.login_url
143
        key = '{}:{}:{}'.format('login_session', url, self.account)
144
        return key
145
146
    def get_login_view_state(self, **kwargs):
147
        ''' 获取登录的view_state '''
148
        base_key = 'login_view:' + self.base_url + self.school.login_url
149
        if not self.session.get(base_key):
150
            view_state = self.get_view_state(self.school.login_url, **kwargs)
151
            self.session.set(base_key, view_state)
152
        return self.session.get(base_key)
153
154
    def session_management(self):
155
        ''' 登录会话管理 '''
156
        login_session = None
157
        if self._get_login_session():
158
            session_expires_time = LOGIN_SESSION_SAVE_TIME \
159
                - self._get_login_session_expires_time()
160
161
            if session_expires_time < 60 * 5:
162
                # 五分钟内,不检测登录会话是否过期
163
                login_session = self
164
            elif self.login.check_session():
165
                # 登录会话检测
166
                if 60 * 5 < session_expires_time < 60 * 10:
167
                    # 登录比较频繁的,更新会话时间 (例如:部门账号操作)
168
                    self.save_login_session()
169
                login_session = self
170
            else:
171
                # 会话过期, 删除会话
172
                self._del_login_session()
173
174
        return login_session
175
176
    def save_login_session(self):
177
        ''' 保存登录会话 '''
178
        key = self.get_login_session_key()
179
        cookie = self._http.cookies.get_dict()
180
        self.session.set(key, cookie, LOGIN_SESSION_SAVE_TIME)
181
182
    def _del_login_session(self):
183
        ''' 删除登录会话 '''
184
        key = self.get_login_session_key()
185
        self.session.delete(key)
186
        self._http.cookies.clear()
187
188
    def _get_login_session(self):
189
        ''' 获取登录会话 '''
190
        key = self.get_login_session_key()
191
        cookie = self.session.get(key)
192
        if not cookie:
193
            return None
194
        url = self.base_url + self.school.login_url
195
        self.update_headers({'Referer': url})
196
        self._http.cookies.update(cookie)
197
        return True
198
199
    def _get_login_session_expires_time(self):
200
        """ 获取登录会话过期时间 """
201
        url = self.base_url + self.school.login_url
202
        key = '{}:{}:{}'.format('login_session', url, self.account)
203
        return self.session.expires_time(key)
204