Annotation of loncom/interface/statistics/lonstudentassessment.pm, revision 1.2
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: # (Publication Handler
3: #
1.2 ! stredwic 4: # $Id: lonstudentassessment.pm,v 1.1 2002/07/24 14:52:32 stredwic Exp $
1.1 stredwic 5: #
6: # Copyright Michigan State University Board of Trustees
7: #
8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
9: #
10: # LON-CAPA is free software; you can redistribute it and/or modify
11: # it under the terms of the GNU General Public License as published by
12: # the Free Software Foundation; either version 2 of the License, or
13: # (at your option) any later version.
14: #
15: # LON-CAPA is distributed in the hope that it will be useful,
16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18: # GNU General Public License for more details.
19: #
20: # You should have received a copy of the GNU General Public License
21: # along with LON-CAPA; if not, write to the Free Software
22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23: #
24: # /home/httpd/html/adm/gpl.txt
25: #
26: # http://www.lon-capa.org/
27: #
28: # (Navigate problems for statistical reports
29: # YEAR=2001
30: # 5/5,7/9,7/25/1,8/11,9/13,9/26,10/5,10/9,10/22,10/26 Behrouz Minaei
31: # 11/1,11/4,11/16,12/14,12/16,12/18,12/20,12/31 Behrouz Minaei
32: # YEAR=2002
33: # 1/22,2/1,2/6,2/25,3/2,3/6,3/17,3/21,3/22,3/26,4/7,5/6 Behrouz Minaei
34: # 5/12,5/14,5/15,5/19,5/26,7/16 Behrouz Minaei
35: #
36: ###
37:
38: package Apache::lonstudentassessment;
39:
40: use strict;
41: use Apache::lonhtmlcommon;
42: use Apache::loncoursedata;
43: use GDBM_File;
44:
45: sub BuildStudentAssessmentPage {
1.2 ! stredwic 46: my ($cacheDB,$students,$courseID,$formName,$headings,$spacing,
! 47: $studentInformation,$r,$c)=@_;
1.1 stredwic 48:
49: my %cache;
50: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
1.2 ! stredwic 51: $r->print('<html><body>Unable to tie database.</body></html>');
! 52: return;
1.1 stredwic 53: }
1.2 ! stredwic 54: my $selectedName = &FindSelectedStudent(\%cache,
! 55: $cache{'StudentAssessmentStudent'},
! 56: $students);
! 57: $r->print(&CreateInterface(\%cache, $selectedName, $students, $formName));
1.1 stredwic 58:
1.2 ! stredwic 59: my $Ptr = '';
1.1 stredwic 60: if($selectedName eq 'No Student Selected') {
61: $Ptr .= '<h3><font color=blue>WARNING: ';
62: $Ptr .= 'Please select a student</font></h3>';
1.2 ! stredwic 63: $r->print($Ptr);
! 64: return;
1.1 stredwic 65: }
66:
1.2 ! stredwic 67: my ($infoHeadings, $infoKeys, $sequenceHeadings, $sequenceKeys) =
! 68: &ShouldShowColumns(\%cache, $headings, $studentInformation);
! 69:
! 70: $r->print(&CreateTableHeadings(\%cache, $spacing, $infoKeys, $infoHeadings,
! 71: $sequenceKeys, $sequenceHeadings));
! 72: untie(%cache);
! 73:
1.1 stredwic 74: my $selected=0;
1.2 ! stredwic 75: $r->print('<pre>'."\n");
1.1 stredwic 76: foreach (@$students) {
77: next if ($_ ne $selectedName &&
78: $selectedName ne 'All Students');
79: $selected = 1;
1.2 ! stredwic 80: my $courseData;
! 81: if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
! 82: if($cache{$_.':lastDownloadTime'} eq 'Not downloaded') {
! 83: untie(%cache);
! 84: $courseData =
! 85: &Apache::loncoursedata::DownloadCourseInformation($_,
! 86: $courseID);
! 87: if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {
! 88: &Apache::loncoursedata::ProcessStudentData(\%cache,
! 89: $courseData, $_);
! 90: untie(%cache);
! 91: } else {
! 92: last if($c->aborted());
! 93: next;
! 94: }
! 95: } else {
! 96: untie(%cache);
! 97: }
! 98: } else {
! 99: last if($c->aborted());
! 100: next;
! 101: }
! 102:
1.1 stredwic 103: last if ($c->aborted());
1.2 ! stredwic 104:
! 105: if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
! 106: my $displayString = 'DISPLAYDATA'.$spacing;
! 107: $r->print(&Apache::lonhtmlcommon::FormatStudentInformation(
! 108: \%cache, $_,
! 109: $infoKeys,
! 110: $displayString,
! 111: 'preformatted'));
! 112: $r->print(&StudentReport(\%cache, $_, $spacing, $sequenceKeys));
! 113: $r->print("\n");
1.1 stredwic 114: untie(%cache);
115: }
116: }
1.2 ! stredwic 117: $r->print('</pre>'."\n");
1.1 stredwic 118: if($selected == 0) {
119: $Ptr .= '<h3><font color=blue>WARNING: ';
120: $Ptr .= 'Please select a student</font></h3>';
1.2 ! stredwic 121: $r->print($Ptr);
1.1 stredwic 122: }
123:
1.2 ! stredwic 124: return;
! 125: }
! 126:
! 127: #---- Student Assessment Web Page --------------------------------------------
! 128:
! 129: sub CreateInterface {
! 130: my($cache,$selectedName,$students,$formName)=@_;
! 131: my $Ptr = '';
! 132: $Ptr .= &CreateLegend();
! 133: $Ptr .= '<table><tr><td>'."\n";
! 134: $Ptr .= '<input type="submit" name="PreviousStudent" ';
! 135: $Ptr .= 'value="Previous Student" />'."\n";
! 136: $Ptr .= '   '."\n";
! 137: $Ptr .= &Apache::lonhtmlcommon::StudentOptions($cache, $students,
! 138: $selectedName,
! 139: 'StudentAssessment',
! 140: $formName);
! 141: $Ptr .= "\n".'   '."\n";
! 142: $Ptr .= '<input type="submit" name="NextStudent" ';
! 143: $Ptr .= 'value="Next Student" />'."\n";
! 144: $Ptr .= '</td></tr></table>'."\n";
! 145:
1.1 stredwic 146: return $Ptr;
147: }
148:
1.2 ! stredwic 149: sub CreateTableHeadings {
! 150: my($cache,$spacing,$infoKeys,$infoHeadings,$sequenceKeys,
! 151: $sequenceHeadings)=@_;
! 152:
! 153: my $Str = '';
! 154: $Str .= '<br><table border="0" cellpadding="0" cellspacing="0"><tr>'."\n";
! 155:
! 156: my $displayString = '<td align="left"><pre><a href="/adm/statistics?';
! 157: $displayString .= 'sort=LINKDATA">DISPLAYDATA</a>FORMATTING';
! 158: $displayString .= $spacing.'</pre></td>'."\n";
! 159: $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,
! 160: $infoKeys,
! 161: $infoHeadings,
! 162: $displayString,
! 163: 'preformatted');
! 164:
! 165: $displayString = '<td align="left"><pre>DISPLAYDATA'.$spacing;
! 166: $displayString .= '</pre></td>'."\n";
! 167: $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,
! 168: $sequenceKeys,
! 169: $sequenceHeadings,
! 170: $displayString,
! 171: 'preformatted');
! 172:
! 173: $Str .= '<td><pre>Total Solved/Total Problems</pre></td>';
! 174: $Str .= '</tr></table>'."\n";
! 175:
! 176: return $Str;
! 177: }
! 178:
! 179: =pod
! 180:
! 181: =item &FormatStudentData()
! 182:
! 183: First, FormatStudentInformation is called and prefixes the course information.
! 184: This function produces a formatted string of the student's course information.
! 185: Each column of data represents all the problems for a given sequence. For
! 186: valid grade data, a link is created for that problem to a submission record
! 187: for that problem.
! 188:
! 189: =over 4
! 190:
! 191: Input: $name, $studentInformation, $ChartDB
! 192:
! 193: $name: The name and domain of the current student in name:domain format
! 194:
! 195: $studentInformation: A pointer to an array holding the names used to
! 196: remove data from the hash. They represent
! 197: the name of the data to be removed.
! 198:
! 199: $ChartDB: The name of the cached data database which will be tied to that
! 200: database.
! 201:
! 202: Output: $Str
! 203:
! 204: $Str: Formatted string that is an entire row of the chart. It is a
! 205: concatenation of student information and student course information.
! 206:
! 207: =back
! 208:
! 209: =cut
1.1 stredwic 210:
211: sub StudentReport {
1.2 ! stredwic 212: my ($cache,$name,$spacing,$showSequences)=@_;
! 213: my ($username,$domain)=split(':',$name);
1.1 stredwic 214:
215: my $Str = '';
216: if($cache->{$name.':error'} =~ /course/) {
217: $Str .= '<b><font color="blue">No course data for student </font>';
218: $Str .= '<font color="red">'.$username.'.</font></b><br>';
219: return $Str;
220: }
221:
1.2 ! stredwic 222: my $Version;
! 223: my $problemsCorrect = 0;
! 224: my $totalProblems = 0;
! 225: my $problemsSolved = 0;
! 226: my $numberOfParts = 0;
! 227: # foreach my $sequence (split(':', $cache->{'orderedSequences'})) {
! 228: foreach my $sequence (@$showSequences) {
! 229: my $characterCount=0;
1.1 stredwic 230: foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {
231: my $problem = $cache->{$problemID.':problem'};
232: my $LatestVersion = $cache->{$name.':version:'.$problem};
233:
234: # Output dashes for all the parts of this problem if there
235: # is no version information about the current problem.
236: if(!$LatestVersion) {
237: foreach my $part (split(/\:/,$cache->{$sequence.':'.
238: $problemID.
239: ':parts'})) {
1.2 ! stredwic 240: $Str .= ' ';
! 241: $totalProblems++;
! 242: $characterCount++;
1.1 stredwic 243: }
244: next;
245: }
246:
247: my %partData=undef;
248: # Initialize part data, display skips correctly
249: # Skip refers to when a student made no submissions on that
250: # part/problem.
251: foreach my $part (split(/\:/,$cache->{$sequence.':'.
252: $problemID.
253: ':parts'})) {
254: $partData{$part.':tries'}=0;
1.2 ! stredwic 255: $partData{$part.':code'}=' ';
1.1 stredwic 256: }
257:
258: # Looping through all the versions of each part, starting with the
259: # oldest version. Basically, it gets the most recent
260: # set of grade data for each part.
261: for(my $Version=1; $Version<=$LatestVersion; $Version++) {
262: foreach my $part (split(/\:/,$cache->{$sequence.':'.
263: $problemID.
264: ':parts'})) {
265:
266: if(!defined($cache->{$name.":$Version:$problem".
267: ":resource.$part.solved"})) {
268: # No grade for this submission, so skip
269: next;
270: }
271:
272: my $tries=0;
1.2 ! stredwic 273: my $code=' ';
1.1 stredwic 274:
1.2 ! stredwic 275: $tries = $cache->{$name.':'.$Version.':'.$problem.
! 276: ':resource.'.$part.'.tries'};
1.1 stredwic 277: $partData{$part.':tries'}=($tries) ? $tries : 0;
278:
1.2 ! stredwic 279: my $val = $cache->{$name.':'.$Version.':'.$problem.
! 280: ':resource.'.$part.'.solved'};
! 281: if ($val eq 'correct_by_student') {$code = '*';}
! 282: elsif ($val eq 'correct_by_override') {$code = '+';}
! 283: elsif ($val eq 'incorrect_attempted') {$code = '.';}
! 284: elsif ($val eq 'incorrect_by_override'){$code = '-';}
1.1 stredwic 285: elsif ($val eq 'excused') {$code = 'x';}
1.2 ! stredwic 286: elsif ($val eq 'ungraded_attempted') {$code = '#';}
! 287: else {$code = ' ';}
1.1 stredwic 288: $partData{$part.':code'}=$code;
289: }
290: }
291:
1.2 ! stredwic 292: # All grades (except for versionless parts) are displayed as links
! 293: # to their submission record. Loop through all the parts for the
! 294: # current problem in the correct order and prepare the output links
! 295: $Str .= '<a href="/adm/grades?symb=';
! 296: $Str .= &Apache::lonnet::escape($problem);
! 297: $Str .= '&student='.$username.'&domain='.$domain;
! 298: $Str .= '&command=submission">';
! 299: foreach(split(/\:/,$cache->{$sequence.':'.$problemID.
! 300: ':parts'})) {
! 301: if($partData{$_.':code'} eq '*') {
! 302: $problemsCorrect++;
! 303: if (($partData{$_.':tries'}<10) &&
! 304: ($partData{$_.':tries'} ne '')) {
! 305: $partData{$_.':code'}=$partData{$_.':tries'};
! 306: }
! 307: } elsif($partData{$_.':code'} eq '+') {
! 308: $problemsCorrect++;
! 309: }
! 310:
! 311: $Str .= $partData{$_.':code'};
! 312: $characterCount++;
! 313:
! 314: if($partData{$_.':code'} ne 'x') {
! 315: $totalProblems++;
! 316: }
! 317: }
! 318: $Str.='</a>';
! 319: }
! 320:
! 321: # Output the number of correct answers for the current sequence.
! 322: # This part takes up 6 character slots, but is formated right
! 323: # justified.
! 324: my $spacesNeeded=$cache->{$sequence.':columnWidth'}-$characterCount;
! 325: $spacesNeeded -= 3;
! 326: $Str .= (' 'x$spacesNeeded);
! 327:
! 328: my $outputProblemsCorrect = sprintf( "%3d", $problemsCorrect );
! 329: $Str .= '<font color="#007700">'.$outputProblemsCorrect.'</font>';
! 330: $problemsSolved += $problemsCorrect;
! 331: $problemsCorrect=0;
! 332:
! 333: $Str .= $spacing;
1.1 stredwic 334: }
335:
1.2 ! stredwic 336: # Output the total correct problems over the total number of problems.
! 337: # I don't like this type of formatting, but it is a solution. Need
! 338: # a way to dynamically determine the space requirements.
! 339: my $outputProblemsSolved = sprintf( "%4d", $problemsSolved );
! 340: my $outputTotalProblems = sprintf( "%4d", $totalProblems );
! 341: $Str .= '<font color="#000088">'.$outputProblemsSolved.
! 342: ' / '.$outputTotalProblems.'</font>';
1.1 stredwic 343:
344: return $Str;
345: }
346:
1.2 ! stredwic 347: =pod
! 348:
! 349: =item &CreateLegend()
! 350:
! 351: This function returns a formatted string containing the legend for the
! 352: chart. The legend describes the symbols used to represent grades for
! 353: problems.
! 354:
! 355: =cut
! 356:
! 357: sub CreateLegend {
! 358: my $Str = "<p><pre>".
! 359: "1..9: correct by student in 1..9 tries\n".
! 360: " *: correct by student in more than 9 tries\n".
! 361: " +: correct by override\n".
! 362: " -: incorrect by override\n".
! 363: " .: incorrect attempted\n".
! 364: " #: ungraded attempted\n".
! 365: " : not attempted\n".
! 366: " x: excused".
! 367: "</pre><p>";
! 368: return $Str;
! 369: }
! 370:
! 371: =pod
! 372:
! 373: =item &CreateColumnSelectionBox()
! 374:
! 375: If there are columns not being displayed then this selection box is created
! 376: with a list of those columns. When selections are made and the page
! 377: refreshed, the columns will be removed from this box and the column is
! 378: put back in the chart. If there is no columns to select, no row is added
! 379: to the interface table.
! 380:
! 381: =over 4
! 382: Input: $CacheData, $headings
! 383:
! 384:
! 385: $CacheData: A pointer to a hash tied to the cached data
! 386:
! 387: $headings: An array of the names of the columns for the student information.
! 388: They are used for displaying which columns are missing.
! 389:
! 390: Output: $notThere
! 391:
! 392: $notThere: The string contains one row of a table. The first column has the
! 393: name of the selection box. The second contains the selection box
! 394: which has a size of four.
! 395:
! 396: =back
! 397:
! 398: =cut
! 399:
! 400: sub CreateColumnSelectionBox {
! 401: my ($CacheData,$headings)=@_;
! 402:
! 403: my $missing=0;
! 404: my $notThere='<tr><td align="right"><b>Select column to view:</b>';
! 405: my $name;
! 406: $notThere .= '<td align="left">';
! 407: $notThere .= '<select name="ChartReselect" size="4" multiple="true">'."\n";
! 408:
! 409: for(my $index=0; $index<(scalar @$headings); $index++) {
! 410: if(&ShouldShowColumn($CacheData, 'ChartHeading'.$index)) {
! 411: next;
! 412: }
! 413: $name = $headings->[$index];
! 414: $notThere .= '<option value="ChartHeading'.$index.'">';
! 415: $notThere .= $name.'</option>'."\n";
! 416: $missing++;
! 417: }
! 418:
! 419: foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
! 420: if(&ShouldShowColumn($CacheData, 'ChartSequence'.$sequence)) {
! 421: next;
! 422: }
! 423: $name = $CacheData->{$sequence.':title'};
! 424: $notThere .= '<option value="ChartSequence'.$sequence.'">';
! 425: $notThere .= $name.'</option>'."\n";
! 426: $missing++;
! 427: }
! 428:
! 429: if($missing) {
! 430: $notThere .= '</select>';
! 431: } else {
! 432: $notThere='<tr><td>';
! 433: }
! 434:
! 435: return $notThere.'</td></tr>';
! 436: }
! 437:
! 438: =pod
! 439:
! 440: =item &CreateColumnSelectors()
! 441:
! 442: This function generates the checkboxes above the column headings. The
! 443: column will be removed if the checkbox is unchecked.
! 444:
! 445: =over 4
! 446:
! 447: Input: $CacheData, $headings
! 448:
! 449: $CacheData: A pointer to a hash tied to the cached data
! 450:
! 451: $headings: An array of the names of the columns for the student
! 452: information. They are used to know what are the student information columns
! 453:
! 454: Output: $present
! 455:
! 456: $present: The string contains the first row of a table. Each column contains
! 457: a checkbox which is left justified. Currently left justification is used
! 458: for consistency of location over the column in which it presides.
! 459:
! 460: =back
! 461:
! 462: =cut
! 463:
! 464: sub CreateColumnSelectors {
! 465: my ($headings)=@_;
! 466: =pod
! 467: my $found=0;
! 468: my ($name, $length, $position);
! 469:
! 470: my $present = '<tr>';
! 471: for(my $index=0; $index<(scalar @$headings); $index++) {
! 472: $present .= '<td align="left">';
! 473: $present .= '<input type="checkbox" checked="on" ';
! 474: $present .= 'name="ChartHeading'.$index.'" />';
! 475: $present .= '</td>';
! 476: $found++;
! 477: }
! 478:
! 479: foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
! 480: if(!&ShouldShowColumn($CacheData, 'ChartSequence'.$sequence)) {
! 481: next;
! 482: }
! 483: $present .= '<td align="left">';
! 484: $present .= '<input type="checkbox" checked="on" ';
! 485: $present .= 'name="ChartSequence'.$sequence.'" />';
! 486: $present .= '</td>';
! 487: $found++;
! 488: }
! 489:
! 490: if(!$found) {
! 491: $present = '';
! 492: }
! 493:
! 494: return $present.'<td></td></tr></form>'."\n";;
! 495: =cut
! 496: }
! 497:
1.1 stredwic 498: #---- END Student Assessment Web Page ----------------------------------------
1.2 ! stredwic 499:
! 500: #---- Student Assessment Worker Functions ------------------------------------
! 501:
! 502: sub FindSelectedStudent {
! 503: my($cache, $selectedName, $students)=@_;
! 504: for(my $index=0;
! 505: ($selectedName ne 'All Students') && ($index<(scalar @$students));
! 506: $index++) {
! 507: my $fullname = $cache->{$students->[$index].':fullname'};
! 508: if($fullname eq $selectedName) {
! 509: if($cache->{'StudentAssessmentMove'} eq 'next') {
! 510: if($index == ((scalar @$students) - 1)) {
! 511: $selectedName = $students->[0];
! 512: } else {
! 513: $selectedName = $students->[$index+1];
! 514: }
! 515: } elsif($cache->{'StudentAssessmentMove'} eq 'previous') {
! 516: if($index == 0) {
! 517: $selectedName = $students->[-1];
! 518: } else {
! 519: $selectedName = $students->[$index-1];
! 520: }
! 521: } else {
! 522: $selectedName = $students->[$index];
! 523: }
! 524: last;
! 525: }
! 526: }
! 527:
! 528: return $selectedName;
! 529: }
! 530:
! 531: =pod
! 532:
! 533: =item &ShouldShowColumn()
! 534:
! 535: Determine if a specified column should be shown on the chart.
! 536:
! 537: =over 4
! 538:
! 539: Input: $cache, $test
! 540:
! 541: $cache: A pointer to the hash tied to the cached data
! 542:
! 543: $test: The form name of the column (heading.$headingIndex) or
! 544: (sequence.$sequenceIndex)
! 545:
! 546: Output: 0 (false), 1 (true)
! 547:
! 548: =back
! 549:
! 550: =cut
! 551:
! 552: sub ShouldShowColumns {
! 553: my ($cache,$headings,$cacheKey)=@_;
! 554:
! 555: my @infoKeys=();
! 556: my @infoHeadings=();
! 557:
! 558: my @sequenceKeys=();
! 559: my @sequenceHeadings=();
! 560:
! 561: my $index;
! 562: for($index=0; $index < scalar @$headings; $index++) {
! 563: push(@infoHeadings, $headings->[$index]);
! 564: push(@infoKeys, $cacheKey->[$index]);
! 565: }
! 566:
! 567: foreach my $sequence (split(/\:/,$cache->{'orderedSequences'})) {
! 568: push(@sequenceHeadings, $cache->{$sequence.':title'});
! 569: push(@sequenceKeys, $sequence);
! 570: }
! 571:
! 572: # my $headings=$cache->{'form.ChartHeadings'};
! 573: # my $sequences=$cache->{'form.ChartSequences'};
! 574: # if($headings eq 'ALLHEADINGS' || $sequences eq 'ALLSEQUENCES' ||
! 575: # $headings=~/$test/ || $sequences=~/$test/) {
! 576: # return 1;
! 577: # }
! 578:
! 579: return (\@infoHeadings, \@infoKeys, \@sequenceHeadings,
! 580: \@sequenceKeys);
! 581: }
! 582:
! 583: #---- END Student Assessment Worker Functions --------------------------------
! 584:
1.1 stredwic 585: 1;
586: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>