Support get exam action

This commit is contained in:
xtaodada 2023-12-29 18:04:00 +08:00
parent e79b7dc429
commit 23407d68ff
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
32 changed files with 441 additions and 281 deletions

View File

@ -1,17 +1,9 @@
from enum import Enum
class ExamRound(str, Enum):
Supplementation = "1"
""" 开学补缓考 """
Scattered = "2"
""" 分散考试 """
Concentration = "3"
""" 集中考试 """
class ScoreSearchType(str, Enum):
""" 成绩查询类型 """
"""成绩查询类型"""
All = "1"
"""入学以来"""
XUENIAN = "2"

View File

@ -6,14 +6,15 @@ class AuthError(CQWUEhallError):
class UsernameOrPasswordError(AuthError):
""" 用户名或密码错误 """
"""用户名或密码错误"""
class CookieError(AuthError):
""" Cookie 失效 """
"""Cookie 失效"""
class NeedCaptchaError(AuthError):
""" 需要验证码才能登录 """
"""需要验证码才能登录"""
def __init__(self, captcha: bytes):
self.captcha = captcha

View File

@ -1,3 +1,4 @@
class CQWUEhallError(Exception):
"""Base class for exceptions in this module."""
pass

View File

@ -6,8 +6,8 @@ class CQWUWebVPNError(CQWUEhallError):
class NoExamData(CQWUWebVPNError):
""" 没有检索到对应的考试记录 """
"""没有检索到对应的考试记录"""
class NoScoreDetailData(CQWUWebVPNError):
""" 没有检索到对应的成绩明细记录 """
"""没有检索到对应的成绩明细记录"""

View File

@ -14,6 +14,6 @@ class Auth(
LoginWithCookie,
LoginWithCookieFile,
LoginWithPassword,
Oauth
Oauth,
):
pass

View File

@ -10,21 +10,23 @@ class CheckCaptcha:
username: int = None,
show_qrcode: bool = True,
):
""" 检查是否需要验证码 """
"""检查是否需要验证码"""
username = username or self.username
params = {
"username": username,
"pwdEncrypt2": "pwdEncryptSalt",
"_": str(round(time.time() * 1000))
"_": str(round(time.time() * 1000)),
}
url = f"{self.auth_host}/authserver/needCaptcha.html"
captcha_html = await self.request.get(url, params=params, follow_redirects=False)
if captcha_html.text == 'true':
params = {
"ts": str(round(time.time()))
}
captcha_html = await self.request.get(
url, params=params, follow_redirects=False
)
if captcha_html.text == "true":
params = {"ts": str(round(time.time()))}
captcha_url = f"{self.auth_host}/authserver/captcha.html"
res = await self.request.get(captcha_url, params=params, follow_redirects=False)
res = await self.request.get(
captcha_url, params=params, follow_redirects=False
)
if not show_qrcode:
raise NeedCaptchaError(res.content)
with open("captcha.jpg", mode="wb") as f:

View File

@ -9,7 +9,7 @@ class Login:
async def login(
self: "cqwu.Client",
):
""" 登录 """
"""登录"""
with contextlib.suppress(CookieError):
if self.cookie:
await self.login_with_cookie()

View File

@ -17,47 +17,51 @@ class LoginWithPassword:
"""
auth_host = auth_host or self.auth_host
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63',
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63",
}
html = await self.request.get(f"{auth_host}/authserver/login", headers=headers, follow_redirects=True)
html = await self.request.get(
f"{auth_host}/authserver/login", headers=headers, follow_redirects=True
)
self.cookies.update(html.cookies)
tree = etree.HTML(html.text)
try:
pwd_default_encrypt_salt = tree.xpath('//*[@id="pwdDefaultEncryptSalt"]/@value')[0]
pwd_default_encrypt_salt = tree.xpath(
'//*[@id="pwdDefaultEncryptSalt"]/@value'
)[0]
except IndexError:
if auth_host == self.auth_host:
self._use_password_login = True # noqa
self.me = await self.get_me() # noqa
return
form_data = {
'username': str(self.username),
'password': encode_password(self.password, pwd_default_encrypt_salt),
'lt': tree.xpath('//*[@id="casLoginForm"]/input[1]/@value')[0],
'dllt': tree.xpath('//*[@id="casLoginForm"]/input[2]/@value')[0],
'execution': tree.xpath('//*[@id="casLoginForm"]/input[3]/@value')[0],
'_eventId': tree.xpath('//*[@id="casLoginForm"]/input[4]/@value')[0],
'rmShown': tree.xpath('//*[@id="casLoginForm"]/input[5]/@value')[0]
"username": str(self.username),
"password": encode_password(self.password, pwd_default_encrypt_salt),
"lt": tree.xpath('//*[@id="casLoginForm"]/input[1]/@value')[0],
"dllt": tree.xpath('//*[@id="casLoginForm"]/input[2]/@value')[0],
"execution": tree.xpath('//*[@id="casLoginForm"]/input[3]/@value')[0],
"_eventId": tree.xpath('//*[@id="casLoginForm"]/input[4]/@value')[0],
"rmShown": tree.xpath('//*[@id="casLoginForm"]/input[5]/@value')[0],
}
# 是否需要验证码
if not captcha_code:
captcha_code = await self.check_captcha(show_qrcode=show_qrcode)
if captcha_code:
form_data['captchaResponse'] = captcha_code
form_data["captchaResponse"] = captcha_code
# 登录
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Origin': auth_host,
'Referer': f'{auth_host}/authserver/login',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63',
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Origin": auth_host,
"Referer": f"{auth_host}/authserver/login",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63",
}
html = await self.request.post(
f"{auth_host}/authserver/login",
@ -66,7 +70,7 @@ class LoginWithPassword:
follow_redirects=False,
)
if auth_host == self.auth_host:
if 'CASTGC' not in html.cookies.keys():
if "CASTGC" not in html.cookies.keys():
raise UsernameOrPasswordError
self.cookies.update(html.cookies)
self._use_password_login = True # noqa

View File

@ -22,6 +22,6 @@ class GetPayBill:
raise CookieError()
data = await self.request.post(
"http://218.194.176.214:8382/epay/thirdapp/loadbill.json",
data={"pageno": page_number}
data={"pageno": page_number},
)
return PayBillPage(**data.json())

View File

@ -19,7 +19,10 @@ class Pay(
html = await self.request.get(url, follow_redirects=False)
if html.status_code == 302:
location = html.headers["location"]
params = {i.split("=")[0]: i.split("=")[1] for i in location.split("?")[1].split("&")}
params = {
i.split("=")[0]: i.split("=")[1]
for i in location.split("?")[1].split("&")
}
self._pay_x_token = params["token"]
@property

View File

@ -18,8 +18,10 @@ class GetPayProjectDetail:
"""
if not self._pay_x_token:
await self._oauth_pay()
url = (f"https://pay.cqwu.edu.cn/api/pay/web/tuitionAndDorm/getTuitionAndDormList/"
f"{self.username}/{project_id}")
url = (
f"https://pay.cqwu.edu.cn/api/pay/web/tuitionAndDorm/getTuitionAndDormList/"
f"{self.username}/{project_id}"
)
html = await self.request.get(url, headers=self.pay_headers)
if html.status_code != 200:
raise CookieError()

View File

@ -1,7 +1,5 @@
from .get_me import GetMe
class Users(
GetMe
):
class Users(GetMe):
pass

View File

@ -6,7 +6,9 @@ from cqwu import types
from cqwu.errors.auth import CookieError
def get_value_from_soup(soup: BeautifulSoup, attr_id: str) -> Union[type(None), str, int]:
def get_value_from_soup(
soup: BeautifulSoup, attr_id: str
) -> Union[type(None), str, int]:
try:
data = soup.find("input", attrs={"id": attr_id})["value"]
try:
@ -53,7 +55,9 @@ class GetMe:
temp = {key: get_value_from_soup(soup, value) for key, value in data.items()}
temp["password"] = self.password
try:
temp["specialty"] = soup.find_all("input", attrs={"id": "detail_xy"})[1]["value"]
temp["specialty"] = soup.find_all("input", attrs={"id": "detail_xy"})[1][
"value"
]
except (ValueError, TypeError, KeyError, IndexError):
temp["specialty"] = None
return types.User(**temp)

View File

