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