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