🎉初次提交
This commit is contained in:
605
router/jwc/__init__.py
Normal file
605
router/jwc/__init__.py
Normal file
@@ -0,0 +1,605 @@
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from provider.aufe.jwc import JWCClient
|
||||
from provider.aufe.jwc.depends import get_jwc_client
|
||||
from provider.loveac.authme import AuthmeResponse
|
||||
from router.jwc.model import (
|
||||
AcademicInfoResponse,
|
||||
TrainingPlanInfoResponse,
|
||||
CourseListResponse,
|
||||
ExamInfoAPIResponse,
|
||||
AllTermsResponse,
|
||||
TermScoreAPIResponse,
|
||||
FetchTermScoreRequest,
|
||||
ScheduleResponse,
|
||||
FetchScheduleRequest,
|
||||
)
|
||||
from router.common_model import ErrorResponse
|
||||
from .evaluate_model import (
|
||||
EvaluationStatsResponse,
|
||||
CurrentCourseInfoResponse,
|
||||
TaskOperationResponse,
|
||||
InitializeResponse,
|
||||
CourseInfo,
|
||||
TaskStatusEnum,
|
||||
EvaluationStatsData,
|
||||
CurrentCourseInfoData,
|
||||
TaskOperationData,
|
||||
InitializeData,
|
||||
)
|
||||
from .evaluate import (
|
||||
get_task_manager,
|
||||
remove_task_manager,
|
||||
)
|
||||
from datetime import datetime
|
||||
from loguru import logger
|
||||
|
||||
jwc_router = APIRouter(prefix="/api/v1/jwc")
|
||||
invite_tokens = []
|
||||
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/fetch_academic_info",
|
||||
summary="获取学业信息",
|
||||
response_model=AcademicInfoResponse | AuthmeResponse | ErrorResponse,
|
||||
)
|
||||
async def fetch_academic_info(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""获取学术信息(课程数量、绩点等)"""
|
||||
try:
|
||||
result = await client.fetch_academic_info()
|
||||
|
||||
# 检查是否是AuthmeResponse(认证错误)
|
||||
if isinstance(result, AuthmeResponse):
|
||||
return result
|
||||
|
||||
# 使用新的错误检测机制
|
||||
response = AcademicInfoResponse.from_data(
|
||||
data=result,
|
||||
success_message="学业信息获取成功",
|
||||
error_message="获取学业信息失败,网络请求多次重试后仍无法连接教务系统,请稍后重试或联系管理员",
|
||||
)
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return ErrorResponse(message=f"获取学业信息时发生系统错误:{str(e)}", code=500)
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/fetch_education_plan_info",
|
||||
summary="获取培养方案信息",
|
||||
response_model=TrainingPlanInfoResponse | AuthmeResponse | ErrorResponse,
|
||||
)
|
||||
async def fetch_education_plan_info(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""获取培养方案信息"""
|
||||
try:
|
||||
result = await client.fetch_training_plan_info()
|
||||
|
||||
# 检查是否是AuthmeResponse(认证错误)
|
||||
if isinstance(result, AuthmeResponse):
|
||||
return result
|
||||
|
||||
# 使用新的错误检测机制
|
||||
response = TrainingPlanInfoResponse.from_data(
|
||||
data=result,
|
||||
success_message="培养方案信息获取成功",
|
||||
error_message="获取培养方案信息失败,网络请求多次重试后仍无法连接教务系统,请稍后重试或联系管理员",
|
||||
)
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return ErrorResponse(
|
||||
message=f"获取培养方案信息时发生系统错误:{str(e)}", code=500
|
||||
)
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/fetch_evaluation_course_list",
|
||||
summary="获取评教课程列表",
|
||||
response_model=CourseListResponse | AuthmeResponse | ErrorResponse,
|
||||
)
|
||||
async def fetch_evaluation_course_list(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""获取评教课程列表"""
|
||||
try:
|
||||
result = await client.fetch_evaluation_course_list()
|
||||
|
||||
# 检查是否是AuthmeResponse(认证错误)
|
||||
if isinstance(result, AuthmeResponse):
|
||||
return result
|
||||
|
||||
# 对于列表类型,使用特殊的检查逻辑
|
||||
if result and len(result) > 0:
|
||||
# 检查第一个元素是否是错误数据
|
||||
first_course = result[0]
|
||||
if (
|
||||
hasattr(first_course, "evaluated_people")
|
||||
and first_course.evaluated_people == "请求失败"
|
||||
):
|
||||
return CourseListResponse.error(
|
||||
message="获取评教课程列表失败,网络请求多次重试后仍无法连接教务系统,请稍后重试或联系管理员",
|
||||
code=500,
|
||||
data=[],
|
||||
)
|
||||
else:
|
||||
return CourseListResponse.success(
|
||||
data=result, message="评教课程列表获取成功"
|
||||
)
|
||||
else:
|
||||
return CourseListResponse.success(data=[], message="暂无需要评教的课程")
|
||||
|
||||
except Exception as e:
|
||||
return ErrorResponse(
|
||||
message=f"获取评教课程列表时发生系统错误:{str(e)}", code=500
|
||||
)
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/fetch_exam_info",
|
||||
summary="获取考试信息",
|
||||
response_model=ExamInfoAPIResponse | AuthmeResponse | ErrorResponse,
|
||||
)
|
||||
async def fetch_exam_info(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""获取考试信息,包括校统考和其他考试"""
|
||||
try:
|
||||
train_plan_info = await client.fetch_training_plan_info()
|
||||
|
||||
# 检查培养方案信息是否获取失败
|
||||
if not train_plan_info or (
|
||||
hasattr(train_plan_info, "plan_name")
|
||||
and train_plan_info.plan_name == "请求失败,请稍后重试"
|
||||
):
|
||||
return ErrorResponse(
|
||||
message="无法获取培养方案信息,导致考试信息获取失败。网络请求多次重试后仍无法连接教务系统,请稍后重试或联系管理员",
|
||||
code=500,
|
||||
)
|
||||
|
||||
# 检查是否是AuthmeResponse
|
||||
if isinstance(train_plan_info, AuthmeResponse):
|
||||
return train_plan_info
|
||||
|
||||
_term_code = train_plan_info.current_term
|
||||
# _term_code -> term_code: "2024-2025春季学期" 转换为 "2024-2025-2-1" "2024-2025秋季学期" 转换为 "2024-2025-1-1"
|
||||
# 进行转换
|
||||
term_code = f"{_term_code[:4]}-{_term_code[5:9]}-{"1" if _term_code[10] == "秋" else "2"}-1"
|
||||
print(f"当前学期代码: {term_code}")
|
||||
start_date = datetime.now()
|
||||
# termcode 结尾为 1 为秋季学期,考试应在3月之前,2为春季学期,考试应在9月之前
|
||||
end_date = datetime(
|
||||
year=start_date.year + (1 if term_code.endswith("1") else 0),
|
||||
month=3 if term_code.endswith("1") else 9,
|
||||
day=30,
|
||||
)
|
||||
|
||||
result = await client.fetch_unified_exam_info(
|
||||
start_date=start_date.strftime("%Y-%m-%d"),
|
||||
end_date=end_date.strftime("%Y-%m-%d"),
|
||||
term_code=term_code,
|
||||
)
|
||||
|
||||
# 检查是否是AuthmeResponse(认证错误)
|
||||
if isinstance(result, AuthmeResponse):
|
||||
return result
|
||||
|
||||
# 使用新的错误检测机制
|
||||
response = ExamInfoAPIResponse.from_data(
|
||||
data=result,
|
||||
success_message="考试信息获取成功",
|
||||
error_message="获取考试信息失败,网络请求多次重试后仍无法连接教务系统,请稍后重试或联系管理员",
|
||||
)
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return ErrorResponse(message=f"获取考试信息时发生系统错误:{str(e)}", code=500)
|
||||
|
||||
|
||||
# ==================== 评价系统API ====================
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/evaluation/initialize",
|
||||
summary="初始化评价任务",
|
||||
response_model=InitializeResponse | AuthmeResponse,
|
||||
)
|
||||
async def initialize_evaluation_task(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""初始化评价任务,获取课程列表"""
|
||||
try:
|
||||
# 获取用户ID (从JWC客户端获取)
|
||||
user_id = getattr(client, "user_id", "unknown")
|
||||
|
||||
# 检查是否已有活跃的任务管理器
|
||||
existing_manager = get_task_manager(user_id)
|
||||
if existing_manager:
|
||||
current_status = existing_manager.get_task_status().status
|
||||
if current_status in [
|
||||
TaskStatusEnum.RUNNING,
|
||||
TaskStatusEnum.PAUSED,
|
||||
TaskStatusEnum.INITIALIZING,
|
||||
]:
|
||||
return InitializeResponse(
|
||||
code=400,
|
||||
message="您已有一个评价任务在进行中,请先完成或终止当前任务",
|
||||
data=None,
|
||||
)
|
||||
# 如果任务已完成、失败或终止,移除旧的任务管理器
|
||||
elif current_status in [
|
||||
TaskStatusEnum.COMPLETED,
|
||||
TaskStatusEnum.FAILED,
|
||||
TaskStatusEnum.TERMINATED,
|
||||
]:
|
||||
remove_task_manager(user_id)
|
||||
|
||||
# 获取或创建任务管理器
|
||||
task_manager = get_task_manager(user_id, client)
|
||||
if not task_manager:
|
||||
return InitializeResponse(code=400, message="创建任务管理器失败", data=None)
|
||||
|
||||
# 执行初始化
|
||||
success = await task_manager.initialize()
|
||||
stats = task_manager.get_task_status()
|
||||
|
||||
# 转换课程列表格式
|
||||
course_list = []
|
||||
for course in stats.course_list:
|
||||
course_info = CourseInfo(
|
||||
course_id=(
|
||||
getattr(course.id, "coure_sequence_number", "") if course.id else ""
|
||||
),
|
||||
course_name=course.evaluation_content,
|
||||
teacher_name=course.evaluated_people,
|
||||
is_evaluated=course.is_evaluated,
|
||||
evaluation_content=course.evaluation_content,
|
||||
)
|
||||
course_list.append(course_info)
|
||||
|
||||
initialize_data = InitializeData(
|
||||
total_courses=stats.total_courses,
|
||||
pending_courses=stats.pending_courses,
|
||||
course_list=course_list,
|
||||
)
|
||||
|
||||
return InitializeResponse(
|
||||
code=200 if success else 400, message=stats.message, data=initialize_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return InitializeResponse(code=500, message=f"初始化失败: {str(e)}", data=None)
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/evaluation/start",
|
||||
summary="开始评价任务",
|
||||
response_model=TaskOperationResponse | AuthmeResponse,
|
||||
)
|
||||
async def start_evaluation_task(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""开始评价任务"""
|
||||
try:
|
||||
user_id = getattr(client, "user_id", "unknown")
|
||||
|
||||
# 检查是否已有运行中的任务
|
||||
existing_manager = get_task_manager(user_id)
|
||||
if existing_manager:
|
||||
current_status = existing_manager.get_task_status().status
|
||||
if current_status.value in [
|
||||
TaskStatusEnum.RUNNING.value,
|
||||
TaskStatusEnum.PAUSED.value,
|
||||
]:
|
||||
task_data = TaskOperationData(
|
||||
task_status=TaskStatusEnum(current_status.value)
|
||||
)
|
||||
return TaskOperationResponse(
|
||||
code=400,
|
||||
message="您已有一个评价任务在运行中,请先完成或终止当前任务",
|
||||
data=task_data,
|
||||
)
|
||||
|
||||
task_manager = get_task_manager(user_id, client)
|
||||
if not task_manager:
|
||||
task_data = TaskOperationData(task_status=TaskStatusEnum.FAILED)
|
||||
return TaskOperationResponse(
|
||||
code=400, message="任务管理器不存在,请先初始化", data=task_data
|
||||
)
|
||||
|
||||
success = await task_manager.start_evaluation_task()
|
||||
stats = task_manager.get_task_status()
|
||||
|
||||
task_data = TaskOperationData(task_status=TaskStatusEnum(stats.status.value))
|
||||
|
||||
return TaskOperationResponse(
|
||||
code=200 if success else 400,
|
||||
message="任务已启动" if success else "任务启动失败,可能已有任务在运行",
|
||||
data=task_data,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
task_data = TaskOperationData(task_status=TaskStatusEnum.FAILED)
|
||||
return TaskOperationResponse(
|
||||
code=500, message=f"启动任务失败: {str(e)}", data=task_data
|
||||
)
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/evaluation/terminate",
|
||||
summary="终止评价任务",
|
||||
response_model=TaskOperationResponse | AuthmeResponse,
|
||||
)
|
||||
async def terminate_evaluation_task(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""终止评价任务"""
|
||||
try:
|
||||
user_id = getattr(client, "user_id", "unknown")
|
||||
task_manager = get_task_manager(user_id)
|
||||
|
||||
if not task_manager:
|
||||
task_data = TaskOperationData(task_status=TaskStatusEnum.IDLE)
|
||||
return TaskOperationResponse(
|
||||
code=400, message="任务管理器不存在", data=task_data
|
||||
)
|
||||
|
||||
success = await task_manager.terminate_task()
|
||||
stats = task_manager.get_task_status()
|
||||
|
||||
# 移除任务管理器
|
||||
remove_task_manager(user_id)
|
||||
|
||||
task_data = TaskOperationData(task_status=TaskStatusEnum(stats.status.value))
|
||||
|
||||
return TaskOperationResponse(
|
||||
code=200 if success else 400,
|
||||
message="任务已终止" if success else "终止失败",
|
||||
data=task_data,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
task_data = TaskOperationData(task_status=TaskStatusEnum.FAILED)
|
||||
return TaskOperationResponse(
|
||||
code=500, message=f"终止任务失败: {str(e)}", data=task_data
|
||||
)
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/evaluation/status",
|
||||
summary="获取评价任务状态",
|
||||
response_model=EvaluationStatsResponse | AuthmeResponse,
|
||||
)
|
||||
async def get_evaluation_task_status(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""获取评价任务状态"""
|
||||
try:
|
||||
user_id = getattr(client, "user_id", "unknown")
|
||||
task_manager = get_task_manager(user_id)
|
||||
|
||||
if not task_manager:
|
||||
return EvaluationStatsResponse(code=200, message="无活跃任务", data=None)
|
||||
|
||||
stats = task_manager.get_task_status()
|
||||
|
||||
# 转换课程列表格式
|
||||
course_list = []
|
||||
for course in stats.course_list:
|
||||
course_info = CourseInfo(
|
||||
course_id=(
|
||||
getattr(course.id, "coure_sequence_number", "") if course.id else ""
|
||||
),
|
||||
course_name=course.evaluation_content,
|
||||
teacher_name=course.evaluated_people,
|
||||
is_evaluated=course.is_evaluated,
|
||||
evaluation_content=course.evaluation_content,
|
||||
)
|
||||
course_list.append(course_info)
|
||||
|
||||
stats_data = EvaluationStatsData(
|
||||
total_courses=stats.total_courses,
|
||||
pending_courses=stats.pending_courses,
|
||||
success_count=stats.success_count,
|
||||
fail_count=stats.fail_count,
|
||||
current_index=stats.current_index,
|
||||
status=TaskStatusEnum(stats.status.value),
|
||||
current_countdown=stats.current_countdown,
|
||||
start_time=stats.start_time,
|
||||
end_time=stats.end_time,
|
||||
error_message=stats.error_message,
|
||||
course_list=course_list,
|
||||
)
|
||||
|
||||
return EvaluationStatsResponse(code=200, message=stats.message, data=stats_data)
|
||||
|
||||
except Exception as e:
|
||||
return EvaluationStatsResponse(
|
||||
code=500, message=f"获取状态失败: {str(e)}", data=None
|
||||
)
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/evaluation/current",
|
||||
summary="获取当前评价课程信息",
|
||||
response_model=CurrentCourseInfoResponse | AuthmeResponse,
|
||||
)
|
||||
async def get_current_course_info(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""获取当前评价课程信息"""
|
||||
try:
|
||||
user_id = getattr(client, "user_id", "unknown")
|
||||
task_manager = get_task_manager(user_id)
|
||||
|
||||
if not task_manager:
|
||||
return CurrentCourseInfoResponse(code=200, message="无活跃任务", data=None)
|
||||
|
||||
current_info = task_manager.get_current_course_info()
|
||||
|
||||
course_info_data = CurrentCourseInfoData(
|
||||
is_evaluating=current_info.is_evaluating,
|
||||
course_name=current_info.course_name,
|
||||
teacher_name=current_info.teacher_name,
|
||||
progress_text=current_info.progress_text,
|
||||
countdown_seconds=current_info.countdown_seconds,
|
||||
current_index=current_info.current_index,
|
||||
total_pending=current_info.total_pending,
|
||||
)
|
||||
|
||||
return CurrentCourseInfoResponse(
|
||||
code=200, message="获取成功", data=course_info_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return CurrentCourseInfoResponse(
|
||||
code=500, message=f"获取信息失败: {str(e)}", data=None
|
||||
)
|
||||
|
||||
|
||||
# ==================== 学期和成绩相关API ====================
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/fetch_all_terms",
|
||||
summary="获取所有学期信息",
|
||||
response_model=AllTermsResponse | AuthmeResponse | ErrorResponse,
|
||||
)
|
||||
async def fetch_all_terms(client: JWCClient = Depends(get_jwc_client)):
|
||||
"""获取所有可查询的学期信息"""
|
||||
try:
|
||||
result = await client.fetch_all_terms()
|
||||
|
||||
# 检查结果
|
||||
if result and len(result) > 0:
|
||||
return AllTermsResponse.success(data=result, message="学期信息获取成功")
|
||||
else:
|
||||
return AllTermsResponse.error(
|
||||
message="获取学期信息失败,网络请求多次重试后仍无法连接教务系统,请稍后重试或联系管理员",
|
||||
code=500,
|
||||
data={},
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return ErrorResponse(message=f"获取学期信息时发生系统错误:{str(e)}", code=500)
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/fetch_term_score",
|
||||
summary="获取指定学期成绩",
|
||||
response_model=TermScoreAPIResponse | AuthmeResponse | ErrorResponse,
|
||||
)
|
||||
async def fetch_term_score(
|
||||
request: FetchTermScoreRequest,
|
||||
client: JWCClient = Depends(get_jwc_client),
|
||||
):
|
||||
"""
|
||||
获取指定学期的成绩信息
|
||||
"""
|
||||
try:
|
||||
raw_result = await client.fetch_term_score(
|
||||
term_id=request.term_id,
|
||||
course_code=request.course_code,
|
||||
course_name=request.course_name,
|
||||
page_num=request.page_num,
|
||||
page_size=request.page_size,
|
||||
)
|
||||
|
||||
if not raw_result:
|
||||
return TermScoreAPIResponse.error(
|
||||
message="获取成绩信息失败,网络请求多次重试后仍无法连接教务系统,请稍后重试或联系管理员",
|
||||
code=500,
|
||||
data=None,
|
||||
)
|
||||
|
||||
try:
|
||||
# 解析原始数据为结构化数据
|
||||
from provider.aufe.jwc.model import TermScoreResponse, ScoreRecord
|
||||
|
||||
list_data = raw_result.get("list", {})
|
||||
page_context = list_data.get("pageContext", {})
|
||||
records_raw = list_data.get("records", [])
|
||||
|
||||
# 转换记录格式
|
||||
score_records = []
|
||||
for record in records_raw:
|
||||
if len(record) >= 13: # 确保数据完整
|
||||
score_record = ScoreRecord(
|
||||
sequence=record[0] if record[0] else 0,
|
||||
term_id=record[1] if record[1] else "",
|
||||
course_code=record[2] if record[2] else "",
|
||||
course_class=record[3] if record[3] else "",
|
||||
course_name_cn=record[4] if record[4] else "",
|
||||
course_name_en=record[5] if record[5] else "",
|
||||
credits=record[6] if record[6] else "",
|
||||
hours=record[7] if record[7] else 0,
|
||||
course_type=record[8] if record[8] else "",
|
||||
exam_type=record[9] if record[9] else "",
|
||||
score=record[10] if record[10] else "",
|
||||
retake_score=(
|
||||
record[11] if len(record) > 11 and record[11] else None
|
||||
),
|
||||
makeup_score=(
|
||||
record[12] if len(record) > 12 and record[12] else None
|
||||
),
|
||||
)
|
||||
score_records.append(score_record)
|
||||
|
||||
result = TermScoreResponse(
|
||||
page_size=list_data.get("pageSize", 50),
|
||||
page_num=list_data.get("pageNum", 1),
|
||||
total_count=page_context.get("totalCount", 0),
|
||||
records=score_records,
|
||||
)
|
||||
|
||||
return TermScoreAPIResponse(
|
||||
code=200,
|
||||
message="success",
|
||||
data=result,
|
||||
)
|
||||
|
||||
except Exception as parse_error:
|
||||
return TermScoreAPIResponse.error(
|
||||
message=f"解析成绩数据失败:{str(parse_error)}", code=500, data=None
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取学期成绩失败: {str(e)}")
|
||||
return ErrorResponse(code=1, message=f"获取学期成绩失败: {str(e)}")
|
||||
|
||||
|
||||
@jwc_router.post(
|
||||
"/fetch_course_schedule",
|
||||
summary="获取课表信息",
|
||||
response_model=ScheduleResponse | AuthmeResponse | ErrorResponse,
|
||||
)
|
||||
async def fetch_course_schedule(
|
||||
request: FetchScheduleRequest,
|
||||
client: JWCClient = Depends(get_jwc_client)
|
||||
):
|
||||
"""
|
||||
获取聚合的课表信息,包含:
|
||||
- 课程基本信息(课程名、教师、学分等)
|
||||
- 上课时间和地点信息
|
||||
- 时间段详情
|
||||
- 学期信息
|
||||
|
||||
特殊处理:
|
||||
- 自动过滤无用字段
|
||||
- 标记没有具体时间安排的课程
|
||||
- 清理教师姓名中的特殊字符
|
||||
"""
|
||||
try:
|
||||
logger.info(f"获取课表请求: plan_code={request.plan_code}")
|
||||
|
||||
# 检查环境和Cookie有效性
|
||||
is_valid = await client.validate_environment_and_cookie()
|
||||
if not is_valid:
|
||||
return AuthmeResponse(
|
||||
code=401,
|
||||
message="Cookie已失效或不在VPN/校园网环境,请重新登录",
|
||||
)
|
||||
|
||||
# 获取处理后的课表数据
|
||||
schedule_data = await client.get_processed_schedule(request.plan_code)
|
||||
|
||||
if not schedule_data:
|
||||
return ErrorResponse(
|
||||
code=1,
|
||||
message="获取课表信息失败,请稍后重试"
|
||||
)
|
||||
|
||||
return ScheduleResponse(
|
||||
code=0,
|
||||
message="success",
|
||||
data=schedule_data,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"获取课表信息失败: {str(e)}")
|
||||
return ErrorResponse(code=1, message=f"获取课表信息失败: {str(e)}")
|
||||
670
router/jwc/evaluate.py
Normal file
670
router/jwc/evaluate.py
Normal file
@@ -0,0 +1,670 @@
|
||||
from provider.aufe.jwc import JWCClient
|
||||
from provider.aufe.jwc.model import Course, EvaluationRequestParam
|
||||
|
||||
import asyncio
|
||||
import random
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Callable
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class TaskStatus(Enum):
|
||||
"""任务状态枚举"""
|
||||
|
||||
IDLE = "idle" # 空闲
|
||||
INITIALIZING = "initializing" # 初始化中
|
||||
RUNNING = "running" # 运行中
|
||||
PAUSED = "paused" # 暂停
|
||||
COMPLETED = "completed" # 完成
|
||||
FAILED = "failed" # 失败
|
||||
TERMINATED = "terminated" # 已终止
|
||||
|
||||
|
||||
@dataclass
|
||||
class EvaluationStats:
|
||||
"""评价统计信息"""
|
||||
|
||||
total_courses: int = 0
|
||||
pending_courses: int = 0
|
||||
success_count: int = 0
|
||||
fail_count: int = 0
|
||||
current_index: int = 0
|
||||
status: TaskStatus = TaskStatus.IDLE
|
||||
message: str = ""
|
||||
course_list: List[Course] = field(default_factory=list)
|
||||
current_countdown: int = 0
|
||||
current_course: Optional[Course] = None
|
||||
start_time: Optional[datetime] = None
|
||||
end_time: Optional[datetime] = None
|
||||
error_message: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class CurrentCourseInfo:
|
||||
"""当前评价课程信息"""
|
||||
|
||||
is_evaluating: bool = False
|
||||
course_name: str = ""
|
||||
teacher_name: str = ""
|
||||
progress_text: str = ""
|
||||
countdown_seconds: int = 0
|
||||
current_index: int = -1
|
||||
total_pending: int = 0
|
||||
|
||||
|
||||
class Constants:
|
||||
"""常量定义"""
|
||||
|
||||
# 等待评价的冷却时间(秒)
|
||||
COUNTDOWN_SECONDS = 140 # 2分20秒
|
||||
|
||||
# 随机评价文案 - 总体评价文案
|
||||
ZGPGS = [
|
||||
"老师授课生动形象,课堂氛围活跃。",
|
||||
"教学方法新颖,能够激发学习兴趣。",
|
||||
"讲解耐心细致,知识点清晰易懂。",
|
||||
"对待学生公平公正,很有亲和力。",
|
||||
"课堂管理有序,效率高。",
|
||||
"能理论联系实际,深入浅出。",
|
||||
"作业布置合理,有助于巩固知识。",
|
||||
"教学经验丰富,讲解深入浅出。",
|
||||
"关注学生反馈,及时调整教学。",
|
||||
"教学资源丰富,便于学习。",
|
||||
"课堂互动性强,能充分调动积极性。",
|
||||
"教学重点突出,难点突破到位。",
|
||||
"性格开朗,课堂充满活力。",
|
||||
"批改作业认真,评语有指导性。",
|
||||
"教学目标明确,条理清晰。",
|
||||
]
|
||||
|
||||
# 额外描述性文案
|
||||
NICE_0000000200 = [
|
||||
"常把晦涩理论生活化,知识瞬间亲近起来。",
|
||||
"总用类比解难点,复杂概念秒懂。",
|
||||
"引入行业前沿案例,打开视野新窗口。",
|
||||
"设问巧妙引深思,激发自主探寻答案。",
|
||||
"常分享学科冷知识,拓宽知识边界。",
|
||||
"用跨学科视角解题,思维更灵动。",
|
||||
"鼓励尝试多元解法,创新思维被激活。",
|
||||
"常分享科研趣事,点燃学术热情。",
|
||||
"用思维导图梳理知识,结构一目了然。",
|
||||
"常把学习方法倾囊相授,效率直线提升。",
|
||||
"用历史事件类比,知识记忆更深刻。",
|
||||
"常鼓励跨学科学习,综合素养渐涨。",
|
||||
"分享行业大咖故事,奋斗动力满满。",
|
||||
"总能挖掘知识背后的趣味,学习味十足。",
|
||||
"常组织知识竞赛,学习热情被点燃。",
|
||||
]
|
||||
|
||||
# 建议文案
|
||||
NICE_0000000201 = [
|
||||
"无",
|
||||
"没有",
|
||||
"没有什么建议,老师很好",
|
||||
"继续保持这么好的教学风格",
|
||||
"希望老师继续分享更多精彩案例",
|
||||
"感谢老师的悉心指导",
|
||||
]
|
||||
|
||||
|
||||
class EvaluationTaskManager:
|
||||
"""评价任务管理器 - 基于学号管理"""
|
||||
|
||||
def __init__(self, jwc_client: JWCClient, user_id: str):
|
||||
"""
|
||||
初始化评价任务管理器
|
||||
|
||||
Args:
|
||||
jwc_client: JWC客户端实例
|
||||
user_id: 用户学号
|
||||
"""
|
||||
self.jwc_client = jwc_client
|
||||
self.user_id = user_id
|
||||
self.stats = EvaluationStats()
|
||||
self._task: Optional[asyncio.Task] = None
|
||||
self._stop_event = asyncio.Event()
|
||||
self._progress_callbacks: List[Callable[[EvaluationStats], None]] = []
|
||||
|
||||
logger.info(f"初始化评价任务管理器,用户ID: {user_id}")
|
||||
|
||||
def add_progress_callback(self, callback: Callable[[EvaluationStats], None]):
|
||||
"""添加进度回调函数"""
|
||||
self._progress_callbacks.append(callback)
|
||||
|
||||
def _notify_progress(self):
|
||||
"""通知所有进度回调"""
|
||||
for callback in self._progress_callbacks:
|
||||
try:
|
||||
callback(self.stats)
|
||||
except Exception as e:
|
||||
logger.error(f"进度回调执行失败: {str(e)}")
|
||||
|
||||
async def initialize(self) -> bool:
|
||||
"""
|
||||
初始化评价环境
|
||||
|
||||
Returns:
|
||||
bool: 初始化是否成功
|
||||
"""
|
||||
try:
|
||||
self.stats.status = TaskStatus.INITIALIZING
|
||||
self.stats.message = "正在检查网络..."
|
||||
self._notify_progress()
|
||||
|
||||
# 检查网络连接
|
||||
if not await self.jwc_client.check_network_connection():
|
||||
self.stats.status = TaskStatus.FAILED
|
||||
self.stats.message = "网络连接失败,请确保连接到校园网或VPN"
|
||||
self.stats.error_message = "网络连接失败"
|
||||
self._notify_progress()
|
||||
return False
|
||||
|
||||
# 验证环境和Cookie
|
||||
self.stats.message = "正在验证登录状态..."
|
||||
self._notify_progress()
|
||||
|
||||
if not await self.jwc_client.validate_environment_and_cookie():
|
||||
self.stats.status = TaskStatus.FAILED
|
||||
self.stats.message = "登录状态失效,请重新登录"
|
||||
self.stats.error_message = "Cookie验证失败"
|
||||
self._notify_progress()
|
||||
return False
|
||||
|
||||
# 获取Token
|
||||
self.stats.message = "正在获取Token..."
|
||||
self._notify_progress()
|
||||
|
||||
token = await self.jwc_client.get_token()
|
||||
if not token:
|
||||
self.stats.status = TaskStatus.FAILED
|
||||
self.stats.message = "获取Token失败,可能是评教系统未开放"
|
||||
self.stats.error_message = "Token获取失败"
|
||||
self._notify_progress()
|
||||
return False
|
||||
|
||||
# 获取课程列表
|
||||
self.stats.message = "正在获取课程列表..."
|
||||
self._notify_progress()
|
||||
|
||||
courses = await self.jwc_client.fetch_evaluation_course_list()
|
||||
if not courses:
|
||||
self.stats.status = TaskStatus.FAILED
|
||||
self.stats.message = "未获取到课程列表,请稍后再试"
|
||||
self.stats.error_message = "课程列表获取失败"
|
||||
self._notify_progress()
|
||||
return False
|
||||
|
||||
# 更新统计信息
|
||||
pending_courses = [
|
||||
course
|
||||
for course in courses
|
||||
if getattr(course, "is_evaluated", "否") != "是"
|
||||
]
|
||||
self.stats.course_list = courses
|
||||
self.stats.total_courses = len(courses)
|
||||
self.stats.pending_courses = len(pending_courses)
|
||||
self.stats.status = TaskStatus.IDLE
|
||||
self.stats.message = (
|
||||
f"初始化完成,找到 {self.stats.pending_courses} 门待评价课程"
|
||||
)
|
||||
self.stats.current_course = None
|
||||
|
||||
logger.info(
|
||||
f"用户 {self.user_id} 初始化完成,待评价课程: {self.stats.pending_courses}"
|
||||
)
|
||||
self._notify_progress()
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.stats.status = TaskStatus.FAILED
|
||||
self.stats.message = f"初始化异常: {str(e)}"
|
||||
self.stats.error_message = str(e)
|
||||
logger.error(f"用户 {self.user_id} 初始化失败: {str(e)}")
|
||||
self._notify_progress()
|
||||
return False
|
||||
|
||||
async def evaluate_course(self, course: Course, token: str) -> bool:
|
||||
"""
|
||||
评价单门课程
|
||||
|
||||
Args:
|
||||
course: 课程信息
|
||||
token: CSRF Token
|
||||
|
||||
Returns:
|
||||
bool: 评价是否成功
|
||||
"""
|
||||
try:
|
||||
# 设置当前课程
|
||||
self.stats.current_course = course
|
||||
|
||||
# 如果课程已评价,则跳过
|
||||
if getattr(course, "is_evaluated", "否") == "是":
|
||||
logger.info(f"课程已评价,跳过: {course.evaluation_content}")
|
||||
return True
|
||||
|
||||
# 第一步:访问评价页面
|
||||
if not await self.jwc_client.access_evaluation_page(token, course):
|
||||
return False
|
||||
|
||||
course_name = course.evaluation_content
|
||||
logger.info(f"正在准备评价: {course_name}")
|
||||
|
||||
self.stats.message = "已访问评价页面,等待服务器倒计时完成后提交评价..."
|
||||
self._notify_progress()
|
||||
|
||||
# 等待服务器倒计时
|
||||
server_wait_time = Constants.COUNTDOWN_SECONDS
|
||||
|
||||
# 显示倒计时
|
||||
for second in range(server_wait_time, 0, -1):
|
||||
# 检查是否被终止
|
||||
if self._stop_event.is_set():
|
||||
self.stats.status = TaskStatus.TERMINATED
|
||||
self.stats.message = "任务已被终止"
|
||||
self._notify_progress()
|
||||
return False
|
||||
|
||||
self.stats.current_countdown = second
|
||||
self.stats.message = f"服务器倒计时: {second} 秒,然后提交评价..."
|
||||
self._notify_progress()
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
self.stats.current_countdown = 0
|
||||
self.stats.message = "倒计时结束,正在提交评价..."
|
||||
self._notify_progress()
|
||||
|
||||
# 生成评价数据
|
||||
evaluation_ratings = {}
|
||||
for i in range(180, 202):
|
||||
key = f"0000000{i}"
|
||||
if i == 200:
|
||||
evaluation_ratings[key] = random.choice(Constants.NICE_0000000200)
|
||||
elif i == 201:
|
||||
evaluation_ratings[key] = random.choice(Constants.NICE_0000000201)
|
||||
else:
|
||||
evaluation_ratings[key] = f"5_{random.choice(['0.8', '1'])}"
|
||||
|
||||
# 创建评价请求参数
|
||||
evaluation_param = EvaluationRequestParam(
|
||||
token_value=token,
|
||||
questionnaire_code=(
|
||||
course.questionnaire.questionnaire_number
|
||||
if course.questionnaire
|
||||
else ""
|
||||
),
|
||||
evaluation_content=(
|
||||
course.id.evaluation_content_number if course.id else ""
|
||||
),
|
||||
evaluated_people_number=course.id.evaluated_people if course.id else "",
|
||||
zgpj=random.choice(Constants.ZGPGS),
|
||||
rating_items=evaluation_ratings,
|
||||
)
|
||||
|
||||
# 提交评价
|
||||
response = await self.jwc_client.submit_evaluation(evaluation_param)
|
||||
success = response.result == "success"
|
||||
|
||||
if success:
|
||||
logger.info(f"课程评价成功: {course_name}")
|
||||
else:
|
||||
logger.error(f"课程评价失败: {course_name}, 错误: {response.msg}")
|
||||
|
||||
# 清除当前课程信息
|
||||
self.stats.current_course = None
|
||||
self.stats.current_countdown = 0
|
||||
|
||||
return success
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"评价课程异常: {str(e)}")
|
||||
return False
|
||||
|
||||
async def start_evaluation_task(self) -> bool:
|
||||
"""
|
||||
开始评价任务
|
||||
确保一个用户只能有一个运行中的任务
|
||||
|
||||
Returns:
|
||||
bool: 任务是否成功启动
|
||||
"""
|
||||
# 检查当前状态
|
||||
if self.stats.status == TaskStatus.RUNNING:
|
||||
logger.warning(f"用户 {self.user_id} 的评价任务已在运行中")
|
||||
return False
|
||||
|
||||
if self.stats.status == TaskStatus.INITIALIZING:
|
||||
logger.warning(f"用户 {self.user_id} 的评价任务正在初始化中")
|
||||
return False
|
||||
|
||||
# 检查是否有未完成的异步任务
|
||||
if self._task and not self._task.done():
|
||||
logger.warning(f"用户 {self.user_id} 已有任务在执行")
|
||||
return False
|
||||
|
||||
# 确保任务已经初始化
|
||||
if self.stats.status == TaskStatus.IDLE and len(self.stats.course_list) == 0:
|
||||
logger.warning(f"用户 {self.user_id} 任务未初始化,请先调用initialize")
|
||||
return False
|
||||
|
||||
# 重置停止事件
|
||||
self._stop_event.clear()
|
||||
|
||||
# 创建新任务
|
||||
self._task = asyncio.create_task(self._evaluate_all_courses())
|
||||
|
||||
logger.info(f"用户 {self.user_id} 开始评价任务")
|
||||
return True
|
||||
|
||||
async def _evaluate_all_courses(self):
|
||||
"""批量评价所有课程(内部方法)"""
|
||||
try:
|
||||
# 获取Token
|
||||
token = await self.jwc_client.get_token()
|
||||
if not token:
|
||||
self.stats.status = TaskStatus.FAILED
|
||||
self.stats.message = "获取Token失败"
|
||||
self._notify_progress()
|
||||
return
|
||||
|
||||
# 获取待评价课程
|
||||
pending_courses = [
|
||||
course
|
||||
for course in self.stats.course_list
|
||||
if getattr(course, "is_evaluated", "否") != "是"
|
||||
]
|
||||
|
||||
if not pending_courses:
|
||||
self.stats.status = TaskStatus.COMPLETED
|
||||
self.stats.message = "所有课程已评价完成!"
|
||||
self._notify_progress()
|
||||
return
|
||||
|
||||
# 开始评价流程
|
||||
self.stats.status = TaskStatus.RUNNING
|
||||
self.stats.success_count = 0
|
||||
self.stats.fail_count = 0
|
||||
self.stats.current_course = None
|
||||
self.stats.start_time = datetime.now()
|
||||
|
||||
index = 0
|
||||
while index < len(pending_courses):
|
||||
# 检查是否被终止
|
||||
if self._stop_event.is_set():
|
||||
self.stats.status = TaskStatus.TERMINATED
|
||||
self.stats.message = "任务已被终止"
|
||||
self.stats.end_time = datetime.now()
|
||||
self._notify_progress()
|
||||
return
|
||||
|
||||
course = pending_courses[index]
|
||||
self.stats.current_index = index
|
||||
self.stats.current_course = course
|
||||
|
||||
course_name = getattr(
|
||||
course.questionnaire,
|
||||
"questionnaire_name",
|
||||
course.evaluation_content,
|
||||
)
|
||||
self.stats.message = f"正在处理第 {index + 1}/{len(pending_courses)} 门课程: {course_name}"
|
||||
self._notify_progress()
|
||||
|
||||
# 评价当前课程
|
||||
success = await self.evaluate_course(course, token)
|
||||
|
||||
if success:
|
||||
self.stats.success_count += 1
|
||||
self.stats.message = f"课程评价成功: {course_name}"
|
||||
else:
|
||||
self.stats.fail_count += 1
|
||||
self.stats.message = f"课程评价失败: {course_name}"
|
||||
|
||||
self._notify_progress()
|
||||
|
||||
# 评价完一门课程后,重新获取课程列表
|
||||
self.stats.message = "正在更新课程列表..."
|
||||
self._notify_progress()
|
||||
|
||||
# 重新获取课程列表
|
||||
updated_courses = await self.jwc_client.fetch_evaluation_course_list()
|
||||
if updated_courses:
|
||||
self.stats.course_list = updated_courses
|
||||
pending_courses = [
|
||||
course
|
||||
for course in updated_courses
|
||||
if getattr(course, "is_evaluated", "否") != "是"
|
||||
]
|
||||
self.stats.total_courses = len(updated_courses)
|
||||
self.stats.pending_courses = len(pending_courses)
|
||||
self.stats.message = (
|
||||
f"课程列表已更新,剩余待评价课程: {self.stats.pending_courses}"
|
||||
)
|
||||
self._notify_progress()
|
||||
|
||||
# 给服务器一些处理时间
|
||||
if pending_courses and index < len(pending_courses) - 1:
|
||||
self.stats.message = "准备处理下一门课程..."
|
||||
self._notify_progress()
|
||||
await asyncio.sleep(3)
|
||||
|
||||
index += 1
|
||||
|
||||
# 评价完成
|
||||
self.stats.status = TaskStatus.COMPLETED
|
||||
self.stats.current_course = None
|
||||
self.stats.end_time = datetime.now()
|
||||
self.stats.message = f"评价完成!成功: {self.stats.success_count},失败: {self.stats.fail_count}"
|
||||
|
||||
logger.info(
|
||||
f"用户 {self.user_id} 评价任务完成,成功: {self.stats.success_count},失败: {self.stats.fail_count}"
|
||||
)
|
||||
self._notify_progress()
|
||||
|
||||
except Exception as e:
|
||||
self.stats.status = TaskStatus.FAILED
|
||||
self.stats.error_message = str(e)
|
||||
self.stats.message = f"评价任务异常: {str(e)}"
|
||||
self.stats.end_time = datetime.now()
|
||||
logger.error(f"用户 {self.user_id} 评价任务异常: {str(e)}")
|
||||
self._notify_progress()
|
||||
|
||||
async def pause_task(self) -> bool:
|
||||
"""
|
||||
暂停任务
|
||||
|
||||
Returns:
|
||||
bool: 是否成功暂停
|
||||
"""
|
||||
if self.stats.status != TaskStatus.RUNNING:
|
||||
return False
|
||||
|
||||
self.stats.status = TaskStatus.PAUSED
|
||||
self.stats.message = "任务已暂停"
|
||||
logger.info(f"用户 {self.user_id} 任务已暂停")
|
||||
self._notify_progress()
|
||||
return True
|
||||
|
||||
async def resume_task(self) -> bool:
|
||||
"""
|
||||
恢复任务
|
||||
|
||||
Returns:
|
||||
bool: 是否成功恢复
|
||||
"""
|
||||
if self.stats.status != TaskStatus.PAUSED:
|
||||
return False
|
||||
|
||||
self.stats.status = TaskStatus.RUNNING
|
||||
self.stats.message = "任务已恢复"
|
||||
logger.info(f"用户 {self.user_id} 任务已恢复")
|
||||
self._notify_progress()
|
||||
return True
|
||||
|
||||
async def terminate_task(self) -> bool:
|
||||
"""
|
||||
终止任务
|
||||
|
||||
Returns:
|
||||
bool: 是否成功终止
|
||||
"""
|
||||
if self.stats.status not in [TaskStatus.RUNNING, TaskStatus.PAUSED]:
|
||||
return False
|
||||
|
||||
# 设置停止事件
|
||||
self._stop_event.set()
|
||||
|
||||
# 如果有运行中的任务,等待其完成
|
||||
if self._task and not self._task.done():
|
||||
try:
|
||||
await asyncio.wait_for(self._task, timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
self._task.cancel()
|
||||
try:
|
||||
await self._task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
self.stats.status = TaskStatus.TERMINATED
|
||||
self.stats.message = "任务已终止"
|
||||
self.stats.end_time = datetime.now()
|
||||
logger.info(f"用户 {self.user_id} 任务已终止")
|
||||
self._notify_progress()
|
||||
return True
|
||||
|
||||
def get_current_course_info(self) -> CurrentCourseInfo:
|
||||
"""
|
||||
获取当前评价课程信息
|
||||
|
||||
Returns:
|
||||
CurrentCourseInfo: 当前课程信息
|
||||
"""
|
||||
# 如果没有运行评价任务
|
||||
if self.stats.status != TaskStatus.RUNNING:
|
||||
return CurrentCourseInfo(
|
||||
is_evaluating=False, progress_text="当前无评价任务"
|
||||
)
|
||||
|
||||
# 正在评价但还没有确定是哪门课程
|
||||
if (
|
||||
self.stats.current_index < 0
|
||||
or self.stats.current_index >= len(self.stats.course_list)
|
||||
or self.stats.current_course is None
|
||||
):
|
||||
return CurrentCourseInfo(
|
||||
is_evaluating=True,
|
||||
progress_text="准备中...",
|
||||
total_pending=self.stats.pending_courses,
|
||||
)
|
||||
|
||||
# 正在评价特定课程
|
||||
course = self.stats.current_course
|
||||
pending_courses = [
|
||||
c
|
||||
for c in self.stats.course_list
|
||||
if getattr(c, "is_evaluated", "否") != "是"
|
||||
]
|
||||
index = self.stats.current_index + 1
|
||||
total = len(pending_courses)
|
||||
|
||||
countdown_text = (
|
||||
f" (倒计时: {self.stats.current_countdown}秒)"
|
||||
if self.stats.current_countdown > 0
|
||||
else ""
|
||||
)
|
||||
|
||||
course_name = course.evaluation_content[:20]
|
||||
if len(course.evaluation_content) > 20:
|
||||
course_name += "..."
|
||||
|
||||
return CurrentCourseInfo(
|
||||
is_evaluating=True,
|
||||
course_name=course_name,
|
||||
teacher_name=course.evaluated_people,
|
||||
progress_text=f"正在评价({index}/{total}): {course_name} - {course.evaluated_people}{countdown_text}",
|
||||
countdown_seconds=self.stats.current_countdown,
|
||||
current_index=self.stats.current_index,
|
||||
total_pending=total,
|
||||
)
|
||||
|
||||
def get_task_status(self) -> EvaluationStats:
|
||||
"""
|
||||
获取任务状态
|
||||
|
||||
Returns:
|
||||
EvaluationStats: 任务统计信息
|
||||
"""
|
||||
return self.stats
|
||||
|
||||
def get_user_id(self) -> str:
|
||||
"""获取用户ID"""
|
||||
return self.user_id
|
||||
|
||||
|
||||
# 全局任务管理器字典,以学号为键
|
||||
_task_managers: Dict[str, EvaluationTaskManager] = {}
|
||||
|
||||
|
||||
def get_task_manager(
|
||||
user_id: str, jwc_client: Optional[JWCClient] = None
|
||||
) -> Optional[EvaluationTaskManager]:
|
||||
"""
|
||||
获取或创建任务管理器
|
||||
一个用户只能有一个活跃的任务管理器
|
||||
|
||||
Args:
|
||||
user_id: 用户学号
|
||||
jwc_client: JWC客户端(创建新管理器时需要)
|
||||
|
||||
Returns:
|
||||
Optional[EvaluationTaskManager]: 任务管理器实例
|
||||
"""
|
||||
if user_id in _task_managers:
|
||||
existing_manager = _task_managers[user_id]
|
||||
# 检查现有任务的状态
|
||||
current_status = existing_manager.get_task_status().status
|
||||
|
||||
# 如果任务已完成、失败或终止,自动清理
|
||||
if current_status in [
|
||||
TaskStatus.COMPLETED,
|
||||
TaskStatus.FAILED,
|
||||
TaskStatus.TERMINATED,
|
||||
]:
|
||||
logger.info(f"自动清理用户 {user_id} 的已完成任务")
|
||||
del _task_managers[user_id]
|
||||
else:
|
||||
# 返回现有的管理器
|
||||
return existing_manager
|
||||
|
||||
# 创建新的管理器
|
||||
if jwc_client is None:
|
||||
return None
|
||||
|
||||
manager = EvaluationTaskManager(jwc_client, user_id)
|
||||
_task_managers[user_id] = manager
|
||||
logger.info(f"为用户 {user_id} 创建新的任务管理器")
|
||||
return manager
|
||||
|
||||
|
||||
def remove_task_manager(user_id: str) -> bool:
|
||||
"""
|
||||
移除任务管理器
|
||||
|
||||
Args:
|
||||
user_id: 用户学号
|
||||
|
||||
Returns:
|
||||
bool: 是否成功移除
|
||||
"""
|
||||
if user_id in _task_managers:
|
||||
del _task_managers[user_id]
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_all_task_managers() -> Dict[str, EvaluationTaskManager]:
|
||||
"""获取所有任务管理器"""
|
||||
return _task_managers.copy()
|
||||
95
router/jwc/evaluate_model.py
Normal file
95
router/jwc/evaluate_model.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from enum import Enum
|
||||
from router.common_model import BaseResponse
|
||||
|
||||
|
||||
class TaskStatusEnum(str, Enum):
|
||||
"""任务状态枚举"""
|
||||
|
||||
IDLE = "idle"
|
||||
INITIALIZING = "initializing"
|
||||
RUNNING = "running"
|
||||
PAUSED = "paused"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
TERMINATED = "terminated"
|
||||
|
||||
|
||||
class CourseInfo(BaseModel):
|
||||
"""课程信息响应模型"""
|
||||
|
||||
course_id: str = Field("", description="课程ID")
|
||||
course_name: str = Field("", description="课程名称")
|
||||
teacher_name: str = Field("", description="教师姓名")
|
||||
is_evaluated: str = Field("", description="是否已评价")
|
||||
evaluation_content: str = Field("", description="评价内容")
|
||||
|
||||
|
||||
# 统一响应数据模型
|
||||
class EvaluationStatsData(BaseModel):
|
||||
"""评价统计信息数据模型"""
|
||||
|
||||
total_courses: int = Field(0, description="总课程数")
|
||||
pending_courses: int = Field(0, description="待评价课程数")
|
||||
success_count: int = Field(0, description="成功评价数")
|
||||
fail_count: int = Field(0, description="失败评价数")
|
||||
current_index: int = Field(0, description="当前评价索引")
|
||||
status: TaskStatusEnum = Field(TaskStatusEnum.IDLE, description="任务状态")
|
||||
current_countdown: int = Field(0, description="当前倒计时")
|
||||
start_time: Optional[datetime] = Field(None, description="开始时间")
|
||||
end_time: Optional[datetime] = Field(None, description="结束时间")
|
||||
error_message: str = Field("", description="错误消息")
|
||||
course_list: List[CourseInfo] = Field(default_factory=list, description="课程列表")
|
||||
|
||||
|
||||
class CurrentCourseInfoData(BaseModel):
|
||||
"""当前评价课程信息数据模型"""
|
||||
|
||||
is_evaluating: bool = Field(False, description="是否正在评价")
|
||||
course_name: str = Field("", description="课程名称")
|
||||
teacher_name: str = Field("", description="教师姓名")
|
||||
progress_text: str = Field("", description="进度文本")
|
||||
countdown_seconds: int = Field(0, description="倒计时秒数")
|
||||
current_index: int = Field(-1, description="当前索引")
|
||||
total_pending: int = Field(0, description="总待评价数")
|
||||
|
||||
|
||||
class TaskOperationData(BaseModel):
|
||||
"""任务操作数据模型"""
|
||||
|
||||
task_status: TaskStatusEnum = Field(TaskStatusEnum.IDLE, description="任务状态")
|
||||
|
||||
|
||||
class InitializeData(BaseModel):
|
||||
"""初始化数据模型"""
|
||||
|
||||
total_courses: int = Field(0, description="总课程数")
|
||||
pending_courses: int = Field(0, description="待评价课程数")
|
||||
course_list: List[CourseInfo] = Field(default_factory=list, description="课程列表")
|
||||
|
||||
|
||||
# 统一响应模型
|
||||
class EvaluationStatsResponse(BaseResponse[EvaluationStatsData]):
|
||||
"""评价统计信息响应模型"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CurrentCourseInfoResponse(BaseResponse[CurrentCourseInfoData]):
|
||||
"""当前评价课程信息响应模型"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TaskOperationResponse(BaseResponse[TaskOperationData]):
|
||||
"""任务操作响应模型"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class InitializeResponse(BaseResponse[InitializeData]):
|
||||
"""初始化响应模型"""
|
||||
|
||||
pass
|
||||
122
router/jwc/model.py
Normal file
122
router/jwc/model.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from router.common_model import BaseResponse
|
||||
from provider.aufe.jwc.model import (
|
||||
AcademicInfo,
|
||||
TrainingPlanInfo,
|
||||
Course,
|
||||
ExamInfoResponse,
|
||||
TermScoreResponse,
|
||||
)
|
||||
from typing import List, Dict, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# 统一响应模型
|
||||
class AcademicInfoResponse(BaseResponse[AcademicInfo]):
|
||||
"""学业信息响应"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TrainingPlanInfoResponse(BaseResponse[TrainingPlanInfo]):
|
||||
"""培养方案信息响应"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CourseListResponse(BaseResponse[List[Course]]):
|
||||
"""评教课程列表响应"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ExamInfoAPIResponse(BaseResponse[ExamInfoResponse]):
|
||||
"""考试信息响应"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# ==================== 学期和成绩相关响应模型 ====================
|
||||
|
||||
|
||||
class FetchTermScoreRequest(BaseModel):
|
||||
"""获取学期成绩请求模型"""
|
||||
|
||||
term_id: str = Field(..., description="学期ID,如:2024-2025-2-1")
|
||||
course_code: str = Field("", description="课程代码(可选,用于筛选)")
|
||||
course_name: str = Field("", description="课程名称(可选,用于筛选)")
|
||||
page_num: int = Field(1, description="页码,默认为1", ge=1)
|
||||
page_size: int = Field(50, description="每页大小,默认为50", ge=1, le=100)
|
||||
|
||||
|
||||
class AllTermsResponse(BaseResponse[Dict[str, str]]):
|
||||
"""所有学期信息响应"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TermScoreAPIResponse(BaseResponse[TermScoreResponse]):
|
||||
"""学期成绩响应"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# ==================== 课表相关响应模型 ====================
|
||||
|
||||
|
||||
class TimeSlot(BaseModel):
|
||||
"""时间段模型"""
|
||||
|
||||
session: int = Field(..., description="节次")
|
||||
session_name: str = Field(..., description="节次名称")
|
||||
start_time: str = Field(..., description="开始时间,格式:HHMM")
|
||||
end_time: str = Field(..., description="结束时间,格式:HHMM")
|
||||
time_length: str = Field(..., description="时长(分钟)")
|
||||
djjc: int = Field(..., description="大节节次")
|
||||
|
||||
|
||||
class CourseTimeLocation(BaseModel):
|
||||
"""课程时间地点模型"""
|
||||
|
||||
class_day: int = Field(..., description="上课星期几(1-7)")
|
||||
class_sessions: int = Field(..., description="上课节次")
|
||||
continuing_session: int = Field(..., description="持续节次数")
|
||||
class_week: str = Field(..., description="上课周次(24位二进制字符串)")
|
||||
week_description: str = Field(..., description="上课周次描述")
|
||||
campus_name: str = Field(..., description="校区名称")
|
||||
teaching_building_name: str = Field(..., description="教学楼名称")
|
||||
classroom_name: str = Field(..., description="教室名称")
|
||||
|
||||
|
||||
class ScheduleCourse(BaseModel):
|
||||
"""课表课程模型"""
|
||||
|
||||
course_name: str = Field(..., description="课程名称")
|
||||
course_code: str = Field(..., description="课程代码")
|
||||
course_sequence: str = Field(..., description="课程序号")
|
||||
teacher_name: str = Field(..., description="授课教师")
|
||||
course_properties: str = Field(..., description="课程性质")
|
||||
exam_type: str = Field(..., description="考试类型")
|
||||
unit: float = Field(..., description="学分")
|
||||
time_locations: List[CourseTimeLocation] = Field(..., description="时间地点列表")
|
||||
is_no_schedule: bool = Field(False, description="是否无具体时间安排")
|
||||
|
||||
|
||||
class ScheduleData(BaseModel):
|
||||
"""课表数据模型"""
|
||||
|
||||
total_units: float = Field(..., description="总学分")
|
||||
time_slots: List[TimeSlot] = Field(..., description="时间段列表")
|
||||
courses: List[ScheduleCourse] = Field(..., description="课程列表")
|
||||
semester_info: Dict[str, str] = Field(..., description="学期信息")
|
||||
|
||||
|
||||
class ScheduleResponse(BaseResponse[ScheduleData]):
|
||||
"""课表响应模型"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class FetchScheduleRequest(BaseModel):
|
||||
"""获取课表请求模型"""
|
||||
|
||||
plan_code: str = Field(..., description="培养方案代码,如:2024-2025-2-1")
|
||||
Reference in New Issue
Block a user