(**** * * EditTest provides operations to modify the questions and parameters of a * test. This test may be empty, just created from a wizard, or produced * from an earlier session. The module works with a RawTest object, and * only modifies the parameters existing in that object. Therefore, the * test remains a RawTest object in this module. * *) module EditTest; from AdvancedTest import all; from QuestionBank import all; from Question import all; export all; object CurrentTest is components: testData:RawTest; operations: AddTestQuestion, DeleteTestQuestion, MoveQuestionUp, MoveQuestionDown, MoveQuestionTop, MoveQuestionBottom, ReplaceQuestion, ViewStatistics, RandomizeQuestionOrder, AverageDifficulty; description: (* A copy of the test opened from an existing test, or just created, for purposes of editing. All modifications are made to it, and it may be saved, possibly overwriting an existing RawTest. *); end CurrentTest; operation AddTestQuestion is inputs: currentTest:CurrentTest, questionBank:QuestionBank, whatQuestion:integer; outputs: currentTest':CurrentTest; precondition: (* One or more questions exist in the question database and a question exists with the 'whatQuestion' QID. Note: 'q' stands for the collection of Questions in the database and 'question' stands for the collection of Questions is a RawTest (or CurrentTest.testData) *) (questionBank.q != nil) and (exists (qs in questionBank.q) qs.qid = whatQuestion); postcondition: (* the newly added test question is in the modified test *) currentTest.testData.question = currentTest'.testData.question + GetQuestionFromQuestionBank(questionBank, whatQuestion); description: (* *); end AddTestQuestion; function GetQuestionFromQuestionBank(questionBank:QuestionBank, qid:integer)->(q:Question) = questionBank.q[qid]; operation DeleteTestQuestion is inputs: currentTest:CurrentTest, whatQuestions:integer*; outputs: currentTest':CurrentTest; precondition: (*One or more questions exist in currentTest. Questions containing the QIDs in whatQuestions exist in the test. A question exists at each position specified by whatQuestions.*) (currentTest.testData.question != nil) and forall (i:integer | (i >= 1) and (i < #currentTest.testData.question)) exists(qs in currentTest.testData.question) qs.qid = whatQuestions[i]; postcondition: (*The questions at the positions of whatQuestions are removed from the collection of DatabaseQuestions in currentTest. The ordering of the DatabaseQuestion collection is updated by the methods inherent in the implemented data structure.*) not ( forall (i:integer | (i >= 1) and (i < (#currentTest'.testData.question))) exists(qs in currentTest.testData.question) qs.qid = whatQuestions[i] ) and (#currentTest'.testData.question = #currentTest.testData.question - #whatQuestions); description: (* Deletes selected questions from a test. *); end DeleteTestQuestion; operation EditQuestion is inputs: currentTest:CurrentTest, whatQuestion:integer; outputs: currentTest':CurrentTest; precondition:(* One or more questions exist in currenTest*) (currentTest.testData.question != nil) and (* The question referenced by whatQuestion exists *) (whatQuestion >= 1) and (whatQuestion <= #currentTest.testData.question); postcondition:(* any modified editable parameters are reflected in that question of currentTest' *); description: (* Edits parameters of a question as in Add / Edit dialog *); end EditQuestion; operation MoveQuestionUp is inputs: currentTest:CurrentTest, whatQuestions:integer*; outputs: currentTest':CurrentTest; precondition: (*The list of selected questions is in the range of # on the test minus one (if all are selected, no moving is possible)*) (#whatQuestions >= 1) and (#whatQuestions <= #currentTest.testData.question - 1) (*There are questions in the test*) and (currentTest.testData.question != nil) (*the whatQuestions list is in increasing order (helps simplify postcondition modelling) *) and forall (i:integer | (i >= 1) and (i < (#whatQuestions - 1))) whatQuestions[i] < whatQuestions[i+1] and (*One or more questions exist in currentTest. A question exists at each position specified by whatQuestions.*) forall (i:integer | (i >= 1) and (i < #currentTest.testData.question)) exists(qs in currentTest.testData.question) qs.qid = whatQuestions[i]; postcondition:(* The questions at the positions of whatQuestions are moved up in the collection of Questions in currentTest. That is, the positions are subtracted by one. If one to N selected questions already exist at the top of the test (positions of 0 to N), they are not moved. The ordering of the DatabaseQuestion collection is updated by the methods inherent in the implemented data structure.*) forall (i:integer | (i >= 1) and (i < #whatQuestions)) if(whatQuestions[i] != 1) then currentTest.testData.question[whatQuestions[i]] = currentTest'.testData.question[whatQuestions[i]-1] or currentTest.testData.question[whatQuestions[i]] = currentTest'.testData.question[whatQuestions[i-1]]; description: (* Moves selected questions one position towards the beginning of the test. *); end MoveQuestionUp; operation MoveQuestionTop is inputs: currentTest:CurrentTest, whatQuestions:integer*; outputs: currentTest':CurrentTest; precondition: (*The list of selected questions is in the range of # on the test minus one (if all are selected, no moving is possible)*) (#whatQuestions >= 1) and (#whatQuestions <= #currentTest.testData.question - 1) (*There are questions in the test*) and (currentTest.testData.question != nil) (*the whatQuestions list is in increasing order (helps simplify postcondition modelling) *) and forall (i:integer | (i >= 1) and (i < (#whatQuestions - 1))) whatQuestions[i] < whatQuestions[i+1] and (*One or more questions exist in currentTest. A question exists at each position specified by whatQuestions.*) forall (i:integer | (i >= 1) and (i < #currentTest.testData.question)) exists(qs in currentTest.testData.question) qs.qid = whatQuestions[i]; postcondition: (*The questions at the positions of whatQuestions are moved to the beginning of the collection of DatabaseQuestions in currentTest. If N questions are selected, they take the positions of 0 to N-1. The ordering of the DatabaseQuestion collection is updated by the methods inherent in the implemented data structure.*) forall (i:integer | (i >= 1) and (i < #whatQuestions)) if(whatQuestions[i] != 1) then currentTest.testData.question[whatQuestions[i]] = currentTest'.testData.question[i]; description: (* Moves selected questions to the beginning of the test. *); end MoveQuestionTop; operation MoveQuestionDown is inputs: currentTest:CurrentTest, whatQuestions:integer*; outputs: currentTest':CurrentTest; precondition: (*The list of selected questions is in the range of # on the test minus one (if all are selected, no moving is possible)*) (#whatQuestions >= 1) and (#whatQuestions <= #currentTest.testData.question - 1) (*There are questions in the test*) and (currentTest.testData.question != nil) (*the whatQuestions list is in increasing order (helps simplify postcondition modelling) *) and forall (i:integer | (i >= 1) and (i < (#whatQuestions - 1))) whatQuestions[i] < whatQuestions[i+1] and (*One or more questions exist in currentTest. A question exists at each position specified by whatQuestions.*) forall (i:integer | (i >= 1) and (i < #currentTest.testData.question)) exists(qs in currentTest.testData.question) qs.qid = whatQuestions[i]; postcondition: (*The questions at the positions of whatQuestions are moved down in the collection of DatabaseQuestions in currentTest. That is, the positions are added by one. If one to N selected questions already exist at the bottom of the test (positions of MAX-N-1 to MAX-1), they are not moved. The ordering of the DatabaseQuestion collection is updated by the methods inherent in the implemented data structure.*) forall (i:integer | (i >= 1) and (i < #whatQuestions)) if(whatQuestions[i] != #currentTest.testData.question) then currentTest.testData.question[whatQuestions[i]] = currentTest'.testData.question[whatQuestions[i]+1] or currentTest.testData.question[whatQuestions[i]] = currentTest'.testData.question[whatQuestions[i+1]]; description: (* Moves selected questions one position towards the end of the test. *); end MoveQuestionDown; operation MoveQuestionBottom is inputs: currentTest:CurrentTest, whatQuestions:integer*; outputs: currentTest':CurrentTest; precondition: (*The list of selected questions is in the range of # on the test minus one (if all are selected, no moving is possible)*) (#whatQuestions >= 1) and (#whatQuestions <= #currentTest.testData.question - 1) (*There are questions in the test*) and (currentTest.testData.question != nil) (*the whatQuestions list is in increasing order (helps simplify postcondition modelling) *) and forall (i:integer | (i >= 1) and (i < (#whatQuestions - 1))) whatQuestions[i] < whatQuestions[i+1] and (*One or more questions exist in currentTest. A question exists at each position specified by whatQuestions.*) forall (i:integer | (i >= 1) and (i < #currentTest.testData.question)) exists(qs in currentTest.testData.question) qs.qid = whatQuestions[i]; postcondition: (*The questions at the positions of whatQuestions are moved to the end of the collection of DatabaseQuestions in currentTest. If N questions are selected, they take the positions of MAX-N-1 to MAX-1. The ordering of the DatabaseQuestion collection is updated by the methods inherent in the implemented data structure.*) forall (i:integer | (i >= 1) and (i < #whatQuestions)) if(whatQuestions[i] != #currentTest.testData.question) then currentTest.testData.question[whatQuestions[i]] = currentTest'.testData.question[#currentTest.testData.question - (#whatQuestions - i)]; description: (* Moves selected questions to the end of the test. *); end MoveQuestionBottom; operation ReplaceQuestion is inputs: currentTest:CurrentTest, whatQuestion:integer, newQuestionQID:integer, questionBank:QuestionBank; outputs: currentTest':CurrentTest; precondition: (*One or more questions exist in currentTest.*) (#currentTest.testData.question >= 1) and (*One or more questions exist in db.*) (#questionBank.q >= 1) and (*The question referenced by whatQuestion exists*) (whatQuestion >= 1) and (whatQuestion <= #currentTest.testData.question) and (*The question referenced by newQuestionID exists*) exists(aQuestion in questionBank.q) aQuestion.qid = newQuestionQID; postcondition: ; description: (* *); end ReplaceQuestion; operation RandomizeQuestionOrder is inputs: currentTest:CurrentTest; outputs: currentTest':CurrentTest; precondition: (*One or more questions exist in currentTest.*); postcondition: (*The order of the questions is randomized. The random number method is left to the decision of the implementors. The order of the data structure representing the collection of DatabaseQuestions in currentTest' is updated in whatever method is applicable for that particular data structure.*); description: (* *); end RandomizeQuestionOrder; operation ViewStatistics is inputs: currentTest:CurrentTest; outputs: ; precondition: (*The parameters existing in currentTest are valid (see the RawTest definitions)*); postcondition: (*The following items are listed in the 'Statistics' tab of the test editor: * -Test Title * -Class Name * -Test Type * -Test Points * -Test duration * -Number of questions * -Average difficulty of questions * -Highest difficulty of a question * -Lowest difficulty of a question * -Amount of each question type in test * -Test creation date * -Test Author*); description: (* Displays various test statistics. Viewable in the 'Statistics' tab of the test editor. *); end ViewStatistics; operation AverageDifficulty is inputs: currentTest:CurrentTest; outputs: averageDifficulty:integer; precondition: (* every question in currentTest has a valid difficulty *) forall(i:integer | (i >= 1) and (i <= #currentTest.testData.question)) (currentTest.testData.question[i].questionDifficulty >= 1) and (currentTest.testData.question[i].questionDifficulty <= 10); postcondition:(*The QuestionDifficulty fields in the collection of DatabaseQuestions in currentTest are summed and divided by the total number of DatabaseQuestions. If no questions exist in the exam, a 0 is returned.*) if #currentTest.testData.question = 0 then averageDifficulty = 0 else forall(i:integer | (i >= 1) and (i <= #currentTest.testData.question)) averageDifficulty = (averageDifficulty + currentTest.testData.question[i].questionDifficulty) / i; description: (* Gives the average difficulty of all the questions in the currentTest. *); end AverageDifficulty; end EditTest;