Skip to content

Commit caf0959

Browse files
committed
Merge branch 'main' into issue-829-3
2 parents 9548d88 + 8a06ac1 commit caf0959

98 files changed

Lines changed: 7346 additions & 1068 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
repos:
2+
- repo: https://github.com/psf/black
3+
rev: 24.4.2
4+
hooks:
5+
- id: black
6+
types: [python]
7+
files: ^(bases|components)/
8+
9+
- repo: https://github.com/astral-sh/ruff-pre-commit
10+
rev: v0.4.4
11+
hooks:
12+
- id: ruff
13+
types: [python]
14+
files: ^(bases|components)/

CHANGELOG.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# ChangeLog
2+
3+
## Updates since last changelog entry (2026-02-21 → 2026-02-27)
4+
5+
Coverage: commits from **2026-02-21** through **2026-02-27** (i.e., changes after the prior cutoff on 2026-02-20).
6+
7+
### Highlights
8+
9+
- **Learning clues (prototype) + student context improvements:** initial prototype of “learning clues” integration landed, plus follow-on work to add context and automate lookup of book ids (primarily in `assignment_server_api/routers/student.py` and `bookfuncs.js`).
10+
- **Peer/LLM chat robustness:** improvements to async peer messaging (prompt + behavior), fixes for message ordering, and a key fix so the correct API token field is used and LLM peer lookup can retrieve keys at call time.
11+
- **LTI1p3 UX:** better messaging when an LMS rejects access to an **expired course**.
12+
- **UI theming iteration:** dark-mode dropdown styling changes were introduced and then reverted (net effect: continued iteration/experimentation in this area).
13+
- **Code quality + dependencies:** a broad pass fixing **Black/Ruff** issues and updating lock files / dependencies.
14+
15+
### Commit notes (for reference)
16+
17+
- 1a181c81 Initial prototype of learning clues integration
18+
- 3f42fae0 log todos
19+
- 5964439a Add context and automate lookup of book ids
20+
- c523c054 Fix API token field selection + make LLM peer lookup retrieve keys at call time
21+
- 8e375a9c Fix message ordering in async peer chat display
22+
- c34931a8 update prompt and async peer messaging
23+
- bde25ecd LTI1p3: better message when LMS rejects accessing an expired course
24+
- eca2ee17 fix dark mode for dropdown menu…
25+
- cacd4e49 Revert "fix dark mode for dropdown menu…"
26+
- 968f1cd9 Fix all black errors
27+
- 0b40954b Fix all black and ruff issues
28+
29+
---
30+
31+
## Updates since last changelog entry (2026-02-12 → 2026-02-20)
32+
33+
Coverage: commits from **2026-02-12** through **2026-02-20** (i.e., changes after the prior cutoff on 2026-02-11).
34+
35+
### Highlights
36+
37+
- **Peer chat cleanup + reliability:** merged fixes to clean up the peer A/B chat experience and to ensure students can still send messages during synchronous chat.
38+
- **Interactive evaluation UX:** added a new `showEval` capability for interactives.
39+
- **Assessment data correctness:** fixed a shortanswer issue by correcting the underlying answers table name.
40+
- **Verbal discussion improvements:** updated the verbal discussion UI to show who a student is grouped with.
41+
- **Scratch ActiveCode layout polish:** adjusted Scratch ActiveCode positioning / CSS.
42+
- **Ops/config + dependency updates:** multiple internal updates (logging, course OpenAI key plumbing, fernet secret handling) plus package/version bumps.
43+
44+
### Commit notes (for reference)
45+
46+
- 6d5383df Merge PR #1153 (peer A/B chat cleanup)
47+
- 3efce80b new showEval for use with interactives
48+
- 8d56fcaa Fix: correct the table name for shortanswer answers
49+
- b2dfc2f1 Updated the verbal discussion to show who students are in a group with
50+
- 6a40b256 Merge PR #1147 (Scratch AC CSS update)
51+
- 36c319a3 Update packages
52+
- 0ccdc6e5 Merge PR #10 (fastapi-peer-llm)
53+
- dea3f13d ensure students can send message during sync chat
54+
- 90b2636f update logging
55+
- de3fa2a7 update get course openai key
56+
- 21f64240 update fernet secret
57+
- b6134724 new version
58+
59+
---
60+
61+
## Updates since last changelog entry (2026-02-07 → 2026-02-11)
62+
63+
Coverage: commits from **2026-02-07** through **2026-02-11** (since the prior cutoff of 2026-02-06).
64+
65+
### Highlights
66+
67+
- **Java/ActiveCode execution (JOBE) + unit test results:** wired in JOBE-based submission flow for Java ActiveCode with unit tests, and surfaced results end-to-end. This work primarily touched the personalized-parsons endpoints and ActiveCode client JS.
68+
- **Personalized Parsons cleanup:** removed unused variables and tidied related evaluation code.
69+
- **Question counting fixes:** corrected “number of questions” accounting in both backend CRUD (`question.py`) and frontend book utilities (`bookfuncs.js`).
70+
- **Build output enhancements:** added a new preprocessor to inject **GitHub source links** into generated HTML output (`add_github_links.py`).
71+
- **Repo hygiene:** removed empty placeholder files (`content`, `docker-compose.override.yml`, `pi_attempt_id`).
72+
- **Release/version bump:** bumped interactives version (`projects/interactives/pyproject.toml`).
73+
74+
### Commit notes (for reference)
75+
76+
- 682e940d Applied JOBE for submitting Javacode with unit tests and get results
77+
- bf3b9553 removed unused variables
78+
- 06b5b8da Remove empty placeholder files
79+
- 2ebf9adb preprocessor to add github links to html output
80+
- 30773448 Fix: get number of questions correct
81+
- 6c6d1fa9 Count questions correctly
82+
- 4bfc6ed4 new version
83+
84+
---
85+
86+
## 2026 (Year to Date)
87+
88+
Coverage: commits from **2026-01-01** through **2026-02-06**.
89+
90+
### Themes
91+
92+
- **Assignment experience + navigation:** continued refinement of assignment navigation (including “readings” integration) and UI polish.
93+
- **Authoring/build stability:** better build hygiene in `rsptx` tooling (clean logs, cleaned output folders) and dependency work to reduce PreTeXt friction.
94+
- **Instructor/admin capabilities:** expanded tooling for course administration (token cleanup, CSV enrollment) and billing/invoice-related fixes.
95+
- **Assignment Builder options:** improved exercise configuration—especially for ActiveCode (CodeLens) and the new/expanded IFrame exercise type.
96+
97+
### Notable changes (grouped)
98+
99+
#### Releases / version bumps
100+
- Multiple **release/version** bumps (primarily in `projects/interactives/pyproject.toml`).
101+
102+
#### Assignment navigation + readings UX
103+
- Implemented and iterated on **two-way assignment navigation** (top/bottom navigation, readings integration, styling fixes).
104+
- Added `readingNames` support and related UI/markup adjustments.
105+
- Improved assignment navigation behavior and added material icons to assignment pages.
106+
107+
#### Assignment Builder: new capabilities and settings fixes
108+
- **IFrame exercise type:** added IFrame exercise type/components and later removed an iframe height restriction in preview/input.
109+
- **ActiveCode improvements:**
110+
- Added support for enabling/disabling **CodeLens**.
111+
- Tightened up settings/preview plumbing and types.
112+
- **CodeTailor options:** updated handling to correctly modify `parsonspersonalize` values.
113+
- Misc. robustness fixes around label toggles (avoid replace/split when `toggleLabels` is null).
114+
115+
#### Peer / Parsons + assessment behavior
116+
- Parsons improvements including fallback to the problem source when restoring a student answer fails.
117+
- Multiple changes in the peer/PI area (dashboard + templates + JS), along with ongoing refinements in the peer router.
118+
- Fixes for MathJax processing and a regression involving counting questions for async.
119+
120+
#### Instructor/Admin operations
121+
- **API token cleanup:** added ability to delete API token(s) for a course, including a “delete all tokens” capability and supporting UI.
122+
- **CSV enrollment:** allow a user to be enrolled in a new course by CSV.
123+
124+
#### Billing / invoicing
125+
- Fixes related to course creation billing flows (invoice checkbox handling) and an additional invoice request fix.
126+
127+
#### Build tooling + dependencies (PreTeXt/author server)
128+
- Build improvements in `components/rsptx/build_tools/core.py`:
129+
- Start builds with a clean log
130+
- Clean output folders more reliably
131+
- Remove leftover debugging (`set_trace`)
132+
- `rsmanage build` gained a `--target` option.
133+
- Dependency work to address **PreTeXt** issues (notably updates in `projects/author_server/poetry.lock` / `pyproject.toml`).
134+
135+
### Month-by-month timeline
136+
137+
#### January 2026
138+
- Merged/landed the two-way assignment navigation work and related readings UI improvements.
139+
- Added/expanded Assignment Builder capabilities (CodeTailor options; IFrame exercise type).
140+
- Improved assignment sorting and decoration of assigned problems.
141+
- Multiple dependency updates (lxml/pretext/runestone) and several small bug-fix releases.
142+
- Addressed billing/invoicing edge cases.
143+
144+
#### February 2026 (so far)
145+
- Instructor tooling: token deletion support (including bulk delete).
146+
- Authoring/build stability: PreTeXt dependency fixes; cleaner build logs; output folder cleanup; `rsmanage build --target`.
147+
- Minor version bumps and cleanup.
148+
149+
---
150+
151+
### How to read this repo’s recent work
152+
Most of the work since Jan 1 clusters into three areas:
153+
1) **Learner/instructor experience** (assignment nav + builder settings)
154+
2) **Operational/admin tooling** (billing, enrollment, token hygiene)
155+
3) **Platform stability** (dependencies + build predictability)

