From ea42e2cbaf34a0391256aee696a64add18e7f10b Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Tue, 9 Sep 2025 06:42:55 -0500 Subject: [PATCH] Add an "accessibility time factor" to provide for extra time on timed tests. This accesibility time factor is a user property and is set when editing a student on the "Accounts Manager" page. The time that a student will have to complete any timed test is the product of the "Test Time Limit" set for the test on the "Set Detail" page, and this accesibility time factor for the student taking the test. By default the accesibility time factor for each student is 1, but can be set to something like 1.5 to determine that a student is allowed time and a half to complete timed tests. The point of this is that it is a bit tedious to need to go through all timed tests and change the test time limit for all of the students that need to be given extra time for accesibility accomodations. With this you only need to set one number, and it is rather convenient to do so for all of the students in the class that need it from one page. Since this is a per user setting, this requires the addition of a new column to the user table in the database. --- lib/WeBWorK/ContentGenerator/GatewayQuiz.pm | 8 +++-- .../Instructor/ProblemSetDetail.pm | 4 ++- .../ContentGenerator/Instructor/UserList.pm | 28 +++++++++--------- lib/WeBWorK/ContentGenerator/ProblemSet.pm | 19 +++++++----- lib/WeBWorK/DB/Record/User.pm | 29 ++++++++++--------- .../Instructor/UserList/user_list.html.ep | 1 + .../ProblemSet/version_list.html.ep | 2 +- .../HelpFiles/InstructorUserList.html.ep | 15 ++++++++-- 8 files changed, 63 insertions(+), 43 deletions(-) diff --git a/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm b/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm index e4e827d069..a6d946bf1d 100644 --- a/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm +++ b/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm @@ -502,7 +502,6 @@ async sub pre_header_initialize ($c) { my $maxAttemptsPerVersion = $tmplSet->attempts_per_version || 0; my $timeInterval = $tmplSet->time_interval || 0; my $versionsPerInterval = $tmplSet->versions_per_interval || 0; - my $timeLimit = $tmplSet->version_time_limit || 0; # What happens if someone didn't set one of these? Perhaps this can happen if we're handed a malformed set, where # the values in the database are null. @@ -588,7 +587,8 @@ async sub pre_header_initialize ($c) { $set = $db->getMergedSetVersion($effectiveUserID, $setID, $setVersionNumber); $set->visible(1); - # If there is a cap on problems per page, make sure that is respected in case something higher snuck in. + # If there is a cap on problems per page, make sure that is respected + # in case something higher snuck in. if ( $ce->{test}{maxProblemsPerPage} && ($tmplSet->problems_per_page == 0 @@ -603,6 +603,8 @@ async sub pre_header_initialize ($c) { # Convert the floating point value from Time::HiRes to an integer for use below. Truncate toward 0. my $timeNowInt = int($c->submitTime); + my $timeLimit = ($tmplSet->version_time_limit || 0) * $effectiveUser->accessibility_time_factor; + # Set up creation time, and open and due dates. my $ansOffset = $set->answer_date - $set->due_date; $set->version_creation_time($timeNowInt); @@ -625,7 +627,7 @@ async sub pre_header_initialize ($c) { $cleanSet->due_date($set->due_date); $cleanSet->answer_date($set->answer_date); $cleanSet->version_last_attempt_time($set->version_last_attempt_time); - $cleanSet->version_time_limit($set->version_time_limit); + $cleanSet->version_time_limit($set->version_time_limit * $effectiveUser->accessibility_time_factor); $cleanSet->attempts_per_version($set->attempts_per_version); $cleanSet->assignment_type($set->assignment_type); $db->putSetVersion($cleanSet); diff --git a/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm b/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm index eba2d2aa82..2174da34de 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -281,7 +281,9 @@ use constant FIELD_PROPERTIES => { 'This sets a number of minutes for each version of a test, once it is started. Use "0" to indicate no ' . 'time limit. If there is a time limit, then there will be an indication that this is a timed ' . 'test on the main "Assignments" page. Additionally the student will be sent to a confirmation ' - . 'page beefore they can begin.' + . 'page before they can begin. Note that the actual time a student will have to complete a timed test ' + . 'is the product of this time limit and the accessibility time factor set for the student in the ' + . 'accounts manager.' ) }, time_limit_cap => { diff --git a/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm b/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm index a3eff9d7b6..f8b6651bc1 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm @@ -93,24 +93,26 @@ use constant SORT_SUBS => { }; use constant FIELDS => [ - 'user_id', 'first_name', 'last_name', 'email_address', 'student_id', 'status', - 'section', 'recitation', 'comment', 'permission', 'password' + 'user_id', 'first_name', 'last_name', 'email_address', + 'student_id', 'status', 'accessibility_time_factor', 'section', + 'recitation', 'comment', 'permission', 'password' ]; # Note that only the editable fields need a type (i.e. all but user_id), # and only the text fields need a size. use constant FIELD_PROPERTIES => { - user_id => { name => x('Login Name') }, - first_name => { name => x('First Name'), type => 'text', size => 10 }, - last_name => { name => x('Last Name'), type => 'text', size => 10 }, - email_address => { name => x('Email Address'), type => 'text', size => 20 }, - student_id => { name => x('Student ID'), type => 'text', size => 11 }, - status => { name => x('Enrollment Status'), type => 'status' }, - section => { name => x('Section'), type => 'text', size => 3 }, - recitation => { name => x('Recitation'), type => 'text', size => 3 }, - comment => { name => x('Comment'), type => 'text', size => 20 }, - permission => { name => x('Permission Level'), type => 'permission' }, - password => { name => x('Password'), type => 'password' }, + user_id => { name => x('Login Name') }, + first_name => { name => x('First Name'), type => 'text', size => 10 }, + last_name => { name => x('Last Name'), type => 'text', size => 10 }, + email_address => { name => x('Email Address'), type => 'text', size => 20 }, + student_id => { name => x('Student ID'), type => 'text', size => 11 }, + status => { name => x('Enrollment Status'), type => 'status' }, + accessibility_time_factor => { name => x('Accessibility Time Factor'), type => 'text', size => 5 }, + section => { name => x('Section'), type => 'text', size => 3 }, + recitation => { name => x('Recitation'), type => 'text', size => 3 }, + comment => { name => x('Comment'), type => 'text', size => 20 }, + permission => { name => x('Permission Level'), type => 'permission' }, + password => { name => x('Password'), type => 'password' }, }; sub pre_header_initialize ($c) { diff --git a/lib/WeBWorK/ContentGenerator/ProblemSet.pm b/lib/WeBWorK/ContentGenerator/ProblemSet.pm index ba77939ec5..d25a8a62a9 100644 --- a/lib/WeBWorK/ContentGenerator/ProblemSet.pm +++ b/lib/WeBWorK/ContentGenerator/ProblemSet.pm @@ -180,12 +180,14 @@ sub gateway_body ($c) { my $ce = $c->ce; my $db = $c->db; - my $set = $c->{set}; - my $effectiveUser = $c->param('effectiveUser'); - my $user = $c->param('user'); + my $set = $c->{set}; + my $effectiveUserID = $c->param('effectiveUser'); + my $userID = $c->param('user'); + + my $effectiveUser = $db->getUser($effectiveUserID); my $timeNow = time; - my $timeLimit = $set->version_time_limit || 0; + my $timeLimit = ($set->version_time_limit || 0) * $effectiveUser->accessibility_time_factor; # Compute how many versions have been launched within timeInterval to determine if a new version can be created, # if a version can be continued, and the date a next version can be started. If there is an open version that @@ -206,8 +208,9 @@ sub gateway_body ($c) { } # Get a problem to determine how many submits have been made. - my @ProblemNums = $db->listUserProblems($effectiveUser, $set->set_id); - my $Problem = $db->getMergedProblemVersion($effectiveUser, $set->set_id, $verSet->version_id, $ProblemNums[0]); + my @ProblemNums = $db->listUserProblems($effectiveUserID, $set->set_id); + my $Problem = + $db->getMergedProblemVersion($effectiveUserID, $set->set_id, $verSet->version_id, $ProblemNums[0]); my $verSubmits = defined $Problem ? $Problem->num_correct + $Problem->num_incorrect : 0; my $maxSubmits = $verSet->attempts_per_version || 0; @@ -292,11 +295,11 @@ sub gateway_body ($c) { $data->{score} = ''; # Only show score if user has permission and assignment has at least one submit. - if ($authz->hasPermissions($user, 'view_hidden_work') + if ($authz->hasPermissions($userID, 'view_hidden_work') || ($verSet->hide_score eq 'N' && $verSubmits >= 1) || ($verSet->hide_score eq 'BeforeAnswerDate' && $timeNow > $set->answer_date)) { - my ($total, $possible) = grade_set($db, $verSet, $effectiveUser, 1); + my ($total, $possible) = grade_set($db, $verSet, $effectiveUserID, 1); $total = wwRound(2, $total); $data->{score} = "$total/$possible"; } diff --git a/lib/WeBWorK/DB/Record/User.pm b/lib/WeBWorK/DB/Record/User.pm index b712f3473d..b7dfea59fc 100644 --- a/lib/WeBWorK/DB/Record/User.pm +++ b/lib/WeBWorK/DB/Record/User.pm @@ -12,20 +12,21 @@ use warnings; BEGIN { __PACKAGE__->_fields( - user_id => { type => "VARCHAR(100) NOT NULL", key => 1 }, - first_name => { type => "TEXT" }, - last_name => { type => "TEXT" }, - email_address => { type => "TEXT" }, - student_id => { type => "TEXT" }, - status => { type => "TEXT" }, - section => { type => "TEXT" }, - recitation => { type => "TEXT" }, - comment => { type => "TEXT" }, - displayMode => { type => "TEXT" }, - showOldAnswers => { type => "INT" }, - useMathView => { type => "INT" }, - useMathQuill => { type => "INT" }, - lis_source_did => { type => "TEXT" }, + user_id => { type => "VARCHAR(100) NOT NULL", key => 1 }, + first_name => { type => "TEXT" }, + last_name => { type => "TEXT" }, + email_address => { type => "TEXT" }, + student_id => { type => "TEXT" }, + status => { type => "TEXT" }, + accessibility_time_factor => { type => "FLOAT NOT NULL DEFAULT 1" }, + section => { type => "TEXT" }, + recitation => { type => "TEXT" }, + comment => { type => "TEXT" }, + displayMode => { type => "TEXT" }, + showOldAnswers => { type => "INT" }, + useMathView => { type => "INT" }, + useMathQuill => { type => "INT" }, + lis_source_did => { type => "TEXT" }, ); } diff --git a/templates/ContentGenerator/Instructor/UserList/user_list.html.ep b/templates/ContentGenerator/Instructor/UserList/user_list.html.ep index 3abe3e2bc6..0ec940e0ca 100644 --- a/templates/ContentGenerator/Instructor/UserList/user_list.html.ep +++ b/templates/ContentGenerator/Instructor/UserList/user_list.html.ep @@ -52,6 +52,7 @@ <%= include 'ContentGenerator/Instructor/UserList/sort_button', field => 'status' =%> + <%= maketext('Accessibility Time Factor') %>
<%= link_to maketext('Section') => '#', class => 'sort-header', diff --git a/templates/ContentGenerator/ProblemSet/version_list.html.ep b/templates/ContentGenerator/ProblemSet/version_list.html.ep index 0d332c749e..f5eaa58597 100644 --- a/templates/ContentGenerator/ProblemSet/version_list.html.ep +++ b/templates/ContentGenerator/ProblemSet/version_list.html.ep @@ -15,7 +15,7 @@
<%= $c->{invalidSet} %>
% } elsif ($continueVersion) { % # Display information about the current test and a continue open test button. - % if ($timeLimit > 0) { + % if ($continueVersion->version_time_limit > 0) { % if ($timeNow >= $continueVersion->due_date) { % # If the currently open test is in the grace period, display a mesage stating this.
diff --git a/templates/HelpFiles/InstructorUserList.html.ep b/templates/HelpFiles/InstructorUserList.html.ep index 57ed8676a7..5e13139d03 100644 --- a/templates/HelpFiles/InstructorUserList.html.ep +++ b/templates/HelpFiles/InstructorUserList.html.ep @@ -3,9 +3,9 @@ %

<%== maketext('From this page you can add new students, edit user data ' - . '(name, email address, recitation, section, permission level, enrollment status, and password), ' - . 'and export (save) class lists for back-up or use in another course. ' - . 'You can also delete students from the class roster, but this cannot be undone.') =%> + . '(name, email address, student ID, enrollment status, accessibility time factor, section, recitation, ' + . 'comment, permission level, and password), and export (save) class lists for back-up or use ' + . 'in another course. You can also delete students from the class roster, but this cannot be undone.') =%>

<%= maketext('This page gives access to information about the student, independent of the assignments ' @@ -142,6 +142,15 @@ . 'grade for each problem is listed as "status" on this third page).') =%> +

<%= maketext('Give one student or several students additional time for all timed tests.') %>
+
+ <%= maketext('Click on the "Select" checkbox next to the names of the students that additional time is to be ' + . 'assigned, click on the radio button for editing selected users and then click the "Edit" button .' + . 'Set the "Accesibility Time Factor" to the desired multiplier for each student selected. The time ' + . 'that a student will have to complete a timed test will be the product of the "Test Time Limit" for the ' + . 'test set in the "Sets Manager" and the "Accessibility Time Factor" set here.') =%> +
+
<%= maketext('Extend the number of attempts allowed a student on a given problem.') %>
<%= maketext(q{Click first in the "Assigned Sets" column in the student's row. This will take you to a new }