diff --git a/agent/prompt.py b/agent/prompt.py index 431b86f..6665995 100644 --- a/agent/prompt.py +++ b/agent/prompt.py @@ -21,6 +21,7 @@ study_prompt = """ - JSON 必须是合法格式,可被程序直接解析。 4. **返回格式(严格遵循)** +- select字段是用户实际选择的,永远为空数组 [ { "id": 1, @@ -32,7 +33,8 @@ study_prompt = """ "选项C内容", "选项D内容" ], - "answer": ["A"] + "answer": ["A"], + "select": [] }, { "id": 2, @@ -44,7 +46,8 @@ study_prompt = """ "选项C内容", "选项D内容" ], - "answer": ["A", "C"] + "answer": ["A", "C"], + "select": [] } ] """ \ No newline at end of file diff --git a/main.py b/main.py index 567de11..34e85c4 100644 --- a/main.py +++ b/main.py @@ -8,8 +8,8 @@ from starlette.responses import StreamingResponse from database import get_db from schemas.agent import QueryRequest from schemas.library import LibraryFileRequest, LibraryFileResponse -from schemas.mistake import MistakeResponse -from schemas.practice import PracticeQuestionResponse, PracticeCheckResult, PracticeRecordResponse +from schemas.mistake import MistakeResponse, MistakeRequest +from schemas.practice import PracticeRecordResponse, PracticeRecordRequest from service import library_service, practice_service, mistake_service from service.agent_service import query_agent from storage import upload_rustfs @@ -60,21 +60,26 @@ async def edit_file(id: int, data: LibraryFileRequest, db: AsyncSession = Depend return await library_service.edit_file(db, id, data) -@app.get("/practice/files/{id}", response_model=List[PracticeQuestionResponse]) -async def get_question(id: int, db: AsyncSession = Depends(get_db)): - return await practice_service.get_question(db, id) - - @app.get("/practice/record/{id}", response_model=List[PracticeRecordResponse]) async def get_record(id: int, db: AsyncSession = Depends(get_db)): return await practice_service.get_record(db, id) -@app.post("/practice/check/{id}", response_model=List[PracticeCheckResult]) -async def check_practice(id: int, data: List[PracticeQuestionResponse], db: AsyncSession = Depends(get_db)): - return await practice_service.check_practice(db, id, data) +@app.post("/practice/record", response_model=bool) +async def add_record(data: PracticeRecordRequest, db: AsyncSession = Depends(get_db)): + return await practice_service.add_record(db, data) @app.get("/mistake", response_model=List[MistakeResponse]) async def list_mistakes(db: AsyncSession = Depends(get_db)): return await mistake_service.list_mistakes(db) + + +@app.post("/mistake", response_model=bool) +async def list_mistakes(data: list[MistakeRequest], db: AsyncSession = Depends(get_db)): + return await mistake_service.add_mistakes(db, data) + + +@app.delete("/mistake/{id}", response_model=bool) +async def remove_mistake(id: int, db: AsyncSession = Depends(get_db)): + return await mistake_service.remove_mistake(db, id) diff --git a/models/library.py b/models/library.py index 13b6686..ec8f181 100644 --- a/models/library.py +++ b/models/library.py @@ -13,44 +13,12 @@ class LibraryFile(AuditBase): size = Column(Integer, nullable=False) content = Column(Text, nullable=False) - questions = relationship( - "LibraryFileQuestion", - back_populates="library_file", - cascade="all, delete-orphan" - ) - practice_record = relationship( "PracticeRecord", back_populates="library_file", cascade="all, delete-orphan" ) - mistake = relationship( - "Mistake", - back_populates="library_file", - cascade="all, delete-orphan" - ) - - -class LibraryFileQuestion(AuditBase): - library_file_id = Column(BigInteger, ForeignKey("library_file.id"), nullable=False) - - type = Column(String(45), nullable=False) - question = Column(Text, nullable=False) - options = Column(JSONB, nullable=False) - answer = Column(JSONB, nullable=False) - - library_file = relationship( - "LibraryFile", - back_populates="questions" - ) - - mistake = relationship( - "Mistake", - back_populates="library_file_question", - cascade="all, delete-orphan" - ) - class PracticeRecord(AuditBase): library_file_id = Column(BigInteger, ForeignKey("library_file.id"), nullable=False) @@ -64,18 +32,12 @@ class PracticeRecord(AuditBase): back_populates="practice_record", ) + class Mistake(AuditBase): - library_file_id = Column(BigInteger, ForeignKey("library_file.id"), nullable=False) - question_id = Column(BigInteger, ForeignKey("library_file_question.id"), nullable=False) - - wrong_count = Column(Integer, nullable=False) - - library_file = relationship( - "LibraryFile", - back_populates="mistake", - ) - - library_file_question = relationship( - "LibraryFileQuestion", - back_populates="mistake", - ) + subject = Column(String(45), nullable=False) + module = Column(String(45), nullable=False) + name = Column(String(45), nullable=False) + type = Column(String(45), nullable=False) + question = Column(Text, nullable=False) + options = Column(JSONB, nullable=False) + answer = Column(JSONB, nullable=False) diff --git a/schemas/library.py b/schemas/library.py index e0c6fc8..01416eb 100644 --- a/schemas/library.py +++ b/schemas/library.py @@ -1,19 +1,4 @@ from pydantic import BaseModel -from typing import List - - -class QuestionRequest(BaseModel): - type: str - question: str - options: List[str] - answer: List[str] - - -class QuestionResponse(QuestionRequest): - id: int - - class Config: - from_attributes = True class LibraryFileRequest(BaseModel): @@ -24,13 +9,10 @@ class LibraryFileRequest(BaseModel): type: str content: str - questions: List[QuestionRequest] = [] - class LibraryFileResponse(LibraryFileRequest): id: int uploadTime: str - questions: List[QuestionResponse] = [] class Config: from_attributes = True diff --git a/schemas/mistake.py b/schemas/mistake.py index bf82671..f0b7f99 100644 --- a/schemas/mistake.py +++ b/schemas/mistake.py @@ -3,8 +3,7 @@ from typing import List from pydantic import BaseModel -class MistakeResponse(BaseModel): - id: int +class MistakeRequest(BaseModel): subject: str module: str name: str @@ -12,7 +11,13 @@ class MistakeResponse(BaseModel): question: str options: List[str] answer: List[str] - wrongCount: int + + class Config: + from_attributes = True + + +class MistakeResponse(MistakeRequest): + id: int class Config: from_attributes = True diff --git a/schemas/practice.py b/schemas/practice.py index dd97cf9..3f277dd 100644 --- a/schemas/practice.py +++ b/schemas/practice.py @@ -2,8 +2,6 @@ from typing import List from pydantic import BaseModel -from schemas.library import QuestionRequest - class PracticeRecordRequest(BaseModel): libraryFileId: int @@ -20,14 +18,6 @@ class PracticeRecordResponse(PracticeRecordRequest): from_attributes = True -class PracticeQuestionResponse(QuestionRequest): - id: int - select: List[str] - - class Config: - from_attributes = True - - class PracticeCheckResult(BaseModel): id: int answer: List[str] @@ -35,4 +25,4 @@ class PracticeCheckResult(BaseModel): isCorrect: bool class Config: - from_attributes = True \ No newline at end of file + from_attributes = True diff --git a/service/library_service.py b/service/library_service.py index bbfbd55..0704a78 100644 --- a/service/library_service.py +++ b/service/library_service.py @@ -1,9 +1,8 @@ -from sqlalchemy import select, delete +from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy.orm import selectinload -from models.library import LibraryFile, LibraryFileQuestion -from schemas.library import LibraryFileRequest, LibraryFileResponse, QuestionResponse +from models.library import LibraryFile +from schemas.library import LibraryFileRequest, LibraryFileResponse async def list_files(db: AsyncSession) -> list[LibraryFileResponse]: @@ -20,7 +19,6 @@ async def list_files(db: AsyncSession) -> list[LibraryFileResponse]: size=file.size, content=file.content, uploadTime=file.create_time.strftime("%Y-%m-%d %H:%M:%S"), - questions=[], ) for file in files ] @@ -28,7 +26,7 @@ async def list_files(db: AsyncSession) -> list[LibraryFileResponse]: async def add_file(db: AsyncSession, data: LibraryFileRequest) -> bool: """ - 创建题库文件及其题目 + 创建题库文件 """ file = LibraryFile( subject=data.subject, @@ -40,18 +38,6 @@ async def add_file(db: AsyncSession, data: LibraryFileRequest) -> bool: ) db.add(file) - await db.flush() - - for q in data.questions: - question = LibraryFileQuestion( - library_file_id=file.id, - type=q.type, - question=q.question, - options=q.options, - answer=q.answer - ) - db.add(question) - await db.commit() return True @@ -59,7 +45,7 @@ async def add_file(db: AsyncSession, data: LibraryFileRequest) -> bool: async def edit_file(db: AsyncSession, file_id: int, data: LibraryFileRequest) -> bool: """ - 编辑题库文件及其题目 + 编辑题库文件 """ result = await db.execute( @@ -76,33 +62,13 @@ async def edit_file(db: AsyncSession, file_id: int, data: LibraryFileRequest) -> file.type = data.type file.content = data.content - await db.execute( - delete(LibraryFileQuestion) - .where(LibraryFileQuestion.library_file_id == file.id) - ) - - # 4. 插入新题目 - for q in data.questions: - question = LibraryFileQuestion( - library_file_id=file.id, - type=q.type, - question=q.question, - options=q.options, - answer=q.answer, - ) - db.add(question) - await db.commit() return True async def get_file(db: AsyncSession, file_id: int) -> LibraryFileResponse: - stmt = ( - select(LibraryFile) - .options(selectinload(LibraryFile.questions)) - .where(LibraryFile.id == file_id) - ) + stmt = (select(LibraryFile).where(LibraryFile.id == file_id)) result = await db.execute(stmt) file = result.scalar_one_or_none() @@ -115,14 +81,4 @@ async def get_file(db: AsyncSession, file_id: int) -> LibraryFileResponse: size=file.size, content=file.content, uploadTime=file.create_time.strftime("%Y-%m-%d %H:%M:%S"), - questions=[ - QuestionResponse( - id=q.id, - type=q.type, - question=q.question, - options=q.options, - answer=q.answer - ) - for q in file.questions - ], ) diff --git a/service/mistake_service.py b/service/mistake_service.py index a48222c..e32ef54 100644 --- a/service/mistake_service.py +++ b/service/mistake_service.py @@ -1,68 +1,51 @@ -from sqlalchemy import select +from sqlalchemy import select, delete from sqlalchemy.ext.asyncio import AsyncSession -from models.library import Mistake, LibraryFile, LibraryFileQuestion -from schemas.mistake import MistakeResponse +from models.library import Mistake +from schemas.mistake import MistakeResponse, MistakeRequest async def list_mistakes(db: AsyncSession) -> list[MistakeResponse]: - stmt = ( - select( - Mistake.id, - LibraryFile.subject, - LibraryFile.module, - LibraryFile.name, - LibraryFileQuestion.type, - LibraryFileQuestion.question, - LibraryFileQuestion.options, - LibraryFileQuestion.answer, - Mistake.wrong_count, - ) - .join( - LibraryFileQuestion, - Mistake.question_id == LibraryFileQuestion.id, - ) - .join( - LibraryFile, - Mistake.library_file_id == LibraryFile.id, - ) - ) - stmt = stmt.order_by(Mistake.create_time.desc()) - result = await db.execute(stmt) - rows = result.mappings().all() + stmt = select(Mistake).order_by(Mistake.id.desc()) + mistakes = (await db.execute(stmt)).scalars().all() return [ MistakeResponse( - id=row["id"], - subject=row["subject"], - module=row["module"], - name=row["name"], - type=row["type"], - question=row["question"], - options=row["options"], - answer=row["answer"], - wrongCount=row["wrong_count"], + id=mistake.id, + subject=mistake.subject, + module=mistake.module, + name=mistake.name, + type=mistake.type, + question=mistake.question, + options=mistake.options, + answer=mistake.answer ) - for row in rows + for mistake in mistakes ] -async def update_mistake(db: AsyncSession, library_file_id: int, question_id: int): - stmt = select(Mistake).where( - Mistake.library_file_id == library_file_id, - Mistake.question_id == question_id, +async def add_mistakes(db: AsyncSession, data: list[MistakeRequest]): + db.add_all( + [ + Mistake( + subject=item.subject, + module=item.module, + name=item.name, + type=item.type, + question=item.question, + options=item.options, + answer=item.answer, + ) + for item in data + ] ) - result = await db.execute(stmt) - wrong = result.scalar_one_or_none() - if wrong: - wrong.wrong_count += 1 - else: - wrong = Mistake( - library_file_id=library_file_id, - question_id=question_id, - wrong_count=1 - ) - db.add(wrong) + await db.commit() + return True - await db.flush() + +async def remove_mistake(db: AsyncSession, mistake_id: int) -> bool: + await db.execute(delete(Mistake).where(Mistake.id == mistake_id)) + await db.commit() + + return True diff --git a/service/practice_service.py b/service/practice_service.py index 641ece9..5ecba50 100644 --- a/service/practice_service.py +++ b/service/practice_service.py @@ -2,18 +2,13 @@ from typing import List from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy.orm import selectinload -from models.library import LibraryFile, PracticeRecord -from schemas.practice import PracticeQuestionResponse, PracticeRecordRequest, PracticeCheckResult, \ - PracticeRecordResponse -from service.library_service import get_file -from service.mistake_service import update_mistake +from models.library import PracticeRecord +from schemas.practice import PracticeRecordRequest, PracticeRecordResponse async def get_record(db: AsyncSession, library_file_id: int) -> List[PracticeRecordResponse]: - stmt = select(PracticeRecord).where(PracticeRecord.library_file_id == library_file_id).order_by( - PracticeRecord.create_time.desc()) + stmt = select(PracticeRecord).where(PracticeRecord.library_file_id == library_file_id).order_by(PracticeRecord.create_time.desc()) records = (await db.execute(stmt)).scalars().all() return [ @@ -29,71 +24,7 @@ async def get_record(db: AsyncSession, library_file_id: int) -> List[PracticeRec ] -async def get_question(db: AsyncSession, file_id: int) -> List[PracticeQuestionResponse]: - stmt = ( - select(LibraryFile) - .options(selectinload(LibraryFile.questions)) - .where(LibraryFile.id == file_id) - ) - result = await db.execute(stmt) - file = result.scalar_one_or_none() - - return [ - PracticeQuestionResponse( - id=q.id, - type=q.type, - question=q.question, - options=q.options, - answer=[], - select=[] - ) - for q in file.questions - ] - - -async def check_practice(db: AsyncSession, - library_file_id: int, - questions: List[PracticeQuestionResponse]) -> List[PracticeCheckResult]: - library_file = await get_file(db, library_file_id) - db_questions = library_file.questions - check_results = [] - - for user_q in questions: - db_q = next((q for q in db_questions if q.id == user_q.id), None) - if not db_q: - continue - - is_correct = set(user_q.select) == set(db_q.answer) - - if not is_correct: - await update_mistake( - db=db, - library_file_id=library_file_id, - question_id=user_q.id, - ) - - check_results.append(PracticeCheckResult( - id=user_q.id, - answer=db_q.answer, - select=user_q.select, - isCorrect=is_correct, - )) - - correct_count = sum(1 for r in check_results if r.isCorrect) - await add_practice_record( - db, - PracticeRecordRequest( - libraryFileId=library_file_id, - totalCount=len(check_results), - correctCount=correct_count, - wrongCount=len(check_results) - correct_count, - ), - ) - - return check_results - - -async def add_practice_record(db: AsyncSession, data: PracticeRecordRequest): +async def add_record(db: AsyncSession, data: PracticeRecordRequest): record = PracticeRecord( library_file_id=data.libraryFileId, total_count=data.totalCount,