跳转至

客户端

同步客户端

QBClient

QBClient(
    base_url: str,
    *,
    access_token: str | None = None,
    timeout: float = 30.0,
    check_version: bool = True
)

Bases: AuthMixin, QuestionsMixin, PapersMixin

同步题库客户端,面向 bot 账号。

Bot 账号使用管理员签发的 access token 认证,无需用户名密码登录。

用法::

client = QBClient("http://localhost:8080", access_token="bot-token-xxx")
questions = client.list_questions(category="T")
client.close()

或作为上下文管理器::

with QBClient("http://localhost:8080", access_token="bot-token-xxx") as c:
    ...

Args: base_url: API 服务地址,如 http://localhost:8080。 access_token: 管理员签发的 bot access token。 timeout: HTTP 请求超时时间(秒)。 check_version: 是否在首次连接时校验后端版本兼容性。

close

close() -> None

关闭 HTTP 连接。

health

health() -> HealthResponse

健康检查,无需认证。

version

version() -> VersionResponse

获取后端版本号。

me

me() -> UserProfile

获取当前登录用户信息。

search_users

search_users(
    q: str,
    *,
    limit: int | None = None,
    offset: int | None = None
) -> dict

按关键词搜索用户(用于审阅人分配)。

参数:

名称 类型 描述 默认
q str

搜索关键词,匹配 username / display_name。

必需
limit int | None

每页数量 (1-100)。

None
offset int | None

偏移量。

None

list_questions

list_questions(
    *,
    paper_id: str | None = None,
    category: str | None = None,
    tag: str | None = None,
    author: str | None = None,
    reviewer: str | None = None,
    assigned_reviewer_id: str | None = None,
    score_min: int | None = None,
    score_max: int | None = None,
    difficulty_tag: str | None = None,
    difficulty_min: int | None = None,
    difficulty_max: int | None = None,
    q: str | None = None,
    created_after: str | None = None,
    created_before: str | None = None,
    updated_after: str | None = None,
    updated_before: str | None = None,
    limit: int | None = None,
    offset: int | None = None
) -> PaginatedResponse[QuestionSummary]

分页查询题目。

参数:

名称 类型 描述 默认
paper_id str | None

按所属试卷过滤。

None
category str | None

按分类过滤 ("none" / "T" / "E")。

None
tag str | None

按标签过滤。

None
author str | None

按命题人过滤(支持逗号分隔多值)。

None
reviewer str | None

按审核人过滤(支持逗号分隔多值)。

None
assigned_reviewer_id str | None

按分配的审阅人 UUID 过滤。

None
score_min int | None

分值下限。

None
score_max int | None

分值上限。

None
difficulty_tag str | None

难度 tag,如 "human"。

None
difficulty_min int | None

难度下限 (1-10)。

None
difficulty_max int | None

难度上限 (1-10)。

None
q str | None

关键词,模糊匹配 description。

None
created_after str | None

创建时间下限 (ISO 8601)。

None
created_before str | None

创建时间上限 (ISO 8601)。

None
updated_after str | None

更新时间下限 (ISO 8601)。

None
updated_before str | None

更新时间上限 (ISO 8601)。

None
limit int | None

每页数量 (1-100),默认 20。

None
offset int | None

偏移量。

None

get_question

get_question(question_id: str) -> QuestionDetail

获取单个题目完整详情。

参数:

名称 类型 描述 默认
question_id str

题目 UUID。

必需

create_question

create_question(
    file: str | Path | IO[bytes],
    *,
    description: str,
    category: str | None = None,
    tags: list[str] | None = None
) -> QuestionCreateResult

上传新题目(zip 包)。

参数:

名称 类型 描述 默认
file str | Path | IO[bytes]

zip 文件路径或已打开的二进制文件对象。

必需
description str

题目描述。

必需
category str | None

分类 ("none" / "T" / "E")。

None
tags list[str] | None

标签列表。

None

update_question_description

update_question_description(
    question_id: str, description: str
) -> QuestionDetail

更新题目描述。

参数:

名称 类型 描述 默认
question_id str

题目 UUID。

必需
description str

题目描述。

必需

update_question_category

update_question_category(
    question_id: str, category: str
) -> QuestionDetail

更新题目分类。

update_question_tags

update_question_tags(
    question_id: str, tags: list[str]
) -> QuestionDetail

整体替换题目标签。

update_question_status

update_question_status(
    question_id: str, status: str
) -> QuestionDetail

更新题目状态。

update_question_author

update_question_author(
    question_id: str, author: str
) -> QuestionDetail

更新题目命题人。

update_question_reviewer_names

update_question_reviewer_names(
    question_id: str, reviewers: list[str]
) -> QuestionDetail

整体替换题目审题人显示名列表。

create_question_difficulty