bases/rsptx/admin_server_api/routers/instructor.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
fetch_base_course,
4141
fetch_course_by_id,
4242
fetch_course,
43+
fetch_courses_for_user,
4344
fetch_group,
4445
fetch_instructor_courses,
4546
fetch_library_books,
@@ -862,16 +863,44 @@ async def enroll_students(
862863
accept_tcp=False,
863864
)
864865
# Check if user exists
865-
if await fetch_user(user_data.username):
866-
results.append(
867-
{
868-
"username": user_data.username,
869-
"status": "already exists",
870-
"category": "duplicate",
871-
}
872-
)
873-
failed += 1
874-
continue
866+
temp_user = await fetch_user(user_data.username)
867+
if temp_user:
868+
if temp_user.username == row[0] and temp_user.email == row[1]:
869+
# User exists, enroll in course if not already enrolled
870+
user_courses = await fetch_courses_for_user(temp_user.id)
871+
if any(mc.id == course.id for mc in user_courses):
872+
results.append(
873+
{
874+
"username": user_data.username,
875+
"status": "already enrolled",
876+
"category": "duplicate",
877+
}
878+
)
879+
failed += 1
880+
continue
881+
else:
882+
await create_user_course_entry(
883+
temp_user.id, course.id
884+
) # Enroll the user in the course
885+
results.append(
886+
{
887+
"username": user_data.username,
888+
"status": "enrolled existing user",
889+
"category": "success",
890+
}
891+
)
892+
enrolled += 1
893+
continue
894+
else:
895+
results.append(
896+
{
897+
"username": user_data.username,
898+
"status": "already exists with a different email",
899+
"category": "duplicate",
900+
}
901+
)
902+
failed += 1
903+
continue
875904
new_user = await create_user(user_data)
876905
await create_user_course_entry(
877906
new_user.id, course.id
@@ -1052,6 +1081,7 @@ async def _copy_one_assignment(
10521081
course=target_course.id,
10531082
name=old_assignment.name,
10541083
duedate=due_date,
1084+
updated_date=datetime.datetime.now(),
10551085
description=old_assignment.description,
10561086
points=old_assignment.points,
10571087
threshold_pct=old_assignment.threshold_pct,
@@ -1226,9 +1256,7 @@ async def post_create_course_page(
12261256

12271257
# if invoice is true then we need to create an invoice for the course
12281258
if invoice == "true":
1229-
res = await create_invoice_request(
1230-
user.username, projectname, 0.0, user.email
1231-
)
1259+
await create_invoice_request(user.username, projectname, 0.0, user.email)
12321260
# Copy attributes from base course
12331261
bc = await fetch_course(coursetype)
12341262
attrs = await fetch_all_course_attributes(bc.id)

bases/rsptx/admin_server_api/routers/lti1p3.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
fetch_instructor_courses,
7878
validate_user_credentials,
7979
)
80+
from rsptx.db.crud.assignment import is_assignment_visible_to_students
8081

8182
from rsptx.configuration import settings
8283
from rsptx.logging import rslogger
@@ -445,7 +446,10 @@ async def launch(request: Request):
445446
status_code=400, detail=f"Assignment {lineitem_assign_id} not found"
446447
)
447448

448-
if not rs_assign.visible and not message_launch.check_teacher_access():
449+
if (
450+
not is_assignment_visible_to_students(rs_assign)
451+
and not message_launch.check_teacher_access()
452+
):
449453
raise HTTPException(
450454
status_code=400,
451455
detail=f"Assignment {rs_assign.name} is not open for students",
@@ -874,10 +878,19 @@ async def dynamic_link_entry(request: Request):
874878
assignments.sort(key=lambda a: a.name)
875879
assignments.sort(key=lambda a: a.duedate)
876880

877-
# all lineitems in the LMS
878-
ags = message_launch.get_ags()
879-
l_items = await ags.get_lineitems()
881+
try:
882+
# all lineitems in the LMS - may be rejected if course is closed in LMS
883+
ags = message_launch.get_ags()
884+
l_items = await ags.get_lineitems()
885+
except LtiServiceException as e:
886+
rslogger.error(f"LTI1p3 - Error accessing course line items: {e}")
887+
raise HTTPException(
888+
status_code=422,
889+
detail="This course appears to be closed in your Learning Management System.",
890+
)
891+
880892
l_items.sort(key=lambda li: li.get("label"))
893+
881894
# build lookup dicts for line items
882895
l_items_resourceId_dict = {li.get("resourceId"): li for li in l_items}
883896
l_items_label_dict = {li.get("label"): li for li in l_items}

0 commit comments

Comments
 (0)