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