1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
from __future__ import absolute_import, unicode_literals |
3
|
|
|
|
4
|
|
|
from six.moves.urllib import parse |
5
|
|
|
|
6
|
|
|
from bs4 import BeautifulSoup |
7
|
|
|
from requests import RequestException, TooManyRedirects |
8
|
|
|
|
9
|
|
|
from school_api.client.api.base import BaseSchoolApi |
10
|
|
|
from school_api.client.api.utils import get_alert_tip, get_view_state_from_html |
11
|
|
|
from school_api.client.api.utils.schedule_parse import ScheduleParse |
12
|
|
|
from school_api.client.utils import ScheduleType, UserType |
13
|
|
|
from school_api.utils import to_text |
14
|
|
|
from school_api.exceptions import ScheduleException |
15
|
|
|
|
16
|
|
|
|
17
|
|
|
class Schedule(BaseSchoolApi): |
18
|
|
|
schedule_type = None |
19
|
|
|
schedule_year = None |
20
|
|
|
schedule_term = None |
21
|
|
|
schedule_url = None |
22
|
|
|
|
23
|
|
|
def get_schedule(self, schedule_year=None, schedule_term=None, schedule_type=None, **kwargs): |
24
|
|
|
''' 课表信息 获取入口 |
25
|
|
|
:param schedule_year: 课表学年 |
26
|
|
|
:param schedule_term: 课表学期 |
27
|
|
|
:param schedule_type: 0.个人课表 1.班级课表 |
28
|
|
|
:param kwargs: requests模块参数 |
29
|
|
|
:return: |
30
|
|
|
''' |
31
|
|
|
self.schedule_type = ScheduleType.CLASS if self.user.user_type \ |
32
|
|
|
else schedule_type or ScheduleType.PERSON |
33
|
|
|
self.schedule_year = schedule_year |
34
|
|
|
self.schedule_term = str(schedule_term) if schedule_term else schedule_term |
35
|
|
|
self.schedule_url = self.school_url["SCHEDULE_URL"][self.schedule_type] |
36
|
|
|
|
37
|
|
|
if self.user.user_type != UserType.DEPT: |
38
|
|
|
self.schedule_url += self.user.account |
39
|
|
|
data = self._get_api(**kwargs) |
40
|
|
|
else: |
41
|
|
|
self.schedule_url += parse.quote(self.user.account.encode('gb2312')) |
42
|
|
|
data = self._get_api_by_bm(**kwargs) |
43
|
|
|
if self.schedule_term and self.schedule_year and ( |
44
|
|
|
self.schedule_term != data["schedule_term"] or self.schedule_year != data["schedule_year"]): |
45
|
|
|
raise ScheduleException(self.school_code, '暂无课表信息') |
46
|
|
|
return data |
47
|
|
|
|
48
|
|
|
def _get_api(self, **kwargs): |
49
|
|
|
|
50
|
|
|
try: |
51
|
|
|
res = self._get(self.schedule_url, **kwargs) |
52
|
|
|
except TooManyRedirects: |
53
|
|
|
raise ScheduleException(self.school_code, '课表接口已关闭') |
54
|
|
|
except RequestException: |
55
|
|
|
raise ScheduleException(self.school_code, '获取课表请求参数失败') |
56
|
|
|
|
57
|
|
|
tip = get_alert_tip(res.text) |
58
|
|
|
if tip: |
59
|
|
|
raise ScheduleException(self.school_code, tip) |
60
|
|
|
|
61
|
|
|
schedule = ScheduleParse(res.text, self.time_list, self.schedule_type).get_schedule_dict() |
62
|
|
|
# 第一次请求的时候,教务系统默认返回当前学年学期课表 |
63
|
|
|
# 如果设置了学年跟学期,则获取指定学年学期的课表 |
64
|
|
|
if self.schedule_year and self.schedule_term and ( |
65
|
|
|
self.schedule_year != schedule['schedule_year'] or self.schedule_term != schedule['schedule_term']): |
66
|
|
|
|
67
|
|
|
payload = self._get_payload(res.text) |
68
|
|
|
|
69
|
|
|
try: |
70
|
|
|
res = self._post(self.schedule_url, data=payload, **kwargs) |
71
|
|
|
except RequestException: |
72
|
|
|
raise ScheduleException(self.school_code, '获取课表信息失败') |
73
|
|
|
|
74
|
|
|
schedule = ScheduleParse( |
75
|
|
|
res.text, |
76
|
|
|
self.time_list, |
77
|
|
|
self.schedule_type |
78
|
|
|
).get_schedule_dict() |
79
|
|
|
|
80
|
|
|
return schedule |
81
|
|
|
|
82
|
|
|
def _get_api_by_bm(self, class_name, **kwargs): |
83
|
|
|
''' 部门教师 查询学生班级课表 共3个请求''' |
84
|
|
|
|
85
|
|
|
# steps 1: 获取课表页面 参数信息 |
86
|
|
|
try: |
87
|
|
|
res = self._get(self.schedule_url, **kwargs) |
88
|
|
|
except RequestException: |
89
|
|
|
raise ScheduleException(self.school_code, '获取课表请求参数失败') |
90
|
|
|
|
91
|
|
|
# steps 2: 选择课表 学年学期 |
92
|
|
|
if self.schedule_year and self.schedule_term: |
93
|
|
|
payload = self._get_payload(res.text) |
94
|
|
|
try: |
95
|
|
|
res = self._post(self.schedule_url, data=payload, **kwargs) |
96
|
|
|
except RequestException: |
97
|
|
|
raise ScheduleException(self.school_code, '获取课表请求参数失败') |
98
|
|
|
|
99
|
|
|
# steps 3: 获取课表数据 |
100
|
|
|
payload = self._get_payload_by_bm(res.text, class_name) |
101
|
|
|
try: |
102
|
|
|
res = self._post(self.schedule_url, data=payload, **kwargs) |
103
|
|
|
except RequestException: |
104
|
|
|
raise ScheduleException(self.school_code, '获取课表信息失败') |
105
|
|
|
|
106
|
|
|
schedule = ScheduleParse(res.text, self.time_list, self.schedule_type).get_schedule_dict() |
107
|
|
|
return schedule |
108
|
|
|
|
109
|
|
|
def _get_payload(self, html): |
110
|
|
|
''' 获取课表post 的参数 ''' |
111
|
|
|
view_state = get_view_state_from_html(html) |
112
|
|
|
payload = { |
113
|
|
|
'__VIEWSTATE': view_state, |
114
|
|
|
['xnd', 'xn'][self.schedule_type]: self.schedule_year, |
115
|
|
|
['xqd', 'xq'][self.schedule_type]: self.schedule_term |
116
|
|
|
} |
117
|
|
|
return payload |
118
|
|
|
|
119
|
|
|
def _get_payload_by_bm(self, html, class_name): |
120
|
|
|
''' 提取页面参数用于请求课表 ''' |
121
|
|
|
pre_soup = BeautifulSoup(html, "html.parser") |
122
|
|
|
view_state = pre_soup.find(attrs={"name": "__VIEWSTATE"})['value'] |
123
|
|
|
schedule_id_list = pre_soup.find(id='kb').find_all('option') |
124
|
|
|
class_name = to_text(class_name) |
125
|
|
|
for name in schedule_id_list: |
126
|
|
|
if name.text == class_name: |
127
|
|
|
schedule_id = name['value'] |
128
|
|
|
break |
129
|
|
|
else: |
130
|
|
|
raise ScheduleException(self.school_code, '暂无该班级课表信息') |
131
|
|
|
|
132
|
|
|
# 获取班级课表 |
133
|
|
|
payload = { |
134
|
|
|
'__VIEWSTATE': view_state, |
135
|
|
|
'kb': schedule_id |
136
|
|
|
} |
137
|
|
|
return payload |
138
|
|
|
|