PMS Sync
Deploy 000b308 (flattened for HF Spaces)
4577459
Raw
History Blame Contribute Delete
3.76 kB
"""Export study artifacts: final sheet, Anki CSV."""
from __future__ import annotations
import csv
import io
from typing import Any
from plane_mode_scholar.core.config import DEFAULT_USER_ID
from plane_mode_scholar.memory.scheduler import get_due_reviews
from plane_mode_scholar.storage.sqlite_store import SQLiteStore
from plane_mode_scholar.study.mastery import MasteryTracker
def build_final_sheet_markdown(
store: SQLiteStore,
pack_id: str,
user_id: str = DEFAULT_USER_ID,
) -> str:
pack = store.get_pack(pack_id)
mastery = MasteryTracker(store)
due = get_due_reviews(store, pack_id, user_id)
weak = mastery.get_weak_topics(pack_id)
topics = store.list_topics(pack_id)
misconceptions = [
m for m in store.list_memories(pack_id=pack_id, user_id=user_id, status="active")
if m.type.value == "misconception"
]
lines = [
f"# Final sheet — {pack.name if pack else pack_id}",
"",
"## Must-review (due now)",
]
if due:
for m in due[:8]:
lines.append(f"- {m.content}")
else:
lines.append("- None due — great job!")
lines.append("")
lines.append("## Weak topics")
if weak:
for w in weak[:6]:
lines.append(f"- **{w['name']}** ({int(w.get('mastery', 0) * 100)}%)")
else:
lines.append("- Complete quizzes to identify weak areas.")
lines.append("")
lines.append("## Common gaps")
if misconceptions:
for m in misconceptions[:6]:
lines.append(f"- {m.content[:120]}")
else:
lines.append("- No recorded misconceptions yet.")
lines.append("")
lines.append("## Syllabus checklist")
for t in topics[:12]:
lines.append(f"- [ ] {t.name}")
lines.append("")
lines.append("---")
lines.append("*Generated by Plane Mode Scholar — study offline, remember on landing.*")
return "\n".join(lines)
def build_anki_csv(
store: SQLiteStore,
pack_id: str,
user_id: str = DEFAULT_USER_ID,
) -> str:
"""Anki-compatible CSV: front, back, tags."""
buf = io.StringIO()
writer = csv.writer(buf, quoting=csv.QUOTE_MINIMAL)
writer.writerow(["Front", "Back", "Tags"])
memories = store.list_memories(pack_id=pack_id, user_id=user_id, status="active")
for mem in memories:
if mem.type.value in ("misconception", "review_item", "open_loop"):
front = mem.content[:200]
if mem.type.value == "misconception" and " instead of " in mem.content:
parts = mem.content.split(" instead of ")
back = parts[-1].strip("'") if parts else mem.content
else:
back = mem.content
tags = " ".join(t for t in mem.tags[:5] if not t.startswith(("due:", "interval:", "reps:")))
writer.writerow([front, back[:300], tags or mem.type.value])
chunks = store.list_chunks(pack_id)[:20]
for chunk in chunks:
text = chunk.text.strip()
if len(text) < 40:
continue
sentences = [s.strip() for s in text.split(".") if len(s.strip()) > 25]
if sentences:
front = f"What does {chunk.source_file} say about this topic?"
back = sentences[0][:300]
writer.writerow([front, back, f"chunk {chunk.source_file}"])
return buf.getvalue()
def build_final_sheet_html(
store: SQLiteStore,
pack_id: str,
user_id: str = DEFAULT_USER_ID,
) -> str:
md = build_final_sheet_markdown(store, pack_id, user_id)
body = md.replace("\n## ", "\n<h2>").replace("\n- ", "\n<li>")
body = body.replace("# ", "<h1>").replace("**", "<strong>", 1)
return f'<div class="pms-card pms-final-sheet"><pre class="pms-sheet">{md}</pre></div>'