software copyright
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
# 自然写教学数据分析与学情诊断系统软件 V1.0
|
||||
# api/profile_api.py - 学情画像API接口
|
||||
|
||||
import logging
|
||||
from typing import Optional, List, Dict, Any
|
||||
from datetime import datetime, date, timedelta
|
||||
from enum import Enum
|
||||
|
||||
from fastapi import APIRouter, Query, Path, Depends, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
logger = logging.getLogger("writech.analytics.profile")
|
||||
|
||||
router = APIRouter(tags=["学情画像"])
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 数据模型定义
|
||||
# ============================================================
|
||||
|
||||
class SubjectEnum(str, Enum):
|
||||
"""学科枚举"""
|
||||
CHINESE = "chinese"
|
||||
MATH = "math"
|
||||
ENGLISH = "english"
|
||||
PHYSICS = "physics"
|
||||
CHEMISTRY = "chemistry"
|
||||
BIOLOGY = "biology"
|
||||
|
||||
|
||||
class KnowledgeMastery(BaseModel):
|
||||
"""知识点掌握度模型"""
|
||||
knowledge_id: str = Field(..., description="知识点ID")
|
||||
knowledge_name: str = Field(..., description="知识点名称")
|
||||
chapter: str = Field("", description="所属章节")
|
||||
mastery_level: float = Field(0.0, ge=0.0, le=1.0, description="掌握度(0-1)")
|
||||
practice_count: int = Field(0, description="练习次数")
|
||||
correct_rate: float = Field(0.0, description="正确率")
|
||||
last_practice_at: Optional[str] = Field(None, description="最近练习时间")
|
||||
trend: str = Field("stable", description="趋势: improving/declining/stable")
|
||||
|
||||
|
||||
class WeakPoint(BaseModel):
|
||||
"""薄弱知识点模型"""
|
||||
knowledge_id: str
|
||||
knowledge_name: str
|
||||
mastery_level: float
|
||||
error_count: int = Field(0, description="错误次数")
|
||||
suggested_exercises: List[str] = Field([], description="推荐练习题ID")
|
||||
related_knowledge: List[str] = Field([], description="关联知识点")
|
||||
|
||||
|
||||
class StudentProfile(BaseModel):
|
||||
"""学生学情画像完整模型"""
|
||||
student_id: str
|
||||
student_name: str
|
||||
class_id: str
|
||||
grade: str
|
||||
school_id: str
|
||||
|
||||
# 总体学业水平
|
||||
overall_score: float = Field(0.0, description="综合评分(百分制)")
|
||||
overall_rank: int = Field(0, description="班级排名")
|
||||
overall_trend: str = Field("stable", description="总体趋势")
|
||||
|
||||
# 各科目掌握度
|
||||
subject_scores: Dict[str, float] = Field({}, description="各科目评分")
|
||||
|
||||
# 知识点掌握度矩阵
|
||||
knowledge_mastery: List[KnowledgeMastery] = Field([])
|
||||
|
||||
# 薄弱环节
|
||||
weak_points: List[WeakPoint] = Field([])
|
||||
|
||||
# 书写能力评估
|
||||
writing_quality_score: float = Field(0.0, description="书写规范性评分")
|
||||
stroke_order_accuracy: float = Field(0.0, description="笔顺正确率")
|
||||
writing_speed: float = Field(0.0, description="书写速度(字/分)")
|
||||
|
||||
# 学习习惯统计
|
||||
avg_daily_study_minutes: float = Field(0.0, description="日均学习时长(分)")
|
||||
homework_completion_rate: float = Field(0.0, description="作业完成率")
|
||||
homework_on_time_rate: float = Field(0.0, description="按时提交率")
|
||||
|
||||
# 更新时间
|
||||
updated_at: str = Field("", description="画像更新时间")
|
||||
|
||||
|
||||
class ClassProfile(BaseModel):
|
||||
"""班级学情统计模型"""
|
||||
class_id: str
|
||||
class_name: str
|
||||
grade: str
|
||||
student_count: int
|
||||
|
||||
# 班级整体指标
|
||||
avg_score: float = Field(0.0, description="班级平均分")
|
||||
median_score: float = Field(0.0, description="班级中位分")
|
||||
max_score: float = Field(0.0, description="最高分")
|
||||
min_score: float = Field(0.0, description="最低分")
|
||||
std_deviation: float = Field(0.0, description="标准差")
|
||||
|
||||
# 成绩分布(分数段人数)
|
||||
score_distribution: Dict[str, int] = Field(
|
||||
{}, description="分数段分布: {'90-100': 5, '80-89': 10, ...}"
|
||||
)
|
||||
|
||||
# 知识点班级掌握度
|
||||
knowledge_avg_mastery: List[Dict[str, Any]] = Field([])
|
||||
|
||||
# 薄弱知识点(班级维度)
|
||||
class_weak_points: List[Dict[str, Any]] = Field([])
|
||||
|
||||
# 作业统计
|
||||
homework_avg_completion: float = Field(0.0)
|
||||
homework_avg_score: float = Field(0.0)
|
||||
|
||||
|
||||
class ProfileCompareResponse(BaseModel):
|
||||
"""学情对比响应"""
|
||||
student_profile: StudentProfile
|
||||
class_avg: Dict[str, float]
|
||||
grade_avg: Dict[str, float]
|
||||
percentile: float = Field(0.0, description="年级百分位排名")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# API接口实现
|
||||
# ============================================================
|
||||
|
||||
@router.get("/student/{student_id}", response_model=StudentProfile)
|
||||
async def get_student_profile(
|
||||
student_id: str = Path(..., description="学生ID"),
|
||||
subject: Optional[SubjectEnum] = Query(None, description="筛选科目"),
|
||||
):
|
||||
"""
|
||||
获取学生个人学情画像
|
||||
|
||||
返回学生的知识掌握度、薄弱环节、书写能力、学习习惯等全面画像数据。
|
||||
教师可查看本班学生,家长可查看自己子女。
|
||||
"""
|
||||
logger.info("查询学生画像: student_id=%s, subject=%s", student_id, subject)
|
||||
|
||||
try:
|
||||
# 从ClickHouse查询学生画像宽表数据
|
||||
# profile_data = await query_student_profile(student_id)
|
||||
|
||||
# 从Neo4j查询知识点掌握度和薄弱环节
|
||||
# mastery = await query_knowledge_mastery(student_id, subject)
|
||||
# weak = await query_weak_points(student_id, subject)
|
||||
|
||||
# 组装画像数据
|
||||
profile = StudentProfile(
|
||||
student_id=student_id,
|
||||
student_name="",
|
||||
class_id="",
|
||||
grade="",
|
||||
school_id="",
|
||||
updated_at=datetime.now().isoformat(),
|
||||
)
|
||||
|
||||
return profile
|
||||
|
||||
except Exception as e:
|
||||
logger.error("查询学生画像失败: %s", str(e))
|
||||
raise HTTPException(status_code=500, detail=f"查询学生画像失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/class/{class_id}", response_model=ClassProfile)
|
||||
async def get_class_profile(
|
||||
class_id: str = Path(..., description="班级ID"),
|
||||
subject: Optional[SubjectEnum] = Query(None, description="筛选科目"),
|
||||
start_date: Optional[str] = Query(None, description="起始日期"),
|
||||
end_date: Optional[str] = Query(None, description="结束日期"),
|
||||
):
|
||||
"""
|
||||
获取班级学情统计
|
||||
|
||||
返回班级平均分、分数分布、薄弱知识点等班级维度的统计数据。
|
||||
仅班级教师和校管理员可查看。
|
||||
"""
|
||||
logger.info("查询班级学情: class_id=%s, subject=%s", class_id, subject)
|
||||
|
||||
try:
|
||||
# 从ClickHouse聚合查询班级统计数据
|
||||
# class_stats = await aggregate_class_stats(class_id, subject, ...)
|
||||
|
||||
class_profile = ClassProfile(
|
||||
class_id=class_id,
|
||||
class_name="",
|
||||
grade="",
|
||||
student_count=0,
|
||||
)
|
||||
|
||||
return class_profile
|
||||
|
||||
except Exception as e:
|
||||
logger.error("查询班级学情失败: %s", str(e))
|
||||
raise HTTPException(status_code=500, detail=f"查询班级学情失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/compare/{student_id}", response_model=ProfileCompareResponse)
|
||||
async def compare_student_with_class(
|
||||
student_id: str = Path(..., description="学生ID"),
|
||||
subject: Optional[SubjectEnum] = Query(None),
|
||||
):
|
||||
"""
|
||||
学生与班级/年级对比分析
|
||||
|
||||
将学生各项指标与班级平均和年级平均对比,计算百分位排名。
|
||||
"""
|
||||
logger.info("学情对比分析: student_id=%s", student_id)
|
||||
|
||||
try:
|
||||
# 查询学生个人画像
|
||||
# student = await query_student_profile(student_id)
|
||||
|
||||
# 查询班级和年级平均值
|
||||
# class_avg = await query_class_avg(student.class_id, subject)
|
||||
# grade_avg = await query_grade_avg(student.grade, subject)
|
||||
|
||||
# 计算百分位排名
|
||||
# percentile = await calc_percentile(student_id, student.grade)
|
||||
|
||||
return ProfileCompareResponse(
|
||||
student_profile=StudentProfile(
|
||||
student_id=student_id,
|
||||
student_name="",
|
||||
class_id="",
|
||||
grade="",
|
||||
school_id="",
|
||||
),
|
||||
class_avg={},
|
||||
grade_avg={},
|
||||
percentile=0.0,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("学情对比失败: %s", str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/knowledge-map/{student_id}")
|
||||
async def get_knowledge_map(
|
||||
student_id: str = Path(..., description="学生ID"),
|
||||
subject: SubjectEnum = Query(..., description="科目"),
|
||||
):
|
||||
"""
|
||||
获取知识图谱掌握度可视化数据
|
||||
|
||||
从Neo4j查询该科目知识图谱结构,叠加学生个人掌握度,
|
||||
生成可供前端ECharts渲染的图谱JSON数据。
|
||||
"""
|
||||
logger.info(
|
||||
"查询知识图谱: student_id=%s, subject=%s", student_id, subject
|
||||
)
|
||||
|
||||
try:
|
||||
# 从Neo4j查询知识点节点和边
|
||||
# nodes = await neo4j_query_knowledge_nodes(subject)
|
||||
# edges = await neo4j_query_knowledge_edges(subject)
|
||||
|
||||
# 查询学生对各知识点的掌握度
|
||||
# mastery_map = await query_mastery_map(student_id, subject)
|
||||
|
||||
# 组装ECharts图谱数据格式
|
||||
graph_data = {
|
||||
"nodes": [], # [{id, name, mastery, category, ...}]
|
||||
"edges": [], # [{source, target, relation_type}]
|
||||
"categories": [
|
||||
{"name": "已掌握"},
|
||||
{"name": "部分掌握"},
|
||||
{"name": "未掌握"},
|
||||
{"name": "未学习"},
|
||||
],
|
||||
}
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": graph_data,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error("查询知识图谱失败: %s", str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/weak-analysis/{student_id}")
|
||||
async def analyze_weak_points(
|
||||
student_id: str = Path(..., description="学生ID"),
|
||||
subject: Optional[SubjectEnum] = Query(None),
|
||||
top_n: int = Query(10, ge=1, le=50, description="返回前N个薄弱点"),
|
||||
):
|
||||
"""
|
||||
薄弱知识点深度分析
|
||||
|
||||
结合错题归因和知识图谱前驱关系,分析薄弱根因并给出学习建议。
|
||||
"""
|
||||
logger.info(
|
||||
"薄弱分析: student_id=%s, subject=%s, top=%d",
|
||||
student_id, subject, top_n,
|
||||
)
|
||||
|
||||
try:
|
||||
# 查询错题记录及关联知识点
|
||||
# errors = await query_error_records(student_id, subject)
|
||||
|
||||
# 利用Neo4j知识图谱进行根因分析
|
||||
# 如果某知识点正确率低,检查其前驱知识点是否也未掌握
|
||||
# root_causes = await trace_knowledge_prerequisites(errors)
|
||||
|
||||
# 生成学习建议
|
||||
weak_analysis = {
|
||||
"weak_points": [], # 薄弱知识点列表
|
||||
"root_causes": [], # 根因知识点
|
||||
"suggestions": [], # 学习建议
|
||||
"recommended_exercises": [], # 推荐练习
|
||||
}
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": weak_analysis,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error("薄弱分析失败: %s", str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -0,0 +1,397 @@
|
||||
# 自然写教学数据分析与学情诊断系统软件 V1.0
|
||||
# api/report_api.py - 报告导出与查询API
|
||||
# api/growth_api.py - 成长轨迹API
|
||||
# model/data_models.py - 核心数据模型定义
|
||||
|
||||
import logging
|
||||
from typing import Optional, List, Dict, Any
|
||||
from datetime import datetime, date
|
||||
from enum import Enum
|
||||
|
||||
from fastapi import APIRouter, Query, Path, HTTPException, BackgroundTasks
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
logger = logging.getLogger("writech.analytics.api")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 报告导出API路由
|
||||
# ============================================================
|
||||
|
||||
report_router = APIRouter(tags=["报告导出"])
|
||||
|
||||
|
||||
class ExportRequest(BaseModel):
|
||||
"""报告导出请求"""
|
||||
report_type: str = Field(..., description="报告类型")
|
||||
target_id: str = Field(..., description="目标ID(学生/班级)")
|
||||
start_date: str = Field(..., description="开始日期")
|
||||
end_date: str = Field(..., description="结束日期")
|
||||
format: str = Field("pdf", description="输出格式: json/pdf/html")
|
||||
include_charts: bool = Field(True, description="是否包含图表")
|
||||
|
||||
|
||||
class ExportResponse(BaseModel):
|
||||
"""报告导出响应"""
|
||||
task_id: str
|
||||
status: str
|
||||
download_url: Optional[str] = None
|
||||
estimated_seconds: int = 0
|
||||
|
||||
|
||||
@report_router.post("/export", response_model=ExportResponse)
|
||||
async def export_report(
|
||||
request: ExportRequest,
|
||||
background_tasks: BackgroundTasks,
|
||||
):
|
||||
"""
|
||||
生成并导出学情报告
|
||||
|
||||
异步生成报告,返回任务ID。
|
||||
客户端可通过任务ID轮询状态或等待WebSocket通知。
|
||||
"""
|
||||
logger.info(
|
||||
"报告导出请求: type=%s, target=%s, format=%s",
|
||||
request.report_type,
|
||||
request.target_id,
|
||||
request.format,
|
||||
)
|
||||
|
||||
# 生成任务ID
|
||||
task_id = f"rpt_{datetime.now().strftime('%Y%m%d%H%M%S')}_{request.target_id[:8]}"
|
||||
|
||||
# 将报告生成任务加入后台队列
|
||||
# background_tasks.add_task(
|
||||
# generate_report_task,
|
||||
# task_id=task_id,
|
||||
# config=request,
|
||||
# )
|
||||
|
||||
return ExportResponse(
|
||||
task_id=task_id,
|
||||
status="processing",
|
||||
estimated_seconds=30,
|
||||
)
|
||||
|
||||
|
||||
@report_router.get("/status/{task_id}")
|
||||
async def get_export_status(task_id: str = Path(...)):
|
||||
"""查询报告导出任务状态"""
|
||||
# status = await query_task_status(task_id)
|
||||
return {
|
||||
"task_id": task_id,
|
||||
"status": "completed",
|
||||
"download_url": None,
|
||||
}
|
||||
|
||||
|
||||
@report_router.get("/class/{class_id}")
|
||||
async def get_class_report(
|
||||
class_id: str = Path(..., description="班级ID"),
|
||||
subject: Optional[str] = Query(None),
|
||||
start_date: Optional[str] = Query(None),
|
||||
end_date: Optional[str] = Query(None),
|
||||
):
|
||||
"""
|
||||
获取班级学情统计报告
|
||||
|
||||
返回班级平均分、分数分布、薄弱知识点等统计数据。
|
||||
仅班级教师和校管理员有权限查看。
|
||||
"""
|
||||
logger.info("班级报告查询: class=%s, subject=%s", class_id, subject)
|
||||
|
||||
# 权限校验:教师仅可查看本班数据
|
||||
# verify_class_permission(current_user, class_id)
|
||||
|
||||
# 从ClickHouse查询班级统计数据
|
||||
# stats = await aggregate_class_report(class_id, subject, ...)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"class_id": class_id,
|
||||
"student_count": 0,
|
||||
"avg_score": 0,
|
||||
"score_distribution": {},
|
||||
"weak_points": [],
|
||||
"top_students": [],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@report_router.get("/history")
|
||||
async def list_report_history(
|
||||
target_id: str = Query(..., description="目标ID"),
|
||||
report_type: Optional[str] = Query(None),
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
):
|
||||
"""查询历史报告列表"""
|
||||
# reports = await query_report_history(target_id, report_type, ...)
|
||||
return {
|
||||
"code": 0,
|
||||
"data": {
|
||||
"total": 0,
|
||||
"page": page,
|
||||
"items": [],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 成长轨迹API路由
|
||||
# ============================================================
|
||||
|
||||
growth_router = APIRouter(tags=["成长轨迹"])
|
||||
|
||||
|
||||
@growth_router.get("/{student_id}")
|
||||
async def get_growth_trajectory(
|
||||
student_id: str = Path(..., description="学生ID"),
|
||||
subject: Optional[str] = Query(None, description="科目"),
|
||||
start_date: Optional[str] = Query(None),
|
||||
end_date: Optional[str] = Query(None),
|
||||
granularity: str = Query("weekly", description="粒度: daily/weekly/monthly"),
|
||||
):
|
||||
"""
|
||||
获取学生成长轨迹
|
||||
|
||||
返回学生在指定时间范围内的各项指标时序数据,
|
||||
包括成绩趋势、书写能力变化、学习习惯变化等。
|
||||
家长仅可查看自己子女的数据。
|
||||
"""
|
||||
logger.info(
|
||||
"成长轨迹查询: student=%s, subject=%s, granularity=%s",
|
||||
student_id, subject, granularity,
|
||||
)
|
||||
|
||||
# 权限校验
|
||||
# verify_student_access(current_user, student_id)
|
||||
|
||||
# 从ClickHouse查询时序数据
|
||||
# trend_data = await query_growth_trend(student_id, subject, ...)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"student_id": student_id,
|
||||
"period": f"{start_date} ~ {end_date}",
|
||||
"score_trend": [], # 成绩趋势
|
||||
"writing_trend": [], # 书写能力趋势
|
||||
"habit_trend": [], # 学习习惯趋势
|
||||
"milestones": [], # 里程碑事件
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@growth_router.get("/writing/{student_id}")
|
||||
async def get_writing_growth(
|
||||
student_id: str = Path(..., description="学生ID"),
|
||||
start_date: str = Query(..., description="开始日期"),
|
||||
end_date: str = Query(..., description="结束日期"),
|
||||
):
|
||||
"""
|
||||
获取书写能力成长报告
|
||||
|
||||
返回笔顺准确率、书写规范性、书写速度等维度的成长趋势。
|
||||
"""
|
||||
logger.info(
|
||||
"书写成长查询: student=%s, %s~%s",
|
||||
student_id, start_date, end_date,
|
||||
)
|
||||
|
||||
# 调用书写成长分析引擎
|
||||
# from analytics.writing_growth import WritingGrowthAnalyzer
|
||||
# analyzer = WritingGrowthAnalyzer()
|
||||
# report = await analyzer.analyze_growth(
|
||||
# student_id, start_date, end_date
|
||||
# )
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"student_id": student_id,
|
||||
"overall_level": "",
|
||||
"overall_score": 0,
|
||||
"dimensions": {
|
||||
"stroke_order": {"score": 0, "trend": "stable"},
|
||||
"quality": {"score": 0, "trend": "stable"},
|
||||
"speed": {"score": 0, "trend": "stable"},
|
||||
"structure": {"score": 0, "trend": "stable"},
|
||||
},
|
||||
"snapshots": [],
|
||||
"most_improved_chars": [],
|
||||
"needs_practice_chars": [],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@growth_router.get("/error/analysis/{student_id}")
|
||||
async def get_error_analysis(
|
||||
student_id: str = Path(..., description="学生ID"),
|
||||
subject: Optional[str] = Query(None),
|
||||
top_n: int = Query(20, ge=1, le=100),
|
||||
):
|
||||
"""
|
||||
错题归因分析
|
||||
|
||||
返回学生的错题统计、知识点薄弱分析、错因归类。
|
||||
结合知识图谱进行根因分析。
|
||||
"""
|
||||
logger.info(
|
||||
"错题分析: student=%s, subject=%s", student_id, subject
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"student_id": student_id,
|
||||
"total_errors": 0,
|
||||
"by_subject": {}, # 按科目分组
|
||||
"by_knowledge": [], # 按知识点排序
|
||||
"error_types": {}, # 错因分类
|
||||
"root_causes": [], # 根因分析(知识图谱)
|
||||
"recommendations": [], # 学习建议
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@growth_router.post("/push/parent")
|
||||
async def push_to_parent(
|
||||
student_id: str = Query(..., description="学生ID"),
|
||||
report_type: str = Query("weekly", description="推送报告类型"),
|
||||
background_tasks: BackgroundTasks = None,
|
||||
):
|
||||
"""
|
||||
触发学情报告推送至家长端
|
||||
|
||||
通过WebSocket或APP推送通知家长查看学情报告。
|
||||
家长端展示简化版本的学情摘要。
|
||||
"""
|
||||
logger.info("家长推送: student=%s, type=%s", student_id, report_type)
|
||||
|
||||
# 生成家长版报告
|
||||
# background_tasks.add_task(
|
||||
# generate_and_push_parent_report,
|
||||
# student_id=student_id,
|
||||
# report_type=report_type,
|
||||
# )
|
||||
|
||||
return {
|
||||
"code": 0,
|
||||
"message": "推送任务已提交",
|
||||
"data": {"student_id": student_id},
|
||||
}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 核心数据模型定义(model/data_models.py)
|
||||
# ============================================================
|
||||
|
||||
class GradeLevel(str, Enum):
|
||||
"""年级枚举"""
|
||||
GRADE_1 = "grade_1"
|
||||
GRADE_2 = "grade_2"
|
||||
GRADE_3 = "grade_3"
|
||||
GRADE_4 = "grade_4"
|
||||
GRADE_5 = "grade_5"
|
||||
GRADE_6 = "grade_6"
|
||||
GRADE_7 = "grade_7"
|
||||
GRADE_8 = "grade_8"
|
||||
GRADE_9 = "grade_9"
|
||||
|
||||
|
||||
class StudentInfo(BaseModel):
|
||||
"""学生基本信息"""
|
||||
student_id: str
|
||||
name: str
|
||||
class_id: str
|
||||
grade: GradeLevel
|
||||
school_id: str
|
||||
gender: Optional[str] = None
|
||||
created_at: Optional[str] = None
|
||||
|
||||
|
||||
class ClassInfo(BaseModel):
|
||||
"""班级基本信息"""
|
||||
class_id: str
|
||||
class_name: str
|
||||
grade: GradeLevel
|
||||
school_id: str
|
||||
teacher_id: str
|
||||
student_count: int = 0
|
||||
|
||||
|
||||
class SchoolInfo(BaseModel):
|
||||
"""学校信息"""
|
||||
school_id: str
|
||||
school_name: str
|
||||
region: str
|
||||
district: str
|
||||
|
||||
|
||||
class ErrorRecord(BaseModel):
|
||||
"""错题记录模型(MySQL)"""
|
||||
id: Optional[int] = None
|
||||
student_id: str
|
||||
homework_id: str
|
||||
question_id: str
|
||||
subject: str
|
||||
knowledge_point: str = ""
|
||||
error_type: str = "" # 计算错误/概念混淆/审题不清/粗心
|
||||
student_answer: str = ""
|
||||
correct_answer: str = ""
|
||||
created_at: str = ""
|
||||
|
||||
|
||||
class ExamAnalysis(BaseModel):
|
||||
"""考试分析结果模型(ClickHouse)"""
|
||||
exam_id: str
|
||||
class_id: str
|
||||
subject: str
|
||||
exam_date: str
|
||||
avg_score: float = 0.0
|
||||
median_score: float = 0.0
|
||||
max_score: float = 0.0
|
||||
min_score: float = 0.0
|
||||
std_deviation: float = 0.0
|
||||
pass_rate: float = 0.0
|
||||
excellent_rate: float = 0.0
|
||||
score_distribution: Dict[str, int] = {}
|
||||
difficulty_coefficient: float = 0.0
|
||||
discrimination_index: float = 0.0
|
||||
|
||||
|
||||
class KafkaEventSchema(BaseModel):
|
||||
"""Kafka事件消息Schema"""
|
||||
event_id: str
|
||||
event_type: str
|
||||
student_id: str
|
||||
class_id: str = ""
|
||||
school_id: str = ""
|
||||
timestamp: str
|
||||
source: str = ""
|
||||
payload: Dict[str, Any] = {}
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"event_id": "evt_20240101_001",
|
||||
"event_type": "grade_result",
|
||||
"student_id": "stu_001",
|
||||
"class_id": "cls_001",
|
||||
"school_id": "sch_001",
|
||||
"timestamp": "2024-01-01T10:00:00+08:00",
|
||||
"source": "pad",
|
||||
"payload": {
|
||||
"homework_id": "hw_001",
|
||||
"subject": "chinese",
|
||||
"score": 85,
|
||||
"total_score": 100,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user