(*--- Describe the objects and operations related to grading ---*) (* Filename - grading.rsl Author - Sujit Polpaya (With a lot of help from Chris Noe.) Date - 05/24/2004 CPE 205, Dr.Fisher, Cal Poly, San Luis Obispo. *) (*--- Begin module Grader ---*) module TestToolGrader; (*--- Imports ---*) from Test import Test, TakenTest, GradedTest, TotalPoints, File; from Question import Question, AnsweredQuestion, GradedQuestion, AutoGrade, CorrectAnswer, Response, Points, PointsEarned, Feedback, QuestionNumber; from File import FileType, TakenTestType, GradedTestType; (* There are no exports or objects in this module because this module uses the objects defined in the other sections of the Test Tool. *) (* Automatically grade all the auto-gradable questions in all the available tests. *) operation AutoGradeTests inputs : ttests:TakenTest*; outputs : agtests:GradedTest*; precondition : (* All the tests in the list should be of the type: TakenTest *) (forall (k:integer | (k >= 1) and (k < #(ttests))) (ttests[k].file.file_type?taken_test_type)); postcondition: (* All the auto-gradable questions in all the available tests are graded. If the response matches with the correct answer, full points are awarded, else a zero is awarded. *) (forall (i:integer | (i >= 1) and (i < #(ttests))) (forall (j:integer | (j >= 1) and (j < #(ttests[i].answeredquestionlist))) (if (ttests[i].answeredquestionlist[j].autograde = true) then (if(ttests[i].answeredquestionlist[j].response = ttests[i].answeredquestionlist[j].correctanswer) then agtests[i].gradedquestionlist[j].pointsearned = ttests[i].answeredquestionlist[j].points else agtests[i].gradedquestionlist[j].pointsearned = 0)))); description : (* Automatic grading tests attempts to grade all the tests in the grading folder. But, it grades only the auto-gradable questions in each test, namely the multiple-choice, true or false, fill-in-the-blanks and matching. Functionality to grade a matching question is not implemented yet. Responses are compared with the correct answer only if the question is auto-gradable. Points are assigned if the response matches with the correct answer, which is stored either as a character or a string. If the response does not match with the correct answer, a zero is awarded. *); end AutoGradeTests; (* Display autograding summary. *) operation DisplayAutoGradingSummary inputs : ttests:TakenTest*; outputs : agtests:GradedTest*, total_tests_available:integer, total_tests_graded:integer, total_questions_in_each_test:integer; precondition : (* None. Preconditions are already satisfied in operation AutoGradeTests *); postcondition: (* Total tests available = # of tests in a Taken Test. Total tests graded = # of tests in a Graded Test. Total questions in each test = # of questions in any test. There is no way to implement a counter to count the number of tests graded automatically. *) ((total_tests_available = #(ttests)) and (total_tests_graded = #(agtests)) and (total_questions_in_each_test = #(ttests[1].answeredquestionlist))); description : (* Output the numeric values used to display in the summary dialog box. *); end DisplayAutoGradingSummary; (* Manually grade an individual test by assigning points and feedback to each student response. Feedback can be empty. *) operation ManualGradeTest inputs : ttest:TakenTest, pts:Points*, fdbk:Feedback*; outputs : mgtest:GradedTest; precondition : (* The test should be of the type: TakenTest, and the points awarded cannot be more than the possible points for each question. *) ((ttest.file.file_type?taken_test_type) and (forall (k:integer | (k >= 1) and (k < #(ttest.answeredquestionlist))) (pts[k] <= ttest.answeredquestionlist[k].points))); postcondition: (* Assign the respective points earned and the feedback to the respective question in the test. *) (forall (i:integer | (i >= 1) and (i < #(ttest.answeredquestionlist))) (mgtest.gradedquestionlist[i].pointsearned = pts[i]) and (mgtest.gradedquestionlist[i].feedback = fdbk[i])); description : (* Points assigned for all the short-answer and program source code type questions, and feedback by the grader are added to produce the graded test. The grader has to grade each question in the test manually. *); end ManualGradeTest; (* Manually grade an individual question in an individual test. *) operation ManualGradeQuestion inputs : que:AnsweredQuestion, pts:Points, fdbk:Feedback; outputs : que':GradedQuestion; precondition : (* Points awarded cannot be more than the possible points. *) (pts <= que.points); postcondition: (* The grader assigns a point to the response manually. So, just assign the points and feedback. Feedback can be empty. *) (que'.pointsearned = pts and que'.feedback = fdbk); end ManualGradeQuestion; (* Manually grade an individual questions in all the tests. *) operation ManualGradeQuestionsInTests inputs : ttests:TakenTest*, question_number:integer, pts:Points*, fdbk:Feedback*; outputs : agtests:GradedTest*; precondition : (* All the tests in the list should be of the type: TakenTest, and Points awarded cannot be more than the possible points for the specific question. *) (forall (k:integer | (k >= 1) and (k < #(ttests))) (ttests[k].file.file_type?taken_test_type)) and (forall (k:integer | (k >= 1) and (k < #(ttests))) (pts[k] <= ttests[k].answeredquestionlist[question_number].points)); postcondition: (* The grader assigns a point to the response manually. So, just assign the points and feedback. Feedback can be empty. *) (forall (i:integer | (i >= 1) and (i < #(ttests))) (agtests[i].gradedquestionlist[question_number].pointsearned = pts[i]) and (agtests[i].gradedquestionlist[question_number].feedback = fdbk[i])); description : (* Grades a specific question number in all the available tests. The grader has to input the question number, points and feedback for each question. *); end ManualGradeQuestionsInTests; (* EditPointsEarned can be used to add, or edit points to a question. *) operation EditPointsEarned inputs : que:AnsweredQuestion, pts:Points; outputs : que':GradedQuestion; precondition : (* Points earned cannot be more than the possible points. *) (que'.pointsearned <= que.points); postcondition: (* The grader assigns a point to the response manually. Just assign the points to the points earned. *) (que'.pointsearned = pts); end EditPointsEarned; (* EditFeedback can be used to add, or edit feedback to a question. *) operation EditFeedback inputs : que:AnsweredQuestion, fdbk:Feedback; outputs : que':GradedQuestion; precondition : (* The question should not be automatically gradable. *) (que.autograde = false); postcondition: (* The grader assigns a feedback to the response manually. *) (que'.feedback = fdbk); end EditFeedback; (* Adds the points earned by each response in a test to calculate the final test score. *) operation CalculateTotalEarnedPoints inputs : gtest:GradedTest; outputs : gtest':GradedTest; precondition : (* Test has to be of the type: GradedTest *) (gtest.file.file_type?graded_test_type); postcondition: (* Adds up points earned for each graded question. *) (forall (i:integer | (i >= 1) and (i < #(gtest.gradedquestionlist))) (gtest.totalpoints = gtest.totalpoints + gtest.gradedquestionlist[i].pointsearned)); description : (* Just adds up points earned for each question. There is no way to check if the question is already graded by the grader. This is applicable for manually graded questions. Auto-gradable questions are already graded and points are awarded. *); end CalculateTotalEarnedPoints; (* Display a student test view: GUI output operation. *) operation DisplayStudentTestView inputs : ttest:TakenTest; outputs : gtest:GradedTest; precondition : (* None *); postcondition: (* Initialize the points earned and feedback for each test question. *) (forall (i:integer | (i >= 1) and (i < #(ttest.answeredquestionlist))) (gtest.gradedquestionlist[i].pointsearned = 0) and (gtest.gradedquestionlist[i].feedback = nil)); description : (* A student test view displays all the test questions and student responses, with the points earned for each question initialized to 0, and the feedback initialized to nil. *); end DisplayStudentTestView; (* Display a question view: GUI output operation. *) operation DisplayQuestionView inputs : ttests:TakenTest*, question_number:integer; outputs : gtests:GradedTest*, gque:GradedQuestion*; precondition : (* None *); postcondition: (* A graded question is visible only if the question index matches with the question number input by the user. *) (forall (i:integer | (i >= 1) and (i < #(ttests))) (forall (j:integer | (j >= 1) and (j < #(ttests[i].answeredquestionlist))) (gtests[i].gradedquestionlist[j] in gque) iff (question_number = gtests[i].gradedquestionlist[j].questionnumber) and (gtests[i].gradedquestionlist[j].pointsearned = 0) and (gtests[i].gradedquestionlist[j].feedback = nil))); description : (* A student test view displays all the test questions and student responses, with the points earned for each question initialized to 0, and the feedback initialized to nil. *); end DisplayQuestionView; end TestToolGrader; (*--- End module Grader ---*)