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;