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