1. 目的
- 学生基于码云提交程序设计作业;
- 教师编写检查器,自动对所有人的作业进行判分;
2. 方法
下文中的代码用于说明方法思想,但存在少量 Bug,懒得同步更新了。查找最新代码,请移步码云【链接】。
2.1 作业提交要求
- 教师建立一个空项目,学生在该项目建立自己的分支(分支名 SEXXX,XXX 为学号后三位),
fork
该项目到本地;- 引用自集美大学计算机郑如滨的教学博客:『老师布置程序项目类作业的时候,即使再三强调要按照规范来建立项目目录结构。然而最终提交结果依然不尽如人意。每个人似乎都有自己的一套项目结构,并且这个结构通常是惨不忍睹的,比如一包流,所有代码均放到一个包中。教师完全可以规划好一个项目的标准目录结构,然后让学生Fork或clone下来,这样就无痛的规范了所有学生的项目目录结构。实际上这也是业界流行的一种方式。学生可以参考集美大学-郑如滨 老师的这个专门用于Java教学的一个项目OnlineShop,该项目包含一个可供参考的标准项目目录结构。』
- 学生将完成的程序设计作业
push
到远端自己对应的分支; - 学生完成的程序作业要求满足一定的接口规范:如指定的文件名、函数名、输出输入接口等。
2.2 班级作业检查框架: CheckFramework.sh
- 只需要学生的分支命名规则不变,每次作业的 CheckFramework不需要修改
-
思路
-
代码
#!/bin/sh
# Filename: CheckFramework.sh
#
# Usage checkRepo.sh git_repo_url
#
# Algorithm:
# 1. clone git_repo to local directory
# 2. checkout to each branch (of every student)
# 3. testing and evaluating each program written by students.
# 4. return result (csv 格式)
#
# Input: git_repo_url
# output: result.csv
#
######################################################################
# 0. Global variants
ResultFile="result.csv" ; # 返回每个学生的成绩
WorkSpace=`pwd`;
export WorkSpace; # 输出当前工作路径:WorkSpace
# 1. check repo_url
if [ $# -lt 1 ]; then
echo "Usage:";
echo " 33[1;31m $0 33[m 33[1;32m gitRepo_url 33[m.";
echo "Such as:";
echo " 33[1;31m $0 33[m 33[1;32m https://gitee.com/se16/HelloWorld.git 33[m";
exit;
fi
# 2. git clone repository $1
# 2.0 if the repository have been cloned before, remove (delete) the repository and re-clone it.
RepoName=`echo ${1##*/} | cut -d "." -f 1`;
echo "the Repository name is 33[1;31m$RepoName 33[m.";
if [ -d $RepoName ]; then
echo "deleting the directory named $RepoName";
rm -rf $RepoName; # debug #
fi
# clone the Repository.
echo -e "Cloning the Repository: 33[1;31m$1 33[m.";
git clone $1; # 第一次
# 2.1 check if the repository($2) was correctly cloned.
if [ ! -d $RepoName ]; then
# incorrect repository!
echo "Cloning repository ${1##*/} 33[1;31mfailed 33[m. Check it's url, please!";
exit;
fi
# 2.2 successfully cloned the repository. Now change working directory to sub-directory named $RepoName
# step1 : checkout to $bra,
# step2 : and check the homeworks one by one, give evaluation score of each homework.
# (use an function outsides)
# step3 : output result.
# 2.2.0 delete previous result.csv
if [[ -f $ResultFile ]]; then
#statements
rm result.csv;
fi
echo -e "Processing homeworks in each branch named SEXXX";
cd $WorkSpace/$RepoName;
# 列举本地分支,筛选学生建立的『SEXXX』分支,删除当前分支前的星号 "*"" ,及空格
git branch --all | cut -d "/" -f 3| grep -ai 'se' | sed 's/[* ]//g'> $WorkSpace/branches.txt;
count=0;
while read bra; do
let count=count+1;
# 2.2.1 checkout ; OK
git checkout -q $bra; # -q --quiet # suppress progress reporting
# 2.2.2 run test script, and return a score of current branch(student). Dev
echo "
-e NO. 33[1;31m$count 33[m. Reviewing homeworks of student NO. 33[1;32m$bra ... 33[m ";
# call CheckersCaller : driver , and export TotalScore
# echo $WorkSpace;
source $WorkSpace/CheckersCaller.sh;
# return score of current branch (student's work)
score=$TotalScore;
# 2.2.3 write score to a structure which records all students scores. OK
echo "$bra,$score" >> $WorkSpace/$ResultFile
done < $WorkSpace/branches.txt
echo "
Finished";
2.3 作业的检查器: CheckersCaller.sh
- 每次作业的 Checkers 不一样,需要独立设计
- 对应地
CheckersCaller.sh
需要对 score 的汇总做微调 (包括:权重、汇总项目)
- 思路
- 依次采用 命令
source Checker_i.sh
,调用不同的检查器; - 这些检查器
Checker_i.sh
输出 对应的检查项目得分score_i
; - 对每一项检查赋予权重
wi
, 对所有检查项目得分加权(wi),得到汇总得分TotalScore
; - 输出
TotalScore
。(export TotalScore
)
- 代码
#!/bin/sh
# Filename: CheckersCaller.sh
#
# Usage: CheckersCaller.sh
#
# Algorithm:
# 1. call checkers which export a set of scores
# 2. summation:sum the scores to TotalScore
# 3. export TotalScore
#
# Input:Null
# Output:TotalScore -- summation of each score_i.
#
#####################################################
TotalScore=0
# 1. 调用一组Checker_i.sh 脚本, 得到一组输出 score_i (单项得分)
source $WorkSpace/Checker_1.sh "*.c";
let s1=score_1;
source $WorkSpace/Checker_1.sh "*.cpp";
let s2=score_1;
source $WorkSpace/Checker_1.sh "*.py";
let s3=score_1;
# ...
# source Checker_n.sh
# 2. 汇总得分 (wi 为每一项的权重)
# let TotalScore=score_1*w1+score_2*w2+...+score_n*wn
let TotalScore=s1+s2+s3;
echo "TotalScore : $TotalScore";
# 3. 输出汇总分 TotalScore
export TotalScore;
2.4 作业的单项检查器: Checker_i.sh
-
思路
-
代码示例
- 这里给出一个静态代码审查的示例。(Checker_1.sh)
#!/bin/sh
# Checker_1.sh
#
# Usage Checker_1.sh filename
#
# Algorithm :
# 1. check the file (SUT) existence
# 2. Review or test SUT, give an evaluation score (score)
# 3. export score_i (score_i equals to evaluation result.)
#
# Input: filename -- software under test(review)SUT
# Output:score_i -- export score_i
#
#################################################
# 0. Global variable for export
score_1=0 # here i=1
# 1. checker_i Usage
if [ $# -lt 1 ]; then
echo "Usage:";
echo " 33[1;31m $0 33[m 33[1;32m filename 33[m.";
echo "eg. 33[1;31m $0 33[m 33[1;32m HelloWord.c 33[m";
exit;
fi
# 2. Does file $1 exist? s_temp is the evaluation
full_filename="";
full_filename=`find . -name $1`;
#if [[ ! -f $1 ]]; then # file does not exist
if [[ ${#full_filename} == 0 ]]; then # file does not exist
echo "File 33[1;32m$1 33[m does 33[1;31m not exist 33[m!";
s_temp=0;
else
# in this block, you can call other tools or write scripts for evaluating
# call foreign tools or scripts # return a score
# ......
s_temp=`$WorkSpace/tools/sloccount/c_count $full_filename | sed -n '$p'`; # sed -n '$p'取最后一行数据
echo "Lines of $full_filename is $s_temp";
fi
# 3. export global variable
let score_1=s_temp;
export score_1
-
还可以有其他审查项目,需要老师(助教)自己编写检查器,比如可以:
-
- 编译待测代码. 可编译,给一个评测分 s2
-
- 运行N 个测试用例,若通过测试数量 n,则 给出评测得分 s3= totalOfTesting * n/N
-
2.5 系统文件组织结构及输出
- 文件结构组织
..workspace --
|-- CheckFramework.sh
|-- CheckersCaller.sh
|-- Checker_1.sh
|-- Checker_2.sh
|-- .... ...
|-- Checker_n.sh
|-- oos--
|-- sloccount ..... # 一组代码行计数工具, 这些工具供给 检查器 ``Checker_i.sh``使用
|-- ********* ..... # 其他软件度量工具
|-- ..... #
|-- RepositoriesName # clone 到本地的远程仓库
|-- result.csv # 检查得分,与分支名对应 (不同的学生对应不同的分支)
|-- branches.txt # 临时文件,存放学生分支 (不同的学生对应不同的分支,命名规则 SEXXX ,XXX 学号后三位
- 输出 。 输出文件 result.csv
3. 其他的思考
3.1 本文的方法
- 本文方法的需要老师自己编写检查器(Checker_i.sh),工作量有些大,本文对检查器的接口做了定义,具体检查内容需要老师自己定义。
- 另外,对学生提交程序的接口有较严格的要求。建议老师先建立项目,定义好各类接口,学生 fork 该项目。
3.2 其他方案
- 是不是还可以借助 git push 机制,触发自动检查、评分的。 目前,不清楚
- 本想利用 Jenkins 持续集成的方案,设计触发器的,感觉有点复杂,设置了几次,跑不起来,若有谁搞通了,最好也写篇博客;