Annotation of loncom/interface/statistics/lonstudentassessment.pm, revision 1.30
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.30 ! matthew 3: # $Id: lonstudentassessment.pm,v 1.29 2003/02/25 20:47:47 matthew Exp $
1.1 stredwic 4: #
5: # Copyright Michigan State University Board of Trustees
6: #
7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
8: # LON-CAPA is free software; you can redistribute it and/or modify
9: # it under the terms of the GNU General Public License as published by
10: # the Free Software Foundation; either version 2 of the License, or
11: # (at your option) any later version.
12: #
13: # LON-CAPA is distributed in the hope that it will be useful,
14: # but WITHOUT ANY WARRANTY; without even the implied warranty of
15: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: # GNU General Public License for more details.
17: #
18: # You should have received a copy of the GNU General Public License
19: # along with LON-CAPA; if not, write to the Free Software
20: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21: #
22: # /home/httpd/html/adm/gpl.txt
23: #
24: # http://www.lon-capa.org/
25: #
26: # (Navigate problems for statistical reports
1.28 matthew 27: #
28: #######################################################
29: #######################################################
30:
31: =pod
32:
33: =head1 NAME
34:
35: lonstudentassessment
36:
37: =head1 SYNOPSIS
38:
39: Presents assessment data about a student or a group of students.
40:
41: =head1 Subroutines
42:
43: =over 4
44:
45: =cut
46:
47: #######################################################
48: #######################################################
1.1 stredwic 49:
1.21 minaeibi 50: package Apache::lonstudentassessment;
1.1 stredwic 51:
52: use strict;
1.28 matthew 53: use Apache::lonstatistics;
1.1 stredwic 54: use Apache::lonhtmlcommon;
55: use Apache::loncoursedata;
1.28 matthew 56: use Apache::lonnet; # for logging porpoises
1.1 stredwic 57: use GDBM_File;
58:
1.30 ! matthew 59: my $Statistics;
! 60:
1.28 matthew 61: #######################################################
62: #######################################################
63:
64: =pod
65:
66: =item &BuildStudentAssessmentPage()
67:
68: Inputs:
69:
70: =over 4
71:
72: =item $cacheDB The name of the cache file used to store student data
73:
74: =item $students Array ref containing the name(s) of the students
75: selected for display
76:
77: =item $courseID The ID of the course
78:
79: =item $formName The name of the html form - 'Statistics'
80:
81: =item $headings Array ref of headings to show
82:
83: =item $spacing A string of spaces
1.4 stredwic 84:
1.28 matthew 85: =item $studentInformation Array ref of possible headings for student info
86: ('fullname','section',...)
87:
88: =item $r Apache Request
89:
90: =item $c Apache Connection
91:
92: =back
93:
94: =cut
95:
96: #######################################################
97: #######################################################
1.1 stredwic 98: sub BuildStudentAssessmentPage {
1.30 ! matthew 99: my ($r,$c)=@_;
! 100: undef($Statistics);
! 101:
! 102: #
! 103: $r->print(&CreateInterface());
1.7 stredwic 104: $r->rflush();
1.30 ! matthew 105: #
! 106: $r->print(&CreateTableHeadings());
! 107: if($c->aborted()) { return ; }
! 108:
1.24 minaeibi 109: my $Count = 0;
1.2 stredwic 110: $r->print('<pre>'."\n");
1.30 ! matthew 111: foreach my $student (@Apache::lonstatistics::Students) {
! 112: if($c->aborted()) { return ; }
! 113: $r->print(&ChartOutputStudent($student));
! 114: # output it
1.8 stredwic 115:
1.30 ! matthew 116: $Count++;
! 117: if($Count % 5 == 0) {
! 118: $r->print("</pre>\n<pre>");
1.1 stredwic 119: }
1.30 ! matthew 120:
! 121: $r->rflush();
1.1 stredwic 122: }
1.26 minaeibi 123: $r->print('</pre>'."\n");
1.30 ! matthew 124: my $Str;
1.2 stredwic 125: return;
126: }
1.30 ! matthew 127:
1.28 matthew 128: #######################################################
129: #######################################################
130:
131: =pod
1.2 stredwic 132:
1.28 matthew 133: =item &CreateInterface()
1.21 minaeibi 134:
1.28 matthew 135: Called by &BuildStudentAssessmentPage to create the top part of the
136: page which displays the chart.
137:
1.30 ! matthew 138: Inputs: None
1.28 matthew 139:
140: Returns: A string containing the HTML for the headers and top table for
141: the chart page.
142:
143: =cut
144:
145: #######################################################
146: #######################################################
1.2 stredwic 147: sub CreateInterface {
1.4 stredwic 148: my $Str = '';
1.30 ! matthew 149: # $Str .= &CreateLegend();
! 150: $Str .= '<table cellspacing="5">'."\n";
! 151: $Str .= '<tr>';
! 152: $Str .= '<td align="center"><b>Sections</b></td>';
! 153: $Str .= '<td align="center"><b>Student Data</b></td>';
! 154: $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
! 155: $Str .= '</tr>'."\n";
! 156: #
1.4 stredwic 157: $Str .= '<tr><td align="center">'."\n";
1.29 matthew 158: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
1.4 stredwic 159: $Str .= '</td><td align="center">';
1.30 ! matthew 160: my $only_seq_with_assessments = sub {
! 161: my $s=shift;
! 162: if ($s->{'num_assess'} < 1) {
! 163: return 0;
! 164: } else {
! 165: return 1;
! 166: }
! 167: };
! 168: $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple',
! 169: 5,undef);
1.4 stredwic 170: $Str .= '</td><td>'."\n";
1.30 ! matthew 171: $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
! 172: $only_seq_with_assessments);
! 173: $Str .= '</td></tr>'."\n";
! 174: $Str .= '</table>'."\n";
1.4 stredwic 175: return $Str;
1.1 stredwic 176: }
1.30 ! matthew 177:
! 178:
! 179: #######################################################
! 180: #######################################################
! 181:
! 182: =pod
! 183:
! 184: =item Table Output Routines
! 185:
! 186: =over 4
! 187:
! 188: =cut
! 189:
! 190: #######################################################
! 191: #######################################################
! 192: {
! 193: my $padding;
! 194:
1.28 matthew 195: #######################################################
196: #######################################################
1.1 stredwic 197:
1.28 matthew 198: =pod
199:
200: =item &CreateTableHeadings()
201:
202: Create HTML for the columns of student data to show.
203: Called by &BuildStudentAssessmentPage(). Calls
204: &Apache::lonhtmlcommon::CreateHeadings().
205:
206: Inputs:
207:
208: =over 4
209:
210: =item $cache The ubiquitous cache
211:
212: =item $spacing A string of spaces
213:
214: =item $infoKeys Array ref to names of keys to display from the cache
215: which describe students
216:
217: =item $infoHeadings Array ref to headings of columns for student info
218:
219: =item $sequenceKeys Array ref of names of keys to use to retrieve sequence
220: data from the cache
221:
222: =item $sequenceHeadings Array ref of names of sequences used for output.
223:
224: =back
225:
226: Returns: A string containing the HTML of the table headings.
227:
228: =cut
229:
230: #######################################################
231: #######################################################
1.2 stredwic 232: sub CreateTableHeadings {
1.30 ! matthew 233: #
! 234: $padding = ' 'x3;
! 235: #
! 236: my $Str = '<pre>';
! 237: # First, the @StudentData fields need to be listed
! 238: my @to_show = @Apache::lonstatistics::SelectedStudentData;
! 239: foreach (@to_show) {
! 240: if ($_ eq 'all') {
! 241: @to_show = @Apache::lonstatistics::StudentDataOrder;
! 242: last;
! 243: }
! 244: }
! 245: foreach my $field (@to_show) {
! 246: my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
! 247: my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
! 248: my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
! 249: $Str .= $title.' 'x($width-$base).$padding;
! 250: }
! 251: # Now the selected sequences need to be listed
! 252: foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
! 253: foreach my $sequence (@Apache::lonstatistics::Sequences) {
! 254: next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
! 255: next if ($sequence->{'num_assess'} < 1);
! 256: my $title = $sequence->{'title'};
! 257: my $base = $sequence->{'base_width'};
! 258: my $width = $sequence->{'width'};
! 259: $Str .= $title.' 'x($width-$base).$padding;
! 260: }
! 261: }
! 262: $Str .= 'total';
! 263: $Str .= "</pre>\n";
! 264: return $Str;
! 265: }
! 266:
! 267: #######################################################
! 268: #######################################################
! 269:
! 270: =pod
! 271:
! 272: =item &ChartOutputStudent($student)
1.2 stredwic 273:
1.30 ! matthew 274: Return a line of the chart for a student.
! 275:
! 276: =cut
! 277:
! 278: #######################################################
! 279: #######################################################
! 280: sub ChartOutputStudent {
! 281: my $student = shift;
1.2 stredwic 282: my $Str = '';
1.30 ! matthew 283: # First, the @StudentData fields need to be listed
! 284: my @to_show = @Apache::lonstatistics::SelectedStudentData;
! 285: foreach (@to_show) {
! 286: if ($_ eq 'all') {
! 287: @to_show = @Apache::lonstatistics::StudentDataOrder;
! 288: last;
! 289: }
! 290: }
! 291: foreach my $field (@to_show) {
! 292: my $title=$student->{$field};
! 293: my $base =scalar(my @Tmp = split(//,$title));
! 294: my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
! 295: $Str .= $title.' 'x($width-$base).$padding;
! 296: }
! 297: # Get ALL the students data
! 298: my %StudentsData;
! 299: my @tmp = &Apache::loncoursedata::get_current_state
! 300: ($student->{'username'},$student->{'domain'},undef,
! 301: $ENV{'request.course.id'});
! 302: if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
! 303: %StudentsData = @tmp;
! 304: }
! 305: if (scalar(@tmp) < 1) {
! 306: $Str .= '<font color="blue">No Course Data</font>'."\n";
! 307: return $Str;
! 308: }
! 309: #
! 310: # By sequence build up the data
! 311: my $studentstats;
! 312: foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
! 313: foreach my $seq (@Apache::lonstatistics::Sequences) {
! 314: next if ($map_symb ne $seq->{'symb'} && $map_symb ne 'all');
! 315: next if ($seq->{'num_assess'} < 1);
! 316: my ($performance,$score,$seq_max) =
! 317: &StudentPerformanceOnSequence($student,\%StudentsData,
! 318: $seq,'linkify');
! 319: $Str .= $performance.$padding;
! 320: $studentstats->{$seq->{'symb'}}->{'score'}= $score;
! 321: $studentstats->{$seq->{'symb'}}->{'max'} = $seq_max;
! 322: }
! 323: }
! 324: #
! 325: # Total it up and store the statistics info.
! 326: my ($score,$max) = (0,0);
! 327: while (my ($symb,$seq_stats) = each (%{$studentstats})) {
! 328: $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
! 329: $Statistics->{$symb}->{'max'} += $seq_stats->{'max'};
! 330: $score += $seq_stats->{'score'};
! 331: $max += $seq_stats->{'max'};
! 332: }
! 333: my $scorelength = scalar(my @tmp1 = split(//,$score));
! 334: my $maxlength = scalar(my @tmp2 = split(//,$max));
! 335: $Str .= ' '.' 'x($maxlength-$scorelength).$score.'/'.$max;
! 336: $Str .= " \n";
! 337: return $Str;
! 338: }
1.2 stredwic 339:
1.30 ! matthew 340: #######################################################
! 341: #######################################################
! 342:
! 343: =pod
! 344:
! 345: =back
! 346:
! 347: =cut
! 348:
! 349: #######################################################
! 350: #######################################################
1.2 stredwic 351:
352: }
353:
1.28 matthew 354: #######################################################
355: #######################################################
356:
1.2 stredwic 357: =pod
358:
1.30 ! matthew 359: =item &StudentPerformanceOnSequence()
1.2 stredwic 360:
1.30 ! matthew 361: Inputs:
1.2 stredwic 362:
363: =over 4
364:
1.30 ! matthew 365: =item $student
1.28 matthew 366:
1.30 ! matthew 367: =item $studentdata Hash ref to all student data
1.2 stredwic 368:
1.30 ! matthew 369: =item $seq Hash ref, the sequence we are working on
1.2 stredwic 370:
1.30 ! matthew 371: =item $links if defined we will output links to each resource.
1.2 stredwic 372:
1.28 matthew 373: =back
1.2 stredwic 374:
375: =cut
1.1 stredwic 376:
1.28 matthew 377: #######################################################
378: #######################################################
1.30 ! matthew 379: sub StudentPerformanceOnSequence {
! 380: my ($student,$studentdata,$seq,$links) = @_;
1.1 stredwic 381: my $Str = '';
1.30 ! matthew 382: my $output_width = 0;
! 383: my ($sum,$max) = (0,0);
! 384: foreach my $resource (@{$seq->{'contents'}}) {
! 385: next if ($resource->{'type'} ne 'assessment');
! 386: my $resource_data = $studentdata->{$resource->{'symb'}};
! 387: my $value = '';
! 388: foreach my $partnum (@{$resource->{'parts'}}) {
! 389: $max++;
! 390: my $symbol = ' '; # default to space
! 391: #
! 392: if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
! 393: my $status = $resource_data->{'resource.'.$partnum.'.solved'};
! 394: if ($status eq 'correct_by_override') {
! 395: $symbol = '+';
! 396: $sum++;
! 397: } elsif ($status eq 'incorrect_by_override') {
! 398: $symbol = '-';
! 399: } elsif ($status eq 'ungraded_attempted') {
! 400: $symbol = '#';
! 401: } elsif ($status eq 'incorrect_attempted') {
! 402: $symbol = '.';
! 403: } elsif ($status eq 'excused') {
! 404: $symbol = 'x';
! 405: $max--;
! 406: } elsif ($status eq 'correct_by_student' &&
! 407: exists($resource_data->{'resource.'.$partnum.'.tries'})){
! 408: my $num = $resource_data->{'resource.'.$partnum.'.tries'};
! 409: if ($num > 9) {
! 410: $symbol = '*';
! 411: } elsif ($num > 0) {
! 412: $symbol = $num;
! 413: } else {
! 414: $symbol = ' ';
! 415: }
! 416: $sum++;
! 417: } else {
! 418: $symbol = ' ';
1.2 stredwic 419: }
1.30 ! matthew 420: } else {
! 421: # Unsolved. Did they try?
! 422: if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
! 423: $symbol = '.';
! 424: } else {
! 425: $symbol = ' ';
1.18 matthew 426: }
1.2 stredwic 427: }
1.30 ! matthew 428: #
! 429: $output_width++;
! 430: if (defined($links) && $symbol ne ' ') {
! 431: $symbol = '<a href="/adm/grades'.
! 432: '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
! 433: '&student='.$student->{'username'}.
! 434: '&domain='.$student->{'domain'}.
! 435: '&command=submission">'.$symbol.'</a>';
! 436: }
! 437: $value .= $symbol;
1.2 stredwic 438: }
1.30 ! matthew 439: $Str .= $value;
1.17 minaeibi 440: }
1.30 ! matthew 441: # Put on the totals
! 442: my $ratio = $sum.'/'.$max;
! 443: my $ratio_length = scalar(my @tmp1 = split(//,$ratio));
! 444: # Pad with extra spaces
! 445: my $width = $seq->{'width'};
! 446: $Str .= ' 'x($width-$output_width-$ratio_length).$ratio;
! 447: #
! 448: return ($Str,$sum,$max);
1.17 minaeibi 449: }
450:
1.28 matthew 451: #######################################################
452: #######################################################
1.17 minaeibi 453: sub StudentAverageTotal {
1.21 minaeibi 454: my ($cache, $students, $sequenceKeys)=@_;
1.23 minaeibi 455: my $Str = "\n<b>Summary Tables:</b>\n";
1.21 minaeibi 456: my %Correct = ();
457: my $ProblemsSolved = 0;
458: my $TotalProblems = 0;
459: my $StudentCount = 0;
460:
461: foreach my $name (@$students) {
462: $StudentCount++;
463: foreach my $sequence (@$sequenceKeys) {
1.23 minaeibi 464: $Correct{$sequence} +=
465: $cache->{$name.':'.$sequence.':problemsCorrect'};
1.17 minaeibi 466: }
1.21 minaeibi 467: $ProblemsSolved += $cache->{$name.':problemsSolved'};
468: $TotalProblems += $cache->{$name.':totalProblems'};
1.1 stredwic 469: }
1.25 matthew 470: if ($StudentCount) {
1.27 minaeibi 471: $ProblemsSolved = sprintf( "%.2f",
472: $ProblemsSolved/$StudentCount);
1.25 matthew 473: $TotalProblems /= $StudentCount;
474: } else {
475: $ProblemsSolved = 0;
476: $TotalProblems = 0;
477: }
1.27 minaeibi 478:
1.24 minaeibi 479: $Str .= '<table border=2 cellspacing="1">'."\n";
1.23 minaeibi 480: $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
481: $StudentCount.'</b></td></tr>'."\n";
482: $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
483: $TotalProblems.'</b></td></tr>'."\n";
484: $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
485: $ProblemsSolved.'</b></td></tr>'."\n";
486: $Str .= '</table>'."\n";
487:
1.24 minaeibi 488: $Str .= '<table border=2 cellspacing="1">'."\n";
1.23 minaeibi 489: $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
490: '<th>Average Correct</th></tr>'."\n";
491: foreach my $S(@$sequenceKeys) {
492: my $title=$cache->{$S.':title'};
493: #$Str .= $cache->{$S.':problems'};
1.24 minaeibi 494: #my @problems=split(':', $cache->{$S.':problems'});
1.23 minaeibi 495: #my $pCount=scalar @problems;
496: my $pCount=MaxSeqPr($cache,@$students[0],$S);
1.25 matthew 497: my $crr;
498: if ($StudentCount) {
499: $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
500: } else {
501: $crr="0.00";
502: }
1.23 minaeibi 503: $Str .= '<tr><td>'.$title.
504: '</td><td align=center>'.$pCount.
505: '</td><td align=center>'.$crr.
506: '</td></tr>'."\n";
1.10 stredwic 507: }
1.1 stredwic 508:
1.23 minaeibi 509: $Str .= '</table>'."\n";
510:
1.1 stredwic 511: return $Str;
512: }
1.23 minaeibi 513:
1.28 matthew 514: #######################################################
515: #######################################################
1.23 minaeibi 516:
1.2 stredwic 517: =pod
518:
519: =item &CreateLegend()
520:
521: This function returns a formatted string containing the legend for the
522: chart. The legend describes the symbols used to represent grades for
523: problems.
524:
525: =cut
526:
1.28 matthew 527: #######################################################
528: #######################################################
1.2 stredwic 529: sub CreateLegend {
530: my $Str = "<p><pre>".
1.13 minaeibi 531: " 1 correct by student in 1 try\n".
532: " 7 correct by student in 7 tries\n".
1.12 minaeibi 533: " * correct by student in more than 9 tries\n".
1.20 minaeibi 534: " + correct by hand grading or override\n".
1.12 minaeibi 535: " - incorrect by override\n".
536: " . incorrect attempted\n".
537: " # ungraded attempted\n".
1.13 minaeibi 538: " not attempted (blank field)\n".
1.12 minaeibi 539: " x excused".
1.17 minaeibi 540: "</pre><p>";
1.2 stredwic 541: return $Str;
542: }
543:
1.28 matthew 544: #######################################################
545: #######################################################
546:
1.30 ! matthew 547: =pod
1.2 stredwic 548:
549: =back
550:
551: =cut
552:
1.28 matthew 553: #######################################################
554: #######################################################
1.2 stredwic 555:
1.28 matthew 556: 1;
1.2 stredwic 557:
1.1 stredwic 558: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>