227 lines
6.4 KiB
Python
227 lines
6.4 KiB
Python
"""知识点关系:全局 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
|