File:  [LON-CAPA] / capa / capa51 / CapaTools / capautils.1.1.pl
Revision 1.2: download - view: text, annotated - select for diffs
Fri Jul 7 18:33:29 2000 UTC (23 years, 10 months ago) by albertel
Branches: MAIN
CVS tags: version5-1-2-first_release, HEAD
- added GPL notices

    1: #!/usr/local/bin/perl -I/usr/local/bin
    2: 
    3: # utilities for CAPA superseeded by Manager
    4: #  Copyright (C) 1992-2000 Michigan State University
    5: #
    6: #  The CAPA system is free software; you can redistribute it and/or
    7: #  modify it under the terms of the GNU Library General Public License as
    8: #  published by the Free Software Foundation; either version 2 of the
    9: #  License, or (at your option) any later version.
   10: #
   11: #  The CAPA system is distributed in the hope that it will be useful,
   12: #  but WITHOUT ANY WARRANTY; without even the implied warranty of
   13: #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14: #  Library General Public License for more details.
   15: #
   16: #  You should have received a copy of the GNU Library General Public
   17: #  License along with the CAPA system; see the file COPYING.  If not,
   18: #  write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   19: #  Boston, MA 02111-1307, USA.
   20: #
   21: #  As a special exception, you have permission to link this program
   22: #  with the TtH/TtM library and distribute executables, as long as you
   23: #  follow the requirements of the GNU GPL in regard to all of the
   24: #  software in the executable aside from TtH/TtM.
   25: # <==========================================================================>
   26: # June 1997 version 1.0 Created by Isaac Tsai 
   27: # September 24 1997 version 1.1 by Guy Albertelli II
   28: #        -put in initialization loop in S_ScanSetDB (need to remove hardlimits)
   29: #        -initialize Max_try in S_Average, before being used
   30: #
   31: # <==========================================================================>
   32: use Cwd;
   33: require('getopts.pl');
   34: require('CAPAscreen.pl');
   35: # =============================================================================
   36: #  
   37: # =============================================================================
   38: $Days   = int( time / 86400);
   39: @MonthName = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ); 
   40: #
   41: # produces a string  "ddMmm19xx"  to be used in timestamp or filename
   42: #
   43: sub  TodayString {
   44:     local($ss, $mm, $hh, $mday, $mth, $yy, $wday, $yday,$isdst);
   45:     local($str);
   46:  
   47:   ($ss, $mm, $hh, $mday, $mth, $yy, $wday, $yday,$isdst) = localtime(time);
   48:    if ($mday > 9) {
   49:      $str = "$mday$MonthName[$mth]19$yy";  # year 2000 problem!!
   50:    } else {
   51:      $str = "0$mday$MonthName[$mth]19$yy"; # year 2000 problem!!
   52:    }
   53:    return $str;
   54:  }
   55: #
   56: # produces a string hhmmss-ddMon19xx for timestamps
   57: #
   58: sub  TimeString {
   59:     local($ss, $mm, $hh, $mday, $mth, $yy, $wday, $yday,$isdst);
   60:     local($str);
   61:  
   62:   ($ss, $mm, $hh, $mday, $mth, $yy, $wday, $yday,$isdst) = localtime(time);
   63:    $ss = "0" . "$ss" if $ss <= 9;
   64:    $mm = "0" . "$mm" if $mm <= 9;
   65:    $hh = "0" . "$hh" if $hh <= 9;
   66:    if ($mday > 9) {
   67:      $str = "$hh$mm$ss-$mday$MonthName[$mth]19$yy";  # year 2000 problem!!
   68:    } else {
   69:      $str = "$hh$mm$ss-0$mday$MonthName[$mth]19$yy"; # year 2000 problem!!
   70:    }
   71:    return $str;
   72:  }
   73: # =============================================================================
   74: #  Read capa.config into global variables
   75: #  
   76: sub  S_ReadCAPAconfig {
   77:      local($classpath)=@_;
   78:      local($filename);
   79:      local($filename2);
   80:      local($tempfilename);
   81:      local($input_line);
   82:      local($done)=0;
   83:      local($tmp);
   84:      
   85:      @MainPath=();
   86:      push(@MainPath,$classpath);
   87:      $ClassName = substr($classpath,-8,8);
   88:      $filename="$classpath" . "/capa.config";
   89:      $filename2="$classpath" . "/capautils.config";
   90:      foreach $tempfilename ( $filename, $filename2 ) {
   91:        if(-f $tempfilename) {
   92:           open(IN, "<$tempfilename") || die "Cannot open file $tempfilename!";
   93:           LINE: while( ($input_line = <IN>) && (! $done) ) {
   94:              ## skip over comments
   95:              if( $input_line =~ /^\#/ ) { next LINE; }  
   96:              chop($input_line);
   97:              #  collect _path information and create the corresponding global variable 
   98:              if( ($input_line =~ /^(\w+)_path\s*=\s*["]([\.\d\+\-\w\s\/]+)["]$/) || 
   99:                  ($input_line =~ /^(\w+)_path\s*=\s*(\S+)$/) ) {
  100:                $tmp = ucfirst lc $1;
  101:                $tmp = "$tmp" . "Path";
  102:                push(@MainPath,$2);
  103:                ${$tmp} = "$2";
  104:              # collect printer_option information
  105:              } elsif ( ($input_line =~ /^\s*printer_option\s*=\s*["]([\.\d\+\-\w\s\/\|\$]+)["]$/) ||
  106:                        ($input_line =~ /^\s*printer_option\s*=\s*(\S+)$/ ) ) {
  107:                push(@Printers,$1);
  108:                ##   
  109:              # collect _command information
  110:              } elsif ( ($input_line =~ /^\s*(\w+)_command\s*=\s*["]([\.\d\+\-\w\s\/\|\$]+)["]$/) ||
  111:                        ($input_line =~ /^\s*(\w+)_command\s*=\s*(\S+)$/ ) ) {
  112:                $tmp = ucfirst lc $1;
  113:                $tmp = "$tmp" . "CMD";
  114:                ${$tmp} = "$2";
  115:                ## print "CMD \$$tmp: [$2]\n";
  116:                ## print "Press RETURN to continue"; $tmp = <>;
  117:              # collect _file information
  118:              } elsif ( ($input_line =~ /^\s*(\w+)_file\s*=\s*["]([\.\d\+\-\w\s\/]+)["]$/) ||
  119:                        ($input_line =~ /^\s*(\w+)_file\s*=\s*(\S+)$/ ) ) {
  120:                $tmp = ucfirst lc $1;
  121:                $tmp = "$tmp" . "File";
  122:                ${$tmp} = "$2"; 
  123:              # var_ definition
  124:              } elsif ( ($input_line =~ /^\s*var_(\w+)\s*:=\s*["]([\.\d\+\-\w\s\/]+)["]$/) ||
  125:                        ($input_line =~ /^\s*var_(\w+)\s*:=\s*(\S+)$/ ) ) {
  126:                # we defined a variable name with that value 
  127:                $tmp = ucfirst lc $1;
  128:                $Var_name{$tmp} = "$2";
  129:                # two levels of indirection
  130:                # the variable should be ${$2}
  131:          
  132:              # prefix_ definition
  133:              } elsif ( ($input_line =~ /^\s*prefix_(\w+)\s*:=\s*["]([\.\d\+\-\w\s\/]+)["]$/) ||
  134:                        ($input_line =~ /^\s*prefix_(\w+)\s*:=\s*(\S+)$/ ) ) {
  135:                $tmp = ucfirst lc $1;
  136:                $Prefix_name{$tmp} = "$2";
  137:          
  138:              # assigning an numerical value to a variable
  139:              } elsif ( ($input_line =~ /^\s*(\w+)\s*=\s*([\.\d]+)/) ) {
  140:                ${$1} = $2;
  141:                # print "Assign \$$1: with $2\n";
  142:                # print "Press RETURN to continue"; $tmp = <>;
  143:              # assigning a variable to another variable
  144:              } elsif ( ($input_line =~ /^\s*(\w+)\s*=\s*([\w]+)/) ) {
  145:                $P_code{$1} = "\$$2";
  146:                ## print "PERL $1: { $P_code{$1} }\n";
  147:                ## print "Press RETURN to continue"; $tmp = <>;
  148:              # perl code
  149:              } elsif ( ($input_line =~ /^\s*(\w+)\s*::/) ) {
  150:                $p_var_name = $1;
  151:              } elsif ( ($input_line =~ /^\s*BEGIN_perl/) ) {
  152:                $P_code{$p_var_name} = &E_CollectPerlCode;
  153:                ## print "PERL $p_var_name: { $P_code{$p_var_name} }\n";
  154:                ## print "Press RETURN to continue"; $tmp = <>;
  155:              } elsif ($input_line =~ /[Bb][Aa][Ss][Ee][\s][Uu][Nn][Ii][Tt]/) {
  156:                $done = 1;
  157:              }
  158:          
  159:            }
  160:            close(IN) || die "Cannot close file $tempfilename!";
  161:        }
  162:        # define some global variables if they are not defined properly in capa.config file
  163:        $homework_scores_limit_set = 99 if $homework_scores_limit_set eq "";
  164:        $exam_scores_limit_set     = 99 if $exam_scores_limit_set     eq "";
  165:        $quiz_scores_limit_set     = 99 if $quiz_scores_limit_set     eq "";
  166:        $supp_scores_limit_set     = 99 if $supp_scores_limit_set     eq "";
  167:        $others_scores_limit_set   = 99 if $others_scores_limit_set   eq "";
  168:        $display_score_row_limit   = 40 if $display_score_row_limit   eq "";
  169:        ## print "Press RETURN to continue"; $tmp = <>;
  170:      }
  171:  }
  172: #
  173: #  Collect perl codes from <IN> until it encountered a 
  174: #  END_perl statement
  175: #  Skips over comments (begin with a # mark)
  176: #  It then places all the codes withing a pair of '{' and '}'
  177: #
  178: sub  E_CollectPerlCode {
  179:     local($input_line);
  180:     local($p_code,$done);
  181:     
  182:     $p_code = "{ \n";   # begining of the code
  183:     $done=0;
  184:     while( ($input_line = <IN>) && (! $done) ) {
  185:       if( $input_line =~ /^END_perl/ ) {
  186:         $done = 1;
  187:       } else {
  188:         if( $input_line !~ /^\#/ ) {  # skip over comments
  189:           $p_code = "$p_code" . "$input_line";
  190:         }
  191:       }
  192:     }
  193:     $p_code = "$p_code" . " }\n";  # ending of the code
  194:     return ($p_code);
  195: }
  196: #
  197: # Collects e-mail template code until it encountered 
  198: # a END_template statement in the <IN> file
  199: # It skips over comments that begin with a # mark
  200: #
  201: sub  E_CollectTemplateCode {
  202:     local($input_line);
  203:     local($p_code,$done);
  204:     
  205:     
  206:     $done=0;
  207:     while( ($input_line = <IN>) && (! $done) ) {
  208:       if( $input_line =~ /^END_template/ ) {
  209:         $done = 1;
  210:       } else {
  211:         if( $input_line !~ /^\#/ ) {  # skip over comments
  212:           $p_code = "$p_code" . "$input_line";
  213:         }
  214:       }
  215:     }
  216:     
  217:     return ($p_code);
  218:  }
  219: 
  220: # 
  221: # $MailCMD comes from the entry 'mail_command' in capa.config
  222: # 
  223: sub  S_Mailto {
  224:     local($e_address,$e_file)=@_;
  225:     local($m_subject);
  226:     local($cmd);
  227:     local($tmp);
  228:     
  229:   $m_subject = "Current Status on $ClassName";
  230:   
  231:   $cmd = "$MailCMD -s '$m_subject' $e_address < $e_file";
  232:   print "Mail File $e_file To $e_address\n";
  233:   system($cmd);
  234:   # ??? How do we know this command successfully returns? 
  235:   
  236:  }
  237: 
  238: # TODO:: Check the validity of $e_addr
  239: #        
  240: # INPUT: a string represents the category to mail to
  241: #        it could be "3" or "0" or "1,3,4"
  242: #        "0"      means 1,2,3, and 4
  243: #        "1,3,4"  means 1,3, and 4
  244: #        anything greater than 4 is not valid in the input
  245: #        therefore category 5 represent those that are 
  246: #        falling through the gaps of *_high and *_low
  247: #
  248: sub  S_MailtoCategory {
  249:     local($cat)=@_;
  250:     local(@all_files);
  251:     local(@a_cat);
  252:     local($i,$j,$a_char);
  253:     local($s_id,$s_name,$s_sec,$e_addr);
  254:     local($orig_filename,$new_filename);
  255:     local($tmp);
  256:     
  257:        opendir(EDIR, "$ClassPath/Mail") || die "cannot opendir $ClassPath/Mail!";
  258:        @all_files = grep !/^\.\.?$/, readdir EDIR;
  259:        closedir EDIR;
  260:        
  261:        if( ( $cat =~ /,/ ) || ( $cat == 0 )  ) {
  262:          if( $cat =~ /,/ ) {
  263:            @a_cat = split(/,/,$cat);
  264:          } else {
  265:            @a_cat = (1,2,3,4);
  266:          }
  267:          for( $i=0;$i<=$#all_files;$i++) {
  268:            for( $j=0;$j<=$#a_cat;$j++) {
  269:              $a_char = $a_cat[$j];
  270:              if( $all_files[$i] =~ /([\w\d]+)\.[\w\d]+\.$a_char$/ ) {
  271:                $s_id = $1;
  272:                ($s_name,$s_sec,$e_addr) = S_Lookup_student_name("$s_id");
  273:                if( $e_addr ne "" ) {
  274:                  $orig_filename = "$ClassPath/Mail/$all_files[$i]";
  275:                  S_Mailto("$e_addr","$orig_filename");
  276:                  print "moving $all_files[$i] to $all_files[$i].done\n";
  277:                  # move the completed file to *.done 
  278:                  system("mv $orig_filename $orig_filename.done");
  279:                }
  280:              }
  281:            }
  282:          }
  283:        } else {
  284:          for( $i=0;$i<=$#all_files;$i++) {
  285:            if( $all_files[$i] =~ /([\w\d]+)\.[\w\d]+\.$cat$/ ) {
  286:              $s_id = $1;
  287:              ($s_name,$s_sec,$e_addr) = S_Lookup_student_name("$s_id");
  288:              print "Addr=$e_addr\n";
  289:              print "Press RETURN to continue"; $tmp = <>;
  290:              if( $e_addr ne "" ) {
  291:                  $orig_filename = "$ClassPath/Mail/$all_files[$i]";
  292:                  S_Mailto("$e_addr","$orig_filename");
  293:                  print "moving $all_files[$i] to $all_files[$i].done\n";
  294:                  system("mv $orig_filename $orig_filename.done");
  295:              }
  296:            }
  297:          }
  298:        }
  299:        print "DONE Mail, press RETURN to continue"; $tmp = <>;
  300: 
  301:  }
  302: #
  303: # It prompts the user to enter an absolute path to a regular class
  304: #
  305: sub  S_Enterpath {
  306:     local($set)=@_;
  307:     local($notdone,$path,$cfgfullpath,$cfgutilsfullpath);
  308:     local($cfullpath,$rfullpath,$sfullpath);
  309:     
  310:     $notdone = 1;
  311:     while ($notdone) {
  312:       print "Please enter the CLASS absolute path:\n";
  313:       $path = <>; chomp($path);
  314:       if( $path =~ /\/$/ ) {
  315:         $cfullpath = "$path" . "classl";
  316:         $rfullpath = "$path" . "records";
  317:         $sfullpath = "$path" . "records/set$set.db";
  318:         $cfgfullpath = "$path" . "capa.config";
  319:         $cfgutilsfullpath = "$path" . "capautils.config";
  320:       } else {
  321:         $cfullpath = "$path" . "/classl";
  322:         $rfullpath = "$path" . "/records";
  323:         $sfullpath = "$path" . "/records/set$set.db";
  324:         $cfgfullpath = "$path" . "/capa.config";
  325:         $cfgutilsfullpath = "$path" . "/capautils.config";
  326:       }
  327:       if( -d $path ) {
  328:         if( -d $rfullpath ) {
  329:           if( -f $cfgfullpath ) {
  330: 	      if( -f $cfgutilsfullpath ) {
  331: 		  $notdone = 0;
  332: 	      } else {
  333: 		  print "File [$cfgutilsfullpath] does not exist!\n";
  334: 	      }
  335:           } else {
  336:               print "File [$cfgfullpath] does not exist!\n";
  337:           }
  338:         } else {
  339:           print "Directory [$rfullpath] does not exist!\n";
  340:         }
  341:       } else {
  342:         print "Directory [$path] does not exist!\n";
  343:       }
  344:     
  345:     }
  346:     return ($path);
  347:   }
  348: # ----------------------------------------------------------
  349: #      Global menu items to be selected by user
  350: #
  351: @Main_menu=(
  352:   "Change class path", 
  353:   "Run capastat", 
  354:   "Log analysis on Y, N, S, U, and u", 
  355:   "Student course profile", 
  356:   "CAPA IDs for one student",
  357:   "All CAPA IDs",
  358:   "Item analysis",
  359:   "Item correlation",
  360:   "Email",
  361:   "Print assignment(s) for a student",
  362:   "View score file",
  363:   "View submissions for a student",
  364:   "Quit");
  365: 
  366: @Prof_menu=(
  367:   "Student number",
  368:   "Student name",
  369:   "Cancel" );
  370: 
  371: @Email_menu=(
  372:   "Create score file for all students",
  373:   "Create individual e-mail files from the file in 1.",
  374:   "Preview e-mail file randomly from among the files in 2.",
  375:   "Send e-mail files in 2. to a group of students",
  376:   "Cancel" );
  377: 
  378: @ScoreSortMsg=(
  379:   "Sort by the order of: ", 
  380:   " 1. Grade",
  381:   " 2. Student number",
  382:   " 3. Student name", 
  383:   " 4. Section",
  384:   " ",
  385:   "3,2   means 'name' first, 'student number' second",
  386:   "1,4,3 means sort by 'grade', 'section' and 'name'" );
  387: 
  388: 
  389: @SpecifyCategoryMsg =  ("Which category?",
  390:                         "  enter number(s) between 0 and 4",
  391:                         "  1,2,4 : categories 1, 2 and 4",
  392:                         "  3     : category 3",
  393:                         "  0     : all categories" );
  394: #
  395: # Only accepts input of 0, 1, 2, 3, and 4, nothing else.
  396: #
  397: sub  S_EnterCategory {
  398:     local($cat);
  399:     local($done)=0;
  400:     local(@a_cat,$i);
  401:     
  402:     while(! $done) {
  403:       $cat = C_InputSetNum(4,10,45,"",12,,"CATEGORY:", @SpecifyCategoryMsg);
  404:       if( $cat =~ /,/ ) {
  405:         @a_cat = split(/,/,$cat);
  406:         $done = 1;
  407:         for( $i=0;$i<=$#a_cat;$i++) {
  408:           if( ($a_cat[$i] < 0 || $a_cat[$i] > 4) && ($a_cat[$i] ne "" ) ) {
  409:             $done = 0;
  410:           }
  411:           if( $a_cat[$i] == 0 ) {
  412:             $done = 0;
  413:           }
  414:         }
  415:         
  416:       } else {
  417:         if(  $cat>= 0 && $cat <= 4 ) {
  418:           $done = 1;
  419:         }
  420:       }
  421:     }
  422:     return ($cat);
  423: }
  424: 
  425: # Only allows input of 1, 2, 3, and 4, nothing more
  426: sub  S_EnterSortKey {
  427:     local($key);
  428:     local($done)=0;
  429:     local(@a_key,$i);
  430:     
  431:     while(! $done) {
  432:       $key = C_InputSetNum(2,5,60,"",12,,"KEY:",@ScoreSortMsg);
  433:       if( $key =~ /,/ ) {
  434:         @a_key = split(/,/,$key);
  435:         $done = 1;
  436:         for( $i=0;$i<=$#a_key;$i++) {
  437:           if( ($a_cat[$i] < 0 || $a_cat[$i] > 4) && ($a_key[$i] ne "" ) ) {
  438:             $done = 0;
  439:           }
  440:           if( $a_key[$i] == 0 ) {
  441:             $done = 0;
  442:           }
  443:         }
  444:         
  445:       } else {
  446:         if(  $key> 0 && $key < 5 ) {
  447:           $done = 1;
  448:         }
  449:       }
  450:     }
  451:     return ($key);
  452: }
  453: 
  454: 
  455: 
  456: 
  457: @EnterSetMsg =  ("Which set?");
  458: @EnterSetsMsg = ("Which set(s)?",
  459:                  " 3,7 : from set 3 to set 7 (both inclusive)",
  460:                  " 4   : only set 4" );
  461: 
  462: sub  S_EnterSets {
  463:     local($set);
  464:     local($done)=0;
  465:     local($s_from,$s_to);
  466:     
  467:     while(! $done) {
  468:       $set = C_InputSetNum(4,10,45,"",6,,"SET:", @EnterSetsMsg);
  469:       if( $set =~ /,/ ) {
  470:         ($s_from,$s_to) = split(/,/,$set);
  471:         if( $s_from <= 0 || $s_from >= 100 ) { $s_from = 1; }
  472:         if( $s_to <= 0   || $s_to >= 100 )   { $s_to   = 1; }
  473:         if( $s_from > $s_to) {
  474:           $tmp = $s_from; $s_from = $s_to; $s_to = $tmp;
  475:         }
  476:         $done = 1;
  477:       } else {
  478:         if(  $set> 0 && $set < 100 ) {
  479:           $s_to = $set;
  480:           $s_from = $s_to;
  481:           $done = 1;
  482:         }
  483:       }
  484:     }
  485:     return ($s_from,$s_to);
  486: }
  487: 
  488: sub  S_InputSet {
  489:     local($set);
  490:     local($done)=0;
  491:     
  492:     while(! $done) {
  493:       $set = C_InputSetNum(4,10,15,"",2,,"SET:", @EnterSetMsg);
  494:       if( $set =~ /\d+/ && $set > 0 && $set < 100 ) { # check entered set 
  495:         $done = 1;
  496:       }
  497:     }
  498:     return ($set);
  499: }
  500: 
  501: 
  502: @EnterSSNMsg = ("Enter student number?", " (RETURN to exit)" );
  503: @EnterSNMsg =  ("Enter student name (max 30 chars)?",
  504:                 "Last, First (Middle)",
  505:                 " (RETURN to exit)" );
  506: 
  507: sub  S_InputStudent {
  508:     local($classpath)=@_;
  509:     local($filename);
  510:     local($select,$tmp_sn,$input_line);
  511:     local($student_id,$student_name);
  512:     local($found,$done,$s_name);
  513:     local($match,@matched_entries,$tmp_line);
  514:     
  515:     $select = C_MultipleChoice(4,10,$DialogWidth,"Select student by:","$DisplayPath",@Prof_menu);
  516:     if($select == 1 ) {  # specify student number
  517:        while(! $done) {
  518:          $student_id = C_InputStudentID(4,10,24,"",9,"INPUT:",@EnterSSNMsg);
  519:          if($student_id eq "" ) {
  520:            $done = 1; 
  521:          } else {
  522:            $student_id = uc($student_id);
  523:            $filename = $classpath . "/classl";
  524:            open(IN, "<$filename") || die "Cannot open file $filename!";
  525:            $match = 0; @matched_entries = ();
  526:            while(($input_line = <IN>)) {
  527:              chomp($input_line);
  528:              $tmp_sn = substr($input_line,14,9); $tmp_sn = uc($tmp_sn);
  529:              if($tmp_sn =~ /^$student_id/ ) {
  530:                $match++;
  531:                # student name begins at column 24 and has 30 chars max
  532:                $student_name = substr($input_line,24,30); 
  533:                $tmp_line = "$tmp_sn " . "$student_name";
  534:                push(@matched_entries,$tmp_line);
  535:              }
  536:            }
  537:            close(IN) || die "Cannot close file $filename!";
  538:            if($match > 1 && $match <= 12) {
  539:              $select = C_MultipleChoice(4,10,$DialogWidth,"        Matched Student Records       ",
  540:                           "$DisplayPath",@matched_entries);
  541:              $student_id = substr($matched_entries[$select-1],0,9);
  542:              $student_name = substr($matched_entries[$select-1],10,30);
  543:              $done = 1;
  544:            } elsif ($match == 1) {
  545:              $student_id = substr($matched_entries[0],0,9);
  546:              $student_name = substr($matched_entries[0],10,30);
  547:              $done = 1;
  548:            } elsif ($match > 12) {
  549:              $tmp_line = "There are $match records found.";
  550:              C_Warn(4,10,$DialogWidth,"Too many students matched",$tmp_line);
  551:            } else {
  552:              $tmp_line = "Please re-enter student number.";
  553:              C_Warn(4,10,$DialogWidth,"No student found",$tmp_line);
  554:            }
  555:          }
  556:          
  557:        }
  558:     } elsif ($select == 2) { # specify student name
  559:        while(! $done) {
  560:          $s_name = C_InputStudentID(4,10,40,"Enter student name",30,,"INPUT:", @EnterSNMsg);
  561:          if($s_name eq "" ) {
  562:            $done = 1;
  563:          } else {
  564:            $s_name = uc($s_name);
  565:            $filename = $classpath . "/classl";
  566:            open(IN, "<$filename") || die "Cannot open file $filename!";
  567:            $match = 0; @matched_entries = ();
  568:            while(($input_line = <IN>)) {
  569:              chomp($input_line);
  570:                $tmp_sn = substr($input_line,24,30); $tmp_sn = uc($tmp_sn);
  571:              if( $tmp_sn =~ /^$s_name/ ) {
  572:                $match++;
  573:                $student_id = substr($input_line,14,9); # student number
  574:                $tmp_line = "$student_id " . "$tmp_sn";
  575:                push(@matched_entries,$tmp_line);
  576:              }
  577:            }
  578:            close(IN) || die "Cannot close file $filename!";
  579:            if($match > 1 && $match <= 12) {
  580:              $select = C_MultipleChoice(4,10,$DialogWidth,"        Matched Student Records       ",
  581:                             "$DisplayPath",@matched_entries);
  582:              $student_id = substr($matched_entries[$select-1],0,9);
  583:              $student_name = substr($matched_entries[$select-1],10,30);
  584:              $done = 1;
  585:            } elsif ($match == 1) {
  586:              $student_id = substr($matched_entries[0],0,9);
  587:              $student_name = substr($matched_entries[0],10,30);
  588:              $done = 1;
  589:            } elsif ($match > 12) {
  590:              $tmp_line = "There are $match records found.";
  591:              C_Warn(4,10,$DialogWidth,"Too many students matched",$tmp_line);
  592:            } else {
  593:              $tmp_line = "Please re-enter student name.";
  594:              C_Warn(4,10,$DialogWidth,"No student found",$tmp_line);
  595:            }
  596:          }
  597:        }
  598:     } else {  # cancel 
  599:       $student_id = "";
  600:       $student_name = "";
  601:     }
  602:     return ($student_id,$student_name);
  603: }
  604: 
  605: 
  606: 
  607: #
  608: # INPUT: the class name with full path and the student number
  609: # OUTPUT: total scores , total possible wights
  610: 
  611: sub S_CollectSetScores {
  612:     local($classpath,$student_id,$on_screen,$s_limit)=@_;
  613:     local($filename,$found,$classname);
  614:     local($done)=0;
  615:     local($line_cnt,$input_line);
  616:     local(@weights);       # the wights array for individual set
  617:     local($valid_weights); # the valid weights for a set
  618:     local($total_weights); # the overall valid weights for all sets
  619:     local(@set_weights);   # the array storing all set valid weights
  620:     local($score);         # the valid score for a set
  621:     local($total_scores);  # the overall valid scores for all sets
  622:     local(@set_scores);    # the array storing all set scores
  623:     local($set_idx);
  624:     local($ii,$abscent_cnt,$present_cnt);
  625:     local($s_num,$ans_str,$prefix,$rest);
  626:     local(@ans_char,$ratio,$tmp,$summary_str);
  627:     
  628:     $student_id = uc($student_id);
  629:     $total_scores = 0; $total_weights = 0; # initialize the overall results
  630:     $set_idx = 0;
  631:     
  632:     while( ! $done ) {
  633:       $set_idx++;    # start out as set1.db, then add one to it
  634:       if($set_idx <= $s_limit ) {  # the limit set is inclusive
  635:         $filename = $classpath . "/records/set" . "$set_idx" . ".db";
  636:         if( -f $filename) { # file exists!
  637:           open(IN, "<$filename") || die "Cannot open file $filename!";
  638:           $line_cnt=0; $found = 0;  # for each file opened, initialize $line_cnt and $found
  639:           while ( ($input_line = <IN>) && !$found) { 
  640:             $line_cnt++;       # add one to line count
  641:             if( $line_cnt == 2 ) { # second line is the weight for each problem
  642:               chomp();             # delete the trailing '\n' char
  643:               (@weights) = split(/ */,$input_line);  # split the line into each individual chars
  644:               $valid_weights = 0;
  645:               for($ii=0;$ii<=$#weights;$ii++) {
  646:                 $valid_weights += $weights[$ii];  # for now $valid_weights contains the sum
  647:               }
  648:             ## &C_ClearScreen;
  649:             ## print "Second line, $input_line, total weight=$valid_weights\n";
  650:             ## printf "Press RETURN to continue"; $tmp = <>;
  651:             }
  652:             if( $line_cnt > 3) {    # start from line 4 is the student data
  653:               chomp($input_line);              # delete the trailing '\n' char
  654:               ($prefix,$rest) = split(/,/,$input_line,2); # split the whole line into two parts
  655:               ($s_num,$ans_str) = split(/ /,$prefix,2);   # split into two parts
  656:               $s_num = uc($s_num); 
  657:               if( $student_id eq $s_num ) { # found the student we want
  658:               ## &C_ClearScreen;
  659:               ## print "FOUND [$input_line] $s_num == $student_id: weight= $valid_weights,ANS=$ans_str\n";
  660:               ## printf "Press RETURN to continue"; $tmp = <>;
  661:                 $found = 1;         # so we can exit the search through while loop
  662:                 (@ans_char) = split(/ */,$ans_str);  # split the answer string into individual ans chars
  663:                 for($valid = 'N', $ii=0;$ii<=$#ans_char;$ii++) {  # from question 0, 1, to last question -1
  664:                   $valid = 'Y' if $ans_char[$ii] ne '-';          # if ans char is different from '-', then
  665:                 }
  666:                 if( $valid eq 'Y' ) {   # don't bother with the record full of '-'s
  667:                   for($score=0,$ii=0;$ii<=$#ans_char;$ii++) {  # initialize $score and index $ii
  668:                   
  669:                     if($ans_char[$ii] eq 'Y') {
  670:                       $score += $weights[$ii];
  671:                     }
  672:                     if($ans_char[$ii] eq 'y') {
  673:                       $score += $weights[$ii];
  674:                     }
  675:                     if( $ans_char[$ii] ge '0' && $ans_char[$ii] le '9') {
  676:                       $score += $ans_char[$ii];
  677:                     }
  678:                     if($ans_char[$ii] eq 'E') { # subtract the weight from execused problem
  679:                       $valid_weights -= $weights[$ii];
  680:                     }
  681:                   }
  682:                   $total_scores += $score;  # add the calculated score to the overall sum
  683:                 } else { # end of a valid line
  684:                   $score = '-';
  685:                 }
  686:               } 
  687:             }   # end $line_cnt > 3
  688:           } # end while <IN>
  689:           close(IN) || die "Cannot close file $filename!";
  690:           $total_weights   += $valid_weights; # add the valid weights for a set to the overall sum
  691:           $set_weights[$set_idx-1] = $valid_weights;
  692:         
  693:           if( $found ) {
  694:           ## push(@set_scores, $score);          # push set score into array
  695:           ## push(@set_weights, $valid_weights); # push valid weight for a set into the weight array
  696:             $set_scores[$set_idx-1]  = $score;
  697:           } else { # student not found in the setX.db file
  698:             $set_scores[$set_idx-1]  = '-';
  699:           }
  700:         } else {  # $set_idx > $s_limit
  701:           $done = 1;  # exit the $done while loop
  702:         }
  703:       } else {
  704:         $done = 1;  # exit the $done while loop
  705:       }
  706:     } # end while ! $done
  707:     # print out the report
  708:     # &C_ClearScreen;
  709:     $abscent_cnt=0;
  710:     $present_cnt=0;
  711:     $summary_str = "";
  712:     print " " x 10 if $on_screen;
  713:     for($ii=0;$ii<=$#set_scores;$ii++) {
  714:       if( $set_scores[$ii] eq '-' || $set_scores[$ii] eq "" ) {
  715:         print "  - " if $on_screen;
  716:         $summary_str = "$summary_str" . "x/$set_weights[$ii] ";
  717:         $abscent_cnt++;
  718:       } else {
  719:         printf " %3d", $set_scores[$ii] if $on_screen;
  720:         $summary_str = "$summary_str" . "$set_scores[$ii]/$set_weights[$ii] ";
  721:         $present_cnt++;
  722:       }
  723:     }
  724:     if( $on_screen ) {
  725:       $classname = substr($classpath,-8,8);
  726:       print "\n $classname:";
  727:       for($ii=0;$ii<=$#set_scores;$ii++) {
  728:         print " ---";
  729:       }
  730:       print "\n          ";
  731:       for($ii=0;$ii<=$#set_weights;$ii++) {
  732:         printf " %3d", $set_weights[$ii];
  733:       }
  734:       print "\n";
  735:       if($total_weights != 0 ) {
  736:         $ratio = 100.0 * ($total_scores / $total_weights);
  737:       } else {
  738:         $ratio = '-';
  739:       }
  740:       printf "  %5d\n", $total_scores if $on_screen;
  741:       printf  " ------- = %3.2f%%, scores abscent in %d/%d\n", $ratio, $abscent_cnt, $#set_scores+1;
  742:       printf "  %5d\n", $total_weights;
  743:     ## print "Press RETURN to continue"; $tmp = <>;
  744:     }
  745:     return ($total_scores,$total_weights,$abscent_cnt,$#set_scores+1,$summary_str);
  746:  }
  747: 
  748: # 
  749: #  similar to S_CollectSetScores
  750: # 
  751: sub S_CollectExamScores {
  752:     local($classpath,$student_id)=@_;
  753:     local($filename,$found,$classname);
  754:     local($done)=0;
  755:     local($line_cnt,$input_line);
  756:     local(@weights);       # the wights array for individual set
  757:     local($valid_weights); # the valid weights for a set
  758:     local($total_weights); # the overall valid weights for all sets
  759:     local(@set_weights);   # the array storing all set valid weights
  760:     local($score);         # the valid score for a set
  761:     local($total_scores);  # the overall valid scores for all sets
  762:     local(@set_scores);    # the array storing all set scores
  763:     local($set_idx);
  764:     local($ii,$var_name);
  765:     local($s_num,$ans_str,$prefix,$rest);
  766:     local($midterm1,$midterm2,$midterm3,$f_score);
  767:     local($m_max1,$m_max2,$m_max3,$f_max);
  768:     local(@ans_char,$ratio,$tmp);
  769:     
  770:     $student_id = uc($student_id);
  771:     $total_scores = 0; $total_weights = 0; # initialize the overall results
  772:     $set_idx = 0;
  773:     
  774:     while( ! $done ) {
  775:       $set_idx++;    # start out as set1.db, then add one to it
  776:       if( $set_idx <= $exam_scores_limit_set ) { # $exam_scores_limit_set comes from capa.config
  777:         $filename = $classpath . "/records/set" . "$set_idx" . ".db";
  778:         if( -f $filename) { # file exists!
  779:           open(IN, "<$filename") || die "Cannot open file $filename!";
  780:           $line_cnt=0; $found = 0;  # for each file opened, initialize $line_cnt and $found
  781:           while ( ($input_line = <IN>) && !$found) { 
  782:             $line_cnt++;       # add one to line count
  783:             if( $line_cnt == 2 ) { # second line is the weight for each problem
  784:               chomp();             # delete the trailing '\n' char
  785:               (@weights) = split(/ */,$input_line);  # split the line into each individual chars
  786:               $valid_weights = 0;
  787:               for($ii=0;$ii<=$#weights;$ii++) {
  788:                 $valid_weights += $weights[$ii];  # for now $valid_weights contains the sum
  789:               }
  790:             }
  791:             if( $line_cnt > 3) {    # start from line 4 is the student data
  792:               chomp($input_line);              # delete the trailing '\n' char
  793:               ($prefix,$rest) = split(/,/,$input_line,2); # split the whole line into two parts
  794:               ($s_num,$ans_str) = split(/ /,$prefix,2);   # split into two parts
  795:               $s_num = uc($s_num); 
  796:               if( $student_id eq $s_num ) { # found the student we want
  797:                 $found = 1;         # so we can exit the search through while loop
  798:                 (@ans_char) = split(/ */,$ans_str);  # split the answer string into individual ans chars
  799:                 for($valid = 'N', $ii=0;$ii<=$#ans_char;$ii++) {  # from question 0, 1, to last question -1
  800:                   $valid = 'Y' if $ans_char[$ii] ne '-';          # if ans char is different from '-', then
  801:                 }
  802:                 if( $valid eq 'Y' ) {   # don't bother with the record full of '-'s
  803:                   for($score=0,$ii=0;$ii<=$#ans_char;$ii++) {  # initialize $score and index $ii
  804:                   
  805:                     if($ans_char[$ii] eq 'Y') {
  806:                       $score += $weights[$ii];
  807:                     }
  808:                     if($ans_char[$ii] eq 'y') {
  809:                       $score += $weights[$ii];
  810:                       }
  811:                     if( $ans_char[$ii] ge '0' && $ans_char[$ii] le '9') {
  812:                       $score += $ans_char[$ii];
  813:                     }
  814:                     if($ans_char[$ii] eq 'E') { # subtract the weight from execused problem
  815:                       $valid_weights -= $weights[$ii];
  816:                     }
  817:                   }
  818:                   $total_scores += $score;  # add the calculated score to the overall sum
  819:                 } else { # end of a valid line
  820:                   $score = '-';
  821:                 }
  822:               } # end student number comparison
  823:             }   # end $line_cnt > 3
  824:           } # end while <IN>
  825:           close(IN) || die "Cannot close file $filename!";
  826:           $total_weights   += $valid_weights; # add the valid weights for a set to the overall sum
  827:           $set_weights[$set_idx-1] = $valid_weights;
  828:         # push(@set_scores, $score);          # push set score into array
  829:         # push(@set_weights, $valid_weights); # push valid weight for a set into the weight array
  830:           if( $found ) {
  831:             $set_scores[$set_idx-1]  = $score;
  832:           } else { # could not locate the student in the setX.db file
  833:             $set_scores[$set_idx-1]  = '-';
  834:           }
  835:         } else {  # $set_idx > $exam_scores_limit_set
  836:           $done = 1;
  837:         }
  838:       } else {
  839:         $done = 1;  # exit the $done while loop
  840:       }
  841:     } # end while ! $done
  842: 
  843:     # put scores in the global variables: exam_raw1, exam_raw2 ...
  844:     #     and maximum weights in variables: exam_raw_max1, exam_raw_max2,..
  845:     # prefix is stored in :
  846:     #    $Prefix_name{"Exam_raw_scores"}
  847:     #    $Prefix_name{"Exam_raw_max"}
  848:     # 
  849:     $Prefix_name{'Exam_raw_scores'} = "exam_raw"     if ! defined $Prefix_name{'Exam_raw_scores'};
  850:     $Prefix_name{'Exam_raw_max'}    = "exam_raw_max" if ! defined $Prefix_name{'Exam_raw_max'};
  851:     for($ii=0;$ii<=$#set_scores;$ii++) {
  852:       $set_idx = $ii+1;
  853:       $var_name = "$Prefix_name{'Exam_raw_scores'}" . "$set_idx";
  854:       ${$var_name} = $set_scores[$ii];
  855:       $var_name = "$Prefix_name{'Exam_raw_max'}" . "$set_idx";
  856:       ${$var_name} = $set_weights[$ii];
  857:     }
  858:     #
  859:     # initialize these local variables 
  860:     #
  861:     $midterm1 = '-'; $m_max1 = '-';
  862:     $midterm2 = '-'; $m_max2 = '-';
  863:     $midterm3 = '-'; $m_max3 = '-';
  864:     $f_score  = '-'; $f_max =  '-';
  865:     # After we placed raw scores into the global variables exam_raw1, exam_raw2 ...
  866:     #  we can then, evaluate the definitions of midterm1, midterm2 and midterm3 from capa.config
  867:     if($#set_scores >= 1) {  # at least 2 sets
  868:         $midterm1 = eval $P_code{'midterm1'};
  869:         $var_name =  "$Prefix_name{'Exam_raw_max'}" . "1"; # for max possible scores, just pick a set
  870:         $m_max1 = ${$var_name};
  871:         if($#set_scores >= 3) { # at least 4 sets
  872:           $midterm2 = eval $P_code{'midterm2'};
  873:           $var_name =  "$Prefix_name{'Exam_raw_max'}" . "3"; # for max possible scores, just pick a set
  874:           $m_max2 = ${$var_name};
  875:           if($#set_scores >= 5) { # at least 6 sets
  876:             $midterm3 = eval $P_code{'midterm3'};
  877:             $var_name =  "$Prefix_name{'Exam_raw_max'}" . "5"; # for max possible scores, just pick a set
  878:             $m_max3 = ${$var_name};
  879:             if($#set_scores == 6 ) { # the 7th set
  880:                                      # in capa.config a variable $final_exam is defined as 
  881:                                      #    the same with $exam_raw7
  882:               ## $var_name =  "$Prefix_name{'Exam_raw_scores'}" . "7";
  883:               ## $f_score = ${$var_name};
  884:               $f_score = eval $P_code{'final_exam'};
  885:               $var_name =  "$Prefix_name{'Exam_raw_max'}" . "7";
  886:               $f_max = ${$var_name};
  887:             } else { # only 6 sets
  888:               
  889:             }
  890:           } else { # 4 or 5 sets
  891:             
  892:           }
  893:         } else { # 2 or 3 sets
  894:           
  895:         }
  896:     } else { # 0 or 1 sets
  897:       
  898:     }  
  899:     return ($midterm1,$m_max1,$midterm2,$m_max2,$midterm3,$m_max3,$f_score,$f_max,$#set_scores+1);
  900:    
  901:  }
  902: 
  903: #
  904: # Menu item: capastat
  905: #     ($Q_cnt,$L_cnt) =  &S_ScanSetDB($Sfullpath);
  906: #     Percentage_Scores($Set);
  907: #     S_Average($Q_cnt,$L_cnt);
  908: #
  909: # INPUT: the setX.db file name with full path 
  910: #    
  911: sub S_ScanSetDB  {
  912:     local($filename)=@_;
  913:     local($line_cnt)=0;
  914:     local($valid_cnt)=0;
  915:     local($valid);
  916:     local($ii);
  917:     local($s_num,$ans_str,$prefix,$rest);
  918:     local(@ans_char,@tries);
  919:     local($score);
  920:     
  921:     for($ii=0;$ii<=99;$ii++) {
  922:         $Total_try[$ii]=0;
  923: 	$Yes_cnt[$ii]=0;
  924: 	$yes_cnt[$ii]=0;
  925:         for($jj=0;$jj<=99;$jj++) {
  926: 	    $Student_cnt[$ii][$jj]=0;
  927:             $Student_try[$ii][$jj]=0;
  928: 	}
  929:     }
  930:     $Total_weight=0;
  931:     $Total_scores=0;
  932: 
  933:     open(IN, "<$filename") || die "Cannot open file $filename!";
  934:     while (<IN>) {
  935:       $line_cnt++;
  936:       if( $line_cnt == 2 ) {
  937:         chomp();
  938:         (@Weight) = split(/ */);
  939:       }
  940:       if( $line_cnt > 3) {
  941:         chomp();
  942:         ($prefix,$rest) = split(/,/,$_,2);
  943:         ($s_num,$ans_str) = split(/ /,$prefix,2);
  944:         (@ans_char) = split(/ */,$ans_str);
  945:         (@tries)    = split(/,/,$rest);
  946:         for($valid = 'N', $ii=0;$ii<=$#ans_char;$ii++) {
  947:             $valid = 'Y' if $ans_char[$ii] ne '-';
  948:         }
  949:         if( $valid eq 'Y' ) {
  950:           for($score=0,$ii=0;$ii<=$#tries;$ii++) {
  951:             $Student_cnt[$ii][$tries[$ii]]++;
  952:             $Student_try[$valid_cnt][$ii] = $tries[$ii];
  953:             $Total_try[$ii] += $tries[$ii];
  954:             $Total_weight   += $Weight[$ii];
  955:             if($ans_char[$ii] eq 'Y') {
  956:               $Yes_cnt[$ii]++;
  957:               $score += $Weight[$ii];
  958:             }
  959:             if($ans_char[$ii] eq 'y') {
  960:               $yes_cnt[$ii]++;
  961:               $score += $Weight[$ii];
  962:             }
  963:             if( $ans_char[$ii] ge '0' && $ans_char[$ii] le '9') {
  964:               $score += $ans_char[$ii];
  965:             }
  966:           }
  967:           $Total_scores += $score;
  968:           $Entry{"$valid_cnt"} = "$s_num\n" . "$ans_str," . " $rest\n";
  969:           $Score{"$valid_cnt"} = $score;
  970:           $valid_cnt++;
  971:         }
  972:       }
  973:     }
  974:     close(IN) || die "Cannot close $filename file!";
  975:     return ($#tries+1,$valid_cnt);
  976:  }
  977:  
  978:  
  979: $MAX_TRIES = 99;
  980: 
  981: sub  S_Average {
  982:     local($q_cnt,$l_cnt)=@_;
  983:     local($ii,$jj);
  984:     local(@s_cnt,@avg);
  985:     local(@sd, $sum);
  986:     local($sq);
  987:     local(@sd3,$tmp1,$tmp2,$done);
  988:     
  989:     for($ii=0;$ii<$q_cnt;$ii++) {
  990:       $s_cnt[$ii] = 0;
  991:       $avg[$ii]   = 0.0;
  992:       $Max_try[$ii] = 0;
  993:       for($jj=1;$jj<$MAX_TRIES;$jj++) {  # ignore the 0 try entry
  994:         if( $Student_cnt[$ii][$jj] > 0 ) {
  995:           $avg[$ii]   +=  $jj*$Student_cnt[$ii][$jj];
  996:           $s_cnt[$ii] +=  $Student_cnt[$ii][$jj];
  997:         }
  998:       }
  999:       if( $s_cnt[$ii] > 0 ) {      # avoid division by zero
 1000:         $avg[$ii] = $avg[$ii] / $s_cnt[$ii];
 1001:       }
 1002:     }
 1003:     
 1004:     for($ii=0;$ii<$q_cnt;$ii++) {
 1005:       $sd[$ii] = 0.0;
 1006:       $sum = 0.0;
 1007:       for($jj=0;$jj<$l_cnt;$jj++) {
 1008:         $Max_try[$ii] = ($Student_try[$jj][$ii] > $Max_try[$ii]? $Student_try[$jj][$ii] : $Max_try[$ii]);
 1009:         if( $Student_try[$jj][$ii] > 0 ) {
 1010:           $sq  = ($Student_try[$jj][$ii] - $avg[$ii])*($Student_try[$jj][$ii] - $avg[$ii]);
 1011:           $sum += $sq;
 1012:         }
 1013:         if( $s_cnt[$ii] > 1 ) {
 1014:           $sd[$ii] = $sum / ($s_cnt[$ii] - 1.0 );
 1015:         }
 1016:         if( $sd[$ii] > 0 ) {
 1017:           $sd[$ii] = sqrt( $sd[$ii] );
 1018:         }
 1019:       }
 1020:     }
 1021:     
 1022:     for($ii=0;$ii<$q_cnt;$ii++) {
 1023:       $sd3[$ii] = 0.0;
 1024:       $sum = 0.0;
 1025:       for($jj=0;$jj<$l_cnt;$jj++) {
 1026:         if( $Student_try[$jj][$ii] > 0 ) {
 1027:           $tmp1 = $Student_try[$jj][$ii] - $avg[$ii];
 1028:           $tmp2 = $tmp1*$tmp1*$tmp1;
 1029:           $sum  = $sum + $tmp2;
 1030:         }
 1031:         if( $s_cnt[$ii] > 0 && $sd[$ii] != 0.0 ) {
 1032:           $sd3[$ii] = $sum/$s_cnt[$ii] ;
 1033:           $sd3[$ii] = $sd3[$ii] / ($sd[$ii]*$sd[$ii]*$sd[$ii]);
 1034:         }
 1035:       }
 1036:     }
 1037:     
 1038:     print "This is the statistics for each problem:\n";
 1039:     print "Prob\#   MxTries   avg.     s.d.    s.k.   \#Stdnts ";
 1040:     print "  \#Yes   \#yes   Tries  DoDiff\n";
 1041:     for($ii=0;$ii<$q_cnt;$ii++) {
 1042:       if( $Total_try[$ii] > 0 ) {
 1043:         ## $dod = 1-($Yes_cnt[$ii] + $yes_cnt[$ii]) / $Total_try[$ii];
 1044:         $dod = $Total_try[$ii]/(0.1 + $Yes_cnt[$ii] + $yes_cnt[$ii]);
 1045:       }
 1046:       printf "P %2d:",$ii+1;
 1047:       printf "%7d  %8.2f  %7.2f  %6.2f   %5d   %5d  %5d    %5d   %5.1f\n",
 1048:               $Max_try[$ii],$avg[$ii],$sd[$ii],$sd3[$ii],$s_cnt[$ii],$Yes_cnt[$ii],$yes_cnt[$ii],
 1049:               $Total_try[$ii],$dod;
 1050:      
 1051:     }
 1052:     printf "Press RETURN to continue"; $done = <>;
 1053:   }
 1054:   
 1055: sub  Percentage_Scores {
 1056:     local($set,$valid_cnt)=@_;
 1057:     local($ratio);
 1058:     local($done);
 1059:     
 1060:     if($Total_weight > 0 ) {
 1061:       $ratio = $Total_scores / $Total_weight;
 1062:       $ratio = $ratio * 100.0;
 1063:     }
 1064:     
 1065:     printf "\nThe percentage score (total scores / total valid weights) for set%d.db is:\n %7.2f%%\n",$set,$ratio;
 1066:     printf "The number of valid records for set%d.db is: %d\n", $set, $valid_cnt;
 1067:     printf "Press RETURN to continue"; $done=<>;
 1068:   }
 1069: 
 1070: 
 1071: sub  Large_Tries {
 1072:     local($t,$n,$q_cnt,$l_cnt)=@_;
 1073:     local($ii);
 1074:     
 1075:     print "\nHere is a list of students who attempts $t tries more than $n times: \n\n";
 1076: 
 1077:     for ($i=0;$i<$l_cnt;$i++){
 1078:        $count=0;
 1079:        $credit=0;
 1080:        for ($j=0;$j<$q_cnt;$j++){
 1081:            if ($Student_try[$i][$j]>= $t){
 1082:               $count++;
 1083:            }
 1084:        }
 1085:        if ($count >= $n){
 1086:           print "($Score{$i})  $Entry{$i} \n";
 1087:        }
 1088:     }
 1089:   
 1090:   }
 1091: 
 1092: 
 1093: sub S_ScanLogDB  {
 1094:     local($filename)=@_;
 1095:     local($line_cnt)=0;
 1096:     local($s_num,$dow,$mon,$sp,$day,$time,$yr,$ans_str);
 1097:     local(@ans_char);
 1098:     local($ii,$first,$done);
 1099:     local($Yes_cnt[99],$No_cnt[99],$U_cnt[99],$S_cnt[99],$u_cnt[99]);
 1100:     local($Y_total,$N_total,$U_total,$u_total,$S_total);
 1101:     
 1102:     $Y_total=0; $N_total=0; $U_total=0; $u_total=0; $S_total=0;
 1103:     open(IN, "<$filename") || die "Cannot open file $filename!";
 1104:     for($ii=0;$ii<=99;$ii++) {
 1105:       $Yes_cnt[$ii] = 0; $No_cnt[$ii] = 0; 
 1106:       $U_cnt[$ii]=0; $u_cnt[$ii]=0; $S_cnt[$ii]=0;
 1107:     }
 1108:     while (<IN>) {
 1109:       $line_cnt++;
 1110:       chomp();
 1111:       $ans_str = substr($_,35);
 1112:       ## ($first,$ans_str) = split(/1996 /);  # depends on this special pattern
 1113:        # print "$ans_str\n";
 1114:       (@ans_char) = split(/ */,$ans_str);
 1115:        
 1116:        for($ii=0;$ii<=$#ans_char;$ii++) {
 1117:        
 1118:          if($ans_char[$ii] eq 'Y') {
 1119:            $Yes_cnt[$ii]++;
 1120:            $Y_total++;
 1121:          }
 1122:          if($ans_char[$ii] eq 'N') {
 1123:            $No_cnt[$ii]++;
 1124:            $N_total++;
 1125:          }
 1126:          if($ans_char[$ii] eq 'U') {
 1127:            $U_cnt[$ii]++;
 1128:            $U_total++;
 1129:          }
 1130:          if($ans_char[$ii] eq 'u') {
 1131:            $u_cnt[$ii]++;
 1132:            $u_total++;
 1133:          }
 1134:          if($ans_char[$ii] eq 'S') {
 1135:            $S_cnt[$ii]++;
 1136:            $S_total++;
 1137:          }
 1138:        }
 1139:      }
 1140:      close(IN) || die "Cannot close file $filename!";
 1141:      print "Prob #:     #Y     #N     #S     #U     #u\n";
 1142:      for($ii=0;$ii<=$#ans_char;$ii++) {
 1143:        printf "    %2d: %6d %6d %6d %6d %6d\n",
 1144:               $ii+1, $Yes_cnt[$ii], $No_cnt[$ii], $S_cnt[$ii], $U_cnt[$ii], $u_cnt[$ii];
 1145:      }
 1146:      print  "=" x 45 . "\n";
 1147:      printf  " Total: %6d %6d %6d %6d %6d\n", $Y_total, $N_total, $S_total, $U_total, $u_total;
 1148:      printf "Press RETURN to continue"; $done = <>;
 1149:      
 1150:      return ($Y_total,$N_total,$S_total,$U_total,$u_total);
 1151:   }
 1152: 
 1153: sub S_StudentLoginData {
 1154:     local($filename,$student_id)=@_;
 1155:     local($Y_total,$N_total,$S_total,$U_total,$u_total);
 1156:     local($ans_str,@ans_char);
 1157:     local($ii,$s_id);
 1158:     
 1159:     $Y_total=0; $N_total=0; $U_total=0; $u_total=0; $S_total=0;
 1160:     open(IN, "<$filename") || die "Cannot open file $filename!";
 1161:     while (<IN>) { 
 1162:       chomp();
 1163:       $s_id = substr($_,0,9); # student number begins at column 0 and has 9 chars
 1164:       $s_id = uc($s_id);
 1165:       $student_id = uc($student_id);
 1166:       if( $student_id eq $s_id) {
 1167:         $ans_str = substr($_,35);    # the answer string begins at column 35 (count from 0)
 1168:         (@ans_char) = split(/ */,$ans_str);
 1169:         for($ii=0;$ii<=$#ans_char;$ii++) {
 1170:           if($ans_char[$ii] eq 'Y') {
 1171:             $Y_total++;
 1172:           }
 1173:           if($ans_char[$ii] eq 'N') {
 1174:             $N_total++;
 1175:           }
 1176:           if($ans_char[$ii] eq 'U') {
 1177:             $U_total++;
 1178:           }
 1179:           if($ans_char[$ii] eq 'u') {
 1180:             $u_total++;
 1181:           }
 1182:           if($ans_char[$ii] eq 'S') {
 1183:             $S_total++;
 1184:           }
 1185:         } # end for each problem
 1186:       } # end student number matches
 1187:     } # end while 
 1188:     close(IN) || die "Cannot close file $filename!";
 1189:     return ($Y_total,$N_total,$S_total,$U_total,$u_total);
 1190:  }
 1191: 
 1192: # 
 1193: sub S_LoginAnalysis {
 1194:     local($classpath,$student_id,$s_limit)=@_;
 1195:     local($Y_set,$N_set,$S_set,$U_set,$u_set);
 1196:     local($set_idx,$no_log,$no_weblog,$done,$tmp);
 1197:     
 1198:     print "Login analysis:  telnet session             web session\n\n";
 1199:     print "   set #:   #Y   #N   #S   #U   #u     #Y   #N   #S   #U   #u\n";
 1200:     $set_idx=0; $done=0;
 1201:     while (! $done ) {
 1202:       $set_idx++;
 1203:       if( $set_idx <= $s_limit) {
 1204:         printf "      %2d: ", $set_idx;
 1205:         $filename= $classpath . "/records/log" . "$set_idx" . ".db";
 1206:         if(-f $filename) {
 1207:           ($Y_set,$N_set,$S_set,$U_set,$u_set)=S_StudentLoginData($filename,$student_id);
 1208:           printf "%4d %4d %4d %4d %4d", $Y_set,$N_set,$S_set,$U_set,$u_set;
 1209:           $no_log = 0;
 1210:         } else {
 1211:           print  "=" x 24;
 1212:           $no_log = 1;
 1213:         }
 1214:         print " " x 4;
 1215:         $filename= $classpath . "/records/weblog" . "$set_idx" . ".db";
 1216:         if(-f $filename) {
 1217:           ($Y_set,$N_set,$S_set,$U_set,$u_set)= S_StudentLoginData($filename,$student_id);
 1218:           printf "%4d %4d %4d %4d %4d", $Y_set,$N_set,$S_set,$U_set,$u_set;
 1219:           $no_weblog = 0;
 1220:         } else {
 1221:           print  "=" x 24;
 1222:           $no_weblog = 1;
 1223:         }
 1224:         print "\n";
 1225:         if( $no_log && $no_weblog ) { $done = 1; };
 1226:       } else { # $set_idx > $s_limit
 1227:         $done = 1;
 1228:       }
 1229:     }
 1230:     
 1231:     printf "Press RETURN to continue"; $tmp = <>;
 1232:  }
 1233: 
 1234: # It pulls out the data base entry from setX.db files
 1235: sub S_StudentSetAnalysis {
 1236:     local($classpath,$student_id,$s_limit)=@_;
 1237:     local($filename);
 1238:     local($set_idx,$done);
 1239:     local($line_cnt,$found,$input_line);
 1240:     local($s_num,$data,$ans_str,$try_str,$tmp);
 1241:     
 1242:     $set_idx=0; $student_id = uc($student_id);
 1243:     print " set #:\n";
 1244:     while(! $done) {
 1245:       $set_idx++;
 1246:       if( $set_idx <= $s_limit) {
 1247:         $filename= $classpath . "/records/set" . "$set_idx" . ".db";
 1248:         if(-f $filename) {
 1249:           printf "    %2d: ", $set_idx;
 1250:           open(IN, "<$filename") || die "Cannot open file $filename!";
 1251:           $line_cnt=0; $found = 0;  # for each file opened, initialize $line_cnt and $found
 1252:           while ( ($input_line = <IN>) && !$found) { 
 1253:             $line_cnt++;       # add one to line count
 1254:             if( $line_cnt > 3) {    # start from line 4 is the student data
 1255:               chomp($input_line);              # delete the trailing '\n' char
 1256:               $s_num = substr($input_line,0,9);
 1257:               $s_num = uc($s_num);
 1258:               if( $student_id eq $s_num ) {
 1259:                 $found = 1; 
 1260:                 $data = substr($input_line,10);
 1261:                 ($ans_str,$try_str) = split(/,/,$data,2);
 1262:                 print "$ans_str\n          $try_str\n";
 1263:               }
 1264:             } # $line_cnt > 3 
 1265:           } # end while $input_line and ! $found
 1266:           close(IN) || die "Cannot close file $filename!";
 1267:           if(! $found ) {  # student record entry not in the setX.db file ==> two empty lines
 1268:             print "\n\n";  
 1269:           }
 1270:         } else { # $filename does not exist
 1271:           $done = 1; 
 1272:         }
 1273:       } else { # $set_idx > $s_limit
 1274:         $done = 1; 
 1275:       }
 1276:     }
 1277:     printf "Press RETURN to continue"; $tmp = <>;
 1278:  }
 1279: 
 1280: #
 1281: # INPUTS: class name with full path, set number
 1282: #
 1283: sub S_ItemAnalysis {
 1284:     local($classpath,$set)=@_;
 1285:     local($filename,$found,$classname);
 1286:     local($done)=0;
 1287:     local($line_cnt,$input_line);
 1288:     local($valid,$valid_cnt);
 1289:     local(@weights);       # the wights array for individual set
 1290:     local($valid_weights); # the valid weights for a set
 1291:     local($total_weights); # the overall valid weights for all sets
 1292:     local(@set_weights);   # the array storing all set valid weights
 1293:     local($score);         # the valid score for a set
 1294:     local($total_scores);  # the overall valid scores for all sets
 1295:     local(@set_scores);    # the array storing all set scores
 1296:     local($diff,$disc);
 1297:     local($ii,$upper_percent,$lower_percent);
 1298:     local($s_num,$ans_str,$prefix,$rest);
 1299:     local(@ans_char,$ratio,$tmp);
 1300:     local($Y_cnt[100],$N_cnt[100]);
 1301:     local($Ycnt_upper[100],$Ycnt_lower[100],$tmp_total);
 1302:     local($Y_total,$N_total);
 1303:     local($upperpart_cnt,$lowerpart_limit);
 1304:     local(%s_db);
 1305:     
 1306:     $total_scores = 0; $total_weights = 0; # initialize the overall results
 1307:     $upper_percent = 0.0; $lower_percent = 0.0;
 1308:     
 1309:     for($ii=0;$ii<100;$ii++) {
 1310:       $Y_cnt[$ii] = 0; $N_cnt[$ii] = 0; $Ycnt_upper[$ii] = 0.0; $Ycnt_lower[$ii] = 0.0;
 1311:     }
 1312:     $filename = $classpath . "/records/set" . "$set" . ".db";
 1313:     if( -f $filename) { # file exists!
 1314:       open(IN, "<$filename") || die "Cannot open file $filename!";
 1315:       $valid_cnt = 0; # initialize $valid_cnt
 1316:       $line_cnt=0;    # initialize $line_cnt
 1317:       while (<IN>) {
 1318:         $line_cnt++;       # add one to line count
 1319:         if( $line_cnt == 2 ) { # second line is the weight for each problem
 1320:           chomp();             # delete the trailing '\n' char
 1321:           (@weights) = split(/ */);  # split the line into each individual chars
 1322:           $valid_weights = 0;
 1323:           for($ii=0;$ii<=$#weights;$ii++) {
 1324:             $valid_weights += $weights[$ii];  # for now $valid_weights contains the sum
 1325:           }
 1326:         }
 1327:         if( $line_cnt > 3) {    # start from line 4 is the student data
 1328:           chomp();              # delete the trailing '\n' char
 1329:           ($prefix,$rest) = split(/,/,$_,2);        # split the whole line into two parts
 1330:           ($s_num,$ans_str) = split(/ /,$prefix,2); # split into two parts
 1331:           $s_num = uc($s_num);
 1332:           
 1333:           ### print "$ans_str\n";
 1334:           (@ans_char) = split(/ */,$ans_str);  # split the answer string into in dividual ans chars
 1335:           for($valid = 'N', $ii=0;$ii<=$#ans_char;$ii++) {  # from question 0, 1 , to last question -1
 1336:               $valid = 'Y' if $ans_char[$ii] ne '-';          # if ans char is different from '-', then
 1337:           }
 1338:           if( $valid eq 'Y' ) {   # don't bother with the record full of '-'s
 1339:             $valid_cnt++;
 1340:             for($score=0,$ii=0;$ii<=$#ans_char;$ii++) {  # initialize $score and index $ii
 1341: 
 1342:               if($ans_char[$ii] eq 'Y' || $ans_char[$ii] eq 'y') {
 1343:                 $score += $weights[$ii];
 1344:                 $Y_cnt[$ii]++;
 1345:                 $Y_total++;
 1346:               }
 1347:               if( $ans_char[$ii] eq 'N' || $ans_char[$ii] eq 'n' || 
 1348:                   $ans_char[$ii] eq '0' ) {
 1349:                 $N_cnt[$ii]++;
 1350:                 $N_total++;
 1351:               }
 1352:               if( $ans_char[$ii] gt '0' && $ans_char[$ii] le '9') {
 1353:                 $score += $ans_char[$ii];
 1354:                 if( $ans_char[$ii] eq $weights[$ii] ) {
 1355:                   $Y_cnt[$ii]++;
 1356:                   $Y_total++;
 1357:                 } else {
 1358:                   $N_cnt[$ii]++;
 1359:                   $N_total++;
 1360:                 }
 1361:               } 
 1362:               if($ans_char[$ii] eq 'E') { # subtract the weight from execused problem
 1363:                 $valid_weights -= $weights[$ii];
 1364:               }
 1365:             } # end of score calculation
 1366:             
 1367:             $sort_key = sprintf "%05d%s", $score,$s_num;
 1368:             $s_db{$sort_key} = "$ans_str";
 1369:             
 1370:           }
 1371:         }   # end $line_cnt > 3
 1372:       } # end while <IN>
 1373:       close(IN) || die "Cannot close file $filename!";
 1374: 
 1375:       for($ii=0;$ii<100;$ii++) {
 1376:         ## $Y_cnt[$ii]=0; $N_cnt[$ii]=0; 
 1377:         $Ycnt_upper[$ii] = 0; $Ycnt_lower[$ii] = 0;
 1378:       }
 1379:       $upperpart_cnt   = int(0.27 * $valid_cnt);   # upper  27 percent
 1380:       $lowerpart_limit = ($valid_cnt - $upperpart_cnt);   # beyond 73 percent
 1381:       ### open(DBUG, ">/tmp/f.DBUG") || die "Cannot open /tmp/f.DBUG file!";
 1382:       $line_cnt=0;
 1383:       foreach $sort_key (reverse sort keys %s_db) {
 1384:         $line_cnt++;
 1385:         ### print DBUG "$sort_key\[$s_db{$sort_key}\]";
 1386:         
 1387:         $ans_str = "$s_db{$sort_key}";
 1388:         (@ans_char) = split(/ */,$ans_str);
 1389:         for($ii=0;$ii<=$#ans_char;$ii++) {
 1390:           if( ($ans_char[$ii] eq 'Y') || ($ans_char[$ii] eq 'y') ||
 1391:               ($ans_char[$ii] eq $weights[$ii] ) ) {  ## only if they got a full credit that 
 1392:                                                       ## we count it as 'Y'
 1393:            
 1394:               if($line_cnt <= $upperpart_cnt) {
 1395:                 $Ycnt_upper[$ii]++;
 1396:               } elsif ( $line_cnt > $lowerpart_limit ) {
 1397:                 $Ycnt_lower[$ii]++;
 1398:               }
 1399:           }
 1400:           ### print DBUG " $Ycnt_upper[$ii]/$Ycnt_lower[$ii] ";
 1401:         } # end for $ii
 1402:         ### print DBUG "\n";
 1403:       } #end foreach
 1404:       ### close(DBUG);
 1405:       print " There are $valid_cnt entries in file $filename\n";
 1406:       printf "  The upper 27%% has %d records, the lower 27%% has %d records\n", $upperpart_cnt, $valid_cnt-$lowerpart_limit;
 1407:       print " question \#     DoDiff.      Disc. Factor (%upper - %lower) [#records,#records]\n";
 1408:       
 1409:       for($ii=0;$ii<=$#ans_char;$ii++) {
 1410:         $tmp_total = $N_cnt[$ii] + $Y_cnt[$ii];
 1411:         if( $tmp_total > 0 ) {
 1412:           $diff = 100.0*($N_cnt[$ii] / ($N_cnt[$ii] + $Y_cnt[$ii]));
 1413:         } else {
 1414:           $diff = '-';
 1415:         }
 1416:         $upper_percent =  100.0 * ($Ycnt_upper[$ii] /$upperpart_cnt);
 1417:         $lower_percent =  100.0 * ($Ycnt_lower[$ii] /$upperpart_cnt);
 1418:         $disc = $upper_percent  - $lower_percent;
 1419:         printf "         %2d:    ", $ii+1;
 1420:         printf "%6.1f         %5.1f (% 5.1f - % 5.1f) [%4d,%4d]\n", 
 1421:                 $diff, $disc,$upper_percent,$lower_percent,$Ycnt_upper[$ii],$Ycnt_lower[$ii];
 1422:       }
 1423:       printf "Press RETURN to continue"; $tmp = <>;
 1424:     } else { # file does not exist!
 1425:       print "FILE: $filename does not exist!\n";
 1426:       printf "Press RETURN to continue"; $tmp = <>;
 1427:     }
 1428:  }
 1429: 
 1430: #
 1431: # INPUTS: class name with full path, set number
 1432: #
 1433: sub  S_SetCorrelation {
 1434:      local($classpath,$set)=@_;
 1435:      local($filename);
 1436:      local($line_cnt);
 1437:      local($data,$ans_str,@ans_char,$try_str);
 1438:      local($ii,$jj,$question_cnt);
 1439:      local($index_key,@weights);
 1440:      local($first_char,$second_char);
 1441:      local(%corr,%valid_cnt,$ratio,$tmp,$tmp_len,$ratio_str);
 1442:      
 1443:      $filename= $classpath . "/records/set" . "$set" . ".db";
 1444: 
 1445:      if(-f $filename) {
 1446:        open(IN, "<$filename") || die "Cannot open file $filename!";
 1447:        $line_cnt=0;
 1448:        while(<IN>) {
 1449:          $line_cnt++;
 1450:          if( $line_cnt == 2 ) { # second line is the weight for each problem
 1451:           chomp();             # delete the trailing '\n' char
 1452:           (@weights) = split(/ */);  # split the line into each individual chars
 1453:         }
 1454:          if( $line_cnt > 3) {
 1455:            chomp();
 1456:            $data = substr($_,10);  # skip over student number
 1457:            ($ans_str,$try_str) = split(/,/,$data,2);
 1458:            (@ans_char) = split(/ */,$ans_str);
 1459:            $question_cnt = $#ans_char;
 1460:            for($ii=0;$ii<$#ans_char;$ii++) {
 1461:              for($jj=$ii+1;$jj<=$#ans_char;$jj++) {
 1462:                $index_key = "$ii" . "$jj";
 1463:                if( $ans_char[$ii] eq '-' || $ans_char[$ii] eq 'E' ) {
 1464:                  # do nothing
 1465:                } else {
 1466:                  if( $ans_char[$ii] gt '0' && $ans_char[$ii] le '9') {
 1467:                    if( $ans_char[$ii] eq $weights[$ii] ) {
 1468:                      $first_char = 'Y';
 1469:                    } else {
 1470:                      $first_char = 'N';
 1471:                    }
 1472:                  } elsif ($ans_char[$ii] eq '0') {
 1473:                    $first_char = 'N';
 1474:                  } elsif ($ans_char[$ii] eq 'y' || $ans_char[$ii] eq 'n') {
 1475:                    $first_char = uc($ans_char[$ii]);
 1476:                  } else {
 1477:                    $first_char = $ans_char[$ii];
 1478:                  }
 1479:                  if( $ans_char[$jj] eq '-' || $ans_char[$jj] eq 'E' ) {
 1480:                    # do nothing
 1481:                  } else {
 1482:                    if( $ans_char[$jj] gt '0' && $ans_char[$jj] le '9') {
 1483:                      if( $ans_char[$jj] eq $weights[$jj] ) {
 1484:                        $second_char = 'Y';
 1485:                      } else {
 1486:                        $second_char = 'N';
 1487:                      }
 1488:                    } elsif ($ans_char[$jj] eq '0') {
 1489:                      $second_char = 'N';
 1490:                    } elsif ($ans_char[$jj] eq 'y' || $ans_char[$jj] eq 'n') {
 1491:                      $second_char = uc($ans_char[$jj]);
 1492:                    } else {
 1493:                      $second_char = $ans_char[$jj];
 1494:                    }
 1495:                    if( $first_char eq $second_char ) {
 1496:                      if(defined $corr{$index_key} ) {
 1497:                        $corr{$index_key}++;
 1498:                      } else {
 1499:                        $corr{$index_key} = 1;
 1500:                      }
 1501:                    } else {
 1502:                      if(defined $corr{$index_key} ) {
 1503:                        $corr{$index_key}--;
 1504:                      } else {
 1505:                        $corr{$index_key} = -1;
 1506:                      }
 1507:                    }
 1508:                    # add one count to valid_count
 1509:                    if(defined $valid_cnt{$index_key}) {
 1510:                      $valid_cnt{$index_key}++;
 1511:                    } else {
 1512:                      $valid_cnt{$index_key} = 1;
 1513:                    }
 1514:                  }
 1515:                }
 1516:              } # end $jj loop
 1517:            } # end $ii loop
 1518:          } # end $line_cnt > 3
 1519:        } # end while <IN>
 1520:        close(IN) || die "Cannot close file $filename!";
 1521:        # print out the correlation matrix
 1522:        
 1523:        print "  ";
 1524:        for($ii=1;$ii<=$question_cnt;$ii++) {
 1525:          print " " x 4;
 1526:          printf "%2d", $ii+1;
 1527:        }
 1528:        print "\n";
 1529:        # --------------------------------------
 1530:        for($ii=0;$ii<$question_cnt;$ii++) {
 1531:          printf " %2d:", $ii+1;
 1532:          print  "      " x ($ii);
 1533:          for($jj=$ii+1;$jj<=$question_cnt;$jj++) {
 1534:            $index_key = "$ii" . "$jj";
 1535:            if( defined $corr{$index_key} ) {
 1536:              $ratio = $corr{$index_key} / $valid_cnt{$index_key};
 1537:              printf " % .2f", $ratio;
 1538:            } else {
 1539:              print "  ----";
 1540:            }
 1541:          }
 1542:          print "\n";
 1543:        }
 1544:        printf "Press RETURN to continue"; $tmp = <>;
 1545:      } else { # file exists!
 1546:        print "FILE: $filename does not exist!\n";
 1547:        printf "Press RETURN to continue"; $tmp = <>;
 1548:      }
 1549:  }
 1550: 
 1551: # INPUTS: class name with full path, set number
 1552: #
 1553: # r = \frac{\sum{x_i y_i} - \frac{(\sum x_i)(\sum y_i)}{n}}{\sqrt{(\sum x_i^2 - \frac{}{}}}
 1554: #
 1555: # corr = (sum of prod_xy - (sum_x*sum_y / n) ) / sqrt( (sum of sqr_x - (sum_x*sum_x/n))*
 1556: # 
 1557: sub  S_SetCorrelationStatistics {
 1558:      local($classpath,$set)=@_;
 1559:      local($filename);
 1560:      local($line_cnt);
 1561:      local($data,$ans_str,@ans_char,$try_str);
 1562:      local($ii,$jj,$question_cnt);
 1563:      local($index_key,@weights);
 1564:      local($x_data,$y_data);
 1565:      local(%valid_cnt,$ratio,$tmp,$tmp_len,$ratio_str);
 1566:      local(%prod_xy,%sum_x,%sum_y,%sum_x2,%sum_y2);
 1567:      local($upper_part,$lower_part);
 1568:      
 1569:      $filename= $classpath . "/records/set" . "$set" . ".db";
 1570: 
 1571:      if(-f $filename) {
 1572:        open(IN, "<$filename") || die "Cannot open file $filename!";
 1573:        $line_cnt=0;
 1574:        while(<IN>) {
 1575:          $line_cnt++;
 1576:          if( $line_cnt == 2 ) { # second line is the weight for each problem
 1577:           chomp();             # delete the trailing '\n' char
 1578:           (@weights) = split(/ */);  # split the line into each individual chars
 1579:         }
 1580:          if( $line_cnt > 3) {
 1581:            chomp();
 1582:            $data = substr($_,10);  # skip over student number
 1583:            ($ans_str,$try_str) = split(/,/,$data,2);
 1584:            (@ans_char) = split(/ */,$ans_str);
 1585:            $question_cnt = $#ans_char;
 1586:            for($ii=0;$ii<$#ans_char;$ii++) {
 1587:              for($jj=$ii+1;$jj<=$#ans_char;$jj++) {
 1588:                $index_key = "$ii" . "$jj";
 1589:                if( $ans_char[$ii] eq '-' || $ans_char[$ii] eq 'E' ) {
 1590:                  # do nothing
 1591:                } else { ## $ans_char[$ii] is one of 0 .. 9, Y, y, N, n
 1592:                  if( $ans_char[$jj] eq '-' || $ans_char[$jj] eq 'E' ) {
 1593:                    # do nothing
 1594:                  } else {
 1595:                    if( $ans_char[$ii] eq 'Y' || $ans_char[$ii] eq 'y' ) {
 1596:                      $x_data = $weights[$ii];
 1597:                    } elsif ( $ans_char[$ii] eq 'N' || $ans_char[$ii] eq 'n' ) {
 1598:                      $x_data = 0;
 1599:                    } else { ## must be in 0 .. 9
 1600:                      $x_data = $ans_char[$ii];
 1601:                    }
 1602:                    if( $ans_char[$jj] eq 'Y' || $ans_char[$jj] eq 'y' ) {
 1603:                      $y_data = $weights[$jj];
 1604:                    } elsif ( $ans_char[$jj] eq 'N' || $ans_char[$jj] eq 'n' ) {
 1605:                      $y_data = 0;
 1606:                    } else { ## must be in 0 .. 9
 1607:                      $y_data = $ans_char[$jj];
 1608:                    }
 1609:                    if(defined $prod_xy{$index_key}) {
 1610: 		     if ( $ii == 3 && $jj == 7 ) {
 1611: 		       printf "%f %f %f\n",$x_data,$y_data,$prod_xy{$index_key}
 1612: 		     }
 1613:                      $prod_xy{$index_key} += ($x_data * $y_data);
 1614: 		   } else {
 1615: 		     if ( $ii == 3 && $jj == 7 ) {
 1616: 		       printf "%f %f %f\n",$x_data,$y_data,0.0
 1617: 		     }
 1618:                      $prod_xy{$index_key} = 0.0;
 1619:                    }
 1620:                    if(defined $sum_x{$index_key} ) {
 1621:                      $sum_x{$index_key}  += $x_data;
 1622:                    } else {
 1623:                      $sum_x{$index_key} = 0;
 1624:                    }
 1625:                    if(defined $sum_y{$index_key}) {
 1626:                      $sum_y{$index_key}  += $y_data;
 1627:                    } else {
 1628:                      $sum_y{$index_key} = 0;
 1629:                    }
 1630:                    if(defined $sum_x2{$index_key} ) {
 1631:                      $sum_x2{$index_key} += ($x_data * $x_data);
 1632:                    } else {
 1633:                      $sum_x2{$index_key} = 0;
 1634:                    }
 1635:                    if(defined $sum_y2{$index_key} ) {
 1636:                      $sum_y2{$index_key} += ($y_data * $y_data);
 1637:                    } else {
 1638:                      $sum_y2{$index_key} = 0;
 1639:                    }
 1640:                    # add one count to valid_count
 1641:                    if(defined $valid_cnt{$index_key}) {
 1642:                      $valid_cnt{$index_key}++;
 1643:                    } else {
 1644:                      $valid_cnt{$index_key} = 1;
 1645:                    }
 1646:                  }
 1647:                }
 1648:              } # end $jj loop
 1649:            } # end $ii loop
 1650:          } # end $line_cnt > 3
 1651:        } # end while <IN>
 1652:        close(IN) || die "Cannot close file $filename!";
 1653:        # print out the correlation matrix
 1654:        
 1655:        print "  ";
 1656:        for($ii=1;$ii<=$question_cnt;$ii++) {
 1657:          print " " x 4;
 1658:          printf "%2d", $ii+1;
 1659:        }
 1660:        print "\n";
 1661:        # --------------------------------------
 1662:        for($ii=0;$ii<$question_cnt;$ii++) {
 1663:          printf " %2d:", $ii+1;
 1664:          print  "      " x ($ii);
 1665:          for($jj=$ii+1;$jj<=$question_cnt;$jj++) {
 1666:            $index_key = "$ii" . "$jj";
 1667:            if( defined  $valid_cnt{$index_key}) {  ## there are at least one valid data
 1668:              $upper_part = $prod_xy{$index_key} - ( ($sum_x{$index_key} * $sum_y{$index_key}) / $valid_cnt{$index_key} );
 1669:              $lower_part = $sum_x2{$index_key} - ($sum_x{$index_key} * $sum_x{$index_key} / $valid_cnt{$index_key} );
 1670:              $lower_part = $lower_part * ( $sum_y2{$index_key} - ($sum_y{$index_key} * $sum_y{$index_key} / $valid_cnt{$index_key} ));
 1671:              $lower_part = sqrt($lower_part);
 1672: 	     if ($ii == 3 && $jj==7) {
 1673: 	       printf "%f %f %f %f",$prod_xy{$index_key},$sum_x{$index_key},$sum_y{$index_key},$valid_cnt{$index_key};
 1674: 	     }
 1675:              if( $lower_part != 0.0 ) {
 1676:                $ratio = $upper_part / $lower_part;
 1677:                printf " % .2f", $ratio;
 1678:              } else {
 1679:                print "  inf ";
 1680:              }
 1681:            } else {
 1682:              print "  ----";
 1683:            }
 1684:          }
 1685:          print "\n";
 1686:        }
 1687:        printf "Press RETURN to continue"; $tmp = <>;
 1688:      } else { # file exists!
 1689:        print "FILE: $filename does not exist!\n";
 1690:        print "Press RETURN to continue"; $tmp = <>;
 1691:      }
 1692:  }
 1693: # -------------------------- Create a file contains all scores ----
 1694: #   it will 
 1695: sub   S_CreateScores {
 1696:      local($sfilename)=@_;
 1697:      local($filename);
 1698:      local($c_scores,$c_max_scores,$c_abscent,$c_count,$c_summary);
 1699:      local($q_scores,$q_max_scores,$q_abscent,$q_count,$q_summary);
 1700:      local($e_scores,$e_max_scores,$e_abscent,$e_count,$e_summary);
 1701:      local($s_scores,$s_max_scores,$s_abscent,$s_count,$s_summary);
 1702:      local($o_scores,$o_max_scores,$o_abscent,$o_count,$o_summary);
 1703:      local($c_ratio,$q_ratio,$e_ratio,$s_ratio,$o_ratio);
 1704:      local($m_1,$m_2,$m_3,$f_score);
 1705:      local($m_max1,$m_max2,$m_max3,$f_max);
 1706:      local($mt1_ratio,$mt2_ratio,$mt3_ratio,$f_ratio);
 1707:      local($var_name);
 1708:      local($total_score,$day_str);
 1709:      local($e_entry);
 1710:      local($s_key,%f_entry,$pre_entry,$whole_entry,$tail_entry,$score_str);
 1711:      local($tmp);
 1712:     
 1713:     $day_str = &TimeString;
 1714:     
 1715:     $filename = "$ClassPath" . "/classl";
 1716:     
 1717:     open(CIN, "<$filename") || die "Cannot open file $filename!";
 1718:     while(<CIN>) {
 1719:       chomp();
 1720:       $s_id = substr($_,14,9); $s_id = uc($s_id);
 1721:       
 1722:       # $homework_scores_limit_set comes from capa.config
 1723:       ($c_scores,$c_max_scores,$c_abscent,$c_count,$c_summary) =
 1724:        &S_CollectSetScores($ClassPath,"$s_id",0,$homework_scores_limit_set);
 1725:        
 1726:       $c_ratio = $c_scores/$c_max_scores if $c_max_scores != 0;
 1727:       ## print "$c_scores/$c_max_scores,$c_abscent/$c_count\n";
 1728:       
 1729:       # $quiz_scores_limit_set comes from capa.config
 1730:       ($q_scores,$q_max_scores,$q_abscent,$q_count,$q_summary) = 
 1731:         &S_CollectSetScores($QuizPath,"$s_id",0,$quiz_scores_limit_set) if -d "$QuizPath";
 1732:       $q_ratio = $q_scores/$q_max_scores if $q_max_scores != 0;
 1733:       ## print "$q_scores/$q_max_scores,$q_abscent/$q_count\n";
 1734:       
 1735:       # exam has different formula
 1736:       
 1737:       ($m_1,$m_max1,$m_2,$m_max2,$m_3,$m_max3,$f_score,$f_max,$e_count) = S_CollectExamScores($ExamPath,"$s_id") if -d "$ExamPath";
 1738:       
 1739:       # we can evaluate midterm1 when $e_count > 2 
 1740:       #  the perl code for midterm1 is stored in $P_code{'midterm1'}
 1741:       # FORMULA for exam scores
 1742:       if( $e_count < 1 ) { # 0  set, no extrapolated scores
 1743:         $e_scores = 0;
 1744:         $tail_entry = "     --/--      --/--      --/--   --/--    ";
 1745:                       
 1746:       } elsif ( ($e_count == 1) || ($e_count == 2) ) {
 1747:         $mt1_ratio = $m_1/$m_max1 if $m_max1 != 0;
 1748:         $e_scores = $mt1_percent * $mt1_ratio + $mt2_percent*$mt1_ratio + $mt3_percent * $mt1_ratio + $final_percent * $mt1_ratio;
 1749:         $e_entry = sprintf " %6.2f/%3d     --/--      --/--   --/--    ", $m_1,$m_max1;
 1750:       } elsif ( ($e_count == 3) || ($e_count == 4) ) {
 1751:         $mt1_ratio = $m_1/$m_max1 if $m_max1 != 0;
 1752:         $mt2_ratio = $m_2/$m_max2 if $m_max2 != 0;
 1753:         $e_scores = $mt1_percent * $mt1_ratio + $mt2_percent*$mt2_ratio + 
 1754:                     (($mt1_percent * $mt1_ratio + $mt2_percent*$mt2_ratio)*0.5) + 
 1755:                     $final_percent * (($m_1 + $m_2)/($m_max1 + $m_max2));
 1756:         $e_entry = sprintf " %6.2f/%3d %6.2f/%3d     --/--   --/--    ", $m_1,$m_max1,$m_2,$m_max2;
 1757:       } elsif ( ($e_count == 5) || ($e_count == 6) ) {
 1758:         $mt1_ratio = $m_1/$m_max1 if $m_max1 != 0;
 1759:         $mt2_ratio = $m_2/$m_max2 if $m_max2 != 0;
 1760:         $mt3_ratio = $m_3/$m_max3 if $m_max3 != 0;
 1761:         $e_scores = $mt1_percent * $mt1_ratio + $mt2_percent*$mt2_ratio + $mt3_percent * $mt3_ratio +
 1762:                     $final_percent * (($m_1+$m_2+$m_3)/($m_max1+$m_max2+$m_max3));
 1763:         $e_entry = sprintf " %6.2f/%3d %6.2f/%3d %6.2f/%3d  --/--    ", $m_1,$m_max1,$m_2,$m_max2,$m_3,$m_max3;
 1764:       } else { # suppose to be 7
 1765:         $mt1_ratio = $m_1/$m_max1 if $m_max1 != 0;
 1766:         $mt2_ratio = $m_2/$m_max2 if $m_max2 != 0;
 1767:         $mt3_ratio = $m_3/$m_max3 if $m_max3 != 0;
 1768:         $f_ratio   = $f_score/$f_max if $f_max != 0;
 1769:         $e_scores = $mt1_percent * $mt1_ratio + $mt2_percent*$mt2_ratio + $mt3_percent * $mt3_ratio + $final_percent * $f_ratio;
 1770:         $e_entry = sprintf " %6.2f/%3d %6.2f/%3d %6.2f/%3d %3d/%3d   ",$m_1,$m_max1,$m_2,$m_max2,$m_3,$m_max3,$f_score,$f_max;
 1771:       }
 1772:       
 1773:       # $supp_scores_limit_set comes from capa.config
 1774:       ($s_scores,$s_max_scores,$s_abscent,$s_count,$s_summary) = 
 1775:         &S_CollectSetScores($SuppPath,"$s_id",0,$supp_scores_limit_set) if -d "$SuppPath";
 1776:       $s_ratio = $s_scores/$s_max_scores if $s_max_scores != 0;
 1777:       ## print "$s_scores/$s_max_scores,$s_abscent/$s_count\n";
 1778:       
 1779:       # $others_scores_limit_set comes from capa.config
 1780:       ($o_scores,$o_max_scores,$o_abscent,$o_count,$o_summary) = 
 1781:         &S_CollectSetScores($OthersPath,"$s_id",0,$others_scores_limit_set) if -d "$OthersPath";
 1782:        $o_ratio = $o_scores/$o_max_scores if $o_max_scores != 0;
 1783:       ## print "$o_scores/$o_max_scores,$o_abscent/$o_count\n";
 1784:       
 1785:       
 1786:       $total_score = $hw_percent*$c_ratio + $qz_percent*$q_ratio + $e_scores;
 1787: 
 1788:       $score_str = sprintf "% 6.2f", $total_score;
 1789:       $s_key = sprintf "%06.2f %s", $total_score,$s_id;
 1790:       # #            HW      QZ     QZ-N    SUPP    Mid 1      Mid 2      Mid 3     Final     Total
 1791:       #
 1792:       # A12345678 123/123 123/123 123/123 123/123 123.45/123 123.45/123 123.45/123 123/123   123.45
 1793:       #
 1794:       # A23546721 335/341  45/ 45   0/ 15  76/ 81  30.00/ 30  28.60/ 30  30.00/ 30  56/ 57   100.39
 1795:       # A23778965 333/341  34/ 45   1/ 15  79/ 81  27.90/ 30  28.60/ 30  28.60/ 30  55/ 57    96.72
 1796:       # A11111111   0/341   0/ 45  14/ 15   0/ 81   0.00/ 30   0.00/ 30   0.00/ 30   0/ 57     0.00
 1797:       # 173533390   0/341   0/ 45  15/ 15   0/ 81   0.00/ 30   0.00/ 30   0.00/ 30   0/ 57     0.00
 1798: 
 1799:       $pre_entry = sprintf "%3d/%3d %3d/%3d %3d/%3d %3d/%3d",
 1800:        $c_scores,$c_max_scores,$q_scores,$q_max_scores,$q_abscent,$q_count,$s_scores,$s_max_scores;
 1801:       
 1802:       
 1803:       $tail_entry = sprintf "%6.2f", $total_score;
 1804:       
 1805:       
 1806:       $whole_entry = "$s_id " . "$pre_entry" . "$e_entry" . "$tail_entry\n";
 1807:       
 1808:       $f_entry{$s_key} = $whole_entry;
 1809:       print " $s_id $score_str \[$pre_entry\]\n";
 1810:       print "\t\t\[$e_entry $tail_entry\]\n";
 1811:     }
 1812:     close(CIN) || die "Cannot close file $filename!";
 1813:     # close the classl file and
 1814: 
 1815:     # open the set score report file
 1816:      if(-f $sfilename) {  # if there is already a scores file, move it to something else
 1817:        system("mv $sfilename $sfilename.prior.$day_str");
 1818:      }
 1819:      open(OUT, ">$sfilename") || die "Cannot open file $sfilename!";
 1820:      print OUT "\#            HW      QZ     QZ-N    SUPP    Mid 1      Mid 2      Mid 3     Final     Total\n";
 1821:      foreach $s_key (reverse sort keys %f_entry) {
 1822:        print OUT "$f_entry{$s_key}";
 1823:      }
 1824:      close(OUT) || die "Cannot close file $sfilename!";
 1825:  }
 1826:  
 1827: #
 1828: #  create all emails in the Mail/ directory
 1829: #  input is the master scores file name
 1830: sub    S_CreateEmails {
 1831:     local($sfilename)=@_;
 1832:     local($efilename);
 1833:     local($s_id);
 1834:     local($var_name,$s_sec,$email);
 1835:     local($q_scores,$q_max_scores,$q_absent,$q_count,$q_summary);
 1836:     local($last_n,$first_n,$first_part,$middle_n);
 1837:     local($day_str,$cat,$tmp);
 1838:     
 1839:     # variable $Email_templateFile comes from capa.config entry email_template_file 
 1840:     &ScanMailTemplate("$Email_templateFile");
 1841:     
 1842:     # after scanning the email template file, $P_code{'email_perl'} and $P_code{'template'} are defined
 1843:     #  in the code $P_code{'email_perl'}, we need to define 
 1844:     #     $final_grade and 
 1845:     #   $category_one_high, $category_one_low, .. comes from capa.config
 1846:     #   output $Summary_sentence
 1847:     
 1848:     #  in the code $P_code{'template'}
 1849:     $day_str = &TodayString;
 1850:     
 1851:     if(-f $sfilename) {
 1852:       open(SIN, "<$sfilename") || die "Cannot open file $sfilename!";
 1853:       while(<SIN>) {
 1854:       #           1         2         3         4         5         6         7         8         9
 1855:       # 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
 1856:       # #            HW      QZ     QZ-N    SUPP    Mid 1      Mid 2      Mid 3     Final     Total
 1857:       # A23546721 335/341  45/ 45   0/ 15  76/ 81  30.00/ 30  28.60/ 30  30.00/ 30  56/ 57   100.39
 1858:         if(! /^\#/) {  # non comments line
 1859:           $s_id = substr($_,0,9); $s_id = uc($s_id);
 1860:           $var_name = $Var_name{'Homework_total_score'};
 1861:           ${$var_name} = int( substr($_,10,3) );  # homework total score
 1862:           $var_name = $Var_name{'Homework_total_max'};
 1863:           ${$var_name} = int( substr($_,14,3) );  # max score
 1864:           $var_name = $Var_name{'Quiz_total_score'};
 1865:           ${$var_name} = int( substr($_,18,3) );  # quiz score
 1866:           $var_name = $Var_name{'Quiz_total_max'};
 1867:           ${$var_name} = int( substr($_,22,3) );  # quiz max
 1868:           
 1869:           $var_name = $Var_name{'Quiz_absent'};
 1870:           ${$var_name} = int( substr($_,26,3) );  # quiz absent
 1871:           
 1872:           $var_name = $Var_name{'Quiz_count'};
 1873:           ${$var_name} = int( substr($_,30,3) );  # quiz count
 1874:           
 1875:           # need to take care of '--'
 1876:           
 1877:           $midterm1 = substr($_,42,6);
 1878:           $midterm_max1 = int( substr($_,49,3));
 1879:           
 1880:           $midterm2 = substr($_,53,6);
 1881:           $midterm_max2 = int( substr($_,60,3));
 1882:           
 1883:           $midterm3 = substr($_,64,6);
 1884:           $midterm_max3 = int( substr($_,71,3));
 1885:           
 1886:           $final_exam = substr($_,75,3);
 1887:           $final_exam_max = int( substr($_,79,3));
 1888:           
 1889:           $final_grade = substr($_,85,6);  # server as input to $P_code{'email_perl'}
 1890:           
 1891:           $final_grade = $final_grade * 1.0;
 1892:           
 1893:           ## print "PERL: $P_code{'email_perl'}\n";
 1894:           ## print "Press RETURN to continue"; $tmp = <>;
 1895:           
 1896:           ## print "SUMMARY: $Summary_sentence\n";
 1897:           ## print "Press RETURN to continue"; $tmp = <>;
 1898:           
 1899:           # now we know $Exam_sentence and $Summary_sentence
 1900:           
 1901:     # for $P_code{'template'}, we need to find
 1902:     #   Global input
 1903:     #         $student_name, $ClassName, there is already a $ClassName global variable
 1904:     #         ${$Var_name{'Homework_total_score'}} ==> $HWtotal_scp
 1905:     #         ${$Var_name{'Homework_total_max'}}   ==> $HWtotal_max_scp
 1906:     #         ${$Var_name{'Quiz_count'}}           ==> $QZcount_scp
 1907:     #         ${$Var_name{'Quiz_total_score'}}     ==> $QZtotal_scp 
 1908:     #         ${$Var_name{'Quiz_total_max'}}       ==> $QZtotal_max_scp
 1909:     
 1910:     #         ${$Var_name{'Quiz_summary_string'}}  ==> $QZsummary <-- must read from the qz/records/*.db files
 1911:     
 1912:     #         $midterm1, $midterm2, $midterm3 and $final_exam
 1913:     #     
 1914:     ##  How to allow the format of $student_name customizable from capa.config file?
 1915:     ##
 1916:           ($student_name,$s_sec,$email) = S_Lookup_student_name("$s_id");
 1917:           ($last_n,$first_part) = split(/,/,$student_name);
 1918:           $first_part =~ s/^\s//g;
 1919:           ($first_n,$middle_n) = split(/ /,$first_part);
 1920:           $student_name = "$first_n" . " $last_n";
 1921:           
 1922:           # $quiz_scores_limit_set comes from capa.config
 1923:           ($q_scores,$q_max_scores,$q_absent,$q_count,$q_summary) = 
 1924:             &S_CollectSetScores($QuizPath,"$s_id",0,$quiz_scores_limit_set) if -d "$QuizPath";
 1925:           ${$Var_name{'Quiz_summary_string'}} = "$q_summary";
 1926:           ${$Var_name{'Quiz_absent'}} = $q_absent;
 1927:           ${$Var_name{'Quiz_count'}}  = $q_count;
 1928:           eval "$P_code{'email_perl'}";
 1929:           if( ($final_grade <= $category_one_high) && ($final_grade >= $category_one_low)) {
 1930:             $cat = 1;
 1931:           } elsif ( ($final_grade <= $category_two_high)&&($final_grade >= $category_two_low) ) {
 1932: 
 1933:             $cat = 2;
 1934:   
 1935:           } elsif ( ($final_grade <= $category_three_high)&&($final_grade >= $category_three_low)  ) {
 1936: 
 1937:             $cat = 3;
 1938:   
 1939:           } elsif( ($final_grade <= $category_four_high)&&($final_grade >= $category_four_low) ) {
 1940: 
 1941:             $cat = 4;
 1942:   
 1943:           } else { # not in above category
 1944: 
 1945:             $cat = 5;
 1946:           }
 1947:           
 1948:           $efilename = "$ClassPath" . "/Mail/$s_id.$day_str.$cat";
 1949:           
 1950:           if(-f $efilename) {  # remove the file with the same name, no warning!!
 1951:             system("rm $efilename");
 1952:           }
 1953:           open(EOUT,">$efilename") || die "Cannot create file $efilename!";
 1954:           eval "print EOUT \"$P_code{'template'}\" ";
 1955:           close(EOUT) || die "Cannot close file $efilename!";
 1956:           
 1957:           print " $s_id\t$final_grade,\tcategory $cat\n";
 1958:           
 1959:         }
 1960:       }
 1961:       close(SIN) || die "Cannot close file $sfilename!";
 1962:       print "=" x 45 . "\n";
 1963:       print "DONE creating all email files in $ClassPath/Mail/ directory.\n";
 1964:       print "Press RETURN to continue"; $tmp = <>;
 1965:     } else {  # the master scores file does not exist
 1966:       print "File $sfilename does not exist!\n";
 1967:       print "Press RETURN to continue"; $tmp = <>;
 1968:     }
 1969:        
 1970:  }
 1971: # ----------------------------------------------------------------
 1972: sub    ScanMailTemplate {
 1973:   local($filename) = @_;
 1974:   local($input_line);
 1975:   local($tmp);
 1976:   
 1977:   
 1978:   if(-f $filename) {
 1979:     open(IN, "<$filename") || die "Cannot open $filename\n";
 1980:     LINE: while( $input_line = <IN> ) {
 1981:        chomp($input_line);
 1982:        if($input_line =~ m|^//| )   { next LINE; }          ## ignore comments
 1983:        if($input_line =~ m|^\#| )   { next LINE; }          ## ignore comments
 1984:        if($input_line =~ m|^\s*BEGIN_perl| ) { ## Perl code
 1985:          $P_code{'email_perl'} = &E_CollectPerlCode;
 1986:          ## print "email_perl=$P_code{'email_perl'}";
 1987:          next LINE; 
 1988:        }
 1989:        if($input_line =~ m|^\s*BEGIN_template| )  { ## template code
 1990:          $P_code{'template'} = &E_CollectTemplateCode;
 1991:          ## print "template = $P_code{'template'}";
 1992:          next LINE; 
 1993:        }
 1994:        next LINE;
 1995:     }
 1996:     close(IN) || die "Cannot close $filename\n";
 1997:     
 1998:   } else {
 1999:     printf "File $filename does not exist!"; $tmp = <>;
 2000:   }
 2001: 
 2002:  }
 2003: 
 2004: # 
 2005: #  Randomly pick a file in Mail/ directory and display 
 2006: #   it on screen
 2007: #  
 2008: sub    S_RandomlyPreview {
 2009:     local($filename);
 2010:     local(@all_files);
 2011:     local($upper_bound);
 2012:     local($pick_idx,$pick_filename);
 2013:     local($tmp);
 2014:     
 2015:     opendir(EDIR, "$ClassPath/Mail") || die "cannot opendir $ClassPath/Mail!";
 2016:     @all_files = grep !/^\.\.?$/, readdir EDIR;
 2017:     closedir EDIR;
 2018:        ## srand(time() ^ ($$+( $$ << 15)) );
 2019:        
 2020:     $upper_bound = $#all_files + 1;
 2021:     if( $upper_bound > 0 ) { # something to preview   
 2022:        $pick_idx = ((rand $$) * 1000.0) % $upper_bound;
 2023:        
 2024:        # print "PICK: $pick_idx among $upper_bound\n";
 2025:        
 2026:        $pick_filename = $all_files[$pick_idx];
 2027:        print "Preview File: $pick_filename\n";
 2028:        print "Press RETURN to continue"; $tmp = <>;     &C_ClearScreen;
 2029:        $filename = "$ClassPath/Mail/$pick_filename";
 2030:        open(IN,"<$filename") ||  die "Cannot open file $filename!";
 2031:        while(<IN>) {
 2032:          print;
 2033:        }
 2034:        close(IN) || die "cannot close file $filename!";
 2035:        print "Press RETURN to continue"; $tmp = <>;
 2036:     } else {
 2037:        print "No file in directory $ClassPath/Mail\n";
 2038:        
 2039:        print "Press RETURN to continue"; $tmp = <>;
 2040:     
 2041:     }
 2042: 
 2043:  }
 2044:  
 2045: 
 2046: # The format of a classl file                                                                  
 2047: # 
 2048: #          1         2         3         4         5         6         7         8         9         0
 2049: #0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678
 2050: #PHY 183   001 A23745301 Abraham, Christopher Wil            FS96
 2051: #phy 111   001 A12345678 BUMSTEAD, Blondie                   blondie4@pilot.msu.edu            12345678
 2052: #PHY 183   003 A24469315 Costigan, Timothy Patric            costiga3@pilot.msu.edu            costiga3
 2053: #PHY 183   002 A25425738 Cacossa, Andrew Vincent             cacossaa@pilot.msu.edu            cacossaa
 2054: 
 2055: # lookup the student id supplied in variable $student_id
 2056: # and return the student name and the e-mail address if available
 2057: sub    S_Lookup_student_name {
 2058:      local($student_id)=@_;
 2059:      local($filename);
 2060:      local($found,$input_line);
 2061:      local($tmp_sn,$student_name);
 2062:      local($len,$s_sec,$email);
 2063:      
 2064:   $student_id = uc($student_id);
 2065:   $filename = $ClassPath . "/classl";
 2066:   open(CIN, "<$filename") || die "Cannot open file $filename!";
 2067:   $found = 0;
 2068:   while( ($input_line = <CIN>) && (! $found) ) {
 2069:     chomp($input_line);
 2070:     $tmp_sn = substr($input_line,14,9); $tmp_sn = uc($tmp_sn);
 2071:     if($tmp_sn =~ /^$student_id/ ) {
 2072:       $found=1;
 2073:        # student name begins at column 24 and has 30 chars max
 2074:       $s_sec = substr($input_line,10,3);
 2075:       $student_name = substr($input_line,24,30);
 2076:       $len = length($input_line);
 2077:       $email = "";
 2078:       if($len > 55 ) {
 2079:         $email = substr($input_line,55,32);
 2080:         $email =~ s/\s+//g;
 2081:         if( $email !~ /\@|\./ ) {
 2082:           $email = "";
 2083:         }
 2084:       }
 2085:     }
 2086:   }
 2087:   close(CIN) || die "Cannot close file $filename!";
 2088:   return ($student_name,$s_sec,$email);
 2089:  }
 2090: 
 2091: #  This routine accepts a string represent an absolute path to a class
 2092: #   it checks to see if 
 2093: #      the directory specified by this path did actually exist
 2094: #      the records/ sub-directory did exist
 2095: #      the records/setX.db  file exists
 2096: #      the classl file exists
 2097: 
 2098: sub    S_CheckClassPath {
 2099:     local($path)=@_;
 2100:     local($cfullpath,$rfullpath,$sfullpath);
 2101:     local($correct,$cfgfullpath,$cfgutilsfullpath);
 2102:     
 2103:     $correct = 0;
 2104:     if( $path =~ /\/$/ ) {
 2105:         $cfullpath = "$path" . "classl";
 2106:         $rfullpath = "$path" . "records";
 2107:         $sfullpath = "$path" . "records/set$set.db";
 2108:         $cfgfullpath = "$path" . "capa.config";
 2109:         $cfgutilsfullpath = "$path" . "capautils.config";
 2110:     } else {
 2111:         $cfullpath = "$path" . "/classl";
 2112:         $rfullpath = "$path" . "/records";
 2113:         $sfullpath = "$path" . "/records/set$set.db";
 2114:         $cfgfullpath = "$path" . "/capa.config";
 2115:         $cfgutilsfullpath = "$path" . "/capautils.config";
 2116:     }
 2117:     if( -d $path ) {
 2118:         if( -d $rfullpath ) {
 2119:           if(-f $cfgfullpath ) {
 2120: 	      if(-f $cfgutilsfullpath ) {
 2121: 		  $correct = 1;
 2122: 	      } else {
 2123: 		  print "File [$cfgutilsfullpath] does not exist!\n";
 2124: 	      }
 2125:           } else {
 2126:             print "File [$cfgfullpath] does not exist!\n";
 2127:           }
 2128:         } else {
 2129:           print "Directory [$rfullpath] does not exist!\n";
 2130:         }
 2131:     } else {
 2132:         print "Directory [$path] does not exist!\n";
 2133:     }
 2134:     return ($correct);
 2135:  }
 2136:  
 2137: ##
 2138: # display score file according to the order specified 
 2139: #  by $key_str and the direction in $dir
 2140: #  $dir = 1 means ascending
 2141: #         2 means descending
 2142: #
 2143: sub   S_DisplayScoreFile {
 2144:     local($key_str,$dir,$sfilename)=@_;
 2145:     local(@a_keyidx);
 2146:     local($key_seq);
 2147:     local($s_id,$final_grade,$s_name,$s_sec,$s_email);
 2148:     local($sort_key,$s_display_name,$first_n,$first_part,$middle_n,$last_n);
 2149:     local(%sf_entry);
 2150:     local($line_cnt,$line_total,$len,$pad_len);
 2151:     local($tmp);
 2152:     
 2153:     $key_str =~ s/,//g;
 2154:     $key_str =~ s/1/\$final_grade/g;
 2155:     $key_str =~ s/2/\$s_id/g;
 2156:     if( $key_str =~ /3/ ) { 
 2157:       $key_str =~ s/3/\$s_name/g;
 2158:     }
 2159:     if( $key_str =~ /4/ ) { 
 2160:       $key_str =~ s/4/\$s_sec/g;
 2161:     }
 2162:     $key_seq = '"' . "$key_str" . '"';
 2163:     
 2164:     if(-f $sfilename) {
 2165:       open(SIN, "<$sfilename") || die "Cannot open file $sfilename!";
 2166:       $line_cnt = 0;
 2167:       while(<SIN>) {
 2168:         if(! /^\#/) {  # non comments line
 2169:           chomp();
 2170:           $line_cnt++;
 2171:           $s_id = substr($_,0,9); $s_id = uc($s_id);
 2172:           $final_grade = substr($_,85,6);  #
 2173:           
 2174:           $whole_entry = $_;
 2175:           ($s_name,$s_sec,$s_email) = S_Lookup_student_name("$s_id");
 2176:           # evaluate the code to a real key
 2177:           $sort_key = eval $key_seq;
 2178:           
 2179:           ($last_n,$first_part) = split(/,/,$s_name);
 2180:           $last_n = uc($last_n);
 2181:           $first_part =~ s/^\s//g;
 2182:           ($first_n,$middle_n) = split(/ /,$first_part);
 2183:           $s_display_name = "$last_n" . ", $first_n";
 2184:           $len = length($s_display_name);
 2185:           if( $len < 25 ) {
 2186:             $pad_len = 25 - $len;
 2187:             $s_display_name = "$s_display_name" . " " x $pad_len;
 2188:           } else {
 2189:             $s_display_name = substr($s_display_name,0,25);
 2190:           }
 2191:           $sf_entry{"$sort_key"} = "$s_display_name $s_sec $whole_entry";
 2192:           ### print "KEY:[$sort_key]:$whole_entry\n";
 2193:         } # end of if not comments
 2194:       } # end while <SIN>
 2195:       close(SIN) || die "Cannot close file $sfilename!";
 2196:       $line_total = $line_cnt; $line_cnt = 0;
 2197:       if($dir == 1 ) {
 2198:         foreach $sort_key (sort keys %sf_entry) {
 2199:           $line_cnt++;
 2200:           print $sf_entry{"$sort_key"} . "\n";
 2201:           if( ($line_cnt % $display_score_row_limit) == 0 ) { # $display_score_row_limit from capa.config
 2202:             print " --$line_cnt/$line_total-- Press RETURN to continue"; $tmp = <>;
 2203:           }
 2204:         }
 2205:       } else {
 2206:         foreach $sort_key (reverse sort keys %sf_entry) {
 2207:           $line_cnt++;
 2208:           print $sf_entry{"$sort_key"} . "\n";
 2209:           if( ($line_cnt % $display_score_row_limit) == 0 ) {
 2210:             print " --$line_cnt/$line_total-- Press RETURN to continue"; $tmp = <>;
 2211:           }
 2212:         }
 2213:       }
 2214:     } else {
 2215:       print "File [$sfilename] does not exist!";
 2216:     }
 2217:     
 2218:  }
 2219: 
 2220: ##
 2221: ##   Input: file name to be printed through lpr command
 2222: ##
 2223: ##
 2224: sub  S_LPRFile {
 2225:      local($file)=@_;
 2226:      local($go_on)=0;
 2227:      local($select);
 2228:      local($printer_selected);
 2229:      local($emp);
 2230:      local($cmd);
 2231:      local($PS_file);
 2232:  
 2233:      print "Enter Y or <RETURN> to continue or N to cancel the operation: ";
 2234:      $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2235:      $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n";
 2236:      
 2237:       if( $go_on ) { # run lpr -P.... on the .ps file
 2238:         $select = C_MultipleChoice(4,10,$DialogWidth,"           Printers Available         ",
 2239:                           "Printer Name",@Printers);
 2240:         $Printer_selected = $Printers[$select-1];
 2241:         
 2242:         $go_on = 0;
 2243:         while( ! $go_on ) {
 2244:           print "Enter 1 for one sided or 2 for two sided printing (RETURN = 1 sided): ";
 2245:           $tmp=<>; 
 2246:           if( ($tmp == 1) || ($tmp == 2) || ($tmp eq "\n") ) {
 2247:             $tmp = 1 if $tmp eq "\n";
 2248:             $go_on = 1;
 2249:           }
 2250:         }
 2251:         if( $tmp == 1 ) {
 2252:           $LpronesidedCMD =~ s/\$Printer_selected/$Printer_selected/e;
 2253:           $cmd = "$LpronesidedCMD $file";
 2254:         } else {  # should be 2
 2255:           $PS_file = $file;
 2256:           print "$LprtwosidedCMD\n";
 2257:           $LprtwosidedCMD =~ s/\$Printer_selected/$Printer_selected/e;
 2258:           $LprtwosidedCMD =~ s/\$PS_file/$PS_file/e;
 2259:           $cmd = "$LprtwosidedCMD";
 2260:         }
 2261:         $go_on = 0;
 2262:         print "=" x 70 . "\n";
 2263:         print "CMD: $cmd\n";
 2264:         print "Enter Y or <RETURN> to continue or N to cancel the operation: ";
 2265:         $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2266:         $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n";
 2267:         if($go_on) {
 2268:           open(IN,"$cmd|");
 2269:           while($input_line = <IN>) {
 2270:             print "$input_line";
 2271:           }
 2272:           close(IN);
 2273:           print "=" x 70 . "\n";
 2274:           print " Your job has been sent to the print spooler. Press RETURN to continue"; $tmp = <>;
 2275:         }
 2276:       }
 2277:  }
 2278: 
 2279: 
 2280: 
 2281: # <========================= Main program =======================>
 2282: 
 2283: Getopts('c:s');
 2284: 
 2285: if (! $opt_c) {  ## class path option
 2286:   print "USAGE: capatools.pl -c Full_path_to_class\n";
 2287:   $ClassPath = &S_Enterpath;
 2288: } else {  # need to check the path entered by option -c
 2289:   if( S_CheckClassPath("$opt_c") ) {
 2290:     $ClassPath = $opt_c;
 2291:     if( $opt_s ) {
 2292:       S_ReadCAPAconfig($ClassPath);
 2293:       S_CreateScores("$Master_scoresFile");
 2294:       # print "run score report in silent background\n";
 2295:       # print "The $Master_scoresFile\n";
 2296:       
 2297:       exit (1);
 2298:     }
 2299:   } else {
 2300:     $ClassPath = &S_Enterpath;
 2301:   }
 2302: }
 2303: 
 2304: $DialogWidth = 48;
 2305: $Quit = 0;
 2306: 
 2307: 
 2308: $len = length($ClassPath);
 2309: if(  $len > ($DialogWidth-6) ) {  ## class path too long, display the last portion instead
 2310:     $offset = $len - $DialogWidth +6;
 2311:     $DisplayPath = "-..." . substr($ClassPath, $offset,$DialogWidth-6);
 2312: } else {
 2313:     $DisplayPath = $ClassPath;
 2314: }
 2315: 
 2316: S_ReadCAPAconfig($ClassPath);
 2317: 
 2318: while(! $Quit ) {
 2319:   $What = C_MultipleChoice(1,8,$DialogWidth,"        Welcome to CAPA Utilities Ver 1.1   ",
 2320:                           "$DisplayPath",@Main_menu);
 2321: 
 2322:   if( $What ==  1 ) {        # change class path
 2323:      $ClassPath = &S_Enterpath;
 2324:      $len = length($ClassPath);
 2325:      if(  $len > ($DialogWidth-6) ) {
 2326:        $offset = $len - $DialogWidth +6;
 2327:        $DisplayPath = "-..." . substr($ClassPath, $offset,$DialogWidth-6);
 2328:      } else {
 2329:        $DisplayPath = $ClassPath;
 2330:      }
 2331:      S_ReadCAPAconfig($ClassPath);
 2332:   } elsif ($What ==  2 ) {   # run capastat.pl
 2333:      $Set  = &S_InputSet;
 2334:      $Sfullpath = $ClassPath . "/records/set" . "$Set" . ".db";
 2335:      ## print "Running capastat.pl on $Sfullpath\n";
 2336:      ($Q_cnt,$L_cnt) =  &S_ScanSetDB($Sfullpath);
 2337:      Percentage_Scores($Set,$L_cnt);
 2338:      S_Average($Q_cnt,$L_cnt);
 2339:      
 2340:      ## Large_Tries($opt_t,$opt_n,$Q_cnt,$L_cnt);
 2341:   } elsif ($What == 3 ) {   # log analysis on S, U, 
 2342:      $Set  = &S_InputSet;
 2343:      
 2344:      $Lfullpath = $ClassPath . "/records/log" . "$Set" . ".db";
 2345:      if(-f $Lfullpath) {
 2346:        print "Log analysis for telnet session log$Set.db\n";
 2347:        print " This may take a while to calculate, please wait ...\n";
 2348:        ($Y_l,$N_l,$S_l,$U_l,$u_l) = S_ScanLogDB($Lfullpath);
 2349:      }
 2350:      $Wfullpath = $ClassPath . "/records/weblog" . "$Set" . ".db";
 2351:      if(-f $Wfullpath ) {
 2352:        print "=" x 79 . "\n";
 2353:        print "Log analysis for web session weblog$Set.db\n";
 2354:        print " This may take a while to calculate, please wait ...\n";
 2355:        ($Y_w,$N_w,$S_w,$U_w,$u_w) = S_ScanLogDB($Wfullpath);
 2356:      }
 2357:      $Telnet_total = $Y_l+$N_l+$S_l+$U_l+$u_l;
 2358:      $Web_total    = $Y_w+$N_w+$S_w+$U_w+$u_w;
 2359:      
 2360:      print "============== SUMMARY ====================\n";
 2361:      print "            #Y     #N     #S     #U     #u    Total\n";
 2362:      printf  "telnet: %6d %6d %6d %6d %6d   %6d\n", $Y_l, $N_l, $S_l, $U_l, $u_l,$Telnet_total;
 2363:      printf  "   web: %6d %6d %6d %6d %6d   %6d\n", $Y_w, $N_w, $S_w, $U_w, $u_w,$Web_total;
 2364:      $Y_sum = $Y_l + $Y_w;
 2365:      $Ratio_Y = 100.0 * ($Y_w / $Y_sum) if $Y_sum > 0;
 2366:      $N_sum = $N_l + $N_w;
 2367:      $Ratio_N = 100.0 * ($N_w / $N_sum) if $N_sum > 0;
 2368:      $S_sum = $S_l + $S_w;
 2369:      $Ratio_S = 100.0 * ($S_w / $S_sum) if $S_sum > 0;
 2370:      $U_sum = $U_l + $U_w;
 2371:      $Ratio_U = 100.0 * ($U_w / $U_sum) if $U_sum > 0;
 2372:      $u_sum = $u_l + $u_w;
 2373:      $Ratio_u = 100.0 * ($u_w / $u_sum) if $u_sum > 0;
 2374:      $overall_entries = $Telnet_total+$Web_total;
 2375:      $Ratio_web = 100.0*($Web_total/$overall_entries) if $overall_entries > 0;
 2376:      printf  "  %%web: % 6.1f % 6.1f % 6.1f % 6.1f % 6.1f   % 6.1f\n", 
 2377:        $Ratio_Y, $Ratio_N, $Ratio_S, $Ratio_U, $Ratio_u, $Ratio_web;
 2378:      printf "Press RETURN to continue"; $tmp = <>;
 2379:      
 2380:   } elsif ($What ==  4 ) {   # Student course profile
 2381:     # select either use student name or student number
 2382:     # if there are more than two students, display them in a table to select
 2383:     #    by the user
 2384:     # use student id to find the scores
 2385:     # display set scores in each class, regular class goes first.
 2386:     # S_InputStudent;
 2387:     ($s_id,$s_nm) = S_InputStudent($ClassPath);
 2388:     if($s_id ne "" ) {
 2389:       print "$s_nm\n";
 2390:       &S_CollectSetScores($ClassPath,"$s_id",1,$homework_scores_limit_set);
 2391:       &S_CollectSetScores($QuizPath,"$s_id",1,$quiz_scores_limit_set)   if -d "$QuizPath";
 2392:       &S_CollectSetScores($ExamPath,"$s_id",1,$exam_scores_limit_set)   if -d "$ExamPath";
 2393:       &S_CollectSetScores($SuppPath,"$s_id",1,$supp_scores_limit_set)   if -d "$SuppPath";
 2394:       &S_CollectSetScores($OthersPath,"$s_id",1,$others_scores_limit_set) if -d "$OthersPath";
 2395:       
 2396:       
 2397:       print "\nEnter Y or <RETURN> for Login analysis (may take a while)\n";
 2398:       print "Enter N => return to main menu: ";
 2399:       $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2400:       $go_on = 0;
 2401:       $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n";
 2402:       
 2403:       if($go_on) {
 2404:         # $homework_scores_limit_set comes from capa.config
 2405:         &S_LoginAnalysis($ClassPath,"$s_id",$homework_scores_limit_set);
 2406:         &S_StudentSetAnalysis($ClassPath,"$s_id",$homework_scores_limit_set);
 2407:       }
 2408:       
 2409:     }
 2410:   } elsif ($What ==  5 ) {   # CAPA IDs for one student
 2411:     ($s_id,$s_nm) = S_InputStudent($ClassPath);
 2412:     if($s_id ne "" ) {
 2413:       $go_on = 0;
 2414:       print "$s_nm, $Allcapaid\n";
 2415:       ($S_from, $S_to) = S_EnterSets;
 2416:       
 2417:       print "\nCMD: $AllcapaidCMD -i -stu $s_id -s $S_from -e $S_to\n";
 2418:       print "Enter Y or <RETURN> to continue or N to cancel the operation: ";
 2419:       $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2420:       $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n";
 2421:       if( $go_on ) {
 2422:         open(IN,"$AllcapaidCMD -i -stu $s_id -s $S_from -e $S_to -c $ClassPath|");
 2423:         while($input_line = <IN>) {
 2424:           print "$input_line";
 2425:         }
 2426:         close(IN);
 2427:         printf "Press RETURN to continue"; $tmp = <>;
 2428:       }
 2429:     }
 2430:   } elsif ($What ==  6 ) {   # All CAPA IDs
 2431:      $go_on = 0;
 2432:      ($S_from, $S_to) = S_EnterSets;
 2433:      print "\nCMD: $AllcapaidCMD -s $S_from -e $S_to\n";
 2434:      print "Enter Y or <RETURN> to continue or N to cancel the operation: ";
 2435:      $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2436:      $go_on = 1 if $tmp =~ /^Y/ || $tmp eq "\n";
 2437:      if( $go_on ) { 
 2438:        open(IN,"$AllcapaidCMD -s $S_from -e $S_to -c $ClassPath|");
 2439:        while($input_line = <IN>) {
 2440:          print "$input_line";
 2441:        }
 2442:        close(IN);
 2443:        print "Press RETURN to continue"; $tmp = <>;
 2444:      }
 2445:      
 2446:   } elsif ($What ==  7 ) {   # Item analysis
 2447:      $u_path = C_MultipleChoice(4,10,$DialogWidth,"Select a Class path","",@MainPath);
 2448:      
 2449:      $Set  = &S_InputSet;
 2450:      print "Item analysis for $MainPath[$u_path-1], Set $Set\n";
 2451:      print "This may take a while to calculate, please wait ...\n";
 2452:      S_ItemAnalysis($MainPath[$u_path-1],$Set);
 2453:   } elsif ($What ==  8 ) {   # Correlation chart
 2454:   ## TO DO:
 2455:   ##         Let user specify how many categories to calculate correlation
 2456:   ##             For each category, the user can specify problem numbers to 
 2457:   ##             be in that category
 2458:   ##         Then, the correlations between each category is calculated
 2459:   ##
 2460:      $u_path = C_MultipleChoice(4,10,$DialogWidth,"Select a Class path","",@MainPath);
 2461:      $Set  = &S_InputSet;
 2462:      print "Correlation calculation for $MainPath[$u_path-1], Set $Set\n";
 2463:      print "This may take a while to calculate, please wait ...\n";
 2464:      S_SetCorrelationStatistics($MainPath[$u_path-1],$Set);
 2465:   } elsif ($What ==  9 ) {   # E-Mail
 2466:      $done_email = 0;
 2467:      while(! $done_email ) {
 2468:        $select = C_MultipleChoice(2,10,$DialogWidth+15,"    CAPA E-mail facilities       ",
 2469:                           "$DisplayPath",@Email_menu);
 2470:       if($select == 1 ) {  # Create scores file
 2471:         # the variable $Master_scoresFile comes from capa.config entry master_scores_file = ...
 2472:         print "Create a file containing scores of all students:\n";
 2473:         print " $Master_scoresFile\n";
 2474:         print "This may take a while to complete, please wait ...\n";
 2475:         S_CreateScores("$Master_scoresFile");
 2476:       } elsif ($select == 2 ) { # create all e-mail files in Mail/ dir
 2477:         print "Create e-mail files in $ClassPath/Mail/ directory\n";
 2478:         print " based on scores file $Master_scoresFile\n";
 2479:         print "This action will REPLACE the original file(s) in the Mail/ directory!\n";
 2480:         $go_on = 0;
 2481:         print "Enter Y or <RETURN> to continue or N to cancel the operation: ";
 2482:         $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2483:         $go_on = 1 if $tmp =~ /^Y/ || $tmp eq "\n";
 2484:         if( $go_on ) {
 2485:           print "This may take a while to complete, please wait ...\n";
 2486:         
 2487:           S_CreateEmails("$Master_scoresFile");
 2488:         }
 2489:       } elsif ($select == 3 ) { # Preview e-mail
 2490:        # randomly pick a student 
 2491:        # 
 2492:        &S_RandomlyPreview;
 2493:        
 2494:       } elsif ($select == 4 ) { # Send e-mail to a group of students
 2495:        $entered_cat = &S_EnterCategory;
 2496:        
 2497:        $go_on = 0;
 2498:        print "Entered category: $entered_cat\n";
 2499:        print "Enter Y or <RETURN> to continue or N to cancel the operation: ";
 2500:        $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2501:        $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n";
 2502:        
 2503:        if( $go_on ) {
 2504:          S_MailtoCategory($entered_cat);
 2505:        }
 2506:          
 2507:       
 2508:        # 1. Whole class
 2509:        # 2. Category ---> specify 1, 2, 3, or 4
 2510:       } else { # 4. Cancel
 2511:          $done_email = 1;
 2512:       }
 2513:     }
 2514:   } elsif ($What == 10 ) {  # Print one assignment for a student
 2515:     ($s_id,$s_nm) = S_InputStudent($ClassPath);
 2516:     if($s_id ne "" ) {   # non-empty student id
 2517:       $s_id = uc($s_id); # uppercase
 2518:       
 2519:       ## $Set  = &S_InputSet;
 2520:       ($From_set,$To_set) = &S_EnterSets;
 2521:       print "Printing Set(s) $From_set to $To_set for STUDENT: $s_nm\n";
 2522:       $go_on = 0;
 2523:       print "\nEnter Y or <RETURN> to continue or N to cancel the operation: ";
 2524:       $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2525:       $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n";
 2526:       
 2527:       if($go_on) {
 2528:         $go_on = 0;
 2529:         print "CMD: $QzparseCMD -c $ClassPath -set $From_set:$To_set -stu $s_id\n";
 2530:         open(IN,"$QzparseCMD -c $ClassPath -set $From_set:$To_set -stu $s_id|");
 2531:         while($input_line = <IN>) {
 2532:           print "$input_line";
 2533:         }
 2534:         close(IN);
 2535:         print "=" x 70 . "\n";
 2536:         $tex_file = "$ClassPath" . "/TeX/" . "$s_id" . ".tex";
 2537:         print "CMD: $LatexCMD $tex_file\n";
 2538:         print "Enter Y or <RETURN> to continue or N to cancel the operation: ";
 2539:         $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2540:         $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n";
 2541:         
 2542:       }
 2543:       # run qzparse on the student and the set
 2544:       #     qzparse -c $path -set $set -stu $s_id
 2545:       if( $go_on ) { # run latex on the .tex file, latex will create a .dvi file in cwd()
 2546:         $dir = cwd();
 2547:         $go_on = 0;
 2548:         
 2549:         open(IN,"$LatexCMD $tex_file|");
 2550:         while($input_line = <IN>) {
 2551:           print "$input_line";
 2552:         }
 2553:         close(IN);
 2554:         $cwd_dvi_file     = "$dir/" . "$s_id" . ".dvi";
 2555:         $dvi_file = "$ClassPath" . "/TeX/" . "$s_id" . ".dvi";
 2556:         $cmd = "mv $cwd_dvi_file $dvi_file";
 2557:         system($cmd);
 2558:         print "=" x 70 . "\n";
 2559:         $ps_file  = "$ClassPath" . "/TeX/" . "$s_id" . ".ps";
 2560:         print "CMD: $DvipsCMD $dvi_file -o $ps_file\n";
 2561:         print "\nEnter Y or <RETURN> to continue or N to cancel the operation: ";
 2562:         $tmp=<>; $tmp =~ tr/A-Z/a-z/;
 2563:         $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n";
 2564:       }
 2565:       if( $go_on ) {  # run dvips on the .dvi file
 2566:         
 2567: 
 2568:         open(IN,"$DvipsCMD $dvi_file -o $ps_file|");
 2569:         while($input_line = <IN>) {
 2570:           print "$input_line";
 2571:         }
 2572:         close(IN);
 2573:         print "=" x 70 . "\n";
 2574:         
 2575:         S_LPRFile($ps_file);
 2576:         
 2577:       }
 2578:     }
 2579:   
 2580:   } elsif ($What == 11 ) {  # view score file
 2581:     $entered_key = S_EnterSortKey;
 2582:     
 2583:     $display_key = $entered_key;
 2584:     $display_key =~ s/,/, /g;
 2585:     $display_key =~ s/1/Grade/g;
 2586:     $display_key =~ s/2/Student number/g;
 2587:     $display_key =~ s/3/Student name/g;
 2588:     $display_key =~ s/4/Section/g;
 2589:     print "Entered sorting key: $display_key\n";
 2590:     $go_on = 0;
 2591:     while(! $go_on) {
 2592:       print "Enter 1 for ascending or 2 for decending order (RETURN = 1 ascending): ";
 2593:       $tmp=<>; 
 2594:       if( ($tmp == 1) || ($tmp == 2) || ($tmp eq "\n") ) {
 2595:             $tmp = 1 if $tmp eq "\n";
 2596:             $go_on = 1;
 2597:       }
 2598:     }
 2599:     
 2600:     S_DisplayScoreFile($entered_key,$tmp,$Master_scoresFile);
 2601:     print "Finish displaying score file. Press RETURN to continue"; $tmp = <>;
 2602:   } elsif ($What == 12 ) {  # list student responses
 2603:     ($s_id,$s_nm) = S_InputStudent($ClassPath);
 2604:     if($s_id ne "" ) {
 2605:       $go_on = 0;
 2606:       print "$s_nm, grepping submissions\n";
 2607:       ($S_from, $S_to) = S_EnterSets;
 2608:       for( $i=$S_from;$i<=$S_to;$i++) {
 2609: 	print "Telnet Submissions for $s_nm for set $i\n";
 2610: 	open(IN,"grep -i $s_id $ClassPath/records/submissions$i.db |"); 
 2611: 	while($input_line = <IN>) {
 2612: 	  print "$input_line";
 2613: 	}
 2614: 	close(IN);
 2615: 	printf "Press RETURN to continue"; $tmp = <>;
 2616: 	print "WWW Submissions for $s_nm for set $i\n";
 2617: 	open(IN,"grep -i $s_id $ClassPath/records/websubmissions$i.db|"); 
 2618: 	while($input_line = <IN>) {
 2619: 	  print "$input_line";
 2620: 	}
 2621: 	close(IN);
 2622: 	printf "Press RETURN to continue"; $tmp = <>;
 2623:       }
 2624:     }
 2625:   } elsif ($What == 13 ) {  # quit
 2626:     $Quit = 1;
 2627:   }
 2628: }
 2629: 
 2630: 
 2631: 
 2632: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>