(* * * Module StudentTestTool defines the objects and operations related to students * taking a test (viewing and filling in answers, submitting, etc.) * *) module StudentTestTool; from TestPackage import all; export all; object StudentAnswer is string description: (* The student's answer/response to a question. *); end StudentAnswer; object StudentName is string description: (* The student's name. *); end StudentName; object StudentTimeRemaining is integer description: (* The number of seconds remaining for a test session. *); end StudentTimeRemaining; object IsQuestionAnswered is boolean description: (* Indicates if question is filled in or not. *); end IsQuestionAnswered; object IsQuestionFlagged is boolean description: (* Indicates if the student has flagged the question. *); end IsQuestionFlagged; object IsTestPaused is boolean description: (* Indicates if the student's test session is paused. *); end IsTestPaused; object IsTestFinished is boolean description: (* Indicates if the student' test is finished. *); end IsTestFinished; object GoToQuestionNumber is integer description: (* The question number the student wishes to jump to. *); end GoToQuestionNumber; object QuestionStatus is ia:IsQuestionAnswered and ifg:IsQuestionFlagged description: (* An indicator to the student to whether the question is * answered or unanswered and is flagged or not flagged. *); end QuestionStatus; object QuestionSequence is aqs:AnsweredQuestion* and qstats:QuestionStatus* and cur_ques:integer description: (* The sequence of questions the student's sees during the testing * session. *); end QuestionSequence; object SignalProctorMessage is string description: (* A message notifying the proctor that a student needs help. *); end SignalProctorMessage; object TestSession is components: tt:TakenTest and tr:StudentTimeRemaining and ip:IsTestPaused and itf:IsTestFinished; description: (* A student's testing session which includes the test, * the time remaining and testing status. *); end TestSession; object AnsweredQuestion inherits from TestQuestion components: sa:StudentAnswer; description: (* A question with a student answer. *); end AnsweredQuestion; object TakenTest inherits from Test components: sn:StudentName and aq:AnsweredQuestion*; description: (* A Test that has a student name and the student's answers. *); end TakenTest; operation Next inputs: qs:QuestionSequence and ts:TestSession; outputs: qs':QuestionSequence and ts':TestSession; precondition: (* Must not be the last question. * Test must not be paused. * Test must not be finished. *) (qs.cur_ques < #qs.aqs) and (ts.ip = false) and (ts.itf = false); postcondition: (* The questions in the question sequence remain unchanged. * The flagged status of all questions remain unchanged. * The answered status of each question except the current one * remains unchanged. The current question status changes * depending if it was answered or not. * Advance one in the question sequence. *) qs'.aqs = qs.aqs and (forall (i:integer | (i >= 1) and (i < #qs.aqs)) qs'.qstats[i].ifg = qs.qstats[i].ifg) and (forall (i:integer | (i >= 1) and (i < #qs.aqs) and (i != qs.cur_ques)) qs'.qstats[i].ia = qs.qstats[i].ia) and (if (qs.aqs[qs.cur_ques].sa = nil) then qs'.qstats[qs.cur_ques].ia = false else qs'.qstats[qs.cur_ques].ia = true) and qs'.cur_ques = qs.cur_ques + 1 ; description: (* Advance to the next question in the test. Update * answered/unanswered status for the current question. *); end Next; operation Previous inputs: qs:QuestionSequence and ts:TestSession; outputs: qs':QuestionSequence and ts':TestSession; precondition: (* Must not be the first question. * Test must not be paused. * Test must not be finished. *) (qs.cur_ques > 1) and (ts.ip = false) and (ts.itf = false); postcondition: (* The questions in the question sequence remain unchanged. * The flagged status of all questions remain unchanged. * The answered status of each question except the current one * remains unchanged. The current question status changes * depending if it was answered or not. * Move back one in the question sequence. *) qs'.aqs = qs.aqs and (forall (i:integer | (i >= 1) and (i < #qs.aqs)) qs'.qstats[i].ifg = qs.qstats[i].ifg) and (forall (i:integer | (i >= 1) and (i < #qs.aqs) and (i != qs.cur_ques)) qs'.qstats[i].ia = qs.qstats[i].ia) and (if (qs.aqs[qs.cur_ques].sa = nil) then qs'.qstats[qs.cur_ques].ia = false else qs'.qstats[qs.cur_ques].ia = true) and qs'.cur_ques = qs.cur_ques - 1 ; description: (* Move back to the previous question in the test. Update * answered/unanswered status for the current question. *); end Previous; operation GoToQuestion inputs: qs:QuestionSequence and ts:TestSession and gtnum:GoToQuestionNumber ; outputs: qs':QuestionSequence and ts':TestSession; precondition: (* Current question must be in range. * Test must not be paused. * Test must not be finished. *) (gtnum > 0 and gtnum <= #qs.aqs) and (ts.ip = false) and (ts.itf = false); postcondition: (* The questions in the question sequence remain unchanged. * The flagged status of all questions remain unchanged. * The answered status of each question except the current one * remains unchanged. The current question status changes * depending if it was answered or not. * Set the specified number as the current question number. *) qs'.aqs = qs.aqs and (forall (i:integer | (i >= 1) and (i < #qs.aqs)) qs'.qstats[i].ifg = qs.qstats[i].ifg) and (forall (i:integer | (i >= 1) and (i < #qs.aqs) and (i != qs.cur_ques)) qs'.qstats[i].ia = qs.qstats[i].ia) and (if (qs.aqs[qs.cur_ques].sa = nil) then qs'.qstats[qs.cur_ques].ia = false else qs'.qstats[qs.cur_ques].ia = true) and qs'.cur_ques = gtnum ; description: (* Jump to a specified question number in the test. *); end GoToQuestion; operation FlagQuestion is inputs: qs:QuestionSequence; outputs: qs':QuestionSequence; precondition: qs.cur_ques > 1 and qs.cur_ques <= #qs.aqs; postcondition: (* Flag the current question if it is not flagged. * Unflag the current question if it is already flagged. * The answered/unanswered status of all questions remain unchanged. * The number of questions and the current question remain unchanged. *) (if (qs.qstats[qs.cur_ques].ifg = false) then qs'.qstats[qs.cur_ques].ifg = true) and (if (qs.qstats[qs.cur_ques].ifg = true) then qs'.qstats[qs.cur_ques].ifg = false) and qs'.aqs = qs.aqs and (forall (i:integer | (i >= 1) and (i < #qs.aqs) and (i != qs.cur_ques)) qs'.qstats[i].ifg = qs.qstats[i].ifg) and (forall (i:integer | (i >= 1) and (i < #qs.aqs)) qs'.qstats[i].ia = qs.qstats[i].ia) and qs'.cur_ques = qs.cur_ques ; description: (* Flag a question in the test as questionable. Indicates to the student * that he/she should revisit it. *); end FlagQuestion; operation TakeTest is inputs: tt:TakenTest, sn:StudentName, aq:AnsweredQuestion* and ts:TestSession; outputs: tt':TakenTest; precondition: (* Must have a valid TakenTest. * The student name must 'on' the test. * Test must not be paused. * Test must not be finished. *) (tt != nil) and (sn != nil) and (ts.ip = false) and (ts.itf = false); postcondition: (* Must produce a valid TakenTest. * Test must have student's name. * Test must have student's answers. *) (tt' != nil) and (tt'.sn = sn) and (tt'.aq = aq); description: (* Takes a blank TakenTest and fills it in with the student's name * and the student's answers. *); end TakeTest; operation AnswerQuestion is inputs: aq:AnsweredQuestion and sa:StudentAnswer and ts:TestSession; outputs: aq':AnsweredQuestion; precondition: (ts.ip = false) and (ts.itf = false); postcondition: (* Output answered question must contain student's input answer. *) aq'.sa = sa; description: (* Fills in the question with the student's answer. *); end AnswerQuestion; operation FinishTest is inputs: tt:TakenTest and ts:TestSession; outputs: tt':TakenTest and ts':TestSession; precondition: (* Must be a valid test. * Student name must be present. * (Answers may be blank). * Cannot finish a finished test. *) (tt != nil) and (tt.sn != nil) and (ts.itf = false); postcondition: (* Test remains unchanged. * Test is finished. *) (tt' = tt) and (ts'.itf = true); description: (* Finishes the test and submits the student's * taken test to the proctor. *); end FinishTest; operation SignalProctor is inputs: spm:SignalProctorMessage and ts:TestSession ; outputs: spm':SignalProctorMessage and ts':TestSession; precondition: (spm != nil) and (ts != nil) and (ts.itf = false); postcondition: (spm' != nil) and (ts' = ts); description: (* Sends a message to the proctor. *); end SignalProctor; end StudentTestTool;