Python 后端提交
Python 后端(FastAPI + FastMCP + ...)的初始版本号设定为 0.1.0,这是 uv 在 pypriject.toml 里给我自动设置的,我觉得有道理。
This commit is contained in:
79
njupt_api/zhengfang/zhengfang.py
Normal file
79
njupt_api/zhengfang/zhengfang.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from ddddocr import DdddOcr
|
||||
from playwright.async_api import Browser, BrowserContext, Page, Playwright
|
||||
|
||||
from njupt_api.baselib import PlayContextManager, logger
|
||||
from njupt_api.zhengfang import Course
|
||||
from njupt_api.zhengfang.createcourse import create_course_schedule
|
||||
from njupt_api.zhengfang.sso import SSO
|
||||
|
||||
|
||||
class ZhengFang(PlayContextManager):
|
||||
def __init__(
|
||||
self,
|
||||
playwright: Playwright = None,
|
||||
browser: Browser = None,
|
||||
context: BrowserContext = None,
|
||||
page: Page = None,
|
||||
) -> None:
|
||||
super().__init__(playwright, browser, context, page)
|
||||
|
||||
@classmethod
|
||||
async def init_from_sso(cls, sso: SSO) -> "ZhengFang":
|
||||
await sso.goto_zf()
|
||||
logger.info("从 SSO 进入正方教务系统。")
|
||||
return cls(sso.playwright, sso.browser, sso.context, sso.page)
|
||||
|
||||
async def login(self, username: str, password: str) -> bool:
|
||||
"""
|
||||
使用用户名和密码实现教务系统登录。
|
||||
|
||||
Returns:
|
||||
bool,表明登录是否成功。
|
||||
"""
|
||||
await self.page.goto("http://jwxt.njupt.edu.cn")
|
||||
|
||||
# 填充用户名和密码
|
||||
await self.page.fill("input#txtUserName", username)
|
||||
await self.page.fill("input#TextBox2", password)
|
||||
|
||||
# 处理验证码
|
||||
captcha_img = self.page.locator("img#icode")
|
||||
captcha_bytes = await captcha_img.screenshot()
|
||||
ocr = DdddOcr(show_ad=False)
|
||||
captcha_code = str(ocr.classification(captcha_bytes))
|
||||
logger.debug(f"识别到的验证码为: {captcha_code}")
|
||||
await self.page.fill("input#txtSecretCode", captcha_code)
|
||||
|
||||
async with self.page.expect_event("dialog", timeout=3000) as dialog_info:
|
||||
await self.page.click("input#Button1")
|
||||
dialog = await dialog_info.value
|
||||
if dialog.message == "请到信息维护中完善个人联系方式":
|
||||
await dialog.accept()
|
||||
logger.info(f"{username} | 登录正方教务系统成功。")
|
||||
self.isLogin = True
|
||||
return True
|
||||
if "验证码" in dialog.message:
|
||||
await dialog.accept()
|
||||
logger.warning(f"{username} | 验证码错误,自动重试...")
|
||||
return await self.login(username, password)
|
||||
await dialog.accept()
|
||||
logger.error(f"{username} | 登录失败,教务系统提示信息为: {dialog.message}")
|
||||
return False
|
||||
|
||||
async def get_class_schedule(self) -> list[Course]:
|
||||
await self.page.locator("a.top_link:has-text('公用信息')").click()
|
||||
await self.page.locator("a:has-text('班级课表查询')").click()
|
||||
sub_frame = self.page.frame_locator("iframe[name='zhuti']")
|
||||
logger.debug("获取班级课表。")
|
||||
return create_course_schedule(
|
||||
f"<table>{await sub_frame.locator('table#Table6').inner_html()}</table>",
|
||||
)
|
||||
|
||||
async def get_student_schedule(self) -> list[Course]:
|
||||
await self.page.locator("a.top_link:has-text('信息查询')").click()
|
||||
await self.page.locator("a:has-text('学生个人课表')").click()
|
||||
sub_frame = self.page.frame_locator("iframe[name='zhuti']")
|
||||
logger.debug("获取个人课表。")
|
||||
return create_course_schedule(
|
||||
f"<table>{await sub_frame.locator('table#Table1').inner_html()}</table>",
|
||||
)
|
||||
Reference in New Issue
Block a user