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