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