Compare commits

...

11 Commits

Author SHA1 Message Date
d8eabcb606 更新 README
NJUPT 似乎不准备维护晨跑查询接口,所以晨跑查询功能只好作废llo~(∠·ω< )⌒★ ……
2026-04-26 21:42:31 +08:00
405d7ff3b9 v0.1.5 早期准备
顺便检修一下本地开发环境中的问题。此 commit 无有效信息。
2026-04-26 20:28:42 +08:00
e7892a21a5 更新 changelog
更新 changelog,还有忘记改版本号惹 TT
2026-04-26 18:43:31 +08:00
5500c55b71 v0.1.4 修复初始化时 playwright 命令未找到、补充元数据
1. 初始化时,运行 playwright install chromium 时,现在会指定 sys.excuteable 来避免未找到
playwright 命令。
2. 遵循 AI 和 pypi 的建议在 pyproject.toml 中补充了一些元数据。
2026-04-26 18:40:21 +08:00
2aee776bad v0.1.3 更新命令行入口别名、增加 CHANGELOG.md
pyproject.toml 中的命令行入口别名从 suanapi 改为 njupt-suan-api,与包名一致。这可以方便使用 uvx
部署运行本项目。
另外,增加了 CHANGELOG.md 文件以记录主要变更。
2026-04-26 17:45:58 +08:00
c32995edd4 v0.1.2 修复发布问题
v0.1.2 开始发布至 pypi,可以从 pypi 下载本项目然后四处部署了~
另外添加了 README.md 文件。
2026-04-26 00:00:21 +08:00
247d9f089c v0.1.1 项目结构与构建流程更新
涉及的文件很多,但主要是 PyCharm 自动重构 + 手动微调。

1. 使用 src-layout 结构组织项目,旨在更加规范地管理各个子包,并方便组织代码。
2. 可以使用 uv build 构建项目至 wheel。
3. 可以在 /webui 目录下使用 pnpm run build 来构建 WebUI,构建产物会生成在
/src/njupt_suan_api/static 目录下。同时,uv build 时会一同包含此目录下的 WebUI
构建产物,避免在用户侧执行构建。
4. 使用 typer 实现了命令行入口,代码位于 manage.py;命令行实现了现 main.py 的全部功能。
2026-04-25 21:30:11 +08:00
8db1d27758 尝试 0.1.1 版本 + 命令行 cli 构建中
尝试用 Typer 构建命令行入口 manage.py 。
在 pyproject.toml 中完成有关设计,计划使用 suanapi 命令调用 manage.py 。
2026-04-24 16:23:51 +08:00
16ff315a30 提升依赖版本、安装 typer 和 rich
提升依赖版本:
- uvicorn 和 pydantic

