(**** * * Module TestGenerator defines the objects and operations related to the Test Generator * window. * * Note: Still working on this, not complete yet. *) module TestGen; from Test import GeneratedTest; from Question import all; from QuestionBank import all; export GenerateTest; object TestGenerator is description: (* Test Generation Window, given as input to the GenerateTest operation. Each tab is represented as an object. *); components: basics:Basics and difficulty:SelectDifficulty and question_types:QuestionTypes; end TestGenerator; object Basics is description: (* Basic test information, used to get a generic list of availible questions. *); components: class:Class and time_range:TimeRange and keywords:Keyword*; end Basics; object TimeRange is components: lower_qauntity:LowerQuantity and upper_quantity:UpperQuantity and min:Minutes; description: (* Represents the range that the total time of all the questions in the generated test must sum to. *); end TimeRange; object LowerQuantity is integer description: (* LowerQuantity is the number of minutes that a generated test must be greater then or equal to after generation. *); end LowerQuantity; object UpperQuantity is integer description: (* UpperQuantity is the number of minutes that a generated test must be less then or equal to after generation. *); end LowerQuantity; object Keyword is string description: (* One or more words that will be looked for int he keywords field of questions upon considering questions for selection when generating. *); end Keyword; object SelectDifficulty is description: (* The range that every question selected by the test generator must fall between. *); components: lower_difficulty:lowerDifficulty and upper_difficulty:upperDifficulty; end Difficulty; object lowerDifficulty is integer description: (* A value [1-10] that represents the difficulty each question must meet or exceed to be considered for selection. *); end lowerDifficulty; object upperDifficulty is integer description: (* A value [1-10] that represents the difficulty each question may not exceed to be considered for selection. *); end upperDifficulty; object QuestionTypes is description: (* List of the various question type and the amount of that type of question that must be in the test. *); components: tf_option:TrueFalseOption and mc_option:MultipleChoiceOption and mt_option:MatchingOption and fib_option:FillInTheBlankOption and sa_option:ShortAnswerOption and ea_option:EssayAnswerOption and pc_option:ProgramCodeOption and free_selected:FreeSelected; end QuestionTypes; object TrueFalseOption is description: (* The Percentage of this type of question to include if enabled. *); components: enabled:Enabled and percentage:Percentage; end TrueFalseOption; object MultipleChoiceOption is description: (* The Percentage of this type of question to include if enabled. *); components: enabled:Enabled and percentage:Percentage; end MultipleChoiceOption; object MatchingOption is description: (* The Percentage of this type of question to include if enabled. *); components: enabled:Enabled and percentage:Percentage; end MatchingOption; object FillInTheBlankOption is description: (* The Percentage of this type of question to include if enabled. *); components: enabled:Enabled and percentage:Percentage; end FillInTheBlankOption; object ShortAnswerOption is description: (* The Percentage of this type of question to include if enabled. *); components: enabled:Enabled and percentage:Percentage; end ShortAnswerOption; object EssayAnswerOption is description: (* The Percentage of this type of question to include if enabled. *); components: enabled:Enabled and percentage:Percentage; end EssayAnswerOption; object ProgramCodeOption is description: (* The Percentage of this type of question to include if enabled. *); components: enabled:Enabled and percentage:Percentage; end ProgramCodeOption; object FreeSelected is integer description: (* The Percentage of the test that is choosen by the Test Generator from among the enabled question types. This is in addition the amounts required by the user. *); end FreeSelected; object Enabled is boolean description: (* True if when a question type is to be included in a Generated Test. *); end Enabled; object Percentage is integer description: (* The percentage of the test that should be of a specific question type *); end Percentage; operation GenerateTest is inputs: tGen:TestGenerator, db:Bank.questions; outputs: genTest:GeneratedTest; description: (* Creates a test object from a given TestGenerator object. Searches question database for questions that match the specifications provided in the TestGenerator object. Returns a list of question IDs in the form of a GeneratedTest object. *); precondition: (* * The a selected classname exists *) (tGen.basics.class != nil) and (* * Two values for time have been provided. * ) (tGen.basics.time_range.lower_quantity != nil) and (tGen.basics.time_range.upper_quantity != nil) and (* * The Difficulty must be between 1 and 10, inclusive. *) (tGen.difficulty.lower_difficulty > 0) and (tGen.difficulty.upper_difficulty < 11) and (* * The minimum difficulty must be less then the maximum difficulty. *) (tGen.difficulty.lower_difficulty < tGen.difficulty.upper_difficulty); postcondition: (* * All the question types are of the type asked for. *) CheckQuestionTypes(tGen.question_types, genTest, db, tGen) and (* * The difficulty for each question is inbetween the range defined in tGen. *) forall (i:integer | (i >= 0) and (i < #genTest)) (exists (quest:FullQuestion) (quest.id = genTest[i]) and (checkRange(quest.difficulty, tGen.difficulty.lower_difficulty, tGen.difficulty.upper_difficulty) ) ) and (* * The total time for the test is between the values specified. *) (checkTotalTime(genTest, db, tGen.basics.time_range.lower_qauntity, tGen.basics.time_range.upper_quantity) ); end GenerateTest; function checkTotalTime(genTest:GeneratedTest, db:Bank, low:integer, high:integer) -> boolean = ( (* * Checks the total Time of the test. *) (checkRange(totalTime(getQuestions(genTest, db), 0), low, high)) ); function checkRange(diff:integer, min:integer, max:integer) -> boolean = ( (diff >= min) and (diff <= max) ); function totalTime(questList:FullQuestion*, idx:integer) -> integer = ( if (idx < #questList) then if (questList[idx].timeToComplete.increments?sec) then questList[idx].timeToComplete.quantity / 60 + totalTime(questList, idx+1) else if (questList[idx].timeToComplete.increments?hours) then questList[idx].timeToComplete.quantity * 60 + totalTime(questList, idx+1) else questList[idx].timeToComplete.quantity + totalTime(questList, idx+1) else if (questList[idx].timeToComplete.increments?sec) then questList[idx].timeToComplete.quantity / 60 else if (questList[idx].timeToComplete.increments?hours) then questList[idx].timeToComplete.quantity * 60 else questList[idx].timeToComplete.quantity ); function CheckQuestionTypes(types:QuestionTypes, idList:GeneratedTest, QB:Bank, tPrefs:TestGenerator) -> boolean = ( (* * Returns True if all the questions in the Generated Test are of the Type specified * in the TestGenerator object. *) forall(i:integer | (i >= 0) and (i < #idList)) (exists (quest:FullQuestion) (quest.id = idList[i]) and (forall (j:integer | (j >= 0) and (j < #quest.questionBody)) if (quest.questionBody[i]?answerArea) then ( if (quest.questionBody[i].answerArea?tfaa) then tPrefs.question_types.tf_option.enabled else if (quest.questionBody[i].answerArea?mcaa) then tPrefs.question_types.mc_option.enabled else if (quest.questionBody[i].answerArea?maa) then tPrefs.question_types.mt_option.enabled else if (quest.questionBody[i].answerArea?saa) then tPrefs.question_types.sa_option.enabled else if (quest.questionBody[i].answerArea?eaa) then tPrefs.question_types.ea_option.enabled else tPrefs.question_types.pc_option.enabled ) else true ) ) ); function getQuestions(qIDList:GeneratedTest, QB:Bank) -> FullQuestion* = ( (* * Returns a list of question objects from list of question ids. *) appendQuestion(qIDList, QB, 1) ); function appendQuestion(qIDlist:GeneratedTest, QB:Bank, index:integer) -> FullQuestion* = ( if (index < (#qIDlist)) then findQuestion(qIDlist[index], QB, 1) + appendQuestion(qIDlist, QB, index+1) else findQuestion(qIDlist[index], QB, 1) ); function findQuestion(id:integer, QB:Bank, index:integer) -> FullQuestion = ( if (QB[index].id = id) then QB[index] else findQuestion(id, QB, index + 1) ); end TestGen;