@ -3,6 +3,7 @@ from httpx import URL
from .get_calendar import GetCalendar
from .get_calendar_change import GetCalendarChange
from .get_exam_calendar import GetExamCalendar
from .get_exam_calendar_action import GetExamCalendarAction
from .get_score_detail import GetScoreDetail
from .get_selected_courses import GetSelectedCourses
from .login_jwmis import LoginJwmis
@ -13,6 +14,7 @@ class WebVPN(
GetCalendar,
GetCalendarChange,
GetExamCalendar,
GetExamCalendarAction,
GetScoreDetail,
GetSelectedCourses,
LoginJwmis,

View File

@ -14,7 +14,7 @@ class GetCalendar:
xue_qi: int = None,
use_model: bool = False,
) -> Union[str, List[AiCourse]]:
""" 获取课程表 """
"""获取课程表"""
xue_nian = xue_nian or self.xue_nian
xue_qi = xue_qi or self.xue_qi
jw_html = await self.login_jwmis()
@ -24,22 +24,27 @@ class GetCalendar:
"params": base64.b64encode(f"xn={xue_nian}&xq={xue_qi}".encode()).decode(),
}
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Referer': f'{jw_host}/cqwljw/student/xkjg.wdkb.jsp?menucode=S20301',
'Sec-Fetch-Dest': 'iframe',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41',
'sec-ch-ua': '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Referer": f"{jw_host}/cqwljw/student/xkjg.wdkb.jsp?menucode=S20301",
"Sec-Fetch-Dest": "iframe",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41",
"sec-ch-ua": '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
}
jw_html = await self.request.get(jw_url, params=params, headers=headers, timeout=60, follow_redirects=True)
jw_html = jw_html.text.replace("""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""", "")
jw_html = await self.request.get(
jw_url, params=params, headers=headers, timeout=60, follow_redirects=True
)
jw_html = jw_html.text.replace(
"""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""",
"",
)
return (
parse_courses(jw_html)
if use_model

View File

@ -11,7 +11,7 @@ class GetCalendarChange:
xue_nian: int = None,
xue_qi: int = None,
) -> str:
""" 获取课程表 """
"""获取课程表"""
xue_nian = xue_nian or self.xue_nian
xue_qi = xue_qi or self.xue_qi
jw_html = await self.login_jwmis()
@ -19,50 +19,60 @@ class GetCalendarChange:
jw_url = f"{jw_host}/cqwljw/student/jxap.jxaptzxx_rpt.jsp"
jw_sg_url = f"{jw_host}/cqwljw/STU_DynamicInitDataAction.do"
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Origin': 'https://clientvpn.cqwu.edu.cn',
'Referer': f'{jw_host}/cqwljw/student/jxap.jxaptzxx.html?menucode=S20302',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41',
'content-type': 'application/x-www-form-urlencoded',
'sec-ch-ua': '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Origin": "https://clientvpn.cqwu.edu.cn",
"Referer": f"{jw_host}/cqwljw/student/jxap.jxaptzxx.html?menucode=S20302",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41",
"content-type": "application/x-www-form-urlencoded",
"sec-ch-ua": '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
}
params = {
"classPath": "C73E288D0DEA8D7F772BBD7F8FDC7E66F44C9E3992261989ECBAC5A3D722B306C6354658E0F25121E24CED075326C19885F263F369E5CD668E2EEE7CFB7EB5788F202FC6FD7DB0C96FB6995C1DD96ADE84BE3E72CFFBE9EC74FA044498BD2D21EA0439F9DC625F0EF61B7159924C542D577F814848F27128"
}
res = await self.request.post(jw_sg_url, params=params, headers=headers, timeout=60, follow_redirects=True)
res = await self.request.post(
jw_sg_url, params=params, headers=headers, timeout=60, follow_redirects=True
)
data = {
'xh': get_in_middle(res.text, '<xh>', '</xh>'),
'xn': str(xue_nian),
'xq': str(xue_qi),
'xnxq': f'{xue_nian},{xue_qi}',
'menucode_current': 'S20302',
"xh": get_in_middle(res.text, "<xh>", "</xh>"),
"xn": str(xue_nian),
"xq": str(xue_qi),
"xnxq": f"{xue_nian},{xue_qi}",
"menucode_current": "S20302",
}
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': 'https://clientvpn.cqwu.edu.cn',
'Referer': f'{jw_host}/cqwljw/student/jxap.jxaptzxx.html?menucode=S20302',
'Sec-Fetch-Dest': 'iframe',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41',
'sec-ch-ua': '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "https://clientvpn.cqwu.edu.cn",
"Referer": f"{jw_host}/cqwljw/student/jxap.jxaptzxx.html?menucode=S20302",
"Sec-Fetch-Dest": "iframe",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41",
"sec-ch-ua": '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
}
jw_html = await self.request.post(jw_url, data=data, headers=headers, timeout=60, follow_redirects=True)
jw_html = jw_html.text.replace("""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""", "")
jw_html = jw_html.replace("""<script language='javascript' type='text/javascript' src='../js/Print.js'></script>""", "")
jw_html = await self.request.post(
jw_url, data=data, headers=headers, timeout=60, follow_redirects=True
)
jw_html = jw_html.text.replace(
"""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""",
"",
)
jw_html = jw_html.replace(
"""<script language='javascript' type='text/javascript' src='../js/Print.js'></script>""",
"",
)
return jw_html.replace("<title></title>", '<meta charset="UTF-8">')

View File

