Skip to content

Conversation

@kobryan0619
Copy link
Contributor

@kobryan0619 kobryan0619 commented Dec 10, 2025

Initial start to an admin page for deleting user progress.

Screen.Recording.2025-12-11.at.10.56.34.AM.mov

Links

  • Jira:

Testing story

Follow ups

  • Disable "Delete" buttons when a delete is in progress
  • Include link to a template with the correct header cell names
  • Possibly, add to preview table when the student data has been deleted
  • Overall improve how errors are surfaced to the user
  • Add context for what "test delete" does vs "delete for real"
  • Add better notifications for when the deletions are finished
  • Add tests for new controller endpoints
  • Test for speed of large files and/or determine max size of file

@lfryemason lfryemason changed the title Initial commit for delete page Add an admin-only mass delete student progress UI Dec 11, 2025
end
end

# Path to the delete script
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to leverage the existing script - I will be honest, I relied on ChatGPT for this part and then just tested it afterward.


// Parse header row
const headers = lines[0]
.split(',')
Copy link
Contributor

Choose a reason for hiding this comment

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

CSV files can sometimes use other characters as separators (I think I've seen semicolons and possibly tabs).

I think this works as a hack, however could you test to see if you could use papaparse, which already looks installed and if that doesn't work or gets too hairy just add a comment about the specific CSV format we need.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, great catch! I will make that change to use papaparse


// Determine if conversion is needed
if (hasUsernameColumn(parsedData)) {
setHasUsernames(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Even auto-checking whether it has usernames or not. Beautiful!

end

# POST /admin/convert_usernames_to_ids
def convert_usernames_to_ids
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you know if there are any limits for parameter size or rails endpoints? I know I've seen some massive CSVs, I wonder if those will fail.

Again, probably not something to worry about, but we could put a note on the page.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah... I also thought about this... I did some testing with 100 students, but only for converting usernames_to_ids.

Let me look into the best way to test this...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay Dokie.... I looked into this the TINIEST bit but ultimately put it as a follow up.


const openingDirections = (
<div>
<h1>Mass Delete Student Progress</h1>
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think about adding a link to our manual instructions so that they are easy to find from here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Like it! Added.


if error_message
temp_input.unlink
render json: {success: false, error: error_message}, status: :bad_request
Copy link
Contributor

Choose a reason for hiding this comment

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

optional nit: This fails if we fail on any student. Do we want to return a partial success with the error message filled out? e.g. successfully converted X records, failed on 2 students: studentX, studentY

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we absolutely do want to do this. I think this area in general is "up next" for improvement.

end

# POST /admin/convert_usernames_to_ids
def convert_usernames_to_ids
Copy link
Contributor

Choose a reason for hiding this comment

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

Unfortunately I think these BE endpoints might deserve some tests. I'm fine merging now and adding later if you would prefer though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, makes sense... I will add it to the follow-up sections of this PR.

end

# POST /admin/delete_user_progress
def delete_user_progress
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to confirm, this only works if the teacher specifies the unit_name and does not copy the delete_user_progress_all_units script?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct, currently the uploaded csv requires a username. I asked Andrew what the more common request was and he said "delete by unit" so I started there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we put a note on the page that all unit deletion still needs the manual steps.

@alex-m-brown alex-m-brown changed the base branch from staging-next to staging December 15, 2025 15:28
setCsvData(parsedData);

// Determine if conversion is needed
if (hasUsernameColumn(parsedData)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (hasUsernameColumn(parsedData)) {
if (hasUsernameColumn(parsedData) && !hasUserIds) {

nit: what if a csv has userIds and usernames? Conversion should work fine in that case, but we wouldn't necessary need to convert before.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct... however, I don't see a scenario where we have userIds already as they are so opaque to users.


const deleteProgress = async (isDryRun = true) => {
try {
const response = await fetch('/admin/delete_user_progress', {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Similar to isConverting, we may want a loading state/to disable the button so that we can't call this deleteProgress method multiple times before it completes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

100% that is part of my follow ups too.

@kobryan0619 kobryan0619 merged commit 48d2a05 into staging Dec 18, 2025
4 checks passed
@kobryan0619 kobryan0619 deleted the kt-play-with-mass-delete branch December 18, 2025 16:11
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.

2 participants