Annotation of loncom/interface/lonstatistics.pm, revision 1.143
1.1 albertel 1: # The LearningOnline Network with CAPA
2: #
1.143 ! jms 3: # $Id: lonstatistics.pm,v 1.142 2008/10/22 10:53:21 bisitz Exp $
1.1 albertel 4: #
5: # Copyright Michigan State University Board of Trustees
6: #
7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
8: #
9: # LON-CAPA is free software; you can redistribute it and/or modify
10: # it under the terms of the GNU General Public License as published by
11: # the Free Software Foundation; either version 2 of the License, or
12: # (at your option) any later version.
13: #
14: # LON-CAPA is distributed in the hope that it will be useful,
15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: # GNU General Public License for more details.
18: #
19: # You should have received a copy of the GNU General Public License
20: # along with LON-CAPA; if not, write to the Free Software
21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22: #
23: # /home/httpd/html/adm/gpl.txt
24: #
25: # http://www.lon-capa.org/
26: #
27: # (Navigate problems for statistical reports
1.14 minaeibi 28: #
1.1 albertel 29: ###
30:
1.59 matthew 31: =pod
32:
33: =head1 NAME
34:
35: lonstatistics
36:
37: =head1 SYNOPSIS
38:
39: Main handler for statistics and chart.
40:
1.143 ! jms 41: This is part of the LearningOnline Network with CAPA project
! 42: described at http://www.lon-capa.org.
! 43:
1.59 matthew 44: =over 4
45:
46: =cut
47:
1.55 minaeibi 48: package Apache::lonstatistics;
1.1 albertel 49:
1.30 stredwic 50: use strict;
1.1 albertel 51: use Apache::Constants qw(:common :http);
1.61 matthew 52: use vars qw(
53: @FullClasslist
54: @Students
1.130 raeburn 55: @Sections
56: @Groups
1.61 matthew 57: %StudentData
58: @StudentDataOrder
59: @SelectedStudentData
1.94 matthew 60: $enrollment_status);
1.61 matthew 61:
1.123 albertel 62: use Apache::lonnet;
1.1 albertel 63: use Apache::lonhomework;
1.12 minaeibi 64: use Apache::loncommon;
1.29 stredwic 65: use Apache::loncoursedata;
66: use Apache::lonhtmlcommon;
1.97 matthew 67: use Apache::lonmysql;
68: use Apache::lonlocal;
1.135 raeburn 69: use Apache::longroup;
1.97 matthew 70: use Time::HiRes;
71: #
72: # Statistics Packages
1.61 matthew 73: use Apache::lonproblemanalysis();
1.89 matthew 74: use Apache::lonsubmissiontimeanalysis();
1.94 matthew 75: use Apache::loncorrectproblemplot();
1.61 matthew 76: use Apache::lonproblemstatistics();
77: use Apache::lonstudentassessment();
1.49 stredwic 78: use Apache::lonpercentage;
1.97 matthew 79: use Apache::lonstudentsubmissions();
1.104 matthew 80: use Apache::lonsurveyreports();
1.128 albertel 81: use Apache::longradinganalysis();
1.136 www 82: use LONCAPA;
1.60 matthew 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:
1.130 raeburn 97: =item @Groups The groups available in the class
98:
1.60 matthew 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: #
1.59 matthew 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:
1.60 matthew 129: =item @FullClasslist
130:
131: =item @Students
1.59 matthew 132:
1.60 matthew 133: =item @Sections
1.59 matthew 134:
1.130 raeburn 135: =item @Groups
136:
1.61 matthew 137: =item %StudentData
138:
139: =item @StudentDataOrder
140:
141: =item @SelectedStudentData
142:
1.60 matthew 143: =item $curr_student
1.59 matthew 144:
1.60 matthew 145: =item $prev_student
1.59 matthew 146:
1.60 matthew 147: =item $next_student
1.59 matthew 148:
149: =back
150:
151: =cut
152:
153: #######################################################
154: #######################################################
155: sub clear_classlist_variables {
156: undef(@FullClasslist);
157: undef(@Students);
158: undef(@Sections);
1.130 raeburn 159: undef(@Groups);
1.61 matthew 160: undef(%StudentData);
161: undef(@SelectedStudentData);
1.59 matthew 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:
1.60 matthew 179: =item @FullClasslist
180:
181: =item @Students
1.59 matthew 182:
1.60 matthew 183: =item @Sections
1.59 matthew 184:
1.130 raeburn 185: =item @Groups
186:
1.61 matthew 187: =item %StudentData
188:
189: =item @SelectedStudentData
190:
1.60 matthew 191: =item $curr_student
1.59 matthew 192:
1.60 matthew 193: =item $prev_student
1.59 matthew 194:
1.60 matthew 195: =item $next_student
1.59 matthew 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
1.123 albertel 211: my $cid = $env{'request.course.id'};
212: my $cdom = $env{'course.'.$cid.'.domain'};
213: my $cnum = $env{'course.'.$cid.'.num'};
1.125 albertel 214: my ($classlist,$field_names) = &Apache::loncoursedata::get_classlist($cdom,
215: $cnum);
1.119 matthew 216: my @selected_sections = &get_selected_sections();
1.130 raeburn 217: my @selected_groups = &get_selected_groups();
1.61 matthew 218: #
1.69 matthew 219: # Deal with instructors with restricted section access
1.123 albertel 220: if ($env{'request.course.sec'} !~ /^\s*$/) {
221: @selected_sections = ($env{'request.course.sec'});
1.69 matthew 222: }
223: #
1.61 matthew 224: # Set up %StudentData
1.130 raeburn 225: @StudentDataOrder = qw/fullname username domain id section status groups comments/;
1.61 matthew 226: foreach my $field (@StudentDataOrder) {
1.108 matthew 227: $StudentData{$field}->{'title'} = &mt($field);
228: $StudentData{$field}->{'base_width'} = length(&mt($field));
1.61 matthew 229: $StudentData{$field}->{'width'} =
230: $StudentData{$field}->{'base_width'};
231: }
1.59 matthew 232: #
1.68 matthew 233: # get the status requested
1.94 matthew 234: $enrollment_status = 'Active';
1.123 albertel 235: $enrollment_status = $env{'form.Status'} if (exists($env{'form.Status'}));
1.68 matthew 236: #
1.130 raeburn 237: # Get groupmembership
1.133 albertel 238: my ($classgroups,$studentgroups);
1.135 raeburn 239: my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
1.133 albertel 240: if (%curr_groups) {
1.130 raeburn 241: ($classgroups,$studentgroups) =
1.133 albertel 242: &Apache::loncoursedata::get_group_memberships($classlist,
1.134 raeburn 243: $field_names,
1.133 albertel 244: $cdom,$cnum);
1.130 raeburn 245: }
246: my $now = time;
247:
1.59 matthew 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++) {
1.61 matthew 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}));
1.63 matthew 257: my $length = length($student_data->[$i]);
1.61 matthew 258: if ($StudentData{$field}->{'width'} < $length) {
259: $StudentData{$field}->{'width'} = $length;
260: }
1.59 matthew 261: }
1.130 raeburn 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: }
1.59 matthew 272: push (@FullClasslist,$studenthash);
273: #
274: # Build up a list of sections
275: my $section = $studenthash->{'section'};
1.60 matthew 276: if (! defined($section) || $section =~/^\s*$/ || $section == -1) {
277: $studenthash->{'section'} = 'none';
278: $section = $studenthash->{'section'};
279: }
1.59 matthew 280: $Sections{$section}++;
281: #
282: # Only put in the list those students we are interested in
1.119 matthew 283: foreach my $sect (@selected_sections) {
1.68 matthew 284: if ( (($sect eq 'all') ||
285: ($section eq $sect)) &&
1.94 matthew 286: (($studenthash->{'status'} eq $enrollment_status) ||
287: ($enrollment_status eq 'Any'))
1.68 matthew 288: ){
1.130 raeburn 289: my $groupcheck = 0;
1.131 albertel 290: if (grep(/^all$/,@selected_groups)) {
291: push(@Students,$studenthash);
1.130 raeburn 292: last;
1.131 albertel 293: } elsif (grep(/^none$/,@selected_groups)) {
1.130 raeburn 294: if ($studenthash->{'groups'} eq 'none') {
1.131 albertel 295: push(@Students,$studenthash);
1.130 raeburn 296: last;
297: }
298: } else {
299: foreach my $group (@selected_groups) {
1.131 albertel 300: if (grep(/^$group$/,@studentsgroups)) {
301: push(@Students,$studenthash);
1.130 raeburn 302: $groupcheck = 1;
303: last;
304: }
305: }
306: if ($groupcheck) {
307: last;
308: }
309: }
1.60 matthew 310: }
1.59 matthew 311: }
312: }
313: #
314: # Put the consolidated section data in the right place
1.123 albertel 315: if ($env{'request.course.sec'} !~ /^\s*$/) {
316: @Sections = ($env{'request.course.sec'});
1.69 matthew 317: } else {
1.138 albertel 318: @Sections = sort {
319: if ($a == $a && $b == $b ) { return $a <=> $b; }
320: return $a cmp $b;
321: } keys(%Sections);
322:
1.69 matthew 323: unshift(@Sections,'all'); # Put 'all' at the front of the list
324: }
1.130 raeburn 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:
1.59 matthew 329: #
330: # Sort the Students
331: my $sortby = 'fullname';
1.123 albertel 332: $sortby = $env{'form.sort'} if (exists($env{'form.sort'}));
1.127 albertel 333: my @TmpStudents = sort { lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
1.126 albertel 334: lc($a->{'fullname'}) cmp lc($b->{'fullname'}) ||
335: lc($a->{'username'}) cmp lc($b->{'username'}) } @Students;
1.60 matthew 336: @Students = @TmpStudents;
1.59 matthew 337: #
338: # Now deal with that current student thing....
1.72 matthew 339: $curr_student = undef;
1.123 albertel 340: if (exists($env{'form.SelectedStudent'})) {
1.59 matthew 341: my ($current_uname,$current_dom) =
1.123 albertel 342: split(':',$env{'form.SelectedStudent'});
1.59 matthew 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));
1.60 matthew 347: $curr_student = $Students[$i];
1.59 matthew 348: last; # If we get here, we have our student.
349: }
1.72 matthew 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: }
1.59 matthew 361: }
362: }
1.61 matthew 363: #
1.123 albertel 364: if (exists($env{'form.StudentData'})) {
1.124 albertel 365: @SelectedStudentData =
366: &Apache::loncommon::get_env_multiple('form.StudentData');
1.61 matthew 367: } else {
1.72 matthew 368: @SelectedStudentData = ('username');
1.61 matthew 369: }
370: foreach (@SelectedStudentData) {
371: if ($_ eq 'all') {
372: @SelectedStudentData = ('all');
373: last;
374: }
375: }
376: #
377: return;
378: }
379:
1.119 matthew 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 {
1.124 albertel 394: my @selected_sections =
395: &Apache::loncommon::get_env_multiple('form.Section');
1.119 matthew 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
1.123 albertel 404: if ($env{'request.course.sec'} !~ /^\s*$/) {
405: @selected_sections = ($env{'request.course.sec'});
1.119 matthew 406: }
407: return @selected_sections;
408: }
409:
410: #######################################################
411: #######################################################
1.130 raeburn 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:
1.119 matthew 436: =pod
437:
438: =item §ion_and_enrollment_description
439:
1.130 raeburn 440: Returns a string describing the currently selected section(s), group(s) and
1.137 raeburn 441: access status.
1.122 matthew 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:
1.119 matthew 447: =cut
448:
449: #######################################################
450: #######################################################
451: sub section_and_enrollment_description {
1.122 matthew 452: my ($mode) = @_;
453: if (! defined($mode)) { $mode = 'localized'; }
1.119 matthew 454: my @sections = &Apache::lonstatistics::get_selected_sections();
1.130 raeburn 455: my @groups = &Apache::lonstatistics::get_selected_groups();
1.122 matthew 456: my $description;
457: if ($mode eq 'localized') {
1.137 raeburn 458: $description = &mt('Unable to determine section, groups and access status');
1.122 matthew 459: } elsif ($mode eq 'plaintext') {
1.137 raeburn 460: $description = 'Unable to determine section, groups and access status';
1.122 matthew 461: } else {
462: $description = 'Bad parameter passed to lonstatistics::section_and_enrollment_description';
463: &Apache::lonnet::logthis($description);
464: }
1.130 raeburn 465: $description = §ion_or_group_text($mode,'section',@sections).
1.131 albertel 466: ' '.§ion_or_group_text($mode,'group',@groups);
1.130 raeburn 467: if ($mode eq 'localized') {
1.142 bisitz 468: $description .= ' '.&mt($env{'form.Status'}.' access status.');
1.130 raeburn 469: } elsif ($mode eq 'plaintext') {
1.137 raeburn 470: $description .= ' '.$env{'form.Status'}.' access status.';
1.130 raeburn 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') {
1.122 matthew 493: if ($mode eq 'localized') {
1.142 bisitz 494: $text = &mt($phrases{$type}{single}.' [_1].',$items[0]);
1.122 matthew 495: } elsif ($mode eq 'plaintext') {
1.130 raeburn 496: $text = $phrases{$type}{single}.' '.$items[0].'.';
497:
1.122 matthew 498: }
1.130 raeburn 499: } elsif (scalar(@items) && $items[0] eq 'all') {
1.122 matthew 500: if ($mode eq 'localized') {
1.142 bisitz 501: $text = &mt($phrases{$type}{all}.'.');
1.122 matthew 502: } elsif ($mode eq 'plaintext') {
1.130 raeburn 503: $text = $phrases{$type}{all}.'.';
1.122 matthew 504: }
1.130 raeburn 505: } elsif (scalar(@items)) {
506: my $lastitem = pop(@items);
1.122 matthew 507: if ($mode eq 'localized') {
1.142 bisitz 508: $text = &mt($phrases{$type}{plural}.' [_1] and [_2].',
1.130 raeburn 509: join(', ',@items),$lastitem);
1.122 matthew 510: } elsif ($mode eq 'plaintext') {
1.130 raeburn 511: $text = $phrases{$type}{plural}.' '.join(', ',@items).' and '.
512: $lastitem.'.';
1.122 matthew 513: }
1.119 matthew 514: }
1.130 raeburn 515: return $text;
1.119 matthew 516: }
1.71 matthew 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:
1.61 matthew 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 {
1.72 matthew 551: return $curr_student;
1.61 matthew 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 {
1.72 matthew 569: return $prev_student;
1.59 matthew 570: }
571:
572: #######################################################
573: #######################################################
1.61 matthew 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 {
1.72 matthew 587: return $next_student;
1.61 matthew 588: }
1.60 matthew 589:
1.61 matthew 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
1.60 matthew 609:
610: =cut
611:
1.61 matthew 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;
1.60 matthew 651: }
652:
1.115 matthew 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) = @_;
1.124 albertel 671: my @selected_maps =
672: &Apache::loncommon::get_env_multiple('form.'.$elementname);
673: @selected_maps = ('all') if (! @selected_maps);
1.118 matthew 674: foreach my $map (@selected_maps) {
675: if ($map eq 'all') {
676: @selected_maps = ('all');
677: last;
678: }
679: }
1.115 matthew 680: return @selected_maps;
681: }
682:
683:
684: #######################################################
685: #######################################################
686:
687: =pod
688:
1.116 matthew 689: =item &selected_sequences_with_assessments
1.115 matthew 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: #######################################################
1.116 matthew 703: sub selected_sequences_with_assessments {
1.115 matthew 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);
1.139 raeburn 713: my $toplevelseq = $navmap->getById('0.0');
714: if (!grep(/^\Q$toplevelseq\E$/,@sequences)) {
715: unshift(@sequences,$toplevelseq);
716: }
717:
1.115 matthew 718: my @sequences_with_assessments;
1.139 raeburn 719: foreach my $sequence (@sequences) {
1.120 matthew 720: if ($navmap->hasResource($sequence,sub { shift->is_problem(); },0,1)){
1.115 matthew 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') {
1.116 matthew 730: foreach my $map_symb (&get_selected_maps('Maps')) {
1.115 matthew 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:
1.60 matthew 742: ##############################################
743: ##############################################
744:
745: =pod
746:
1.115 matthew 747: =item &map_select($elementname,$status,$numvisible,$restriction)
1.60 matthew 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: ##############################################
1.115 matthew 767: sub map_select {
768: my ($elementname,$status,$numvisible)=@_;
1.60 matthew 769: if ($numvisible < 1) {
770: return;
771: }
772: #
773: # Set up array of selected items
1.115 matthew 774: my @selected_maps = &get_selected_maps($elementname);
1.60 matthew 775: #
776: # Build the form element
1.115 matthew 777: my $form = "\n";
778: $form .= '<select name="'.$elementname.'" ';
1.60 matthew 779: if ($status ne 'single') {
1.115 matthew 780: $form .= 'multiple="true" ';
1.60 matthew 781: }
1.115 matthew 782: $form .= 'size="'.$numvisible.'" >'."\n";
1.60 matthew 783: #
1.61 matthew 784: # Put in option for 'all'
1.115 matthew 785: $form .= ' <option value="all" ';
1.118 matthew 786: if ($selected_maps[0] eq 'all') {
787: $form .= 'selected ';
1.61 matthew 788: }
1.115 matthew 789: $form .= ">all</option>\n";
1.61 matthew 790: #
1.60 matthew 791: # Loop through the sequences
1.117 matthew 792: my @sequences = &selected_sequences_with_assessments('all');
1.115 matthew 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 ';
1.60 matthew 804: last;
805: }
806: }
1.115 matthew 807: $form .= '>'.$seq->compTitle."</option>\n";
1.60 matthew 808: }
1.115 matthew 809: $form .= "</select>\n";
810: return $form;
1.60 matthew 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:
1.119 matthew 823: Uses the package variables @Sections
1.60 matthew 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: #
1.71 matthew 844: # Make sure we have the data we need to continue
845: if (! @Sections) {
846: &PrepareClasslist()
847: }
848: #
1.60 matthew 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.'" ';
1.119 matthew 860: foreach (&get_selected_sections()) {
1.61 matthew 861: if ($s eq $_) {
1.60 matthew 862: $Str .= 'selected ';
863: last;
864: }
865: }
866: $Str .= '>'.$s."</option>\n";
867: }
868: $Str .= "</select>\n";
869: return $Str;
1.80 matthew 870: }
871:
1.130 raeburn 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:
1.61 matthew 931: ##################################################
932: ##################################################
1.60 matthew 933: sub DisplayClasslist {
934: my ($r)=@_;
1.107 matthew 935: &Apache::lonhtmlcommon::add_breadcrumb
936: ({text=>'Select One Student'});
1.60 matthew 937: #
1.105 matthew 938: # Output some of the standard interface components
939: my $Str;
1.132 albertel 940: $Str .= &Apache::lonhtmlcommon::breadcrumbs('Select One Student');
1.105 matthew 941: $Str .= '<p><table cellspacing="5">'."\n";
942: $Str .= '<tr>';
943: $Str .= '<th align="center"><b>'.&mt('Sections').'</b></th>';
1.130 raeburn 944: $Str .= '<th align="center"><b>'.&mt('Groups').'</b></th>';
1.137 raeburn 945: $Str .= '<th align="center"><b>'.&mt('Access Status').'</b></th>';
1.105 matthew 946: $Str .= '</tr>'.$/;
947: $Str .= '<tr>';
948: $Str .= '<td>'.
949: &Apache::lonstatistics::SectionSelect('Section','multiple',5).
950: '</td>';
1.130 raeburn 951: $Str .= '<td>'.
952: &Apache::lonstatistics::GroupSelect('Group','multiple',5).
953: '</td>';
1.105 matthew 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: #
1.130 raeburn 965: my @Fields = ('fullname','username','domain','id','section','status','groups');
1.60 matthew 966: #
1.105 matthew 967: $Str = '';
1.119 matthew 968: my @selected_sections = &get_selected_sections();
1.78 matthew 969: if (! @Students) {
1.119 matthew 970: if ($selected_sections[0] eq 'all') {
1.141 bisitz 971: if (lc($env{'form.Status'}) eq 'active') {
972: $Str .= '<p class="LC_info">'.
1.106 matthew 973: &mt('There are no currently enrolled students in the course.').
1.141 bisitz 974: '</p>';
1.123 albertel 975: } elsif (lc($env{'form.Status'}) eq 'expired') {
1.141 bisitz 976: $Str .= '<p class="LC_info">'.
1.106 matthew 977: &mt('There are no previously enrolled students in the course.').
1.141 bisitz 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>';
1.78 matthew 987: }
988: } else {
1.141 bisitz 989: if (lc($env{'form.Status'}) eq 'active') {
990: $Str .= '<p class="LC_info">'.
1.106 matthew 991: &mt('There are no currently enrolled students in the selected sections.').
1.141 bisitz 992: '</p>';
1.123 albertel 993: } elsif (lc($env{'form.Status'}) eq 'expired') {
1.141 bisitz 994: $Str .= '<p class="LC_info">'.
1.106 matthew 995: &mt('There are no previously enrolled students in the selected sections.').
1.141 bisitz 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>';
1.78 matthew 1005: }
1006: }
1.141 bisitz 1007: $Str.= '<p>'
1008: .'<a href="/adm/statistics?reportSelected=student_assessment">'
1009: .&mt('Return to the chart').'</a>'
1010: .'</p>';
1.78 matthew 1011: $r->print($Str);
1012: $r->rflush();
1013: return;
1014: }
1015:
1.141 bisitz 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();
1.60 matthew 1020: foreach my $field (@Fields) {
1.101 matthew 1021: $Str .= '<th><a href="/adm/statistics?'.
1022: 'reportSelected=student_assessment&'.
1023: 'selectstudent=1&'.
1.106 matthew 1024: 'sort='.$field.'">'.&mt($field).
1.60 matthew 1025: '</a></th>';
1026: }
1.141 bisitz 1027: $Str .= &Apache::loncommon::end_data_table_header_row();
1.60 matthew 1028: #
1.65 matthew 1029: foreach my $student (@Students) { # @Students is a package variable
1.60 matthew 1030: my $sname = $student->{'username'}.':'.$student->{'domain'};
1.141 bisitz 1031: $Str .= &Apache::loncommon::start_data_table_row();
1.60 matthew 1032: #
1033: foreach my $field (@Fields) {
1034: $Str .= '<td>';
1.78 matthew 1035: if ($field eq 'fullname' || $field eq 'username') {
1.60 matthew 1036: $Str .= '<a href="/adm/statistics?reportSelected=';
1.136 www 1037: $Str .= &escape('student_assessment');
1038: $Str .= '&sort='.&escape($env{'form.sort'});
1.72 matthew 1039: $Str .= '&SelectedStudent=';
1.136 www 1040: $Str .= &escape($sname).'">';
1.60 matthew 1041: $Str .= $student->{$field}.' ';
1042: $Str .= '</a>';
1.106 matthew 1043: } elsif ($field eq 'status') {
1044: $Str .= &mt($student->{$field});
1.60 matthew 1045: } else {
1046: $Str .= $student->{$field};
1047: }
1048: $Str .= '</td>';
1049: }
1.141 bisitz 1050: $Str .= &Apache::loncommon::end_data_table_row();
1.60 matthew 1051: }
1.141 bisitz 1052: $Str .= &Apache::loncommon::end_data_table();
1.60 matthew 1053: #
1054: $r->print($Str);
1055: $r->rflush();
1056: #
1057: return;
1058: }
1059:
1.65 matthew 1060: ##############################################
1061: ##############################################
1.33 stredwic 1062: sub CreateMainMenu {
1.65 matthew 1063: #
1.85 matthew 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: },
1.89 matthew 1075: { internal_name => 'submissiontime_analysis',
1.94 matthew 1076: name => &mt('Submission Time Plots'),
1.89 matthew 1077: short_description =>
1078: &mt('Display and analysis of submission times on assessments.'),
1079: },
1.97 matthew 1080: { internal_name => 'student_submission_reports',
1081: name => &mt('Student Submission Reports'),
1082: short_description =>
1.111 matthew 1083: &mt('Prepare reports of student submissions.'),
1.97 matthew 1084: },
1.104 matthew 1085: { internal_name => 'survey_reports',
1086: name => &mt('Survey Reports'),
1087: short_description =>
1088: &mt('Prepare reports on survey results.'),
1089: },
1.94 matthew 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: },
1.128 albertel 1095: # { internal_name => 'grading_analysis',
1096: # name => &mt('Detailed Grading Analysis'),
1097: # short_description =>
1098: # &mt('Display statistics about who graded who.'),
1099: # },
1.88 matthew 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: # },
1.85 matthew 1105: # 'percentage' => 'Correct-problems Plot',
1106: # 'activitylog' => 'Activity Log',
1107: );
1.65 matthew 1108: #
1.85 matthew 1109: # Create the menu
1110: my $Str;
1.98 matthew 1111: $Str .= '<h2>'.&mt('Please select a report to generate').'</h2>';
1.85 matthew 1112: foreach my $reportdata (@reports) {
1.87 matthew 1113: $Str .=' <h3><a href="/adm/statistics?reportSelected='.
1.85 matthew 1114: $reportdata->{'internal_name'}.'" >'.
1.87 matthew 1115: $reportdata->{'name'}."</a></h3>\n";
1116: $Str .= ' '.(' 'x8).$reportdata->{'short_description'}.
1117: "\n";
1.85 matthew 1118: }
1119: $Str .="</dl>\n";
1.65 matthew 1120: #
1.33 stredwic 1121: return $Str;
1122: }
1123:
1.65 matthew 1124: ##############################################
1125: ##############################################
1.1 albertel 1126: sub handler {
1.31 minaeibi 1127: my $r=shift;
1.65 matthew 1128: my $c = $r->connection();
1129: #
1130: # Check for overloading
1.51 www 1131: my $loaderror=&Apache::lonnet::overloaderror($r);
1132: if ($loaderror) { return $loaderror; }
1133: $loaderror=
1134: &Apache::lonnet::overloaderror($r,
1.123 albertel 1135: $env{'course.'.$env{'request.course.id'}.'.home'});
1.51 www 1136: if ($loaderror) { return $loaderror; }
1.65 matthew 1137: #
1138: # Check for access
1.123 albertel 1139: if (! &Apache::lonnet::allowed('vgr',$env{'request.course.id'})) {
1140: $env{'user.error.msg'}=
1.69 matthew 1141: $r->uri.":vgr:0:0:Cannot view grades for complete course";
1142: if (! &Apache::lonnet::allowed('vgr',
1.123 albertel 1143: $env{'request.course.id'}.'/'.$env{'request.course.sec'})) {
1144: $env{'user.error.msg'}=
1.69 matthew 1145: $r->uri.":vgr:0:0:Cannot view grades with given role";
1146: return HTTP_NOT_ACCEPTABLE;
1147: }
1.27 stredwic 1148: }
1.65 matthew 1149: #
1150: # Send the header
1.92 www 1151: &Apache::loncommon::no_cache($r);
1152: &Apache::loncommon::content_type($r,'text/html');
1.27 stredwic 1153: $r->send_http_header;
1.92 www 1154: if ($r->header_only) { return OK; }
1.65 matthew 1155: #
1156: # Extract form elements from query string
1.60 matthew 1157: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
1.65 matthew 1158: ['sort','reportSelected',
1.72 matthew 1159: 'SelectedStudent']);
1.65 matthew 1160: #
1161: # Give the LON-CAPA page header
1.109 matthew 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 }
1.110 matthew 1166: tr.even { background-color: \#CCCCCC }
1167: td.essay { border: 1px solid gray; }
1.109 matthew 1168: </style>
1169: ENDSTYLE
1.129 albertel 1170:
1171: $r->print(&Apache::loncommon::start_page('Course Statistics and Charts',
1172: $style));
1.65 matthew 1173: $r->rflush();
1.85 matthew 1174: #
1175: # Either print out a menu for them or send them to a report
1.98 matthew 1176: &Apache::lonhtmlcommon::clear_breadcrumbs();
1177: &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/statistics',
1.100 matthew 1178: title=>'Statistics',
1179: text =>'Statistics',
1.98 matthew 1180: faq=>139,
1181: bug=>'Statistics and Charts'});
1.123 albertel 1182: if (! exists($env{'form.reportSelected'}) ||
1183: $env{'form.reportSelected'} eq '') {
1.132 albertel 1184: $r->print(&Apache::lonhtmlcommon::breadcrumbs('Statistics Main Page').
1.98 matthew 1185: &CreateMainMenu());
1.85 matthew 1186: } else {
1.65 matthew 1187: #
1.85 matthew 1188: if (! &Apache::lonmysql::verify_sql_connection()) {
1189: my $serveradmin = $r->dir_config('lonAdmEMail');
1.140 bisitz 1190: $r->print('<h2 class="LC_error">'.
1.85 matthew 1191: &mt('Unable to connect to database!').
1.140 bisitz 1192: '</h2>');
1193: $r->print('<p>'
1194: .&mt('Please notify the server administrator [_1]',
1195: ,'<b>'.$serveradmin.'</b>')
1196: .'</p>');
1.85 matthew 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>');
1.129 albertel 1202: $r->print(&Apache::loncommon::end_page());
1.85 matthew 1203: return;
1204: }
1205: #
1206: # Clean out the caches
1.123 albertel 1207: if (exists($env{'form.ClearCache'})) {
1208: &Apache::loncoursedata::delete_caches($env{'requres.course.id'});
1.85 matthew 1209: }
1210: #
1211: # Begin form output
1212: $r->print('<form name="Statistics" ');
1213: $r->print('method="post" action="/adm/statistics">');
1214: $r->rflush();
1215: #
1.123 albertel 1216: my $GoToPage = $env{'form.reportSelected'};
1.90 matthew 1217: #
1.85 matthew 1218: $r->print('<input type="hidden" name="reportSelected" value="'.
1219: $GoToPage.'">');
1220: if($GoToPage eq 'activitylog') {
1.65 matthew 1221: # &Apache::lonproblemstatistics::Activity();
1.85 matthew 1222: } elsif($GoToPage eq 'problem_statistics') {
1.98 matthew 1223: &Apache::lonhtmlcommon::add_breadcrumb
1224: ({href=>'/adm/statistics?reportselected=problem_statistics',
1.100 matthew 1225: text=>'Overall Problem Statistics'});
1.85 matthew 1226: &Apache::lonproblemstatistics::BuildProblemStatisticsPage($r,$c);
1227: } elsif($GoToPage eq 'problem_analysis') {
1.98 matthew 1228: &Apache::lonhtmlcommon::add_breadcrumb
1229: ({href=>'/adm/statistics?reportselected=problem_analysis',
1.100 matthew 1230: text=>'Detailed Problem Analysis'});
1.85 matthew 1231: &Apache::lonproblemanalysis::BuildProblemAnalysisPage($r,$c);
1.89 matthew 1232: } elsif($GoToPage eq 'submissiontime_analysis') {
1.98 matthew 1233: &Apache::lonhtmlcommon::add_breadcrumb
1234: ({href=>
1235: '/adm/statistics?reportselected=submissiontime_analysis',
1.100 matthew 1236: text=>'Submission Time Plots'});
1.89 matthew 1237: &Apache::lonsubmissiontimeanalysis::BuildSubmissionTimePage($r,$c);
1.97 matthew 1238: } elsif($GoToPage eq 'student_submission_reports') {
1.98 matthew 1239: &Apache::lonhtmlcommon::add_breadcrumb
1240: ({href=>
1241: '/adm/statistics?reportselected=student_submission_reports',
1.100 matthew 1242: text=>'Student Submission Reports'});
1.97 matthew 1243: &Apache::lonstudentsubmissions::BuildStudentSubmissionsPage($r,$c);
1.104 matthew 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);
1.94 matthew 1250: } elsif($GoToPage eq 'correct_problems_plot') {
1.98 matthew 1251: &Apache::lonhtmlcommon::add_breadcrumb
1252: ({href=>'/adm/statistics?reportselected=correct_problems_plot',
1.100 matthew 1253: text=>'Correct Problems Plot'});
1.94 matthew 1254: &Apache::loncorrectproblemplot::BuildCorrectProblemsPage($r,$c);
1.85 matthew 1255: } elsif($GoToPage eq 'student_assessment') {
1.98 matthew 1256: &Apache::lonhtmlcommon::clear_breadcrumbs();
1257: &Apache::lonhtmlcommon::add_breadcrumb
1258: ({href=>'/adm/statistics?reportselected=student_assessment',
1.100 matthew 1259: text=>'Chart'});
1.85 matthew 1260: &Apache::lonstudentassessment::BuildStudentAssessmentPage($r,$c);
1.128 albertel 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: }
1.85 matthew 1267: #
1268: $r->print("</form>\n");
1.65 matthew 1269: }
1.129 albertel 1270: $r->print(&Apache::loncommon::end_page());
1.65 matthew 1271: $r->rflush();
1272: #
1.27 stredwic 1273: return OK;
1.1 albertel 1274: }
1.65 matthew 1275:
1.1 albertel 1276: 1;
1.59 matthew 1277:
1.65 matthew 1278: #######################################################
1279: #######################################################
1280:
1.59 matthew 1281: =pod
1282:
1283: =back
1284:
1285: =cut
1.65 matthew 1286:
1287: #######################################################
1288: #######################################################
1.59 matthew 1289:
1.1 albertel 1290: __END__
1.31 minaeibi 1291:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>