mirror of
https://github.com/cqwu-ehall/cqwu-ehall.git
synced 2024-11-21 18:38:18 +00:00
✨ Support get score detail
This commit is contained in:
parent
35e34caa35
commit
e79b7dc429
@ -15,7 +15,7 @@ class Client(Methods):
|
||||
password: str = None,
|
||||
cookie: str = None,
|
||||
cookie_file_path: str = "cookie.txt",
|
||||
timeout: int = 10,
|
||||
timeout: int = 60,
|
||||
):
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
@ -8,3 +8,13 @@ class ExamRound(str, Enum):
|
||||
""" 分散考试 """
|
||||
Concentration = "3"
|
||||
""" 集中考试 """
|
||||
|
||||
|
||||
class ScoreSearchType(str, Enum):
|
||||
""" 成绩查询类型 """
|
||||
All = "1"
|
||||
"""入学以来"""
|
||||
XUENIAN = "2"
|
||||
"""学年"""
|
||||
XUEQI = "3"
|
||||
"""学期"""
|
||||
|
@ -7,3 +7,7 @@ class CQWUWebVPNError(CQWUEhallError):
|
||||
|
||||
class NoExamData(CQWUWebVPNError):
|
||||
""" 没有检索到对应的考试记录 """
|
||||
|
||||
|
||||
class NoScoreDetailData(CQWUWebVPNError):
|
||||
""" 没有检索到对应的成绩明细记录 """
|
||||
|
@ -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_score_detail import GetScoreDetail
|
||||
from .get_selected_courses import GetSelectedCourses
|
||||
from .login_jwmis import LoginJwmis
|
||||
from .login_webvpn import LoginWebVPN
|
||||
@ -12,6 +13,7 @@ class WebVPN(
|
||||
GetCalendar,
|
||||
GetCalendarChange,
|
||||
GetExamCalendar,
|
||||
GetScoreDetail,
|
||||
GetSelectedCourses,
|
||||
LoginJwmis,
|
||||
LoginWebVPN,
|
||||
|
136
cqwu/methods/webvpn/get_score_detail.py
Normal file
136
cqwu/methods/webvpn/get_score_detail.py
Normal file
@ -0,0 +1,136 @@
|
||||
from typing import Union, List
|
||||
|
||||
from bs4 import BeautifulSoup, Tag
|
||||
|
||||
import cqwu
|
||||
from cqwu.enums import ScoreSearchType
|
||||
from cqwu.errors import NoScoreDetailData
|
||||
from cqwu.types import ScoreDetail, ScoreDetailInfo, ScoreDetailCourse, ScoreDetailTotal
|
||||
|
||||
|
||||
class GetScoreDetail:
|
||||
async def get_score_detail(
|
||||
self: "cqwu.Client",
|
||||
search_type: Union[str, ScoreSearchType] = ScoreSearchType.XUEQI,
|
||||
xue_nian: int = None,
|
||||
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)
|
||||
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/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"',
|
||||
}
|
||||
data = {
|
||||
"sjxz": f"sjxz{search_type.value}",
|
||||
"ysyx": "yxcj",
|
||||
"zx": "1",
|
||||
"fx": "1",
|
||||
"btnExport": "%B5%BC%B3%F6",
|
||||
"rxnj": str(xue_nian),
|
||||
"xn": str(xue_nian),
|
||||
'xn1': str(xue_nian + 1),
|
||||
"xq": str(xue_qi),
|
||||
"ysyxS": "on",
|
||||
"sjxzS": "on",
|
||||
"zxC": "on",
|
||||
"fxC": "on",
|
||||
"xsjd": "1",
|
||||
}
|
||||
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')
|
||||
if not use_model:
|
||||
return jw_html
|
||||
return parse_html(jw_html)
|
||||
|
||||
|
||||
def parse_info(tag: Tag) -> ScoreDetailInfo:
|
||||
divs = tag.find_all("div")
|
||||
|
||||
def get_text(_tag) -> str:
|
||||
return (":".join(_tag.text.split(":")[1:])).strip()
|
||||
|
||||
return ScoreDetailInfo(
|
||||
college=get_text(divs[0]),
|
||||
major=get_text(divs[1]),
|
||||
level=get_text(divs[2]),
|
||||
class_name=get_text(divs[3]),
|
||||
student_id=get_text(divs[4]),
|
||||
student_name=get_text(divs[5]),
|
||||
date=get_text(divs[6]),
|
||||
)
|
||||
|
||||
|
||||
def parse_course(tag: Tag) -> List[ScoreDetailCourse]:
|
||||
trs = tag.find_all("tr")
|
||||
courses = []
|
||||
for tr in trs:
|
||||
tds = tr.find_all("td")
|
||||
course = ScoreDetailCourse(
|
||||
id=tds[0].text,
|
||||
name=tds[1].text,
|
||||
credit=tds[2].text,
|
||||
period=tds[3].text,
|
||||
type=tds[4].text,
|
||||
nature=tds[5].text,
|
||||
exam_method=tds[6].text,
|
||||
score=tds[7].text,
|
||||
score_point=tds[8].text,
|
||||
grade_point=tds[9].text,
|
||||
gp=tds[10].text,
|
||||
remark=tds[11].text,
|
||||
)
|
||||
courses.append(course)
|
||||
return courses
|
||||
|
||||
|
||||
def parse_total(tag: Tag) -> ScoreDetailTotal:
|
||||
tr = tag.find_all("tr")[-1]
|
||||
tds = tr.find_all("td")
|
||||
return ScoreDetailTotal(
|
||||
num=tds[1].text,
|
||||
credit=tds[2].text,
|
||||
get_credit=tds[3].text,
|
||||
grade_point=tds[4].text,
|
||||
gp=tds[5].text,
|
||||
gpa=tds[6].text,
|
||||
score_avg=tds[7].text,
|
||||
weighted_grade_avg=tds[8].text,
|
||||
)
|
||||
|
||||
|
||||
def parse_html(html: str) -> ScoreDetail:
|
||||
soup = BeautifulSoup(html, "lxml")
|
||||
group_div = soup.find("div", {"group": "group"})
|
||||
info = parse_info(group_div)
|
||||
tbody_s = soup.find_all("tbody")
|
||||
courses = []
|
||||
for tbody in tbody_s[:-1]:
|
||||
courses += parse_course(tbody)
|
||||
total = parse_total(tbody_s[-1])
|
||||
return ScoreDetail(info=info, courses=courses, total=total)
|
@ -4,4 +4,5 @@ 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 .user import User
|
||||
|
75
cqwu/types/score_detail.py
Normal file
75
cqwu/types/score_detail.py
Normal file
@ -0,0 +1,75 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ScoreDetailInfo(BaseModel):
|
||||
college: str = ""
|
||||
"""院(系)/部"""
|
||||
major: str = ""
|
||||
"""专业"""
|
||||
level: str = ""
|
||||
"""培养层次"""
|
||||
class_name: str = ""
|
||||
"""行政班级"""
|
||||
student_id: str = ""
|
||||
"""学号"""
|
||||
student_name: str = ""
|
||||
"""姓名"""
|
||||
date: str = ""
|
||||
"""打印时间"""
|
||||
|
||||
|
||||
class ScoreDetailCourse(BaseModel):
|
||||
id: int = 0
|
||||
"""序号"""
|
||||
name: str = ""
|
||||
"""课程/环节"""
|
||||
credit: float = 0.0
|
||||
"""学分"""
|
||||
period: int = 0
|
||||
"""总学时"""
|
||||
type: str = ""
|
||||
"""类别"""
|
||||
nature: str = ""
|
||||
"""修读性质"""
|
||||
exam_method: str = ""
|
||||
"""考核方式"""
|
||||
score: float = 0.0
|
||||
"""成绩"""
|
||||
score_point: float = 0.0
|
||||
"""获得学分"""
|
||||
grade_point: float = 0.0
|
||||
"""绩点"""
|
||||
gp: float = 0.0
|
||||
"""学分绩点"""
|
||||
remark: str = ""
|
||||
"""备注"""
|
||||
|
||||
|
||||
class ScoreDetailTotal(BaseModel):
|
||||
num: int = 0
|
||||
"""修读课程环节数"""
|
||||
credit: float = 0.0
|
||||
"""学分"""
|
||||
get_credit: float = 0.0
|
||||
"""获得学分"""
|
||||
grade_point: float = 0.0
|
||||
"""获得绩点"""
|
||||
gp: float = 0.0
|
||||
"""获得学分绩点"""
|
||||
gpa: float = 0.0
|
||||
"""平均学分绩点"""
|
||||
score_avg: float = 0.0
|
||||
"""平均成绩"""
|
||||
weighted_grade_avg: float = 0.0
|
||||
"""加权平均成绩"""
|
||||
|
||||
|
||||
class ScoreDetail(BaseModel):
|
||||
info: ScoreDetailInfo
|
||||
"""基本信息"""
|
||||
courses: List[ScoreDetailCourse]
|
||||
"""课程信息"""
|
||||
total: ScoreDetailTotal
|
||||
"""总计"""
|
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.16", # 包版本号,便于维护版本
|
||||
version="0.0.17", # 包版本号,便于维护版本
|
||||
author="omg-xtao", # 作者,可以写自己的姓名
|
||||
author_email="xtao@xtaolink.cn", # 作者联系方式,可写自己的邮箱地址
|
||||
description="A cqwu ehall client.", # 包的简述
|
||||
|
Loading…
Reference in New Issue
Block a user