#!/usr/bin/env python # -*- coding: utf-8 -*- import os from fastapi.background import BackgroundTasks from pydantic import Field from sqlalchemy.ext.asyncio import AsyncSession from crud.mark import crud_mark, crud_studentmark from crud.school import crud_grade, crud_system, crud_class from crud.user import crud_teacher, crud_student from models.mark import StudentMarkTask, StudentAnswer from models.school import SchoolGrade, SchoolClass from models.user import Teacher, Student from schemas.app.task import BgUpdateTaskReviewInfo, BgUpdateStudentTaskInfo from schemas.school.school import NewGrade async def delete_related_object(db: AsyncSession, *, sid: int = 0, gid: int = 0, cid: int = 0): # 根据学校删除年级、班级、教师、学生等 if sid and (not gid) and (not cid): # 1、删除年级 await crud_grade.delete(db, where_clauses=[SchoolGrade.school_id == sid]) # 2、删除班级 await crud_class.delete(db, where_clauses=[SchoolClass.school_id == sid]) # TODO: 删除教师、学生、作业、考试、阅卷任务等 await crud_teacher.delete(db, where_clauses=[Teacher.school_id == sid]) await crud_student.delete(db, where_clauses=[Student.school_id == sid]) elif gid and (not cid): # 如果是年级被删除,则删除该年级下的所有班级 await crud_class.delete(db, where_clauses=[SchoolClass.grade_id == gid]) # TODO: 删除学生、作业、考试、阅卷任务等 await crud_student.delete(db, where_clauses=[Student.grade_id == gid]) elif cid: # 如果班级被删除了,则删除该班级下的所有学生 # TODO: 删除学生、作业、考试、阅卷任务等 await crud_student.delete(db, where_clauses=[Student.class_id == cid]) else: pass async def delete_remote_file(filepath: str): urls = [x.strip() for x in filepath.split(";")] try: for url in urls: os.remove(url) except Exception as ex: print(f"[Error] Delete file: {str(ex)}") async def rank_score(db: AsyncSession, task_id: int = Field(..., description="阅卷任务ID")): # 根据 task_id 查询 StudentMarkTask stmt = f"""SELECT id, CASE WHEN @prevRank = score THEN @curRank WHEN @prevRank := score THEN @curRank := @curRank + 1 END AS rank FROM {StudentMarkTask.__tablename__}, (SELECT @curRank :=0, @prevRank := NULL) rb WHERE task_id={task_id} ORDER BY score desc; """ ranks = await crud_studentmark.execute(db, stmt) for idx, sid in enumerate(ranks): stmt = f"UPDATE {StudentMarkTask.__tablename__} SET rank={idx+1} WHERE id={sid};" await crud_studentmark.execute(db, stmt) async def update_mark_task(db: AsyncSession, bg_task: BackgroundTasks, task_id: int = Field(..., description="学生阅卷任务ID"), qtype: int = Field(..., description="试题类型,0:客观题,1: 主观题"), score: int = Field(..., description="得分"), editor_id: int = Field(..., description="最后编辑人ID"), editor_name: str = Field(..., description="最后编辑人姓名")): """ 数据更新流程: 1、根据task_id更新StudentMarkTask, 2、如果该学生的所有试题已经被批阅完,则根据StudentMarkTask.task_id 更新 MarkTask 的最高分/最低分/平均分...等信息。 3、如果MarkTask批阅完成,则对StudentMarkTask进行排名。 """ # ------------------ 更新学生阅卷任----------------------------------- student_task = await crud_studentmark.find_one(db, filters={"id": task_id}) # 判断该学生是否已经被批阅完成 marked_amount = student_task.marked_amount + 1 smt = BgUpdateStudentTaskInfo( marked_amount=marked_amount, is_completed=marked_amount == student_task.question_amount, score=student_task.score + score, editor_id=editor_id, editor_name=editor_name, ) # 按题型统计得分 if qtype: smt.subjective_score = student_task.subjective_score + score else: smt.objective_score = student_task.objective_score + score # 开始更新 student_task = await crud_studentmark.update(db, student_task, smt) # ------------------ 更新学生阅卷任务结束 --------------------------- # ------------------ 更新阅卷任务 --------------------------------- if student_task.is_completed: # 查询MarkTask是否存在 db_obj = await crud_mark.find_one(db, filters={"id": student_task.task_id}) # 批阅完成学生数量 marked_amount = db_obj.marked_amount + 1 if marked_amount == db_obj.student_amount: bg_task.add_task(rank_score, db, student_task.task_id) # 最高分 / 最低分 high_score = student_task.score if student_task.score > db_obj.high_score else db_obj.high_score low_score = student_task.score if student_task.score <= db_obj.low_score else db_obj.low_score # 平均分 avg_score = round( ((db_obj.avg_score * db_obj.marked_amount) + score) / marked_amount, 2) # 及格人数 if student_task.score >= 60: pass_amount = db_obj.pass_amount + 1 else: pass_amount = db_obj.pass_amount pass_rate = round(pass_amount / marked_amount, 2) # 批阅状态 status = 2 if marked_amount == db_obj.student_amount else 1 # 更新 mt = BgUpdateTaskReviewInfo(marked_amount=marked_amount, high_score=high_score, low_score=low_score, avg_score=avg_score, pass_amount=pass_amount, pass_rate=pass_rate, status=status) await crud_mark.update(db, db_obj, mt) print("Async Update MarkTask OK!") async def async_create_grade(db: AsyncSession, sid: int, category: int): # 年级列表 school_system = await crud_system.find_one(db, filters={"id": category}, return_fields={"grades"}) grade_set = set(school_system.grades.split(",")) # 查询学校是否存在 _, db_grades = await crud_grade.find_all(db, filters={"school_id": sid}) db_grade_set = set([x.name for x in db_grades]) diff_grades = grade_set - db_grade_set new_grades = [NewGrade(sid=sid, name=x) for x in diff_grades] try: await crud_grade.insert_many(db, new_grades) except Exception as ex: print(f"AutoCreateGradeError: {str(ex)}") return async def delete_student_task_question(db: AsyncSession, task_id: int): # 查询学生任务ID列表 _, student_tasks = await crud_studentmark.find_all( db, filters={"task_id": task_id}, return_fields=["id"]) student_task_ids = ",".join([str(x.id) for x in student_tasks]) # 删除学生任务和答题 task_stmt = "DELETE FROM `{}` WHERE task_id={};".format( StudentMarkTask.__tablename__, task_id) await crud_studentmark.execute(db, task_stmt) question_stmt = "DELETE FROM `{}` WHERE task_id IN ({});".format( StudentAnswer.__tablename__, student_task_ids) await crud_studentmark.execute(db, question_stmt)