5.5. testgeneration.fmsl

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;

 


Prev: question.fmsl | Next: testgrading.fmsl | Up: specification | Top: index