Setting the Grade Depending on Number of Correct Output Lines

From dftwiki
Jump to: navigation, search

--D. Thiebaut (talk) 16:10, 12 December 2017 (EST)


Setup


Setup the VPL module as you would for any other program. Here are the assumptions:

  • The students have to submit a C program (final.c & final.h), that contain functions only (no main program).
  • The students files are compiled with test0.c or test.c, two programs that energize the functions in the students programs.
  • The solution program (finalSol.c & finalSol.h) is recorded in the VPL module.
  • The grade is computed from a percentage of correct lines of output in the students's output, compared to the output of the solution program.
  • The grade is scaled (see function scale() to match Moodle's internal scale (letter grade to percentage) in vpl_evaluate.sh).
  • test0.c is used by vpl_run.sh, while test.c is used by vpl_evaluate.sh. This allows extra testing when evaluating a student program.


vpl_run.sh


  • This allows the student
#! /bin/bash
# vpl_run.sh
# D. Thiebaut
# Smith College


cat > vpl_execution <<EEOOFF
#! /bin/bash
 
# --- program tested (no extension) ---
prog1=final

gcc -o test0  \${prog1}.c test0.c
./test0

exit 0 

EEOOFF


vpl_evaluate.sh


#! /bin/bash
# D. Thiebaut
# Smith College
# vpl_evaluate.sh script looping through several tests, each test with
# its own input file and its own expected output file.  The output of the
# student program is tested against the expected output file.
#
#set -x

cat > vpl_execution <<EEOOFF
#! /bin/bash
# Moodle scale
#100.00 %93.00 %A
#92.99 %90.00 %A-
#89.99 %87.00 %B+
#86.99 %83.00 %B
#82.99 %80.00 %B-
#79.99 %77.00 %C+
#76.99 %73.00 %C
#72.99 %70.00 %C-
#69.99 %67.00 %D+
#66.99 %60.00 %D
#59.99 %0.00 %F

# ---------------------------------------------------------------
# catcher: catches program crashes
# ---------------------------------------------------------------
function catcher {
    echo "Your program crashes!"
    echo "Grade :=>> 60"     
    exit 0
}
trap catcher ERR

# ---------------------------------------------------------------
# scale( percentGood )
# ---------------------------------------------------------------
function scale {
    percent=\$1
    inGrade=(   95 90 85 80 75 70 65 60 55 50 0 )
    #            A A- B+ B  B- C+ C  C- D+ D  F
    outGrade=( 100 93 87 83 80 77 73 80 67 60 50 )
 
    # get length of an array
    arraylength=\${#inGrade[@]}

    # loop through all the grades
    for (( i=0; i<\${arraylength}; i++ ));
    do
        if (( \$percent > \${inGrade[\$i]} )) ; then 
	    return \${outGrade[\$i]}
        fi
    done
    return 50
}

#--- test scale ---
#for j in \`seq 100 -1 0 \` ; do
#    scale \$j
#    echo "\$j --> \$?"
#done
#exit 0 

# ---------------------------------------------------------------
# computeMatching( solution, student )
# returns the percentage of matching lines
# ---------------------------------------------------------------
function computeMatching {
    # \$1 = solution output                                        
    # \$2 = student output                 
    #echo "computeMatching: solution = \$1"
    #echo "computeMatching: student  = \$2"
    noLinesToMatch=\`cat \$1 | wc -l\`
    noGoodLines=\`grep -Fxf \$2 \$1 | wc -l\`
    #echo "noLinesToMatch = \$noLinesToMatch"                    
    #echo "noGoodLines = \$noGoodLines"                                                           
    percentGood=\$(( \$noGoodLines * 100 / \$noLinesToMatch ))
    #echo "percentGood = \$percentGood"
    return \$percentGood
}

 
# --- program tested (no extension) ---
prog1=funcs
if ! gcc -o \${prog1}  \${prog1}.c main.c &> error.out ; then
    echo "Comment :=>> Your program generates a compilation error"
    echo "<|--"
    cat error.out                                                                                                   
    echo "--|>"
    echo "Grade :=>> 70"  
    exit 1
fi

                                                                                                              
#--- compile solution program and capture output ---
gcc -o \${prog1}sol main.c  \${prog1}sol.c

#--- figure out how to filter the outputs ---
keepOnlyNumbers=false
removeMultipleSpaces=false
removeBlankLines=false

#--- accumulate percentage of good output ---
totalPercent=0
noTests=1

for i in \$noTests  ; do
   echo "Comment :=>>-TEST " # \$i"

   # ==============================================
   # TEST 1 
   # ==============================================

   #-----------------------------------------------------------------
   #--- run program, capture output, keep copy of original output ---
   #-----------------------------------------------------------------
   ./\${prog1} &> user.out
   cp user.out user.out.org

   #--- remove non numbers and non minus---
   if [ \$keepOnlyNumbers = true ] ; then
       cat user.out | sed 's/[^0-9\ -]*//g' > dummy.out
       mv dummy.out user.out
   fi

   #--- remove multiple spaces --- 
   if [ \$removeMultipleSpaces = true ] ; then
       cat user.out | sed 's/  */ /g' > dummy.out
       mv dummy.out user.out
   fi

   #--- remove blank lines ---
   if [ \$removeBlankLines = true ] ; then
       cat user.out | sed '/^\s*\$/d' > dummy.out
       mv dummy.out user.out
   fi

   #-----------------------------------------------------------------
   #--- generate output for solution program and treat it same as ---
   #--- output of user.                                           ---
   #-----------------------------------------------------------------
   ./\${prog1}sol    > expectedOutput\${i}

   #--- remove non numbers and non minus---
   if [ \$keepOnlyNumbers = true ] ; then
       cat expectedOutput\${i} | sed 's/[^0-9\ -]*//g' > dummy.out
       mv dummy.out expectedOutput\${i}
   fi

   #--- remove multiple spaces --- 
   if [ \$removeMultipleSpaces = true ] ; then
       cat expectedOutput\${i} | sed 's/  */ /g' > dummy.out
       mv dummy.out expectedOutput\${i}
   fi

   #--- remove blank lines ---
   if [ \$removeBlankLines = true ] ; then
       cat expectedOutput\${i} | sed '/^\s*\$/d' > dummy.out
       mv dummy.out expectedOutput\${i}
   fi

   #--- compute difference.  Count number of lines that are  --- 
   #--- different                                            ---
   diff -y -w --ignore-all-space user.out expectedOutput\${i} > diff.out
   
   #--- reject if different ---
   if ((\$? > 0)); then
      echo "Comment :=>>- Your output is incorrect."

      #--- display test file ---
      echo "Comment :=>>- Your program tested with:"
      echo "<|--" 
      echo "./\${prog1} \${data}\${i} "
      echo "--|>"

      echo "Comment :=>> ---------------"
      echo "Comment :=>>- Your output:"
      echo "Comment :=>> ---------------"
      echo "<|--"
      cat user.out.org
      echo "--|>"
      echo ""
      echo "Comment :=>> ---------------"
      echo "Comment :=>>- Expected output: "
      echo "Comment :=>> ---------------"
      echo "<|--"
      cat expectedOutput\${i}
      echo "--|>"
   
      #--- accumulate percentages obtained at different tests ---   
      computeMatching expectedOutput\${i} user.out
      percent=\$?
      #echo "percent = \$percent"
      #echo "totalPercent = \$totalPercent"
      totalPercent=\$(( \$totalPercent + \$percent ))

      # --------------------- REWARD IF CORRECT OUTPUT -----------------
   else
      #--- good output ---
      echo "Comment :=>>- Congrats, your output is correct."
      echo "Comment :=>> --------------------------------."
      echo "<|--"
      cat user.out.org
      echo "--|>"
      totalPercent=\$(( \$totalPercent + 100 ))
   fi
done

#--- compute reported grade in moodle ---
averagePercentGood=\$(( \$totalPercent / \$noTests ))
#echo "percent correct: \$averagePercentGood

scale \$averagePercentGood
grade=\$?
echo "Grade :=>> \$grade"


EEOOFF

chmod +x vpl_execution