| … | |
… | |
| 36 | |
36 | |
| 37 | # Important Note: the following two sets of constants may seem similar |
37 | # Important Note: the following two sets of constants may seem similar |
| 38 | # but they are functionally and semantically different |
38 | # but they are functionally and semantically different |
| 39 | |
39 | |
| 40 | # these constants determine which fields belong to what type of record |
40 | # these constants determine which fields belong to what type of record |
| 41 | use constant SET_FIELDS => [qw(set_header hardcopy_header open_date due_date answer_date published restrict_ip relax_restrict_ip assignment_type attempts_per_version version_time_limit versions_per_interval time_interval problem_randorder problems_per_page hide_score hide_score_by_problem hide_work)]; |
41 | use constant SET_FIELDS => [qw(set_header hardcopy_header open_date due_date answer_date published restrict_ip relax_restrict_ip assignment_type attempts_per_version version_time_limit versions_per_interval time_interval problem_randorder problems_per_page hide_score:hide_score_by_problem hide_work)]; |
| 42 | use constant PROBLEM_FIELDS =>[qw(source_file value max_attempts)]; |
42 | use constant PROBLEM_FIELDS =>[qw(source_file value max_attempts)]; |
| 43 | use constant USER_PROBLEM_FIELDS => [qw(problem_seed status num_correct num_incorrect)]; |
43 | use constant USER_PROBLEM_FIELDS => [qw(problem_seed status num_correct num_incorrect)]; |
| 44 | |
44 | |
| 45 | # these constants determine what order those fields should be displayed in |
45 | # these constants determine what order those fields should be displayed in |
| 46 | use constant HEADER_ORDER => [qw(set_header hardcopy_header)]; |
46 | use constant HEADER_ORDER => [qw(set_header hardcopy_header)]; |
| … | |
… | |
| 53 | # FIXME: in the long run, we may want to let hide_score and hide_work be |
53 | # FIXME: in the long run, we may want to let hide_score and hide_work be |
| 54 | # FIXME: set for non-gateway assignments. right now (11/30/06) they are |
54 | # FIXME: set for non-gateway assignments. right now (11/30/06) they are |
| 55 | # FIXME: only used for gateways |
55 | # FIXME: only used for gateways |
| 56 | use constant SET_FIELD_ORDER => [qw(open_date due_date answer_date published restrict_ip relax_restrict_ip assignment_type)]; |
56 | use constant SET_FIELD_ORDER => [qw(open_date due_date answer_date published restrict_ip relax_restrict_ip assignment_type)]; |
| 57 | # use constant GATEWAY_SET_FIELD_ORDER => [qw(attempts_per_version version_time_limit time_interval versions_per_interval problem_randorder problems_per_page hide_score hide_work)]; |
57 | # use constant GATEWAY_SET_FIELD_ORDER => [qw(attempts_per_version version_time_limit time_interval versions_per_interval problem_randorder problems_per_page hide_score hide_work)]; |
| 58 | use constant GATEWAY_SET_FIELD_ORDER => [qw(version_time_limit time_limit_cap attempts_per_version time_interval versions_per_interval problem_randorder problems_per_page hide_score hide_score_by_problem hide_work)]; |
58 | use constant GATEWAY_SET_FIELD_ORDER => [qw(version_time_limit time_limit_cap attempts_per_version time_interval versions_per_interval problem_randorder problems_per_page hide_score:hide_score_by_problem hide_work)]; |
| 59 | |
59 | |
| 60 | # this constant is massive hash of information corresponding to each db field. |
60 | # this constant is massive hash of information corresponding to each db field. |
| 61 | # override indicates for how many students at a time a field can be overridden |
61 | # override indicates for how many students at a time a field can be overridden |
| 62 | # this hash should make it possible to NEVER have explicitly: if (somefield) { blah() } |
62 | # this hash should make it possible to NEVER have explicitly: if (somefield) { blah() } |
| 63 | # |
63 | # |
| … | |
… | |
| 228 | override => "any", |
228 | override => "any", |
| 229 | default => "0", |
229 | default => "0", |
| 230 | format => '[0-9]+', # an integer, possibly zero |
230 | format => '[0-9]+', # an integer, possibly zero |
| 231 | # labels => { "" => 0 }, |
231 | # labels => { "" => 0 }, |
| 232 | }, |
232 | }, |
| 233 | hide_score => { |
233 | 'hide_score:hide_score_by_problem' => { |
| 234 | name => "Show Scores on Finished Assignments?", |
234 | name => "Show Scores on Finished Assignments?", |
| 235 | type => "choose", |
235 | type => "choose", |
| 236 | choices => [ qw(N Y BeforeAnswerDate) ], |
236 | choices => [ qw( N: Y:N BeforeAnswerDate:N Y:Y BeforeAnswerDate:Y ) ], |
| 237 | override => "any", |
237 | override => "any", |
|
|
238 | labels => { 'N:' => 'Yes', 'Y:N' => 'No', 'BeforeAnswerDate:N' => 'Only after set answer date', 'Y:Y' => 'Totals only (not problem scores)', 'BeforeAnswerDate:Y' => 'Totals only, only after answer date' }, |
|
|
239 | }, |
|
|
240 | #################### |
|
|
241 | # FIXME: the above stanza replaces the following two, corresponding to a |
|
|
242 | # single drop-down to set both variables |
|
|
243 | # |
|
|
244 | # hide_score => { |
|
|
245 | # name => "Show Scores on Finished Assignments?", |
|
|
246 | # type => "choose", |
|
|
247 | # choices => [ qw(N Y BeforeAnswerDate) ], |
|
|
248 | # override => "any", |
| 238 | labels => { 'N' => "Yes", 'Y' => "No", 'BeforeAnswerDate' => 'Only after set answer date' }, |
249 | # labels => { 'N' => "Yes", 'Y' => "No", 'BeforeAnswerDate' => 'Only after set answer date' }, |
| 239 | }, |
250 | # }, |
| 240 | hide_score_by_problem => { |
251 | # hide_score_by_problem => { |
| 241 | name => "Show Total (but not scores on each problem)?", |
252 | # name => "Show Total (but not scores on each problem)?", |
| 242 | type => "choose", |
253 | # type => "choose", |
| 243 | choices => [ qw(N Y) ], |
254 | # choices => [ qw(N Y) ], |
| 244 | override => "any", |
255 | # override => "any", |
| 245 | labels => { 'N' => "Show no scores", 'Y' => "Show assignment total only", }, |
256 | # labels => { 'N' => "Show no scores", 'Y' => "Show assignment total only", }, |
| 246 | }, |
257 | # }, |
|
|
258 | #################### |
| 247 | hide_work => { |
259 | hide_work => { |
| 248 | name => "Show Student Work on Finished Tests", |
260 | name => "Show Student Work on Finished Tests", |
| 249 | type => "choose", |
261 | type => "choose", |
| 250 | choices => [ qw(N Y BeforeAnswerDate) ], |
262 | choices => [ qw(N Y BeforeAnswerDate) ], |
| 251 | override => "any", |
263 | override => "any", |
| … | |
… | |
| 350 | if ( $globalRecord->assignment_type() =~ /gateway/ ) { |
362 | if ( $globalRecord->assignment_type() =~ /gateway/ ) { |
| 351 | my $gwhdr = "\n<!-- begin gwoutput table -->\n"; |
363 | my $gwhdr = "\n<!-- begin gwoutput table -->\n"; |
| 352 | my $nF = 0; |
364 | my $nF = 0; |
| 353 | |
365 | |
| 354 | foreach my $gwfield ( @{ GATEWAY_SET_FIELD_ORDER() } ) { |
366 | foreach my $gwfield ( @{ GATEWAY_SET_FIELD_ORDER() } ) { |
|
|
367 | #################### |
|
|
368 | # FIXME: replaced with a single drop-down for all hide_scores options |
|
|
369 | # |
| 355 | # only display filtering of the hide_scores option to |
370 | # # only display filtering of the hide_scores option to |
| 356 | # hide by problem if hide_scores is set |
371 | # # hide by problem if hide_scores is set |
| 357 | next if ( $gwfield eq 'hide_score_by_problem' && |
372 | # next if ( $gwfield eq 'hide_score_by_problem' && |
| 358 | ( ($forUsers && $userRecord->hide_score eq 'N') || |
373 | # ( ($forUsers && $userRecord->hide_score eq 'N') || |
| 359 | (! $forUsers && $globalRecord->hide_score eq 'N') ) ); |
374 | # (! $forUsers && $globalRecord->hide_score eq 'N') ) ); |
|
|
375 | #################### |
|
|
376 | |
| 360 | my @fieldData = |
377 | my @fieldData = |
| 361 | ($self->FieldHTML($userID, $setID, $problemID, |
378 | ($self->FieldHTML($userID, $setID, $problemID, |
| 362 | $globalRecord, $userRecord, |
379 | $globalRecord, $userRecord, |
| 363 | $gwfield)); |
380 | $gwfield)); |
| 364 | if ( @fieldData && defined($fieldData[1]) and $fieldData[1] ne '' ) { |
381 | if ( @fieldData && defined($fieldData[1]) and $fieldData[1] ne '' ) { |
| … | |
… | |
| 507 | return "" if $properties{override} eq "all" && $forUsers; |
524 | return "" if $properties{override} eq "all" && $forUsers; |
| 508 | |
525 | |
| 509 | my $edit = ($properties{type} eq "edit") && ($properties{override} ne "none"); |
526 | my $edit = ($properties{type} eq "edit") && ($properties{override} ne "none"); |
| 510 | my $choose = ($properties{type} eq "choose") && ($properties{override} ne "none"); |
527 | my $choose = ($properties{type} eq "choose") && ($properties{override} ne "none"); |
| 511 | |
528 | |
|
|
529 | # FIXME: allow one selector to set multiple fields |
| 512 | my $globalValue = $globalRecord->{$field}; |
530 | # my $globalValue = $globalRecord->{$field}; |
|
|
531 | # my $userValue = $userRecord->{$field}; |
|
|
532 | my ($globalValue, $userValue) = ('', ''); |
|
|
533 | my $blankfield = ''; |
|
|
534 | if ( $field =~ /:/ ) { |
|
|
535 | foreach my $f ( split(/:/, $field) ) { |
|
|
536 | $globalValue .= $globalRecord->$f . ":"; |
|
|
537 | $userValue .= $userRecord->$f . ":"; |
|
|
538 | $blankfield .= ":"; |
|
|
539 | } |
|
|
540 | $globalValue =~ s/:$//; |
|
|
541 | $userValue =~ s/:$//; |
|
|
542 | $blankfield =~ s/:$//; |
|
|
543 | } else { |
|
|
544 | $globalValue = $globalRecord->{$field}; |
|
|
545 | $userValue = $userRecord->{$field}; |
|
|
546 | } |
|
|
547 | |
| 513 | # use defined instead of value in order to allow 0 to printed, e.g. for the 'value' field |
548 | # use defined instead of value in order to allow 0 to printed, e.g. for the 'value' field |
| 514 | $globalValue = (defined($globalValue)) ? ($labels{$globalValue || ""} || $globalValue) : ""; |
549 | $globalValue = (defined($globalValue)) ? ($labels{$globalValue || ""} || $globalValue) : ""; |
| 515 | my $userValue = $userRecord->{$field}; |
|
|
| 516 | $userValue = (defined($userValue)) ? ($labels{$userValue || ""} || $userValue) : ""; |
550 | $userValue = (defined($userValue)) ? ($labels{$userValue || ""} || $userValue) : ""; |
| 517 | |
551 | |
| 518 | if ($field =~ /_date/) { |
552 | if ($field =~ /_date/) { |
| 519 | $globalValue = $self->formatDateTime($globalValue) if defined $globalValue && $globalValue ne $labels{""}; |
553 | $globalValue = $self->formatDateTime($globalValue) if defined $globalValue && $globalValue ne $labels{""}; |
| 520 | $userValue = $self->formatDateTime($userValue) if defined $userValue && $userValue ne $labels{""}; |
554 | $userValue = $self->formatDateTime($userValue) if defined $userValue && $userValue ne $labels{""}; |
| … | |
… | |
| 551 | }); |
585 | }); |
| 552 | } elsif ($choose) { |
586 | } elsif ($choose) { |
| 553 | # Note that in popup menus, you're almost guaranteed to have the choices hashed to labels in %properties |
587 | # Note that in popup menus, you're almost guaranteed to have the choices hashed to labels in %properties |
| 554 | # but $userValue and and $globalValue are the values in the hash not the keys |
588 | # but $userValue and and $globalValue are the values in the hash not the keys |
| 555 | # so we have to use the actual db record field values to select our default here. |
589 | # so we have to use the actual db record field values to select our default here. |
|
|
590 | |
|
|
591 | # FIXME: this allows us to set one selector from two (or more) fields |
|
|
592 | # if $field matches /:/, we have to get two fields to get the data we need here |
|
|
593 | my $value = $r->param("$recordType.$recordID.$field"); |
|
|
594 | if ( ! $value && $field =~ /:/ ) { |
|
|
595 | my @fields = split(/:/, $field); |
|
|
596 | $value = ''; |
|
|
597 | foreach my $f ( @fields ) { |
|
|
598 | $value .= ($forUsers && $userRecord->$f ne '' ? $userRecord->$f : $globalRecord->$f) . ":"; |
|
|
599 | } |
|
|
600 | $value =~ s/:$//; |
|
|
601 | } elsif ( ! $value ) { |
|
|
602 | $value = ($forUsers && $userRecord->$field ne '' ? $userRecord->$field : $globalRecord->$field); |
|
|
603 | } |
|
|
604 | |
| 556 | $inputType = CGI::popup_menu({ |
605 | $inputType = CGI::popup_menu({ |
| 557 | name => "$recordType.$recordID.$field", |
606 | name => "$recordType.$recordID.$field", |
| 558 | values => $properties{choices}, |
607 | values => $properties{choices}, |
| 559 | labels => \%labels, |
608 | labels => \%labels, |
| 560 | default => $r->param("$recordType.$recordID.$field") || ($forUsers && $userRecord->$field ne '' ? $userRecord->$field : $globalRecord->$field), |
609 | default => $value, |
| 561 | }); |
610 | }); |
| 562 | } |
611 | } |
| 563 | |
612 | |
| 564 | my $gDisplVal = defined($properties{labels}) && defined($properties{labels}->{$globalValue}) ? $properties{labels}->{$globalValue} : $globalValue; |
613 | my $gDisplVal = defined($properties{labels}) && defined($properties{labels}->{$globalValue}) ? $properties{labels}->{$globalValue} : $globalValue; |
| 565 | |
614 | |
|
|
615 | # FIXME: adding ":" in the checked => allows for multiple fields to be set by one selector |
| 566 | # return (($forUsers && $edit && $check) ? CGI::checkbox({ |
616 | # return (($forUsers && $edit && $check) ? CGI::checkbox({ |
| 567 | return (($forUsers && $check) ? CGI::checkbox({ |
617 | return (($forUsers && $check) ? CGI::checkbox({ |
| 568 | type => "checkbox", |
618 | type => "checkbox", |
| 569 | name => "$recordType.$recordID.$field.override", |
619 | name => "$recordType.$recordID.$field.override", |
| 570 | label => "", |
620 | label => "", |
| 571 | value => $field, |
621 | value => $field, |
| 572 | checked => $r->param("$recordType.$recordID.$field.override") || ($userValue ne ($labels{""} || "") ? 1 : 0), |
622 | checked => $r->param("$recordType.$recordID.$field.override") || ($userValue ne ($labels{""} || $blankfield) ? 1 : 0), |
| 573 | }) : "", |
623 | }) : "", |
| 574 | $properties{name}, |
624 | $properties{name}, |
| 575 | $inputType, |
625 | $inputType, |
| 576 | $forUsers ? " $gDisplVal" : "", |
626 | $forUsers ? " $gDisplVal" : "", |
| 577 | ); |
627 | ); |
| … | |
… | |
| 833 | } |
883 | } |
| 834 | if ($answer_date > $cutoff) { |
884 | if ($answer_date > $cutoff) { |
| 835 | $self->addbadmessage("Error: answer date cannot be more than 10 years from now in set $setID"); |
885 | $self->addbadmessage("Error: answer date cannot be more than 10 years from now in set $setID"); |
| 836 | $error = $r->param('submit_changes'); |
886 | $error = $r->param('submit_changes'); |
| 837 | } |
887 | } |
|
|
888 | |
| 838 | } |
889 | } |
| 839 | ######## |
890 | ######## |
| 840 | # commented out |
891 | # commented out |
| 841 | # this runs afoul of the conversion of a set to a gateway |
892 | # this runs afoul of the conversion of a set to a gateway |
| 842 | # assignment, when perforce fields may be empty or zero, and dealing |
893 | # assignment, when perforce fields may be empty or zero, and dealing |
| … | |
… | |
| 894 | $param = $self->parseDateTime($param) unless defined $unlabel; |
945 | $param = $self->parseDateTime($param) unless defined $unlabel; |
| 895 | } |
946 | } |
| 896 | if (defined($properties{$field}->{convertby}) && $properties{$field}->{convertby}) { |
947 | if (defined($properties{$field}->{convertby}) && $properties{$field}->{convertby}) { |
| 897 | $param = $param*$properties{$field}->{convertby}; |
948 | $param = $param*$properties{$field}->{convertby}; |
| 898 | } |
949 | } |
|
|
950 | # special case; does field fill in multiple values? |
|
|
951 | if ( $field =~ /:/ ) { |
|
|
952 | my @values = split(/:/, $param); |
|
|
953 | my @fields = split(/:/, $field); |
|
|
954 | for ( my $i=0; $i<@values; $i++ ) { |
|
|
955 | my $f=$fields[$i]; |
|
|
956 | $record->$f($values[$i]); |
|
|
957 | } |
|
|
958 | } else { |
| 899 | $record->$field($param); |
959 | $record->$field($param); |
|
|
960 | } |
| 900 | } else { |
961 | } else { |
|
|
962 | #################### |
|
|
963 | # FIXME: allow one selector to set multiple fields |
|
|
964 | # |
|
|
965 | if ( $field =~ /:/ ) { |
|
|
966 | foreach my $f ( split(/:/, $field) ) { |
|
|
967 | $record->$f(undef); |
|
|
968 | } |
|
|
969 | } else { |
| 901 | $record->$field(undef); |
970 | $record->$field(undef); |
|
|
971 | } |
| 902 | } |
972 | } |
| 903 | |
973 | |
| 904 | } |
974 | } |
|
|
975 | #################### |
|
|
976 | # FIXME: this is replaced by our allowing multiple fields to be set by one selector |
| 905 | # a check for hiding scores: if we have |
977 | # a check for hiding scores: if we have |
| 906 | # $set->hide_score eq 'N', we also want |
978 | # $set->hide_score eq 'N', we also want |
| 907 | # $set->hide_score_by_problem eq 'N' |
979 | # $set->hide_score_by_problem eq 'N' |
| 908 | if ( $record->hide_score eq 'N' ) { |
980 | # if ( $record->hide_score eq 'N' ) { |
| 909 | $record->hide_score_by_problem('N'); |
981 | # $record->hide_score_by_problem('N'); |
| 910 | } |
982 | # } |
|
|
983 | #################### |
| 911 | $db->putUserSet($record); |
984 | $db->putUserSet($record); |
| 912 | } |
985 | } |
| 913 | |
986 | |
| 914 | # the locations for ip restrictions are saved in the |
987 | # the locations for ip restrictions are saved in the |
| 915 | # set_locations_user table, so we have to update |
988 | # set_locations_user table, so we have to update |
| … | |
… | |
| 966 | $param = $self->parseDateTime($param) unless defined $unlabel; |
1039 | $param = $self->parseDateTime($param) unless defined $unlabel; |
| 967 | } |
1040 | } |
| 968 | if (defined($properties{$field}->{convertby}) && $properties{$field}->{convertby}) { |
1041 | if (defined($properties{$field}->{convertby}) && $properties{$field}->{convertby}) { |
| 969 | $param = $param*$properties{$field}->{convertby}; |
1042 | $param = $param*$properties{$field}->{convertby}; |
| 970 | } |
1043 | } |
|
|
1044 | # special case; does field fill in multiple values? |
|
|
1045 | if ( $field =~ /:/ ) { |
|
|
1046 | my @values = split(/:/, $param); |
|
|
1047 | my @fields = split(/:/, $field); |
|
|
1048 | for ( my $i=0; $i<@values; $i++ ) { |
|
|
1049 | my $f = $fields[$i]; |
|
|
1050 | $setRecord->$f($values[$i]); |
|
|
1051 | } |
|
|
1052 | } else { |
| 971 | $setRecord->$field($param); |
1053 | $setRecord->$field($param); |
|
|
1054 | } |
| 972 | } |
1055 | } |
|
|
1056 | #################### |
|
|
1057 | # FIXME: this is replaced by our setting both hide_score and hide_score_by_problem |
|
|
1058 | # with a single drop down |
|
|
1059 | # |
| 973 | # a check for hiding scores: if we have |
1060 | # # a check for hiding scores: if we have |
| 974 | # $set->hide_score eq 'N', we also want |
1061 | # # $set->hide_score eq 'N', we also want |
| 975 | # $set->hide_score_by_problem eq 'N', and if it's |
1062 | # # $set->hide_score_by_problem eq 'N', and if it's |
| 976 | # changed to 'Y' and hide_score_by_problem is Null, |
1063 | # # changed to 'Y' and hide_score_by_problem is Null, |
| 977 | # give it a value 'N' |
1064 | # # give it a value 'N' |
| 978 | if ( $setRecord->hide_score eq 'N' || |
1065 | # if ( $setRecord->hide_score eq 'N' || |
| 979 | ( ! defined($setRecord->hide_score_by_problem) || |
1066 | # ( ! defined($setRecord->hide_score_by_problem) || |
| 980 | $setRecord->hide_score_by_problem eq '' ) ) { |
1067 | # $setRecord->hide_score_by_problem eq '' ) ) { |
| 981 | $setRecord->hide_score_by_problem('N'); |
1068 | # $setRecord->hide_score_by_problem('N'); |
| 982 | } |
1069 | # } |
|
|
1070 | #################### |
| 983 | $db->putGlobalSet($setRecord); |
1071 | $db->putGlobalSet($setRecord); |
| 984 | |
1072 | |
| 985 | # the locations for ip restrictions are saved in the |
1073 | # the locations for ip restrictions are saved in the |
| 986 | # set_locations table, so we have to update these |
1074 | # set_locations table, so we have to update these |
| 987 | # separately |
1075 | # separately |