Files
HeurAMS/src/heurams/unifront/routes/atoms.py
T

193 lines
5.9 KiB
Python

"""原子信息 REST 路由"""
from fastapi import APIRouter, HTTPException
import heurams.kernel.particles as pt
import heurams.services.timer as timer
from heurams.context import config_var
from heurams.services.favorite_service import favorite_manager
from heurams.services.logger import get_logger
from ..dependencies import get_repo, compute_repo_progress
logger = get_logger(__name__)
router = APIRouter(prefix="/api/repos/{package}", tags=["atoms"])
def _safe_lastdate(e) -> int:
try:
return e.lastdate()
except (KeyError, TypeError):
return 0
@router.get("/atoms")
def list_atoms(package: str, page: int = 1, page_size: int = 50) -> dict:
"""获取仓库的原子列表"""
repo = get_repo(package)
if repo is None:
raise HTTPException(status_code=404, detail=f"仓库 '{package}' 未找到")
idents = list(repo.ident_index)
total = len(idents)
start = (page - 1) * page_size
end = start + page_size
page_idents = idents[start:end]
items = []
for ident in page_idents:
e = pt.Electron.from_data(
electronic_data=repo.electronic_data_lict.get_itemic_unit(ident),
algo_name=repo.config["algorithm"],
)
items.append({
"ident": ident,
"activated": bool(e.is_activated()),
"due": e.is_due(),
"rept": e.rept(real_rept=True),
"interval": e["interval"],
"next_date": e.nextdate(),
"last_date": _safe_lastdate(e),
})
return {"total": total, "page": page, "page_size": page_size, "items": items}
@router.get("/atoms/{ident}")
def get_atom(package: str, ident: str) -> dict:
"""获取单个原子的完整信息"""
repo = get_repo(package)
if repo is None:
raise HTTPException(status_code=404, detail=f"仓库 '{package}' 未找到")
if ident not in repo.ident_index:
raise HTTPException(status_code=404, detail=f"原子 '{ident}' 未找到")
n = pt.Nucleon.from_data(
nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(ident)
)
e = pt.Electron.from_data(
electronic_data=repo.electronic_data_lict.get_itemic_unit(ident),
algo_name=repo.config["algorithm"],
)
nucleon_data = {}
for key in n:
if key in ("puzzles",):
continue
try:
nucleon_data[key] = n[key]
except Exception:
pass
return {
"ident": ident,
"electron": {
"activated": bool(e.is_activated()),
"due": e.is_due(),
"rept": e.rept(real_rept=True),
"interval": e["interval"],
"next_date": e.nextdate(),
"last_date": _safe_lastdate(e),
"last_modify": e.last_modify(),
"algodata": e.algodata.get(e.algoname, {}),
},
"nucleon": nucleon_data,
}
@router.get("/prepare")
def prepare_repo(package: str) -> dict:
"""获取仓库复习准备数据(repo 信息 + 所有原子状态预览)"""
repo = get_repo(package)
if repo is None:
raise HTTPException(status_code=404, detail=f"仓库 '{package}' 未找到")
progress = compute_repo_progress(repo)
today = timer.get_daystamp()
atoms = []
review_count = 0
new_count = 0
for i in repo.ident_index:
e = pt.Electron.from_data(
electronic_data=repo.electronic_data_lict.get_itemic_unit(i),
algo_name=repo.config["algorithm"],
)
activated = bool(e.is_activated())
due = e.is_due()
if activated and due:
status = "R"
review_count += 1
elif activated:
status = "A"
else:
status = "U"
new_count += 1
n = pt.Nucleon.from_data(
nucleonic_data=repo.nucleonic_data_lict.get_itemic_unit(i)
)
content = n.get("content", "") or n.get("tts_text", "") or i
atoms.append({
"ident": i,
"status": status,
"rept": e.rept(real_rept=True),
"interval": e["interval"],
"next_date": e.nextdate(),
"content": str(content)[:60],
})
schedule_num = repo.config["scheduled_num"]
return {
"repo": {
"package": repo.manifest.get("package", package),
"title": repo.manifest.get("title", package),
"author": repo.manifest.get("author", ""),
"desc": repo.manifest.get("desc", ""),
"source": str(repo.source),
"algorithm": repo.config["algorithm"],
"scheduled_num": schedule_num,
},
"progress": progress,
"preview": {"review": review_count, "new": new_count},
"today": today,
"atoms": atoms,
"total_atoms": len(atoms),
}
@router.get("/atoms/{ident}/favorite")
def get_favorite(package: str, ident: str) -> dict:
"""检查原子是否已收藏"""
repo = get_repo(package)
if repo is None:
raise HTTPException(status_code=404)
rel_path = _rel_repo_path(repo)
return {"favorited": favorite_manager.has(rel_path, ident)}
@router.post("/atoms/{ident}/favorite")
def toggle_favorite(package: str, ident: str) -> dict:
"""切换收藏状态"""
repo = get_repo(package)
if repo is None:
raise HTTPException(status_code=404)
rel_path = _rel_repo_path(repo)
if favorite_manager.has(rel_path, ident):
favorite_manager.remove(rel_path, ident)
return {"favorited": False, "action": "removed"}
else:
favorite_manager.add(rel_path, ident)
return {"favorited": True, "action": "added"}
def _rel_repo_path(repo) -> str:
"""获取仓库相对路径"""
from heurams.context import workdir
data_repo = workdir / "data" / "repo"
try:
return str(repo.source.relative_to(data_repo))
except (ValueError, AttributeError):
return str(repo.source)