module TestGrading;
from QuestionModule import ClassName, Answer, CorrectAnswer,
ShortAnswer, Essay, Question, Type, Time,
ID, Notes, LastUsed, Modified, Created, AverageScore, Author,
Difficulty;
from TestGeneration import Test, PointsPossible, TotalPoints,
TotalGrade, TestQuestion, Roster, QuestionNumber;
from StudentInterface import Student;
from TestAdminister import ClassTests, QuestionView,
CurrentQuestion;
from TestTaking import AnsweredQuestion, StudentAnswer, AQStatus;
export GradedQuestion, Mean, Median, Mode, TestGradingView, SuccessRate;
--OBJECTS
object PointsEarned is number;
object SuccessRate is number;
object GradedQuestion inherits from AnsweredQuestion
components: pe:PointsEarned, sr:SuccessRate,
comm:Comment;
description: (* A question
once the auto-grader has graded it *);
end GradedQuestion;
object Mean is integer;
object Median is integer;
object Mode is integer;
object Comment is string;
object TestGradingView
components: qv:QuestionView*, cur:CurrentQuestion,
s:Student,
m:Mean, med:Median, md:Mode, r:Roster;
description: (* The TestGradingView object describes
what the instructor sees when
grading a test. It consists of: a window displaying on
QuestionView at a time, below each question are buttons to
skip to the first, prev, next or last question. On top are the
class statistics, and on the side is a list of all the student
in the class
*);
end TestGradingView;
--TEST GRADING OPERATIONS
operation AutoGradeTests
inputs: ct:ClassTests;
outputs: ct':ClassTests;
pre:
(*
* The tests have been
answered
*)
(forall (t in ct.t)
(t.aq != nil) and (t.qs =
nil) and (t.gq = nil));
post:
(*
* The points earned
are assigned
*)
(forall (i:integer | i <=
#(ct.t))
(forall (aq' in ct.t[i].aq)
(if
(aq'.type = "TrueFalse") or (aq'.type = "MultipleChoice") or
(aq'.type = "Matching") or (aq'.type = "FillIn") or (aq'.type =
"Programming")
then
(if (aq'.sa = aq'.ca)
then (exists (gq in ct'.t[i].gq) ((FieldsMatch(aq',gq)) and (gq.pe =
gq.points)))
else (exists (gq in ct'.t[i].gq) ((FieldsMatch(aq',gq)) and (gq.pe =
0))))
else
(if (aq'.type = "ShortAnswer")
then
(exists (gq in ct'.t[i].gq) ((FieldsMatch(aq',gq) and
(gq.pe = (gq.points * (NumKeywordsSA(aq',#(aq'.sa.sa)) /
#(aq'.sa.sa)))))))
else
(exists (gq in ct'.t[i].gq) ((FieldsMatch(aq',gq) and
(gq.pe = (gq.points * (NumKeywordsE(aq',#(aq'.sa.e)) /
#(aq'.sa.e))))))))))
and
(*
All questions are now graded questions *)
(ct'.t[i].aq = nil) and (ct'.t[i].gq != nil) and (ct'.t[i].qs = nil));
description: (* After the class's tests have been
submitted they are graded by the auto-grader.
Each question type is graded and all answered questions are converted to
graded questions. *);
end AutoGradeTests;
function NumKeywordsSA(aq: AnsweredQuestion, index:
integer)->(number)=
(
(*
* The number of keyword matches within the answer
*)
if (index > 0)
then (if (aq.ca.sa[index] in aq.sa.sa)
then (1 +
NumKeywordsSA(aq,index - 1))
else
(NumKeywordsSA(aq,index - 1)))
else (0)
);
function NumKeywordsE(aq: AnsweredQuestion, index:
integer)->(number)=
(
(*
* The number of keyword matches within the answe
*)
if (index > 0)
then (if (aq.ca.e[index] in aq.sa.sa)
then (1 +
NumKeywordsE(aq,index - 1))
else
(NumKeywordsE(aq,index - 1)))
else (0)
);
function FieldsMatch(aq: AnsweredQuestion, gq:
GradedQuestion)->(boolean)=
(
(*
* All fields in AnsweredQuestion are in
GradedQuestion
*)
gq.id = aq.id and
gq.type = aq.type and
gq.text = aq.text and
gq.difficulty = aq.difficulty and
gq.time = aq.time and
gq.ca = aq.ca and
gq.cn = aq.cn and
gq.topics = aq.topics and
gq.author = aq.author and
gq.as = aq.as and
gq.created = aq.created and
gq.modified = aq.modified and
gq.lu = aq.lu and
gq.notes = aq.notes and
gq.qt = aq.qt and
gq.points = aq.points and
gq.num = aq.num and
gq.sa = aq.sa and
gq.aqs = aq.aqs
);
operation AddComment
inputs: c:Comment, gq:GradedQuestion;
outputs: gq':GradedQuestion;
precondition:
(*
* comment must not empty
*)
c != nil;
postcondition:
(*
* The graded question is returned with
the new comment added
*)
(gq'.comm = c);
description: (*
Add a given comment to an
answer of a student's question.
The comment will appear in a text box below the
question.
*);
end AddComment;
operation DeleteComment
inputs: gq:GradedQuestion;
outputs: gq':GradedQuestion;
precondition:
(*
* There is a comment in the given
graded question.
*)
gq.comm != nil;
postcondition:
(*
* The given comment is not outputted in given graded
question.
*)
(gq'.comm = nil);
description: (*
Delete the selected a
question on a test by the user. After
user comfirms that the question is removed from that
student's
test or the entire class, the test properties are
then adjusted.
*);
end DeleteComment;
operation DeleteQuestionStudent
inputs: gq:GradedQuestion, t:Test;
outputs: t':Test;
precondition:
(*
* The given graded question is in
the given test.
*)
gq in t.gq;
postcondition:
(*
* The given graded question is not outputted in
given test.
*)
(forall (gq' in t.gq)
(gq' in t'.gq)
iff (gq' != gq))
and
(*
* The student's grade
is updated
*)
(t.tg = t.tg - gq.pe)
and
(*
* The test's total
points possible are update
*)
(t.tp = t.tp - gq.points);
description: (*
Delete the selected a
question on a test by the user. After
user comfirms that the question is removed from that
student's
test, the graded test properties are then adjusted.
*);
end DeleteQuestionStudent;
operation DeleteQuestionClass
inputs: gq:GradedQuestion, ct:ClassTests;
outputs: ct':ClassTests;
precondition:
(*
* The given graded question is in
the given tests.
*)
forall (t in ct.t)
(gq in t.gq);
postcondition:
(*
* The given graded question is not outputted in
given test.
*)
(forall (t in ct'.t)
(forall (gq' in t.gq)
(gq' in t.gq)
iff (gq' != gq)))
and
(*
* The students' grades
are updated
*)
(forall (t in ct'.t)
(t.tg = t.tg -
gq.pe))
and
(*
* The students' total
points are updated
*)
(forall (t in ct'.t)
(t.tp = t.tp -
gq.points));
description: (*
Delete the selected a
question on a test by the user. After
user comfirms that the question is removed from that
student's
test, the graded test properties are then adjusted.
*);
end DeleteQuestionClass;
operation ChangeCorrectAnswer
inputs: new_ca:CorrectAnswer, gq:GradedQuestion,
ct:ClassTests;
outputs: ct':ClassTests;
precondition:
(*
* Old and new correct
answer are not the same
*)
(gq.ca != new_ca)
and
(*
* The new correct
answer is not empty.
*)
(new_ca != nil);
postcondition:
(*
* A new correct answer
is in this question for every student's test in the class
*)
(forall (t in ct'.t)
(forall (gqs in
t.gq)
(gqs.ca =
new_ca) iff (gqs = gq)));
description: (*
The old correct answer turn
into a new answer changin the total grade earned
throughout the whole class. The new correct
answer cannot be the same as the
old correct answer.
*);
end ChangeCorrectAnswer;
operation ChangePointsPossible
inputs: new_pp:PointsPossible, gq:GradedQuestion,
ct:ClassTests;
outputs: ct':ClassTests;
precondition:
(*
* The old and new
points possible are not the same.
*)
(gq.points !=
new_pp)
and
(*
* The new points
possible is not empty.
*)
(new_pp != nil);
postcondition:
(*
* A new points
possible is in this question for every student's test in the class
*)
(forall (t in ct'.t)
(forall (gqs in
t.gq)
(gqs.points =
new_pp) iff (gqs = gq)));
description: (* The points possible for a certain
question are changed. The test's other information
is updated appropriately. *);
end ChangePointsPossible;
operation ChangePointsEarned
inputs: new_pe:PointsEarned, gq:GradedQuestion,
t:Test;
outputs: t':Test;
precondition:
(*
* The old and new
points earned are not the same.
*)
(gq.pe != new_pe)
and
(*
* The new points
earned is not empty.
*)
(new_pe != nil)
and
(*
* The graded question
is in the test
*)
(gq in t.gq);
postcondition:
(*
* A new points earned
is in the new graded question
*)
(forall (gqs in
t.gq)
(gqs.pe =
new_pe) iff (gqs = gq))
and
(*
* The points earned
are update
*)
(t'.tg = t.tg - gq.pe +
new_pe);
description: (*
Change the old point value
to a new point value.
*);
end ChangePointsEarned;
operation CalculateMean
inputs: ct: ClassTests;
outputs: m': Mean;
description: (*
A formula will be used to
calculate the mean of the class test scores.
*);
end CalculateMean;
operation CalculateMedian
inputs: ct: ClassTests;
outputs: md': Median;
description: (*
A formula will be used to
calculate the median of the class test scores.
*);
end CalculateMedian;
operation CalculateMode
inputs: ct: ClassTests;
outputs: md': Mode;
description: (*
A formula will be used to calculate the mode of the
class test scores.
*);
end CalculateMode;
operation CalculateSuccessRate
inputs: ct: ClassTests;
outputs: sr': SuccessRate;
description: (*
A formula will be used to calculate the success rate
of each question.
*);
end CalculateMode;
operation CalculateTotalPoint
inputs: t:Test;
outputs: tp:TotalPoints;
post: (* All points summed *)
(forall (q in t.gq)
(tp = tp +
q.points));
description: (*
The points possible of all
the questions within the test summed
*);
end CalculateTotalPoints;
op SelectStudent
inputs: s:Student;
outputs: tgv':TestGradingView;
post: (* Displays Student's test *)
(tgv'.s = s);
description: (* A new student's test is chosen
*);
end SelectStudent;
op DisplayPrevQuestion
inputs: tgv:TestGradingView;
outputs: tgv':TestGradingView;
pre: (* not at first question *)
tgv.cur > 1;
post: (* TestGradingView displays previous question
*)
tgv'.cur = tgv.cur-1;
description: (* The TestGradingView will display the
previous question in the test
*);
end DisplayPrevQuestion;
op DisplayNextQuestion
inputs: tgv:TestGradingView;
outputs: tgv':TestGradingView;
pre: (* not at last question *)
tgv.cur < #(tgv.qv);
post: (* TestGradingView displays next question *)
tgv'.cur = tgv.cur+1;
description: (* The TestGradingView will display the
next question in the test *);
end DisplayNextQuestion;
op DisplayFirstQuestion
inputs: tgv:TestGradingView;
outputs: tgv':TestGradingView;
pre: (* not at first question *)
tgv.cur>1;
post: (* TestGradingView displays first question *)
tgv'.cur=1;
description: (* The TestGradingView will display the
first question in the test *);
end DisplayFirstQuestion;
op DisplayLastQuestion
inputs: tgv:TestGradingView;
outputs: tgv':TestGradingView;
pre: (* not at last question *)
tgv.cur <
#(tgv.qv);
post: (* TestGradingView displays last question *)
tgv'.cur =
#(tgv.qv);
description: (* The TestGradingView will display the
last question in the test*);
end DisplayLastQuestion;
op SkipToQuestion
inputs: tgv:TestGradingView, question:integer;
outputs: tgv':TestGradingView;
pre: (* not at question already *)
tgv.cur !=
question;
post: (* TestGradingView displays specified question
*)
tgv'.cur =
question;
description: (* The TestGradingView will display the
question selected *);
end SkipToQuestion;
end TestGrading;
Prev: 5.6. TestGeneration |
Up: 5. Formal Specifications |
Next: 5.8. TestTaking |