create_question_difficulty(
    question_id: str,
    algorithm_tag: str,
    score: int,
    *,
    notes: str | None = None
) -> QuestionDetail

创建题目难度条目。

update_question_difficulty

update_question_difficulty(
    question_id: str,
    algorithm_tag: str,
    score: int,
    *,
    notes: str | None = None
) -> QuestionDetail

更新题目难度条目。

delete_question_difficulty

delete_question_difficulty(
    question_id: str, algorithm_tag: str
) -> QuestionDetail

删除题目难度条目。

replace_question_file

replace_question_file(
    question_id: str, file: str | Path | IO[bytes]
) -> QuestionFileReplaceResult

替换题目的 zip 文件内容,不修改元数据。

参数:

名称 类型 描述 默认
question_id str

题目 UUID。

必需
file str | Path | IO[bytes]

新的 zip 文件路径或二进制文件对象。

必需

delete_question

delete_question(question_id: str) -> QuestionDeleteResult

软删除题目。

get_question_tags

get_question_tags() -> list[str]

获取所有未删除题目使用中的标签列表。

get_difficulty_tags

get_difficulty_tags() -> list[str]

获取所有未删除题目使用中的难度标签列表。

download_question_bundle

download_question_bundle(
    question_ids: list[str], save_to: str | Path
) -> Path

批量打包下载题目原始文件到本地。

参数:

名称 类型 描述 默认
question_ids list[str]

题目 UUID 列表。

必需
save_to str | Path

本地保存路径。

必需

返回:

类型 描述
Path

保存的文件路径。

list_reviewers

list_reviewers(question_id: str) -> ReviewersResponse

获取题目的审阅人列表。

assign_reviewer

assign_reviewer(
    question_id: str, reviewer_id: str
) -> ReviewersResponse

分配审阅人到题目。

参数:

名称 类型 描述 默认
question_id str

题目 UUID。

必需
reviewer_id str

要分配的用户 UUID(必须为 user 角色且已启用)。

必需

remove_reviewer

remove_reviewer(
    question_id: str, reviewer_id: str
) -> ReviewersResponse

移除题目的审阅人。

list_papers

list_papers(
    *,
    question_id: str | None = None,
    category: str | None = None,
    tag: str | None = None,
    q: str | None = None,
    created_after: str | None = None,
    created_before: str | None = None,
    updated_after: str | None = None,
    updated_before: str | None = None,
    limit: int | None = None,
    offset: int | None = None
) -> PaginatedResponse[PaperSummary]

分页查询试卷。

参数:

名称 类型 描述 默认
question_id str | None

按包含的题目过滤。

None
category str | None

按包含题目的分类过滤。

None
tag str | None

按包含题目的标签过滤。

None
q str | None

关键词,匹配 description、title、subtitle。

None
created_after str | None

创建时间下限 (ISO 8601)。

None
created_before str | None

创建时间上限 (ISO 8601)。

None
updated_after str | None

更新时间下限 (ISO 8601)。

None
updated_before str | None

更新时间上限 (ISO 8601)。

None
limit int | None

每页数量 (1-100)。

None
offset int | None

偏移量。

None

get_paper

get_paper(paper_id: str) -> PaperDetail

获取试卷详情和题目列表。

create_paper

create_paper(
    *,
    description: str,
    title: str,
    subtitle: str,
    question_ids: list[str],
    file: str | Path | IO[bytes] | None = None
) -> PaperCreateResult

update_paper

update_paper(
    paper_id: str,
    *,
    description: str | None = None,
    title: str | None = None,
    subtitle: str | None = None,
    question_ids: list[str] | None = None
) -> PaperDetail

部分更新试卷元数据。

参数:

名称 类型 描述 默认
paper_id str

试卷 UUID。

必需
description str | None

试卷描述。

None
title str | None

试卷标题。

None
subtitle str | None

试卷副标题。

None
question_ids list[str] | None

题目列表(更新后按数组顺序重排)。

None

replace_paper_file

replace_paper_file(
    paper_id: str, file: str | Path | IO[bytes]
) -> PaperFileReplaceResult

替换试卷附录 zip 文件。

参数:

名称 类型 描述 默认
paper_id str

试卷 UUID。

必需
file str | Path | IO[bytes]

新的 zip 文件路径或二进制文件对象。

必需

delete_paper

delete_paper(paper_id: str) -> PaperDeleteResult

软删除试卷。

download_paper_bundle

download_paper_bundle(
    paper_ids: list[str], save_to: str | Path
) -> Path

批量打包下载试卷到本地。

参数:

名称 类型 描述 默认
paper_ids list[str]

试卷 UUID 列表。

必需
save_to str | Path

本地保存路径。

必需

返回:

类型 描述
Path

保存的文件路径。

异步客户端

AsyncQBClient

AsyncQBClient(
    base_url: str,
    *,
    access_token: str | None = None,
    timeout: float = 30.0,
    check_version: bool = True
)

