RuntimeError: Input type (torch.cuda.FloatTensor) and weight type (torch.FloatTensor) should be the same

Pytorch使用过程中

torchsummary.summary( )中出现了上述错误,torchsummary是应用在pytorch中的一种结构表达方式。

# 1
if __name__ == '__main__':
    model = fishnet99()
    torchsummary.summary(model.cuda(), (3, 224, 224))
# 2    
if __name__ == '__main__':
    model = fishnet99()
    torchsummary.summary(model, (3, 224, 224),device='cpu')
   1 import base64
   2 import binascii
   3 import json
   4 import os
   5 import re
   6 import time
   7 import traceback
   8 import unicodedata
   9 from urllib.parse import urljoin
  10 
  11 import requests
  12 import rsa
  13 from pyquery import PyQuery as pq
  14 from requests import exceptions
  15 
  16 
  17 def get_config(section: str, field: str):
  18     filename = os.path.join(os.path.dirname(__file__), "config.json")
  19     if not os.path.exists(filename):
  20         raise Exception("配置文件不存在")
  21     with open(filename, "r", encoding="UTF-8") as f:
  22         config = json.loads(f.read())
  23     return config.get(section, {}).get(field)
  24 
  25 
  26 BASE_URL = get_config("school", "base_url")
  27 RASPISANIE = get_config("school", "raspisanie")
  28 IGNORE_TYPE = get_config("school", "ignore_type")
  29 DETAIL_CATEGORY_TYPE = get_config("school", "detail_category_type")
  30 TIMEOUT = get_config("request", "timeout")
  31 
  32 
  33 class Client:
  34     def __init__(self, cookies={}):
  35         self.key_url = urljoin(BASE_URL, "/xtgl/login_getPublicKey.html")
  36         self.login_url = urljoin(BASE_URL, "/xtgl/login_slogin.html")
  37         self.kaptcha_url = urljoin(BASE_URL, "/kaptcha")
  38         self.headers = requests.utils.default_headers()
  39         self.headers["Referer"] = self.login_url
  40         self.headers[
  41             "User-Agent"
  42         ] = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
  43         self.headers[
  44             "Accept"
  45         ] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"
  46         self.sess = requests.Session()
  47         self.sess.keep_alive = False
  48         self.cookies = cookies
  49 
  50     def login(self, sid, password):
  51         """登录教务系统"""
  52         need_verify = False
  53         try:
  54             # 登录页
  55             req_csrf = self.sess.get(
  56                 self.login_url, headers=self.headers, timeout=TIMEOUT
  57             )
  58             if req_csrf.status_code != 200:
  59                 return {"code": 2333, "msg": "教务系统挂了"}
  60             # 获取csrf_token
  61             doc = pq(req_csrf.text)
  62             csrf_token = doc("#csrftoken").attr("value")
  63             pre_cookies = self.sess.cookies.get_dict()
  64             # 获取publicKey并加密密码
  65             req_pubkey = self.sess.get(
  66                 self.key_url, headers=self.headers, timeout=TIMEOUT
  67             ).json()
  68             modulus = req_pubkey["modulus"]
  69             exponent = req_pubkey["exponent"]
  70             if str(doc("input#yzm")) == "":
  71                 # 不需要验证码
  72                 encrypt_password = self.encrypt_password(password, modulus, exponent)
  73                 # 登录数据
  74                 login_data = {
  75                     "csrftoken": csrf_token,
  76                     "yhm": sid,
  77                     "mm": encrypt_password,
  78                 }
  79                 # 请求登录
  80                 req_login = self.sess.post(
  81                     self.login_url,
  82                     headers=self.headers,
  83                     data=login_data,
  84                     timeout=TIMEOUT,
  85                 )
  86                 doc = pq(req_login.text)
  87                 tips = doc("p#tips")
  88                 if str(tips) != "":
  89                     if "用户名或密码" in tips.text():
  90                         return {"code": 1002, "msg": "用户名或密码不正确"}
  91                     else:
  92                         return {"code": 998, "msg": tips.text()}
  93                 self.cookies = self.sess.cookies.get_dict()
  94                 return {"code": 1000, "msg": "登录成功", "data": {"cookies": self.cookies}}
  95             else:
  96                 # 需要验证码,返回相关页面验证信息给用户,TODO: 增加更多验证方式
  97                 need_verify = True
  98                 req_kaptcha = self.sess.get(
  99                     self.kaptcha_url, headers=self.headers, timeout=TIMEOUT
 100                 )
 101                 kaptcha_pic = base64.b64encode(req_kaptcha.content).decode()
 102                 return {
 103                     "code": 1001,
 104                     "msg": "获取验证码成功",
 105                     "data": {
 106                         "sid": sid,
 107                         "csrf_token": csrf_token,
 108                         "cookies": pre_cookies,
 109                         "password": password,
 110                         "modulus": modulus,
 111                         "exponent": exponent,
 112                         "kaptcha_pic": kaptcha_pic,
 113                         "timestamp": time.time(),
 114                     },
 115                 }
 116         except exceptions.Timeout:
 117             msg = "获取验证码超时" if need_verify else "登录超时"
 118             return {"code": 1003, "msg": msg}
 119         except (
 120             exceptions.RequestException,
 121             json.decoder.JSONDecodeError,
 122             AttributeError,
 123         ):
 124             traceback.print_exc()
 125             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 126         except Exception as e:
 127             traceback.print_exc()
 128             msg = "获取验证码时未记录的错误" if need_verify else "登录时未记录的错误"
 129             return {"code": 999, "msg": f"{msg}:{str(e)}"}
 130 
 131     def login_with_kaptcha(
 132         self, sid, csrf_token, cookies, password, modulus, exponent, kaptcha, **kwargs
 133     ):
 134         """需要验证码的登陆"""
 135         try:
 136             encrypt_password = self.encrypt_password(password, modulus, exponent)
 137             login_data = {
 138                 "csrftoken": csrf_token,
 139                 "yhm": sid,
 140                 "mm": encrypt_password,
 141                 "yzm": kaptcha,
 142             }
 143             req_login = self.sess.post(
 144                 self.login_url,
 145                 headers=self.headers,
 146                 cookies=cookies,
 147                 data=login_data,
 148                 timeout=TIMEOUT,
 149             )
 150             if req_login.status_code != 200:
 151                 return {"code": 2333, "msg": "教务系统挂了"}
 152             # 请求登录
 153             doc = pq(req_login.text)
 154             tips = doc("p#tips")
 155             if str(tips) != "":
 156                 if "验证码" in tips.text():
 157                     return {"code": 1004, "msg": "验证码输入错误"}
 158                 if "用户名或密码" in tips.text():
 159                     return {"code": 1002, "msg": "用户名或密码不正确"}
 160                 return {"code": 998, "msg": tips.text()}
 161             self.cookies = self.sess.cookies.get_dict()
 162             # 不同学校系统兼容差异
 163             if not self.cookies.get("route"):
 164                 route_cookies = {
 165                     "JSESSIONID": self.cookies["JSESSIONID"],
 166                     "route": cookies["route"],
 167                 }
 168                 self.cookies = route_cookies
 169             else:
 170                 return {"code": 1000, "msg": "登录成功", "data": {"cookies": self.cookies}}
 171         except exceptions.Timeout:
 172             return {"code": 1003, "msg": "登录超时"}
 173         except (
 174             exceptions.RequestException,
 175             json.decoder.JSONDecodeError,
 176             AttributeError,
 177         ):
 178             traceback.print_exc()
 179             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 180         except Exception as e:
 181             traceback.print_exc()
 182             return {"code": 999, "msg": "验证码登录时未记录的错误:" + str(e)}
 183 
 184     def get_info(self):
 185         """获取个人信息"""
 186         url = urljoin(BASE_URL, "/xsxxxggl/xsxxwh_cxCkDgxsxx.html?gnmkdm=N100801")
 187         try:
 188             req_info = self.sess.get(
 189                 url,
 190                 headers=self.headers,
 191                 cookies=self.cookies,
 192                 timeout=TIMEOUT,
 193             )
 194             if req_info.status_code != 200:
 195                 return {"code": 2333, "msg": "教务系统挂了"}
 196             doc = pq(req_info.text)
 197             if doc("h5").text() == "用户登录":
 198                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 199             info = req_info.json()
 200             result = {
 201                 "sid": info.get("xh"),
 202                 "name": info.get("xm"),
 203                 "college_name": info.get("zsjg_id", info.get("jg_id")),
 204                 "major_name": info.get("zszyh_id", info.get("zyh_id")),
 205                 "class_name": info.get("bh_id", info.get("xjztdm")),
 206                 "status": info.get("xjztdm"),
 207                 "enrollment_date": info.get("rxrq"),
 208                 "candidate_number": info.get("ksh"),
 209                 "graduation_school": info.get("byzx"),
 210                 "domicile": info.get("jg"),
 211                 "postal_code": info.get("yzbm"),
 212                 "politics_status": info.get("zzmmm"),
 213                 "nationality": info.get("mzm"),
 214                 "education": info.get("pyccdm"),
 215                 "phone_number": info.get("sjhm"),
 216                 "parents_number": info.get("gddh"),
 217                 "email": info.get("dzyx"),
 218                 "birth_day": info.get("csrq"),
 219                 "id_number": info.get("zjhm"),
 220             }
 221             return {"code": 1000, "msg": "获取个人信息成功", "data": result}
 222         except exceptions.Timeout:
 223             return {"code": 1003, "msg": "获取个人信息超时"}
 224         except (
 225             exceptions.RequestException,
 226             json.decoder.JSONDecodeError,
 227             AttributeError,
 228         ):
 229             traceback.print_exc()
 230             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 231         except Exception as e:
 232             traceback.print_exc()
 233             return {"code": 999, "msg": "获取个人信息时未记录的错误:" + str(e)}
 234 
 235     def get_grade(self, year: int, term: int = 0, use_personal_info: bool = False):
 236         """
 237         获取成绩
 238         use_personal_info: 是否使用获取个人信息接口获取成绩
 239         """
 240         url = urljoin(
 241             BASE_URL,
 242             "/cjcx/cjcx_cxDgXscj.html?doType=query&gnmkdm=N305005"
 243             if use_personal_info
 244             else "/cjcx/cjcx_cxXsgrcj.html?doType=query&gnmkdm=N305005",
 245         )
 246         temp_term = term
 247         term = term**2 * 3
 248         term = "" if term == 0 else term
 249         data = {
 250             "xnm": str(year),  # 学年数
 251             "xqm": str(term),  # 学期数,第一学期为3,第二学期为12, 整个学年为空''
 252             "_search": "false",
 253             "nd": int(time.time() * 1000),
 254             "queryModel.showCount": "100",  # 每页最多条数
 255             "queryModel.currentPage": "1",
 256             "queryModel.sortName": "",
 257             "queryModel.sortOrder": "asc",
 258             "time": "0",  # 查询次数
 259         }
 260         try:
 261             req_grade = self.sess.post(
 262                 url,
 263                 headers=self.headers,
 264                 data=data,
 265                 cookies=self.cookies,
 266                 timeout=TIMEOUT,
 267             )
 268             if req_grade.status_code != 200:
 269                 return {"code": 2333, "msg": "教务系统挂了"}
 270             doc = pq(req_grade.text)
 271             if doc("h5").text() == "用户登录":
 272                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 273             grade = req_grade.json()
 274             grade_items = grade.get("items")
 275             if not grade_items:
 276                 return {"code": 1005, "msg": "获取内容为空"}
 277             result = {
 278                 "sid": grade_items[0]["xh"],
 279                 "name": grade_items[0]["xm"],
 280                 "year": year,
 281                 "term": temp_term,
 282                 "count": len(grade_items),
 283                 "courses": [
 284                     {
 285                         "course_id": i.get("kch_id"),
 286                         "title": i.get("kcmc"),
 287                         "teacher": i.get("jsxm"),
 288                         "class_name": i.get("jxbmc"),
 289                         "credit": self.align_floats(i.get("xf")),
 290                         "category": i.get("kclbmc"),
 291                         "nature": i.get("kcxzmc"),
 292                         "grade": self.parse_int(i.get("cj")),
 293                         "grade_point": self.align_floats(i.get("jd")),
 294                         "grade_nature": i.get("ksxz"),
 295                         "start_college": i.get("kkbmmc"),
 296                         "mark": i.get("kcbj"),
 297                     }
 298                     for i in grade_items
 299                 ],
 300             }
 301             return {"code": 1000, "msg": "获取成绩成功", "data": result}
 302         except exceptions.Timeout:
 303             return {"code": 1003, "msg": "获取成绩超时"}
 304         except (
 305             exceptions.RequestException,
 306             json.decoder.JSONDecodeError,
 307             AttributeError,
 308         ):
 309             traceback.print_exc()
 310             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 311         except Exception as e:
 312             traceback.print_exc()
 313             return {"code": 999, "msg": "获取成绩时未记录的错误:" + str(e)}
 314 
 315     def get_schedule(self, year: int, term: int):
 316         """获取课程表信息"""
 317         url = urljoin(BASE_URL, "/kbcx/xskbcx_cxXsKb.html?gnmkdm=N2151")
 318         temp_term = term
 319         term = term**2 * 3
 320         data = {"xnm": str(year), "xqm": str(term)}
 321         try:
 322             req_schedule = self.sess.post(
 323                 url,
 324                 headers=self.headers,
 325                 data=data,
 326                 cookies=self.cookies,
 327                 timeout=TIMEOUT,
 328             )
 329             if req_schedule.status_code != 200:
 330                 return {"code": 2333, "msg": "教务系统挂了"}
 331             doc = pq(req_schedule.text)
 332             if doc("h5").text() == "用户登录":
 333                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 334             schedule = req_schedule.json()
 335             if not schedule.get("kbList"):
 336                 return {"code": 1005, "msg": "获取内容为空"}
 337             result = {
 338                 "sid": schedule["xsxx"].get("XH"),
 339                 "name": schedule["xsxx"].get("XM"),
 340                 "year": year,
 341                 "term": temp_term,
 342                 "count": len(schedule["kbList"]),
 343                 "courses": [
 344                     {
 345                         "course_id": i.get("kch_id"),
 346                         "title": i.get("kcmc"),
 347                         "teacher": i.get("xm"),
 348                         "class_name": i.get("jxbmc"),
 349                         "credit": self.align_floats(i.get("xf")),
 350                         "weekday": self.parse_int(i.get("xqj")),
 351                         "time": self.display_course_time(i.get("jc")),
 352                         "sessions": i.get("jc"),
 353                         "list_sessions": self.list_sessions(i.get("jc")),
 354                         "weeks": i.get("zcd"),
 355                         "list_weeks": self.list_weeks(i.get("zcd")),
 356                         "evaluation_mode": i.get("khfsmc"),
 357                         "campus": i.get("xqmc"),
 358                         "place": i.get("cdmc"),
 359                         "hours_composition": i.get("kcxszc"),
 360                         "weekly_hours": self.parse_int(i.get("zhxs")),
 361                         "total_hours": self.parse_int(i.get("zxs")),
 362                     }
 363                     for i in schedule["kbList"]
 364                 ],
 365                 "extra_courses": [i.get("qtkcgs") for i in schedule.get("sjkList")],
 366             }
 367             result = self.split_merge_display(result)
 368             return {"code": 1000, "msg": "获取课表成功", "data": result}
 369         except exceptions.Timeout:
 370             return {"code": 1003, "msg": "获取课表超时"}
 371         except (
 372             exceptions.RequestException,
 373             json.decoder.JSONDecodeError,
 374             AttributeError,
 375         ):
 376             traceback.print_exc()
 377             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 378         except Exception as e:
 379             traceback.print_exc()
 380             return {"code": 999, "msg": "获取课表时未记录的错误:" + str(e)}
 381 
 382     def get_academia(self):
 383         """获取学业生涯情况"""
 384         url_main = urljoin(
 385             BASE_URL,
 386             "/xsxy/xsxyqk_cxXsxyqkIndex.html?gnmkdm=N105515&layout=default",
 387         )
 388         url_info = urljoin(
 389             BASE_URL, "/xsxy/xsxyqk_cxJxzxjhxfyqKcxx.html?gnmkdm=N105515"
 390         )
 391         try:
 392             req_main = self.sess.get(
 393                 url_main,
 394                 headers=self.headers,
 395                 cookies=self.cookies,
 396                 timeout=TIMEOUT,
 397                 stream=True,
 398             )
 399             if req_main.status_code != 200:
 400                 return {"code": 2333, "msg": "教务系统挂了"}
 401             doc_main = pq(req_main.text)
 402             if doc_main("h5").text() == "用户登录":
 403                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 404             if str(doc_main("div.alert-danger")) != "":
 405                 return {"code": 998, "msg": doc_main("div.alert-danger").text()}
 406             sid = doc_main("form#form input#xh_id").attr("value")
 407             display_statistics = (
 408                 doc_main("div#alertBox").text().replace(" ", "").replace("\n", "")
 409             )
 410             sid = doc_main("input#xh_id").attr("value")
 411             statistics = self.get_academia_statistics(display_statistics)
 412             type_statistics = self.get_academia_type_statistics(req_main.text)
 413             details = {}
 414             for type in type_statistics.keys():
 415                 details[type] = self.sess.post(
 416                     url_info,
 417                     headers=self.headers,
 418                     data={"xfyqjd_id": type_statistics[type]["id"]},
 419                     cookies=self.cookies,
 420                     timeout=TIMEOUT,
 421                     stream=True,
 422                 ).json()
 423             result = {
 424                 "sid": sid,
 425                 "statistics": statistics,
 426                 "details": [
 427                     {
 428                         "type": type,
 429                         "credits": type_statistics[type]["credits"],
 430                         "courses": [
 431                             {
 432                                 "course_id": i.get("KCH"),
 433                                 "title": i.get("KCMC"),
 434                                 "situation": self.parse_int(i.get("XDZT")),
 435                                 "display_term": self.get_display_term(
 436                                     sid, i.get("JYXDXNM"), i.get("JYXDXQMC")
 437                                 ),
 438                                 "credit": self.align_floats(i.get("XF")),
 439                                 "category": self.get_course_category(type, i),
 440                                 "nature": i.get("KCXZMC"),
 441                                 "max_grade": self.parse_int(i.get("MAXCJ")),
 442                                 "grade_point": self.align_floats(i.get("JD")),
 443                             }
 444                             for i in details[type]
 445                         ],
 446                     }
 447                     for type in type_statistics.keys()
 448                     if len(details[type]) > 0
 449                 ],
 450             }
 451             return {"code": 1000, "msg": "获取学业情况成功", "data": result}
 452         except exceptions.Timeout:
 453             return {"code": 1003, "msg": "获取学业情况超时"}
 454         except (
 455             exceptions.RequestException,
 456             json.decoder.JSONDecodeError,
 457             AttributeError,
 458         ):
 459             traceback.print_exc()
 460             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 461         except Exception as e:
 462             traceback.print_exc()
 463             return {"code": 999, "msg": "获取学业情况时未记录的错误:" + str(e)}
 464 
 465     def get_academia_pdf(self):
 466         """获取学业生涯(学生成绩总表)pdf"""
 467         url_view = urljoin(BASE_URL, "/bysxxcx/xscjzbdy_dyXscjzbView.html")
 468         url_window = urljoin(BASE_URL, "/bysxxcx/xscjzbdy_dyCjdyszxView.html")
 469         url_policy = urljoin(BASE_URL, "/xtgl/bysxxcx/xscjzbdy_cxXsCount.html")
 470         url_filetype = urljoin(BASE_URL, "/bysxxcx/xscjzbdy_cxGswjlx.html")
 471         url_common = urljoin(BASE_URL, "/common/common_cxJwxtxx.html")
 472         url_file = urljoin(BASE_URL, "/bysxxcx/xscjzbdy_dyList.html")
 473         url_progress = urljoin(BASE_URL, "/xtgl/progress_cxProgressStatus.html")
 474         data = {
 475             "gsdygx": "10628-zw-mrgs",
 476             "ids": "",
 477             "bdykcxzDms": "",
 478             "cytjkcxzDms": "",
 479             "cytjkclbDms": "",
 480             "cytjkcgsDms": "",
 481             "bjgbdykcxzDms": "",
 482             "bjgbdyxxkcxzDms": "",
 483             "djksxmDms": "",
 484             "cjbzmcDms": "",
 485             "cjdySzxs": "",
 486             "wjlx": "pdf",
 487         }
 488 
 489         try:
 490             data_view = {"time": str(round(time.time() * 1000)), "gnmkdm": "N558020"}
 491             data_params = data_view
 492             del data_params["time"]
 493             # View接口
 494             req_view = self.sess.post(
 495                 url_view,
 496                 headers=self.headers,
 497                 data=data_view,
 498                 params=data_view,
 499                 cookies=self.cookies,
 500                 timeout=TIMEOUT,
 501             )
 502             if req_view.status_code != 200:
 503                 return {"code": 2333, "msg": "教务系统挂了"}
 504             doc = pq(req_view.text)
 505             if doc("h5").text() == "用户登录":
 506                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 507             # Window接口
 508             data_window = {"xh": ""}
 509             self.sess.post(
 510                 url_window,
 511                 headers=self.headers,
 512                 data=data_window,
 513                 params=data_params,
 514                 cookies=self.cookies,
 515                 timeout=TIMEOUT,
 516             )
 517             # 许可接口
 518             data_policy = data
 519             del data_policy["wjlx"]
 520             self.sess.post(
 521                 url_policy,
 522                 headers=self.headers,
 523                 data=data_policy,
 524                 params=data_params,
 525                 cookies=self.cookies,
 526                 timeout=TIMEOUT,
 527             )
 528             # 文件类型接口
 529             data_filetype = data_policy
 530             self.sess.post(
 531                 url_filetype,
 532                 headers=self.headers,
 533                 data=data_filetype,
 534                 params=data_params,
 535                 cookies=self.cookies,
 536                 timeout=TIMEOUT,
 537             )
 538             # Common接口
 539             self.sess.post(
 540                 url_common,
 541                 headers=self.headers,
 542                 data=data_params,
 543                 params=data_params,
 544                 cookies=self.cookies,
 545                 timeout=TIMEOUT,
 546             )
 547             # 获取PDF文件URL
 548             req_file = self.sess.post(
 549                 url_file,
 550                 headers=self.headers,
 551                 data=data,
 552                 params=data_params,
 553                 cookies=self.cookies,
 554                 timeout=TIMEOUT,
 555             )
 556             doc = pq(req_file.text)
 557             if "错误" in doc("title").text():
 558                 error = doc("p.error_title").text()
 559                 return {"code": 998, "msg": error}
 560             # 进度接口
 561             data_progress = {
 562                 "key": "score_print_processed",
 563                 "gnmkdm": "N558020",
 564             }
 565             self.sess.post(
 566                 url_progress,
 567                 headers=self.headers,
 568                 data=data_progress,
 569                 params=data_progress,
 570                 cookies=self.cookies,
 571                 timeout=TIMEOUT,
 572             )
 573             # 生成PDF文件URL
 574             pdf = (
 575                 req_file.text.replace("#成功", "")
 576                 .replace('"', "")
 577                 .replace("/", "\\")
 578                 .replace("\\\\", "/")
 579             )
 580             # 下载PDF文件
 581             req_pdf = self.sess.get(
 582                 urljoin(BASE_URL, pdf),
 583                 headers=self.headers,
 584                 cookies=self.cookies,
 585                 timeout=TIMEOUT + 2,
 586             )
 587             result = req_pdf.content  # 二进制内容
 588             return {"code": 1000, "msg": "获取学生成绩总表pdf成功", "data": result}
 589         except exceptions.Timeout:
 590             return {"code": 1003, "msg": "获取成绩总表pdf超时"}
 591         except (
 592             exceptions.RequestException,
 593             json.decoder.JSONDecodeError,
 594             AttributeError,
 595         ):
 596             traceback.print_exc()
 597             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 598         except Exception as e:
 599             traceback.print_exc()
 600             return {"code": 999, "msg": "获取成绩总表pdf时未记录的错误:" + str(e)}
 601 
 602     def get_schedule_pdf(self, year: int, term: int, name: str = "导出"):
 603         """获取课表pdf"""
 604         url_policy = urljoin(BASE_URL, "/kbdy/bjkbdy_cxXnxqsfkz.html")
 605         url_file = urljoin(BASE_URL, "/kbcx/xskbcx_cxXsShcPdf.html")
 606         origin_term = term
 607         term = term**2 * 3
 608         data = {
 609             "xm": name,
 610             "xnm": str(year),
 611             "xqm": str(term),
 612             "xnmc": f"{year}-{year+1}",
 613             "xqmmc": str(origin_term),
 614             "jgmc": "undefined",
 615             "xxdm": "",
 616             "xszd.sj": "true",
 617             "xszd.cd": "true",
 618             "xszd.js": "true",
 619             "xszd.jszc": "false",
 620             "xszd.jxb": "true",
 621             "xszd.xkbz": "true",
 622             "xszd.kcxszc": "true",
 623             "xszd.zhxs": "true",
 624             "xszd.zxs": "true",
 625             "xszd.khfs": "true",
 626             "xszd.xf": "true",
 627             "xszd.skfsmc": "false",
 628             "kzlx": "dy",
 629         }
 630 
 631         try:
 632             # 许可接口
 633             pilicy_params = {"gnmkdm": "N2151"}
 634             req_policy = self.sess.post(
 635                 url_policy,
 636                 headers=self.headers,
 637                 data=data,
 638                 params=pilicy_params,
 639                 cookies=self.cookies,
 640                 timeout=TIMEOUT,
 641             )
 642             if req_policy.status_code != 200:
 643                 return {"code": 2333, "msg": "教务系统挂了"}
 644             doc = pq(req_policy.text)
 645             if doc("h5").text() == "用户登录":
 646                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 647             # 获取PDF文件URL
 648             file_params = {"doType": "table"}
 649             req_file = self.sess.post(
 650                 url_file,
 651                 headers=self.headers,
 652                 data=data,
 653                 params=file_params,
 654                 cookies=self.cookies,
 655                 timeout=TIMEOUT,
 656             )
 657             doc = pq(req_file.text)
 658             if "错误" in doc("title").text():
 659                 error = doc("p.error_title").text()
 660                 return {"code": 998, "msg": error}
 661             result = req_file.content  # 二进制内容
 662             return {"code": 1000, "msg": "获取课程表pdf成功", "data": result}
 663         except exceptions.Timeout:
 664             return {"code": 1003, "msg": "获取课程表pdf超时"}
 665         except (
 666             exceptions.RequestException,
 667             json.decoder.JSONDecodeError,
 668             AttributeError,
 669         ):
 670             traceback.print_exc()
 671             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 672         except Exception as e:
 673             traceback.print_exc()
 674             return {"code": 999, "msg": "获取课程表pdf时未记录的错误:" + str(e)}
 675 
 676     def get_notifications(self):
 677         """获取通知消息"""
 678         url = urljoin(BASE_URL, "/xtgl/index_cxDbsy.html?doType=query")
 679         data = {
 680             "sfyy": "0",  # 是否已阅,未阅未1,已阅为2
 681             "flag": "1",
 682             "_search": "false",
 683             "nd": int(time.time() * 1000),
 684             "queryModel.showCount": "1000",  # 最多条数
 685             "queryModel.currentPage": "1",  # 当前页数
 686             "queryModel.sortName": "cjsj",
 687             "queryModel.sortOrder": "desc",  # 时间倒序, asc正序
 688             "time": "0",
 689         }
 690         try:
 691             req_notification = self.sess.post(
 692                 url,
 693                 headers=self.headers,
 694                 data=data,
 695                 cookies=self.cookies,
 696                 timeout=TIMEOUT,
 697             )
 698             if req_notification.status_code != 200:
 699                 return {"code": 2333, "msg": "教务系统挂了"}
 700             doc = pq(req_notification.text)
 701             if doc("h5").text() == "用户登录" or "错误" in doc("title").text():
 702                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 703             notifications = req_notification.json()
 704             result = [
 705                 {**self.split_notifications(i), "create_time": i.get("cjsj")}
 706                 for i in notifications.get("items")
 707             ]
 708             return {"code": 1000, "msg": "获取消息成功", "data": result}
 709         except exceptions.Timeout:
 710             return {"code": 1003, "msg": "获取消息超时"}
 711         except (
 712             exceptions.RequestException,
 713             json.decoder.JSONDecodeError,
 714             AttributeError,
 715         ):
 716             traceback.print_exc()
 717             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 718         except Exception as e:
 719             traceback.print_exc()
 720             return {"code": 999, "msg": "获取消息时未记录的错误:" + str(e)}
 721 
 722     def get_selected_courses(self, year: int, term: int):
 723         """获取已选课程信息"""
 724         try:
 725             url = urljoin(
 726                 BASE_URL, "/xsxk/zzxkyzb_cxZzxkYzbChoosedDisplay.html?gnmkdm=N253512"
 727             )
 728             temp_term = term
 729             term = term**2 * 3
 730             data = {"xkxnm": str(year), "xkxqm": str(term)}
 731             req_selected = self.sess.post(
 732                 url,
 733                 data=data,
 734                 headers=self.headers,
 735                 cookies=self.cookies,
 736                 timeout=TIMEOUT,
 737             )
 738             if req_selected.status_code != 200:
 739                 return {"code": 2333, "msg": "教务系统挂了"}
 740             doc = pq(req_selected.text)
 741             if doc("h5").text() == "用户登录":
 742                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 743             selected = req_selected.json()
 744             result = {
 745                 "year": year,
 746                 "term": temp_term,
 747                 "count": len(selected),
 748                 "courses": [
 749                     {
 750                         "course_id": i.get("kch"),
 751                         "class_id": i.get("jxb_id"),
 752                         "do_id": i.get("do_jxb_id"),
 753                         "title": i.get("kcmc"),
 754                         "teacher_id": (re.findall(r"(.*?\d+)/", i.get("jsxx")))[0],
 755                         "teacher": (re.findall(r"/(.*?)/", i.get("jsxx")))[0],
 756                         "credit": float(i.get("xf", 0)),
 757                         "category": i.get("kklxmc"),
 758                         "capacity": int(i.get("jxbrs", 0)),
 759                         "selected_number": int(i.get("yxzrs", 0)),
 760                         "place": self.get_place(i.get("jxdd")),
 761                         "time": self.get_course_time(i.get("sksj")),
 762                         "optional": int(i.get("zixf", 0)),
 763                         "waiting": i.get("sxbj"),
 764                     }
 765                     for i in selected
 766                 ],
 767             }
 768             return {"code": 1000, "msg": "获取已选课程成功", "data": result}
 769         except exceptions.Timeout:
 770             return {"code": 1003, "msg": "获取已选课程超时"}
 771         except (
 772             exceptions.RequestException,
 773             json.decoder.JSONDecodeError,
 774             AttributeError,
 775         ):
 776             traceback.print_exc()
 777             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 778         except Exception as e:
 779             traceback.print_exc()
 780             return {"code": 999, "msg": f"获取已选课程时未记录的错误:{str(e)}"}
 781 
 782     def get_block_courses(self, year: int, term: int, block: int):
 783         """获取板块课选课列表"""
 784         # TODO: 优化代码
 785         try:
 786             # 获取head_data
 787             url_head = urljoin(
 788                 BASE_URL,
 789                 "/xsxk/zzxkyzb_cxZzxkYzbIndex.html?gnmkdm=N253512&layout=default",
 790             )
 791             req_head_data = self.sess.get(
 792                 url_head, headers=self.headers, cookies=self.cookies, timeout=TIMEOUT
 793             )
 794             if req_head_data.status_code != 200:
 795                 return {"code": 2333, "msg": "教务系统挂了"}
 796             doc = pq(req_head_data.text)
 797             if doc("h5").text() == "用户登录":
 798                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 799             if str(doc("div.nodata")) != "":
 800                 return {"code": 998, "msg": doc("div.nodata").text()}
 801             got_credit_list = [i for i in doc("font[color='red']").items()]
 802             if len(got_credit_list) == 0:
 803                 return {"code": 1005, "msg": "板块课内容为空"}
 804             head_data = {"got_credit": got_credit_list[2].string}
 805 
 806             kklxdm_list = []
 807             xkkz_id_list = []
 808             for tab_content in doc("a[role='tab']").items():
 809                 onclick_content = tab_content.attr("onclick")
 810                 r = re.findall(r"'(.*?)'", str(onclick_content))
 811                 kklxdm_list.append(r[0].strip())
 812                 xkkz_id_list.append(r[1].strip())
 813             head_data["bkk1_kklxdm"] = kklxdm_list[0]
 814             head_data["bkk2_kklxdm"] = kklxdm_list[1]
 815             head_data["bkk3_kklxdm"] = kklxdm_list[2]
 816             head_data["bkk1_xkkz_id"] = xkkz_id_list[0]
 817             head_data["bkk2_xkkz_id"] = xkkz_id_list[1]
 818             head_data["bkk3_xkkz_id"] = xkkz_id_list[2]
 819 
 820             for head_data_content in doc("input[type='hidden']"):
 821                 name = head_data_content.attr("name")
 822                 value = head_data_content.attr("value")
 823                 head_data[str(name)] = str(value)
 824 
 825             url_display = urljoin(
 826                 BASE_URL, "/xsxk/zzxkyzb_cxZzxkYzbDisplay.html?gnmkdm=N253512"
 827             )
 828             display_req_data = {
 829                 "xkkz_id": head_data[f"bkk{block}_xkkz_id"],
 830                 "xszxzt": "1",
 831                 "kspage": "0",
 832             }
 833             req_display_data = self.sess.post(
 834                 url_display,
 835                 headers=self.headers,
 836                 data=display_req_data,
 837                 cookies=self.cookies,
 838                 timeout=TIMEOUT,
 839             )
 840             doc_display = pq(req_display_data.text)
 841             display_data = {}
 842             for display_data_content in doc_display("input[type='hidden']").items():
 843                 name = display_data_content.get("name")
 844                 value = display_data_content.get("value")
 845                 display_data[str(name)] = str(value)
 846             head_data.update(display_data)
 847 
 848             # 获取课程列表
 849             url_kch = urljoin(
 850                 BASE_URL, "/xsxk/zzxkyzb_cxZzxkYzbPartDisplay.html?gnmkdm=N253512"
 851             )
 852             url_bkk = urljoin(
 853                 BASE_URL, "/xsxk/zzxkyzb_cxJxbWithKchZzxkYzb.html?gnmkdm=N253512"
 854             )
 855             term = term**2 * 3
 856             kch_data = {
 857                 "bklx_id": head_data["bklx_id"],
 858                 "xqh_id": head_data["xqh_id"],
 859                 "zyfx_id": head_data["zyfx_id"],
 860                 "njdm_id": head_data["njdm_id"],
 861                 "bh_id": head_data["bh_id"],
 862                 "xbm": head_data["xbm"],
 863                 "xslbdm": head_data["xslbdm"],
 864                 "ccdm": head_data["ccdm"],
 865                 "xsbj": head_data["xsbj"],
 866                 "xkxnm": str(year),
 867                 "xkxqm": str(term),
 868                 "kklxdm": head_data[f"bkk{block}_kklxdm"],
 869                 "kkbk": head_data["kkbk"],
 870                 "rwlx": head_data["rwlx"],
 871                 "kspage": "1",
 872                 "jspage": "10",
 873             }
 874             kch_res = self.sess.post(
 875                 url_kch,
 876                 headers=self.headers,
 877                 data=kch_data,
 878                 cookies=self.cookies,
 879                 timeout=TIMEOUT,
 880             )
 881             jkch_res = kch_res.json()
 882             bkk_data = {
 883                 "bklx_id": head_data["bklx_id"],
 884                 "xkxnm": str(year),
 885                 "xkxqm": str(term),
 886                 "xkkz_id": head_data[f"bkk{block}_xkkz_id"],
 887                 "xqh_id": head_data["xqh_id"],
 888                 "zyfx_id": head_data["zyfx_id"],
 889                 "njdm_id": head_data["njdm_id"],
 890                 "bh_id": head_data["bh_id"],
 891                 "xbm": head_data["xbm"],
 892                 "xslbdm": head_data["xslbdm"],
 893                 "ccdm": head_data["ccdm"],
 894                 "xsbj": head_data["xsbj"],
 895                 "kklxdm": head_data[f"bkk{block}_kklxdm"],
 896                 "kch_id": jkch_res["tmpList"][0]["kch_id"],
 897                 "kkbk": head_data["kkbk"],
 898                 "rwlx": head_data["rwlx"],
 899                 "zyh_id": head_data["zyh_id"],
 900             }
 901             bkk_res = self.sess.post(
 902                 url_bkk,
 903                 headers=self.headers,
 904                 data=bkk_data,
 905                 cookies=self.cookies,
 906                 timeout=TIMEOUT,
 907             )
 908             jbkk_res = bkk_res.json()
 909             if block != 3 and (len(jkch_res["tmpList"]) != len(jbkk_res)):
 910                 return {"code": 999, "msg": "板块课编号及长度错误"}
 911             temp_list = jkch_res["tmpList"]
 912             block_list = jbkk_res
 913             for i in range(len(temp_list)):
 914                 temp_list[i].update(block_list[i])
 915 
 916             result = {
 917                 "count": len(temp_list),
 918                 "courses": [
 919                     {
 920                         "course_id": j["kch_id"],
 921                         "class_id": j.get("jxb_id"),
 922                         "do_id": j.get("do_jxb_id"),
 923                         "title": j.get("kcmc"),
 924                         "teacher_id": (re.findall(r"(.*?\d+)/", j.get("jsxx")))[0],
 925                         "teacher": (re.findall(r"/(.*?)/", j.get("jsxx")))[0],
 926                         "credit": float(j.get("xf"), 0),
 927                         "kklxdm": head_data[f"bkk{block}_kklxdm"],
 928                         "capacity": int(i.get("jxbrl", 0)),
 929                         "selected_number": int(i.get("yxzrs", 0)),
 930                         "place": self.get_place(i.get("jxdd")),
 931                         "time": self.get_course_time(i.get("sksj")),
 932                     }
 933                     for j in temp_list
 934                 ],
 935             }
 936             return {"code": 1000, "msg": "获取板块课信息成功", "data": result}
 937         except exceptions.Timeout:
 938             return {"code": 1003, "msg": "获取板块课信息超时"}
 939         except (
 940             exceptions.RequestException,
 941             json.decoder.JSONDecodeError,
 942             AttributeError,
 943         ):
 944             traceback.print_exc()
 945             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
 946         except Exception as e:
 947             traceback.print_exc()
 948             return {"code": 999, "msg": f"获取板块课信息时未记录的错误:{str(e)}"}
 949 
 950     def select_course(
 951         self,
 952         sid: str,
 953         course_id: str,
 954         do_id: str,
 955         kklxdm: str,
 956         year: int,
 957         term: int,
 958     ):
 959         """选课"""
 960         try:
 961             url_select = urljoin(
 962                 BASE_URL, "/xsxk/zzxkyzb_xkBcZyZzxkYzb.html?gnmkdm=N253512"
 963             )
 964             term = term**2 * 3
 965             select_data = {
 966                 "jxb_ids": do_id,
 967                 "kch_id": course_id,
 968                 # 'rwlx': '3',
 969                 # 'rlkz': '0',
 970                 # 'rlzlkz': '1',
 971                 # 'sxbj': '1',
 972                 # 'xxkbj': '0',
 973                 # 'cxbj': '0',
 974                 "qz": "0",
 975                 # 'xkkz_id': '9B247F4EFD6291B9E055000000000001',
 976                 "xkxnm": str(year),
 977                 "xkxqm": str(term),
 978                 "njdm_id": str(sid[0:2]),
 979                 "zyh_id": str(sid[2:6]),
 980                 "kklxdm": str(kklxdm),
 981                 # 'xklc': '1',
 982             }
 983             req_select = self.sess.post(
 984                 url_select,
 985                 headers=self.headers,
 986                 data=select_data,
 987                 cookies=self.cookies,
 988                 timeout=TIMEOUT,
 989             )
 990             if req_select.status_code != 200:
 991                 return {"code": 2333, "msg": "教务系统挂了"}
 992             doc = pq(req_select.text)
 993             if doc("h5").text() == "用户登录":
 994                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
 995             result = req_select.json()
 996             return {"code": 1000, "msg": "选课成功", "data": result}
 997         except exceptions.Timeout:
 998             return {"code": 1003, "msg": "选课超时"}
 999         except (
1000             exceptions.RequestException,
1001             json.decoder.JSONDecodeError,
1002             AttributeError,
1003         ):
1004             traceback.print_exc()
1005             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
1006         except Exception as e:
1007             traceback.print_exc()
1008             return {"code": 999, "msg": f"选课时未记录的错误:{str(e)}"}
1009 
1010     def cancel_course(self, do_id: str, course_id: str, year: int, term: int):
1011         """取消选课"""
1012         try:
1013             url_cancel = urljoin(
1014                 BASE_URL, "/xsxk/zzxkyzb_tuikBcZzxkYzb.html?gnmkdm=N253512"
1015             )
1016             term = term**2 * 3
1017             cancel_data = {
1018                 "jxb_ids": do_id,
1019                 "kch_id": course_id,
1020                 "xkxnm": str(year),
1021                 "xkxqm": str(term),
1022             }
1023             req_cancel = self.sess.post(
1024                 url_cancel,
1025                 headers=self.headers,
1026                 data=cancel_data,
1027                 cookies=self.cookies,
1028                 timeout=TIMEOUT,
1029             )
1030             if req_cancel.status_code != 200:
1031                 return {"code": 2333, "msg": "教务系统挂了"}
1032             doc = pq(req_cancel.text)
1033             if doc("h5").text() == "用户登录":
1034                 return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
1035             result = {"status": re.findall(r"(\d+)", req_cancel.text)[0]}
1036             return {"code": 1000, "msg": "退课成功", "data": result}
1037         except exceptions.Timeout:
1038             return {"code": 1003, "msg": "选课超时"}
1039         except (
1040             exceptions.RequestException,
1041             json.decoder.JSONDecodeError,
1042             AttributeError,
1043         ):
1044             traceback.print_exc()
1045             return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"}
1046         except Exception as e:
1047             traceback.print_exc()
1048             return {"code": 999, "msg": f"选课时未记录的错误:{str(e)}"}
1049 
1050     # ============= utils =================
1051 
1052     def get_gpa(self):
1053         """获取GPA"""
1054         url = urljoin(
1055             BASE_URL,
1056             "/xsxy/xsxyqk_cxXsxyqkIndex.html?gnmkdm=N105515&layout=default",
1057         )
1058         req_gpa = self.sess.get(
1059             url,
1060             headers=self.headers,
1061             cookies=self.cookies,
1062             timeout=TIMEOUT,
1063         )
1064         doc = pq(req_gpa.text)
1065         if doc("h5").text() == "用户登录":
1066             return {"code": 1006, "msg": "未登录或已过期,请重新登录"}
1067         allc_str = [allc.text() for allc in doc("font[size='2px']").items()]
1068         try:
1069             gpa = float(allc_str[2])
1070             return gpa
1071         except Exception:
1072             return "init"
1073 
1074     def get_course_category(self, type, item):
1075         """根据课程号获取类别"""
1076         if type not in DETAIL_CATEGORY_TYPE:
1077             return item.get("KCLBMC")
1078         if not item.get("KCH"):
1079             return None
1080         url = urljoin(BASE_URL, f"/jxjhgl/common_cxKcJbxx.html?id={item['KCH']}")
1081         req_category = self.sess.get(
1082             url,
1083             headers=self.headers,
1084             cookies=self.cookies,
1085             timeout=TIMEOUT,
1086         )
1087         doc = pq(req_category.text)
1088         ths = doc("th")
1089         try:
1090             data_list = [(th.text).strip() for th in ths]
1091             return data_list[6]
1092         except:
1093             return None
1094 
1095     @classmethod
1096     def encrypt_password(cls, pwd, n, e):
1097         """对密码base64编码"""
1098         message = str(pwd).encode()
1099         rsa_n = binascii.b2a_hex(binascii.a2b_base64(n))
1100         rsa_e = binascii.b2a_hex(binascii.a2b_base64(e))
1101         key = rsa.PublicKey(int(rsa_n, 16), int(rsa_e, 16))
1102         encropy_pwd = rsa.encrypt(message, key)
1103         result = binascii.b2a_base64(encropy_pwd)
1104         return result
1105 
1106     @classmethod
1107     def parse_int(cls, digits):
1108         if not digits:
1109             return None
1110         if not digits.isdigit():
1111             return digits
1112         return int(digits)
1113 
1114     @classmethod
1115     def align_floats(cls, floats):
1116         if not floats:
1117             return None
1118         if floats == "":
1119             return "0.0"
1120         return format(float(floats), ".1f")
1121 
1122     @classmethod
1123     def display_course_time(cls, sessions):
1124         if not sessions:
1125             return None
1126         args = re.findall(r"(\d+)", sessions)
1127         start_time = RASPISANIE[int(args[0]) + 1][0]
1128         end_time = RASPISANIE[int(args[0]) + 1][1]
1129         return f"{start_time}~{end_time}"
1130 
1131     @classmethod
1132     def list_sessions(cls, sessions):
1133         if not sessions:
1134             return None
1135         args = re.findall(r"(\d+)", sessions)
1136         return [n for n in range(int(args[0]), int(args[1]) + 1)]
1137 
1138     @classmethod
1139     def list_weeks(cls, weeks):
1140         """返回课程所含周列表"""
1141         if not weeks:
1142             return None
1143         args = re.findall(r"[^,]+", weeks)
1144         week_list = []
1145         for item in args:
1146             if "-" in item:
1147                 weeks_pair = re.findall(r"(\d+)", item)
1148                 if len(weeks_pair) != 2:
1149                     continue
1150                 if "" in item:
1151                     for i in range(int(weeks_pair[0]), int(weeks_pair[1]) + 1):
1152                         if i % 2 == 1:
1153                             week_list.append(i)
1154                 elif "" in item:
1155                     for i in range(int(weeks_pair[0]), int(weeks_pair[1]) + 1):
1156                         if i % 2 == 0:
1157                             week_list.append(i)
1158                 else:
1159                     for i in range(int(weeks_pair[0]), int(weeks_pair[1]) + 1):
1160                         week_list.append(i)
1161             else:
1162                 week_num = re.findall(r"(\d+)", item)
1163                 if len(week_num) == 1:
1164                     week_list.append(int(week_num[0]))
1165         return week_list
1166 
1167     @classmethod
1168     def get_academia_statistics(cls, display_statistics):
1169         display_statistics = "".join(display_statistics.split())
1170         gpa_list = re.findall(r"([0-9]{1,}[.][0-9]*)", display_statistics)
1171         if len(gpa_list) == 0 or not cls.is_number(gpa_list[0]):
1172             gpa = None
1173         else:
1174             gpa = float(gpa_list[0])
1175         plan_list = re.findall(
1176             r"计划总课程(\d+)门通过(\d+)门?.*未通过(\d+)门?.*未修(\d+)?.*在读(\d+)门?.*计划外?.*通过(\d+)门?.*未通过(\d+)门",
1177             display_statistics,
1178         )
1179         if len(plan_list) == 0 or len(plan_list[0]) < 7:
1180             return {"gpa": gpa}
1181         plan_list = plan_list[0]
1182         return {
1183             "gpa": gpa,  # 平均学分绩点GPA
1184             "planed_courses": {
1185                 "total": int(plan_list[0]),  # 计划内总课程数
1186                 "passed": int(plan_list[1]),  # 计划内已过课程数
1187                 "failed": int(plan_list[2]),  # 计划内未过课程数
1188                 "missed": int(plan_list[3]),  # 计划内未修课程数
1189                 "in": int(plan_list[4]),  # 计划内在读课程数
1190             },
1191             "unplaned_courses": {
1192                 "passed": int(plan_list[5]),  # 计划外已过课程数
1193                 "failed": int(plan_list[6]),  # 计划外未过课程数
1194             },
1195         }
1196 
1197     @classmethod
1198     def get_academia_type_statistics(cls, content: str):
1199         finder = re.findall(
1200             r"\"(.*)&nbsp.*要求学分.*:([0-9]{1,}[.][0-9]*|0|&nbsp;).*获得学分.*:([0-9]{1,}[.][0-9]*|0|&nbsp;).*未获得学分.*:([0-9]{1,}[.][0-9]*|0|&nbsp;)[\s\S]*?<span id='showKc(.*)'></span>",
1201             content,
1202         )
1203         finder_list = list({}.fromkeys(finder).keys())
1204         academia_list = [
1205             list(i)
1206             for i in finder_list
1207             if i[0] != ""  # 类型名称不为空
1208             and len(i[0]) <= 20  # 避免正则到首部过长类型名称
1209             and "span" not in i[-1]  # 避免正则到尾部过长类型名称
1210             and i[0] not in IGNORE_TYPE  # 忽略的类型名称
1211         ]
1212         result = {
1213             i[0]: {
1214                 "id": i[-1],
1215                 "credits": {
1216                     "required": i[1] if cls.is_number(i[1]) and i[1] != "0" else None,
1217                     "earned": i[2] if cls.is_number(i[2]) and i[2] != "0" else None,
1218                     "missed": i[3] if cls.is_number(i[3]) and i[3] != "0" else None,
1219                 },
1220             }
1221             for i in academia_list
1222         }
1223         return result
1224 
1225     @classmethod
1226     def get_display_term(cls, sid, year, term):
1227         """
1228         计算培养方案具体学期转化成中文
1229         note: 留级和当兵等情况会不准确
1230         """
1231         if (sid and year and term) is None:
1232             return None
1233         grade = int(sid[0:2])
1234         year = int(year[2:4])
1235         term = int(term)
1236         dict = {
1237             grade: "大一上" if term == 1 else "大一下",
1238             grade + 1: "大二上" if term == 1 else "大二下",
1239             grade + 2: "大三上" if term == 1 else "大三下",
1240             grade + 3: "大四上" if term == 1 else "大四下",
1241         }
1242         return dict.get(year)
1243 
1244     @classmethod
1245     def split_merge_display(cls, schedule):
1246         """
1247         拆分同周同天同课程不同时段数据合并的问题
1248         """
1249         repetIndex = []
1250         count = 0
1251         for items in schedule["courses"]:
1252             for index in range(len(schedule["courses"])):
1253                 if (schedule["courses"]).index(items) == count:  # 如果对比到自己就忽略
1254                     continue
1255                 elif (
1256                     items["course_id"]
1257                     == schedule["courses"][index]["course_id"]  # 同周同天同课程
1258                     and items["weekday"] == schedule["courses"][index]["weekday"]
1259                     and items["weeks"] == schedule["courses"][index]["weeks"]
1260                 ):
1261                     repetIndex.append(index)  # 满足条件记录索引
1262             count += 1  # 记录当前对比课程的索引
1263         if len(repetIndex) % 2 != 0:  # 暂时考虑一天两个时段上同一门课,不满足条件不进行修改
1264             return schedule
1265         for r in range(0, len(repetIndex), 2):  # 索引数组两两成对,故步进2循环
1266             fir = repetIndex[r]
1267             sec = repetIndex[r + 1]
1268             if len(re.findall(r"(\d+)", schedule["courses"][fir]["sessions"])) == 4:
1269                 schedule["courses"][fir]["sessions"] = (
1270                     re.findall(r"(\d+)", schedule["courses"][fir]["sessions"])[0]
1271                     + "-"
1272                     + re.findall(r"(\d+)", schedule["courses"][fir]["sessions"])[1]
1273                     + ""
1274                 )
1275                 schedule["courses"][fir]["list_sessions"] = cls.list_sessions(
1276                     schedule["courses"][fir]["sessions"]
1277                 )
1278                 schedule["courses"][fir]["time"] = cls.display_course_time(
1279                     schedule["courses"][fir]["sessions"]
1280                 )
1281 
1282                 schedule["courses"][sec]["sessions"] = (
1283                     re.findall(r"(\d+)", schedule["courses"][sec]["sessions"])[2]
1284                     + "-"
1285                     + re.findall(r"(\d+)", schedule["courses"][sec]["sessions"])[3]
1286                     + ""
1287                 )
1288                 schedule["courses"][sec]["list_sessions"] = cls.list_sessions(
1289                     schedule["courses"][sec]["sessions"]
1290                 )
1291                 schedule["courses"][sec]["time"] = cls.display_course_time(
1292                     schedule["courses"][sec]["sessions"]
1293                 )
1294         return schedule
1295 
1296     @classmethod
1297     def split_notifications(cls, item):
1298         if not item.get("xxnr"):
1299             return {"type": None, "content": None}
1300         content_list = re.findall(r"(.*):(.*)", item["xxnr"])
1301         if len(content_list) == 0:
1302             return {"type": None, "content": item["xxnr"]}
1303         return {"type": content_list[0][0], "content": content_list[0][1]}
1304 
1305     @classmethod
1306     def get_place(cls, place):
1307         return place.split("<br/>")[0] if "<br/>" in place else place
1308 
1309     @classmethod
1310     def get_course_time(cls, time):
1311         return "".join(time.split("<br/>")) if "<br/>" in time else time
1312 
1313     @classmethod
1314     def is_number(cls, s):
1315         if s == "":
1316             return False
1317         try:
1318             float(s)
1319             return True
1320         except ValueError:
1321             pass
1322         try:
1323             for i in s:
1324                 unicodedata.numeric(i)
1325             return True
1326         except (TypeError, ValueError):
1327             pass
1328         return False
View Code

 

  

posted @ 2020-10-19 20:03  blog_wu  阅读(1008)  评论(0编辑  收藏  举报