#!/usr/local/bin/perl -I/usr/local/bin # utilities for CAPA superseeded by Manager # Copyright (C) 1992-2000 Michigan State University # # The CAPA system is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # The CAPA system is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with the CAPA system; see the file COPYING. If not, # write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # # As a special exception, you have permission to link this program # with the TtH/TtM library and distribute executables, as long as you # follow the requirements of the GNU GPL in regard to all of the # software in the executable aside from TtH/TtM. # <==========================================================================> # June 1997 version 1.0 Created by Isaac Tsai # September 24 1997 version 1.1 by Guy Albertelli II # -put in initialization loop in S_ScanSetDB (need to remove hardlimits) # -initialize Max_try in S_Average, before being used # # <==========================================================================> use Cwd; require('getopts.pl'); require('CAPAscreen.pl'); # ============================================================================= # # ============================================================================= $Days = int( time / 86400); @MonthName = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ); # # produces a string "ddMmm19xx" to be used in timestamp or filename # sub TodayString { local($ss, $mm, $hh, $mday, $mth, $yy, $wday, $yday,$isdst); local($str); ($ss, $mm, $hh, $mday, $mth, $yy, $wday, $yday,$isdst) = localtime(time); if ($mday > 9) { $str = "$mday$MonthName[$mth]19$yy"; # year 2000 problem!! } else { $str = "0$mday$MonthName[$mth]19$yy"; # year 2000 problem!! } return $str; } # # produces a string hhmmss-ddMon19xx for timestamps # sub TimeString { local($ss, $mm, $hh, $mday, $mth, $yy, $wday, $yday,$isdst); local($str); ($ss, $mm, $hh, $mday, $mth, $yy, $wday, $yday,$isdst) = localtime(time); $ss = "0" . "$ss" if $ss <= 9; $mm = "0" . "$mm" if $mm <= 9; $hh = "0" . "$hh" if $hh <= 9; if ($mday > 9) { $str = "$hh$mm$ss-$mday$MonthName[$mth]19$yy"; # year 2000 problem!! } else { $str = "$hh$mm$ss-0$mday$MonthName[$mth]19$yy"; # year 2000 problem!! } return $str; } # ============================================================================= # Read capa.config into global variables # sub S_ReadCAPAconfig { local($classpath)=@_; local($filename); local($filename2); local($tempfilename); local($input_line); local($done)=0; local($tmp); @MainPath=(); push(@MainPath,$classpath); $ClassName = substr($classpath,-8,8); $filename="$classpath" . "/capa.config"; $filename2="$classpath" . "/capautils.config"; foreach $tempfilename ( $filename, $filename2 ) { if(-f $tempfilename) { open(IN, "<$tempfilename") || die "Cannot open file $tempfilename!"; LINE: while( ($input_line = ) && (! $done) ) { ## skip over comments if( $input_line =~ /^\#/ ) { next LINE; } chop($input_line); # collect _path information and create the corresponding global variable if( ($input_line =~ /^(\w+)_path\s*=\s*["]([\.\d\+\-\w\s\/]+)["]$/) || ($input_line =~ /^(\w+)_path\s*=\s*(\S+)$/) ) { $tmp = ucfirst lc $1; $tmp = "$tmp" . "Path"; push(@MainPath,$2); ${$tmp} = "$2"; # collect printer_option information } elsif ( ($input_line =~ /^\s*printer_option\s*=\s*["]([\.\d\+\-\w\s\/\|\$]+)["]$/) || ($input_line =~ /^\s*printer_option\s*=\s*(\S+)$/ ) ) { push(@Printers,$1); ## # collect _command information } elsif ( ($input_line =~ /^\s*(\w+)_command\s*=\s*["]([\.\d\+\-\w\s\/\|\$]+)["]$/) || ($input_line =~ /^\s*(\w+)_command\s*=\s*(\S+)$/ ) ) { $tmp = ucfirst lc $1; $tmp = "$tmp" . "CMD"; ${$tmp} = "$2"; ## print "CMD \$$tmp: [$2]\n"; ## print "Press RETURN to continue"; $tmp = <>; # collect _file information } elsif ( ($input_line =~ /^\s*(\w+)_file\s*=\s*["]([\.\d\+\-\w\s\/]+)["]$/) || ($input_line =~ /^\s*(\w+)_file\s*=\s*(\S+)$/ ) ) { $tmp = ucfirst lc $1; $tmp = "$tmp" . "File"; ${$tmp} = "$2"; # var_ definition } elsif ( ($input_line =~ /^\s*var_(\w+)\s*:=\s*["]([\.\d\+\-\w\s\/]+)["]$/) || ($input_line =~ /^\s*var_(\w+)\s*:=\s*(\S+)$/ ) ) { # we defined a variable name with that value $tmp = ucfirst lc $1; $Var_name{$tmp} = "$2"; # two levels of indirection # the variable should be ${$2} # prefix_ definition } elsif ( ($input_line =~ /^\s*prefix_(\w+)\s*:=\s*["]([\.\d\+\-\w\s\/]+)["]$/) || ($input_line =~ /^\s*prefix_(\w+)\s*:=\s*(\S+)$/ ) ) { $tmp = ucfirst lc $1; $Prefix_name{$tmp} = "$2"; # assigning an numerical value to a variable } elsif ( ($input_line =~ /^\s*(\w+)\s*=\s*([\.\d]+)/) ) { ${$1} = $2; # print "Assign \$$1: with $2\n"; # print "Press RETURN to continue"; $tmp = <>; # assigning a variable to another variable } elsif ( ($input_line =~ /^\s*(\w+)\s*=\s*([\w]+)/) ) { $P_code{$1} = "\$$2"; ## print "PERL $1: { $P_code{$1} }\n"; ## print "Press RETURN to continue"; $tmp = <>; # perl code } elsif ( ($input_line =~ /^\s*(\w+)\s*::/) ) { $p_var_name = $1; } elsif ( ($input_line =~ /^\s*BEGIN_perl/) ) { $P_code{$p_var_name} = &E_CollectPerlCode; ## print "PERL $p_var_name: { $P_code{$p_var_name} }\n"; ## print "Press RETURN to continue"; $tmp = <>; } elsif ($input_line =~ /[Bb][Aa][Ss][Ee][\s][Uu][Nn][Ii][Tt]/) { $done = 1; } } close(IN) || die "Cannot close file $tempfilename!"; } # define some global variables if they are not defined properly in capa.config file $homework_scores_limit_set = 99 if $homework_scores_limit_set eq ""; $exam_scores_limit_set = 99 if $exam_scores_limit_set eq ""; $quiz_scores_limit_set = 99 if $quiz_scores_limit_set eq ""; $supp_scores_limit_set = 99 if $supp_scores_limit_set eq ""; $others_scores_limit_set = 99 if $others_scores_limit_set eq ""; $display_score_row_limit = 40 if $display_score_row_limit eq ""; ## print "Press RETURN to continue"; $tmp = <>; } } # # Collect perl codes from until it encountered a # END_perl statement # Skips over comments (begin with a # mark) # It then places all the codes withing a pair of '{' and '}' # sub E_CollectPerlCode { local($input_line); local($p_code,$done); $p_code = "{ \n"; # begining of the code $done=0; while( ($input_line = ) && (! $done) ) { if( $input_line =~ /^END_perl/ ) { $done = 1; } else { if( $input_line !~ /^\#/ ) { # skip over comments $p_code = "$p_code" . "$input_line"; } } } $p_code = "$p_code" . " }\n"; # ending of the code return ($p_code); } # # Collects e-mail template code until it encountered # a END_template statement in the file # It skips over comments that begin with a # mark # sub E_CollectTemplateCode { local($input_line); local($p_code,$done); $done=0; while( ($input_line = ) && (! $done) ) { if( $input_line =~ /^END_template/ ) { $done = 1; } else { if( $input_line !~ /^\#/ ) { # skip over comments $p_code = "$p_code" . "$input_line"; } } } return ($p_code); } # # $MailCMD comes from the entry 'mail_command' in capa.config # sub S_Mailto { local($e_address,$e_file)=@_; local($m_subject); local($cmd); local($tmp); $m_subject = "Current Status on $ClassName"; $cmd = "$MailCMD -s '$m_subject' $e_address < $e_file"; print "Mail File $e_file To $e_address\n"; system($cmd); # ??? How do we know this command successfully returns? } # TODO:: Check the validity of $e_addr # # INPUT: a string represents the category to mail to # it could be "3" or "0" or "1,3,4" # "0" means 1,2,3, and 4 # "1,3,4" means 1,3, and 4 # anything greater than 4 is not valid in the input # therefore category 5 represent those that are # falling through the gaps of *_high and *_low # sub S_MailtoCategory { local($cat)=@_; local(@all_files); local(@a_cat); local($i,$j,$a_char); local($s_id,$s_name,$s_sec,$e_addr); local($orig_filename,$new_filename); local($tmp); opendir(EDIR, "$ClassPath/Mail") || die "cannot opendir $ClassPath/Mail!"; @all_files = grep !/^\.\.?$/, readdir EDIR; closedir EDIR; if( ( $cat =~ /,/ ) || ( $cat == 0 ) ) { if( $cat =~ /,/ ) { @a_cat = split(/,/,$cat); } else { @a_cat = (1,2,3,4); } for( $i=0;$i<=$#all_files;$i++) { for( $j=0;$j<=$#a_cat;$j++) { $a_char = $a_cat[$j]; if( $all_files[$i] =~ /([\w\d]+)\.[\w\d]+\.$a_char$/ ) { $s_id = $1; ($s_name,$s_sec,$e_addr) = S_Lookup_student_name("$s_id"); if( $e_addr ne "" ) { $orig_filename = "$ClassPath/Mail/$all_files[$i]"; S_Mailto("$e_addr","$orig_filename"); print "moving $all_files[$i] to $all_files[$i].done\n"; # move the completed file to *.done system("mv $orig_filename $orig_filename.done"); } } } } } else { for( $i=0;$i<=$#all_files;$i++) { if( $all_files[$i] =~ /([\w\d]+)\.[\w\d]+\.$cat$/ ) { $s_id = $1; ($s_name,$s_sec,$e_addr) = S_Lookup_student_name("$s_id"); print "Addr=$e_addr\n"; print "Press RETURN to continue"; $tmp = <>; if( $e_addr ne "" ) { $orig_filename = "$ClassPath/Mail/$all_files[$i]"; S_Mailto("$e_addr","$orig_filename"); print "moving $all_files[$i] to $all_files[$i].done\n"; system("mv $orig_filename $orig_filename.done"); } } } } print "DONE Mail, press RETURN to continue"; $tmp = <>; } # # It prompts the user to enter an absolute path to a regular class # sub S_Enterpath { local($set)=@_; local($notdone,$path,$cfgfullpath,$cfgutilsfullpath); local($cfullpath,$rfullpath,$sfullpath); $notdone = 1; while ($notdone) { print "Please enter the CLASS absolute path:\n"; $path = <>; chomp($path); if( $path =~ /\/$/ ) { $cfullpath = "$path" . "classl"; $rfullpath = "$path" . "records"; $sfullpath = "$path" . "records/set$set.db"; $cfgfullpath = "$path" . "capa.config"; $cfgutilsfullpath = "$path" . "capautils.config"; } else { $cfullpath = "$path" . "/classl"; $rfullpath = "$path" . "/records"; $sfullpath = "$path" . "/records/set$set.db"; $cfgfullpath = "$path" . "/capa.config"; $cfgutilsfullpath = "$path" . "/capautils.config"; } if( -d $path ) { if( -d $rfullpath ) { if( -f $cfgfullpath ) { if( -f $cfgutilsfullpath ) { $notdone = 0; } else { print "File [$cfgutilsfullpath] does not exist!\n"; } } else { print "File [$cfgfullpath] does not exist!\n"; } } else { print "Directory [$rfullpath] does not exist!\n"; } } else { print "Directory [$path] does not exist!\n"; } } return ($path); } # ---------------------------------------------------------- # Global menu items to be selected by user # @Main_menu=( "Change class path", "Run capastat", "Log analysis on Y, N, S, U, and u", "Student course profile", "CAPA IDs for one student", "All CAPA IDs", "Item analysis", "Item correlation", "Email", "Print assignment(s) for a student", "View score file", "View submissions for a student", "Quit"); @Prof_menu=( "Student number", "Student name", "Cancel" ); @Email_menu=( "Create score file for all students", "Create individual e-mail files from the file in 1.", "Preview e-mail file randomly from among the files in 2.", "Send e-mail files in 2. to a group of students", "Cancel" ); @ScoreSortMsg=( "Sort by the order of: ", " 1. Grade", " 2. Student number", " 3. Student name", " 4. Section", " ", "3,2 means 'name' first, 'student number' second", "1,4,3 means sort by 'grade', 'section' and 'name'" ); @SpecifyCategoryMsg = ("Which category?", " enter number(s) between 0 and 4", " 1,2,4 : categories 1, 2 and 4", " 3 : category 3", " 0 : all categories" ); # # Only accepts input of 0, 1, 2, 3, and 4, nothing else. # sub S_EnterCategory { local($cat); local($done)=0; local(@a_cat,$i); while(! $done) { $cat = C_InputSetNum(4,10,45,"",12,,"CATEGORY:", @SpecifyCategoryMsg); if( $cat =~ /,/ ) { @a_cat = split(/,/,$cat); $done = 1; for( $i=0;$i<=$#a_cat;$i++) { if( ($a_cat[$i] < 0 || $a_cat[$i] > 4) && ($a_cat[$i] ne "" ) ) { $done = 0; } if( $a_cat[$i] == 0 ) { $done = 0; } } } else { if( $cat>= 0 && $cat <= 4 ) { $done = 1; } } } return ($cat); } # Only allows input of 1, 2, 3, and 4, nothing more sub S_EnterSortKey { local($key); local($done)=0; local(@a_key,$i); while(! $done) { $key = C_InputSetNum(2,5,60,"",12,,"KEY:",@ScoreSortMsg); if( $key =~ /,/ ) { @a_key = split(/,/,$key); $done = 1; for( $i=0;$i<=$#a_key;$i++) { if( ($a_cat[$i] < 0 || $a_cat[$i] > 4) && ($a_key[$i] ne "" ) ) { $done = 0; } if( $a_key[$i] == 0 ) { $done = 0; } } } else { if( $key> 0 && $key < 5 ) { $done = 1; } } } return ($key); } @EnterSetMsg = ("Which set?"); @EnterSetsMsg = ("Which set(s)?", " 3,7 : from set 3 to set 7 (both inclusive)", " 4 : only set 4" ); sub S_EnterSets { local($set); local($done)=0; local($s_from,$s_to); while(! $done) { $set = C_InputSetNum(4,10,45,"",6,,"SET:", @EnterSetsMsg); if( $set =~ /,/ ) { ($s_from,$s_to) = split(/,/,$set); if( $s_from <= 0 || $s_from >= 100 ) { $s_from = 1; } if( $s_to <= 0 || $s_to >= 100 ) { $s_to = 1; } if( $s_from > $s_to) { $tmp = $s_from; $s_from = $s_to; $s_to = $tmp; } $done = 1; } else { if( $set> 0 && $set < 100 ) { $s_to = $set; $s_from = $s_to; $done = 1; } } } return ($s_from,$s_to); } sub S_InputSet { local($set); local($done)=0; while(! $done) { $set = C_InputSetNum(4,10,15,"",2,,"SET:", @EnterSetMsg); if( $set =~ /\d+/ && $set > 0 && $set < 100 ) { # check entered set $done = 1; } } return ($set); } @EnterSSNMsg = ("Enter student number?", " (RETURN to exit)" ); @EnterSNMsg = ("Enter student name (max 30 chars)?", "Last, First (Middle)", " (RETURN to exit)" ); sub S_InputStudent { local($classpath)=@_; local($filename); local($select,$tmp_sn,$input_line); local($student_id,$student_name); local($found,$done,$s_name); local($match,@matched_entries,$tmp_line); $select = C_MultipleChoice(4,10,$DialogWidth,"Select student by:","$DisplayPath",@Prof_menu); if($select == 1 ) { # specify student number while(! $done) { $student_id = C_InputStudentID(4,10,24,"",9,"INPUT:",@EnterSSNMsg); if($student_id eq "" ) { $done = 1; } else { $student_id = uc($student_id); $filename = $classpath . "/classl"; open(IN, "<$filename") || die "Cannot open file $filename!"; $match = 0; @matched_entries = (); while(($input_line = )) { chomp($input_line); $tmp_sn = substr($input_line,14,9); $tmp_sn = uc($tmp_sn); if($tmp_sn =~ /^$student_id/ ) { $match++; # student name begins at column 24 and has 30 chars max $student_name = substr($input_line,24,30); $tmp_line = "$tmp_sn " . "$student_name"; push(@matched_entries,$tmp_line); } } close(IN) || die "Cannot close file $filename!"; if($match > 1 && $match <= 12) { $select = C_MultipleChoice(4,10,$DialogWidth," Matched Student Records ", "$DisplayPath",@matched_entries); $student_id = substr($matched_entries[$select-1],0,9); $student_name = substr($matched_entries[$select-1],10,30); $done = 1; } elsif ($match == 1) { $student_id = substr($matched_entries[0],0,9); $student_name = substr($matched_entries[0],10,30); $done = 1; } elsif ($match > 12) { $tmp_line = "There are $match records found."; C_Warn(4,10,$DialogWidth,"Too many students matched",$tmp_line); } else { $tmp_line = "Please re-enter student number."; C_Warn(4,10,$DialogWidth,"No student found",$tmp_line); } } } } elsif ($select == 2) { # specify student name while(! $done) { $s_name = C_InputStudentID(4,10,40,"Enter student name",30,,"INPUT:", @EnterSNMsg); if($s_name eq "" ) { $done = 1; } else { $s_name = uc($s_name); $filename = $classpath . "/classl"; open(IN, "<$filename") || die "Cannot open file $filename!"; $match = 0; @matched_entries = (); while(($input_line = )) { chomp($input_line); $tmp_sn = substr($input_line,24,30); $tmp_sn = uc($tmp_sn); if( $tmp_sn =~ /^$s_name/ ) { $match++; $student_id = substr($input_line,14,9); # student number $tmp_line = "$student_id " . "$tmp_sn"; push(@matched_entries,$tmp_line); } } close(IN) || die "Cannot close file $filename!"; if($match > 1 && $match <= 12) { $select = C_MultipleChoice(4,10,$DialogWidth," Matched Student Records ", "$DisplayPath",@matched_entries); $student_id = substr($matched_entries[$select-1],0,9); $student_name = substr($matched_entries[$select-1],10,30); $done = 1; } elsif ($match == 1) { $student_id = substr($matched_entries[0],0,9); $student_name = substr($matched_entries[0],10,30); $done = 1; } elsif ($match > 12) { $tmp_line = "There are $match records found."; C_Warn(4,10,$DialogWidth,"Too many students matched",$tmp_line); } else { $tmp_line = "Please re-enter student name."; C_Warn(4,10,$DialogWidth,"No student found",$tmp_line); } } } } else { # cancel $student_id = ""; $student_name = ""; } return ($student_id,$student_name); } # # INPUT: the class name with full path and the student number # OUTPUT: total scores , total possible wights sub S_CollectSetScores { local($classpath,$student_id,$on_screen,$s_limit)=@_; local($filename,$found,$classname); local($done)=0; local($line_cnt,$input_line); local(@weights); # the wights array for individual set local($valid_weights); # the valid weights for a set local($total_weights); # the overall valid weights for all sets local(@set_weights); # the array storing all set valid weights local($score); # the valid score for a set local($total_scores); # the overall valid scores for all sets local(@set_scores); # the array storing all set scores local($set_idx); local($ii,$abscent_cnt,$present_cnt); local($s_num,$ans_str,$prefix,$rest); local(@ans_char,$ratio,$tmp,$summary_str); $student_id = uc($student_id); $total_scores = 0; $total_weights = 0; # initialize the overall results $set_idx = 0; while( ! $done ) { $set_idx++; # start out as set1.db, then add one to it if($set_idx <= $s_limit ) { # the limit set is inclusive $filename = $classpath . "/records/set" . "$set_idx" . ".db"; if( -f $filename) { # file exists! open(IN, "<$filename") || die "Cannot open file $filename!"; $line_cnt=0; $found = 0; # for each file opened, initialize $line_cnt and $found while ( ($input_line = ) && !$found) { $line_cnt++; # add one to line count if( $line_cnt == 2 ) { # second line is the weight for each problem chomp(); # delete the trailing '\n' char (@weights) = split(/ */,$input_line); # split the line into each individual chars $valid_weights = 0; for($ii=0;$ii<=$#weights;$ii++) { $valid_weights += $weights[$ii]; # for now $valid_weights contains the sum } ## &C_ClearScreen; ## print "Second line, $input_line, total weight=$valid_weights\n"; ## printf "Press RETURN to continue"; $tmp = <>; } if( $line_cnt > 3) { # start from line 4 is the student data chomp($input_line); # delete the trailing '\n' char ($prefix,$rest) = split(/,/,$input_line,2); # split the whole line into two parts ($s_num,$ans_str) = split(/ /,$prefix,2); # split into two parts $s_num = uc($s_num); if( $student_id eq $s_num ) { # found the student we want ## &C_ClearScreen; ## print "FOUND [$input_line] $s_num == $student_id: weight= $valid_weights,ANS=$ans_str\n"; ## printf "Press RETURN to continue"; $tmp = <>; $found = 1; # so we can exit the search through while loop (@ans_char) = split(/ */,$ans_str); # split the answer string into individual ans chars for($valid = 'N', $ii=0;$ii<=$#ans_char;$ii++) { # from question 0, 1, to last question -1 $valid = 'Y' if $ans_char[$ii] ne '-'; # if ans char is different from '-', then } if( $valid eq 'Y' ) { # don't bother with the record full of '-'s for($score=0,$ii=0;$ii<=$#ans_char;$ii++) { # initialize $score and index $ii if($ans_char[$ii] eq 'Y') { $score += $weights[$ii]; } if($ans_char[$ii] eq 'y') { $score += $weights[$ii]; } if( $ans_char[$ii] ge '0' && $ans_char[$ii] le '9') { $score += $ans_char[$ii]; } if($ans_char[$ii] eq 'E') { # subtract the weight from execused problem $valid_weights -= $weights[$ii]; } } $total_scores += $score; # add the calculated score to the overall sum } else { # end of a valid line $score = '-'; } } } # end $line_cnt > 3 } # end while close(IN) || die "Cannot close file $filename!"; $total_weights += $valid_weights; # add the valid weights for a set to the overall sum $set_weights[$set_idx-1] = $valid_weights; if( $found ) { ## push(@set_scores, $score); # push set score into array ## push(@set_weights, $valid_weights); # push valid weight for a set into the weight array $set_scores[$set_idx-1] = $score; } else { # student not found in the setX.db file $set_scores[$set_idx-1] = '-'; } } else { # $set_idx > $s_limit $done = 1; # exit the $done while loop } } else { $done = 1; # exit the $done while loop } } # end while ! $done # print out the report # &C_ClearScreen; $abscent_cnt=0; $present_cnt=0; $summary_str = ""; print " " x 10 if $on_screen; for($ii=0;$ii<=$#set_scores;$ii++) { if( $set_scores[$ii] eq '-' || $set_scores[$ii] eq "" ) { print " - " if $on_screen; $summary_str = "$summary_str" . "x/$set_weights[$ii] "; $abscent_cnt++; } else { printf " %3d", $set_scores[$ii] if $on_screen; $summary_str = "$summary_str" . "$set_scores[$ii]/$set_weights[$ii] "; $present_cnt++; } } if( $on_screen ) { $classname = substr($classpath,-8,8); print "\n $classname:"; for($ii=0;$ii<=$#set_scores;$ii++) { print " ---"; } print "\n "; for($ii=0;$ii<=$#set_weights;$ii++) { printf " %3d", $set_weights[$ii]; } print "\n"; if($total_weights != 0 ) { $ratio = 100.0 * ($total_scores / $total_weights); } else { $ratio = '-'; } printf " %5d\n", $total_scores if $on_screen; printf " ------- = %3.2f%%, scores abscent in %d/%d\n", $ratio, $abscent_cnt, $#set_scores+1; printf " %5d\n", $total_weights; ## print "Press RETURN to continue"; $tmp = <>; } return ($total_scores,$total_weights,$abscent_cnt,$#set_scores+1,$summary_str); } # # similar to S_CollectSetScores # sub S_CollectExamScores { local($classpath,$student_id)=@_; local($filename,$found,$classname); local($done)=0; local($line_cnt,$input_line); local(@weights); # the wights array for individual set local($valid_weights); # the valid weights for a set local($total_weights); # the overall valid weights for all sets local(@set_weights); # the array storing all set valid weights local($score); # the valid score for a set local($total_scores); # the overall valid scores for all sets local(@set_scores); # the array storing all set scores local($set_idx); local($ii,$var_name); local($s_num,$ans_str,$prefix,$rest); local($midterm1,$midterm2,$midterm3,$f_score); local($m_max1,$m_max2,$m_max3,$f_max); local(@ans_char,$ratio,$tmp); $student_id = uc($student_id); $total_scores = 0; $total_weights = 0; # initialize the overall results $set_idx = 0; while( ! $done ) { $set_idx++; # start out as set1.db, then add one to it if( $set_idx <= $exam_scores_limit_set ) { # $exam_scores_limit_set comes from capa.config $filename = $classpath . "/records/set" . "$set_idx" . ".db"; if( -f $filename) { # file exists! open(IN, "<$filename") || die "Cannot open file $filename!"; $line_cnt=0; $found = 0; # for each file opened, initialize $line_cnt and $found while ( ($input_line = ) && !$found) { $line_cnt++; # add one to line count if( $line_cnt == 2 ) { # second line is the weight for each problem chomp(); # delete the trailing '\n' char (@weights) = split(/ */,$input_line); # split the line into each individual chars $valid_weights = 0; for($ii=0;$ii<=$#weights;$ii++) { $valid_weights += $weights[$ii]; # for now $valid_weights contains the sum } } if( $line_cnt > 3) { # start from line 4 is the student data chomp($input_line); # delete the trailing '\n' char ($prefix,$rest) = split(/,/,$input_line,2); # split the whole line into two parts ($s_num,$ans_str) = split(/ /,$prefix,2); # split into two parts $s_num = uc($s_num); if( $student_id eq $s_num ) { # found the student we want $found = 1; # so we can exit the search through while loop (@ans_char) = split(/ */,$ans_str); # split the answer string into individual ans chars for($valid = 'N', $ii=0;$ii<=$#ans_char;$ii++) { # from question 0, 1, to last question -1 $valid = 'Y' if $ans_char[$ii] ne '-'; # if ans char is different from '-', then } if( $valid eq 'Y' ) { # don't bother with the record full of '-'s for($score=0,$ii=0;$ii<=$#ans_char;$ii++) { # initialize $score and index $ii if($ans_char[$ii] eq 'Y') { $score += $weights[$ii]; } if($ans_char[$ii] eq 'y') { $score += $weights[$ii]; } if( $ans_char[$ii] ge '0' && $ans_char[$ii] le '9') { $score += $ans_char[$ii]; } if($ans_char[$ii] eq 'E') { # subtract the weight from execused problem $valid_weights -= $weights[$ii]; } } $total_scores += $score; # add the calculated score to the overall sum } else { # end of a valid line $score = '-'; } } # end student number comparison } # end $line_cnt > 3 } # end while close(IN) || die "Cannot close file $filename!"; $total_weights += $valid_weights; # add the valid weights for a set to the overall sum $set_weights[$set_idx-1] = $valid_weights; # push(@set_scores, $score); # push set score into array # push(@set_weights, $valid_weights); # push valid weight for a set into the weight array if( $found ) { $set_scores[$set_idx-1] = $score; } else { # could not locate the student in the setX.db file $set_scores[$set_idx-1] = '-'; } } else { # $set_idx > $exam_scores_limit_set $done = 1; } } else { $done = 1; # exit the $done while loop } } # end while ! $done # put scores in the global variables: exam_raw1, exam_raw2 ... # and maximum weights in variables: exam_raw_max1, exam_raw_max2,.. # prefix is stored in : # $Prefix_name{"Exam_raw_scores"} # $Prefix_name{"Exam_raw_max"} # $Prefix_name{'Exam_raw_scores'} = "exam_raw" if ! defined $Prefix_name{'Exam_raw_scores'}; $Prefix_name{'Exam_raw_max'} = "exam_raw_max" if ! defined $Prefix_name{'Exam_raw_max'}; for($ii=0;$ii<=$#set_scores;$ii++) { $set_idx = $ii+1; $var_name = "$Prefix_name{'Exam_raw_scores'}" . "$set_idx"; ${$var_name} = $set_scores[$ii]; $var_name = "$Prefix_name{'Exam_raw_max'}" . "$set_idx"; ${$var_name} = $set_weights[$ii]; } # # initialize these local variables # $midterm1 = '-'; $m_max1 = '-'; $midterm2 = '-'; $m_max2 = '-'; $midterm3 = '-'; $m_max3 = '-'; $f_score = '-'; $f_max = '-'; # After we placed raw scores into the global variables exam_raw1, exam_raw2 ... # we can then, evaluate the definitions of midterm1, midterm2 and midterm3 from capa.config if($#set_scores >= 1) { # at least 2 sets $midterm1 = eval $P_code{'midterm1'}; $var_name = "$Prefix_name{'Exam_raw_max'}" . "1"; # for max possible scores, just pick a set $m_max1 = ${$var_name}; if($#set_scores >= 3) { # at least 4 sets $midterm2 = eval $P_code{'midterm2'}; $var_name = "$Prefix_name{'Exam_raw_max'}" . "3"; # for max possible scores, just pick a set $m_max2 = ${$var_name}; if($#set_scores >= 5) { # at least 6 sets $midterm3 = eval $P_code{'midterm3'}; $var_name = "$Prefix_name{'Exam_raw_max'}" . "5"; # for max possible scores, just pick a set $m_max3 = ${$var_name}; if($#set_scores == 6 ) { # the 7th set # in capa.config a variable $final_exam is defined as # the same with $exam_raw7 ## $var_name = "$Prefix_name{'Exam_raw_scores'}" . "7"; ## $f_score = ${$var_name}; $f_score = eval $P_code{'final_exam'}; $var_name = "$Prefix_name{'Exam_raw_max'}" . "7"; $f_max = ${$var_name}; } else { # only 6 sets } } else { # 4 or 5 sets } } else { # 2 or 3 sets } } else { # 0 or 1 sets } return ($midterm1,$m_max1,$midterm2,$m_max2,$midterm3,$m_max3,$f_score,$f_max,$#set_scores+1); } # # Menu item: capastat # ($Q_cnt,$L_cnt) = &S_ScanSetDB($Sfullpath); # Percentage_Scores($Set); # S_Average($Q_cnt,$L_cnt); # # INPUT: the setX.db file name with full path # sub S_ScanSetDB { local($filename)=@_; local($line_cnt)=0; local($valid_cnt)=0; local($valid); local($ii); local($s_num,$ans_str,$prefix,$rest); local(@ans_char,@tries); local($score); for($ii=0;$ii<=99;$ii++) { $Total_try[$ii]=0; $Yes_cnt[$ii]=0; $yes_cnt[$ii]=0; for($jj=0;$jj<=99;$jj++) { $Student_cnt[$ii][$jj]=0; $Student_try[$ii][$jj]=0; } } $Total_weight=0; $Total_scores=0; open(IN, "<$filename") || die "Cannot open file $filename!"; while () { $line_cnt++; if( $line_cnt == 2 ) { chomp(); (@Weight) = split(/ */); } if( $line_cnt > 3) { chomp(); ($prefix,$rest) = split(/,/,$_,2); ($s_num,$ans_str) = split(/ /,$prefix,2); (@ans_char) = split(/ */,$ans_str); (@tries) = split(/,/,$rest); for($valid = 'N', $ii=0;$ii<=$#ans_char;$ii++) { $valid = 'Y' if $ans_char[$ii] ne '-'; } if( $valid eq 'Y' ) { for($score=0,$ii=0;$ii<=$#tries;$ii++) { $Student_cnt[$ii][$tries[$ii]]++; $Student_try[$valid_cnt][$ii] = $tries[$ii]; $Total_try[$ii] += $tries[$ii]; $Total_weight += $Weight[$ii]; if($ans_char[$ii] eq 'Y') { $Yes_cnt[$ii]++; $score += $Weight[$ii]; } if($ans_char[$ii] eq 'y') { $yes_cnt[$ii]++; $score += $Weight[$ii]; } if( $ans_char[$ii] ge '0' && $ans_char[$ii] le '9') { $score += $ans_char[$ii]; } } $Total_scores += $score; $Entry{"$valid_cnt"} = "$s_num\n" . "$ans_str," . " $rest\n"; $Score{"$valid_cnt"} = $score; $valid_cnt++; } } } close(IN) || die "Cannot close $filename file!"; return ($#tries+1,$valid_cnt); } $MAX_TRIES = 99; sub S_Average { local($q_cnt,$l_cnt)=@_; local($ii,$jj); local(@s_cnt,@avg); local(@sd, $sum); local($sq); local(@sd3,$tmp1,$tmp2,$done); for($ii=0;$ii<$q_cnt;$ii++) { $s_cnt[$ii] = 0; $avg[$ii] = 0.0; $Max_try[$ii] = 0; for($jj=1;$jj<$MAX_TRIES;$jj++) { # ignore the 0 try entry if( $Student_cnt[$ii][$jj] > 0 ) { $avg[$ii] += $jj*$Student_cnt[$ii][$jj]; $s_cnt[$ii] += $Student_cnt[$ii][$jj]; } } if( $s_cnt[$ii] > 0 ) { # avoid division by zero $avg[$ii] = $avg[$ii] / $s_cnt[$ii]; } } for($ii=0;$ii<$q_cnt;$ii++) { $sd[$ii] = 0.0; $sum = 0.0; for($jj=0;$jj<$l_cnt;$jj++) { $Max_try[$ii] = ($Student_try[$jj][$ii] > $Max_try[$ii]? $Student_try[$jj][$ii] : $Max_try[$ii]); if( $Student_try[$jj][$ii] > 0 ) { $sq = ($Student_try[$jj][$ii] - $avg[$ii])*($Student_try[$jj][$ii] - $avg[$ii]); $sum += $sq; } if( $s_cnt[$ii] > 1 ) { $sd[$ii] = $sum / ($s_cnt[$ii] - 1.0 ); } if( $sd[$ii] > 0 ) { $sd[$ii] = sqrt( $sd[$ii] ); } } } for($ii=0;$ii<$q_cnt;$ii++) { $sd3[$ii] = 0.0; $sum = 0.0; for($jj=0;$jj<$l_cnt;$jj++) { if( $Student_try[$jj][$ii] > 0 ) { $tmp1 = $Student_try[$jj][$ii] - $avg[$ii]; $tmp2 = $tmp1*$tmp1*$tmp1; $sum = $sum + $tmp2; } if( $s_cnt[$ii] > 0 && $sd[$ii] != 0.0 ) { $sd3[$ii] = $sum/$s_cnt[$ii] ; $sd3[$ii] = $sd3[$ii] / ($sd[$ii]*$sd[$ii]*$sd[$ii]); } } } print "This is the statistics for each problem:\n"; print "Prob\# MxTries avg. s.d. s.k. \#Stdnts "; print " \#Yes \#yes Tries DoDiff\n"; for($ii=0;$ii<$q_cnt;$ii++) { if( $Total_try[$ii] > 0 ) { ## $dod = 1-($Yes_cnt[$ii] + $yes_cnt[$ii]) / $Total_try[$ii]; $dod = $Total_try[$ii]/(0.1 + $Yes_cnt[$ii] + $yes_cnt[$ii]); } printf "P %2d:",$ii+1; printf "%7d %8.2f %7.2f %6.2f %5d %5d %5d %5d %5.1f\n", $Max_try[$ii],$avg[$ii],$sd[$ii],$sd3[$ii],$s_cnt[$ii],$Yes_cnt[$ii],$yes_cnt[$ii], $Total_try[$ii],$dod; } printf "Press RETURN to continue"; $done = <>; } sub Percentage_Scores { local($set,$valid_cnt)=@_; local($ratio); local($done); if($Total_weight > 0 ) { $ratio = $Total_scores / $Total_weight; $ratio = $ratio * 100.0; } printf "\nThe percentage score (total scores / total valid weights) for set%d.db is:\n %7.2f%%\n",$set,$ratio; printf "The number of valid records for set%d.db is: %d\n", $set, $valid_cnt; printf "Press RETURN to continue"; $done=<>; } sub Large_Tries { local($t,$n,$q_cnt,$l_cnt)=@_; local($ii); print "\nHere is a list of students who attempts $t tries more than $n times: \n\n"; for ($i=0;$i<$l_cnt;$i++){ $count=0; $credit=0; for ($j=0;$j<$q_cnt;$j++){ if ($Student_try[$i][$j]>= $t){ $count++; } } if ($count >= $n){ print "($Score{$i}) $Entry{$i} \n"; } } } sub S_ScanLogDB { local($filename)=@_; local($line_cnt)=0; local($s_num,$dow,$mon,$sp,$day,$time,$yr,$ans_str); local(@ans_char); local($ii,$first,$done); local($Yes_cnt[99],$No_cnt[99],$U_cnt[99],$S_cnt[99],$u_cnt[99]); local($Y_total,$N_total,$U_total,$u_total,$S_total); $Y_total=0; $N_total=0; $U_total=0; $u_total=0; $S_total=0; open(IN, "<$filename") || die "Cannot open file $filename!"; for($ii=0;$ii<=99;$ii++) { $Yes_cnt[$ii] = 0; $No_cnt[$ii] = 0; $U_cnt[$ii]=0; $u_cnt[$ii]=0; $S_cnt[$ii]=0; } while () { $line_cnt++; chomp(); $ans_str = substr($_,35); ## ($first,$ans_str) = split(/1996 /); # depends on this special pattern # print "$ans_str\n"; (@ans_char) = split(/ */,$ans_str); for($ii=0;$ii<=$#ans_char;$ii++) { if($ans_char[$ii] eq 'Y') { $Yes_cnt[$ii]++; $Y_total++; } if($ans_char[$ii] eq 'N') { $No_cnt[$ii]++; $N_total++; } if($ans_char[$ii] eq 'U') { $U_cnt[$ii]++; $U_total++; } if($ans_char[$ii] eq 'u') { $u_cnt[$ii]++; $u_total++; } if($ans_char[$ii] eq 'S') { $S_cnt[$ii]++; $S_total++; } } } close(IN) || die "Cannot close file $filename!"; print "Prob #: #Y #N #S #U #u\n"; for($ii=0;$ii<=$#ans_char;$ii++) { printf " %2d: %6d %6d %6d %6d %6d\n", $ii+1, $Yes_cnt[$ii], $No_cnt[$ii], $S_cnt[$ii], $U_cnt[$ii], $u_cnt[$ii]; } print "=" x 45 . "\n"; printf " Total: %6d %6d %6d %6d %6d\n", $Y_total, $N_total, $S_total, $U_total, $u_total; printf "Press RETURN to continue"; $done = <>; return ($Y_total,$N_total,$S_total,$U_total,$u_total); } sub S_StudentLoginData { local($filename,$student_id)=@_; local($Y_total,$N_total,$S_total,$U_total,$u_total); local($ans_str,@ans_char); local($ii,$s_id); $Y_total=0; $N_total=0; $U_total=0; $u_total=0; $S_total=0; open(IN, "<$filename") || die "Cannot open file $filename!"; while () { chomp(); $s_id = substr($_,0,9); # student number begins at column 0 and has 9 chars $s_id = uc($s_id); $student_id = uc($student_id); if( $student_id eq $s_id) { $ans_str = substr($_,35); # the answer string begins at column 35 (count from 0) (@ans_char) = split(/ */,$ans_str); for($ii=0;$ii<=$#ans_char;$ii++) { if($ans_char[$ii] eq 'Y') { $Y_total++; } if($ans_char[$ii] eq 'N') { $N_total++; } if($ans_char[$ii] eq 'U') { $U_total++; } if($ans_char[$ii] eq 'u') { $u_total++; } if($ans_char[$ii] eq 'S') { $S_total++; } } # end for each problem } # end student number matches } # end while close(IN) || die "Cannot close file $filename!"; return ($Y_total,$N_total,$S_total,$U_total,$u_total); } # sub S_LoginAnalysis { local($classpath,$student_id,$s_limit)=@_; local($Y_set,$N_set,$S_set,$U_set,$u_set); local($set_idx,$no_log,$no_weblog,$done,$tmp); print "Login analysis: telnet session web session\n\n"; print " set #: #Y #N #S #U #u #Y #N #S #U #u\n"; $set_idx=0; $done=0; while (! $done ) { $set_idx++; if( $set_idx <= $s_limit) { printf " %2d: ", $set_idx; $filename= $classpath . "/records/log" . "$set_idx" . ".db"; if(-f $filename) { ($Y_set,$N_set,$S_set,$U_set,$u_set)=S_StudentLoginData($filename,$student_id); printf "%4d %4d %4d %4d %4d", $Y_set,$N_set,$S_set,$U_set,$u_set; $no_log = 0; } else { print "=" x 24; $no_log = 1; } print " " x 4; $filename= $classpath . "/records/weblog" . "$set_idx" . ".db"; if(-f $filename) { ($Y_set,$N_set,$S_set,$U_set,$u_set)= S_StudentLoginData($filename,$student_id); printf "%4d %4d %4d %4d %4d", $Y_set,$N_set,$S_set,$U_set,$u_set; $no_weblog = 0; } else { print "=" x 24; $no_weblog = 1; } print "\n"; if( $no_log && $no_weblog ) { $done = 1; }; } else { # $set_idx > $s_limit $done = 1; } } printf "Press RETURN to continue"; $tmp = <>; } # It pulls out the data base entry from setX.db files sub S_StudentSetAnalysis { local($classpath,$student_id,$s_limit)=@_; local($filename); local($set_idx,$done); local($line_cnt,$found,$input_line); local($s_num,$data,$ans_str,$try_str,$tmp); $set_idx=0; $student_id = uc($student_id); print " set #:\n"; while(! $done) { $set_idx++; if( $set_idx <= $s_limit) { $filename= $classpath . "/records/set" . "$set_idx" . ".db"; if(-f $filename) { printf " %2d: ", $set_idx; open(IN, "<$filename") || die "Cannot open file $filename!"; $line_cnt=0; $found = 0; # for each file opened, initialize $line_cnt and $found while ( ($input_line = ) && !$found) { $line_cnt++; # add one to line count if( $line_cnt > 3) { # start from line 4 is the student data chomp($input_line); # delete the trailing '\n' char $s_num = substr($input_line,0,9); $s_num = uc($s_num); if( $student_id eq $s_num ) { $found = 1; $data = substr($input_line,10); ($ans_str,$try_str) = split(/,/,$data,2); print "$ans_str\n $try_str\n"; } } # $line_cnt > 3 } # end while $input_line and ! $found close(IN) || die "Cannot close file $filename!"; if(! $found ) { # student record entry not in the setX.db file ==> two empty lines print "\n\n"; } } else { # $filename does not exist $done = 1; } } else { # $set_idx > $s_limit $done = 1; } } printf "Press RETURN to continue"; $tmp = <>; } # # INPUTS: class name with full path, set number # sub S_ItemAnalysis { local($classpath,$set)=@_; local($filename,$found,$classname); local($done)=0; local($line_cnt,$input_line); local($valid,$valid_cnt); local(@weights); # the wights array for individual set local($valid_weights); # the valid weights for a set local($total_weights); # the overall valid weights for all sets local(@set_weights); # the array storing all set valid weights local($score); # the valid score for a set local($total_scores); # the overall valid scores for all sets local(@set_scores); # the array storing all set scores local($diff,$disc); local($ii,$upper_percent,$lower_percent); local($s_num,$ans_str,$prefix,$rest); local(@ans_char,$ratio,$tmp); local($Y_cnt[100],$N_cnt[100]); local($Ycnt_upper[100],$Ycnt_lower[100],$tmp_total); local($Y_total,$N_total); local($upperpart_cnt,$lowerpart_limit); local(%s_db); $total_scores = 0; $total_weights = 0; # initialize the overall results $upper_percent = 0.0; $lower_percent = 0.0; for($ii=0;$ii<100;$ii++) { $Y_cnt[$ii] = 0; $N_cnt[$ii] = 0; $Ycnt_upper[$ii] = 0.0; $Ycnt_lower[$ii] = 0.0; } $filename = $classpath . "/records/set" . "$set" . ".db"; if( -f $filename) { # file exists! open(IN, "<$filename") || die "Cannot open file $filename!"; $valid_cnt = 0; # initialize $valid_cnt $line_cnt=0; # initialize $line_cnt while () { $line_cnt++; # add one to line count if( $line_cnt == 2 ) { # second line is the weight for each problem chomp(); # delete the trailing '\n' char (@weights) = split(/ */); # split the line into each individual chars $valid_weights = 0; for($ii=0;$ii<=$#weights;$ii++) { $valid_weights += $weights[$ii]; # for now $valid_weights contains the sum } } if( $line_cnt > 3) { # start from line 4 is the student data chomp(); # delete the trailing '\n' char ($prefix,$rest) = split(/,/,$_,2); # split the whole line into two parts ($s_num,$ans_str) = split(/ /,$prefix,2); # split into two parts $s_num = uc($s_num); ### print "$ans_str\n"; (@ans_char) = split(/ */,$ans_str); # split the answer string into in dividual ans chars for($valid = 'N', $ii=0;$ii<=$#ans_char;$ii++) { # from question 0, 1 , to last question -1 $valid = 'Y' if $ans_char[$ii] ne '-'; # if ans char is different from '-', then } if( $valid eq 'Y' ) { # don't bother with the record full of '-'s $valid_cnt++; for($score=0,$ii=0;$ii<=$#ans_char;$ii++) { # initialize $score and index $ii if($ans_char[$ii] eq 'Y' || $ans_char[$ii] eq 'y') { $score += $weights[$ii]; $Y_cnt[$ii]++; $Y_total++; } if( $ans_char[$ii] eq 'N' || $ans_char[$ii] eq 'n' || $ans_char[$ii] eq '0' ) { $N_cnt[$ii]++; $N_total++; } if( $ans_char[$ii] gt '0' && $ans_char[$ii] le '9') { $score += $ans_char[$ii]; if( $ans_char[$ii] eq $weights[$ii] ) { $Y_cnt[$ii]++; $Y_total++; } else { $N_cnt[$ii]++; $N_total++; } } if($ans_char[$ii] eq 'E') { # subtract the weight from execused problem $valid_weights -= $weights[$ii]; } } # end of score calculation $sort_key = sprintf "%05d%s", $score,$s_num; $s_db{$sort_key} = "$ans_str"; } } # end $line_cnt > 3 } # end while close(IN) || die "Cannot close file $filename!"; for($ii=0;$ii<100;$ii++) { ## $Y_cnt[$ii]=0; $N_cnt[$ii]=0; $Ycnt_upper[$ii] = 0; $Ycnt_lower[$ii] = 0; } $upperpart_cnt = int(0.27 * $valid_cnt); # upper 27 percent $lowerpart_limit = ($valid_cnt - $upperpart_cnt); # beyond 73 percent ### open(DBUG, ">/tmp/f.DBUG") || die "Cannot open /tmp/f.DBUG file!"; $line_cnt=0; foreach $sort_key (reverse sort keys %s_db) { $line_cnt++; ### print DBUG "$sort_key\[$s_db{$sort_key}\]"; $ans_str = "$s_db{$sort_key}"; (@ans_char) = split(/ */,$ans_str); for($ii=0;$ii<=$#ans_char;$ii++) { if( ($ans_char[$ii] eq 'Y') || ($ans_char[$ii] eq 'y') || ($ans_char[$ii] eq $weights[$ii] ) ) { ## only if they got a full credit that ## we count it as 'Y' if($line_cnt <= $upperpart_cnt) { $Ycnt_upper[$ii]++; } elsif ( $line_cnt > $lowerpart_limit ) { $Ycnt_lower[$ii]++; } } ### print DBUG " $Ycnt_upper[$ii]/$Ycnt_lower[$ii] "; } # end for $ii ### print DBUG "\n"; } #end foreach ### close(DBUG); print " There are $valid_cnt entries in file $filename\n"; printf " The upper 27%% has %d records, the lower 27%% has %d records\n", $upperpart_cnt, $valid_cnt-$lowerpart_limit; print " question \# DoDiff. Disc. Factor (%upper - %lower) [#records,#records]\n"; for($ii=0;$ii<=$#ans_char;$ii++) { $tmp_total = $N_cnt[$ii] + $Y_cnt[$ii]; if( $tmp_total > 0 ) { $diff = 100.0*($N_cnt[$ii] / ($N_cnt[$ii] + $Y_cnt[$ii])); } else { $diff = '-'; } $upper_percent = 100.0 * ($Ycnt_upper[$ii] /$upperpart_cnt); $lower_percent = 100.0 * ($Ycnt_lower[$ii] /$upperpart_cnt); $disc = $upper_percent - $lower_percent; printf " %2d: ", $ii+1; printf "%6.1f %5.1f (% 5.1f - % 5.1f) [%4d,%4d]\n", $diff, $disc,$upper_percent,$lower_percent,$Ycnt_upper[$ii],$Ycnt_lower[$ii]; } printf "Press RETURN to continue"; $tmp = <>; } else { # file does not exist! print "FILE: $filename does not exist!\n"; printf "Press RETURN to continue"; $tmp = <>; } } # # INPUTS: class name with full path, set number # sub S_SetCorrelation { local($classpath,$set)=@_; local($filename); local($line_cnt); local($data,$ans_str,@ans_char,$try_str); local($ii,$jj,$question_cnt); local($index_key,@weights); local($first_char,$second_char); local(%corr,%valid_cnt,$ratio,$tmp,$tmp_len,$ratio_str); $filename= $classpath . "/records/set" . "$set" . ".db"; if(-f $filename) { open(IN, "<$filename") || die "Cannot open file $filename!"; $line_cnt=0; while() { $line_cnt++; if( $line_cnt == 2 ) { # second line is the weight for each problem chomp(); # delete the trailing '\n' char (@weights) = split(/ */); # split the line into each individual chars } if( $line_cnt > 3) { chomp(); $data = substr($_,10); # skip over student number ($ans_str,$try_str) = split(/,/,$data,2); (@ans_char) = split(/ */,$ans_str); $question_cnt = $#ans_char; for($ii=0;$ii<$#ans_char;$ii++) { for($jj=$ii+1;$jj<=$#ans_char;$jj++) { $index_key = "$ii" . "$jj"; if( $ans_char[$ii] eq '-' || $ans_char[$ii] eq 'E' ) { # do nothing } else { if( $ans_char[$ii] gt '0' && $ans_char[$ii] le '9') { if( $ans_char[$ii] eq $weights[$ii] ) { $first_char = 'Y'; } else { $first_char = 'N'; } } elsif ($ans_char[$ii] eq '0') { $first_char = 'N'; } elsif ($ans_char[$ii] eq 'y' || $ans_char[$ii] eq 'n') { $first_char = uc($ans_char[$ii]); } else { $first_char = $ans_char[$ii]; } if( $ans_char[$jj] eq '-' || $ans_char[$jj] eq 'E' ) { # do nothing } else { if( $ans_char[$jj] gt '0' && $ans_char[$jj] le '9') { if( $ans_char[$jj] eq $weights[$jj] ) { $second_char = 'Y'; } else { $second_char = 'N'; } } elsif ($ans_char[$jj] eq '0') { $second_char = 'N'; } elsif ($ans_char[$jj] eq 'y' || $ans_char[$jj] eq 'n') { $second_char = uc($ans_char[$jj]); } else { $second_char = $ans_char[$jj]; } if( $first_char eq $second_char ) { if(defined $corr{$index_key} ) { $corr{$index_key}++; } else { $corr{$index_key} = 1; } } else { if(defined $corr{$index_key} ) { $corr{$index_key}--; } else { $corr{$index_key} = -1; } } # add one count to valid_count if(defined $valid_cnt{$index_key}) { $valid_cnt{$index_key}++; } else { $valid_cnt{$index_key} = 1; } } } } # end $jj loop } # end $ii loop } # end $line_cnt > 3 } # end while close(IN) || die "Cannot close file $filename!"; # print out the correlation matrix print " "; for($ii=1;$ii<=$question_cnt;$ii++) { print " " x 4; printf "%2d", $ii+1; } print "\n"; # -------------------------------------- for($ii=0;$ii<$question_cnt;$ii++) { printf " %2d:", $ii+1; print " " x ($ii); for($jj=$ii+1;$jj<=$question_cnt;$jj++) { $index_key = "$ii" . "$jj"; if( defined $corr{$index_key} ) { $ratio = $corr{$index_key} / $valid_cnt{$index_key}; printf " % .2f", $ratio; } else { print " ----"; } } print "\n"; } printf "Press RETURN to continue"; $tmp = <>; } else { # file exists! print "FILE: $filename does not exist!\n"; printf "Press RETURN to continue"; $tmp = <>; } } # INPUTS: class name with full path, set number # # r = \frac{\sum{x_i y_i} - \frac{(\sum x_i)(\sum y_i)}{n}}{\sqrt{(\sum x_i^2 - \frac{}{}}} # # corr = (sum of prod_xy - (sum_x*sum_y / n) ) / sqrt( (sum of sqr_x - (sum_x*sum_x/n))* # sub S_SetCorrelationStatistics { local($classpath,$set)=@_; local($filename); local($line_cnt); local($data,$ans_str,@ans_char,$try_str); local($ii,$jj,$question_cnt); local($index_key,@weights); local($x_data,$y_data); local(%valid_cnt,$ratio,$tmp,$tmp_len,$ratio_str); local(%prod_xy,%sum_x,%sum_y,%sum_x2,%sum_y2); local($upper_part,$lower_part); $filename= $classpath . "/records/set" . "$set" . ".db"; if(-f $filename) { open(IN, "<$filename") || die "Cannot open file $filename!"; $line_cnt=0; while() { $line_cnt++; if( $line_cnt == 2 ) { # second line is the weight for each problem chomp(); # delete the trailing '\n' char (@weights) = split(/ */); # split the line into each individual chars } if( $line_cnt > 3) { chomp(); $data = substr($_,10); # skip over student number ($ans_str,$try_str) = split(/,/,$data,2); (@ans_char) = split(/ */,$ans_str); $question_cnt = $#ans_char; for($ii=0;$ii<$#ans_char;$ii++) { for($jj=$ii+1;$jj<=$#ans_char;$jj++) { $index_key = "$ii" . "$jj"; if( $ans_char[$ii] eq '-' || $ans_char[$ii] eq 'E' ) { # do nothing } else { ## $ans_char[$ii] is one of 0 .. 9, Y, y, N, n if( $ans_char[$jj] eq '-' || $ans_char[$jj] eq 'E' ) { # do nothing } else { if( $ans_char[$ii] eq 'Y' || $ans_char[$ii] eq 'y' ) { $x_data = $weights[$ii]; } elsif ( $ans_char[$ii] eq 'N' || $ans_char[$ii] eq 'n' ) { $x_data = 0; } else { ## must be in 0 .. 9 $x_data = $ans_char[$ii]; } if( $ans_char[$jj] eq 'Y' || $ans_char[$jj] eq 'y' ) { $y_data = $weights[$jj]; } elsif ( $ans_char[$jj] eq 'N' || $ans_char[$jj] eq 'n' ) { $y_data = 0; } else { ## must be in 0 .. 9 $y_data = $ans_char[$jj]; } if(defined $prod_xy{$index_key}) { if ( $ii == 3 && $jj == 7 ) { printf "%f %f %f\n",$x_data,$y_data,$prod_xy{$index_key} } $prod_xy{$index_key} += ($x_data * $y_data); } else { if ( $ii == 3 && $jj == 7 ) { printf "%f %f %f\n",$x_data,$y_data,0.0 } $prod_xy{$index_key} = 0.0; } if(defined $sum_x{$index_key} ) { $sum_x{$index_key} += $x_data; } else { $sum_x{$index_key} = 0; } if(defined $sum_y{$index_key}) { $sum_y{$index_key} += $y_data; } else { $sum_y{$index_key} = 0; } if(defined $sum_x2{$index_key} ) { $sum_x2{$index_key} += ($x_data * $x_data); } else { $sum_x2{$index_key} = 0; } if(defined $sum_y2{$index_key} ) { $sum_y2{$index_key} += ($y_data * $y_data); } else { $sum_y2{$index_key} = 0; } # add one count to valid_count if(defined $valid_cnt{$index_key}) { $valid_cnt{$index_key}++; } else { $valid_cnt{$index_key} = 1; } } } } # end $jj loop } # end $ii loop } # end $line_cnt > 3 } # end while close(IN) || die "Cannot close file $filename!"; # print out the correlation matrix print " "; for($ii=1;$ii<=$question_cnt;$ii++) { print " " x 4; printf "%2d", $ii+1; } print "\n"; # -------------------------------------- for($ii=0;$ii<$question_cnt;$ii++) { printf " %2d:", $ii+1; print " " x ($ii); for($jj=$ii+1;$jj<=$question_cnt;$jj++) { $index_key = "$ii" . "$jj"; if( defined $valid_cnt{$index_key}) { ## there are at least one valid data $upper_part = $prod_xy{$index_key} - ( ($sum_x{$index_key} * $sum_y{$index_key}) / $valid_cnt{$index_key} ); $lower_part = $sum_x2{$index_key} - ($sum_x{$index_key} * $sum_x{$index_key} / $valid_cnt{$index_key} ); $lower_part = $lower_part * ( $sum_y2{$index_key} - ($sum_y{$index_key} * $sum_y{$index_key} / $valid_cnt{$index_key} )); $lower_part = sqrt($lower_part); if ($ii == 3 && $jj==7) { printf "%f %f %f %f",$prod_xy{$index_key},$sum_x{$index_key},$sum_y{$index_key},$valid_cnt{$index_key}; } if( $lower_part != 0.0 ) { $ratio = $upper_part / $lower_part; printf " % .2f", $ratio; } else { print " inf "; } } else { print " ----"; } } print "\n"; } printf "Press RETURN to continue"; $tmp = <>; } else { # file exists! print "FILE: $filename does not exist!\n"; print "Press RETURN to continue"; $tmp = <>; } } # -------------------------- Create a file contains all scores ---- # it will sub S_CreateScores { local($sfilename)=@_; local($filename); local($c_scores,$c_max_scores,$c_abscent,$c_count,$c_summary); local($q_scores,$q_max_scores,$q_abscent,$q_count,$q_summary); local($e_scores,$e_max_scores,$e_abscent,$e_count,$e_summary); local($s_scores,$s_max_scores,$s_abscent,$s_count,$s_summary); local($o_scores,$o_max_scores,$o_abscent,$o_count,$o_summary); local($c_ratio,$q_ratio,$e_ratio,$s_ratio,$o_ratio); local($m_1,$m_2,$m_3,$f_score); local($m_max1,$m_max2,$m_max3,$f_max); local($mt1_ratio,$mt2_ratio,$mt3_ratio,$f_ratio); local($var_name); local($total_score,$day_str); local($e_entry); local($s_key,%f_entry,$pre_entry,$whole_entry,$tail_entry,$score_str); local($tmp); $day_str = &TimeString; $filename = "$ClassPath" . "/classl"; open(CIN, "<$filename") || die "Cannot open file $filename!"; while() { chomp(); $s_id = substr($_,14,9); $s_id = uc($s_id); # $homework_scores_limit_set comes from capa.config ($c_scores,$c_max_scores,$c_abscent,$c_count,$c_summary) = &S_CollectSetScores($ClassPath,"$s_id",0,$homework_scores_limit_set); $c_ratio = $c_scores/$c_max_scores if $c_max_scores != 0; ## print "$c_scores/$c_max_scores,$c_abscent/$c_count\n"; # $quiz_scores_limit_set comes from capa.config ($q_scores,$q_max_scores,$q_abscent,$q_count,$q_summary) = &S_CollectSetScores($QuizPath,"$s_id",0,$quiz_scores_limit_set) if -d "$QuizPath"; $q_ratio = $q_scores/$q_max_scores if $q_max_scores != 0; ## print "$q_scores/$q_max_scores,$q_abscent/$q_count\n"; # exam has different formula ($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"; # we can evaluate midterm1 when $e_count > 2 # the perl code for midterm1 is stored in $P_code{'midterm1'} # FORMULA for exam scores if( $e_count < 1 ) { # 0 set, no extrapolated scores $e_scores = 0; $tail_entry = " --/-- --/-- --/-- --/-- "; } elsif ( ($e_count == 1) || ($e_count == 2) ) { $mt1_ratio = $m_1/$m_max1 if $m_max1 != 0; $e_scores = $mt1_percent * $mt1_ratio + $mt2_percent*$mt1_ratio + $mt3_percent * $mt1_ratio + $final_percent * $mt1_ratio; $e_entry = sprintf " %6.2f/%3d --/-- --/-- --/-- ", $m_1,$m_max1; } elsif ( ($e_count == 3) || ($e_count == 4) ) { $mt1_ratio = $m_1/$m_max1 if $m_max1 != 0; $mt2_ratio = $m_2/$m_max2 if $m_max2 != 0; $e_scores = $mt1_percent * $mt1_ratio + $mt2_percent*$mt2_ratio + (($mt1_percent * $mt1_ratio + $mt2_percent*$mt2_ratio)*0.5) + $final_percent * (($m_1 + $m_2)/($m_max1 + $m_max2)); $e_entry = sprintf " %6.2f/%3d %6.2f/%3d --/-- --/-- ", $m_1,$m_max1,$m_2,$m_max2; } elsif ( ($e_count == 5) || ($e_count == 6) ) { $mt1_ratio = $m_1/$m_max1 if $m_max1 != 0; $mt2_ratio = $m_2/$m_max2 if $m_max2 != 0; $mt3_ratio = $m_3/$m_max3 if $m_max3 != 0; $e_scores = $mt1_percent * $mt1_ratio + $mt2_percent*$mt2_ratio + $mt3_percent * $mt3_ratio + $final_percent * (($m_1+$m_2+$m_3)/($m_max1+$m_max2+$m_max3)); $e_entry = sprintf " %6.2f/%3d %6.2f/%3d %6.2f/%3d --/-- ", $m_1,$m_max1,$m_2,$m_max2,$m_3,$m_max3; } else { # suppose to be 7 $mt1_ratio = $m_1/$m_max1 if $m_max1 != 0; $mt2_ratio = $m_2/$m_max2 if $m_max2 != 0; $mt3_ratio = $m_3/$m_max3 if $m_max3 != 0; $f_ratio = $f_score/$f_max if $f_max != 0; $e_scores = $mt1_percent * $mt1_ratio + $mt2_percent*$mt2_ratio + $mt3_percent * $mt3_ratio + $final_percent * $f_ratio; $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; } # $supp_scores_limit_set comes from capa.config ($s_scores,$s_max_scores,$s_abscent,$s_count,$s_summary) = &S_CollectSetScores($SuppPath,"$s_id",0,$supp_scores_limit_set) if -d "$SuppPath"; $s_ratio = $s_scores/$s_max_scores if $s_max_scores != 0; ## print "$s_scores/$s_max_scores,$s_abscent/$s_count\n"; # $others_scores_limit_set comes from capa.config ($o_scores,$o_max_scores,$o_abscent,$o_count,$o_summary) = &S_CollectSetScores($OthersPath,"$s_id",0,$others_scores_limit_set) if -d "$OthersPath"; $o_ratio = $o_scores/$o_max_scores if $o_max_scores != 0; ## print "$o_scores/$o_max_scores,$o_abscent/$o_count\n"; $total_score = $hw_percent*$c_ratio + $qz_percent*$q_ratio + $e_scores; $score_str = sprintf "% 6.2f", $total_score; $s_key = sprintf "%06.2f %s", $total_score,$s_id; # # HW QZ QZ-N SUPP Mid 1 Mid 2 Mid 3 Final Total # # A12345678 123/123 123/123 123/123 123/123 123.45/123 123.45/123 123.45/123 123/123 123.45 # # A23546721 335/341 45/ 45 0/ 15 76/ 81 30.00/ 30 28.60/ 30 30.00/ 30 56/ 57 100.39 # A23778965 333/341 34/ 45 1/ 15 79/ 81 27.90/ 30 28.60/ 30 28.60/ 30 55/ 57 96.72 # A11111111 0/341 0/ 45 14/ 15 0/ 81 0.00/ 30 0.00/ 30 0.00/ 30 0/ 57 0.00 # 173533390 0/341 0/ 45 15/ 15 0/ 81 0.00/ 30 0.00/ 30 0.00/ 30 0/ 57 0.00 $pre_entry = sprintf "%3d/%3d %3d/%3d %3d/%3d %3d/%3d", $c_scores,$c_max_scores,$q_scores,$q_max_scores,$q_abscent,$q_count,$s_scores,$s_max_scores; $tail_entry = sprintf "%6.2f", $total_score; $whole_entry = "$s_id " . "$pre_entry" . "$e_entry" . "$tail_entry\n"; $f_entry{$s_key} = $whole_entry; print " $s_id $score_str \[$pre_entry\]\n"; print "\t\t\[$e_entry $tail_entry\]\n"; } close(CIN) || die "Cannot close file $filename!"; # close the classl file and # open the set score report file if(-f $sfilename) { # if there is already a scores file, move it to something else system("mv $sfilename $sfilename.prior.$day_str"); } open(OUT, ">$sfilename") || die "Cannot open file $sfilename!"; print OUT "\# HW QZ QZ-N SUPP Mid 1 Mid 2 Mid 3 Final Total\n"; foreach $s_key (reverse sort keys %f_entry) { print OUT "$f_entry{$s_key}"; } close(OUT) || die "Cannot close file $sfilename!"; } # # create all emails in the Mail/ directory # input is the master scores file name sub S_CreateEmails { local($sfilename)=@_; local($efilename); local($s_id); local($var_name,$s_sec,$email); local($q_scores,$q_max_scores,$q_absent,$q_count,$q_summary); local($last_n,$first_n,$first_part,$middle_n); local($day_str,$cat,$tmp); # variable $Email_templateFile comes from capa.config entry email_template_file &ScanMailTemplate("$Email_templateFile"); # after scanning the email template file, $P_code{'email_perl'} and $P_code{'template'} are defined # in the code $P_code{'email_perl'}, we need to define # $final_grade and # $category_one_high, $category_one_low, .. comes from capa.config # output $Summary_sentence # in the code $P_code{'template'} $day_str = &TodayString; if(-f $sfilename) { open(SIN, "<$sfilename") || die "Cannot open file $sfilename!"; while() { # 1 2 3 4 5 6 7 8 9 # 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 # # HW QZ QZ-N SUPP Mid 1 Mid 2 Mid 3 Final Total # A23546721 335/341 45/ 45 0/ 15 76/ 81 30.00/ 30 28.60/ 30 30.00/ 30 56/ 57 100.39 if(! /^\#/) { # non comments line $s_id = substr($_,0,9); $s_id = uc($s_id); $var_name = $Var_name{'Homework_total_score'}; ${$var_name} = int( substr($_,10,3) ); # homework total score $var_name = $Var_name{'Homework_total_max'}; ${$var_name} = int( substr($_,14,3) ); # max score $var_name = $Var_name{'Quiz_total_score'}; ${$var_name} = int( substr($_,18,3) ); # quiz score $var_name = $Var_name{'Quiz_total_max'}; ${$var_name} = int( substr($_,22,3) ); # quiz max $var_name = $Var_name{'Quiz_absent'}; ${$var_name} = int( substr($_,26,3) ); # quiz absent $var_name = $Var_name{'Quiz_count'}; ${$var_name} = int( substr($_,30,3) ); # quiz count # need to take care of '--' $midterm1 = substr($_,42,6); $midterm_max1 = int( substr($_,49,3)); $midterm2 = substr($_,53,6); $midterm_max2 = int( substr($_,60,3)); $midterm3 = substr($_,64,6); $midterm_max3 = int( substr($_,71,3)); $final_exam = substr($_,75,3); $final_exam_max = int( substr($_,79,3)); $final_grade = substr($_,85,6); # server as input to $P_code{'email_perl'} $final_grade = $final_grade * 1.0; ## print "PERL: $P_code{'email_perl'}\n"; ## print "Press RETURN to continue"; $tmp = <>; ## print "SUMMARY: $Summary_sentence\n"; ## print "Press RETURN to continue"; $tmp = <>; # now we know $Exam_sentence and $Summary_sentence # for $P_code{'template'}, we need to find # Global input # $student_name, $ClassName, there is already a $ClassName global variable # ${$Var_name{'Homework_total_score'}} ==> $HWtotal_scp # ${$Var_name{'Homework_total_max'}} ==> $HWtotal_max_scp # ${$Var_name{'Quiz_count'}} ==> $QZcount_scp # ${$Var_name{'Quiz_total_score'}} ==> $QZtotal_scp # ${$Var_name{'Quiz_total_max'}} ==> $QZtotal_max_scp # ${$Var_name{'Quiz_summary_string'}} ==> $QZsummary <-- must read from the qz/records/*.db files # $midterm1, $midterm2, $midterm3 and $final_exam # ## How to allow the format of $student_name customizable from capa.config file? ## ($student_name,$s_sec,$email) = S_Lookup_student_name("$s_id"); ($last_n,$first_part) = split(/,/,$student_name); $first_part =~ s/^\s//g; ($first_n,$middle_n) = split(/ /,$first_part); $student_name = "$first_n" . " $last_n"; # $quiz_scores_limit_set comes from capa.config ($q_scores,$q_max_scores,$q_absent,$q_count,$q_summary) = &S_CollectSetScores($QuizPath,"$s_id",0,$quiz_scores_limit_set) if -d "$QuizPath"; ${$Var_name{'Quiz_summary_string'}} = "$q_summary"; ${$Var_name{'Quiz_absent'}} = $q_absent; ${$Var_name{'Quiz_count'}} = $q_count; eval "$P_code{'email_perl'}"; if( ($final_grade <= $category_one_high) && ($final_grade >= $category_one_low)) { $cat = 1; } elsif ( ($final_grade <= $category_two_high)&&($final_grade >= $category_two_low) ) { $cat = 2; } elsif ( ($final_grade <= $category_three_high)&&($final_grade >= $category_three_low) ) { $cat = 3; } elsif( ($final_grade <= $category_four_high)&&($final_grade >= $category_four_low) ) { $cat = 4; } else { # not in above category $cat = 5; } $efilename = "$ClassPath" . "/Mail/$s_id.$day_str.$cat"; if(-f $efilename) { # remove the file with the same name, no warning!! system("rm $efilename"); } open(EOUT,">$efilename") || die "Cannot create file $efilename!"; eval "print EOUT \"$P_code{'template'}\" "; close(EOUT) || die "Cannot close file $efilename!"; print " $s_id\t$final_grade,\tcategory $cat\n"; } } close(SIN) || die "Cannot close file $sfilename!"; print "=" x 45 . "\n"; print "DONE creating all email files in $ClassPath/Mail/ directory.\n"; print "Press RETURN to continue"; $tmp = <>; } else { # the master scores file does not exist print "File $sfilename does not exist!\n"; print "Press RETURN to continue"; $tmp = <>; } } # ---------------------------------------------------------------- sub ScanMailTemplate { local($filename) = @_; local($input_line); local($tmp); if(-f $filename) { open(IN, "<$filename") || die "Cannot open $filename\n"; LINE: while( $input_line = ) { chomp($input_line); if($input_line =~ m|^//| ) { next LINE; } ## ignore comments if($input_line =~ m|^\#| ) { next LINE; } ## ignore comments if($input_line =~ m|^\s*BEGIN_perl| ) { ## Perl code $P_code{'email_perl'} = &E_CollectPerlCode; ## print "email_perl=$P_code{'email_perl'}"; next LINE; } if($input_line =~ m|^\s*BEGIN_template| ) { ## template code $P_code{'template'} = &E_CollectTemplateCode; ## print "template = $P_code{'template'}"; next LINE; } next LINE; } close(IN) || die "Cannot close $filename\n"; } else { printf "File $filename does not exist!"; $tmp = <>; } } # # Randomly pick a file in Mail/ directory and display # it on screen # sub S_RandomlyPreview { local($filename); local(@all_files); local($upper_bound); local($pick_idx,$pick_filename); local($tmp); opendir(EDIR, "$ClassPath/Mail") || die "cannot opendir $ClassPath/Mail!"; @all_files = grep !/^\.\.?$/, readdir EDIR; closedir EDIR; ## srand(time() ^ ($$+( $$ << 15)) ); $upper_bound = $#all_files + 1; if( $upper_bound > 0 ) { # something to preview $pick_idx = ((rand $$) * 1000.0) % $upper_bound; # print "PICK: $pick_idx among $upper_bound\n"; $pick_filename = $all_files[$pick_idx]; print "Preview File: $pick_filename\n"; print "Press RETURN to continue"; $tmp = <>; &C_ClearScreen; $filename = "$ClassPath/Mail/$pick_filename"; open(IN,"<$filename") || die "Cannot open file $filename!"; while() { print; } close(IN) || die "cannot close file $filename!"; print "Press RETURN to continue"; $tmp = <>; } else { print "No file in directory $ClassPath/Mail\n"; print "Press RETURN to continue"; $tmp = <>; } } # The format of a classl file # # 1 2 3 4 5 6 7 8 9 0 #0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 #PHY 183 001 A23745301 Abraham, Christopher Wil FS96 #phy 111 001 A12345678 BUMSTEAD, Blondie blondie4@pilot.msu.edu 12345678 #PHY 183 003 A24469315 Costigan, Timothy Patric costiga3@pilot.msu.edu costiga3 #PHY 183 002 A25425738 Cacossa, Andrew Vincent cacossaa@pilot.msu.edu cacossaa # lookup the student id supplied in variable $student_id # and return the student name and the e-mail address if available sub S_Lookup_student_name { local($student_id)=@_; local($filename); local($found,$input_line); local($tmp_sn,$student_name); local($len,$s_sec,$email); $student_id = uc($student_id); $filename = $ClassPath . "/classl"; open(CIN, "<$filename") || die "Cannot open file $filename!"; $found = 0; while( ($input_line = ) && (! $found) ) { chomp($input_line); $tmp_sn = substr($input_line,14,9); $tmp_sn = uc($tmp_sn); if($tmp_sn =~ /^$student_id/ ) { $found=1; # student name begins at column 24 and has 30 chars max $s_sec = substr($input_line,10,3); $student_name = substr($input_line,24,30); $len = length($input_line); $email = ""; if($len > 55 ) { $email = substr($input_line,55,32); $email =~ s/\s+//g; if( $email !~ /\@|\./ ) { $email = ""; } } } } close(CIN) || die "Cannot close file $filename!"; return ($student_name,$s_sec,$email); } # This routine accepts a string represent an absolute path to a class # it checks to see if # the directory specified by this path did actually exist # the records/ sub-directory did exist # the records/setX.db file exists # the classl file exists sub S_CheckClassPath { local($path)=@_; local($cfullpath,$rfullpath,$sfullpath); local($correct,$cfgfullpath,$cfgutilsfullpath); $correct = 0; if( $path =~ /\/$/ ) { $cfullpath = "$path" . "classl"; $rfullpath = "$path" . "records"; $sfullpath = "$path" . "records/set$set.db"; $cfgfullpath = "$path" . "capa.config"; $cfgutilsfullpath = "$path" . "capautils.config"; } else { $cfullpath = "$path" . "/classl"; $rfullpath = "$path" . "/records"; $sfullpath = "$path" . "/records/set$set.db"; $cfgfullpath = "$path" . "/capa.config"; $cfgutilsfullpath = "$path" . "/capautils.config"; } if( -d $path ) { if( -d $rfullpath ) { if(-f $cfgfullpath ) { if(-f $cfgutilsfullpath ) { $correct = 1; } else { print "File [$cfgutilsfullpath] does not exist!\n"; } } else { print "File [$cfgfullpath] does not exist!\n"; } } else { print "Directory [$rfullpath] does not exist!\n"; } } else { print "Directory [$path] does not exist!\n"; } return ($correct); } ## # display score file according to the order specified # by $key_str and the direction in $dir # $dir = 1 means ascending # 2 means descending # sub S_DisplayScoreFile { local($key_str,$dir,$sfilename)=@_; local(@a_keyidx); local($key_seq); local($s_id,$final_grade,$s_name,$s_sec,$s_email); local($sort_key,$s_display_name,$first_n,$first_part,$middle_n,$last_n); local(%sf_entry); local($line_cnt,$line_total,$len,$pad_len); local($tmp); $key_str =~ s/,//g; $key_str =~ s/1/\$final_grade/g; $key_str =~ s/2/\$s_id/g; if( $key_str =~ /3/ ) { $key_str =~ s/3/\$s_name/g; } if( $key_str =~ /4/ ) { $key_str =~ s/4/\$s_sec/g; } $key_seq = '"' . "$key_str" . '"'; if(-f $sfilename) { open(SIN, "<$sfilename") || die "Cannot open file $sfilename!"; $line_cnt = 0; while() { if(! /^\#/) { # non comments line chomp(); $line_cnt++; $s_id = substr($_,0,9); $s_id = uc($s_id); $final_grade = substr($_,85,6); # $whole_entry = $_; ($s_name,$s_sec,$s_email) = S_Lookup_student_name("$s_id"); # evaluate the code to a real key $sort_key = eval $key_seq; ($last_n,$first_part) = split(/,/,$s_name); $last_n = uc($last_n); $first_part =~ s/^\s//g; ($first_n,$middle_n) = split(/ /,$first_part); $s_display_name = "$last_n" . ", $first_n"; $len = length($s_display_name); if( $len < 25 ) { $pad_len = 25 - $len; $s_display_name = "$s_display_name" . " " x $pad_len; } else { $s_display_name = substr($s_display_name,0,25); } $sf_entry{"$sort_key"} = "$s_display_name $s_sec $whole_entry"; ### print "KEY:[$sort_key]:$whole_entry\n"; } # end of if not comments } # end while close(SIN) || die "Cannot close file $sfilename!"; $line_total = $line_cnt; $line_cnt = 0; if($dir == 1 ) { foreach $sort_key (sort keys %sf_entry) { $line_cnt++; print $sf_entry{"$sort_key"} . "\n"; if( ($line_cnt % $display_score_row_limit) == 0 ) { # $display_score_row_limit from capa.config print " --$line_cnt/$line_total-- Press RETURN to continue"; $tmp = <>; } } } else { foreach $sort_key (reverse sort keys %sf_entry) { $line_cnt++; print $sf_entry{"$sort_key"} . "\n"; if( ($line_cnt % $display_score_row_limit) == 0 ) { print " --$line_cnt/$line_total-- Press RETURN to continue"; $tmp = <>; } } } } else { print "File [$sfilename] does not exist!"; } } ## ## Input: file name to be printed through lpr command ## ## sub S_LPRFile { local($file)=@_; local($go_on)=0; local($select); local($printer_selected); local($emp); local($cmd); local($PS_file); print "Enter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n"; if( $go_on ) { # run lpr -P.... on the .ps file $select = C_MultipleChoice(4,10,$DialogWidth," Printers Available ", "Printer Name",@Printers); $Printer_selected = $Printers[$select-1]; $go_on = 0; while( ! $go_on ) { print "Enter 1 for one sided or 2 for two sided printing (RETURN = 1 sided): "; $tmp=<>; if( ($tmp == 1) || ($tmp == 2) || ($tmp eq "\n") ) { $tmp = 1 if $tmp eq "\n"; $go_on = 1; } } if( $tmp == 1 ) { $LpronesidedCMD =~ s/\$Printer_selected/$Printer_selected/e; $cmd = "$LpronesidedCMD $file"; } else { # should be 2 $PS_file = $file; print "$LprtwosidedCMD\n"; $LprtwosidedCMD =~ s/\$Printer_selected/$Printer_selected/e; $LprtwosidedCMD =~ s/\$PS_file/$PS_file/e; $cmd = "$LprtwosidedCMD"; } $go_on = 0; print "=" x 70 . "\n"; print "CMD: $cmd\n"; print "Enter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n"; if($go_on) { open(IN,"$cmd|"); while($input_line = ) { print "$input_line"; } close(IN); print "=" x 70 . "\n"; print " Your job has been sent to the print spooler. Press RETURN to continue"; $tmp = <>; } } } # <========================= Main program =======================> Getopts('c:s'); if (! $opt_c) { ## class path option print "USAGE: capatools.pl -c Full_path_to_class\n"; $ClassPath = &S_Enterpath; } else { # need to check the path entered by option -c if( S_CheckClassPath("$opt_c") ) { $ClassPath = $opt_c; if( $opt_s ) { S_ReadCAPAconfig($ClassPath); S_CreateScores("$Master_scoresFile"); # print "run score report in silent background\n"; # print "The $Master_scoresFile\n"; exit (1); } } else { $ClassPath = &S_Enterpath; } } $DialogWidth = 48; $Quit = 0; $len = length($ClassPath); if( $len > ($DialogWidth-6) ) { ## class path too long, display the last portion instead $offset = $len - $DialogWidth +6; $DisplayPath = "-..." . substr($ClassPath, $offset,$DialogWidth-6); } else { $DisplayPath = $ClassPath; } S_ReadCAPAconfig($ClassPath); while(! $Quit ) { $What = C_MultipleChoice(1,8,$DialogWidth," Welcome to CAPA Utilities Ver 1.1 ", "$DisplayPath",@Main_menu); if( $What == 1 ) { # change class path $ClassPath = &S_Enterpath; $len = length($ClassPath); if( $len > ($DialogWidth-6) ) { $offset = $len - $DialogWidth +6; $DisplayPath = "-..." . substr($ClassPath, $offset,$DialogWidth-6); } else { $DisplayPath = $ClassPath; } S_ReadCAPAconfig($ClassPath); } elsif ($What == 2 ) { # run capastat.pl $Set = &S_InputSet; $Sfullpath = $ClassPath . "/records/set" . "$Set" . ".db"; ## print "Running capastat.pl on $Sfullpath\n"; ($Q_cnt,$L_cnt) = &S_ScanSetDB($Sfullpath); Percentage_Scores($Set,$L_cnt); S_Average($Q_cnt,$L_cnt); ## Large_Tries($opt_t,$opt_n,$Q_cnt,$L_cnt); } elsif ($What == 3 ) { # log analysis on S, U, $Set = &S_InputSet; $Lfullpath = $ClassPath . "/records/log" . "$Set" . ".db"; if(-f $Lfullpath) { print "Log analysis for telnet session log$Set.db\n"; print " This may take a while to calculate, please wait ...\n"; ($Y_l,$N_l,$S_l,$U_l,$u_l) = S_ScanLogDB($Lfullpath); } $Wfullpath = $ClassPath . "/records/weblog" . "$Set" . ".db"; if(-f $Wfullpath ) { print "=" x 79 . "\n"; print "Log analysis for web session weblog$Set.db\n"; print " This may take a while to calculate, please wait ...\n"; ($Y_w,$N_w,$S_w,$U_w,$u_w) = S_ScanLogDB($Wfullpath); } $Telnet_total = $Y_l+$N_l+$S_l+$U_l+$u_l; $Web_total = $Y_w+$N_w+$S_w+$U_w+$u_w; print "============== SUMMARY ====================\n"; print " #Y #N #S #U #u Total\n"; printf "telnet: %6d %6d %6d %6d %6d %6d\n", $Y_l, $N_l, $S_l, $U_l, $u_l,$Telnet_total; printf " web: %6d %6d %6d %6d %6d %6d\n", $Y_w, $N_w, $S_w, $U_w, $u_w,$Web_total; $Y_sum = $Y_l + $Y_w; $Ratio_Y = 100.0 * ($Y_w / $Y_sum) if $Y_sum > 0; $N_sum = $N_l + $N_w; $Ratio_N = 100.0 * ($N_w / $N_sum) if $N_sum > 0; $S_sum = $S_l + $S_w; $Ratio_S = 100.0 * ($S_w / $S_sum) if $S_sum > 0; $U_sum = $U_l + $U_w; $Ratio_U = 100.0 * ($U_w / $U_sum) if $U_sum > 0; $u_sum = $u_l + $u_w; $Ratio_u = 100.0 * ($u_w / $u_sum) if $u_sum > 0; $overall_entries = $Telnet_total+$Web_total; $Ratio_web = 100.0*($Web_total/$overall_entries) if $overall_entries > 0; printf " %%web: % 6.1f % 6.1f % 6.1f % 6.1f % 6.1f % 6.1f\n", $Ratio_Y, $Ratio_N, $Ratio_S, $Ratio_U, $Ratio_u, $Ratio_web; printf "Press RETURN to continue"; $tmp = <>; } elsif ($What == 4 ) { # Student course profile # select either use student name or student number # if there are more than two students, display them in a table to select # by the user # use student id to find the scores # display set scores in each class, regular class goes first. # S_InputStudent; ($s_id,$s_nm) = S_InputStudent($ClassPath); if($s_id ne "" ) { print "$s_nm\n"; &S_CollectSetScores($ClassPath,"$s_id",1,$homework_scores_limit_set); &S_CollectSetScores($QuizPath,"$s_id",1,$quiz_scores_limit_set) if -d "$QuizPath"; &S_CollectSetScores($ExamPath,"$s_id",1,$exam_scores_limit_set) if -d "$ExamPath"; &S_CollectSetScores($SuppPath,"$s_id",1,$supp_scores_limit_set) if -d "$SuppPath"; &S_CollectSetScores($OthersPath,"$s_id",1,$others_scores_limit_set) if -d "$OthersPath"; print "\nEnter Y or for Login analysis (may take a while)\n"; print "Enter N => return to main menu: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 0; $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n"; if($go_on) { # $homework_scores_limit_set comes from capa.config &S_LoginAnalysis($ClassPath,"$s_id",$homework_scores_limit_set); &S_StudentSetAnalysis($ClassPath,"$s_id",$homework_scores_limit_set); } } } elsif ($What == 5 ) { # CAPA IDs for one student ($s_id,$s_nm) = S_InputStudent($ClassPath); if($s_id ne "" ) { $go_on = 0; print "$s_nm, $Allcapaid\n"; ($S_from, $S_to) = S_EnterSets; print "\nCMD: $AllcapaidCMD -i -stu $s_id -s $S_from -e $S_to\n"; print "Enter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n"; if( $go_on ) { open(IN,"$AllcapaidCMD -i -stu $s_id -s $S_from -e $S_to -c $ClassPath|"); while($input_line = ) { print "$input_line"; } close(IN); printf "Press RETURN to continue"; $tmp = <>; } } } elsif ($What == 6 ) { # All CAPA IDs $go_on = 0; ($S_from, $S_to) = S_EnterSets; print "\nCMD: $AllcapaidCMD -s $S_from -e $S_to\n"; print "Enter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^Y/ || $tmp eq "\n"; if( $go_on ) { open(IN,"$AllcapaidCMD -s $S_from -e $S_to -c $ClassPath|"); while($input_line = ) { print "$input_line"; } close(IN); print "Press RETURN to continue"; $tmp = <>; } } elsif ($What == 7 ) { # Item analysis $u_path = C_MultipleChoice(4,10,$DialogWidth,"Select a Class path","",@MainPath); $Set = &S_InputSet; print "Item analysis for $MainPath[$u_path-1], Set $Set\n"; print "This may take a while to calculate, please wait ...\n"; S_ItemAnalysis($MainPath[$u_path-1],$Set); } elsif ($What == 8 ) { # Correlation chart ## TO DO: ## Let user specify how many categories to calculate correlation ## For each category, the user can specify problem numbers to ## be in that category ## Then, the correlations between each category is calculated ## $u_path = C_MultipleChoice(4,10,$DialogWidth,"Select a Class path","",@MainPath); $Set = &S_InputSet; print "Correlation calculation for $MainPath[$u_path-1], Set $Set\n"; print "This may take a while to calculate, please wait ...\n"; S_SetCorrelationStatistics($MainPath[$u_path-1],$Set); } elsif ($What == 9 ) { # E-Mail $done_email = 0; while(! $done_email ) { $select = C_MultipleChoice(2,10,$DialogWidth+15," CAPA E-mail facilities ", "$DisplayPath",@Email_menu); if($select == 1 ) { # Create scores file # the variable $Master_scoresFile comes from capa.config entry master_scores_file = ... print "Create a file containing scores of all students:\n"; print " $Master_scoresFile\n"; print "This may take a while to complete, please wait ...\n"; S_CreateScores("$Master_scoresFile"); } elsif ($select == 2 ) { # create all e-mail files in Mail/ dir print "Create e-mail files in $ClassPath/Mail/ directory\n"; print " based on scores file $Master_scoresFile\n"; print "This action will REPLACE the original file(s) in the Mail/ directory!\n"; $go_on = 0; print "Enter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^Y/ || $tmp eq "\n"; if( $go_on ) { print "This may take a while to complete, please wait ...\n"; S_CreateEmails("$Master_scoresFile"); } } elsif ($select == 3 ) { # Preview e-mail # randomly pick a student # &S_RandomlyPreview; } elsif ($select == 4 ) { # Send e-mail to a group of students $entered_cat = &S_EnterCategory; $go_on = 0; print "Entered category: $entered_cat\n"; print "Enter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n"; if( $go_on ) { S_MailtoCategory($entered_cat); } # 1. Whole class # 2. Category ---> specify 1, 2, 3, or 4 } else { # 4. Cancel $done_email = 1; } } } elsif ($What == 10 ) { # Print one assignment for a student ($s_id,$s_nm) = S_InputStudent($ClassPath); if($s_id ne "" ) { # non-empty student id $s_id = uc($s_id); # uppercase ## $Set = &S_InputSet; ($From_set,$To_set) = &S_EnterSets; print "Printing Set(s) $From_set to $To_set for STUDENT: $s_nm\n"; $go_on = 0; print "\nEnter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n"; if($go_on) { $go_on = 0; print "CMD: $QzparseCMD -c $ClassPath -set $From_set:$To_set -stu $s_id\n"; open(IN,"$QzparseCMD -c $ClassPath -set $From_set:$To_set -stu $s_id|"); while($input_line = ) { print "$input_line"; } close(IN); print "=" x 70 . "\n"; $tex_file = "$ClassPath" . "/TeX/" . "$s_id" . ".tex"; print "CMD: $LatexCMD $tex_file\n"; print "Enter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n"; } # run qzparse on the student and the set # qzparse -c $path -set $set -stu $s_id if( $go_on ) { # run latex on the .tex file, latex will create a .dvi file in cwd() $dir = cwd(); $go_on = 0; open(IN,"$LatexCMD $tex_file|"); while($input_line = ) { print "$input_line"; } close(IN); $cwd_dvi_file = "$dir/" . "$s_id" . ".dvi"; $dvi_file = "$ClassPath" . "/TeX/" . "$s_id" . ".dvi"; $cmd = "mv $cwd_dvi_file $dvi_file"; system($cmd); print "=" x 70 . "\n"; $ps_file = "$ClassPath" . "/TeX/" . "$s_id" . ".ps"; print "CMD: $DvipsCMD $dvi_file -o $ps_file\n"; print "\nEnter Y or to continue or N to cancel the operation: "; $tmp=<>; $tmp =~ tr/A-Z/a-z/; $go_on = 1 if $tmp =~ /^y/ || $tmp eq "\n"; } if( $go_on ) { # run dvips on the .dvi file open(IN,"$DvipsCMD $dvi_file -o $ps_file|"); while($input_line = ) { print "$input_line"; } close(IN); print "=" x 70 . "\n"; S_LPRFile($ps_file); } } } elsif ($What == 11 ) { # view score file $entered_key = S_EnterSortKey; $display_key = $entered_key; $display_key =~ s/,/, /g; $display_key =~ s/1/Grade/g; $display_key =~ s/2/Student number/g; $display_key =~ s/3/Student name/g; $display_key =~ s/4/Section/g; print "Entered sorting key: $display_key\n"; $go_on = 0; while(! $go_on) { print "Enter 1 for ascending or 2 for decending order (RETURN = 1 ascending): "; $tmp=<>; if( ($tmp == 1) || ($tmp == 2) || ($tmp eq "\n") ) { $tmp = 1 if $tmp eq "\n"; $go_on = 1; } } S_DisplayScoreFile($entered_key,$tmp,$Master_scoresFile); print "Finish displaying score file. Press RETURN to continue"; $tmp = <>; } elsif ($What == 12 ) { # list student responses ($s_id,$s_nm) = S_InputStudent($ClassPath); if($s_id ne "" ) { $go_on = 0; print "$s_nm, grepping submissions\n"; ($S_from, $S_to) = S_EnterSets; for( $i=$S_from;$i<=$S_to;$i++) { print "Telnet Submissions for $s_nm for set $i\n"; open(IN,"grep -i $s_id $ClassPath/records/submissions$i.db |"); while($input_line = ) { print "$input_line"; } close(IN); printf "Press RETURN to continue"; $tmp = <>; print "WWW Submissions for $s_nm for set $i\n"; open(IN,"grep -i $s_id $ClassPath/records/websubmissions$i.db|"); while($input_line = ) { print "$input_line"; } close(IN); printf "Press RETURN to continue"; $tmp = <>; } } } elsif ($What == 13 ) { # quit $Quit = 1; } }