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