安装新依赖:
- typer 和 rich
2026-04-23 23:27:52 +08:00
df17a73c31 让顶部导航栏真正可用
添加顶部导航栏内的链接
2026-04-22 23:31:45 +08:00
285a92d9cb 修改 .gitignore
添加一个脚本目录,里面放的是芒果给自己准备的妙妙小道具(
2026-04-22 23:31:07 +08:00
45 changed files with 545 additions and 122 deletions

5
.gitignore vendored
View File

@@ -10,3 +10,8 @@ temp/
# Virtual environments # Virtual environments
.venv .venv
.scripts
# vite 构建产物
src/njupt_suan_api/static/

View File

@@ -2,6 +2,7 @@
<module type="PYTHON_MODULE" version="4"> <module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.venv" /> <excludeFolder url="file://$MODULE_DIR$/.venv" />
</content> </content>
<orderEntry type="jdk" jdkName="uv (NJUPT-API-Suan)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="uv (NJUPT-API-Suan)" jdkType="Python SDK" />

27
CHANGELOG.md Normal file
View File

@@ -0,0 +1,27 @@
# CHANGELOG
此文件中记录 NJUPT Suan API 的主要的版本变更日志。
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 种更新类别Added、Changed、Deprecated、Removed、Fixed、Security。
## [Unreleased]
等待中...
## [0.1.4] - 2026-04-26
### Fixed
- 修复从命令行运行 `njupt-suan-api init` 时由于 playwright 命令未找到而失败的问题。
## [0.1.3] - 2026-04-26
### Changed
- 更改项目的 cli 入口别名与包名 `njupt-suan-api` 一致,以支持更简单地通过 uv tool / uvx 部署本项目。
- 依赖更新:
- fastapi -> 0.136.1
- typer -> 0.25.0

21
LICENSE.txt Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 MangoFanFanw
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,42 @@
# NJUPT Suan API
NJUPT Suan API 是一个 FastAPI 项目,目标在于实现对 NJUPT南京邮电大学的信息获取 API 和 MCP 服务。
`pyproject.toml` 中,本项目的包名,以及命令行入口名为 `njupt-suan-api`
## 文档
虽然项目还没个两样,但是文档其实也没个两样 ~~(什么东西)~~
[中文名叫芒果酸](https://suan.mangofanfan.cn) - `suan.mangofanfan.cn`
## 功能
| 计划功能(芒果画饼中) | 支持进度 |
|---------------|------------------|
| 教务系统 - 课程表获取 | ✅ |
| 教务系统 - 课程获取 | ⌛️ |
| 教务系统 - 成绩获取 | ⌛️ |
| 体育部系统 - 早锻炼获取 | ❌(无从破解微信小程序 QAQ |
## 运行
建议查阅文档了解更多部署方式。
如需从源代码直接运行的话,项目的源码位于 `src/njupt_suan_api` 目录下,`main.py` 是旧的入口文件,可以直接传统方式启动。
`manage.py` 是命令行入口,提供了完整的帮助信息。
`server.py` 是 FastAPI app 所在文件,可以使用 uvicorn 命令启动。
另外如需从源代码启动项目,你需要自行构建 WebUI。
```bash
cd webui/
pnpm install
pnpm run build
```
AI 说 `npm install` 然后 `npm run build` 也可以,但我还没试过,你可以帮我试试(?)
vite 的构建产物会放在 `src/njupt_suan_api/static` 目录下,**构建产物不会被 git 管理,但是会被项目打包进 wheel。**

View File

@@ -1,26 +1,65 @@
[project] [project]
name = "njupt-suan-api" name = "njupt-suan-api"
version = "0.1.0" version = "0.1.5"
description = "API and MCP server for NJUPT infomation ~" description = "API and MCP server for NJUPT infomation ~"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
license = "MIT"
authors = [
{ name = "MangoFanFanw", email = "mangofanfanw@icloud.com" }
]
maintainers = [
{ name = "MangoFanFanw", email = "mangofanfanw@icloud.com" }
]
classifiers = [
"Development Status :: 3 - Alpha",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.13",
]
dependencies = [ dependencies = [
"aiofiles>=25.1.0", "aiofiles>=25.1.0",
"beautifulsoup4>=4.14.3", "beautifulsoup4>=4.14.3",
"ddddocr>=1.6.1", "ddddocr>=1.6.1",
"fastapi>=0.135.3", "fastapi>=0.136.1",
"fastmcp>=3.2.0", "fastmcp>=3.2.0",
"loguru>=0.7.3", "loguru>=0.7.3",
"mcp>=1.27.0", "mcp>=1.27.0",
"playwright>=1.58.0", "playwright>=1.58.0",
"pydantic>=2.12.5", "pydantic>=2.13.3",
"rich>=15.0.0",
"sqlalchemy>=2.0.49", "sqlalchemy>=2.0.49",
"sqlmodel>=0.0.38", "sqlmodel>=0.0.38",
"uvicorn>=0.43.0", "typer>=0.25.0",
"uvicorn>=0.46.0",
"watchfiles>=1.1.1", "watchfiles>=1.1.1",
"websockets>=16.0", "websockets>=16.0",
] ]
[project.urls]
Homepage = "https://suan.mangofanfan.cn"
Documentation = "https://suan.mangofanfan.cn"
Repository = "https://github.com/mangofanfan/NJUPT-Suan-API.git"
Changelog = "https://github.com/mangofanfan/NJUPT-Suan-API/blob/master/CHANGELOG.md"
Issues = "https://github.com/mangofanfan/NJUPT-Suan-API/issues"
[project.scripts]
njupt-suan-api = "njupt_suan_api.manage:app"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.sdist]
artifacts = ["src/njupt_suan_api/static/**"]
[tool.hatch.build.targets.wheel]
packages = ["src/njupt_suan_api"]
artifacts = ["src/njupt_suan_api/static/**"]
[tool.ruff] [tool.ruff]
preview = true preview = true
line-length = 120 line-length = 120

View File

@@ -1 +0,0 @@
from .__version__ import __version__

View File

@@ -18,7 +18,7 @@ class Config:
async def load_json(self) -> None: async def load_json(self) -> None:
""" """
Toml 配置文件中读取配置 异步从配置文件中读取配置
""" """
logger.debug("异步读取配置文件。") logger.debug("异步读取配置文件。")
async with aiofiles.open(file=CONFIG_PATH, mode="r") as f: async with aiofiles.open(file=CONFIG_PATH, mode="r") as f:
@@ -49,7 +49,7 @@ class Config:
def init_config(self) -> None: def init_config(self) -> None:
""" """
重新初始化 Toml 配置文件这会重置所有配置 重新初始化配置文件这会重置所有配置
""" """
logger.warning("初始化配置文件,这会重置所有配置。") logger.warning("初始化配置文件,这会重置所有配置。")
self._doc.clear() self._doc.clear()
@@ -59,7 +59,7 @@ class Config:
doc_system["host"] = "0.0.0.0" doc_system["host"] = "0.0.0.0"
doc_system["port"] = 8000 doc_system["port"] = 8000
doc_system["reload"] = True doc_system["reload"] = False
doc_system["public_host"] = "http://127.0.0.1:8000" doc_system["public_host"] = "http://127.0.0.1:8000"
doc_schedule["playwright_headless"] = True doc_schedule["playwright_headless"] = True
@@ -78,7 +78,7 @@ class Config:
async def save_json(self) -> None: async def save_json(self) -> None:
""" """
异步保存 Toml 配置文件 异步保存配置文件
""" """
logger.debug("异步保存配置文件。") logger.debug("异步保存配置文件。")
async with aiofiles.open(file=CONFIG_PATH, mode="w") as f: async with aiofiles.open(file=CONFIG_PATH, mode="w") as f:

View File

@@ -7,7 +7,7 @@ from playwright.async_api import (
async_playwright, async_playwright,
) )
from . import config from .config import config
class PlayContextManager: class PlayContextManager:

View File

@@ -1,5 +1,4 @@
from njupt_api.baselib import PlayContextManager, logger from ..baselib import PlayContextManager, logger
from .exc import LoginError from .exc import LoginError

View File

@@ -1,8 +1,7 @@
from ddddocr import DdddOcr from ddddocr import DdddOcr
from playwright.async_api import Browser, BrowserContext, Page, Playwright from playwright.async_api import Browser, BrowserContext, Page, Playwright
from njupt_api.baselib import PlayContextManager, logger from ..baselib import PlayContextManager, logger
from .createcourse import create_course_schedule from .createcourse import create_course_schedule
from .exc import LoginError from .exc import LoginError
from .sso import SSO from .sso import SSO

View File

@@ -0,0 +1,13 @@
from .message import ALREADY_INIT_MESSAGE, INIT_STAGE_MESSAGE, NOT_INIT_MESSAGE, RUN_CHECK_MESSAGE, TOKEN_CHECK_MESSAGE
from .path import DATA_DIR, TEMP_DIR, WORKSPACE_DIR
__all__ = [
WORKSPACE_DIR,
DATA_DIR,
TEMP_DIR,
NOT_INIT_MESSAGE,
ALREADY_INIT_MESSAGE,
RUN_CHECK_MESSAGE,
TOKEN_CHECK_MESSAGE,
INIT_STAGE_MESSAGE,
]

View File

@@ -0,0 +1,25 @@
NOT_INIT_MESSAGE = """
❓ 当前目录或指定目录下[yellow]似乎还没有执行过初始化命令[/yellow]。
❓ 你也许需要先执行 [green]suanapi init[/green] 。
"""
ALREADY_INIT_MESSAGE = """
❕ 当前目录或指定目录下[yellow]似乎已经执行初始化命令过[/yellow]。
❕ 你也许需要先删除已经存在的 [blue]data[/blue] 和 [blue]temp[/blue] 目录。
"""
RUN_CHECK_MESSAGE = """
[bright_black]运行时的配置可能来自命令行参数、配置文件以及默认值,列在这里供你检查。[/bright_black]
[cyan]主机名[/cyan] - [cyan]host[/cyan] - {host}
[cyan]端口[/cyan] - [cyan]port[/cyan] - {port}
[cyan]自动重启[/cyan] - [cyan]reload[/cyan] - {reload}
[bright_black]NJUPT Suan API 会很快启动。使用 [green]Ctrl + C[/green] 以退出。[/bright_black]
"""
TOKEN_CHECK_MESSAGE = """
🔐 [green] 令牌 - [/green]{token}
🔐 [green]有效期 - [/green]无限
✅ WebUI 设计的令牌 cookie 有效期为一天,所以你每天都需要重新登录一次 WebUI这并非令牌本身的有效期。
"""
INIT_STAGE_MESSAGE = "[cyan]- {stage} / 3 -[/cyan] [bright_black]{message}[/bright_black]"

View File

@@ -0,0 +1,5 @@
from pathlib import Path
WORKSPACE_DIR = Path.cwd()
DATA_DIR = WORKSPACE_DIR / "data"
TEMP_DIR = WORKSPACE_DIR / "temp"

View File

@@ -1,8 +1,12 @@
"""
main.py 未来不再作为项目入口日后可能会被删除请参照 README 使用命令行作为入口或直接使用 uvicorn 命令
"""
from pathlib import Path from pathlib import Path
from secrets import token_urlsafe from secrets import token_urlsafe
from njupt_api.baselib import config, logger from njupt_suan_api.api.baselib import config, logger
from router import __version__ from njupt_suan_api.router import __version__
DATA_DIR = Path.cwd() / "data" DATA_DIR = Path.cwd() / "data"
TEMP_DIR = Path.cwd() / "temp" TEMP_DIR = Path.cwd() / "temp"
@@ -10,7 +14,7 @@ TEMP_DIR = Path.cwd() / "temp"
if __name__ == "__main__": if __name__ == "__main__":
try: try:
with open(file=Path.cwd() / "njupt_api" / "art.txt", mode="r", encoding="utf-8") as f: with open(file=Path.cwd() / "api" / "art.txt", mode="r", encoding="utf-8") as f:
print(f.read().format(__version__)) # noqa:T201 print(f.read().format(__version__)) # noqa:T201
except FileNotFoundError: except FileNotFoundError:
pass pass
@@ -77,7 +81,7 @@ if __name__ == "__main__":
host=host, host=host,
port=port, port=port,
reload=reload, reload=reload,
reload_dirs=["njupt_api", "router"], reload_dirs=["api", "router"],
access_log=False, access_log=False,
log_level="critical", log_level="critical",
timeout_graceful_shutdown=2, timeout_graceful_shutdown=2,

View File

@@ -0,0 +1,185 @@
import subprocess
import sys
from secrets import token_urlsafe
import typer
from rich.console import Console
from rich.panel import Panel
from njupt_suan_api.api.baselib import config
from njupt_suan_api.cli import (
ALREADY_INIT_MESSAGE,
DATA_DIR,
INIT_STAGE_MESSAGE,
NOT_INIT_MESSAGE,
RUN_CHECK_MESSAGE,
TEMP_DIR,
TOKEN_CHECK_MESSAGE,
WORKSPACE_DIR,
)
from njupt_suan_api.router import __version__
console = Console()
app = typer.Typer(
name="NJUPT-Suan-API",
help="NJUPT Suan API 部署与管理工具",
rich_markup_mode="rich",
no_args_is_help=True,
)
def version_callback(value: bool = False) -> None:
if value:
console.print(f"NJUPT Suan API [green]v.{__version__}[/green]")
if __version__ == "dev":
console.print("""
[bright_black]显示的版本为 dev ?这是因为你正在从源代码运行 cli 入口manage.py。[/bright_black]
[bright_black]从[green]已安装版本[/green]中运行 [green]suanapi --version[/green] 可以正确获取版本号。[/bright_black]""")
raise typer.Exit
@app.callback(invoke_without_command=True)
def main(
version: bool = typer.Option(
False,
"--version",
"-v",
help="显示版本号并退出,没有其他命令会被执行。",
callback=version_callback,
is_eager=True, # 优先处理,避免触发其他逻辑
),
) -> None:
"""
CLI 入口回调,所有子命令执行前都会经过这里。
可以在这里放全局初始化(如日志级别、环境检查)。
"""
# 没有 --version 时就正常放行,继续执行子命令
console.print(
"[bright_black]NJUPT Suan API 仍然处于极早期的阶段。如果遇到任何问题,请告诉芒果帆帆喵![/bright_black]\n"
)
@app.command()
def init(force: bool = typer.Option(False, "--force", "-f", help="强制初始化,可能导致问题,不建议使用。")) -> None:
"""
初始化 NJUPT Suan API [green]工作目录[/green]。(可能需要较长时间)
会在当前目录或指定目录下创建新文件,并尝试安装 playwright chromium。
视网络情况,安装过程可能(几乎必然)需要较长时间。
如果已存在 data 和/或 temp 目录,初始化会失败。你也可以强制初始化,但可能导致先前的数据丢失。
Raises:
typer.Exit: 如果初始化失败,返回 1。
"""
if (DATA_DIR.exists() or TEMP_DIR.exists()) and not force:
console.print(Panel(ALREADY_INIT_MESSAGE, title="工作目录已存在"))
console.print("[bright_black]如果你想要强制初始化,使用 [green]suanapi init -f[/green] 命令。[/bright_black]")
raise typer.Exit(code=1)
# 1 创建 data 和 temp 目录
DATA_DIR.mkdir(parents=True, exist_ok=True)
TEMP_DIR.mkdir(parents=True, exist_ok=True)
console.print(INIT_STAGE_MESSAGE.format(stage=1, message="工作目录 data 和 temp 已创建。"))
# 2 初始化配置文件
try:
config.sync_load_json()
except FileNotFoundError:
config.init_config()
config.sync_create_json()
console.print(INIT_STAGE_MESSAGE.format(stage=2, message="已初始化配置并创建配置文件。"))
else:
console.print(INIT_STAGE_MESSAGE.format(stage=2, message="配置文件已存在,跳过配置初始化。"))
# 3 执行 uv run playwright install chromium
console.print("[bright_black]即将安装 playwright 的 chromium这可能是耗时最长的部分。[/bright_black]")
cp3 = subprocess.run([sys.executable, "-m", "playwright", "install", "chromium"], cwd=WORKSPACE_DIR)
if cp3.returncode != 0:
console.print("[yellow]运行 playwright install chromuim 失败,双是什么原因呢?[/yellow]")
raise typer.Exit(code=cp3.returncode)
console.print(INIT_STAGE_MESSAGE.format(stage=3, message="已安装 playwright chromium。"))
console.print("[green]初始化完成。接下来可以执行 suanapi run 来启动 NJUPT Suan API。[/green]")
console.print(
"[bright_black]初始化只需要执行一次即可,以后即使更新 NJUPT Suan API也无再需重复执行。[/bright_black]"
)
@app.command()
def token(reset: bool = typer.Option(False, "--reload", "-r", help="强制重新生成令牌,即使令牌已存在。")) -> None:
"""
查看或重新生成[green]管理后端令牌[/green]。
需要先运行过 init 初始化目录。
Raises:
typer.Exit: 如果未初始化,返回 1。
"""
token_ = None
# 首先检查数据目录是否存在
if not DATA_DIR.exists():
console.print(Panel(NOT_INIT_MESSAGE, title="数据目录不存在"))
raise typer.Exit(code=1)
# 确认存在后再判断是否需要重新生成令牌
if not reset:
try:
with open(file=DATA_DIR / "token.txt", mode="r") as f:
token_ = f.read()
except FileNotFoundError:
pass
if not token_:
console.print("[yellow]重新生成令牌...[/yellow]")
token_ = token_urlsafe(32)
with open(file=DATA_DIR / "token.txt", mode="w") as f:
f.write(token_)
panel = Panel(TOKEN_CHECK_MESSAGE.format(token=token_), title="WebUI 令牌")
console.print(panel)
@app.command()
def run(
host: str | None = typer.Option(None, "--host", help="监听主机名,默认 0.0.0.0。"),
port: int | None = typer.Option(None, "--port", help="监听端口,默认 8000。"),
reload: bool | None = typer.Option(None, "--reload", "-r", help="在检测到代码变化时自动重启,默认 False。"),
) -> None:
"""
运行 NJUPT Suan API。
可接收运行参数。若不提供,则尝试使用配置文件中的值,最后回退到默认值。
Raises:
typer.Exit: 如果未初始化,返回 1。
"""
if host is None:
host = config.get("system", "host", "0.0.0.0")
if port is None:
port = config.get("system", "port", 8000)
if reload is None:
reload = config.get("system", "reload", False)
if not DATA_DIR.exists() or not TEMP_DIR.exists():
console.print(Panel(NOT_INIT_MESSAGE, title="工作目录不存在"))
raise typer.Exit(code=1)
token(False)
console.print(Panel(RUN_CHECK_MESSAGE.format(host=host, port=port, reload=reload), title="运行前检查"))
import uvicorn
uvicorn.run(
"njupt_suan_api.server:app",
host=host,
port=port,
reload=reload,
access_log=False,
log_level="critical",
timeout_graceful_shutdown=2,
)
if __name__ == "__main__":
app()

View File

@@ -0,0 +1,14 @@
from .__version__ import __version__ as __version__
from .admin_router import admin_router
from .api_router import api_router
from .mcp_router import mcp_app
from .webui_router import ASSETS_DIR, webui_router
__all__ = [
admin_router,
api_router,
mcp_app,
webui_router,
__version__,
ASSETS_DIR,
]

View File

@@ -6,11 +6,11 @@ from fastapi import APIRouter, Depends
from pydantic import BaseModel from pydantic import BaseModel
from sqlmodel import Session, delete, select from sqlmodel import Session, delete, select
from njupt_api.baselib import config, logger from njupt_suan_api.api.baselib import config, logger
from njupt_api.zhengfang import LoginError, course_list_serializer, jwxt from njupt_suan_api.api.zhengfang import LoginError, course_list_serializer, jwxt
from router.enhance.auth import verify_token from njupt_suan_api.router.enhance.auth import verify_token
from router.enhance.lib import AliasDto, ReturnDto, TestDto, get_session from njupt_suan_api.router.enhance.lib import AliasDto, ReturnDto, TestDto, get_session
from router.enhance.model import Alias, Course from njupt_suan_api.router.enhance.model import Alias, Course
class ValidateTokenDto(BaseModel): class ValidateTokenDto(BaseModel):

View File

@@ -5,15 +5,10 @@ from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from sqlmodel import Session, select from sqlmodel import Session, select
from njupt_api.baselib import logger from njupt_suan_api.api.baselib import logger
from njupt_api.zhengfang import ( from njupt_suan_api.api.zhengfang import LoginError, course_dict_serializer, course_list_serializer, jwxt
course_dict_serializer, from njupt_suan_api.router.enhance.lib import ReturnDto, ScheduleQueryDto, apply_enhance, get_session
course_list_serializer, from njupt_suan_api.router.enhance.model import Course
jwxt,
)
from njupt_api.zhengfang.exc import LoginError
from router.enhance.lib import ReturnDto, ScheduleQueryDto, apply_enhance, get_session
from router.enhance.model import Course
TEMP_DIR = Path.cwd() / "temp" TEMP_DIR = Path.cwd() / "temp"

View File

@@ -0,0 +1,8 @@
from .lib import ReturnDto, apply_enhance
from .model import create_db_and_tables
__all__ = [
ReturnDto,
apply_enhance,
create_db_and_tables,
]

View File

@@ -7,8 +7,8 @@ from typing import Sequence
from sqlmodel import Session, select from sqlmodel import Session, select
from njupt_api.baselib import logger from njupt_suan_api.api.baselib import logger
from router.enhance.model import Alias, engine from njupt_suan_api.router.enhance.model import Alias, engine
def apply_alias(courses: list[dict]) -> list[dict]: def apply_alias(courses: list[dict]) -> list[dict]:

View File

@@ -4,7 +4,7 @@ from typing import Any, Generator, Literal
from pydantic import BaseModel from pydantic import BaseModel
from sqlmodel import Session from sqlmodel import Session
from njupt_api.baselib import config from njupt_suan_api.api.baselib import config
from .alias import apply_alias from .alias import apply_alias
from .model import engine from .model import engine

View File

@@ -3,7 +3,7 @@ from typing import Optional
from sqlalchemy import JSON, Column from sqlalchemy import JSON, Column
from sqlmodel import Field, SQLModel, create_engine from sqlmodel import Field, SQLModel, create_engine
sqlite_file_name = "data/njupt_api.db" sqlite_file_name = "data/njupt-api.db"
sqlite_url = f"sqlite:///{sqlite_file_name}" sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, connect_args={"check_same_thread": False}) engine = create_engine(sqlite_url, connect_args={"check_same_thread": False})

