File:
[LON-CAPA] /
loncom /
interface /
statistics /
lonstudentassessment.pm
Revision
1.30:
download - view:
text,
annotated -
select for diffs
Fri Feb 28 21:19:00 2003 UTC (21 years, 4 months ago) by
matthew
Branches:
MAIN
CVS tags:
HEAD
I did *NOT* rewrite the subroutines &CreateLegend and &StudentAverageTotal.
&StudentAverageTotal is not called at this time and will be rewritten.
Removed:
&ShouldShowColumns
&FindSelectedStudent
&CreateColumnSelectors
&CreateColumnSelectionBox
&MaxSeqPr
&StudentReport
Added:
&ChartOutputStudent
&StudentPerformanceOnSequence
Rewrote:
&CreateTableHeadings
&CreateInterface
&BuildStudentAssessmentPage
Now use &Apache::loncommon::get_current_state() to get the chart data (see
&ChartOutputStudent()). Note this uses the symb-less call since we are
most likely to process all the students data.
1: # The LearningOnline Network with CAPA
2: #
3: # $Id: lonstudentassessment.pm,v 1.30 2003/02/28 21:19:00 matthew Exp $
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
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: #######################################################
49:
50: package Apache::lonstudentassessment;
51:
52: use strict;
53: use Apache::lonstatistics;
54: use Apache::lonhtmlcommon;
55: use Apache::loncoursedata;
56: use Apache::lonnet; # for logging porpoises
57: use GDBM_File;
58:
59: my $Statistics;
60:
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
84:
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: #######################################################
98: sub BuildStudentAssessmentPage {
99: my ($r,$c)=@_;
100: undef($Statistics);
101:
102: #
103: $r->print(&CreateInterface());
104: $r->rflush();
105: #
106: $r->print(&CreateTableHeadings());
107: if($c->aborted()) { return ; }
108:
109: my $Count = 0;
110: $r->print('<pre>'."\n");
111: foreach my $student (@Apache::lonstatistics::Students) {
112: if($c->aborted()) { return ; }
113: $r->print(&ChartOutputStudent($student));
114: # output it
115:
116: $Count++;
117: if($Count % 5 == 0) {
118: $r->print("</pre>\n<pre>");
119: }
120:
121: $r->rflush();
122: }
123: $r->print('</pre>'."\n");
124: my $Str;
125: return;
126: }
127:
128: #######################################################
129: #######################################################
130:
131: =pod
132:
133: =item &CreateInterface()
134:
135: Called by &BuildStudentAssessmentPage to create the top part of the
136: page which displays the chart.
137:
138: Inputs: None
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: #######################################################
147: sub CreateInterface {
148: my $Str = '';
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: #
157: $Str .= '<tr><td align="center">'."\n";
158: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
159: $Str .= '</td><td align="center">';
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);
170: $Str .= '</td><td>'."\n";
171: $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
172: $only_seq_with_assessments);
173: $Str .= '</td></tr>'."\n";
174: $Str .= '</table>'."\n";
175: return $Str;
176: }
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:
195: #######################################################
196: #######################################################
197:
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: #######################################################
232: sub CreateTableHeadings {
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)
273:
274: Return a line of the chart for a student.
275:
276: =cut
277:
278: #######################################################
279: #######################################################
280: sub ChartOutputStudent {
281: my $student = shift;
282: my $Str = '';
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: }
339:
340: #######################################################
341: #######################################################
342:
343: =pod
344:
345: =back
346:
347: =cut
348:
349: #######################################################
350: #######################################################
351:
352: }
353:
354: #######################################################
355: #######################################################
356:
357: =pod
358:
359: =item &StudentPerformanceOnSequence()
360:
361: Inputs:
362:
363: =over 4
364:
365: =item $student
366:
367: =item $studentdata Hash ref to all student data
368:
369: =item $seq Hash ref, the sequence we are working on
370:
371: =item $links if defined we will output links to each resource.
372:
373: =back
374:
375: =cut
376:
377: #######################################################
378: #######################################################
379: sub StudentPerformanceOnSequence {
380: my ($student,$studentdata,$seq,$links) = @_;
381: my $Str = '';
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 = ' ';
419: }
420: } else {
421: # Unsolved. Did they try?
422: if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
423: $symbol = '.';
424: } else {
425: $symbol = ' ';
426: }
427: }
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;
438: }
439: $Str .= $value;
440: }
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);
449: }
450:
451: #######################################################
452: #######################################################
453: sub StudentAverageTotal {
454: my ($cache, $students, $sequenceKeys)=@_;
455: my $Str = "\n<b>Summary Tables:</b>\n";
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) {
464: $Correct{$sequence} +=
465: $cache->{$name.':'.$sequence.':problemsCorrect'};
466: }
467: $ProblemsSolved += $cache->{$name.':problemsSolved'};
468: $TotalProblems += $cache->{$name.':totalProblems'};
469: }
470: if ($StudentCount) {
471: $ProblemsSolved = sprintf( "%.2f",
472: $ProblemsSolved/$StudentCount);
473: $TotalProblems /= $StudentCount;
474: } else {
475: $ProblemsSolved = 0;
476: $TotalProblems = 0;
477: }
478:
479: $Str .= '<table border=2 cellspacing="1">'."\n";
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:
488: $Str .= '<table border=2 cellspacing="1">'."\n";
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'};
494: #my @problems=split(':', $cache->{$S.':problems'});
495: #my $pCount=scalar @problems;
496: my $pCount=MaxSeqPr($cache,@$students[0],$S);
497: my $crr;
498: if ($StudentCount) {
499: $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
500: } else {
501: $crr="0.00";
502: }
503: $Str .= '<tr><td>'.$title.
504: '</td><td align=center>'.$pCount.
505: '</td><td align=center>'.$crr.
506: '</td></tr>'."\n";
507: }
508:
509: $Str .= '</table>'."\n";
510:
511: return $Str;
512: }
513:
514: #######################################################
515: #######################################################
516:
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:
527: #######################################################
528: #######################################################
529: sub CreateLegend {
530: my $Str = "<p><pre>".
531: " 1 correct by student in 1 try\n".
532: " 7 correct by student in 7 tries\n".
533: " * correct by student in more than 9 tries\n".
534: " + correct by hand grading or override\n".
535: " - incorrect by override\n".
536: " . incorrect attempted\n".
537: " # ungraded attempted\n".
538: " not attempted (blank field)\n".
539: " x excused".
540: "</pre><p>";
541: return $Str;
542: }
543:
544: #######################################################
545: #######################################################
546:
547: =pod
548:
549: =back
550:
551: =cut
552:
553: #######################################################
554: #######################################################
555:
556: 1;
557:
558: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>