@ -3,7 +3,6 @@ from typing import Union, List
from bs4 import BeautifulSoup
import cqwu
from cqwu.enums import ExamRound
from cqwu.errors import NoExamData
from cqwu.types import AiExam
@ -11,56 +10,63 @@ from cqwu.types import AiExam
class GetExamCalendar:
async def get_exam_calendar(
self: "cqwu.Client",
exam_round: Union[str, ExamRound] = ExamRound.Supplementation,
exam_round: str = "1",
xue_nian: int = None,
xue_qi: int = None,
use_model: bool = False,
) -> Union[str, List[AiExam]]:
""" 获取考试安排表 """
"""获取考试安排表"""
xue_nian = xue_nian or self.xue_nian
xue_qi = xue_qi or self.xue_qi
exam_round = ExamRound(exam_round)
jw_html = await self.login_jwmis()
jw_host = self.get_web_vpn_host(jw_html.url, https=True)
jw_url = f"{jw_host}/cqwljw/student/ksap.ksapb_date.jsp"
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9,zh-Hans;q=0.8,und;q=0.7,en;q=0.6,zh-Hant;q=0.5,ja;q=0.4',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'DNT': '1',
'Pragma': 'no-cache',
'Referer': f'{jw_host}/cqwljw/student/ksap.ksapb.html?menucode=S20403',
'Sec-Fetch-Dest': 'iframe',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
'sec-ch-ua': '"Chromium";v="112", "Not:A-Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9,zh-Hans;q=0.8,und;q=0.7,en;q=0.6,zh-Hant;q=0.5,ja;q=0.4",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded",
"DNT": "1",
"Pragma": "no-cache",
"Referer": f"{jw_host}/cqwljw/student/ksap.ksapb.html?menucode=S20403",
"Sec-Fetch-Dest": "iframe",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
"sec-ch-ua": '"Chromium";v="112", "Not:A-Brand";v="99"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
}
data = {
'xn': str(xue_nian),
'xq': str(xue_qi),
'title': '',
'xnxq': f'{xue_nian}{xue_qi}',
'kslc': exam_round.value,
"xn": str(xue_nian),
"xq": str(xue_qi),
"title": "",
"xnxq": f"{xue_nian}{xue_qi}",
"kslc": exam_round,
}
jw_html = await self.request.post(jw_url, data=data, headers=headers, timeout=60, follow_redirects=True)
jw_html = await self.request.post(
jw_url, data=data, headers=headers, timeout=60, follow_redirects=True
)
if "没有检索到记录!" in jw_html.text:
raise NoExamData("没有检索到记录!")
jw_html = jw_html.text.replace("""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""", "")
jw_html = jw_html.replace("""<script language='javascript' type='text/javascript' src='../js/Print.js'></script>""", "")
jw_html = jw_html.replace("charset=GBK", 'charset=UTF-8')
jw_html = jw_html.text.replace(
"""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""",
"",
)
jw_html = jw_html.replace(
"""<script language='javascript' type='text/javascript' src='../js/Print.js'></script>""",
"",
)
jw_html = jw_html.replace("charset=GBK", "charset=UTF-8")
if not use_model:
return jw_html
return parse_html(jw_html, exam_round)
def parse_html(html: str, exam_round: ExamRound) -> List[AiExam]:
def parse_html(html: str, exam_round: str) -> List[AiExam]:
data: List[AiExam] = []
soup = BeautifulSoup(html, "html.parser")
trs = soup.find_all("tr")[1:]

View File

@ -0,0 +1,64 @@
from json import JSONDecodeError
import cqwu
from cqwu.types.exam import ExamType
class GetExamCalendarAction:
async def get_exam_calendar_action(
self: "cqwu.Client",
xue_nian: int = None,
xue_qi: int = None,
) -> ExamType:
"""获取考试安排可用类型"""
xue_nian = xue_nian or self.xue_nian
xue_qi = xue_qi or self.xue_qi
jw_html = await self.login_jwmis()
jw_host = self.get_web_vpn_host(jw_html.url, https=True)
jw_url = f"{jw_host}/cqwljw/frame/droplist/getDropLists.action"
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9,zh-Hans;q=0.8,und;q=0.7,en;q=0.6,zh-Hant;q=0.5,ja;q=0.4",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded",
"DNT": "1",
"Pragma": "no-cache",
"Referer": f"{jw_host}/cqwljw/student/ksap.ksapb.html?menucode=S20403",
"Sec-Fetch-Dest": "iframe",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
"sec-ch-ua": '"Chromium";v="112", "Not:A-Brand";v="99"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
}
data = {
"comboBoxName": "Ms_KSSW_FBKSLC",
"paramValue": f"xtdm=jw&zxtdm=7&kgmc=kw_fbksap&xnxq={xue_nian}{xue_qi}",
"isYXB": "0",
"isCDDW": "0",
"isXQ": "0",
"isDJKSLB": "0",
"isZY": "0",
}
jw_html = await self.request.post(
jw_url, data=data, headers=headers, timeout=60, follow_redirects=True
)
data = ExamType()
try:
jw_data = jw_html.json()
for i in jw_data:
if "开学补缓考" in i["name"]:
data.supplementation = i["code"]
elif "毕业年级考试" in i["name"]:
data.graduate = i["code"]
elif "分散考试" in i["name"]:
data.scattered = i["code"]
elif "集中考试" in i["name"]:
data.concentration = i["code"]
except JSONDecodeError:
pass
return data

