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