--- loncom/interface/lonquickgrades.pm 2023/01/20 20:25:20 1.49.6.8.2.2
+++ loncom/interface/lonquickgrades.pm 2011/05/21 01:02:27 1.86
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Quick Student Grades Display
#
-# $Id: lonquickgrades.pm,v 1.49.6.8.2.2 2023/01/20 20:25:20 raeburn Exp $
+# $Id: lonquickgrades.pm,v 1.86 2011/05/21 01:02:27 www Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -29,13 +29,12 @@
package Apache::lonquickgrades;
use strict;
-use Apache::Constants qw(:common :http REDIRECT);
+use Apache::Constants qw(:common :http);
use POSIX;
use Apache::loncommon;
use Apache::lonlocal;
use Apache::lonnet;
use Apache::grades;
-use Apache::lonuserstate;
sub handler {
my $r = shift;
@@ -58,110 +57,31 @@ sub real_handler {
return OK;
}
- my $cangrade=&Apache::lonnet::allowed('mgr');
- my $showPoints =
- $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'standard';
-
- my $reinitresult;
+ # Send header, don't cache this page
+ &Apache::loncommon::no_cache($r);
+ $r->send_http_header;
- if ($env{'request.course.id'}) {
- my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
- my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
- my $clientip = &Apache::lonnet::get_requestor_ip($r);
- my ($blocked,$blocktext) =
- &Apache::loncommon::blocking_status('grades',$clientip,$cnum,$cdom);
- if ($blocked) {
- my $checkrole = "cm./$cdom/$cnum";
- if ($env{'request.course.sec'} ne '') {
- $checkrole .= "/$env{'request.course.sec'}";
- }
- unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
- ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
- &grades_blocked($r,$blocktext,$showPoints);
- return OK;
- }
- }
- }
+ my $showPoints =
+ (($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'standard')
+ || ($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'categories'));
+ my $notshowSPRSlink =
+ (($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'external')
+ || ($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'externalnototals'));
+ my $notshowTotals=
+ $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'externalnototals';
+ my $showCategories=
+ $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'categories';
- unless ($cangrade) {
- # Check for critical messages and redirect if present.
- my ($redirect,$url) = &Apache::loncommon::critical_redirect(300,'grades');
- if ($redirect) {
- &Apache::loncommon::content_type($r,'text/html');
- $r->header_out(Location => $url);
- return REDIRECT;
- }
-
- # Check if course needs to be re-initialized
- my $loncaparev = $r->dir_config('lonVersion');
- ($reinitresult,my @reinit) = &Apache::loncommon::needs_coursereinit($loncaparev);
-
- if ($reinitresult eq 'switch') {
- &Apache::loncommon::content_type($r,'text/html');
- $r->send_http_header;
- $r->print(&Apache::loncommon::check_release_result(@reinit));
- return OK;
- }
- my ($cid,$cnum,$cdom);
- if ($reinitresult) {
- $cid = $env{'request.course.id'};
- $cnum = $env{'course.'.$cid.'.num'};
- $cdom = $env{'course.'.$cid.'.domain'};
- }
- if (($reinitresult eq 'main') || ($reinitresult eq 'both')) {
- &Apache::loncommon::content_type($r,'text/html');
- $r->send_http_header;
- &startpage($r,$showPoints);
- my $preamble = '
'.
- '
'.
- &mt('Your course session is being updated because of recent changes by course personnel.').
- ' '.&mt('Please be patient').'.
'.
- '';
- my %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($r,undef,$preamble);
- &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,&mt('Updating course'));
- $r->rflush();
- my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
- &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,&mt('Finished!'));
- &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
- my $closure = <
-//
-
-ENDCLOSE
- if ($ferr) {
- $r->print($closure.&Apache::loncommon::end_page());
- my $requrl = $r->uri;
- $env{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
- $env{'user.reinit'} = 1;
- return HTTP_NOT_ACCEPTABLE;
- } else {
- $r->print($closure);
- }
- }
- if (($reinitresult eq 'supp') || ($reinitresult eq 'both')) {
- my $possdel;
- if ($reinitresult eq 'supp') {
- $possdel = 1;
- }
- my ($supplemental,$refs_updated) = &Apache::loncommon::get_supplemental($cnum,$cdom,'',$possdel);
- unless ($refs_updated) {
- &Apache::loncommon::set_supp_httprefs($cnum,$cdom,$supplemental);
- }
- }
- }
- unless (($reinitresult eq 'main') || ($reinitresult eq 'both')) {
- # Send header, don't cache this page
- &Apache::loncommon::no_cache($r);
- $r->send_http_header;
- &startpage($r,$showPoints);
- }
- $r->rflush();
+ my $title = "Grading and Statistics";#$showPoints ? "Points Display" : "Completed Problems Display";
+ my $brcrum = [{href=>"/adm/quickgrades",text => "Points Display"}];
+ $r->print(&Apache::loncommon::start_page($title,undef,
+ {'bread_crumbs' => $brcrum})
+ );
- &startGradeScreen($r);
+ &startGradeScreen($r,'quick');
+ my $cangrade=&Apache::lonnet::allowed('mgr');
#
# Pick student
#
@@ -173,16 +93,12 @@ ENDCLOSE
if ($env{'form.udom'}) { $udom=$env{'form.udom'}; }
if ($env{'form.id'}) { $stdid=$env{'form.id'}; }
if (($stdid) && ($udom)) {
- $uname=(&Apache::lonnet::idget($udom,[$stdid],'ids'))[1];
+ $uname=(&Apache::lonnet::idget($udom,$stdid))[1];
}
if (($stdid) && (!$uname)) {
$r->print(''.&mt("Unknown Student/Employee ID: [_1]",$stdid).'
');
$stdid='';
}
- if (($uname eq '') && ($udom eq '')) {
- $uname = $env{'user.name'};
- $udom = $env{'user.domain'};
- }
$r->print('\n"); }
- my $requrl = $r->uri;
- $env{'user.error.msg'} = "$requrl:bre:0:0:Navmap initialization failed.";
- return HTTP_NOT_ACCEPTABLE;
+ &outputTable($r,$showPoints,$notshowTotals,
+ $navmap,$totalParts,$totalPossible,$totalRight,$totalAttempted,$topLevelParts,$topLevelRight,$topLevelAttempted);
}
if ($cangrade) { $r->print("\n\n"); }
&endGradeScreen($r);
return OK;
+
}
-sub grades_blocked {
- my ($r,$blocktext,$caller) = @_;
- my $title = 'Points Display';
- if ($caller eq 'spreadsheet') {
- $title = 'Spreadsheet';
- } elsif ($env{'course.'.$env{'request.course.id'}.'.grading'} ne 'standard') {
- $title = 'Completed Problems Display';
- }
- my $brcrum = [{href=>"/adm/quickgrades",text => $title}];
- &Apache::lonhtmlcommon::clear_breadcrumbs();
- &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/quickgrades',
- text=> $title});
- my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs($title);
- &Apache::loncommon::content_type($r,'text/html');
- &Apache::loncommon::no_cache($r);
- $r->send_http_header;
- $r->print(&Apache::loncommon::start_page($title).
- $breadcrumbs.
- $blocktext.
- &Apache::loncommon::end_page());
- return;
+sub startGradeScreen {
+ my ($r,$mode)=@_;
+
+ my $showPoints =
+ $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'standard';
+ my $notshowSPRSlink =
+ (($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'external')
+ || ($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'externalnototals')
+ || ($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'categories'));
+ my $notshowTotals=
+ $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'externalnototals';
+ my $showCategories=
+ $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'categories';
+
+ my $allowed_to_view = &Apache::lonnet::allowed('vgr',$env{'request.course.id'});
+ my $allowed_to_edit = &Apache::lonnet::allowed('mgr',$env{'request.course.id'});
+
+ if ($allowed_to_view) {
+ my @notes;
+ push(@notes,&mt('Students do not see total points.')) if ($notshowTotals);
+ push(@notes,&mt('Students do not see link to spreadsheet.')) if ($notshowSPRSlink);
+ push(@notes,&mt('Students will see points based on problem weights.')) if ($showPoints);
+ push(@notes,&mt('Students will see points based on categories.')) if ($showCategories);
+ push(@notes, &Apache::lonhtmlcommon::coursepreflink(&mt('Grade display settings'),'grading'));
+ $r->print(&Apache::loncommon::head_subbox(join(' ',@notes)));
+ }
+
+
+ $r->print("\n".'');
+ $r->print("\n".'- '.
+ ($showPoints?&mt('Individual Points Overview'):($showCategories?&mt('Grades Overview'):&mt('Completion Overview'))).
+ '
');
+
+ if (!($showPoints || $notshowSPRSlink) || ($allowed_to_view)) {
+ $r->print("\n".'- '.
+ &mt('Spreadsheet (Detailed)').'
');
+ }
+ if ($allowed_to_view) {
+ $r->print("\n".'- '.
+ &mt('Statistics and Reports').'
');
+
+ $r->print("\n".'- '.
+ &mt('Assessment Overview Chart').'
');
+
+ }
+ if ($allowed_to_edit) {
+ $r->print("\n".'- '.
+ &mt('Content Grading').'
');
+ if ($env{'form.symb'}) {
+ $r->print("\n".'- '.
+ &mt('Problem Grading').'
');
+
+ }
+ }
+ $r->print("\n".'
'."\n");
+ $r->print('');
+}
+
+sub endGradeScreen {
+ my ($r)=@_;
+ $r->print('
'.&Apache::loncommon::end_page());
}
-#
-# Go through the complete course and collect data
-#
sub getData {
@@ -255,17 +203,13 @@ sub getData {
# Create the nav map
my $navmap = Apache::lonnavmaps::navmap->new($uname,$udom);
- if (!defined($navmap)) {
- return ();
- }
-
my $res = $navmap->firstResource(); # temp resource to access constants
my $iterator = $navmap->getIterator(undef, undef, undef, 1);
my $depth = 1;
$iterator->next(); # ignore first BEGIN_MAP
my $curRes = $iterator->next();
-
+
# General overview of the following: Walk along the course resources.
# For every problem in the resource, tell its parent maps how many
# parts and how many parts correct it has. After that, each map will
@@ -298,36 +242,39 @@ sub getData {
my $stack = $iterator->getStack();
for my $part (@{$parts}) {
+ my $completionStatus = $curRes->getCompletionStatus($part);
my $dateStatus = $curRes->getDateStatus($part);
- my $weight = $curRes->weight($part);
- my $problemstatus = $curRes->problemstatus($part);
-
- if ($curRes->solved($part) eq 'excused') {
+
+ if ($completionStatus == $curRes->EXCUSED()) {
next;
}
if ($showPoints) {
my $score = 0;
# If we're not telling status and the answer date isn't passed yet,
# it's an "attempted" point
- if ((($problemstatus eq 'no') ||
- ($problemstatus eq 'no_feedback_ever')) &&
+ if ((($curRes->problemstatus($part) eq 'no') ||
+ ($curRes->problemstatus($part) eq 'no_feedback_ever')) &&
($dateStatus != $curRes->ANSWER_OPEN)) {
my $status = $curRes->simpleStatus($part);
if ($status == $curRes->ATTEMPTED) {
- $partsAttempted += $weight;
+ $partsAttempted += $curRes->weight($part);
$totalAttempted += $partsAttempted;
}
} else {
- $score = &Apache::grades::compute_points($weight, $curRes->awarded($part));
+ $score = &Apache::grades::compute_points($curRes->weight($part), $curRes->awarded($part));
}
$partsRight += $score;
$totalRight += $score;
- $partsCount += $weight;
+ $partsCount += $curRes->weight($part);
+
+ $curRes->{DATA}->{PROB_SCORE} += $score;
+ $curRes->{DATA}->{PROB_WEIGHT} += $curRes->weight($part);
if ($curRes->opendate($part) < $now) {
- $totalPossible += $weight;
+ $totalPossible += $curRes->weight($part);
+ $curRes->{DATA}->{PROB_POSSIBLE} += $curRes->weight($part);
}
- $totalParts += $weight;
+ $totalParts += $curRes->weight($part);
} else {
my $status = $curRes->simpleStatus($part);
my $thisright = 0;
@@ -344,6 +291,7 @@ sub getData {
$totalAttempted++;
}
+ my $dateStatus = $curRes->getDateStatus($part);
$totalParts++;
if ($curRes->opendate($part) < $now) {
$totalPossible++;
@@ -374,8 +322,7 @@ sub getData {
}
$curRes = $iterator->next();
}
- return ($navmap,$totalParts,$totalPossible,$totalRight,$totalAttempted,
- $topLevelParts,$topLevelRight,$topLevelAttempted);
+ return ($navmap,$totalParts,$totalPossible,$totalRight,$totalAttempted,$topLevelParts,$topLevelRight,$topLevelAttempted);
}
#
@@ -384,8 +331,8 @@ sub getData {
sub outputTable {
- my ($r,$showPoints,$notshowTotals,$navmap,$totalParts,$totalPossible,$totalRight,
- $totalAttempted,$topLevelParts,$topLevelRight,$topLevelAttempted)=@_;
+ my ($r,$showPoints,$notshowTotals,
+ $navmap,$totalParts,$totalPossible,$totalRight,$totalAttempted,$topLevelParts,$topLevelRight,$topLevelAttempted)=@_;
my @start = (255, 255, 192);
my @end = (0, 192, 0);
@@ -470,85 +417,602 @@ sub outputTable {
# show totals (if applicable), close table
#
if ($showPoints) {
- my $maxHelpLink = &Apache::loncommon::help_open_topic("Quick_Grades_Possibly_Correct");
+ my $maxHelpLink = &Apache::loncommon::help_open_topic("Quick_Grades_Possibly_Correct");
- $title = $showPoints ? "Points" : "Parts Done";
- my $totaltitle = $showPoints ? &mt("Awarded Total Points") : &mt("Total Parts Done");
- $r->print(&Apache::loncommon::start_data_table_row()
+ $title = $showPoints ? "Points" : "Parts Done";
+ my $totaltitle = $showPoints ? &mt("Awarded Total Points") : &mt("Total Parts Done");
+ $r->print(&Apache::loncommon::start_data_table_row()
.''.$totaltitle.': '.$totalRight.' ');
- $r->print(&mt('Max Possible To Date')." $maxHelpLink: $totalPossible ");
- $title = $showPoints ? "Points" : "Parts";
- $r->print(&mt("Total $title In Course").': '.$totalParts.' | '
+ $r->print(&mt('Max Possible To Date')." $maxHelpLink: $totalPossible
");
+ $title = $showPoints ? "Points" : "Parts";
+ $r->print(&mt("Total $title In Course").': '.$totalParts.''
.&Apache::loncommon::end_data_table_row());
}
$r->print(&Apache::loncommon::end_data_table());
- return;
}
-sub startpage {
- my ($r,$showPoints) = @_;
- my $title = "Grading and Statistics";#$showPoints ? "Points Display" : "Completed Problems Display";
- my $brcrum = [{href=>"/adm/quickgrades",text => "Points Display"}];
- $r->print(&Apache::loncommon::start_page($title,undef,
- {'bread_crumbs' => $brcrum})
- );
+#
+# === Outputting category-based grades.
+#
+# $category{'order'}: output order of categories by id
+# $category{'all'}: complete list of all categories
+# $category{$id.'_name'}: display-name of category
+#
+
+sub outputCategories {
+
+ my ($r,$showPoints,$notshowTotals,
+ $navmap,$totalParts,$totalPossible,$totalRight,$totalAttempted,$topLevelParts,$topLevelRight,$topLevelAttempted)=@_;
+# Take care of storing and retrieving categories
+
+ my $cangrade=&Apache::lonnet::allowed('mgr');
+
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my %categories=();
+# Loading old categories
+ %categories=&Apache::lonnet::dump('grading_categories',$cdom,$cnum);
+# Storing
+ if (($cangrade) && (($env{'form.storechanges'}) || ($env{'form.storemove'} ne '') || ($env{'form.cmd'} ne ''))) {
+# Process the changes
+ %categories=&process_category_edits($r,$cangrade,%categories);
+# Actually store
+ &Apache::lonnet::put('grading_categories',\%categories,$cdom,$cnum);
+ }
+# new categories loaded now
+ &output_category_table($r,$cangrade,$navmap,%categories);
+#
+ if ($cangrade) {
+ $r->print(&Apache::loncommon::resourcebrowser_javascript().
+ ''.
+ ''.
+ ''.
+ ''.
+ '');
+ }
+#
+# Debug
+#
+# my %data=&dumpdata($navmap);
+# foreach (keys(%data)) {
+# $r->print("\n
".$_.'='.$data{$_});
+# }
}
-sub startGradeScreen {
- my ($r)=@_;
+#
+# Get data for all symbs
+#
- my $showPoints =
- $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'standard';
- my $notshowSPRSlink =
- (($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'external')
- || ($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'externalnototals'));
- my $notshowTotals =
- $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'externalnototals';
- my $showSPRSlink =
- $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'spreadsheet';
+sub dumpdata {
+ my ($navmap)=@_;
+ my %returndata=();
- my $allowed_to_view = &Apache::lonnet::allowed('vgr',$env{'request.course.id'});
- if ((!$allowed_to_view) && ($env{'request.course.sec'} ne '')) {
- $allowed_to_view = &Apache::lonnet::allowed('vgr',
- "$env{'request.course.id'}/$env{'request.course.sec'}");
+# Run through the map and get all data
+
+ my $iterator = $navmap->getIterator(undef, undef, undef, 1);
+ my $depth = 1;
+ $iterator->next(); # ignore first BEGIN_MAP
+ my $curRes = $iterator->next();
+
+ while ($depth > 0) {
+ if ($curRes == $iterator->BEGIN_MAP()) {$depth++;}
+ if ($curRes == $iterator->END_MAP()) { $depth--; }
+ if (ref($curRes)) {
+ if ($curRes->is_map()) {
+ $returndata{$curRes->symb()}='folder:'.$curRes->{DATA}->{CHILD_PARTS}.':'.$curRes->{DATA}->{CHILD_ATTEMPTED}.':'.$curRes->{DATA}->{CHILD_CORRECT};
+ } else {
+ $returndata{$curRes->symb()}='res:'.$curRes->{DATA}->{PROB_WEIGHT}.':'.$curRes->{DATA}->{PROB_POSSIBLE}.':'.$curRes->{DATA}->{PROB_SCORE};
+ }
+ }
+ $curRes = $iterator->next();
}
+ return %returndata;
+}
+
+#
+# Process editing commands, update category hash
+#
+
+sub process_category_edits {
+ my ($r,$cangrade,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+# First store everything
+ foreach my $id (split(/\,/,$categories{'order'})) {
+# Set names, types, and weight (there is only one of each per category)
+ %categories=&set_category_name($cangrade,$id,$env{'form.name_'.$id},%categories);
+ %categories=&set_category_total($cangrade,$id,$env{'form.totaltype_'.$id},$env{'form.total_'.$id},%categories);
+ %categories=&set_category_weight($cangrade,$id,$env{'form.weight_'.$id},%categories);
+ %categories=&set_category_displayachieved($cangrade,$id,$env{'form.displayachieved_'.$id},%categories);
+# Set values for category rules (before names may change)
+ %categories=&set_category_rules($cangrade,$id,%categories);
+ }
+
+# Now deal with commands
+ my $cmd=$env{'form.cmd'};
+ if ($cmd eq 'createnewcat') {
+ %categories=&make_new_category($r,$cangrade,undef,%categories);
+ } elsif ($cmd=~/^up\_(.+)$/) {
+ %categories=&move_up_category($1,$cangrade,%categories);
+ } elsif ($cmd=~/^down\_(.+)$/) {
+ %categories=&move_down_category($1,$cangrade,%categories);
+ } elsif ($cmd=~/^delcat\_(.+)$/) {
+ %categories=&del_category($1,$cangrade,%categories);
+ } elsif ($cmd=~/^addcont\_(.+)$/) {
+ %categories=&add_category_content($1,$cangrade,$env{'form.addcont_'.$1.'_symb'},%categories);
+ } elsif ($cmd=~/^delcont\_(.+)\_\_\_\_\_\_(.+)$/) {
+ %categories=&del_category_content($1,$cangrade,$2,%categories);
+ } elsif ($cmd=~/^newrule\_(.+)$/) {
+ %categories=&add_calculation_rule($1,$cangrade,':',%categories);
+ } elsif ($cmd=~/^delrule\_(.+)\_\_\_\_\_\_(.*)$/) {
+ %categories=&del_calculation_rule($1,$cangrade,$2,%categories);
+ }
+# Move to a new position
+ my $moveid=$env{'form.storemove'};
+ if ($moveid) {
+ %categories=&move_category($moveid,$cangrade,$env{'form.newpos_'.$moveid},%categories);
+ }
+ return %categories;
+}
+
+#
+# Output the table
+#
- my $allowed_to_edit = &Apache::lonnet::allowed('mgr',$env{'request.course.id'});
- if ((!$allowed_to_edit) && ($env{'request.course.sec'} ne '')) {
- $allowed_to_edit = &Apache::lonnet::allowed('mgr',
- "$env{'request.course.id'}/$env{'request.course.sec'}");
+sub output_category_table {
+ my ($r,$cangrade,$navmaps,%categories)=@_;
+ my $sum=0;
+ my $total=0;
+ $r->print(&Apache::loncommon::start_data_table());
+#
+ &output_category_table_header($r,$cangrade);
+#
+ my @order=split(/\,/,$categories{'order'});
+#
+ my $maxpos=$#order;
+ for (my $i=0;$i<=$maxpos;$i++) {
+ my ($value,$weight)=&output_and_calc_category($r,$cangrade,$navmaps,$order[$i],$i,$maxpos,%categories);
+ $sum+=$value*$weight;
+ $total+=$weight;
}
+#
+ &bottom_line_category($r,$cangrade,$sum,$total);
+#
+ $r->print(&Apache::loncommon::end_data_table());
+ return $sum;
+}
- if ($allowed_to_view) {
- my @notes;
- push(@notes,&mt('Students do not see total points.')) if ($notshowTotals);
- push(@notes,&mt('Students do not see link to spreadsheet.')) if ($notshowSPRSlink);
- push(@notes,&mt('Students will see points based on problem weights.')) if ($showPoints);
- push(@notes,&mt('Students will see link to spreadsheet.')) if ($showSPRSlink);
- push(@notes,&Apache::lonhtmlcommon::coursepreflink(&mt('Grade display settings'),'grading'));
- $r->print(&Apache::loncommon::head_subbox(join(' ',@notes)));
- } elsif (!$allowed_to_edit) {
- if (!$showPoints && !$notshowSPRSlink ) {
- $r->print(&Apache::loncommon::head_subbox(
- &mt('This screen shows how many problems (or problem parts) you have completed'
- .', and how many you have not yet done.'
- .' You can also look at [_1]a detailed score sheet[_2].'
- ,'','')));
+sub output_category_table_header {
+ my ($r,$cangrade)=@_;
+ $r->print(&Apache::loncommon::start_data_table_header_row());
+ if ($cangrade) {
+ $r->print(''.&mt("Move").' | '.&mt('Action').' | ');
+ }
+ $r->print(''.&mt('Category').' | '.
+ ''.&mt('Contents').' | '.
+ ''.&mt('Total Points').' | '.
+ ''.&mt('Calculation').' | '.
+ ''.&mt('Relative Weight').' | '.
+ ''.&mt('Achieved').' | ');
+ $r->print(&Apache::loncommon::end_data_table_header_row());
+}
+
+
+#
+# Output one category to table
+#
+
+sub output_and_calc_category {
+ my ($r,$cangrade,$navmaps,$id,$currentpos,$maxpos,%categories)=@_;
+ my $value=0;
+ my $weight=0;
+ my $iconpath = &Apache::loncommon::lonhttpdurl($r->dir_config('lonIconsURL') . "/");
+ my %lt=&Apache::lonlocal::texthash(
+ 'up' => 'Move Up',
+ 'dw' => 'Move Down');
+ $r->print("\n".&Apache::loncommon::start_data_table_row());
+
+ if ($cangrade) {
+ $r->print(<
+
+
+
+
+
+
+
+
+
+
+
+ENDMOVE
+ $r->print("\n\n\n | \n");
+ $r->print(''.&mt('Delete').' | ');
+ $r->print(' | ');
+ } else {
+ $r->print(''.$categories{$id.'_name'}.' | ');
+ }
+# Content
+ $r->print('');
+ foreach my $contentid (split(/\,/,$categories{$id.'_content'})) {
+ $r->print('- ');
+ $r->print(&Apache::lonnet::gettitle($contentid));
+ if ($cangrade) {
+ $r->print(' '.&mt('Delete').'');
+ }
+ $r->print('
');
+ }
+ $r->print(' ');
+ if ($cangrade) {
+ $r->print(' '.&Apache::loncommon::selectresource_link('quickform','addcont_'.$id,&mt('Add Problem or Folder')).' ');
+ }
+ $r->print(' | ');
+
+# Total
+ if ($cangrade) {
+ $r->print(''.
+ ''.
+ ' | ');
+ } else {
+ $r->print(''.($categories{$id.'_totaltype'} eq 'default'?&mt('default'):$categories{$id.'_total'}).' | ');
+ }
+
+
+# Calculation
+ $r->print('');
+ foreach my $calcrule (split(/\,/,$categories{$id.'_calculations'})) {
+ $r->print('- ');
+ my ($code,$value)=split(/\:/,$calcrule);
+ $r->print(&pretty_prt_rule($cangrade,$id,$code,$value));
+ if ($cangrade) {
+ $r->print(' '.&mt('Delete').'');
}
+ $r->print('
');
}
- return;
+ $r->print(' ');
+ if ($cangrade) {
+ $r->print(' '.&new_calc_rule_form($id));
+ }
+ $r->print(' | ');
+
+# Weight
+ if ($cangrade) {
+ $r->print(''.
+ ' | ');
+ } else {
+ $r->print(''.$categories{$id.'_weight'}.' | ');
+ }
+# Achieved
+ $r->print('');
+ if ($cangrade) {
+ $r->print('');
+ } else {
+ if ($categories{$id.'_displayachieved'} eq 'percent') {
+ $r->print(&mt('percent'));
+ } else {
+ $r->print(&mt('points'));
+ }
+ }
+ $r->print(' | ');
+
+ return ($value,$weight);
}
-sub endGradeScreen {
- my ($r)=@_;
- $r->print(&Apache::loncommon::end_page());
- return;
+#
+# Bottom line with grades
+#
+
+sub bottom_line_category {
+ my ($r,$cangrade,$sum,$total)=@_;
+ $r->print(&Apache::loncommon::start_data_table_row());
+ if ($cangrade) {
+ $r->print(''.&mt('Create New Category').' | ');
+ }
+ $r->print(''.&mt('Current:').$sum.' '.&mt('Total:').$total.'
| ');
+}
+
+#
+# Make one new category
+#
+
+sub make_new_category {
+ my ($r,$cangrade,$ordernum,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+# Generate new ID
+ my $id=time.'_'.$$.'_'.rand(10000);
+# Add new ID to list of all IDs ever created in this course
+ $categories{'all'}.=','.$id;
+ $categories{'all'}=~s/^\,//;
+# Add new ID to ordered list of displayed and evaluated categories
+ $categories{'order'}.=','.$id;
+ $categories{'order'}=~s/^\,//;
+# Move it into desired space
+ if (defined($ordernum)) {
+ %categories=&move_category($id,$cangrade,$ordernum,%categories);
+ }
+ $categories{$id.'_weight'}=0;
+ $categories{$id.'_totaltype'}='default';
+ $categories{$id.'_displayachieved'}='percent';
+ return %categories;
+}
+
+
+# === Calculation Rule Editing
+
+sub category_rule_codes {
+ return &Apache::lonlocal::texthash(
+ 'droplow' => 'Drop N lowest grade assignments',
+ 'drophigh' => 'Drop N highest grade assignments',
+ 'capabove' => 'Cap percentage above N percent',
+ 'capbelow' => 'Cap percentage below N percent');
+}
+
+sub pretty_prt_rule {
+ my ($cangrade,$id,$code,$value)=@_;
+ my $cid=$id.'_'.$code;
+ my %lt=&category_rule_codes();
+ my $ret='';
+ if ($cangrade) {
+ $ret.=' N=';
+ } else {
+ $ret.=$lt{$code}.'; N='.$value;
+ }
+ $ret.='';
+ return $ret;
+}
+
+sub new_calc_rule_form {
+ my ($id)=@_;
+ return ''.&mt('New Calculation Rule').'';
+}
+
+#
+# Add a calculation rule
+#
+
+sub add_calculation_rule {
+ my ($id,$cangrade,$newcontent,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ my %newcontent=($newcontent => 1);
+ foreach my $current (split(/\,/,$categories{$id.'_calculations'})) {
+ $newcontent{$current}=1;
+ }
+ $categories{$id.'_calculations'}=join(',',sort(keys(%newcontent)));
+ return %categories;
+}
+
+#
+# Delete a calculation rule
+#
+
+sub del_calculation_rule {
+ my ($id,$cangrade,$delcontent,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ my @newcontent=();
+ foreach my $current (split(/\,/,$categories{$id.'_calculations'})) {
+ unless ($current=~/^\Q$delcontent\E\:/) {
+ push(@newcontent,$current);
+ }
+ }
+ $categories{$id.'_calculations'}=join(',',@newcontent);
+ return %categories;
+}
+
+sub set_category_rules {
+ my ($cangrade,$id,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ my %lt=&category_rule_codes();
+ my @newrules=();
+ foreach my $code ('',(keys(%lt))) {
+ if ($env{'form.sel_'.$id.'_'.$code}) {
+ push(@newrules,$env{'form.sel_'.$id.'_'.$code}.':'.$env{'form.val_'.$id.'_'.$code});
+ }
+ }
+ $categories{$id.'_calculations'}=join(',',sort(@newrules));
+ return %categories;
}
+
+# === Category Editing
+
+#
+# Add to category content
+#
+
+sub add_category_content {
+ my ($id,$cangrade,$newcontent,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ my %newcontent=($newcontent => 1);
+ foreach my $current (split(/\,/,$categories{$id.'_content'})) {
+ $newcontent{$current}=1;
+ }
+ $categories{$id.'_content'}=join(',',sort(keys(%newcontent)));
+ return %categories;
+}
+
+#
+# Delete from category content
+#
+
+sub del_category_content {
+ my ($id,$cangrade,$delcontent,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ my @newcontent=();
+ foreach my $current (split(/\,/,$categories{$id.'_content'})) {
+ unless ($current eq $delcontent) {
+ push(@newcontent,$current);
+ }
+ }
+ $categories{$id.'_content'}=join(',',@newcontent);
+ return %categories;
+}
+
+#
+# Delete category
+#
+
+sub del_category {
+ my ($id,$cangrade,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ my @neworder=();
+ foreach my $currentid (split(/\,/,$categories{'order'})) {
+ unless ($currentid eq $id) {
+ push(@neworder,$currentid);
+ }
+ }
+ $categories{'order'}=join(',',@neworder);
+ return %categories;
+}
+
+#
+# Move category up
+#
+
+sub move_up_category {
+ my ($id,$cangrade,%categories)=@_;
+ my $currentpos=¤t_pos_category($id,%categories);
+ if ($currentpos<1) { return %categories; }
+ return &move_category($id,$cangrade,$currentpos-1,%categories);
+}
+
+#
+# Move category down
+#
+
+sub move_down_category {
+ my ($id,$cangrade,%categories)=@_;
+ my $currentpos=¤t_pos_category($id,%categories);
+ my @order=split(/\,/,$categories{'order'});
+ if ($currentpos>=$#order) { return %categories; }
+ return &move_category($id,$cangrade,$currentpos+1,%categories);
+}
+
+#
+# Move a category to a desired position n the display order
+#
+
+sub move_category {
+ my ($id,$cangrade,$ordernum,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ my @order=split(/\,/,$categories{'order'});
+# Where is the index currently?
+ my $currentpos=¤t_pos_category($id,%categories);
+ if (defined($currentpos)) {
+ if ($currentpos<$ordernum) {
+# This is moving to a higher index
+# ....X1234....
+# ....1234X....
+ for (my $i=$currentpos;$i<$ordernum;$i++) {
+ $order[$i]=$order[$i+1];
+ }
+ $order[$ordernum]=$id;
+ }
+ if ($currentpos>$ordernum) {
+# This is moving to a lower index
+# ....1234X....
+# ....X1234....
+ for (my $i=$currentpos;$i>$ordernum;$i--) {
+ $order[$i]=$order[$i-1];
+ }
+ $order[$ordernum]=$id;
+ }
+ }
+ $categories{'order'}=join(',',@order);
+ return %categories;
+}
+
+#
+# Find current postion of a category in the order
+#
+
+sub current_pos_category {
+ my ($id,%categories)=@_;
+ my @order=split(/\,/,$categories{'order'});
+ for (my $i=0;$i<=$#order;$i++) {
+ if ($order[$i] eq $id) { return $i; }
+ }
+# not found
+ return undef;
+}
+
+#
+# Set name of a category
+#
+sub set_category_name {
+ my ($cangrade,$id,$name,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ $categories{$id.'_name'}=$name;
+ return %categories;
+}
+
+#
+# Set total of a category
+#
+sub set_category_total {
+ my ($cangrade,$id,$totaltype,$total,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ if (($categories{$id.'_total'} eq '') && ($total=~/\d/)) {
+ $totaltype='typein';
+ }
+ $categories{$id.'_totaltype'}=$totaltype;
+ if ($totaltype eq 'default') {
+ $categories{$id.'_total'}='';
+ } else {
+ $total=~s/\D//gs;
+ unless ($total) { $total=0; }
+ $categories{$id.'_total'}=$total;
+ }
+ return %categories;
+}
+
+sub set_category_weight {
+ my ($cangrade,$id,$weight,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ $weight=~s/\D//gs;
+ unless ($weight) { $weight=0; }
+ $categories{$id.'_weight'}=$weight;
+ return %categories;
+}
+
+sub set_category_displayachieved {
+ my ($cangrade,$id,$value,%categories)=@_;
+ unless ($cangrade) { return %categories; }
+ unless (($value eq 'percent') || ($value eq 'points')) { $value='percent'; }
+ $categories{$id.'_displayachieved'}=$value;
+ return %categories;
+}
+
+
+#
+# === end category-related
+#
+#
# Pass this two refs to arrays for the start and end color, and a number
# from 0 to 1 for how much of the latter you want to mix in. It will
# return a string ready to show ("#FFC309");
+
sub mixColors {
my $start = shift;
my $end = shift;