Annotation of loncom/interface/lonstatistics.pm, revision 1.102
1.1 albertel 1: # The LearningOnline Network with CAPA
2: #
1.102 ! matthew 3: # $Id: lonstatistics.pm,v 1.101 2004/04/01 18:49:39 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 {
1.102 ! matthew 474: my ($mode) = @_;
! 475: $mode = 'selected' if (! defined($mode));
1.64 matthew 476: my @Sequences_to_Show;
1.102 ! matthew 477: foreach my $sequence (@Sequences) {
! 478: next if ($sequence->{'num_assess'} < 1);
! 479: if ($mode eq 'all') {
1.64 matthew 480: push (@Sequences_to_Show,$sequence);
1.102 ! matthew 481: } elsif ($mode eq 'selected') {
! 482: foreach my $map_symb (@SelectedMaps) {
! 483: if ($sequence->{'symb'} eq $map_symb || $map_symb eq 'all'){
! 484: push (@Sequences_to_Show,$sequence);
! 485: last; # Only put it in once
! 486: }
! 487: }
1.64 matthew 488: }
1.102 ! matthew 489:
1.64 matthew 490: }
491: return @Sequences_to_Show;
1.61 matthew 492: }
493:
494: #######################################################
495: #######################################################
496:
497: =pod
498:
1.60 matthew 499: =item &PrepareCourseData($r)
500:
501: =cut
502:
503: #######################################################
504: #######################################################
505: sub PrepareCourseData {
506: my ($r) = @_;
507: &clear_sequence_variables();
1.61 matthew 508: my ($top,$sequences,$assessments) =
509: &Apache::loncoursedata::get_sequence_assessment_data();
1.60 matthew 510: if (! defined($top) || ! ref($top)) {
511: # There has been an error, better report it
512: &Apache::lonnet::logthis('top is undefined');
513: return;
514: }
515: $top_map = $top if (ref($top));
516: @Sequences = @{$sequences} if (ref($sequences) eq 'ARRAY');
1.61 matthew 517: @Assessments = @{$assessments} if (ref($assessments) eq 'ARRAY');
518: return;
519: }
520:
521: #######################################################
522: #######################################################
1.60 matthew 523:
524: =pod
525:
1.61 matthew 526: =item &log_sequence($sequence,$recursive,$padding)
527:
528: Write data about the sequence to a logfile. If $recursive is not
529: undef the data is written recursively. $padding is used for recursive
530: calls.
531:
532: =cut
533:
534: #######################################################
535: #######################################################
536: sub log_sequence {
537: my ($seq,$recursive,$padding) = @_;
538: $padding = '' if (! defined($padding));
539: if (ref($seq) ne 'HASH') {
540: &Apache::lonnet::logthis('log_sequence passed bad sequnce');
541: return;
542: }
543: &Apache::lonnet::logthis($padding.'sequence '.$seq->{'title'});
544: while (my($key,$value) = each(%$seq)) {
545: next if ($key eq 'contents');
546: if (ref($value) eq 'ARRAY') {
547: for (my $i=0;$i< scalar(@$value);$i++) {
548: &Apache::lonnet::logthis($padding.$key.'['.$i.']='.
549: $value->[$i]);
550: }
551: } else {
552: &Apache::lonnet::logthis($padding.$key.'='.$value);
553: }
554: }
555: if (defined($recursive)) {
556: &Apache::lonnet::logthis($padding.'-'x20);
557: &Apache::lonnet::logthis($padding.'contains:');
558: foreach my $item (@{$seq->{'contents'}}) {
559: if ($item->{'type'} eq 'container') {
560: &log_sequence($item,$recursive,$padding.' ');
561: } else {
562: &Apache::lonnet::logthis($padding.'title = '.$item->{'title'});
563: while (my($key,$value) = each(%$item)) {
564: next if ($key eq 'title');
565: if (ref($value) eq 'ARRAY') {
566: for (my $i=0;$i< scalar(@$value);$i++) {
567: &Apache::lonnet::logthis($padding.$key.'['.$i.']='.
568: $value->[$i]);
569: }
570: } else {
571: &Apache::lonnet::logthis($padding.$key.'='.$value);
572: }
573: }
574: }
1.60 matthew 575: }
1.61 matthew 576: &Apache::lonnet::logthis($padding.'end contents of '.$seq->{'title'});
577: &Apache::lonnet::logthis($padding.'-'x20);
1.60 matthew 578: }
1.61 matthew 579: return;
580: }
581:
582: ##############################################
583: ##############################################
584:
585: =pod
586:
587: =item &StudentDataSelect($elementname,$status,$numvisible,$selected)
588:
589: Returns html for a selection box allowing the user to choose one (or more)
590: of the fields of student data available (fullname, username, id, section, etc)
591:
592: =over 4
593:
594: =item $elementname The name of the HTML form element
595:
596: =item $status 'multiple' or 'single' selection box
597:
598: =item $numvisible The number of options to be visible
599:
600: =back
1.60 matthew 601:
602: =cut
603:
1.61 matthew 604: ##############################################
605: ##############################################
606: sub StudentDataSelect {
607: my ($elementname,$status,$numvisible)=@_;
608: if ($numvisible < 1) {
609: return;
610: }
611: #
612: # Build the form element
613: my $Str = "\n";
614: $Str .= '<select name="'.$elementname.'" ';
615: if ($status ne 'single') {
616: $Str .= 'multiple="true" ';
617: }
618: $Str .= 'size="'.$numvisible.'" >'."\n";
619: #
620: # Deal with 'all'
621: $Str .= ' <option value="all" ';
622: foreach (@SelectedStudentData) {
623: if ($_ eq 'all') {
624: $Str .= 'selected ';
625: last;
626: }
627: }
628: $Str .= ">all</option>\n";
629: #
630: # Loop through the student data fields
631: foreach my $item (@StudentDataOrder) {
632: $Str .= ' <option value="'.$item.'" ';
633: foreach (@SelectedStudentData) {
634: if ($item eq $_ ) {
635: $Str .= 'selected ';
636: last;
637: }
638: }
639: $Str .= '>'.$item."</option>\n";
640: }
641: $Str .= "</select>\n";
642: return $Str;
1.60 matthew 643: }
644:
645: ##############################################
646: ##############################################
647:
648: =pod
649:
1.61 matthew 650: =item &MapSelect($elementname,$status,$numvisible,$restriction)
1.60 matthew 651:
652: Returns html for a selection box allowing the user to choose one (or more)
653: of the sequences in the course. The values of the sequences are the symbs.
654: If the top sequence is selected, the value 'top' will result.
655:
656: =over 4
657:
658: =item $elementname The name of the HTML form element
659:
660: =item $status 'multiple' or 'single' selection box
661:
662: =item $numvisible The number of options to be visible
663:
664: =item $restriction Code reference to subroutine which returns true or
665: false. The code must expect a reference to a sequence data structure.
666:
667: =back
668:
669: =cut
670:
671: ##############################################
672: ##############################################
673: sub MapSelect {
1.61 matthew 674: my ($elementname,$status,$numvisible,$restriction)=@_;
1.60 matthew 675: if ($numvisible < 1) {
676: return;
677: }
678: #
679: # Set up array of selected items
1.61 matthew 680: &SetSelectedMaps($elementname);
1.60 matthew 681: #
682: # Set up the restriction call
683: if (! defined($restriction)) {
684: $restriction = sub { 1; };
685: }
686: #
687: # Build the form element
688: my $Str = "\n";
689: $Str .= '<select name="'.$elementname.'" ';
690: if ($status ne 'single') {
691: $Str .= 'multiple="true" ';
692: }
693: $Str .= 'size="'.$numvisible.'" >'."\n";
694: #
1.61 matthew 695: # Deal with 'all'
696: foreach (@SelectedMaps) {
697: if ($_ eq 'all') {
698: @SelectedMaps = ('all');
699: last;
700: }
701: }
702: #
703: # Put in option for 'all'
704: $Str .= ' <option value="all" ';
705: foreach (@SelectedMaps) {
706: if ($_ eq 'all') {
707: $Str .= 'selected ';
708: last;
709: }
710: }
711: $Str .= ">all</option>\n";
712: #
1.60 matthew 713: # Loop through the sequences
1.61 matthew 714: foreach my $seq (@Sequences) {
715: next if (! $restriction->($seq));
716: $Str .= ' <option value="'.$seq->{'symb'}.'" ';
717: foreach (@SelectedMaps) {
718: if ($seq->{'symb'} eq $_) {
1.60 matthew 719: $Str .= 'selected ';
720: last;
721: }
722: }
1.61 matthew 723: $Str .= '>'.$seq->{'title'}."</option>\n";
1.60 matthew 724: }
725: $Str .= "</select>\n";
726: return $Str;
727: }
728:
729: ##############################################
730: ##############################################
731:
732: =pod
733:
734: =item &SectionSelect($elementname,$status,$numvisible)
735:
736: Returns html for a selection box allowing the user to choose one (or more)
737: of the sections in the course.
738:
1.71 matthew 739: Uses the package variables @Sections and @SelectedSections
1.60 matthew 740: =over 4
741:
742: =item $elementname The name of the HTML form element
743:
744: =item $status 'multiple' or 'single' selection box
745:
746: =item $numvisible The number of options to be visible
747:
748: =back
749:
750: =cut
751:
752: ##############################################
753: ##############################################
754: sub SectionSelect {
755: my ($elementname,$status,$numvisible)=@_;
756: if ($numvisible < 1) {
757: return;
758: }
759: #
1.71 matthew 760: # Make sure we have the data we need to continue
761: if (! @Sections) {
762: &PrepareClasslist()
763: }
764: #
1.60 matthew 765: # Build the form element
766: my $Str = "\n";
767: $Str .= '<select name="'.$elementname.'" ';
768: if ($status ne 'single') {
769: $Str .= 'multiple="true" ';
770: }
771: $Str .= 'size="'.$numvisible.'" >'."\n";
772: #
773: # Loop through the sequences
774: foreach my $s (@Sections) {
775: $Str .= ' <option value="'.$s.'" ';
776: foreach (@SelectedSections) {
1.61 matthew 777: if ($s eq $_) {
1.60 matthew 778: $Str .= 'selected ';
779: last;
780: }
781: }
782: $Str .= '>'.$s."</option>\n";
783: }
784: $Str .= "</select>\n";
785: return $Str;
1.80 matthew 786: }
787:
788: #######################################################
789: #######################################################
790:
791: =pod
792:
793: =item &CreateAndParseOutputSelector()
794:
795: Construct a selection list of options for output and parse output selections.
796:
797: =cut
798:
799: #######################################################
800: #######################################################
801: sub OutputDescriptions {
802: my (@OutputOptions) = @_;
803: my $Str = '';
804: $Str .= "<h2>Output Modes</h2>\n";
805: $Str .= "<dl>\n";
806: foreach my $outputmode (@OutputOptions) {
807: $Str .=" <dt>".$outputmode->{'name'}."</dt>\n";
808: $Str .=" <dd>".$outputmode->{'description'}."</dd>\n";
809: }
810: $Str .= "</dl>\n";
811: return $Str;
812: }
813:
814: sub CreateAndParseOutputSelector {
815: my ($elementname,$default,@OutputOptions) = @_;
816: my $output_mode;
817: my $show;
818: my $Str = '';
819: #
820: # Format for output options is 'mode, restrictions';
821: my $selected = $default;
822: if (exists($ENV{'form.'.$elementname})) {
823: if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
824: $selected = $ENV{'form.'.$elementname}->[0];
825: } else {
826: $selected = $ENV{'form.'.$elementname};
827: }
828: }
829: #
830: # Set package variables describing output mode
831: $output_mode = 'html';
832: $show = 'all';
833: foreach my $option (@OutputOptions) {
834: next if ($option->{'value'} ne $selected);
835: $output_mode = $option->{'mode'};
836: $show = $option->{'show'};
837: }
838: #
839: # Build the form element
840: $Str = qq/<select size="5" name="$elementname">/;
841: foreach my $option (@OutputOptions) {
842: if (exists($option->{'special'}) &&
843: $option->{'special'} =~ /do not show/) {
844: next;
845: }
846: $Str .= "\n".' <option value="'.$option->{'value'}.'"';
847: $Str .= " selected " if ($option->{'value'} eq $selected);
1.81 matthew 848: $Str .= ">".&mt($option->{'name'})."<\/option>";
1.80 matthew 849: }
850: $Str .= "\n</select>";
851: return ($Str,$output_mode,$show);
1.79 matthew 852: }
853:
854: ###############################################
855: ###############################################
856:
857: =pod
858:
859: =item &Gather_Student_Data()
860:
861: Ensures all student data is up to date.
862:
863: =cut
864:
865: ###############################################
866: ###############################################
867: sub Gather_Student_Data {
868: my ($r) = @_;
869: my $c = $r->connection();
1.83 matthew 870: #
871: &Apache::loncoursedata::clear_internal_caches();
1.79 matthew 872: #
873: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
874: #
875: my @Students = @Apache::lonstatistics::Students;
876: #
877: # Open the progress window
878: my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
879: ($r,'Statistics Compilation Status',
880: 'Statistics Compilation Progress', scalar(@Students));
881: #
882: while (my $student = shift @Students) {
883: return if ($c->aborted());
884: my ($status,undef) = &Apache::loncoursedata::ensure_current_data
885: ($student->{'username'},$student->{'domain'},
886: $ENV{'request.course.id'});
887: &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
1.86 www 888: &mt('last student'));
1.79 matthew 889: }
890: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
891: $r->rflush();
1.60 matthew 892: }
893:
1.82 matthew 894: ###############################################
895: ###############################################
896:
897: =pod
898:
899: =item &Gather_Full_Student_Data()
900:
901: Ensures all student data is up to date.
902:
903: =cut
904:
905: ###############################################
906: ###############################################
907: sub Gather_Full_Student_Data {
908: my ($r) = @_;
909: my $c = $r->connection();
1.84 matthew 910: #
911: &Apache::loncoursedata::clear_internal_caches();
1.82 matthew 912: #
913: my @Students = @Apache::lonstatistics::Students;
914: #
915: # Open the progress window
916: my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
917: ($r,&mt('Student Data Compilation Status'),
918: &mt('Student Data Compilation Progress'), scalar(@Students));
919: #
920: while (my $student = shift @Students) {
921: return if ($c->aborted());
922: my ($status,undef) = &Apache::loncoursedata::ensure_current_full_data
923: ($student->{'username'},$student->{'domain'},
924: $ENV{'request.course.id'});
925: &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
926: &mt('last student'));
927: }
928: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
929: $r->rflush();
930: }
931:
1.61 matthew 932: ##################################################
933: ##################################################
1.60 matthew 934: sub DisplayClasslist {
935: my ($r)=@_;
936: #
937: my @Fields = ('fullname','username','domain','id','section');
938: #
939: my $Str='';
1.78 matthew 940: if (! @Students) {
941: if ($SelectedSections[0] eq 'all') {
942: if (lc($ENV{'form.Status'}) eq 'any') {
943: $Str .= '<h2>There are no students in the course.</h2>';
944: } elsif (lc($ENV{'form.Status'}) eq 'active') {
945: $Str .= '<h2>There are no currently enrolled students in '.
946: 'the course.</h2>';
947: } elsif (lc($ENV{'form.Status'}) eq 'expired') {
948: $Str .= '<h2>There are no previously enrolled '.
949: 'students in the course.</h2>';
950: }
951: } else {
952: my $sections;
953: if (@SelectedSections == 1) {
954: $sections = 'section '.$SelectedSections[0];
955: } elsif (@SelectedSections > 2) {
956: $sections = 'sections '.join(', ',@SelectedSections);
957: $sections =~ s/, ([^,])*$/, and $1/;
958: } else {
959: $sections = 'sections '.join(' and ',@SelectedSections);
960: }
961: if (lc($ENV{'form.Status'}) eq 'any') {
962: $Str .= '<h2>There are no students in '.$sections.'.</h2>';
963: } elsif (lc($ENV{'form.Status'}) eq 'active') {
964: $Str .= '<h2>There are no currently enrolled students '.
965: 'in '.$sections.'.</h2>';
966: } elsif (lc($ENV{'form.Status'}) eq 'expired') {
967: $Str .= '<h2>There are no previously enrolled students '.
968: 'in '.$sections.'.</h2>';
969: }
970: }
971: $Str.= '<a href="/adm/statistics?reportSelected=student_assessment">'.
972: 'Return to the chart.</a>';
973: $r->print($Str);
974: $r->rflush();
975: return;
976: }
977:
1.76 matthew 978: # "Click" is asinine but it is probably not my place to change the world.
1.78 matthew 979: $Str .= '<h2>Click on a students name or username to view their chart</h2>';
1.60 matthew 980: $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";
981: $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";
982: foreach my $field (@Fields) {
1.101 matthew 983: $Str .= '<th><a href="/adm/statistics?'.
984: 'reportSelected=student_assessment&'.
985: 'selectstudent=1&'.
986: 'sort='.$field.'">'.$field.
1.60 matthew 987: '</a></th>';
988: }
989: $Str .= '</tr>'."\n";
990: #
991: my $alternate = 0;
1.65 matthew 992: foreach my $student (@Students) { # @Students is a package variable
1.60 matthew 993: my $sname = $student->{'username'}.':'.$student->{'domain'};
994: if($alternate) {
995: $Str .= '<tr bgcolor="#ffffe6">';
996: } else {
997: $Str .= '<tr bgcolor="#ffffc6">';
998: }
999: $alternate = ($alternate + 1) % 2;
1000: #
1001: foreach my $field (@Fields) {
1002: $Str .= '<td>';
1.78 matthew 1003: if ($field eq 'fullname' || $field eq 'username') {
1.60 matthew 1004: $Str .= '<a href="/adm/statistics?reportSelected=';
1.65 matthew 1005: $Str .= &Apache::lonnet::escape('student_assessment');
1.72 matthew 1006: $Str .= '&sort='.&Apache::lonnet::escape($ENV{'form.sort'});
1007: $Str .= '&SelectedStudent=';
1.61 matthew 1008: $Str .= &Apache::lonnet::escape($sname).'">';
1.60 matthew 1009: $Str .= $student->{$field}.' ';
1010: $Str .= '</a>';
1011: } else {
1012: $Str .= $student->{$field};
1013: }
1014: $Str .= '</td>';
1015: }
1016: $Str .= "</tr>\n";
1017: }
1018: $Str .= '</table></td></tr></table>'."\n";
1019: #
1020: $r->print($Str);
1021: $r->rflush();
1022: #
1023: return;
1024: }
1025:
1.65 matthew 1026: ##############################################
1027: ##############################################
1.33 stredwic 1028: sub CreateMainMenu {
1.65 matthew 1029: #
1.85 matthew 1030: # Define menu data
1031: my @reports = ({ internal_name => 'problem_statistics',
1032: name => &mt('Overall Problem Statistics'),
1033: short_description =>
1034: &mt('Student performance statistics on all problems.'),
1035: },
1036: { internal_name => 'problem_analysis',
1037: name => &mt('Detailed Problem Analysis'),
1038: short_description =>
1039: &mt('Detailed statistics and graphs of student performance on problems.'),
1040: },
1.89 matthew 1041: { internal_name => 'submissiontime_analysis',
1.94 matthew 1042: name => &mt('Submission Time Plots'),
1.89 matthew 1043: short_description =>
1044: &mt('Display and analysis of submission times on assessments.'),
1045: },
1.97 matthew 1046: { internal_name => 'student_submission_reports',
1047: name => &mt('Student Submission Reports'),
1048: short_description =>
1049: &mt('Prepare Excel spreadsheets of student submissions.'),
1050: },
1.94 matthew 1051: { internal_name => 'correct_problems_plot',
1052: name => &mt('Correct Problems Plot'),
1053: short_description =>
1054: &mt('Display a histogram of student performance in the course.'),
1055: },
1.88 matthew 1056: # { internal_name => 'student_assessment',
1057: # name => &mt('Problem Status Chart'),
1058: # short_description =>
1059: # &mt('Brief view of each students performance in course.'),
1060: # },
1.85 matthew 1061: # 'percentage' => 'Correct-problems Plot',
1062: # 'activitylog' => 'Activity Log',
1063: );
1.65 matthew 1064: #
1.85 matthew 1065: # Create the menu
1066: my $Str;
1.98 matthew 1067: $Str .= '<h2>'.&mt('Please select a report to generate').'</h2>';
1.85 matthew 1068: foreach my $reportdata (@reports) {
1.87 matthew 1069: $Str .=' <h3><a href="/adm/statistics?reportSelected='.
1.85 matthew 1070: $reportdata->{'internal_name'}.'" >'.
1.87 matthew 1071: $reportdata->{'name'}."</a></h3>\n";
1072: $Str .= ' '.(' 'x8).$reportdata->{'short_description'}.
1073: "\n";
1.85 matthew 1074: }
1075: $Str .="</dl>\n";
1.65 matthew 1076: #
1.33 stredwic 1077: return $Str;
1078: }
1079:
1.65 matthew 1080: ##############################################
1081: ##############################################
1.1 albertel 1082: sub handler {
1.31 minaeibi 1083: my $r=shift;
1.65 matthew 1084: my $c = $r->connection();
1085: #
1086: # Check for overloading
1.51 www 1087: my $loaderror=&Apache::lonnet::overloaderror($r);
1088: if ($loaderror) { return $loaderror; }
1089: $loaderror=
1090: &Apache::lonnet::overloaderror($r,
1091: $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
1092: if ($loaderror) { return $loaderror; }
1.65 matthew 1093: #
1094: # Check for access
1.69 matthew 1095: if (! &Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
1.27 stredwic 1096: $ENV{'user.error.msg'}=
1.69 matthew 1097: $r->uri.":vgr:0:0:Cannot view grades for complete course";
1098: if (! &Apache::lonnet::allowed('vgr',
1099: $ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) {
1100: $ENV{'user.error.msg'}=
1101: $r->uri.":vgr:0:0:Cannot view grades with given role";
1102: return HTTP_NOT_ACCEPTABLE;
1103: }
1.27 stredwic 1104: }
1.65 matthew 1105: #
1106: # Send the header
1.92 www 1107: &Apache::loncommon::no_cache($r);
1108: &Apache::loncommon::content_type($r,'text/html');
1.27 stredwic 1109: $r->send_http_header;
1.92 www 1110: if ($r->header_only) { return OK; }
1.65 matthew 1111: #
1112: # Extract form elements from query string
1.60 matthew 1113: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
1.65 matthew 1114: ['sort','reportSelected',
1.72 matthew 1115: 'SelectedStudent']);
1.65 matthew 1116: #
1117: # Give the LON-CAPA page header
1.96 matthew 1118: $r->print('<html><head><title>'.
1119: &mt('Course Statistics and Charts').
1120: "</title></head>\n".
1.98 matthew 1121: &Apache::loncommon::bodytag('Course Statistics and Charts'));
1.65 matthew 1122: $r->rflush();
1.85 matthew 1123: #
1124: # Either print out a menu for them or send them to a report
1.98 matthew 1125: &Apache::lonhtmlcommon::clear_breadcrumbs();
1126: &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/statistics',
1.100 matthew 1127: title=>'Statistics',
1128: text =>'Statistics',
1.98 matthew 1129: faq=>139,
1130: bug=>'Statistics and Charts'});
1.85 matthew 1131: if (! exists($ENV{'form.reportSelected'}) ||
1132: $ENV{'form.reportSelected'} eq '') {
1.98 matthew 1133: $r->print(&Apache::lonhtmlcommon::breadcrumbs
1.99 matthew 1134: (undef,&mt('Statistics Main Page')).
1.98 matthew 1135: &CreateMainMenu());
1.85 matthew 1136: } else {
1.65 matthew 1137: #
1.85 matthew 1138: if (! &Apache::lonmysql::verify_sql_connection()) {
1139: my $serveradmin = $r->dir_config('lonAdmEMail');
1140: $r->print('<h2><font color="Red">'.
1141: &mt('Unable to connect to database!').
1142: '</font></h2>');
1143: $r->print('<p>'.
1144: &mt('Please notify the server administrator ').
1145: '<b>'.$serveradmin.'</b></p>');
1146: $r->print('<p>'.
1147: &mt('Course Statistics and Charts cannot be '.
1148: 'retrieved until the database is restarted. '.
1149: 'Your data is intact but cannot be displayed '.
1150: 'at this time.').'</p>');
1151: $r->print('</body></html>');
1152: return;
1153: }
1154: #
1155: # Clean out the caches
1156: if (exists($ENV{'form.ClearCache'})) {
1157: &Apache::loncoursedata::delete_caches($ENV{'requres.course.id'});
1158: }
1159: #
1160: # Begin form output
1161: $r->print('<form name="Statistics" ');
1162: $r->print('method="post" action="/adm/statistics">');
1163: $r->rflush();
1164: #
1165: my $GoToPage = $ENV{'form.reportSelected'};
1.90 matthew 1166: #
1167: # Set up the statistics and chart environment
1168: &PrepareCourseData($r);
1169: #
1.85 matthew 1170: $r->print('<input type="hidden" name="reportSelected" value="'.
1171: $GoToPage.'">');
1172: if($GoToPage eq 'activitylog') {
1.65 matthew 1173: # &Apache::lonproblemstatistics::Activity();
1.85 matthew 1174: } elsif($GoToPage eq 'problem_statistics') {
1.98 matthew 1175: &Apache::lonhtmlcommon::add_breadcrumb
1176: ({href=>'/adm/statistics?reportselected=problem_statistics',
1.100 matthew 1177: text=>'Overall Problem Statistics'});
1.85 matthew 1178: &Apache::lonproblemstatistics::BuildProblemStatisticsPage($r,$c);
1179: } elsif($GoToPage eq 'problem_analysis') {
1.98 matthew 1180: &Apache::lonhtmlcommon::add_breadcrumb
1181: ({href=>'/adm/statistics?reportselected=problem_analysis',
1.100 matthew 1182: text=>'Detailed Problem Analysis'});
1.85 matthew 1183: &Apache::lonproblemanalysis::BuildProblemAnalysisPage($r,$c);
1.89 matthew 1184: } elsif($GoToPage eq 'submissiontime_analysis') {
1.98 matthew 1185: &Apache::lonhtmlcommon::add_breadcrumb
1186: ({href=>
1187: '/adm/statistics?reportselected=submissiontime_analysis',
1.100 matthew 1188: text=>'Submission Time Plots'});
1.89 matthew 1189: &Apache::lonsubmissiontimeanalysis::BuildSubmissionTimePage($r,$c);
1.97 matthew 1190: } elsif($GoToPage eq 'student_submission_reports') {
1.98 matthew 1191: &Apache::lonhtmlcommon::add_breadcrumb
1192: ({href=>
1193: '/adm/statistics?reportselected=student_submission_reports',
1.100 matthew 1194: text=>'Student Submission Reports'});
1.97 matthew 1195: &Apache::lonstudentsubmissions::BuildStudentSubmissionsPage($r,$c);
1.94 matthew 1196: } elsif($GoToPage eq 'correct_problems_plot') {
1.98 matthew 1197: &Apache::lonhtmlcommon::add_breadcrumb
1198: ({href=>'/adm/statistics?reportselected=correct_problems_plot',
1.100 matthew 1199: text=>'Correct Problems Plot'});
1.94 matthew 1200: &Apache::loncorrectproblemplot::BuildCorrectProblemsPage($r,$c);
1.85 matthew 1201: } elsif($GoToPage eq 'student_assessment') {
1.98 matthew 1202: &Apache::lonhtmlcommon::clear_breadcrumbs();
1203: &Apache::lonhtmlcommon::add_breadcrumb
1204: ({href=>'/adm/statistics?reportselected=student_assessment',
1.100 matthew 1205: text=>'Chart'});
1.85 matthew 1206: &Apache::lonstudentassessment::BuildStudentAssessmentPage($r,$c);
1207: }
1208: #
1209: $r->print("</form>\n");
1.65 matthew 1210: }
1211: $r->print("</body>\n</html>\n");
1212: $r->rflush();
1213: #
1.27 stredwic 1214: return OK;
1.1 albertel 1215: }
1.65 matthew 1216:
1.1 albertel 1217: 1;
1.59 matthew 1218:
1.65 matthew 1219: #######################################################
1220: #######################################################
1221:
1.59 matthew 1222: =pod
1223:
1224: =back
1225:
1226: =cut
1.65 matthew 1227:
1228: #######################################################
1229: #######################################################
1.59 matthew 1230:
1.1 albertel 1231: __END__
1.31 minaeibi 1232:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>