View File

@@ -7,7 +7,7 @@ from uuid import uuid4
from playwright.async_api import ViewportSize from playwright.async_api import ViewportSize
from njupt_api.baselib import PlayContextManager, logger from njupt_suan_api.api.baselib import PlayContextManager, logger
TEMP_DIR = Path.cwd() / "temp" TEMP_DIR = Path.cwd() / "temp"

View File

@@ -7,15 +7,10 @@ from mcp.types import ToolAnnotations
from pydantic import Field from pydantic import Field
from sqlmodel import Session, select from sqlmodel import Session, select
from njupt_api.baselib import LoggingMiddleware, logger from njupt_suan_api.api.baselib import LoggingMiddleware, logger
from njupt_api.zhengfang import ( from njupt_suan_api.api.zhengfang import LoginError, course_dict_serializer, course_list_serializer, jwxt
course_dict_serializer, from njupt_suan_api.router.enhance.lib import ReturnDto, apply_enhance
course_list_serializer, from njupt_suan_api.router.enhance.model import Course, engine
jwxt,
)
from njupt_api.zhengfang.exc import LoginError
from router.enhance.lib import ReturnDto, apply_enhance
from router.enhance.model import Course, engine
mcp = FastMCP("NJUPT API Suan") mcp = FastMCP("NJUPT API Suan")