View File

@ -16,7 +16,7 @@ class GetScoreDetail:
xue_qi: int = None,
use_model: bool = False,
) -> Union[str, ScoreDetail]:
""" 获取学业成绩 """
"""获取学业成绩"""
xue_nian = xue_nian or self.xue_nian
xue_qi = xue_qi or self.xue_qi
search_type = ScoreSearchType(search_type)
@ -24,23 +24,23 @@ class GetScoreDetail:
jw_host = self.get_web_vpn_host(jw_html.url, https=True)
jw_url = f"{jw_host}/cqwljw/student/xscj.stuckcj_data.jsp"
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9,zh-Hans;q=0.8,und;q=0.7,en;q=0.6,zh-Hant;q=0.5,ja;q=0.4',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'DNT': '1',
'Pragma': 'no-cache',
'Referer': f'{jw_host}/cqwljw/student/ksap.ksapb.html?menucode=S20403',
'Sec-Fetch-Dest': 'iframe',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
'sec-ch-ua': '"Chromium";v="112", "Not:A-Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9,zh-Hans;q=0.8,und;q=0.7,en;q=0.6,zh-Hant;q=0.5,ja;q=0.4",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded",
"DNT": "1",
"Pragma": "no-cache",
"Referer": f"{jw_host}/cqwljw/student/ksap.ksapb.html?menucode=S20403",
"Sec-Fetch-Dest": "iframe",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
"sec-ch-ua": '"Chromium";v="112", "Not:A-Brand";v="99"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
}
data = {
"sjxz": f"sjxz{search_type.value}",
@ -50,7 +50,7 @@ class GetScoreDetail:
"btnExport": "%B5%BC%B3%F6",
"rxnj": str(xue_nian),
"xn": str(xue_nian),
'xn1': str(xue_nian + 1),
"xn1": str(xue_nian + 1),
"xq": str(xue_qi),
"ysyxS": "on",
"sjxzS": "on",
@ -58,12 +58,20 @@ class GetScoreDetail:
"fxC": "on",
"xsjd": "1",
}
jw_html = await self.request.post(jw_url, data=data, headers=headers, timeout=60, follow_redirects=True)
jw_html = await self.request.post(
jw_url, data=data, headers=headers, timeout=60, follow_redirects=True
)
if "没有检索到记录!" in jw_html.text:
raise NoScoreDetailData("没有检索到记录!")
jw_html = jw_html.text.replace("""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""", "")
jw_html = jw_html.replace("""<script language='javascript' type='text/javascript' src='../js/Print.js'></script>""", "")
jw_html = jw_html.replace("charset=GBK", 'charset=UTF-8')
jw_html = jw_html.text.replace(
"""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""",
"",
)
jw_html = jw_html.replace(
"""<script language='javascript' type='text/javascript' src='../js/Print.js'></script>""",
"",
)
jw_html = jw_html.replace("charset=GBK", "charset=UTF-8")
if not use_model:
return jw_html
return parse_html(jw_html)

View File

