From: Mike G. v. a. <we...@ma...> - 2005-12-26 22:07:54
|
Log Message: ----------- This file replaces the SetsAssignedToUser.pm module. It allows you to=20 change the dates on several homework sets for a single student from one p= age. This is a common activity, particularly when adding students, and is now = a much faster procedure. Added Files: ----------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: UserDetail.pm Revision Data ------------- --- /dev/null +++ lib/WeBWorK/ContentGenerator/Instructor/UserDetail.pm @@ -0,0 +1,500 @@ +########################################################################= ######## +# WeBWorK Online Homework Delivery System +# Copyright =A9 2000-2003 The WeBWorK Project, http://openwebwork.sf.net= / +# $CVSHeader:=20 +#=20 +# This program is free software; you can redistribute it and/or modify i= t under +# the terms of either: (a) the GNU General Public License as published b= y the +# Free Software Foundation; either version 2, or (at your option) any la= ter +# version, or (b) the "Artistic License" which comes with this package. +#=20 +# This program is distributed in the hope that it will be useful, but WI= THOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or = FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License o= r the +# Artistic License for more details. +########################################################################= ######## + +package WeBWorK::ContentGenerator::Instructor::UserDetail; +use base qw(WeBWorK::ContentGenerator::Instructor); + +=3Dhead1 NAME + +WeBWorK::ContentGenerator::Instructor::UserDetail - Detailed User specif= ic information + +=3Dcut + +use strict; +use warnings; +use CGI qw(); +use WeBWorK::Utils qw(sortByName); +use WeBWorK::Debug; + +use constant DATE_FIELDS =3D> { open_date =3D> " Open: ", + due_date =3D> " Due : ", + answer_date =3D> " Ans : " +}; +use constant DATE_FIELDS_ORDER =3D>[qw(open_date due_date answer_date )]= ; +sub initialize { + my ($self) =3D @_; + my $r =3D $self->r; + my $urlpath =3D $r->urlpath; + my $db =3D $r->db; + my $authz =3D $r->authz; + my $userID =3D $r->param("user"); + my $editForUserID =3D $urlpath->arg("userID"); + + return CGI::div({class =3D> "ResultsWithError"}, "You are not authorize= d to edit user specific information.") + unless $authz->hasPermissions($userID, "access_instructor_tools"); + + # templates for getting field names + my $userTemplate =3D $self->{userTemplate} =3D $db->newUser; + my $permissionLevelTemplate =3D $self->{permissionLevelTemplate} =3D $d= b->newPermissionLevel; +=09 + # first check to see if a save form has been submitted + return '' unless $r->param('save_button'); +=09 + # As it stands we need to check each set to see if it is still assigned= =20 + # the forms are not currently set up to simply transmit changes +=09 + #Get the list of sets and the global set records + my @setIDs =3D $db->listGlobalSets; + my @setRecords =3D grep { defined $_ } $db->getGlobalSets(@setIDs); +=09 + my @assignedSets =3D (); + foreach my $setID (@setIDs) { + push @assignedSets, $setID if defined($r->param("set.$setID.assignment= ")); + } + debug("assignedSets", join(" ", @assignedSets)); + my %selectedSets =3D map { $_ =3D> 1 } @assignedSets; + #debug ########################## + #print STDERR ("aSsigned sets", join(" ",@assignedSets)); + #my @params =3D $r->param(); + #print STDERR " parameters ", join(" ", @params); + ############### + #Get the user(s) whose records are to be modified + # for now: $editForUserID + # check the user exists? Is this necessary? + my $editUserRecord =3D $db->getUser($editForUserID); + die "record not found for $editForUserID.\n" unless $editUserRecord; +=09 +=09 + #Perform the desired assignments or deletions + my %userSets =3D map { $_ =3D> 1 } $db->listUserSets($editForUserID); + =09 + # go through each possible set + debug(" parameters ", join(" ", $r->param()) ); + foreach my $setRecord (@setRecords) { + my $setID =3D $setRecord->set_id; + # does the user want it to be assigned to the selected user + if (exists $selectedSets{$setID}) { + $self->assignSetToUser($editForUserID, $setRecord); + #override dates + =09 + + my $userSetRecord =3D $db->getUserSet($editForUserID, $setID); + # get the dates + =09 + + + #do checks to see if new dates meet criteria + my $rh_dates =3D $self->checkDates($setRecord,$setID); + unless ( $rh_dates->{error} ) { #returns 1 if error + # if no error update database + foreach my $field (keys %{DATE_FIELDS()}) { + if (defined $r->param("set.$setID.$field.override")) { + $userSetRecord->$field($rh_dates->{$field}); =20 + } else { + $userSetRecord->$field(undef); #stop override + } + } + $db->putUserSet($userSetRecord); + =09 + } + + } else { + # user asked to NOT have the set assigned to the selected user + # debug("deleteUserSet($editForUserID, $setID)"); + $db->deleteUserSet($editForUserID, $setID); + # debug("done deleteUserSet($editForUserID, $setID)"); + } + } +=09 + return ''; +=09 +=09 +=09 +} + +sub body { + my ($self) =3D @_; + my $r =3D $self->r; + my $urlpath =3D $r->urlpath; + my $db =3D $r->db; + my $ce =3D $r->ce; + my $authz =3D $r->authz; + my $courseID =3D $urlpath->arg("courseID"); + my $editForUserID =3D $urlpath->arg("userID"); + my $userID =3D $r->param("user"); +=09 + my @editForSets =3D $r->param("editForSets"); +=09 + return CGI::div({class =3D> "ResultsWithError"}, "You are not authorize= d to edit user specific information.") + unless $authz->hasPermissions($userID, "access_instructor_tools"); +=09 + my $UserRecord =3D $db->getUser($editForUserID); + my $PermissionRecord =3D $db->getPermissionLevel($editForUserID); + my @UserSetIDs =3D $db->listUserSets($editForUserID); +=09 + my $userName =3D $UserRecord->first_name . " " . $UserRecord->last_name= ; + + # templates for getting field names + my $userTemplate =3D $self->{userTemplate}; + my $permissionLevelTemplate =3D $self->{permissionLevelTemplate}; +=09 + # This table can be consulted when display-ready forms of field names a= re needed. + my %prettyFieldNames =3D map { $_ =3D> $_ }=20 + $userTemplate->FIELDS(); +=09 +# @prettyFieldNames{qw( +# #user_id +# first_name +# last_name +# email_address +# student_id +# status +# section +# recitation +# comment +# permission +# )} =3D ( +# #"Login Name", +# "First Name", +# "Last Name", +# "Email", +# "Student ID", +# "Status", +# "Section", +# "Recitation", +# "Comment", +# "Permission Level", +# ); +=09 + my @dateFields =3D @{DATE_FIELDS_ORDER()}; + my $rh_dateFieldLabels =3D DATE_FIELDS(); + + + # create a link to the SetsAssignedToUser page +# my $editSetsPath =3D $urlpath->newFromModule( +# "WeBWorK::ContentGenerator::Instructor::SetsAssignedToUser", +# courseID =3D> $courseID, +# userID =3D> $userID, +# ); +# my $editSetsAssignedToUserURL =3D $self->systemLink($editSetsPath); +=09 + # create a message about how many sets have been assigned to this user + my $setCount =3D $db->countUserSets($editForUserID); +# my $userCountMessage =3D CGI::a({href=3D>$editSetsAssignedToUserURL}= , $setCount . " sets."); +# $userCountMessage =3D "The user " . CGI::b($userName . " ($editForUse= rID)") . " has been assigned " . $userCountMessage; + my $basicInfoPage =3D $urlpath->new(type =3D>'instructor_user_list', + args =3D>{ + courseID =3D> $courseID, + } + ); + my $basicInfoUrl =3D $self->systemLink($basicInfoPage, + params =3D>{visible_users =3D> $e= ditForUserID, + editMode =3D> 1, + } + ); + + print CGI::h4({align=3D>'center'},"Edit ",CGI::a({href=3D>$basicInfoUrl= },'class list data')," for $userName ($editForUserID) who has been assig= ned $setCount sets."); +=09 + #print CGI::h4("User Data"); +# print CGI::start_table({ align=3D>'center', border=3D>1,cellpadding=3D= >5}); +# print CGI::Tr( +# CGI::th(CGI::checkbox({ type =3D> 'checkbox', +# name =3D> "edit.basic.info", +# label =3D> '', +# checked =3D> 0 +# }),"Edit class list data for $editForUserID"), +# CGI::th(CGI::checkbox({ type =3D> 'checkbox', +# name =3D> "change.password", +# label =3D> '', +# checked =3D> 0 +# }),"Change Password for $editForUserID")); +# =20 +# print "<tr><td rowspan=3D\"2\">"; +# ######################################## +# # Basic student data +# ######################################## +# print CGI::start_table(); +# foreach ($userTemplate->FIELDS()) { +# next if $_ eq 'user_id'; # don't print login name +# print CGI::Tr( +# CGI::td([ +# $prettyFieldNames{$_},=20 +# CGI::input({ -value =3D> $UserRecord->$_, -size =3D> 25 }) +# ]) +# ); +# } +# foreach ($permissionLevelTemplate->FIELDS()) { +# print CGI::Tr( +# CGI::td([ +# $prettyFieldNames{$_},=20 +# CGI::input({ -value =3D> $PermissionRecord->$_, -size =3D> 25 }) +# ]) +# ); +# } +# print CGI::end_table(); +# =09 +# #print CGI::br(); +# print "</td><td valign=3D\"top\">"; +# ######################################## +# # Change password section +# ######################################## +# my $profRecord =3D $db->getUser($userID); +# my $profName =3D $profRecord->first_name . " " . $profRecord->last_na= me; +# my $poss =3D "'s "; +# my $pass =3D " password "; +# =09 +# print CGI::start_table(); +# print CGI::Tr(CGI::td(["<b>$profName</b>$poss$pass", CGI::input({ -ty= pe =3D> "password", -name =3D> "$userID.password"})])); +# print CGI::Tr(CGI::td(["<b>$userName</b>$poss new $pass", CGI::input(= { -type =3D> "password", -name =3D> "$editForUserID.password.1"})])); +# print CGI::Tr(CGI::td(["Confirm <b>$userName</b>$poss new $pass", CGI= ::input({ -type =3D> "password", -name =3D> "$editForUserID.password.2"})= ])); +# print CGI::end_table(); +# print "</td></tr>"; +# print CGI::Tr(CGI::th( #FIXME enable this once it can be handled +# # CGI::checkbox({ type =3D> 'checkbox', +# # name =3D> "change.login", +# # label =3D> '', +# # checked =3D> 0 +# # }), +# "Change login name $editForUserID to ", CGI::input({-name=3D>'= new_login', -value=3D>'' ,-size=3D>25}) +# )); +# print CGI::end_table(); +=09 + print CGI::br(); + + #print CGI::h4("Sets assigned to $userName"); + # construct url for the form + my $userDetailPage =3D $urlpath->new(type =3D>'instructor_user_detail', + args =3D>{ + courseID =3D> $courseID, + userID =3D> $editForUserID, #FIXME = eventually this should be a list?? + } + ); + my $userDetailUrl =3D $self->systemLink($userDetailPage,authen=3D>0); + + my %GlobalSetRecords =3D map { $_->set_id =3D> $_ } $db->getGlobalSets(= $db->listGlobalSets()); + my @UserSetRefs =3D map { [$editForUserID, $_] } sortByName(undef, @Use= rSetIDs); + my %UserSetRecords =3D map { $_->set_id =3D> $_ } $db->getUserSets(@Use= rSetRefs); + my @MergedSetRefs =3D map { [$editForUserID, $_] } sortByName(undef, @U= serSetIDs); + my %MergedSetRecords =3D map { $_->set_id =3D> $_ } $db->getMergedSets(= @MergedSetRefs); +=09 + ######################################## + # Print warning + ######################################## + print CGI::div({-class=3D>'ResultsWithError'}, + "Do not uncheck a set unless you know what you are doing.", CGI= ::br(), + "There is NO undo for unassigning a set."); + + print CGI::p("To change status (scores or grades) for this student for = one + set, click on the individual set link."); + + print CGI::div({-class=3D>'ResultsWithError'},"When you uncheck a homew= ork set (and save the changes), you destroy all + of the data for that set for this student. If you + reassign the set, the student will receive a new version of each= problem. + Make sure this is what you want to do before unchecking sets." + ); + ######################################## + # Assigned sets form + ######################################## + + print CGI::start_form( {method=3D>'post',action=3D>$userDetailUrl, name= =3D>'UserDetail'}),"\n"; + print $self->hidden_authen_fields(); + print CGI::p(CGI::submit(-name=3D>'save_button',-label=3D>'Save changes= ',)); +=09 + print CGI::start_table({ border=3D> 1,cellpadding=3D>5}),"\n"; + print CGI::Tr( + CGI::th({align=3D>'center',colspan=3D>3}, "Sets assigned to $userName = ($editForUserID)") + ),"\n"; + print CGI::Tr( + CGI::th({ -align =3D> "center"}, [ + "Assigned", + "Edit set for $editForUserID", + "Dates", + ]) + ),"\n"; + foreach my $setID (sortByName(undef, $db->listGlobalSets())) { + my $GlobalSetRecord =3D $GlobalSetRecords{$setID}; + my $UserSetRecord =3D $UserSetRecords{$setID}; + my $MergedSetRecord =3D $MergedSetRecords{$setID}; + my $setListPage =3D $urlpath->new(type =3D>'instructor_set_detail', + args =3D>{ + courseID =3D> $courseID, + setID =3D> $setID + } + ); + my $url =3D $self->systemLink($setListPage, + params =3D>{effectiveUser =3D> $editForUserID, + editForUser =3D> $editForUserID, + }); + + print CGI::Tr( + CGI::td({ -align =3D> "center" }, [ + CGI::checkbox({ type =3D> 'checkbox', + name =3D> "set.$setID.assignment", + label =3D> '', + value =3D> 'assigned', + checked =3D> (defined $MergedSetRecord) + }), + defined($MergedSetRecord) ? CGI::b(CGI::a({href=3D>$url},$setID, ) )= : CGI::b($setID, ), + join "\n", $self->DBFieldTable($GlobalSetRecord, $UserSetRecord, $Me= rgedSetRecord, "set", $setID, \@dateFields,$rh_dateFieldLabels), + ]) + ),"\n"; + } + print CGI::end_table(),"\n"; + print CGI::p(CGI::submit(-name=3D>'save_button',-label=3D>'Save changes= ',)); + print CGI::end_form(),"\n"; + ######################################## + # Print warning + ######################################## + + CGI::div( {class=3D>'ResultsWithError'}, + "There is NO undo for this function. =20 + Do not use it unless you know what you are doing! When you unassig= n + sets using this button, or by unchecking their set names, you destr= oy all + of the data for those sets for this student." + ); + + +# print CGI::start_table(); +# print CGI::Tr( +# CGI::th({ -align =3D> "center"},[ +# "Assigned", +# "Set Name", +# "Opens", +# "Answers Due", +# "Answers Available", +# ]) +# ); + =09 +# foreach my $setID (sortByName(undef, @UserSetIDs)) { +# my $MergedSetRecord =3D $MergedSetRecords{$setID}; +# print CGI::Tr( +# CGI::td({ -align =3D> "center" }, [ +# CGI::checkbox({checked =3D> (defined $MergedSetRecord)}), +# $setID, +# CGI::checkbox() . +# CGI::input({ -value =3D> $self->formatDateTime($MergedSetRecord->op= en_date), -size =3D> 25}), +# CGI::checkbox() . +# CGI::input({ -value =3D> $self->formatDateTime($MergedSetRecord->du= e_date), -size =3D> 25}), +# CGI::checkbox() . +# CGI::input({ -value =3D> $self->formatDateTime($MergedSetRecord->an= swer_date), -size =3D> 25}), +# ]) +# ); +# } + return ''; +} + +sub checkDates {=20 + my $self =3D shift; + my $setRecord =3D shift; + my $setID =3D shift; + my $r =3D $self->r; + my %dates =3D (); + my $error_undefined_override =3D 0; + my $numerical_date=3D0; + my $error =3D 0; + foreach my $field (@{DATE_FIELDS_ORDER()}) { # check that override dat= es can be parsed and are not blank + $dates{$field} =3D $setRecord->$field; + if (defined $r->param("set.$setID.$field.override") ){ + eval{ $numerical_date =3D $self->parseDateTime($r->param("set.$setID.= $field"))}; + unless( $@ ) { + $dates{$field}=3D$numerical_date; + } else { + $self->addbadmessage(" * Badly defined time for set $set= ID $field. No date changes made:<br/>$@"); + $error =3D 1; + } + } + =09 + + } + return {%dates,error=3D>1} if $error; # no point in going on if the = dates can't be parsed. +=09 + my ($open_date, $due_date, $answer_date) =3D map { $dates{$_} } @{DATE_= FIELDS_ORDER()}; + + if ($answer_date < $due_date || $answer_date < $open_date) { =09 + $self->addbadmessage("Answers cannot be made available until on or aft= er the due date in set $setID!"); + $error =3D 1; + } +=09 + if ($due_date < $open_date) { + $self->addbadmessage("Answers cannot be due until on or after the open= date in set $setID!"); + $error =3D 1; + } +=09 + # make sure the dates are not more than 10 years in the future + my $curr_time =3D time; + my $seconds_per_year =3D 31_556_926; + my $cutoff =3D $curr_time + $seconds_per_year*10; + if ($open_date > $cutoff) { + $self->addbadmessage("Error: open date cannot be more than 10 years fr= om now in set $setID"); + $error =3D 1; + } + if ($due_date > $cutoff) { + $self->addbadmessage("Error: due date cannot be more than 10 years fro= m now in set $setID"); + $error =3D 1; + } + if ($answer_date > $cutoff) { + $self->addbadmessage("Error: answer date cannot be more than 10 years = from now in set $setID"); + $error =3D 1; + } +=09 +=09 + if ($error) { + $self->addbadmessage("No date changes were saved!"); + } + return {%dates,error=3D>$error}; +} + +sub DBFieldTable { + my ($self, $GlobalRecord, $UserRecord, $MergedRecord, $recordType, $rec= ordID, $fieldsRef,$rh_fieldLabels) =3D @_; +=09 + return CGI::div({class =3D> "ResultsWithError"}, "No record exists for = $recordType $recordID") unless defined $GlobalRecord; +=09 + my $r =3D $self->r; + my @fields =3D @$fieldsRef; + my @results; + foreach my $field (@fields) { + my $globalValue =3D $GlobalRecord->$field; + my $userValue =3D defined $UserRecord ? $UserRecord->$field : $globalV= alue; + my $mergedValue =3D defined $MergedRecord ? $MergedRecord->$field : $= globalValue; + push @results,=20 + [$rh_fieldLabels->{$field}, + defined $UserRecord ?=20 + CGI::checkbox({ + type =3D> "checkbox", + name =3D> "$recordType.$recordID.$field.override", + label =3D> "", + value =3D> $field, + checked =3D> $r->param("$recordType.$recordID.$field.override") || = ($mergedValue ne $globalValue ? 1 : 0) + }) : "", + defined $UserRecord ?=20 + (CGI::input({ -name=3D>"$recordType.$recordID.$field", + -value =3D> $userValue ? $self->formatDateTime($userV= alue) : "",=20 + -size =3D> 25}) + ) : "", + $self->formatDateTime($globalValue), =09 + ] + =09 + } + + my @table; + foreach my $row (@results) { + push @table, CGI::Tr(CGI::td({-align =3D> "center"}, $row)); + } +=09 + return (CGI::start_table({border =3D> 0}), @table, CGI::end_table()); +} + +1; |