View File

@@ -4,8 +4,10 @@ import aiofiles
from fastapi import APIRouter from fastapi import APIRouter
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
WEBUI_INDEX = Path.cwd() / "webui" / "dist" / "index.html" STATIC_DIR = Path(__file__).parent.parent / "static"
SCHEDULE_INDEX = Path.cwd() / "webui" / "dist" / "index-schedule.html" WEBUI_INDEX = STATIC_DIR / "index.html"
SCHEDULE_INDEX = STATIC_DIR / "index-schedule.html"
ASSETS_DIR = STATIC_DIR / "assets"
webui_router = APIRouter(prefix="/webui") webui_router = APIRouter(prefix="/webui")

View File

@@ -12,20 +12,15 @@ from fastapi.staticfiles import StaticFiles
from fastmcp.utilities.lifespan import combine_lifespans from fastmcp.utilities.lifespan import combine_lifespans
from watchfiles import awatch from watchfiles import awatch
from njupt_api.baselib import ( from njupt_suan_api.api.baselib import (
LogRecord, LogRecord,
config, config,
log_buffer, log_buffer,
log_record_serialize, log_record_serialize,
logger, logger,
) )
from router import __version__ from njupt_suan_api.router import ASSETS_DIR, __version__, admin_router, api_router, mcp_app, webui_router
from router.admin_router import admin_router from njupt_suan_api.router.enhance import ReturnDto, create_db_and_tables
from router.api_router import api_router
from router.enhance.lib import ReturnDto
from router.enhance.model import create_db_and_tables
from router.mcp_router import mcp_app
from router.webui_router import webui_router
DATA_DIR = Path.cwd() / "data" DATA_DIR = Path.cwd() / "data"
@@ -166,7 +161,7 @@ app.include_router(webui_router)
app.mount("/mcp", mcp_app) app.mount("/mcp", mcp_app)
app.mount( app.mount(
"/assets", "/assets",
StaticFiles(directory=Path.cwd() / "webui" / "dist" / "assets"), StaticFiles(directory=ASSETS_DIR),
name="assets", name="assets",
) )

150
uv.lock generated
View File