@ -12,27 +12,32 @@ class GetSelectedCourses:
self: "cqwu.Client",
use_model: bool = False,
) -> Union[str, List[AiCourse]]:
""" 获取选课结果 """
"""获取选课结果"""
jw_html = await self.login_jwmis()
jw_host = self.get_web_vpn_host(jw_html.url)
jw_url = f"{jw_host}/cqwljw/student/wsxk.zxjg.jsp"
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Referer': f'{jw_host}/cqwljw/frame/homes.html',
'Sec-Fetch-Dest': 'iframe',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41',
'sec-ch-ua': '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Referer": f"{jw_host}/cqwljw/frame/homes.html",
"Sec-Fetch-Dest": "iframe",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41",
"sec-ch-ua": '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
}
jw_html = await self.request.get(jw_url, headers=headers, timeout=60, follow_redirects=True)
jw_html = jw_html.text.replace("""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""", "")
jw_html = await self.request.get(
jw_url, headers=headers, timeout=60, follow_redirects=True
)
jw_html = jw_html.text.replace(
"""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""",
"",
)
return (
parse_courses(jw_html)
if use_model
@ -57,7 +62,9 @@ def parse_courses(jw_html: str) -> List[AiCourse]:
for calendar in calendars:
text = (BeautifulSoup(calendar, "lxml")).text.strip()
try:
position, weeks, day, start_num, sections = parse_weeks_and_sections(text)
position, weeks, day, start_num, sections = parse_weeks_and_sections(
text
)
except Exception:
continue
item = AiCourse(

View File

@ -8,9 +8,10 @@ class LoginJwmis:
async def login_jwmis(
self: "cqwu.Client",
) -> Response:
""" 登录教学管理平台 """
"""登录教学管理平台"""
jw_html = await self.request.get(
f"{self.web_ehall_path}/appShow?appId=5299144291521305", follow_redirects=True
f"{self.web_ehall_path}/appShow?appId=5299144291521305",
follow_redirects=True,
)
if "教学管理服务平台" not in jw_html.text:
raise CookieError

View File

@ -13,7 +13,9 @@ class LoginWebVPN:
url = "https://webvpn.cqwu.edu.cn"
ehall_html = await self.request.get(url, follow_redirects=True)
self.web_ehall_path = self.get_web_vpn_host(ehall_html.url) # noqa
await self.oauth("https://authserver.cqwu.edu.cn/authserver/login?service=https://clientvpn.cqwu.edu.cn/enlink/api/client/callback/cas")
await self.oauth(
"https://authserver.cqwu.edu.cn/authserver/login?service=https://clientvpn.cqwu.edu.cn/enlink/api/client/callback/cas"
)
auth_html = await self.request.get(
f"{self.web_ehall_path}/login", follow_redirects=True
)

View File

@ -22,15 +22,15 @@ class XG(
raise CookieError()
url = "http://xg.cqwu.edu.cn/xsfw/sys/swpubapp/indexmenu/getAppConfig.do"
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,hu;q=0.5',
'Connection': 'keep-alive',
'Referer': 'http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51',
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,hu;q=0.5",
"Connection": "keep-alive",
"Referer": "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51",
}
params = {
'appId': '5275772372599202',
'appName': 'zhcptybbapp',
'v': '021534151969418724',
"appId": "5275772372599202",
"appName": "zhcptybbapp",
"v": "021534151969418724",
}
await self.request.get(url, headers=headers, params=params)

View File

@ -13,33 +13,33 @@ class GetCP:
year: int = None,
semester: int = None,
) -> CP:
""" 获取综合测评结果 """
"""获取综合测评结果"""
xue_nian = year or self.xue_nian
xue_qi = semester or self.xue_qi
await self.oauth_xg()
url = "http://xg.cqwu.edu.cn/xsfw/sys/emapcomponent/imexport/export.do"
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,hu;q=0.5',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'http://xg.cqwu.edu.cn',
'Referer': 'http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51',
'X-Requested-With': 'XMLHttpRequest',
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,hu;q=0.5",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Origin": "http://xg.cqwu.edu.cn",
"Referer": "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51",
"X-Requested-With": "XMLHttpRequest",
}
data = {
'app': 'zhcptybbapp',
'contextPath': 'http://xg.cqwu.edu.cn/xsfw',
'module': 'modules',
'page': 'cpjgcx',
'action': 'cpjgcxbgdz',
'containerId': 'cpjg_grid',
'CPXN': str(xue_nian),
'CPXQ': str(xue_qi),
'filename': '综合测评结果',
'colnames': 'XH,XM,CPXN,CPXQ,DWDM,DZ_ZYFX,BJDM,ZCJ,BJPM,BJRS,ZYNJPM,ZYNJRS,FS1,FS10,FS11,FS12,'
'XZNJ,DZ_BJPM,DZ_ZYPM',
"app": "zhcptybbapp",
"contextPath": "http://xg.cqwu.edu.cn/xsfw",
"module": "modules",
"page": "cpjgcx",
"action": "cpjgcxbgdz",
"containerId": "cpjg_grid",
"CPXN": str(xue_nian),
"CPXQ": str(xue_qi),
"filename": "综合测评结果",
"colnames": "XH,XM,CPXN,CPXQ,DWDM,DZ_ZYFX,BJDM,ZCJ,BJPM,BJRS,ZYNJPM,ZYNJRS,FS1,FS10,FS11,FS12,"
"XZNJ,DZ_BJPM,DZ_ZYPM",
}
html = await self.request.post(url, headers=headers, data=data)
if html.status_code != 200:

View File

