"""知识点关系:全局 Knowledge + 章节/题目多对多 + 用户掌握度。""" from __future__ import annotations from sqlalchemy.orm import Session from models import ( ChapterKnowledge, Knowledge, KnowledgeRelation, KnowledgeResource, Question, QuestionKnowledge, UserKnowledge, ) def ensure_knowledge(db: Session, name: str) -> Knowledge: clean = name.strip() row = db.query(Knowledge).filter(Knowledge.name == clean).first() if row is None: row = Knowledge(name=clean) db.add(row) db.flush() return row def ensure_knowledge_relation( db: Session, source: Knowledge, target: Knowledge, relation_type: str = "前置", ) -> KnowledgeRelation: row = ( db.query(KnowledgeRelation) .filter( KnowledgeRelation.source_id == source.id, KnowledgeRelation.target_id == target.id, KnowledgeRelation.relation_type == relation_type, ) .first() ) if row is None: row = KnowledgeRelation( source_id=source.id, target_id=target.id, relation_type=relation_type, ) db.add(row) db.flush() return row def chapter_knowledge_names(db: Session, chapter_id: int) -> list[str]: rows = ( db.query(Knowledge.name) .join(ChapterKnowledge, ChapterKnowledge.knowledge_id == Knowledge.id) .filter(ChapterKnowledge.chapter_id == chapter_id) .order_by(ChapterKnowledge.id.asc()) .all() ) return [name for (name,) in rows] def knowledge_resources(db: Session, knowledge_id: int) -> list[KnowledgeResource]: return ( db.query(KnowledgeResource) .filter(KnowledgeResource.knowledge_id == knowledge_id) .order_by(KnowledgeResource.position.asc(), KnowledgeResource.id.asc()) .all() ) def ensure_knowledge_resource( db: Session, knowledge_id: int, *, kind: str, title: str, url: str, textbook_id: int | None = None, course_id: int | None = None, ) -> KnowledgeResource: row = ( db.query(KnowledgeResource) .filter( KnowledgeResource.knowledge_id == knowledge_id, KnowledgeResource.kind == kind, KnowledgeResource.url == url, ) .first() ) if row is None: row = KnowledgeResource( knowledge_id=knowledge_id, kind=kind, title=title, url=url, textbook_id=textbook_id, course_id=course_id, ) db.add(row) db.flush() return row def set_chapter_knowledge_names( db: Session, chapter_id: int, names: list[str] ) -> list[Knowledge]: cleaned = list(dict.fromkeys(name.strip() for name in names if name.strip())) db.query(ChapterKnowledge).filter( ChapterKnowledge.chapter_id == chapter_id ).delete(synchronize_session=False) db.flush() ordered: list[Knowledge] = [] for name in cleaned: knowledge = ensure_knowledge(db, name) db.add(ChapterKnowledge(chapter_id=chapter_id, knowledge_id=knowledge.id)) ordered.append(knowledge) db.flush() return ordered def set_chapter_knowledge_ids( db: Session, chapter_id: int, knowledge_ids: list[int] ) -> list[Knowledge]: ids = list(dict.fromkeys(int(kid) for kid in knowledge_ids)) rows = ( db.query(Knowledge).filter(Knowledge.id.in_(ids)).all() if ids else [] ) by_id = {row.id: row for row in rows} if len(by_id) != len(ids): raise ValueError("部分知识点不存在") db.query(ChapterKnowledge).filter( ChapterKnowledge.chapter_id == chapter_id ).delete(synchronize_session=False) db.flush() ordered = [by_id[kid] for kid in ids] for knowledge in ordered: db.add(ChapterKnowledge(chapter_id=chapter_id, knowledge_id=knowledge.id)) db.flush() return ordered def question_knowledge_names(db: Session, question: Question) -> list[str]: rows = ( db.query(Knowledge.name) .join(QuestionKnowledge, QuestionKnowledge.knowledge_id == Knowledge.id) .filter(QuestionKnowledge.question_id == question.id) .order_by(QuestionKnowledge.id.asc()) .all() ) names = [name for (name,) in rows] return names or ([question.knowledge_name] if question.knowledge_name else []) def set_question_knowledge_names( db: Session, question: Question, names: list[str], *, attach_to_chapter: bool = False, ) -> list[Knowledge]: cleaned = list(dict.fromkeys(name.strip() for name in names if name.strip())) if not cleaned and question.knowledge_name: cleaned = [question.knowledge_name] db.query(QuestionKnowledge).filter( QuestionKnowledge.question_id == question.id ).delete(synchronize_session=False) db.flush() ordered: list[Knowledge] = [] for name in cleaned: knowledge = ensure_knowledge(db, name) db.add( QuestionKnowledge(question_id=question.id, knowledge_id=knowledge.id) ) ordered.append(knowledge) if attach_to_chapter and question.chapter_id is not None: exists = ( db.query(ChapterKnowledge) .filter( ChapterKnowledge.chapter_id == question.chapter_id, ChapterKnowledge.knowledge_id == knowledge.id, ) .first() ) if exists is None: db.add( ChapterKnowledge( chapter_id=question.chapter_id, knowledge_id=knowledge.id, ) ) db.flush() if cleaned: question.knowledge_name = cleaned[0] return ordered def ensure_user_knowledge( db: Session, user_id: int, knowledge: Knowledge ) -> UserKnowledge: row = ( db.query(UserKnowledge) .filter( UserKnowledge.user_id == user_id, UserKnowledge.knowledge_id == knowledge.id, ) .first() ) if row is None: max_position = ( db.query(UserKnowledge.position) .filter(UserKnowledge.user_id == user_id) .count() ) row = UserKnowledge( user_id=user_id, knowledge_id=knowledge.id, mastery=60, position=max_position, ) db.add(row) db.flush() return row