@@ -333,7 +333,7 @@ wheels = [
[[package]] [[package]]
name = "fastapi" name = "fastapi"
version = "0.136.0" version = "0.136.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-doc" }, { name = "annotated-doc" },
@@ -342,9 +342,9 @@ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
{ name = "typing-inspection" }, { name = "typing-inspection" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/4e/d9/e66315807e41e69e7f6a1b42a162dada2f249c5f06ad3f1a95f84ab336ef/fastapi-0.136.0.tar.gz", hash = "sha256:cf08e067cc66e106e102d9ba659463abfac245200752f8a5b7b1e813de4ff73e", size = 396607, upload-time = "2026-04-16T11:47:13.623Z" } sdist = { url = "https://files.pythonhosted.org/packages/5d/45/c130091c2dfa061bbfe3150f2a5091ef1adf149f2a8d2ae769ecaf6e99a2/fastapi-0.136.1.tar.gz", hash = "sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f", size = 397448, upload-time = "2026-04-23T16:49:44.046Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/26/a3/0bd5f0cdb0bbc92650e8dc457e9250358411ee5d1b65e42b6632387daf81/fastapi-0.136.0-py3-none-any.whl", hash = "sha256:8793d44ec7378e2be07f8a013cf7f7aa47d6327d0dfe9804862688ec4541a6b4", size = 117556, upload-time = "2026-04-16T11:47:11.922Z" }, { url = "https://files.pythonhosted.org/packages/5a/ff/2e4eca3ade2c22fe1dea7043b8ee9dabe47753349eb1b56a202de8af6349/fastapi-0.136.1-py3-none-any.whl", hash = "sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f", size = 117683, upload-time = "2026-04-23T16:49:42.437Z" },
] ]
[[package]] [[package]]
@@ -701,8 +701,8 @@ wheels = [
[[package]] [[package]]
name = "njupt-suan-api" name = "njupt-suan-api"
version = "0.1.0" version = "0.1.3"
source = { virtual = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aiofiles" }, { name = "aiofiles" },
{ name = "beautifulsoup4" }, { name = "beautifulsoup4" },
@@ -713,8 +713,10 @@ dependencies = [
{ name = "mcp" }, { name = "mcp" },
{ name = "playwright" }, { name = "playwright" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "rich" },
{ name = "sqlalchemy" }, { name = "sqlalchemy" },
{ name = "sqlmodel" }, { name = "sqlmodel" },
{ name = "typer" },
{ name = "uvicorn" }, { name = "uvicorn" },
{ name = "watchfiles" }, { name = "watchfiles" },
{ name = "websockets" }, { name = "websockets" },
@@ -725,15 +727,17 @@ requires-dist = [
{ name = "aiofiles", specifier = ">=25.1.0" }, { name = "aiofiles", specifier = ">=25.1.0" },
{ name = "beautifulsoup4", specifier = ">=4.14.3" }, { name = "beautifulsoup4", specifier = ">=4.14.3" },
{ name = "ddddocr", specifier = ">=1.6.1" }, { name = "ddddocr", specifier = ">=1.6.1" },
{ name = "fastapi", specifier = ">=0.135.3" }, { name = "fastapi", specifier = ">=0.136.1" },
{ name = "fastmcp", specifier = ">=3.2.0" }, { name = "fastmcp", specifier = ">=3.2.0" },
{ name = "loguru", specifier = ">=0.7.3" }, { name = "loguru", specifier = ">=0.7.3" },
{ name = "mcp", specifier = ">=1.27.0" }, { name = "mcp", specifier = ">=1.27.0" },
{ name = "playwright", specifier = ">=1.58.0" }, { name = "playwright", specifier = ">=1.58.0" },
{ name = "pydantic", specifier = ">=2.12.5" }, { name = "pydantic", specifier = ">=2.13.3" },
{ name = "rich", specifier = ">=15.0.0" },
{ name = "sqlalchemy", specifier = ">=2.0.49" }, { name = "sqlalchemy", specifier = ">=2.0.49" },
{ name = "sqlmodel", specifier = ">=0.0.38" }, { name = "sqlmodel", specifier = ">=0.0.38" },
{ name = "uvicorn", specifier = ">=0.43.0" }, { name = "typer", specifier = ">=0.25.0" },
{ name = "uvicorn", specifier = ">=0.46.0" },
{ name = "watchfiles", specifier = ">=1.1.1" }, { name = "watchfiles", specifier = ">=1.1.1" },
{ name = "websockets", specifier = ">=16.0" }, { name = "websockets", specifier = ">=16.0" },
] ]
@@ -1024,7 +1028,7 @@ wheels = [
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.13.2" version = "2.13.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-types" }, { name = "annotated-types" },
@@ -1032,9 +1036,9 @@ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
{ name = "typing-inspection" }, { name = "typing-inspection" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/09/e5/06d23afac9973109d1e3c8ad38e1547a12e860610e327c05ee686827dc37/pydantic-2.13.2.tar.gz", hash = "sha256:b418196607e61081c3226dcd4f0672f2a194828abb9109e9cfb84026564df2d1", size = 843836, upload-time = "2026-04-17T09:31:59.636Z" } sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/77/ca/b45c378e6e8d0b90577288b533e04e95b7afd61bb1d51b6c263176435489/pydantic-2.13.2-py3-none-any.whl", hash = "sha256:a525087f4c03d7e7456a3de89b64cd693d2229933bb1068b9af6befd5563694e", size = 471947, upload-time = "2026-04-17T09:31:57.541Z" }, { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" },
] ]
[package.optional-dependencies] [package.optional-dependencies]
@@ -1044,58 +1048,58 @@ email = [
[[package]] [[package]]
name = "pydantic-core" name = "pydantic-core"
version = "2.46.2" version = "2.46.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/43/bb/4742f05b739b2478459bb16fa8470549518c802e06ddcf3f106c5081315e/pydantic_core-2.46.2.tar.gz", hash = "sha256:37bb079f9ee3f1a519392b73fda2a96379b31f2013c6b467fe693e7f2987f596", size = 471269, upload-time = "2026-04-17T09:10:07.017Z" } sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/07/2b/662e48254479a2d3450ba24b1e25061108b64339794232f503990c519144/pydantic_core-2.46.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d26e9eea3715008a09a74585fe9becd0c67fbb145dc4df9756d597d7230a652c", size = 2101762, upload-time = "2026-04-17T09:10:13.87Z" }, { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109, upload-time = "2026-04-20T14:41:24.219Z" },
{ url = "https://files.pythonhosted.org/packages/73/ab/bafd7c7503757ccc8ec4d1911e106fe474c629443648c51a88f08b0fe91a/pydantic_core-2.46.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48b36e3235140510dc7861f0cd58b714b1cdd3d48f75e10ce52e69866b746f10", size = 1951814, upload-time = "2026-04-17T09:12:25.934Z" }, { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820, upload-time = "2026-04-20T14:42:14.236Z" },
{ url = "https://files.pythonhosted.org/packages/92/cc/7549c2d57ba2e9a42caa5861a2d398dbe31c02c6aca783253ace59ce84f8/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36b1f99dc451f1a3981f236151465bcf995bbe712d0727c9f7b236fe228a8133", size = 1977329, upload-time = "2026-04-17T09:13:37.605Z" }, { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785, upload-time = "2026-04-20T14:42:31.648Z" },
{ url = "https://files.pythonhosted.org/packages/18/50/7ed4a8a0d478a4dca8f0134a5efa7193f03cc8520dd4c9509339fb2e5002/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8641c8d535c2d95b45c2e19b646ecd23ebba35d461e0ae48a3498277006250ab", size = 2051832, upload-time = "2026-04-17T09:12:49.771Z" }, { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761, upload-time = "2026-04-20T14:40:37.093Z" },
{ url = "https://files.pythonhosted.org/packages/dc/16/bb35b193741c0298ddc5f5e4234269efdc0c65e2bcd198aa0de9b68845e4/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20fb194788a0a50993e87013e693494ba183a2af5b44e99cf060bbae10912b11", size = 2233127, upload-time = "2026-04-17T09:11:04.449Z" }, { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989, upload-time = "2026-04-20T14:42:59.254Z" },
{ url = "https://files.pythonhosted.org/packages/91/a5/98f4b637149185addea19e1785ea20c373cca31b202f589111d8209d9873/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9262d11d0cd11ee3303a95156939402bed6cedfe5ed0e331b95a283a4da6eb8b", size = 2297418, upload-time = "2026-04-17T09:11:25.929Z" }, { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975, upload-time = "2026-04-20T14:41:32.287Z" },
{ url = "https://files.pythonhosted.org/packages/36/90/93a5d21990b152da7b7507b7fddb0b935f6a0984d57ac3ec45a6e17777a2/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac204542736aa295fa25f713b7fad6fc50b46ab7764d16087575c85f085174f3", size = 2093735, upload-time = "2026-04-17T09:12:06.908Z" }, { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325, upload-time = "2026-04-20T14:42:47.685Z" },
{ url = "https://files.pythonhosted.org/packages/14/22/b8b1ffdddf08b4e84380bcb67f41dbbf4c171377c1d36fc6290794bb2094/pydantic_core-2.46.2-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9a7c43a0584742dface3ca0daf6f719d46c1ac2f87cf080050f9ae052c75e1b2", size = 2127570, upload-time = "2026-04-17T09:11:53.906Z" }, { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368, upload-time = "2026-04-20T14:41:22.766Z" },
{ url = "https://files.pythonhosted.org/packages/c6/26/e60d72b4e2d0ce1fa811044a974412ac1c567fe067d97b3e6b290530786e/pydantic_core-2.46.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fd05e1edb6a90ad446fa268ab09e59202766b837597b714b2492db11ee87fab9", size = 2183524, upload-time = "2026-04-17T09:11:30.092Z" }, { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908, upload-time = "2026-04-20T14:42:52.044Z" },
{ url = "https://files.pythonhosted.org/packages/35/32/36bec7584a1eefb17dec4dfa1c946d3fe4440f466c5705b8adfda69c9a9f/pydantic_core-2.46.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:91155b110788b5501abc7ea954f1d08606219e4e28e3c73a94124307c06efb80", size = 2185408, upload-time = "2026-04-17T09:10:57.228Z" }, { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422, upload-time = "2026-04-20T14:43:29.55Z" },
{ url = "https://files.pythonhosted.org/packages/fc/d6/1a5689d873620efd67d6b163db0c444c056adb0849b5bc33e2b9f09665a6/pydantic_core-2.46.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e4e2c72a529fa03ff228be1d2b76944013f428220b764e03cc50ada67e17a42c", size = 2335171, upload-time = "2026-04-17T09:11:43.369Z" }, { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709, upload-time = "2026-04-20T14:42:16.134Z" },
{ url = "https://files.pythonhosted.org/packages/3e/8e/675104802abe8ef502b072050ee5f2e915251aa1a3af87e1015ce31ec42d/pydantic_core-2.46.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:56291ec1a11c3499890c99a8fd9053b47e60fe837a77ec72c0671b1b8b3dce24", size = 2362743, upload-time = "2026-04-17T09:10:18.333Z" }, { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428, upload-time = "2026-04-20T14:41:10.974Z" },
{ url = "https://files.pythonhosted.org/packages/8d/bc/86c5dde4fa6e24467680eef5047da3c1a19be0a527d0d8e14aa76b39307c/pydantic_core-2.46.2-cp313-cp313-win32.whl", hash = "sha256:b50f9c5f826ddca1246f055148df939f5f3f2d0d96db73de28e2233f22210d4c", size = 1958074, upload-time = "2026-04-17T09:12:38.622Z" }, { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601, upload-time = "2026-04-20T14:41:42.128Z" },
{ url = "https://files.pythonhosted.org/packages/2a/97/2537e8c1282b2c4eb062580c0d7a4339e10b072b803d1ee0b7f1f0a5c22c/pydantic_core-2.46.2-cp313-cp313-win_amd64.whl", hash = "sha256:251a57788823230ca8cbc99e6245d1a2ed6e180ec4864f251c94182c580c7f2e", size = 2071741, upload-time = "2026-04-17T09:13:32.405Z" }, { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517, upload-time = "2026-04-20T14:43:36.096Z" },
{ url = "https://files.pythonhosted.org/packages/da/aa/2ee75798706f9dbc4e76dbe59e41a396c5c311e3d6223b9cf6a5fa7780be/pydantic_core-2.46.2-cp313-cp313-win_arm64.whl", hash = "sha256:315d32d1a71494d6b4e1e14a9fa7a4329597b4c4340088ad7e1a9dafbeed92a9", size = 2025955, upload-time = "2026-04-17T09:10:15.567Z" }, { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802, upload-time = "2026-04-20T14:43:38.507Z" },
{ url = "https://files.pythonhosted.org/packages/d0/96/a50ccb6b539ae780f73cea74905468777680e30c6c3bdf714b9d4c116ea0/pydantic_core-2.46.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:4f59b45f3ef8650c0c736a57f59031d47ed9df4c0a64e83796849d7d14863a2d", size = 2097111, upload-time = "2026-04-17T09:10:49.617Z" }, { url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614, upload-time = "2026-04-20T14:44:38.374Z" },
{ url = "https://files.pythonhosted.org/packages/34/5f/fdead7b3afa822ab6e5a18ee0ecffd54937de1877c01ed13a342e0fb3f07/pydantic_core-2.46.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a075a29ebef752784a91532a1a85be6b234ccffec0a9d7978a92696387c3da6", size = 1951904, upload-time = "2026-04-17T09:12:32.062Z" }, { url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896, upload-time = "2026-04-20T14:40:53.996Z" },
{ url = "https://files.pythonhosted.org/packages/95/e0/1c5d547e550cdab1bec737492aa08865337af6fe7fc9b96f7f45f17d9519/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d12d786e30c04a9d307c5d7080bf720d9bac7f1668191d8e37633a9562749e2", size = 1978667, upload-time = "2026-04-17T09:11:35.589Z" }, { url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314, upload-time = "2026-04-20T14:41:49.446Z" },
{ url = "https://files.pythonhosted.org/packages/0e/cb/665ce629e218c8228302cb94beff4f6531082a2c87d3ecc3d5e63a26f392/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d5e6d6343b0b5dcacb3503b5de90022968da8ed0ab9ab39d3eda71c20cbf84e", size = 2046721, upload-time = "2026-04-17T09:11:47.725Z" }, { url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133, upload-time = "2026-04-20T14:42:23.574Z" },
{ url = "https://files.pythonhosted.org/packages/77/e9/6cb2cf60f54c1472bbdfce19d957553b43dbba79d1d7b2930a195c594785/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:233eebac0999b6b9ba76eb56f3ec8fce13164aa16b6d2225a36a79e0f95b5973", size = 2228483, upload-time = "2026-04-17T09:12:08.837Z" }, { url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726, upload-time = "2026-04-20T14:44:22.816Z" },
{ url = "https://files.pythonhosted.org/packages/0d/2a/93e018dd5571f781ebaeda8c0cf65398489d5bee9b1f484df0b6149b43b9/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cc0eee720dd2f14f3b7c349469402b99ad81a174ab49d3533974529e9d93992", size = 2294663, upload-time = "2026-04-17T09:12:52.053Z" }, { url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214, upload-time = "2026-04-20T14:44:48.792Z" },
{ url = "https://files.pythonhosted.org/packages/5e/4f/49e57ca55c770c93d9bb046666a54949b42e3c9099a0c5fe94557873fe30/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83ee76bf2c9910513dbc19e7d82367131fa7508dedd6186a462393071cc11059", size = 2098742, upload-time = "2026-04-17T09:13:45.472Z" }, { url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927, upload-time = "2026-04-20T14:41:40.196Z" },
{ url = "https://files.pythonhosted.org/packages/c6/b0/6e46b5cd3332af665f794b8cdeea206618a8630bd9e7bcc36864518fce81/pydantic_core-2.46.2-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:d61db38eb4ee5192f0c261b7f2d38e420b554df8912245e3546aee5c45e2fd78", size = 2125922, upload-time = "2026-04-17T09:12:54.304Z" }, { url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789, upload-time = "2026-04-20T14:41:15.868Z" },
{ url = "https://files.pythonhosted.org/packages/06/d1/40850c81585be443a2abfdf7f795f8fae831baf8e2f9b2133c8246ac671c/pydantic_core-2.46.2-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f09a713d17bcd55da8ab02ebd9110c5246a49c44182af213b5212800af8bc83", size = 2183000, upload-time = "2026-04-17T09:10:59.027Z" }, { url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815, upload-time = "2026-04-20T14:44:25.152Z" },
{ url = "https://files.pythonhosted.org/packages/04/af/8493d7dfa03ebb7866909e577c6aa65ea0de7377b86023cc51d0c8e11db3/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:30cacc5fb696e64b8ef6fd31d9549d394dd7d52760db072eecb98e37e3af1677", size = 2180335, upload-time = "2026-04-17T09:12:57.01Z" }, { url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608, upload-time = "2026-04-20T14:41:28.933Z" },
{ url = "https://files.pythonhosted.org/packages/72/5b/1f6a344c4ffdf284da41c6067b82d5ebcbd11ce1b515ae4b662d4adb6f61/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:7ccfb105fcfe91a22bbb5563ad3dc124bc1aa75bfd2e53a780ab05f78cdf6108", size = 2330002, upload-time = "2026-04-17T09:12:02.958Z" }, { url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968, upload-time = "2026-04-20T14:42:03.962Z" },
{ url = "https://files.pythonhosted.org/packages/25/ff/9a694126c12d6d2f48a0cafa6f8eef88ef0d8825600e18d03ff2e896c3b2/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:13ffef637dc8370c249e5b26bd18e9a80a4fca3d809618c44e18ec834a7ca7a8", size = 2359920, upload-time = "2026-04-17T09:10:27.764Z" }, { url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842, upload-time = "2026-04-20T14:41:35.52Z" },
{ url = "https://files.pythonhosted.org/packages/51/c8/3a35c763d68a9cb2675eb10ef242cf66c5d4701b28ae12e688d67d2c180e/pydantic_core-2.46.2-cp314-cp314-win32.whl", hash = "sha256:1b0ab6d756ca2704a938e6c31b53f290c2f9c10d3914235410302a149de1a83e", size = 1953701, upload-time = "2026-04-17T09:13:30.021Z" }, { url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661, upload-time = "2026-04-20T14:41:00.042Z" },
{ url = "https://files.pythonhosted.org/packages/1a/6a/f2726a780365f7dfd89d62036f984f7acb99978c60c5e1fa7c0cb898ed11/pydantic_core-2.46.2-cp314-cp314-win_amd64.whl", hash = "sha256:99ebade8c9ada4df975372d8dd25883daa0e379a05f1cd0c99aa0c04368d01a6", size = 2071867, upload-time = "2026-04-17T09:10:39.205Z" }, { url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686, upload-time = "2026-04-20T14:43:16.471Z" },
{ url = "https://files.pythonhosted.org/packages/e1/79/76baacb9feba3d7c399b245ca1a29c74ea0db04ea693811374827eec2290/pydantic_core-2.46.2-cp314-cp314-win_arm64.whl", hash = "sha256:de87422197cf7f83db91d89c86a21660d749b3cd76cd8a45d115b8e675670f02", size = 2017252, upload-time = "2026-04-17T09:10:26.175Z" }, { url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907, upload-time = "2026-04-20T14:43:31.732Z" },
{ url = "https://files.pythonhosted.org/packages/f1/3b/77c26938f817668d9ad9bab1a905cb23f11d9a3d4bf724d429b3e55a8eaf/pydantic_core-2.46.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:236f22b4a206b5b61db955396b7cf9e2e1ff77f372efe9570128ccfcd6a525eb", size = 2094545, upload-time = "2026-04-17T09:12:19.339Z" }, { url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047, upload-time = "2026-04-20T14:42:37.982Z" },
{ url = "https://files.pythonhosted.org/packages/fe/de/42c13f590e3c260966aa49bcdb1674774f975467c49abd51191e502bea28/pydantic_core-2.46.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c2012f64d2cd7cca50f49f22445aa5a88691ac2b4498ee0a9a977f8ca4f7289f", size = 1933953, upload-time = "2026-04-17T09:09:55.889Z" }, { url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329, upload-time = "2026-04-20T14:43:49.655Z" },
{ url = "https://files.pythonhosted.org/packages/4e/84/ebe3ebb3e2d8db656937cfa6f97f544cb7132f2307a4a7dfdcd0ea102a12/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d07d6c63106d3a9c9a333e2636f9c82c703b1a9e3b079299e58747964e4fdb72", size = 1974435, upload-time = "2026-04-17T09:10:12.371Z" }, { url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847, upload-time = "2026-04-20T14:42:10.737Z" },
{ url = "https://files.pythonhosted.org/packages/b9/15/0bf51ca6709477cd4ef86148b6d7844f3308f029eac361dd0383f1e17b1a/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c326a2b4b85e959d9a1fc3a11f32f84611b6ec07c053e1828a860edf8d068208", size = 2031113, upload-time = "2026-04-17T09:10:00.752Z" }, { url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742, upload-time = "2026-04-20T14:40:44.262Z" },
{ url = "https://files.pythonhosted.org/packages/02/ae/b7b5af9b79db036d9e61a44c481c17a213dc8fc4b8b71fe6875a72fc778b/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac8a65e798f2462552c00d2e013d532c94d646729dda98458beaf51f9ec7b120", size = 2236325, upload-time = "2026-04-17T09:10:33.227Z" }, { url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235, upload-time = "2026-04-20T14:41:27.307Z" },
{ url = "https://files.pythonhosted.org/packages/a6/ae/ecef7477b5a03d4a499708f7e75d2836452ebb70b776c2d64612b334f57a/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a3c2bc1cc8164bedbc160b7bb1e8cc1e8b9c27f69ae4f9ae2b976cdae02b2dd", size = 2278135, upload-time = "2026-04-17T09:10:23.287Z" }, { url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633, upload-time = "2026-04-20T14:44:33.271Z" },
{ url = "https://files.pythonhosted.org/packages/db/e4/2f9d82faa47af6c39fc3f120145fd915971e1e0cb6b55b494fad9fdf8275/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69aa5e10b7e8b1bb4a6888650fd12fcbf11d396ca11d4a44de1450875702830", size = 2109071, upload-time = "2026-04-17T09:11:06.149Z" }, { url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679, upload-time = "2026-04-20T14:44:41.065Z" },
{ url = "https://files.pythonhosted.org/packages/f1/9c/677cf10873fbd0b116575ab7b97c90482b21564f8a8040beb18edef7a577/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4e6df5c3301e65fb42bc5338bf9a1027a02b0a31dc7f54c33775229af474daf0", size = 2106028, upload-time = "2026-04-17T09:10:51.525Z" }, { url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342, upload-time = "2026-04-20T14:42:50.144Z" },
{ url = "https://files.pythonhosted.org/packages/d6/53/6a06183544daba51c059123a2064a99039df25f115a06bdb26f2ea177038/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c2f6e32548ac8d559b47944effcf8ae4d81c161f6b6c885edc53bc08b8f192d", size = 2164816, upload-time = "2026-04-17T09:11:56.187Z" }, { url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208, upload-time = "2026-04-20T14:42:08.133Z" },
{ url = "https://files.pythonhosted.org/packages/57/6f/10fcdd9e3eca66fc828eef0f6f5850f2dd3bca2c59e6e041fb8bc3da39be/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:b089a81c58e6ea0485562bbbbbca4f65c0549521606d5ef27fba217aac9b665a", size = 2166130, upload-time = "2026-04-17T09:10:03.804Z" }, { url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237, upload-time = "2026-04-20T14:42:25.412Z" },
{ url = "https://files.pythonhosted.org/packages/29/83/92d3fd0e0156cad2e3cb5c26de73794af78ac9fa0c22ab666e566dd67061/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:7f700a6d6f64112ae9193709b84303bbab84424ad4b47d0253301aabce9dfc70", size = 2316605, upload-time = "2026-04-17T09:12:45.249Z" }, { url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540, upload-time = "2026-04-20T14:40:40.313Z" },
{ url = "https://files.pythonhosted.org/packages/97/f1/facffdb970981068219582e499b8d0871ed163ffcc6b347de5c412669e4c/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:67db6814beaa5fefe91101ec7eb9efda613795767be96f7cf58b1ca8c9ca9972", size = 2358385, upload-time = "2026-04-17T09:09:54.657Z" }, { url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556, upload-time = "2026-04-20T14:41:09.427Z" },
{ url = "https://files.pythonhosted.org/packages/8b/a1/b8160b2f22b2199467bc68581a4ed380643c16b348a27d6165c6c242d694/pydantic_core-2.46.2-cp314-cp314t-win32.whl", hash = "sha256:32fbc7447be8e3be99bf7869f7066308f16be55b61f9882c2cefc7931f5c7664", size = 1942373, upload-time = "2026-04-17T09:12:59.594Z" }, { url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756, upload-time = "2026-04-20T14:41:25.717Z" },
{ url = "https://files.pythonhosted.org/packages/0d/90/db89acabe5b150e11d1b59fe3d947dda2ef6abbfef5c82f056ff63802f5d/pydantic_core-2.46.2-cp314-cp314t-win_amd64.whl", hash = "sha256:b317a2b97019c0b95ce99f4f901ae383f40132da6706cdf1731066a73394c25c", size = 2052078, upload-time = "2026-04-17T09:10:19.96Z" }, { url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305, upload-time = "2026-04-20T14:43:18.627Z" },
{ url = "https://files.pythonhosted.org/packages/97/32/e19b83ceb07a3f1bb21798407790bbc9a31740158fd132b94139cb84e16c/pydantic_core-2.46.2-cp314-cp314t-win_arm64.whl", hash = "sha256:7dcb9d40930dfad7ab6b20bcc6ca9d2b030b0f347a0cd9909b54bd53ead521b1", size = 2016941, upload-time = "2026-04-17T09:12:34.447Z" }, { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310, upload-time = "2026-04-20T14:41:01.778Z" },
] ]
[[package]] [[package]]
@@ -1350,6 +1354,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" },
] ]
[[package]]
name = "shellingham"
version = "1.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
]
[[package]] [[package]]
name = "soupsieve" name = "soupsieve"
version = "2.8.3" version = "2.8.3"
@@ -1449,6 +1462,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
] ]
[[package]]
name = "typer"
version = "0.25.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-doc" },
{ name = "click" },
{ name = "rich" },
{ name = "shellingham" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7b/27/ede8cec7596e0041ba7e7b80b47d132562f56ff454313a16f6084e555c9f/typer-0.25.0.tar.gz", hash = "sha256:123eaf9f19bb40fd268310e12a542c0c6b4fab9c98d9d23342a01ff95e3ce930", size = 120150, upload-time = "2026-04-26T08:46:14.767Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/72/193d4e586ec5a4db834a36bbeb47641a62f951f114ffd0fe5b1b46e8d56f/typer-0.25.0-py3-none-any.whl", hash = "sha256:ac01b48823d3db9a83c9e164338057eadbb1c9957a2a6b4eeb486669c560b5dc", size = 55993, upload-time = "2026-04-26T08:46:15.889Z" },
]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.15.0" version = "4.15.0"
@@ -1481,15 +1509,15 @@ wheels = [
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.44.0" version = "0.46.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
{ name = "h11" }, { name = "h11" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz", hash = "sha256:6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", size = 86947, upload-time = "2026-04-06T09:23:22.826Z" } sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758, upload-time = "2026-04-23T07:16:00.151Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/23/a5bbd9600dd607411fa644c06ff4951bec3a4d82c4b852374024359c19c0/uvicorn-0.44.0-py3-none-any.whl", hash = "sha256:ce937c99a2cc70279556967274414c087888e8cec9f9c94644dfca11bd3ced89", size = 69425, upload-time = "2026-04-06T09:23:21.524Z" }, { url = "https://files.pythonhosted.org/packages/31/a3/5b1562db76a5a488274b2332a97199b32d0442aca0ed193697fd47786316/uvicorn-0.46.0-py3-none-any.whl", hash = "sha256:bbebbcbed972d162afca128605223022bedd345b7bc7855ce66deb31487a9048", size = 70926, upload-time = "2026-04-23T07:15:58.355Z" },
] ]
[[package]] [[package]]