@ -12,25 +12,27 @@ class GetPublicCP:
page_number: int = 1,
total: bool = True,
) -> List[PublicCPRaw]:
""" 获取综合测评公示结果 """
"""获取综合测评公示结果"""
await self.oauth_xg()
async def get_public_cp_raw(page_size_: int, page_number_: int) -> CPGS:
url = "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/modules/cpgs/cpgs_cpgsbg.do"
url = (
"http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/modules/cpgs/cpgs_cpgsbg.do"
)
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,hu;q=0.5',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'http://xg.cqwu.edu.cn',
'Referer': 'http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51',
'X-Requested-With': 'XMLHttpRequest',
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,hu;q=0.5",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Origin": "http://xg.cqwu.edu.cn",
"Referer": "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51",
"X-Requested-With": "XMLHttpRequest",
}
data = {
'querySetting': '[]',
'pageSize': str(page_size_),
'pageNumber': str(page_number_),
"querySetting": "[]",
"pageSize": str(page_size_),
"pageNumber": str(page_number_),
}
html = await self.request.post(url, headers=headers, data=data)
if html.status_code != 200:
@ -59,7 +61,10 @@ class GetPublicCP:
return_datas.extend(html_raw_datas.rows)
if html_raw_datas.totalSize == 0:
break
elif html_raw_datas.pageNumber * html_raw_datas.pageSize >= html_raw_datas.totalSize:
elif (
html_raw_datas.pageNumber * html_raw_datas.pageSize
>= html_raw_datas.totalSize
):
break
else:
page_number += 1

View File

