Annotation of loncom/interface/lonstatistics.pm, revision 1.97
1.1 albertel 1: # The LearningOnline Network with CAPA
2: #
1.97 ! matthew 3: # $Id: lonstatistics.pm,v 1.96 2004/02/12 22:23:30 matthew Exp $
1.1 albertel 4: #
5: # Copyright Michigan State University Board of Trustees
6: #
7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
8: #
9: # LON-CAPA is free software; you can redistribute it and/or modify
10: # it under the terms of the GNU General Public License as published by
11: # the Free Software Foundation; either version 2 of the License, or
12: # (at your option) any later version.
13: #
14: # LON-CAPA is distributed in the hope that it will be useful,
15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: # GNU General Public License for more details.
18: #
19: # You should have received a copy of the GNU General Public License
20: # along with LON-CAPA; if not, write to the Free Software
21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22: #
23: # /home/httpd/html/adm/gpl.txt
24: #
25: # http://www.lon-capa.org/
26: #
27: # (Navigate problems for statistical reports
1.14 minaeibi 28: #
1.1 albertel 29: ###
30:
1.59 matthew 31: =pod
32:
33: =head1 NAME
34:
35: lonstatistics
36:
37: =head1 SYNOPSIS
38:
39: Main handler for statistics and chart.
40:
41: =over 4
42:
43: =cut
44:
1.55 minaeibi 45: package Apache::lonstatistics;
1.1 albertel 46:
1.30 stredwic 47: use strict;
1.1 albertel 48: use Apache::Constants qw(:common :http);
1.61 matthew 49: use vars qw(
50: @FullClasslist
51: @Students
52: @Sections
53: @SelectedSections
54: %StudentData
55: @StudentDataOrder
56: @SelectedStudentData
57: $top_map
58: @Sequences
59: @SelectedMaps
1.94 matthew 60: @Assessments
61: $enrollment_status);
1.61 matthew 62:
1.1 albertel 63: use Apache::lonnet();
64: use Apache::lonhomework;
1.12 minaeibi 65: use Apache::loncommon;
1.29 stredwic 66: use Apache::loncoursedata;
67: use Apache::lonhtmlcommon;
1.97 ! matthew 68: use Apache::lonmysql;
! 69: use Apache::lonlocal;
! 70: use Time::HiRes;
! 71: #
! 72: # Statistics Packages
1.61 matthew 73: use Apache::lonproblemanalysis();
1.89 matthew 74: use Apache::lonsubmissiontimeanalysis();
1.94 matthew 75: use Apache::loncorrectproblemplot();
1.61 matthew 76: use Apache::lonproblemstatistics();
77: use Apache::lonstudentassessment();
1.49 stredwic 78: use Apache::lonpercentage;
1.97 ! matthew 79: use Apache::lonstudentsubmissions();
1.60 matthew 80:
81: #######################################################
82: #######################################################
83:
84: =pod
85:
86: =item Package Variables
87:
88: =item @FullClasslist The full classlist
89:
90: =item @Students The students we are concerned with for this invocation
91:
92: =item @Sections The sections available in this class
93:
94: =item $curr_student The student currently being examined
95:
96: =item $prev_student The student previous in the classlist
97:
98: =item $next_student The student next in the classlist
99:
100: =over
101:
102: =cut
103:
104: #######################################################
105: #######################################################
106: #
107: # Classlist variables
108: #
1.59 matthew 109: my $curr_student;
110: my $prev_student;
111: my $next_student;
112:
113: #######################################################
114: #######################################################
115:
116: =pod
117:
118: =item &clear_classlist_variables()
119:
120: undef the following package variables:
121:
122: =over
123:
1.60 matthew 124: =item @FullClasslist
125:
126: =item @Students
1.59 matthew 127:
1.60 matthew 128: =item @Sections
1.59 matthew 129:
1.60 matthew 130: =item @SelectedSections
1.59 matthew 131:
1.61 matthew 132: =item %StudentData
133:
134: =item @StudentDataOrder
135:
136: =item @SelectedStudentData
137:
1.60 matthew 138: =item $curr_student
1.59 matthew 139:
1.60 matthew 140: =item $prev_student
1.59 matthew 141:
1.60 matthew 142: =item $next_student
1.59 matthew 143:
144: =back
145:
146: =cut
147:
148: #######################################################
149: #######################################################
150: sub clear_classlist_variables {
151: undef(@FullClasslist);
152: undef(@Students);
153: undef(@Sections);
1.60 matthew 154: undef(@SelectedSections);
1.61 matthew 155: undef(%StudentData);
156: undef(@SelectedStudentData);
1.59 matthew 157: undef($curr_student);
158: undef($prev_student);
159: undef($next_student);
160: }
161:
162: #######################################################
163: #######################################################
164:
165: =pod
166:
167: =item &PrepareClasslist()
168:
169: Build up the classlist information. The classlist information is kept in
170: the following package variables:
171:
172: =over
173:
1.60 matthew 174: =item @FullClasslist
175:
176: =item @Students
1.59 matthew 177:
1.60 matthew 178: =item @Sections
1.59 matthew 179:
1.60 matthew 180: =item @SelectedSections
1.59 matthew 181:
1.61 matthew 182: =item %StudentData
183:
184: =item @SelectedStudentData
185:
1.60 matthew 186: =item $curr_student
1.59 matthew 187:
1.60 matthew 188: =item $prev_student
1.59 matthew 189:
1.60 matthew 190: =item $next_student
1.59 matthew 191:
192: =back
193:
194: $curr_student, $prev_student, and $next_student may not be defined, depending
195: upon the calling context.
196:
197: =cut
198:
199: #######################################################
200: #######################################################
201: sub PrepareClasslist {
202: my %Sections;
203: &clear_classlist_variables();
204: #
205: # Retrieve the classlist
206: my $cid = $ENV{'request.course.id'};
207: my $cdom = $ENV{'course.'.$cid.'.domain'};
208: my $cnum = $ENV{'course.'.$cid.'.num'};
209: my ($classlist,$field_names) = &Apache::loncoursedata::get_classlist($cid,
210: $cdom,$cnum);
1.60 matthew 211: if (exists($ENV{'form.Section'})) {
1.59 matthew 212: if (ref($ENV{'form.Section'})) {
1.61 matthew 213: @SelectedSections = @{$ENV{'form.Section'}};
214: } elsif ($ENV{'form.Section'} !~ /^\s*$/) {
215: @SelectedSections = ($ENV{'form.Section'});
216: }
217: }
218: @SelectedSections = ('all') if (! @SelectedSections);
219: foreach (@SelectedSections) {
220: if ($_ eq 'all') {
221: @SelectedSections = ('all');
1.59 matthew 222: }
223: }
1.61 matthew 224: #
1.69 matthew 225: # Deal with instructors with restricted section access
1.70 matthew 226: if ($ENV{'request.course.sec'} !~ /^\s*$/) {
1.69 matthew 227: @SelectedSections = ($ENV{'request.course.sec'});
228: }
229: #
1.61 matthew 230: # Set up %StudentData
231: @StudentDataOrder = qw/fullname username domain id section status/;
232: foreach my $field (@StudentDataOrder) {
233: $StudentData{$field}->{'title'} = $field;
1.63 matthew 234: $StudentData{$field}->{'base_width'} = length($field);
1.61 matthew 235: $StudentData{$field}->{'width'} =
236: $StudentData{$field}->{'base_width'};
237: }
1.59 matthew 238: #
1.68 matthew 239: # get the status requested
1.94 matthew 240: $enrollment_status = 'Active';
241: $enrollment_status = $ENV{'form.Status'} if (exists($ENV{'form.Status'}));
1.68 matthew 242: #
1.59 matthew 243: # Process the classlist
244: while (my ($student,$student_data) = each (%$classlist)) {
245: my $studenthash = ();
246: for (my $i=0; $i< scalar(@$field_names);$i++) {
1.61 matthew 247: my $field = $field_names->[$i];
248: # Store the data
249: $studenthash->{$field}=$student_data->[$i];
250: # Keep track of the width of the fields
251: next if (! exists($StudentData{$field}));
1.63 matthew 252: my $length = length($student_data->[$i]);
1.61 matthew 253: if ($StudentData{$field}->{'width'} < $length) {
254: $StudentData{$field}->{'width'} = $length;
255: }
1.59 matthew 256: }
257: push (@FullClasslist,$studenthash);
258: #
259: # Build up a list of sections
260: my $section = $studenthash->{'section'};
1.60 matthew 261: if (! defined($section) || $section =~/^\s*$/ || $section == -1) {
262: $studenthash->{'section'} = 'none';
263: $section = $studenthash->{'section'};
264: }
1.59 matthew 265: $Sections{$section}++;
266: #
267: # Only put in the list those students we are interested in
1.60 matthew 268: foreach my $sect (@SelectedSections) {
1.68 matthew 269: if ( (($sect eq 'all') ||
270: ($section eq $sect)) &&
1.94 matthew 271: (($studenthash->{'status'} eq $enrollment_status) ||
272: ($enrollment_status eq 'Any'))
1.68 matthew 273: ){
1.60 matthew 274: push (@Students,$studenthash);
275: last;
276: }
1.59 matthew 277: }
278: }
279: #
280: # Put the consolidated section data in the right place
1.70 matthew 281: if ($ENV{'request.course.sec'} !~ /^\s*$/) {
1.69 matthew 282: @Sections = ($ENV{'request.course.sec'});
283: } else {
284: @Sections = sort {$a cmp $b} keys(%Sections);
285: unshift(@Sections,'all'); # Put 'all' at the front of the list
286: }
1.59 matthew 287: #
288: # Sort the Students
289: my $sortby = 'fullname';
1.60 matthew 290: $sortby = $ENV{'form.sort'} if (exists($ENV{'form.sort'}));
291: my @TmpStudents = sort { $a->{$sortby} cmp $b->{$sortby} ||
292: $a->{'fullname'} cmp $b->{'fullname'} } @Students;
293: @Students = @TmpStudents;
1.59 matthew 294: #
295: # Now deal with that current student thing....
1.72 matthew 296: $curr_student = undef;
297: if (exists($ENV{'form.SelectedStudent'})) {
1.59 matthew 298: my ($current_uname,$current_dom) =
1.72 matthew 299: split(':',$ENV{'form.SelectedStudent'});
1.59 matthew 300: my $i;
301: for ($i = 0; $i<=$#Students; $i++) {
302: next if (($Students[$i]->{'username'} ne $current_uname) ||
303: ($Students[$i]->{'domain'} ne $current_dom));
1.60 matthew 304: $curr_student = $Students[$i];
1.59 matthew 305: last; # If we get here, we have our student.
306: }
1.72 matthew 307: if (defined($curr_student)) {
308: if ($i == 0) {
309: $prev_student = undef;
310: } else {
311: $prev_student = $Students[$i-1];
312: }
313: if ($i == $#Students) {
314: $next_student = undef;
315: } else {
316: $next_student = $Students[$i+1];
317: }
1.59 matthew 318: }
319: }
1.61 matthew 320: #
321: if (exists($ENV{'form.StudentData'})) {
322: if (ref($ENV{'form.StudentData'}) eq 'ARRAY') {
323: @SelectedStudentData = @{$ENV{'form.StudentData'}};
324: } else {
325: @SelectedStudentData = ($ENV{'form.StudentData'});
326: }
327: } else {
1.72 matthew 328: @SelectedStudentData = ('username');
1.61 matthew 329: }
330: foreach (@SelectedStudentData) {
331: if ($_ eq 'all') {
332: @SelectedStudentData = ('all');
333: last;
334: }
335: }
336: #
337: return;
338: }
339:
1.71 matthew 340:
341: #######################################################
342: #######################################################
343:
344: =pod
345:
346: =item get_students
347:
348: Returns a list of the selected students
349:
350: =cut
351:
352: #######################################################
353: #######################################################
354: sub get_students {
355: if (! @Students) {
356: &PrepareClasslist()
357: }
358: return @Students;
359: }
360:
1.61 matthew 361: #######################################################
362: #######################################################
363:
364: =pod
365:
366: =item ¤t_student()
367:
368: Returns a pointer to a hash containing data about the currently
369: selected student.
370:
371: =cut
372:
373: #######################################################
374: #######################################################
375: sub current_student {
1.72 matthew 376: return $curr_student;
1.61 matthew 377: }
378:
379: #######################################################
380: #######################################################
381:
382: =pod
383:
384: =item &previous_student()
385:
386: Returns a pointer to a hash containing data about the student prior
387: in the list of students. Or something.
388:
389: =cut
390:
391: #######################################################
392: #######################################################
393: sub previous_student {
1.72 matthew 394: return $prev_student;
1.59 matthew 395: }
396:
397: #######################################################
398: #######################################################
1.61 matthew 399:
400: =pod
401:
402: =item &next_student()
403:
404: Returns a pointer to a hash containing data about the next student
405: to be viewed.
406:
407: =cut
408:
409: #######################################################
410: #######################################################
411: sub next_student {
1.72 matthew 412: return $next_student;
1.61 matthew 413: }
1.60 matthew 414:
415: #######################################################
416: #######################################################
417:
418: =pod
419:
420: =item &clear_sequence_variables()
421:
422: =cut
423:
424: #######################################################
425: #######################################################
426: sub clear_sequence_variables {
427: undef($top_map);
428: undef(@Sequences);
429: undef(@Assessments);
430: }
431:
432: #######################################################
433: #######################################################
434:
435: =pod
436:
1.61 matthew 437: =item &SetSelectedMaps($elementname)
438:
439: Sets the @SelectedMaps array from $ENV{'form.'.$elementname};
440:
441: =cut
442:
443: #######################################################
444: #######################################################
445: sub SetSelectedMaps {
446: my $elementname = shift;
447: if (exists($ENV{'form.'.$elementname})) {
448: if (ref($ENV{'form.'.$elementname})) {
449: @SelectedMaps = @{$ENV{'form.'.$elementname}};
450: } else {
451: @SelectedMaps = ($ENV{'form.'.$elementname});
452: }
453: } else {
454: @SelectedMaps = ('all');
455: }
1.64 matthew 456: }
457:
458:
459: #######################################################
460: #######################################################
461:
462: =pod
463:
464: =item &Sequences_with_Assess()
465:
466: Returns an array containing the subset of @Sequences which contain
467: assessments.
468:
469: =cut
470:
471: #######################################################
472: #######################################################
473: sub Sequences_with_Assess {
474: my @Sequences_to_Show;
475: foreach my $map_symb (@SelectedMaps) {
476: foreach my $sequence (@Sequences) {
477: next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
478: next if ($sequence->{'num_assess'} < 1);
479: push (@Sequences_to_Show,$sequence);
480: }
481: }
482: return @Sequences_to_Show;
1.61 matthew 483: }
484:
485: #######################################################
486: #######################################################
487:
488: =pod
489:
1.60 matthew 490: =item &PrepareCourseData($r)
491:
492: =cut
493:
494: #######################################################
495: #######################################################
496: sub PrepareCourseData {
497: my ($r) = @_;
498: &clear_sequence_variables();
1.61 matthew 499: my ($top,$sequences,$assessments) =
500: &Apache::loncoursedata::get_sequence_assessment_data();
1.60 matthew 501: if (! defined($top) || ! ref($top)) {
502: # There has been an error, better report it
503: &Apache::lonnet::logthis('top is undefined');
504: return;
505: }
506: $top_map = $top if (ref($top));
507: @Sequences = @{$sequences} if (ref($sequences) eq 'ARRAY');
1.61 matthew 508: @Assessments = @{$assessments} if (ref($assessments) eq 'ARRAY');
509: return;
510: }
511:
512: #######################################################
513: #######################################################
1.60 matthew 514:
515: =pod
516:
1.61 matthew 517: =item &log_sequence($sequence,$recursive,$padding)
518:
519: Write data about the sequence to a logfile. If $recursive is not
520: undef the data is written recursively. $padding is used for recursive
521: calls.
522:
523: =cut
524:
525: #######################################################
526: #######################################################
527: sub log_sequence {
528: my ($seq,$recursive,$padding) = @_;
529: $padding = '' if (! defined($padding));
530: if (ref($seq) ne 'HASH') {
531: &Apache::lonnet::logthis('log_sequence passed bad sequnce');
532: return;
533: }
534: &Apache::lonnet::logthis($padding.'sequence '.$seq->{'title'});
535: while (my($key,$value) = each(%$seq)) {
536: next if ($key eq 'contents');
537: if (ref($value) eq 'ARRAY') {
538: for (my $i=0;$i< scalar(@$value);$i++) {
539: &Apache::lonnet::logthis($padding.$key.'['.$i.']='.
540: $value->[$i]);
541: }
542: } else {
543: &Apache::lonnet::logthis($padding.$key.'='.$value);
544: }
545: }
546: if (defined($recursive)) {
547: &Apache::lonnet::logthis($padding.'-'x20);
548: &Apache::lonnet::logthis($padding.'contains:');
549: foreach my $item (@{$seq->{'contents'}}) {
550: if ($item->{'type'} eq 'container') {
551: &log_sequence($item,$recursive,$padding.' ');
552: } else {
553: &Apache::lonnet::logthis($padding.'title = '.$item->{'title'});
554: while (my($key,$value) = each(%$item)) {
555: next if ($key eq 'title');
556: if (ref($value) eq 'ARRAY') {
557: for (my $i=0;$i< scalar(@$value);$i++) {
558: &Apache::lonnet::logthis($padding.$key.'['.$i.']='.
559: $value->[$i]);
560: }
561: } else {
562: &Apache::lonnet::logthis($padding.$key.'='.$value);
563: }
564: }
565: }
1.60 matthew 566: }
1.61 matthew 567: &Apache::lonnet::logthis($padding.'end contents of '.$seq->{'title'});
568: &Apache::lonnet::logthis($padding.'-'x20);
1.60 matthew 569: }
1.61 matthew 570: return;
571: }
572:
573: ##############################################
574: ##############################################
575:
576: =pod
577:
578: =item &StudentDataSelect($elementname,$status,$numvisible,$selected)
579:
580: Returns html for a selection box allowing the user to choose one (or more)
581: of the fields of student data available (fullname, username, id, section, etc)
582:
583: =over 4
584:
585: =item $elementname The name of the HTML form element
586:
587: =item $status 'multiple' or 'single' selection box
588:
589: =item $numvisible The number of options to be visible
590:
591: =back
1.60 matthew 592:
593: =cut
594:
1.61 matthew 595: ##############################################
596: ##############################################
597: sub StudentDataSelect {
598: my ($elementname,$status,$numvisible)=@_;
599: if ($numvisible < 1) {
600: return;
601: }
602: #
603: # Build the form element
604: my $Str = "\n";
605: $Str .= '<select name="'.$elementname.'" ';
606: if ($status ne 'single') {
607: $Str .= 'multiple="true" ';
608: }
609: $Str .= 'size="'.$numvisible.'" >'."\n";
610: #
611: # Deal with 'all'
612: $Str .= ' <option value="all" ';
613: foreach (@SelectedStudentData) {
614: if ($_ eq 'all') {
615: $Str .= 'selected ';
616: last;
617: }
618: }
619: $Str .= ">all</option>\n";
620: #
621: # Loop through the student data fields
622: foreach my $item (@StudentDataOrder) {
623: $Str .= ' <option value="'.$item.'" ';
624: foreach (@SelectedStudentData) {
625: if ($item eq $_ ) {
626: $Str .= 'selected ';
627: last;
628: }
629: }
630: $Str .= '>'.$item."</option>\n";
631: }
632: $Str .= "</select>\n";
633: return $Str;
1.60 matthew 634: }
635:
636: ##############################################
637: ##############################################
638:
639: =pod
640:
1.61 matthew 641: =item &MapSelect($elementname,$status,$numvisible,$restriction)
1.60 matthew 642:
643: Returns html for a selection box allowing the user to choose one (or more)
644: of the sequences in the course. The values of the sequences are the symbs.
645: If the top sequence is selected, the value 'top' will result.
646:
647: =over 4
648:
649: =item $elementname The name of the HTML form element
650:
651: =item $status 'multiple' or 'single' selection box
652:
653: =item $numvisible The number of options to be visible
654:
655: =item $restriction Code reference to subroutine which returns true or
656: false. The code must expect a reference to a sequence data structure.
657:
658: =back
659:
660: =cut
661:
662: ##############################################
663: ##############################################
664: sub MapSelect {
1.61 matthew 665: my ($elementname,$status,$numvisible,$restriction)=@_;
1.60 matthew 666: if ($numvisible < 1) {
667: return;
668: }
669: #
670: # Set up array of selected items
1.61 matthew 671: &SetSelectedMaps($elementname);
1.60 matthew 672: #
673: # Set up the restriction call
674: if (! defined($restriction)) {
675: $restriction = sub { 1; };
676: }
677: #
678: # Build the form element
679: my $Str = "\n";
680: $Str .= '<select name="'.$elementname.'" ';
681: if ($status ne 'single') {
682: $Str .= 'multiple="true" ';
683: }
684: $Str .= 'size="'.$numvisible.'" >'."\n";
685: #
1.61 matthew 686: # Deal with 'all'
687: foreach (@SelectedMaps) {
688: if ($_ eq 'all') {
689: @SelectedMaps = ('all');
690: last;
691: }
692: }
693: #
694: # Put in option for 'all'
695: $Str .= ' <option value="all" ';
696: foreach (@SelectedMaps) {
697: if ($_ eq 'all') {
698: $Str .= 'selected ';
699: last;
700: }
701: }
702: $Str .= ">all</option>\n";
703: #
1.60 matthew 704: # Loop through the sequences
1.61 matthew 705: foreach my $seq (@Sequences) {
706: next if (! $restriction->($seq));
707: $Str .= ' <option value="'.$seq->{'symb'}.'" ';
708: foreach (@SelectedMaps) {
709: if ($seq->{'symb'} eq $_) {
1.60 matthew 710: $Str .= 'selected ';
711: last;
712: }
713: }
1.61 matthew 714: $Str .= '>'.$seq->{'title'}."</option>\n";
1.60 matthew 715: }
716: $Str .= "</select>\n";
717: return $Str;
718: }
719:
720: ##############################################
721: ##############################################
722:
723: =pod
724:
725: =item &SectionSelect($elementname,$status,$numvisible)
726:
727: Returns html for a selection box allowing the user to choose one (or more)
728: of the sections in the course.
729:
1.71 matthew 730: Uses the package variables @Sections and @SelectedSections
1.60 matthew 731: =over 4
732:
733: =item $elementname The name of the HTML form element
734:
735: =item $status 'multiple' or 'single' selection box
736:
737: =item $numvisible The number of options to be visible
738:
739: =back
740:
741: =cut
742:
743: ##############################################
744: ##############################################
745: sub SectionSelect {
746: my ($elementname,$status,$numvisible)=@_;
747: if ($numvisible < 1) {
748: return;
749: }
750: #
1.71 matthew 751: # Make sure we have the data we need to continue
752: if (! @Sections) {
753: &PrepareClasslist()
754: }
755: #
1.60 matthew 756: # Build the form element
757: my $Str = "\n";
758: $Str .= '<select name="'.$elementname.'" ';
759: if ($status ne 'single') {
760: $Str .= 'multiple="true" ';
761: }
762: $Str .= 'size="'.$numvisible.'" >'."\n";
763: #
764: # Loop through the sequences
765: foreach my $s (@Sections) {
766: $Str .= ' <option value="'.$s.'" ';
767: foreach (@SelectedSections) {
1.61 matthew 768: if ($s eq $_) {
1.60 matthew 769: $Str .= 'selected ';
770: last;
771: }
772: }
773: $Str .= '>'.$s."</option>\n";
774: }
775: $Str .= "</select>\n";
776: return $Str;
1.80 matthew 777: }
778:
779: #######################################################
780: #######################################################
781:
782: =pod
783:
784: =item &CreateAndParseOutputSelector()
785:
786: Construct a selection list of options for output and parse output selections.
787:
788: =cut
789:
790: #######################################################
791: #######################################################
792: sub OutputDescriptions {
793: my (@OutputOptions) = @_;
794: my $Str = '';
795: $Str .= "<h2>Output Modes</h2>\n";
796: $Str .= "<dl>\n";
797: foreach my $outputmode (@OutputOptions) {
798: $Str .=" <dt>".$outputmode->{'name'}."</dt>\n";
799: $Str .=" <dd>".$outputmode->{'description'}."</dd>\n";
800: }
801: $Str .= "</dl>\n";
802: return $Str;
803: }
804:
805: sub CreateAndParseOutputSelector {
806: my ($elementname,$default,@OutputOptions) = @_;
807: my $output_mode;
808: my $show;
809: my $Str = '';
810: #
811: # Format for output options is 'mode, restrictions';
812: my $selected = $default;
813: if (exists($ENV{'form.'.$elementname})) {
814: if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
815: $selected = $ENV{'form.'.$elementname}->[0];
816: } else {
817: $selected = $ENV{'form.'.$elementname};
818: }
819: }
820: #
821: # Set package variables describing output mode
822: $output_mode = 'html';
823: $show = 'all';
824: foreach my $option (@OutputOptions) {
825: next if ($option->{'value'} ne $selected);
826: $output_mode = $option->{'mode'};
827: $show = $option->{'show'};
828: }
829: #
830: # Build the form element
831: $Str = qq/<select size="5" name="$elementname">/;
832: foreach my $option (@OutputOptions) {
833: if (exists($option->{'special'}) &&
834: $option->{'special'} =~ /do not show/) {
835: next;
836: }
837: $Str .= "\n".' <option value="'.$option->{'value'}.'"';
838: $Str .= " selected " if ($option->{'value'} eq $selected);
1.81 matthew 839: $Str .= ">".&mt($option->{'name'})."<\/option>";
1.80 matthew 840: }
841: $Str .= "\n</select>";
842: return ($Str,$output_mode,$show);
1.79 matthew 843: }
844:
845: ###############################################
846: ###############################################
847:
848: =pod
849:
850: =item &Gather_Student_Data()
851:
852: Ensures all student data is up to date.
853:
854: =cut
855:
856: ###############################################
857: ###############################################
858: sub Gather_Student_Data {
859: my ($r) = @_;
860: my $c = $r->connection();
1.83 matthew 861: #
862: &Apache::loncoursedata::clear_internal_caches();
1.79 matthew 863: #
864: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
865: #
866: my @Students = @Apache::lonstatistics::Students;
867: #
868: # Open the progress window
869: my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
870: ($r,'Statistics Compilation Status',
871: 'Statistics Compilation Progress', scalar(@Students));
872: #
873: while (my $student = shift @Students) {
874: return if ($c->aborted());
875: my ($status,undef) = &Apache::loncoursedata::ensure_current_data
876: ($student->{'username'},$student->{'domain'},
877: $ENV{'request.course.id'});
878: &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
1.86 www 879: &mt('last student'));
1.79 matthew 880: }
881: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
882: $r->rflush();
1.60 matthew 883: }
884:
1.82 matthew 885: ###############################################
886: ###############################################
887:
888: =pod
889:
890: =item &Gather_Full_Student_Data()
891:
892: Ensures all student data is up to date.
893:
894: =cut
895:
896: ###############################################
897: ###############################################
898: sub Gather_Full_Student_Data {
899: my ($r) = @_;
900: my $c = $r->connection();
1.84 matthew 901: #
902: &Apache::loncoursedata::clear_internal_caches();
1.82 matthew 903: #
904: my @Students = @Apache::lonstatistics::Students;
905: #
906: # Open the progress window
907: my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
908: ($r,&mt('Student Data Compilation Status'),
909: &mt('Student Data Compilation Progress'), scalar(@Students));
910: #
911: while (my $student = shift @Students) {
912: return if ($c->aborted());
913: my ($status,undef) = &Apache::loncoursedata::ensure_current_full_data
914: ($student->{'username'},$student->{'domain'},
915: $ENV{'request.course.id'});
916: &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
917: &mt('last student'));
918: }
919: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
920: $r->rflush();
921: }
922:
1.61 matthew 923: ##################################################
924: ##################################################
1.60 matthew 925: sub DisplayClasslist {
926: my ($r)=@_;
927: #
928: my @Fields = ('fullname','username','domain','id','section');
929: #
930: my $Str='';
1.78 matthew 931: if (! @Students) {
932: if ($SelectedSections[0] eq 'all') {
933: if (lc($ENV{'form.Status'}) eq 'any') {
934: $Str .= '<h2>There are no students in the course.</h2>';
935: } elsif (lc($ENV{'form.Status'}) eq 'active') {
936: $Str .= '<h2>There are no currently enrolled students in '.
937: 'the course.</h2>';
938: } elsif (lc($ENV{'form.Status'}) eq 'expired') {
939: $Str .= '<h2>There are no previously enrolled '.
940: 'students in the course.</h2>';
941: }
942: } else {
943: my $sections;
944: if (@SelectedSections == 1) {
945: $sections = 'section '.$SelectedSections[0];
946: } elsif (@SelectedSections > 2) {
947: $sections = 'sections '.join(', ',@SelectedSections);
948: $sections =~ s/, ([^,])*$/, and $1/;
949: } else {
950: $sections = 'sections '.join(' and ',@SelectedSections);
951: }
952: if (lc($ENV{'form.Status'}) eq 'any') {
953: $Str .= '<h2>There are no students in '.$sections.'.</h2>';
954: } elsif (lc($ENV{'form.Status'}) eq 'active') {
955: $Str .= '<h2>There are no currently enrolled students '.
956: 'in '.$sections.'.</h2>';
957: } elsif (lc($ENV{'form.Status'}) eq 'expired') {
958: $Str .= '<h2>There are no previously enrolled students '.
959: 'in '.$sections.'.</h2>';
960: }
961: }
962: $Str.= '<a href="/adm/statistics?reportSelected=student_assessment">'.
963: 'Return to the chart.</a>';
964: $r->print($Str);
965: $r->rflush();
966: return;
967: }
968:
1.76 matthew 969: # "Click" is asinine but it is probably not my place to change the world.
1.78 matthew 970: $Str .= '<h2>Click on a students name or username to view their chart</h2>';
1.60 matthew 971: $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";
972: $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";
973: foreach my $field (@Fields) {
1.65 matthew 974: $Str .= '<th><a href="/adm/statistics?reportSelected=classlist&sort='.$field.'">'.$field.
1.60 matthew 975: '</a></th>';
976: }
977: $Str .= '</tr>'."\n";
978: #
979: my $alternate = 0;
1.65 matthew 980: foreach my $student (@Students) { # @Students is a package variable
1.60 matthew 981: my $sname = $student->{'username'}.':'.$student->{'domain'};
982: if($alternate) {
983: $Str .= '<tr bgcolor="#ffffe6">';
984: } else {
985: $Str .= '<tr bgcolor="#ffffc6">';
986: }
987: $alternate = ($alternate + 1) % 2;
988: #
989: foreach my $field (@Fields) {
990: $Str .= '<td>';
1.78 matthew 991: if ($field eq 'fullname' || $field eq 'username') {
1.60 matthew 992: $Str .= '<a href="/adm/statistics?reportSelected=';
1.65 matthew 993: $Str .= &Apache::lonnet::escape('student_assessment');
1.72 matthew 994: $Str .= '&sort='.&Apache::lonnet::escape($ENV{'form.sort'});
995: $Str .= '&SelectedStudent=';
1.61 matthew 996: $Str .= &Apache::lonnet::escape($sname).'">';
1.60 matthew 997: $Str .= $student->{$field}.' ';
998: $Str .= '</a>';
999: } else {
1000: $Str .= $student->{$field};
1001: }
1002: $Str .= '</td>';
1003: }
1004: $Str .= "</tr>\n";
1005: }
1006: $Str .= '</table></td></tr></table>'."\n";
1007: #
1008: $r->print($Str);
1009: $r->rflush();
1010: #
1011: return;
1012: }
1013:
1.65 matthew 1014: ##############################################
1015: ##############################################
1.33 stredwic 1016: sub CreateMainMenu {
1.65 matthew 1017: #
1.85 matthew 1018: # Define menu data
1019: my @reports = ({ internal_name => 'problem_statistics',
1020: name => &mt('Overall Problem Statistics'),
1021: short_description =>
1022: &mt('Student performance statistics on all problems.'),
1023: },
1024: { internal_name => 'problem_analysis',
1025: name => &mt('Detailed Problem Analysis'),
1026: short_description =>
1027: &mt('Detailed statistics and graphs of student performance on problems.'),
1028: },
1.89 matthew 1029: { internal_name => 'submissiontime_analysis',
1.94 matthew 1030: name => &mt('Submission Time Plots'),
1.89 matthew 1031: short_description =>
1032: &mt('Display and analysis of submission times on assessments.'),
1033: },
1.97 ! matthew 1034: { internal_name => 'student_submission_reports',
! 1035: name => &mt('Student Submission Reports'),
! 1036: short_description =>
! 1037: &mt('Prepare Excel spreadsheets of student submissions.'),
! 1038: },
1.94 matthew 1039: { internal_name => 'correct_problems_plot',
1040: name => &mt('Correct Problems Plot'),
1041: short_description =>
1042: &mt('Display a histogram of student performance in the course.'),
1043: },
1.88 matthew 1044: # { internal_name => 'student_assessment',
1045: # name => &mt('Problem Status Chart'),
1046: # short_description =>
1047: # &mt('Brief view of each students performance in course.'),
1048: # },
1.85 matthew 1049: # 'percentage' => 'Correct-problems Plot',
1050: # 'activitylog' => 'Activity Log',
1051: );
1.65 matthew 1052: #
1.85 matthew 1053: # Create the menu
1054: my $Str;
1.87 matthew 1055: $Str .= '<h1>'.&mt('Please select a report to generate').'</h1>';
1.85 matthew 1056: foreach my $reportdata (@reports) {
1.87 matthew 1057: $Str .=' <h3><a href="/adm/statistics?reportSelected='.
1.85 matthew 1058: $reportdata->{'internal_name'}.'" >'.
1.87 matthew 1059: $reportdata->{'name'}."</a></h3>\n";
1060: $Str .= ' '.(' 'x8).$reportdata->{'short_description'}.
1061: "\n";
1.85 matthew 1062: }
1063: $Str .="</dl>\n";
1.65 matthew 1064: #
1.33 stredwic 1065: return $Str;
1066: }
1067:
1.65 matthew 1068: ##############################################
1069: ##############################################
1.1 albertel 1070: sub handler {
1.31 minaeibi 1071: my $r=shift;
1.65 matthew 1072: my $c = $r->connection();
1073: #
1074: # Check for overloading
1.51 www 1075: my $loaderror=&Apache::lonnet::overloaderror($r);
1076: if ($loaderror) { return $loaderror; }
1077: $loaderror=
1078: &Apache::lonnet::overloaderror($r,
1079: $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
1080: if ($loaderror) { return $loaderror; }
1.65 matthew 1081: #
1082: # Check for access
1.69 matthew 1083: if (! &Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
1.27 stredwic 1084: $ENV{'user.error.msg'}=
1.69 matthew 1085: $r->uri.":vgr:0:0:Cannot view grades for complete course";
1086: if (! &Apache::lonnet::allowed('vgr',
1087: $ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) {
1088: $ENV{'user.error.msg'}=
1089: $r->uri.":vgr:0:0:Cannot view grades with given role";
1090: return HTTP_NOT_ACCEPTABLE;
1091: }
1.27 stredwic 1092: }
1.65 matthew 1093: #
1094: # Send the header
1.92 www 1095: &Apache::loncommon::no_cache($r);
1096: &Apache::loncommon::content_type($r,'text/html');
1.27 stredwic 1097: $r->send_http_header;
1.92 www 1098: if ($r->header_only) { return OK; }
1.65 matthew 1099: #
1100: # Extract form elements from query string
1.60 matthew 1101: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
1.65 matthew 1102: ['sort','reportSelected',
1.72 matthew 1103: 'SelectedStudent']);
1.65 matthew 1104: #
1105: # Give the LON-CAPA page header
1.96 matthew 1106: $r->print('<html><head><title>'.
1107: &mt('Course Statistics and Charts').
1108: "</title></head>\n".
1109: &Apache::loncommon::bodytag('Course Statistics and Charts')."\n".
1.93 www 1110: &Apache::loncommon::help_open_faq(139).
1111: &Apache::loncommon::help_open_bug('Statistics and Charts'));
1.65 matthew 1112: $r->rflush();
1.85 matthew 1113: #
1114: # Either print out a menu for them or send them to a report
1115: if (! exists($ENV{'form.reportSelected'}) ||
1116: $ENV{'form.reportSelected'} eq '') {
1117: $r->print(&CreateMainMenu());
1118: } else {
1.65 matthew 1119: #
1.85 matthew 1120: if (! &Apache::lonmysql::verify_sql_connection()) {
1121: my $serveradmin = $r->dir_config('lonAdmEMail');
1122: $r->print('<h2><font color="Red">'.
1123: &mt('Unable to connect to database!').
1124: '</font></h2>');
1125: $r->print('<p>'.
1126: &mt('Please notify the server administrator ').
1127: '<b>'.$serveradmin.'</b></p>');
1128: $r->print('<p>'.
1129: &mt('Course Statistics and Charts cannot be '.
1130: 'retrieved until the database is restarted. '.
1131: 'Your data is intact but cannot be displayed '.
1132: 'at this time.').'</p>');
1133: $r->print('</body></html>');
1134: return;
1135: }
1136: #
1137: # Clean out the caches
1138: if (exists($ENV{'form.ClearCache'})) {
1139: &Apache::loncoursedata::delete_caches($ENV{'requres.course.id'});
1140: }
1141: #
1142: # Begin form output
1143: $r->print('<form name="Statistics" ');
1144: $r->print('method="post" action="/adm/statistics">');
1145: $r->rflush();
1146: #
1147: my $GoToPage = $ENV{'form.reportSelected'};
1.90 matthew 1148: #
1149: # Set up the statistics and chart environment
1150: &PrepareCourseData($r);
1151: #
1.85 matthew 1152: $r->print('<input type="hidden" name="reportSelected" value="'.
1153: $GoToPage.'">');
1154: if($GoToPage eq 'activitylog') {
1.65 matthew 1155: # &Apache::lonproblemstatistics::Activity();
1.85 matthew 1156: } elsif($GoToPage eq 'problem_statistics') {
1157: &Apache::lonproblemstatistics::BuildProblemStatisticsPage($r,$c);
1158: } elsif($GoToPage eq 'problem_analysis') {
1159: &Apache::lonproblemanalysis::BuildProblemAnalysisPage($r,$c);
1.89 matthew 1160: } elsif($GoToPage eq 'submissiontime_analysis') {
1161: &Apache::lonsubmissiontimeanalysis::BuildSubmissionTimePage($r,$c);
1.97 ! matthew 1162: } elsif($GoToPage eq 'student_submission_reports') {
! 1163: &Apache::lonstudentsubmissions::BuildStudentSubmissionsPage($r,$c);
1.94 matthew 1164: } elsif($GoToPage eq 'correct_problems_plot') {
1165: &Apache::loncorrectproblemplot::BuildCorrectProblemsPage($r,$c);
1.85 matthew 1166: } elsif($GoToPage eq 'student_assessment') {
1167: &Apache::lonstudentassessment::BuildStudentAssessmentPage($r,$c);
1168: } elsif($GoToPage eq 'DoDiffGraph' || $GoToPage eq 'PercentWrongGraph') {
1.65 matthew 1169: # &Apache::lonproblemstatistics::BuildGraphicChart($r,$c);
1.85 matthew 1170: } elsif($GoToPage eq 'Correct-problems Plot') {
1171: # &Apache::lonpercentage::BuildPercentageGraph($r,$c);
1172: }
1173: #
1174: $r->print("</form>\n");
1.65 matthew 1175: }
1176: $r->print("</body>\n</html>\n");
1177: $r->rflush();
1178: #
1.27 stredwic 1179: return OK;
1.1 albertel 1180: }
1.65 matthew 1181:
1.1 albertel 1182: 1;
1.59 matthew 1183:
1.65 matthew 1184: #######################################################
1185: #######################################################
1186:
1.59 matthew 1187: =pod
1188:
1189: =back
1190:
1191: =cut
1.65 matthew 1192:
1193: #######################################################
1194: #######################################################
1.59 matthew 1195:
1.1 albertel 1196: __END__
1.31 minaeibi 1197:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>