View File

@@ -33,6 +33,7 @@ declare module 'vue' {
NMenu: typeof import('naive-ui')['NMenu'] NMenu: typeof import('naive-ui')['NMenu']
NMessageProvider: typeof import('naive-ui')['NMessageProvider'] NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NP: typeof import('naive-ui')['NP'] NP: typeof import('naive-ui')['NP']
NPopover: typeof import('naive-ui')['NPopover']
NSelect: typeof import('naive-ui')['NSelect'] NSelect: typeof import('naive-ui')['NSelect']
NSwitch: typeof import('naive-ui')['NSwitch'] NSwitch: typeof import('naive-ui')['NSwitch']
NTag: typeof import('naive-ui')['NTag'] NTag: typeof import('naive-ui')['NTag']
@@ -65,6 +66,7 @@ declare global {
const NMenu: typeof import('naive-ui')['NMenu'] const NMenu: typeof import('naive-ui')['NMenu']
const NMessageProvider: typeof import('naive-ui')['NMessageProvider'] const NMessageProvider: typeof import('naive-ui')['NMessageProvider']
const NP: typeof import('naive-ui')['NP'] const NP: typeof import('naive-ui')['NP']
const NPopover: typeof import('naive-ui')['NPopover']
const NSelect: typeof import('naive-ui')['NSelect'] const NSelect: typeof import('naive-ui')['NSelect']
const NSwitch: typeof import('naive-ui')['NSwitch'] const NSwitch: typeof import('naive-ui')['NSwitch']
const NTag: typeof import('naive-ui')['NTag'] const NTag: typeof import('naive-ui')['NTag']

View File

@@ -5,9 +5,28 @@
<div id="nav-title"> <div id="nav-title">
<n-h3 class="no-margin-bottom">NJUPT Suan API</n-h3> <n-h3 class="no-margin-bottom">NJUPT Suan API</n-h3>
</div> </div>
<a href="https://suan.mangofanfan.cn/" target="_blank">
<n-button secondary type="primary">概览</n-button> <n-button secondary type="primary">概览</n-button>
</a>
<n-popover trigger="hover">
<template #trigger>
<n-button secondary type="info">源码</n-button>
</template>
<n-p class="no-margin-bottom"
>NJUPT Suan API 的代码开源在以下两个地方后面的那个可能更快</n-p
>
<n-flex justify="center">
<a href="https://github.com/mangofanfan/NJUPT-Suan-API" target="_blank">
<n-button secondary type="info">GitHub</n-button> <n-button secondary type="info">GitHub</n-button>
</a>
<a href="https://gitea.mangofanfan.cn/SuanDev/NJUPT-Suan-API" target="_blank">
<n-button secondary type="success">Mango Gitea</n-button>
</a>
</n-flex>
</n-popover>
<a href="https://mangofanfan.cn/" target="_blank">
<n-button secondary type="error">芒果</n-button> <n-button secondary type="error">芒果</n-button>
</a>
</div> </div>
</template> </template>

View File

@@ -35,9 +35,10 @@ export default defineConfig({
input: { input: {
index: path.resolve(__dirname, 'index.html'), index: path.resolve(__dirname, 'index.html'),
'index-schedule': path.resolve(__dirname, 'index-schedule.html'), 'index-schedule': path.resolve(__dirname, 'index-schedule.html'),
}
}, },
}, },
outDir: "../src/njupt_suan_api/static"
},
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)), '@': fileURLToPath(new URL('./src', import.meta.url)),