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