@ -7,9 +7,9 @@ from cqwu import types
class GetScore:
async def get_score(
self: "cqwu.Client",
year: int = None,
semester: int = None,
self: "cqwu.Client",
year: int = None,
semester: int = None,
) -> List["types.Score"]:
"""
获取期末成绩
@ -20,16 +20,18 @@ class GetScore:
year = year or self.xue_nian
semester = semester or (self.xue_qi + 1)
await self.oauth_xg()
query_url = "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/modules/cjcx/cjcxbgdz.do"
query_url = (
"http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/modules/cjcx/cjcxbgdz.do"
)
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'http://xg.cqwu.edu.cn',
'Referer': 'http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63',
'X-Requested-With': 'XMLHttpRequest',
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Origin": "http://xg.cqwu.edu.cn",
"Referer": "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63",
"X-Requested-With": "XMLHttpRequest",
}
query_data = [
{
@ -47,12 +49,12 @@ class GetScore:
"builderList": "cbl_m_List",
"builder": "m_value_equal",
"value": str(semester),
}
},
]
data = {
'querySetting': json.dumps(query_data),
'pageSize': '100',
'pageNumber': '1',
"querySetting": json.dumps(query_data),
"pageSize": "100",
"pageNumber": "1",
}
html = await self.request.post(query_url, headers=headers, data=data)
data = [types.Score(**i) for i in html.json()["datas"]["cjcxbgdz"]["rows"]]

View File

@ -4,5 +4,10 @@ from .epay import PayBill, PayBillPage
from .exam import AiExam
from .pay import PayProject, PayProjectDetail, PayUser
from .score import Score
from .score_detail import ScoreDetail, ScoreDetailInfo, ScoreDetailCourse, ScoreDetailTotal
from .score_detail import (
ScoreDetail,
ScoreDetailInfo,
ScoreDetailCourse,
ScoreDetailTotal,
)
from .user import User

View File

@ -29,5 +29,11 @@ class AiCourse(BaseModel):
"weeks": ",".join(list(map(str, self.weeks))),
"day": self.day,
"style": "",
"sections": ",".join(list(map(str, list(range(self.start_num, self.start_num + self.sections)))))
"sections": ",".join(
list(
map(
str, list(range(self.start_num, self.start_num + self.sections))
)
)
),
}

View File

@ -40,77 +40,77 @@ class PublicCPRaw(BaseModel):
@property
def id(self) -> int:
""" 学号 """
"""学号"""
return int(self.XH)
@property
def name(self) -> str:
""" 姓名 """
"""姓名"""
return self.XM
@property
def yuan_xi(self) -> str:
""" 院系 """
"""院系"""
return self.DWDM_DISPLAY
@property
def zhuan_ye(self) -> str:
""" 专业 """
"""专业"""
return self.ZYDM_DISPLAY
@property
def class_name(self) -> str:
""" 班级 """
"""班级"""
return self.BJDM_DISPLAY
@property
def total_score(self) -> float:
""" 总成绩 """
"""总成绩"""
return float(self.ZCJ)
@property
def class_rank(self) -> int:
""" 班级排名 """
"""班级排名"""
return int(self.BJPM)
@property
def grade_rank(self) -> int:
""" 专业年级排名 """
"""专业年级排名"""
return int(self.ZYNJPM)
@property
def dysz(self) -> float:
""" 德育素质分 """
"""德育素质分"""
return float(self.FS1)
@property
def zysz(self) -> float:
""" 智育素质测评 """
"""智育素质测评"""
return float(self.FS10)
@property
def cxsz(self) -> float:
""" 创新素质测评 """
"""创新素质测评"""
return float(self.FS11)
@property
def wtsz(self) -> float:
""" 文体素质 """
"""文体素质"""
return float(self.FS12)
@property
def dysz_raw(self) -> float:
""" 德育原始成绩 """
"""德育原始成绩"""
return float(self.DYYSCJ)
@property
def wtsz_raw(self) -> float:
""" 文体原始成绩 """
"""文体原始成绩"""
return float(self.WTYSCJ)
@property
def cxsz_raw(self) -> float:
""" 创新原始成绩 """
"""创新原始成绩"""
return float(self.CXYSCJ)

View File

@ -3,7 +3,28 @@ from typing import Tuple
from pydantic import BaseModel
from cqwu.enums import ExamRound
class ExamType:
supplementation: str
""" 开学补缓考 """
graduate: str
""" 毕业年级考试 """
scattered: str
""" 分散考试 """
concentration: str
""" 集中考试 """
def __init__(
self,
supplementation: str = "",
graduate: str = "",
scattered: str = "",
concentration: str = "",
):
self.supplementation = supplementation
self.graduate = graduate
self.scattered = scattered
self.concentration = concentration
class AiExam(BaseModel):
@ -17,25 +38,33 @@ class AiExam(BaseModel):
""" 考试地点 """
seat: str
""" 座位号 """
exam_round: ExamRound
exam_round: str
""" 考试轮次 """
@property
def name_no_id(self) -> str:
""" 获取课程名称(去除课程编号) """
"""获取课程名称(去除课程编号)"""
return self.name.split("]")[-1]
@property
def days_left(self) -> int:
""" 获取距离考试的天数 """
"""获取距离考试的天数"""
return (self.get_time()[0] - datetime.datetime.now()).days
def get_time(self) -> Tuple[datetime.datetime, datetime.datetime]:
""" 获取格式化后的考试时间 """
"""获取格式化后的考试时间"""
# 2023-06-25(18周 星期日)09:00-11:00
day = datetime.datetime.strptime(self.time.split("(")[0], "%Y-%m-%d")
start_time = datetime.datetime.strptime(self.time.split(")")[1].split("-")[0], "%H:%M")
start_time = datetime.datetime(day.year, day.month, day.day, start_time.hour, start_time.minute)
end_time = datetime.datetime.strptime(self.time.split(")")[1].split("-")[1], "%H:%M")
end_time = datetime.datetime(day.year, day.month, day.day, end_time.hour, end_time.minute)
start_time = datetime.datetime.strptime(
self.time.split(")")[1].split("-")[0], "%H:%M"
)
start_time = datetime.datetime(
day.year, day.month, day.day, start_time.hour, start_time.minute
)
end_time = datetime.datetime.strptime(
self.time.split(")")[1].split("-")[1], "%H:%M"
)
end_time = datetime.datetime(
day.year, day.month, day.day, end_time.hour, end_time.minute
)
return start_time, end_time

View File

@ -2,7 +2,8 @@ from pydantic import BaseModel
class Score(BaseModel):
""" 成绩类 """
"""成绩类"""
KCMC: str
XF: float
ZCJ: float
@ -14,30 +15,30 @@ class Score(BaseModel):
@property
def name(self) -> str:
""" 课程名称 """
"""课程名称"""
return self.KCMC
@property
def credit(self) -> float:
""" 学分 """
"""学分"""
return self.XF
@property
def score(self) -> float:
""" 成绩 """
"""成绩"""
return self.ZCJ
@property
def grade_point(self) -> float:
""" 绩点 """
"""绩点"""
return float(self.JD)
@property
def year(self) -> int:
""" 学年 """
"""学年"""
return int(self.XN)
@property
def semester(self) -> int:
""" 学期 """
"""学期"""
return int(self.XQ)

View File

@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
setuptools.setup(
name="cqwu", # 用自己的名替换其中的YOUR_USERNAME_
version="0.0.17", # 包版本号,便于维护版本
version="0.0.18", # 包版本号,便于维护版本
author="omg-xtao", # 作者,可以写自己的姓名
author_email="xtao@xtaolink.cn", # 作者联系方式,可写自己的邮箱地址
description="A cqwu ehall client.", # 包的简述
@ -18,7 +18,7 @@ setuptools.setup(
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.8', # 对python的最低版本要求
python_requires=">=3.8", # 对python的最低版本要求
install_requires=[
"httpx",
"lxml",