module TestGeneration; (* Imports *) import Filtering.Filter; import Courses.Course; import Courses.TestType; import Questions.Question; import QuestionManagement.Database; import TestTaking.AnsweredTest; (* Exports *) export GeneratedTest; export GeneratedSection; export TestQuestion; export AuthorizedStudent; (* Objects *) object GenerationSession components: test:GeneratedTest, sectionCriteria:SectionCriteria*; description: (* The criteria for a test and the associated generated test. *); end GenerationSession; object GeneratedTest components: name:string, targetNumber:LockableInteger, targetLength:LockableInteger, term:Term, course:Course, students:AuthorizedStudent*, sections:GeneratedSection*, type:TestType, databases:Database*, deltaLength:integer, deltaNumber:integer, deltaType:integer; description: (* A test as it goes through the steps of the generation process. *); end GeneratedTest; object SectionCriteria components: targetNumber:LockableInteger, targetLength:LockableInteger, filter:Filter, databases:Database*; description: (* All of the information that is needed for the auto generator to determine how to populate a given section with appropriate quesitons. *); end SectionCriteria; object GeneratedSection components: questions:TestQuestion*, header:string; description: (* A section of a generated test that holds its questions and its section header. If an empty string is given as the header, the section will be displayed in print views and to students as part of the previous section. *); end GeneratedSection; object TestQuestion extends Question components: questionNumber:integer, points:integer; description: (* Holds all of the information about a question and its test-specific information. *); end TestQuestion; object LockableInteger components: isLocked:boolean, integerValue:integer; description: (* Holds an integer value and its locked or unlocked status information for the auto-generator to check. *); end LockableInteger; object AuthorizedStudent components: userName:string; description: (* A student who is allowed to take this test. *); end AuthorizedStudent; object Term components: termName:string, year:integer; description: (* Represents a term of a school year. *); end Term; object GeneratedTestPrint extends GeneratedTest components: printout:string; description: (* A generated test in the form that is printer-ready. *); end GeneratedTestPrint; object GeneratedTestFile extends GeneratedTest components: data:string; description: (* A generated test in the form of data ready to be written to a file. *); end GeneratedTestFile; (* Functions for Pre/Postconditions *) (* None Yet *) (* Operations *) operation AutoGenerateSection inputs: criteria:SectionCriteria; outputs: gensec:GeneratedSection; precondition: criteria.targetNumber.integerValue >= 0 and criteria.targetLength.integerValue >= 0 and #criteria.databases >= 1; postcondition: (* the outputted GeneratedSection has questions that meet all of the criteria in the given SectionCriteria object, no duplicates *); description: (* Autogenerates a section of a test, based on the given criteria. No other sections are modified/added/deleted. *); end AutoGenerateSection; operation NewTest inputs: course:Course, testtype:TestType, name:string, term: Term, numQuestions:integer, length:integer, databases:Database*, filter:Filter; outputs: test:GeneratedTest, sections:SectionCriteria*, session:GenerationSession; precondition: course.course != "" and name != "" and length >= 0 and numQuestions >= 0; postcondition: test.course = course and test.targetNumber.integerValue = numQuestions and test.targetLength.integerValue = length and test.type = testtype and test.databases = databases and #sections = 1 and sections[0].filter = filter; description: (* Creates a new test. *); end NewTest; operation NewSection inputs: targetLength:integer, targetNumber:integer, session:GenerationSession; outputs: session':GenerationSession, sectionCriteria:SectionCriteria, section:GeneratedSection; precondition: targetLength >= 0 and targetNumber >= 0 and not (sectionCriteria in session.sectionCriteria) and not (section in session.test.sections); postcondition: forall(sc:SectionCriteria) (sc in session'.sectionCriteria <=> (sc in session.sectionCriteria xor sc = sectionCriteria)) and forall(s:GeneratedSection) (s in session'.test.sections <=> (s in session.test.sections xor s = section)); description: (* Creates a new section for the test. *); end NewSection; operation DeleteSection inputs: section:GeneratedSection, sectionCriteria:SectionCriteria, session:GenerationSession; outputs: session':GenerationSession; precondition: section in session.test.sections and sectionCriteria in session.sectionCriteria; postcondition: forall(sc:SectionCriteria) ((sc in session'.sectionCriteria xor sc = sectionCriteria) <=> sc in session.sectionCriteria) and forall(s:GeneratedSection) ((s in session'.test.sections xor s = section) <=> s in session.test.sections); description: (* Removes a given section from a test. *); end DeleteSection; operation AddDatabase inputs: database:Database, test:GeneratedTest; outputs: test':GeneratedTest; precondition: not (database in test.databases); postcondition: forall(db:Database) (db in test'.databases <=> (db = database xor db in test.databases)); description: (* Adds a database to the consideration for this test. *); end AddDatabase; operation RemoveDatabase inputs: database:Database, test:GeneratedTest; outputs: test':GeneratedTest; precondition: database in test.databases; postcondition: forall(db:Database) ((db in test'.databases xor db = database) <=> db in test.databases); description: (* Removes a database from the consideration for this test. *); end RemoveDatabase; operation PrintTest inputs: test:GeneratedTest; outputs: print:GeneratedTestPrint; precondition: (* a valid test *); postcondition: (* all of the test data is in printable form *); description: (* Prints out a copy of a test. *); end PrintTest; operation SaveTest inputs: test:GeneratedTest; outputs: file:GeneratedTestFile; precondition: (* a valid test *); postcondition: (* the test is in a savable form *); description: (* Saves a test to a file. *); end SaveTest; operation ToggleLock inputs: int:LockableInteger; outputs: int':LockableInteger; precondition: ; postcondition: int.isLocked = not int'.isLocked; description: (* Toggles the lock state for a given LockableInteger *); end ToggleLock; operation EditQuestion inputs: question:TestQuestion; (* and what to change *) outputs: TestQuestion; precondition: (* a valid question on a test and valid options for change *); postcondition: (* the question has been changed using the given options, no other question has been modified, edited, or deleted *); description: (* Edits a question on the Test. *); end EditQuestion; operation SwapQuestionLocation inputs: question1:TestQuestion, question2:TestQuestion, section:GeneratedSection; outputs: question1':TestQuestion, question2':TestQuestion, section':GeneratedSection; precondition: question1 in section.questions and question2 in section.questions; postcondition: (question1'.questionNumber = question2.questionNumber) and (question2'.questionNumber = question1.questionNumber) and question1' in section'.questions and question2' in section'.questions and forall(q:TestQuestion) ((q.questionNumber != question1.questionNumber and q.questionNumber != question2.questionNumber) => (q in section.questions <=> q in section'.questions)); description: (* used to swap the location of two questions within the same section of a test *); end SwapQuestionLocation; operation SwapQuestionFromDB inputs: questionNew:Question, questionOld:TestQuestion, section:GeneratedSection; outputs: section':GeneratedSection, questionNew':TestQuestion; precondition: questionOld in section.questions; postcondition: not (questionOld in section'.questions) and questionNew' in section'.questions and questionNew'.questionNumber = questionOld.questionNumber and questionNew'.points = questionOld.points and forall(q:TestQuestion) ((q in section.questions xor q = questionNew') <=> (q in section'.questions xor q = questionOld)); description: (* Swaps a question in a GeneratedSection with one in a Database. *); end SwapQuestionFromDB; operation NewQuestion inputs: section:GeneratedSection (* info for a new question *); outputs: section':GeneratedSection, question:TestQuestion; precondition: (* valid info to create a question *); postcondition: question in section'.questions (* used the input data and no other questions have been added/removed/modified; this is using an unused questionNumber*); description: (* Makes a new question for a section of a test. Optionally, it is saved back to a question database. *); end NewQuestion; operation PreviewTest inputs: test:GeneratedTest; outputs: AnsweredTest; precondition: (* test is a valid GeneratedTest *); postcondition: (* the test has been copied into an empty AnsweredTest and displayed to the user on the screen. *); description: (* Shows the user what the test will look like to a student who is taking it. *); end PreviewTest; end TestGeneration;