Skip to content

Commit ada1b90

Browse files
authored
Merge pull request #2641 from somiaj/grade-test-for-others
Updates to grading tests for other users.
2 parents 56d153d + f0aca96 commit ada1b90

File tree

6 files changed

+137
-71
lines changed

6 files changed

+137
-71
lines changed

htdocs/js/GatewayQuiz/gateway.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -158,22 +158,20 @@
158158

159159
const remainingTime = serverDueTime - browserTime + timeDelta;
160160

161-
if (!timerDiv.dataset.acting) {
162-
if (remainingTime <= 10 - gracePeriod) {
163-
if (sessionStorage.getItem('gatewayAlertStatus')) {
164-
sessionStorage.removeItem('gatewayAlertStatus');
165-
166-
// Submit the test if time is expired and near the end of grace period.
167-
actuallySubmit = true;
168-
submitAnswers.click();
169-
}
170-
} else {
171-
// Set the timer text and check alerts at page load.
172-
updateTimer();
161+
if (!timerDiv.dataset.acting && remainingTime <= 10 - gracePeriod) {
162+
if (sessionStorage.getItem('gatewayAlertStatus')) {
163+
sessionStorage.removeItem('gatewayAlertStatus');
173164

174-
// Start the timer.
175-
setInterval(updateTimer, 1000);
165+
// Submit the test if time is expired and near the end of grace period.
166+
actuallySubmit = true;
167+
submitAnswers.click();
176168
}
169+
} else {
170+
// Set the timer text and check alerts at page load.
171+
updateTimer();
172+
173+
// Start the timer.
174+
setInterval(updateTimer, 1000);
177175
}
178176
}
179177

lib/WeBWorK.pm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ async sub dispatch ($c) {
217217
# current server time during a gateway quiz, and that definitely should not revoke proctor
218218
# authorization.
219219
delete $c->authen->session->{proctor_authorization_granted};
220+
delete $c->authen->session->{acting_proctor};
220221
}
221222
return 1;
222223
} else {

lib/WeBWorK/Authen/Proctor.pm

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,17 @@ sub verify_normal_user {
9696
# is 'No', then the verify method will have returned 1, and this never happens. For an ongoing login session, only
9797
# a key with versioned set information is accepted, and that version must match the requested set version. The set
9898
# id will not have a version when opening a new version. For that new proctor credentials are required.
99-
if ($self->{login_type} eq 'proctor_login'
100-
&& $c->stash('setID') =~ /,v\d+$/
99+
if (
100+
$self->{login_type} eq 'proctor_login'
101101
&& $c->authen->session('proctor_authorization_granted')
102-
&& $c->authen->session('proctor_authorization_granted') eq $c->stash('setID'))
102+
&& (
103+
(
104+
$c->stash('setID') =~ /,v\d+$/
105+
&& $c->authen->session('proctor_authorization_granted') eq $c->stash('setID')
106+
)
107+
|| $c->authen->session('acting_proctor')
108+
)
109+
)
103110
{
104111
return 1;
105112
} else {

lib/WeBWorK/ConfigValues.pm

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,14 @@ sub getConfigValues ($ce) {
529529
var => 'permissionLevels{record_answers_when_acting_as_student}',
530530
doc => x('Can submit answers for a student'),
531531
doc2 => x(
532-
'When acting as a student, this permission level and higher can submit answers for that student.'),
532+
'When acting as a student, this permission level and higher can submit answers for that student, '
533+
. 'which includes starting and grading test versions. This permission should only be turned '
534+
. 'on temporarily and set back to "nobody" after you are done submitting answers for a '
535+
. 'student. Leaving this permission on is dangerous, as you could unintentionally submit '
536+
. 'answers for a student, which can use up their total number of attempts. Further, if you '
537+
. 'are viewing an open test version, your answers on each page will be saved when you move '
538+
. q/between pages, which will overwrite the student's saved answers./
539+
),
533540
type => 'permission'
534541
},
535542
{

lib/WeBWorK/ContentGenerator/GatewayQuiz.pm

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,12 @@ sub can_recordAnswers ($c, $user, $permissionLevel, $effectiveUser, $set, $probl
141141

142142
if ($user->user_id ne $effectiveUser->user_id) {
143143
# If the user is not allowed to record answers as another user, return that permission. If the user is allowed
144-
# to record only set version answers, then allow that between the open and close dates, and so drop out of this
145-
# conditional to the usual one.
146-
return 1 if $authz->hasPermissions($user->user_id, 'record_answers_when_acting_as_student');
144+
# to record an unsubmitted test, allow that. If the user is allowed to record only set version answers, then
145+
# allow that between the open and close dates, and so drop out of this conditional to the usual one.
146+
return 1
147+
if $authz->hasPermissions($user->user_id, 'record_answers_when_acting_as_student')
148+
|| $c->can_gradeUnsubmittedTest($user, $permissionLevel, $effectiveUser, $set, $problem, $tmplSet,
149+
$submitAnswers);
147150
return 0 if !$authz->hasPermissions($user->user_id, 'record_set_version_answers_when_acting_as_student');
148151
}
149152

@@ -224,6 +227,15 @@ sub can_checkAnswers ($c, $user, $permissionLevel, $effectiveUser, $set, $proble
224227
return 0;
225228
}
226229

230+
# If user can use the problem grader, and the test is past due and has not been submitted, allow them to submit.
231+
sub can_gradeUnsubmittedTest ($c, $user, $permissionLevel, $effectiveUser, $set, $problem, $tmplSet, $submitAnswers = 0)
232+
{
233+
return
234+
!$submitAnswers
235+
&& $c->can_showProblemGrader($user, $permissionLevel, $effectiveUser, $set, $problem, $tmplSet)
236+
&& (after($set->due_date + $c->ce->{gatewayGracePeriod}) && !$set->version_last_attempt_time);
237+
}
238+
227239
sub can_showScore ($c, $user, $permissionLevel, $effectiveUser, $set, $problem, $tmplSet) {
228240
return
229241
$c->authz->hasPermissions($user->user_id, 'view_hidden_work')
@@ -533,7 +545,7 @@ async sub pre_header_initialize ($c) {
533545
$authz->hasPermissions($userID, 'record_answers_when_acting_as_student')
534546
|| $authz->hasPermissions($userID, 'create_new_set_version_when_acting_as_student')
535547
)
536-
&& $c->param('createnew_ok')
548+
&& $c->param('submit_for_student_ok')
537549
)
538550
)
539551
)
@@ -597,24 +609,18 @@ async sub pre_header_initialize ($c) {
597609
|| $authz->hasPermissions($userID, 'create_new_set_version_when_acting_as_student'))
598610
)
599611
{
600-
601-
$c->{invalidSet} = $c->maketext(
612+
$c->stash->{actingConfirmation} = $c->maketext(
602613
'You are acting as user [_1]. If you continue, you will create a new version of '
603614
. 'this test for that user, which will count against their allowed maximum '
604615
. 'number of versions for the current time interval. In general, this is not '
605616
. 'what you want to do. Please be sure that you want to do this before clicking '
606617
. 'the "Create New Test Version" button below. Alternatively, click "Cancel".',
607618
$effectiveUserID
608619
);
609-
$c->{invalidVersionCreation} = 1;
620+
$c->stash->{actingConfirmationButton} = $c->maketext('Create New Test Version');
610621

611622
} elsif ($effectiveUserID ne $userID) {
612-
$c->{invalidSet} = $c->maketext(
613-
'You are acting as user [_1], and do not have the permission to create a new test version '
614-
. 'when acting as another user.',
615-
$effectiveUserID
616-
);
617-
$c->{invalidVersionCreation} = 2;
623+
$c->{actingCreationError} = 1;
618624

619625
} elsif (($maxAttemptsPerVersion == 0 || $currentNumAttempts < $maxAttemptsPerVersion)
620626
&& $c->submitTime < $set->due_date() + $ce->{gatewayGracePeriod})
@@ -641,11 +647,29 @@ async sub pre_header_initialize ($c) {
641647
if (
642648
($currentNumAttempts < $maxAttemptsPerVersion)
643649
&& ($effectiveUserID eq $userID
644-
|| $authz->hasPermissions($userID, 'record_set_version_answers_when_acting_as_student'))
650+
|| $authz->hasPermissions($userID, 'record_set_version_answers_when_acting_as_student')
651+
|| $authz->hasPermissions($userID, 'record_answers_when_acting_as_student'))
645652
)
646653
{
647654
if (between($set->open_date(), $set->due_date() + $ce->{gatewayGracePeriod}, $c->submitTime)) {
648655
$versionIsOpen = 1;
656+
657+
# If acting as another user, then the user has permissions to record answers for the
658+
# student which is dangerous for open test versions. Give a warning unless the user
659+
# has already confirmed they understand the risk.
660+
if ($effectiveUserID ne $userID && !$c->param('submit_for_student_ok')) {
661+
$c->stash->{actingConfirmation} = $c->maketext(
662+
'You are trying to view an open test version for [_1] and have the permission to submit '
663+
. 'answers for that user. This is dangerous, as your answers can overwrite the '
664+
. q/student's answers as you move between test pages, preview, or check answers. /
665+
. 'If you are planing to submit answers for this student, click "View Test Version" '
666+
. 'below to continue. If you only want to view the test version, click "Cancel" '
667+
. 'below, then disable the permission to record answers when acting as a student '
668+
. 'before viewing open test versions.',
669+
$effectiveUserID
670+
);
671+
$c->stash->{actingConfirmationButton} = $c->maketext('View Test Version');
672+
}
649673
}
650674
}
651675
}
@@ -654,6 +678,13 @@ async sub pre_header_initialize ($c) {
654678
$c->{invalidSet} = $c->maketext('This test is closed. No new test versions may be taken.');
655679
}
656680

681+
if ($c->stash->{actingConfirmation}) {
682+
# Store session while waiting for confirmation for proctored tests.
683+
$c->authen->session(acting_proctor => 1) if $c->{assignment_type} eq 'proctored_gateway';
684+
return;
685+
}
686+
delete $c->authen->session->{acting_proctor};
687+
657688
# If the proctor session key does not have a set version id, then add it. This occurs when a student
658689
# initially enters a proctored test, since the version id is not determined until just above.
659690
if ($c->authen->session('proctor_authorization_granted')
@@ -663,8 +694,8 @@ async sub pre_header_initialize ($c) {
663694
else { delete $c->authen->session->{proctor_authorization_granted}; }
664695
}
665696

666-
# If the set or problem is invalid, then delete any proctor session keys and return.
667-
if ($c->{invalidSet}) {
697+
# If the set is invalid, then delete any proctor session keys and return.
698+
if ($c->{invalidSet} || $c->{actingCreationError}) {
668699
if (defined $c->{assignment_type} && $c->{assignment_type} eq 'proctored_gateway') {
669700
delete $c->authen->session->{proctor_authorization_granted};
670701
}
@@ -740,6 +771,7 @@ async sub pre_header_initialize ($c) {
740771
checkAnswers => $c->can_checkAnswers(@args),
741772
recordAnswersNextTime => $c->can_recordAnswers(@args, $c->{submitAnswers}),
742773
checkAnswersNextTime => $c->can_checkAnswers(@args, $c->{submitAnswers}),
774+
gradeUnsubmittedTest => $c->can_gradeUnsubmittedTest(@args, $c->{submitAnswers}),
743775
showScore => $c->can_showScore(@args),
744776
showProblemScores => $c->can_showProblemScores(@args),
745777
showWork => $c->can_showWork(@args),
@@ -754,6 +786,12 @@ async sub pre_header_initialize ($c) {
754786
$c->{can} = \%can;
755787
$c->{will} = \%will;
756788

789+
# Issue a warning if a test has not been submitted, but can still be graded by the instructor.
790+
$c->addbadmessage(
791+
$c->maketext(
792+
'This test version is past due, but has not been graded. You can still grade the test for this user.')
793+
) if $can{gradeUnsubmittedTest} && $userID ne $effectiveUserID;
794+
757795
# Set up problem numbering and multipage variables.
758796

759797
my @problemNumbers;
@@ -1330,7 +1368,8 @@ sub path ($c, $args) {
13301368
$args,
13311369
'WeBWorK' => $navigation_allowed ? $c->url_for('root') : '',
13321370
$courseName => $navigation_allowed ? $c->url_for('set_list') : '',
1333-
$setID eq 'Undefined_Set' || $c->{invalidSet}
1371+
$setID eq 'Undefined_Set'
1372+
|| $c->{invalidSet} || $c->{actingCreationError} || $c->stash->{actingConfirmation}
13341373
? ($setID => '')
13351374
: (
13361375
$c->{set}->set_id => $c->url_for('problem_list', setID => $c->{set}->set_id),
@@ -1344,7 +1383,7 @@ sub nav ($c, $args) {
13441383
my $userID = $c->param('user');
13451384
my $effectiveUserID = $c->param('effectiveUser');
13461385

1347-
return '' if $c->{invalidSet};
1386+
return '' if $c->{invalidSet} || $c->{actingCreationError} || $c->stash->{actingConfirmation};
13481387

13491388
# Set up and display a student navigation for those that have permission to act as a student.
13501389
if ($c->authz->hasPermissions($userID, 'become_student') && $effectiveUserID ne $userID) {

templates/ContentGenerator/GatewayQuiz.html.ep

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,39 +65,45 @@
6565
% my $userID = param('user');
6666
% my $effectiveUserID = param('effectiveUser');
6767
%
68-
% # If the set or problem is invalid, then show that information and exit.
68+
% # If the set is invalid, then show that information and exit.
6969
% if ($c->{invalidSet}) {
7070
<div class="alert alert-danger mb-2">
7171
<div class="mb-2">
72-
% if ($c->{invalidVersionCreation}) {
73-
<%= maketext(
74-
'The selected test ([_1]) is not a valid test for [_2] (acted as by [_3]).',
75-
$setID, $effectiveUserID, $userID
76-
) =%>
77-
% } else {
78-
<%= maketext(
79-
'The selected test ([_1]) is not a valid test for [_2].',
80-
$setID, $effectiveUserID
81-
) =%>
82-
% }
72+
<%= maketext('The selected test ([_1]) is not a valid test for [_2].', $setID, $effectiveUserID) =%>
8373
</div>
8474
<div><%= $c->{invalidSet} %></div>
85-
% if ($c->{invalidVersionCreation} && $c->{invalidVersionCreation} == 1) {
86-
<div class="mt-3">
87-
<%= link_to maketext('Create New Test Version') => $c->systemLink(
88-
url_for,
89-
params => { effectiveUser => $effectiveUserID, user => $userID, createnew_ok => 1 }
90-
),
91-
class => 'btn btn-primary'
92-
=%>
93-
<%= link_to maketext('Cancel') => $c->systemLink(
94-
url_for('problem_list', setID => $setID),
95-
params => { effectiveUser => $effectiveUserID, user => $userID }
96-
),
97-
class => 'btn btn-primary'
98-
=%>
99-
</div>
100-
% }
75+
</div>
76+
% last;
77+
% }
78+
% # If user tried to create a test version for another user without correct permissions, show message and exit.
79+
% if ($c->{actingCreationError}) {
80+
<div class="alert alert-danger mb-2">
81+
<%= maketext(
82+
'You are acting as user [_1] and do not have the permission to create a new test version when acting '
83+
. 'as another user.',
84+
$effectiveUserID
85+
) =%>
86+
</div>
87+
% last;
88+
% }
89+
% # Get confirmation before creating new test version or working on an open test for another user.
90+
% if (stash->{actingConfirmation}) {
91+
<div class="alert alert-danger mb-2">
92+
<div class="mb-2"><%= stash->{actingConfirmation} =%></div>
93+
<div>
94+
<%= link_to stash->{actingConfirmationButton} => $c->systemLink(
95+
url_for,
96+
params => { effectiveUser => $effectiveUserID, user => $userID, submit_for_student_ok => 1 }
97+
),
98+
class => 'btn btn-primary'
99+
=%>
100+
<%= link_to maketext('Cancel') => $c->systemLink(
101+
url_for('problem_list', setID => $setID =~ s/,v\d+$//r),
102+
params => { effectiveUser => $effectiveUserID, user => $userID }
103+
),
104+
class => 'btn btn-primary'
105+
=%>
106+
</div>
101107
</div>
102108
%
103109
% last;
@@ -233,7 +239,7 @@
233239
% # Remaining output of test headers.
234240
% # Display timer or information about elapsed time, output link, and information about any recorded score if not
235241
% # submitAnswers or checkAnswers.
236-
% if ($c->{can}{recordAnswersNextTime}) {
242+
% if ($c->{can}{recordAnswersNextTime} && before($c->{set}->due_date + $ce->{gatewayGracePeriod}, $submitTime)) {
237243
% my $timeLeft = $c->{set}->due_date - int($submitTime); # This is in seconds
238244
%
239245
% # Print the timer if there is less than 24 hours left.
@@ -267,11 +273,13 @@
267273
) =%>
268274
% }
269275
%
270-
% if ($timeLeft < 60 && $timeLeft > 0 && !$authz->hasPermissions($userID, 'record_answers_when_acting_as_student')) {
276+
% if ($timeLeft < 60 && $timeLeft > 0 && !$c->{can}{gradeUnsubmittedTest}
277+
% && !$authz->hasPermissions($userID, 'record_answers_when_acting_as_student')) {
271278
<div class="alert alert-danger d-inline-block mb-2 p-1">
272279
<strong><%= maketext('You have less than 1 minute to complete this test.') %></strong>
273280
</div>
274-
% } elsif ($timeLeft <= 0 && !$authz->hasPermissions($userID, 'record_answers_when_acting_as_student')) {
281+
% } elsif ($timeLeft <= 0 && !$c->{can}{gradeUnsubmittedTest} &&
282+
% !$authz->hasPermissions($userID, 'record_answers_when_acting_as_student')) {
275283
<div class="alert alert-danger d-inline-block mb-2 p-1">
276284
<strong>
277285
<%= maketext('You are out of time!')
@@ -408,6 +416,10 @@
408416
<%= hidden_field newPage => '' =%>
409417
<%= hidden_field currentPage => $pageNumber =%>
410418
% }
419+
% # Keep track that a user has confirmed it is okay to submit for a student.
420+
% if (param('submit_for_student_ok')) {
421+
<%= hidden_field submit_for_student_ok => 1 =%>
422+
% }
411423
%
412424
% # Set up links between problems and, for multi-page tests, pages.
413425
% for my $i (0 .. $#$pg_results) {
@@ -651,11 +663,16 @@
651663
%
652664
<div class="submit-buttons-container col-12 mb-2">
653665
<%= submit_button maketext('Preview Test'), name => 'previewAnswers', class => 'btn btn-primary mb-1' =%>
666+
% if ($c->{can}{checkAnswersNextTime}
667+
% && (!$c->{can}{recordAnswersNextTime} || $c->{can}{showProblemGrader})) {
668+
<%= submit_button maketext('Check Test'), name => 'checkAnswers', class => 'btn btn-primary mb-1' =%>
669+
% }
654670
% if ($c->{can}{recordAnswersNextTime}) {
655671
<%= tag('input',
656672
type => 'submit',
657673
name => 'submitAnswers',
658-
value => maketext('Grade Test'),
674+
value => $effectiveUserID ne $userID
675+
? maketext('Grade Test for [_1]', $effectiveUserID) : maketext('Grade Test'),
659676
class => 'btn btn-primary mb-1',
660677
$c->{set}->attempts_per_version
661678
? (
@@ -685,9 +702,6 @@
685702
: ()
686703
) =%>
687704
% }
688-
% if ($c->{can}{checkAnswersNextTime} && !$c->{can}{recordAnswersNextTime}) {
689-
<%= submit_button maketext('Check Test'), name => 'checkAnswers', class => 'btn btn-primary mb-1' =%>
690-
% }
691705
</div>
692706
% if ($numProbPerPage && $numPages > 1 && $c->{can}{recordAnswersNextTime}) {
693707
<p><em><%= maketext('Note: grading the test grades all problems, not just those on this page.') %></em></p>

0 commit comments

Comments
 (0)