Bases: AsyncAuthMixin, AsyncQuestionsMixin, AsyncPapersMixin

异步题库客户端,面向 bot 账号。

用法::

async with AsyncQBClient("http://localhost:8080", access_token="bot-token-xxx") as c:
    questions = await c.list_questions(category="T")

close async

close() -> None

health async

health() -> HealthResponse

健康检查,无需认证。

version async

version() -> VersionResponse

获取后端版本号。

me async

me() -> UserProfile

获取当前登录用户信息。

search_users async

search_users(
    q: str,
    *,
    limit: int | None = None,
    offset: int | None = None
) -> dict

按关键词搜索用户。参见 AuthMixin.search_users。

list_questions async

list_questions(
    *,
    paper_id: str | None = None,
    category: str | None = None,
    tag: str | None = None,
    author: str | None = None,
    reviewer: str | None = None,
    assigned_reviewer_id: str | None = None,
    score_min: int | None = None,
    score_max: int | None = None,
    difficulty_tag: str | None = None,
    difficulty_min: int | None = None,
    difficulty_max: int | None = None,
    q: str | None = None,
    created_after: str | None = None,
    created_before: str | None = None,
    updated_after: str | None = None,
    updated_before: str | None = None,
    limit: int | None = None,
    offset: int | None = None
) -> PaginatedResponse[QuestionSummary]

分页查询题目。参见 QuestionsMixin.list_questions。

get_question async

get_question(question_id: str) -> QuestionDetail

create_question async

create_question(
    file: str | Path | IO[bytes],
    *,
    description: str,
    category: str | None = None,
    tags: list[str] | None = None
) -> QuestionCreateResult

update_question_description async

update_question_description(
    question_id: str, description: str
) -> QuestionDetail

update_question_category async

update_question_category(
    question_id: str, category: str
) -> QuestionDetail

update_question_tags async

update_question_tags(
    question_id: str, tags: list[str]
) -> QuestionDetail

update_question_status async

update_question_status(
    question_id: str, status: str
) -> QuestionDetail

update_question_author async

update_question_author(
    question_id: str, author: str
) -> QuestionDetail

update_question_reviewer_names async

update_question_reviewer_names(
    question_id: str, reviewers: list[str]
) -> QuestionDetail

create_question_difficulty async

create_question_difficulty(
    question_id: str,
    algorithm_tag: str,
    score: int,
    *,
    notes: str | None = None
) -> QuestionDetail

update_question_difficulty async

update_question_difficulty(
    question_id: str,
    algorithm_tag: str,
    score: int,
    *,
    notes: str | None = None
) -> QuestionDetail

delete_question_difficulty async

delete_question_difficulty(
    question_id: str, algorithm_tag: str
) -> QuestionDetail

replace_question_file async

replace_question_file(
    question_id: str, file: str | Path | IO[bytes]
) -> QuestionFileReplaceResult

delete_question async

delete_question(question_id: str) -> QuestionDeleteResult

get_question_tags async

get_question_tags() -> list[str]

get_difficulty_tags async

get_difficulty_tags() -> list[str]

download_question_bundle async

download_question_bundle(
    question_ids: list[str], save_to: str | Path
) -> Path

list_reviewers async

list_reviewers(question_id: str) -> ReviewersResponse

assign_reviewer async

assign_reviewer(
    question_id: str, reviewer_id: str
) -> ReviewersResponse

remove_reviewer async

remove_reviewer(
    question_id: str, reviewer_id: str
) -> ReviewersResponse

list_papers async

list_papers(
    *,
    question_id: str | None = None,
    category: str | None = None,
    tag: str | None = None,
    q: str | None = None,
    created_after: str | None = None,
    created_before: str | None = None,
    updated_after: str | None = None,
    updated_before: str | None = None,
    limit: int | None = None,
    offset: int | None = None
) -> PaginatedResponse[PaperSummary]

分页查询试卷。参见 PapersMixin.list_papers。

get_paper async

get_paper(paper_id: str) -> PaperDetail

create_paper async

create_paper(
    *,
    description: str,
    title: str,
    subtitle: str,
    question_ids: list[str],
    file: str | Path | IO[bytes] | None = None
) -> PaperCreateResult

update_paper async

update_paper(
    paper_id: str,
    *,
    description: str | None = None,
    title: str | None = None,
    subtitle: str | None = None,
    question_ids: list[str] | None = None
) -> PaperDetail

replace_paper_file async

replace_paper_file(
    paper_id: str, file: str | Path | IO[bytes]
) -> PaperFileReplaceResult

delete_paper async

delete_paper(paper_id: str) -> PaperDeleteResult

download_paper_bundle async

download_paper_bundle(
    paper_ids: list[str], save_to: str | Path
) -> Path