Skip to content

Conversation

@barnesc3
Copy link

Summary

Users

  • Full names are now stored at creation (User.name is populated; no more nulls).

Admin Reports

  • Courses Page: Added an Instructors column in the table.
  • Single Course Report: Instructor list displayed under the course title (renders only when present).
  • Refresh Logic: Instructor list refreshes from the LMS when stale (TTL = 24h).

Key Changes

Configuration

  • LTI tool config updated to provide the user’s display name (privacy_level = name_only).
  • Existing session → DB logic now ensures User.name is set during user creation.

Backend

  • Introduced course_user join table (CourseUser mapping) to cache instructors:
    • Columns: course_id (FK), user_id (nullable FK), lms_user_id, display_name, fetched_at.
    • Constraints/Indexes: Unique (course_id, lms_user_id); indexed on course_id, user_id.
  • LMS integration fetches active teacher enrollments (fallback: users) and upserts into course_user.
  • API now returns instructors: string[] for:
    • GET /api/admin/courses/account/{account}/term/{term}
    • GET /api/admin/courses/{course}/reports/full
  • Cache invalidation: refresh from LMS if empty or older than 24h.

Frontend

  • CoursesPage: Added Instructors header/column with comma-separated names.
  • ReportsPage (single course): Render instructor list under the title; hidden when empty.

@mbusch3
Copy link
Contributor

mbusch3 commented Oct 3, 2025

NOTE: This commit changes the database schema, but does include a migration. In order to update the database to match the new commit, go to Docker desktop and click on the udoit3-php container when it's running. On the tab bar, select "Exec" and then run:
php bin/console doctrine:migrations:migrate
to run the migration script. This should update your local DB. I'd already deleted the var/cache/prod folder and rebuilt, but had no other issues getting this up and running.

Copy link
Contributor

@mbusch3 mbusch3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this out, and it works beautifully on my system, and the code looks clean and readable.

There is one issue, however, and that's when I'm on the Courses page and choose to scan a course. After the scan returns with data, it rewrites that data and I no longer have the instructor name(s). One fix I've done on my end is in AdminApp, I've tried to preserve any instructors.

  const handleCourseUpdate = (courseData) => {
    let tempCourses = Object.assign({}, courses)
    let tempInstructors = (tempCourses[courseData.id]?.instructors) ? tempCourses[courseData.id].instructors : []
    tempCourses[courseData.id] = courseData
    tempCourses[courseData.id].instructors = tempInstructors
    setCourses(tempCourses)
  }

This works, but is a little unsatisfying. If you could double check the handleScanClick function in CoursesPage.js. It uses api.scanCourse. Maybe that API call can be updated to return the instructor names similar to how you've done it in AdminController.php (using getInstructorNamesForCourse).

@mbusch3
Copy link
Contributor

mbusch3 commented Oct 30, 2025

This is wonderful work and seems to be working for the most part.

I have a couple issues that I'm seeing when I run it on my end:

  • When a course has not been scanned, I'm usually not getting people's full names. For instance, a course on my sub-account listed the instructors as "Corey, Erika" before a scan, and afterwards as "Corey Peterson, Erika Alexander." I am not sure why it shows differently.
  • When I try scanning multiple courses at the same time, I get odd results where some things come back and others just hang. One solution would be to just disable all of the scan buttons when any course is scanning, and another would be to make multiple synchronous scans work consistently.

That said, I haven't found any other major issues, and the report screen updates data immediately. Good work and well done!

@barnesc3
Copy link
Author

@mbusch3

  • Added a global scan lock: while any course is scanning, all scan buttons are disabled to prevent concurrent scans and inconsistent states.

  • Instructor names:

    • The account courses endpoint (GET /api/v1/accounts/:account_id/courses?include[]=teachers) returns lightweight teacher objects (often display_name/short_name), not guaranteed full names. This explains why only first names are used for unscanned courses.
    • Full names are reliably available via course-specific endpoints used during scan/rescan (e.g., GET /api/v1/courses/:course_id/enrollments?type[]=TeacherEnrollment&state[]=active&include[]=user). This is why upon scan/rescan, full names are used.
    • Changes Ive made:
      • For unscanned courses, we now prefer name when present and gracefully fall back to display_name/short_name. Showing full names pre-scan would require additional per-course API calls, which is costly; current approach balances accuracy with performance.

@dmols dmols self-requested a review November 7, 2025 15:28
Copy link
Contributor

@dmols dmols left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good and the admin page works as intended! Great work!

@dmols dmols merged commit 9db8c02 into ucfopen:new-main-page-UX Dec 3, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants