Skip to content

Commit 875bdb0

Browse files
fix(web): Course Vendor dropdown populates from study root directly
_courses_roots() required a Study/Courses/ subdir that the real vault doesn't have (vendors live directly under Study/), leaving the Course Vendor picker empty and cascading to an empty Course picker. Now scans each study root directly when it has no Courses/ child, honouring the nested layout when present. Verified live in browser: 8 vendors + 29 courses populate with correct parent linkage.
1 parent 86fa64c commit 875bdb0

2 files changed

Lines changed: 56 additions & 4 deletions

File tree

packages/studyloop/src/studyloop/web/routes/session.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,9 +1256,10 @@ async def ws_to_pty() -> None:
12561256
if not request_id:
12571257
# Missing requestId — silently drop, can't correlate.
12581258
pass
1259-
elif hasattr(transport, "send_permission_response"):
1260-
if isinstance(outcome, dict):
1261-
await transport.send_permission_response(request_id, outcome)
1259+
elif hasattr(transport, "send_permission_response") and isinstance(
1260+
outcome, dict
1261+
):
1262+
await transport.send_permission_response(request_id, outcome)
12621263
# Silently drop unknown frame types — no error channel needed.
12631264

12641265
# --- Pump with TaskGroup (plan Blocker B5) ---------------------------
@@ -1418,7 +1419,22 @@ def _lesson_options() -> list[SessionOption]:
14181419

14191420

14201421
def _courses_roots() -> list[Path]:
1421-
candidates = [root / "Courses" for root in _study_roots()]
1422+
# Course vendors (ArjanCodes, CodeWithMosh, …) live directly under each
1423+
# study root — the same level "Topic" targets scan. Requiring an
1424+
# intermediate ``Courses/`` directory left the vendor picker empty
1425+
# because the real vault has no such level.
1426+
#
1427+
# A ``Courses/`` subdirectory is still honoured when one exists, so a
1428+
# vault that nests courses under it keeps working. To avoid surfacing
1429+
# ``Courses`` itself as a bogus vendor in that case, a study root is
1430+
# only used directly when it has no ``Courses/`` child.
1431+
candidates: list[Path] = []
1432+
for root in _study_roots():
1433+
nested = root / "Courses"
1434+
if nested.is_dir():
1435+
candidates.append(nested)
1436+
else:
1437+
candidates.append(root)
14221438
candidates.extend(
14231439
[
14241440
Path("~/Obsidian/Personal/Study/Courses").expanduser(),

packages/studyloop/tests/test_web_live_session.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,39 @@ def __init__(self) -> None:
5151
assert "agents" in body
5252
assert all(agent["recommended_transport"] == "ttyd" for agent in body["agents"])
5353
assert all(agent["acp_ready"] is False for agent in body["agents"])
54+
55+
56+
def test_session_options_lists_vendors_directly_under_study_root(
57+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
58+
) -> None:
59+
"""Course vendors live directly under the study root (no ``Courses/`` level).
60+
61+
Regression: ``_courses_roots()`` required an intermediate ``Courses/``
62+
directory that does not exist in the real vault — vendor dirs
63+
(ArjanCodes, CodeWithMosh, …) sit directly under ``Study/``. The missing
64+
level left the Course Vendor picker empty, which cascaded to empty
65+
Course and Lesson pickers.
66+
"""
67+
study_root = tmp_path / "Study"
68+
lesson = study_root / "ArjanCodes" / "The_Software_Designer_Mindset" / "Module_01"
69+
lesson.mkdir(parents=True)
70+
(study_root / "CodeWithMosh").mkdir()
71+
72+
class Content:
73+
def __init__(self) -> None:
74+
self.study_paths = [study_root]
75+
76+
class Settings:
77+
def __init__(self) -> None:
78+
self.content = Content()
79+
80+
monkeypatch.setattr("studyloop.settings.load_settings", Settings)
81+
82+
client = TestClient(create_app())
83+
body = client.get("/api/session/options").json()
84+
85+
vendor_labels = {v["label"] for v in body["vendors"]}
86+
assert "ArjanCodes" in vendor_labels
87+
assert "CodeWithMosh" in vendor_labels
88+
assert any(c["label"] == "The Software Designer Mindset" for c in body["courses"])
89+
assert any(lesson_["label"] == "Module 01" for lesson_ in body["lessons"])

0 commit comments

Comments
 (0)