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