mirror of
https://github.com/cqwu-ehall/cqwu-ehall.git
synced 2024-11-21 18:38:18 +00:00
✨ Support get exam calendar
This commit is contained in:
parent
979d575182
commit
99daff1564
@ -29,8 +29,10 @@ class Client(Methods):
|
||||
self.loop = asyncio.get_event_loop()
|
||||
self.me: Optional[User] = None
|
||||
self._use_password_login = False
|
||||
self.xue_nian = 2022 # 学年
|
||||
self.xue_qi = 1 # 学期 0 为第一学期, 1 为第二学期
|
||||
self.xue_nian = 2022
|
||||
""" 学年 """
|
||||
self.xue_qi = 1
|
||||
""" 学期,0 为第一学期,1 为第二学期 """
|
||||
|
||||
@staticmethod
|
||||
def get_input(word: str = "", is_int: bool = False):
|
||||
|
@ -0,0 +1 @@
|
||||
from .webvpn import *
|
10
cqwu/enums/webvpn.py
Normal file
10
cqwu/enums/webvpn.py
Normal file
@ -0,0 +1,10 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ExamRound(str, Enum):
|
||||
Supplementation = "1"
|
||||
""" 开学补缓考 """
|
||||
Scattered = "2"
|
||||
""" 分散考试 """
|
||||
Concentration = "3"
|
||||
""" 集中考试 """
|
@ -1,3 +1,4 @@
|
||||
from .auth import *
|
||||
from .base import *
|
||||
from .epay import *
|
||||
from .webvpn import *
|
||||
|
9
cqwu/errors/webvpn.py
Normal file
9
cqwu/errors/webvpn.py
Normal file
@ -0,0 +1,9 @@
|
||||
from .base import CQWUEhallError
|
||||
|
||||
|
||||
class CQWUWebVPNError(CQWUEhallError):
|
||||
pass
|
||||
|
||||
|
||||
class NoExamData(CQWUWebVPNError):
|
||||
""" 没有检索到对应的考试记录 """
|
@ -2,12 +2,16 @@ from httpx import URL
|
||||
|
||||
from .get_calendar import GetCalendar
|
||||
from .get_calendar_change import GetCalendarChange
|
||||
from .get_exam_calendar import GetExamCalendar
|
||||
from .login_jwmis import LoginJwmis
|
||||
from .login_webvpn import LoginWebVPN
|
||||
|
||||
|
||||
class WebVPN(
|
||||
GetCalendar,
|
||||
GetCalendarChange,
|
||||
GetExamCalendar,
|
||||
LoginJwmis,
|
||||
LoginWebVPN,
|
||||
):
|
||||
@staticmethod
|
||||
|
@ -4,7 +4,6 @@ from typing import Tuple, List, Union
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
import cqwu
|
||||
from cqwu.errors import CookieError
|
||||
from cqwu.types.calendar import AiCourse
|
||||
|
||||
|
||||
@ -18,11 +17,7 @@ class GetCalendar:
|
||||
""" 获取课程表 """
|
||||
xue_nian = xue_nian or self.xue_nian
|
||||
xue_qi = xue_qi or self.xue_qi
|
||||
jw_html = await self.request.get(
|
||||
f"{self.web_ehall_path}/appShow?appId=5299144291521305", follow_redirects=True
|
||||
)
|
||||
if "教学管理服务平台" not in jw_html.text:
|
||||
raise CookieError
|
||||
jw_html = await self.login_jwmis()
|
||||
jw_host = self.get_web_vpn_host(jw_html.url)
|
||||
jw_url = f"{jw_host}/cqwljw/student/wsxk.xskcb10319.jsp"
|
||||
params = {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import cqwu
|
||||
from cqwu.errors import CookieError
|
||||
|
||||
|
||||
def get_in_middle(text: str, start: str, end: str) -> str:
|
||||
@ -15,11 +14,7 @@ class GetCalendarChange:
|
||||
""" 获取课程表 """
|
||||
xue_nian = xue_nian or self.xue_nian
|
||||
xue_qi = xue_qi or self.xue_qi
|
||||
jw_html = await self.request.get(
|
||||
f"{self.web_ehall_path}/appShow?appId=5299144291521305", follow_redirects=True
|
||||
)
|
||||
if "教学管理服务平台" not in jw_html.text:
|
||||
raise CookieError
|
||||
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/jxap.jxaptzxx_rpt.jsp"
|
||||
jw_sg_url = f"{jw_host}/cqwljw/STU_DynamicInitDataAction.do"
|
||||
|
81
cqwu/methods/webvpn/get_exam_calendar.py
Normal file
81
cqwu/methods/webvpn/get_exam_calendar.py
Normal file
@ -0,0 +1,81 @@
|
||||
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
|
||||
|
||||
|
||||
class GetExamCalendar:
|
||||
async def get_exam_calendar(
|
||||
self: "cqwu.Client",
|
||||
exam_round: Union[str, ExamRound] = ExamRound.Supplementation,
|
||||
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"',
|
||||
}
|
||||
data = {
|
||||
'xn': str(xue_nian),
|
||||
'xq': str(xue_qi),
|
||||
'title': '',
|
||||
'xnxq': f'{xue_nian}{xue_qi}',
|
||||
'kslc': exam_round.value,
|
||||
}
|
||||
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')
|
||||
if not use_model:
|
||||
return jw_html
|
||||
return parse_html(jw_html, exam_round)
|
||||
|
||||
|
||||
def parse_html(html: str, exam_round: ExamRound) -> List[AiExam]:
|
||||
data: List[AiExam] = []
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
trs = soup.find_all("tr")[1:]
|
||||
for tr in trs:
|
||||
tds = tr.find_all("td")
|
||||
if len(tds) != 5:
|
||||
continue
|
||||
data.append(
|
||||
AiExam(
|
||||
name=tds[0].text,
|
||||
credit=float(tds[1].text),
|
||||
time=tds[2].text,
|
||||
position=tds[3].text,
|
||||
seat=tds[4].text,
|
||||
exam_round=exam_round,
|
||||
)
|
||||
)
|
||||
return data
|
17
cqwu/methods/webvpn/login_jwmis.py
Normal file
17
cqwu/methods/webvpn/login_jwmis.py
Normal file
@ -0,0 +1,17 @@
|
||||
from httpx import Response
|
||||
|
||||
import cqwu
|
||||
from cqwu.errors import CookieError
|
||||
|
||||
|
||||
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
|
||||
)
|
||||
if "教学管理服务平台" not in jw_html.text:
|
||||
raise CookieError
|
||||
return jw_html
|
@ -1,2 +1,6 @@
|
||||
from .calendar import AiCourse
|
||||
from .cp import CP, PublicCPRaw, CPGS
|
||||
from .epay import PayBill, PayBillPage
|
||||
from .exam import AiExam
|
||||
from .score import Score
|
||||
from .user import User
|
||||
|
36
cqwu/types/exam.py
Normal file
36
cqwu/types/exam.py
Normal file
@ -0,0 +1,36 @@
|
||||
import datetime
|
||||
from typing import Tuple
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from cqwu.enums import ExamRound
|
||||
|
||||
|
||||
class AiExam(BaseModel):
|
||||
name: str
|
||||
""" 课程名称 """
|
||||
credit: float
|
||||
""" 学分 """
|
||||
time: str
|
||||
""" 考试时间 """
|
||||
position: str
|
||||
""" 考试地点 """
|
||||
seat: str
|
||||
""" 座位号 """
|
||||
exam_round: ExamRound
|
||||
""" 考试轮次 """
|
||||
|
||||
@property
|
||||
def name_no_id(self) -> str:
|
||||
""" 获取课程名称(去除课程编号) """
|
||||
return self.name.split("]")[-1]
|
||||
|
||||
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)
|
||||
return start_time, end_time
|
@ -1,7 +1,7 @@
|
||||
httpx==0.23.3
|
||||
httpx==0.24.1
|
||||
lxml==4.9.2
|
||||
PyExecJS2==1.6.1
|
||||
beautifulsoup4==4.11.2
|
||||
beautifulsoup4==4.12.2
|
||||
qrcode==7.4.2
|
||||
pillow
|
||||
pydantic==1.10.5
|
||||
|
2
setup.py
2
setup.py
@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
||||
|
||||
setuptools.setup(
|
||||
name="cqwu", # 用自己的名替换其中的YOUR_USERNAME_
|
||||
version="0.0.10", # 包版本号,便于维护版本
|
||||
version="0.0.11", # 包版本号,便于维护版本
|
||||
author="omg-xtao", # 作者,可以写自己的姓名
|
||||
author_email="xtao@xtaolink.cn", # 作者联系方式,可写自己的邮箱地址
|
||||
description="A cqwu ehall client.", # 包的简述
|
||||
|
Loading…
Reference in New Issue
Block a user