(**** * * Question.rsl * By: Tyson Tate * * The Question module defines the objects contained in the various types of questions in the Test * Tool. *) module Question; export all; object FullQuestion is components: class:Class and keywords:Keywords and difficulty:Difficulty and timeToComplete:TimeToComplete and dateAdded:DateAdded and dateModified:DateModified and dateLastUsed:DateLastUsed and owner:User and questionBody:QuestionBody and keyAnswers:KeyAnswer* and id:ID; description: (* The question as it exists in a question bank and also the base class for the different question objects. *); end FullQuestion; object TestQuestion inherits from FullQuestion components: ptsPossible:PointsPossible* and position:TestPositionIndex; description: (* The question as it exists in an untaken test. *); end TestQuestion; object PointsPossible is integer description: (* The number of points possible in a question. *); end PointsPossible; object TestPositionIndex is integer description: (* The position that a question appears in a test. (1 is first, 2 is second, etc) *); end TestPositionIndex; object AnsweredQuestion inherits from TestQuestion components: studentAnswers:StudentAnswer*; description: (* A question that has been answered by a student but hasn't yet been graded. *); end AnsweredQuestion; object GradedQuestion inherits from AnsweredQuestion components: pointsEarned:PointsEarned* and feedback:Feedback; description: (* A question that has been graded. *); end GradedQuestion; object Feedback is string description: (* Teacher comments in a GradedQuestion. *); end Feedback; object PointsEarned is integer description: (* The number of points earned for an associated answer area. *); end PointsEarned; object ID is integer description: (* A unique number that identifies one and only one question in a question bank. ID numbers are assigned to a question when the question is created. Conflicts (for example two separate users who create questions that happen to have the same ID and then both users synchronize to the server) are resolved on a first-come, first-dibs basis meaning that questions with conflicting ID's that are being imported (added) are assigned a new ID. *); end ID; object Class is string description: (* The specific course that a question is primarily used in. *); end Class; object Keywords is string* description: (* A list of zero or more words and/or phrases that describe a question. *); end Keywords; object Difficulty is integer description: (* Number from 1 to 10 (inclusive) that indicates the difficulty of a question. 1 is low and 10 is high. *); end Difficulty; object TimeToComplete is components: quantity:Quantity and increments:Increments; description: (* The time it takes (on average) to complete a question. ('Quantity' number of 'Increments' increments. *); end TimeToComplete; object Quantity is integer description: (* A quantative amount. *); end Quantity; object Increments is components: sec:Seconds or min:Minutes or hours:Hours; description: (* Represents a measurement of time. *); end Increments; object Seconds is description: (* Unit of time. *); end Seconds; object Minutes is description: (* Unit of time (equivalent to 60 Seconds). *); end Minutes; object Hours is description: (* Unit of time (equivalent to 60 Minutes or 3,600 Seconds). *); end Hours; object QuestionBody is components: (txt:RawText or tag:Tag or img:Image or answerArea:AnswerArea)*; description: (* The actual question text represented as a series of RawText, Tag, or Image objects in HTML format. *); end QuestionBody; object RawText is string description: (* Plain text within an HTML document (i.e. not an HTML tag). *); end RawText; object Tag is string description: (* An HTML tag within a body of HTML code. *); end Tag; object Image is components: string; description: (* An image's data. Format doesn't matter as long as the operating system has the capability to display it. *); end Image; object DateAdded is components: date:Date; description: (* The date a question was first created. *); end DateAdded; object DateModified is components: date:Date; description: (* The date that a question was last modified (i.e. edited after the initial creation). *); end DateModified; object DateLastUsed is components: date:Date; description: (* The date a question was last used in a test. *); end DateLastUsed; object Date is components: day:Day and month:Month and year:Year and hour:Hour and minute:Minute; description: (* A specific day and time. *); end Date; object Day is integer description: (* The n-th day of a month. (1 though 31) *); end Day ; object Month is integer description: (* The n-th month of a year. (1 = January and 12 = December) *); end Month ; object Year is integer description: (* A specific year 2004 or later. (No object could have been made or used before the Test Tool was created). *); end Year ; object Hour is integer description: (* The hour in 24-hour format. (0 = 12:00 am and 23 = 11:00 pm) *); end Hour ; object Minute is integer description: (* The n-th minute of an hour. (Between 0 and 59, inclusive) *); end Minute ; object User is string description: (* The username of a valid user. *); end User; object AnswerArea is components: tfaa:TrueFalseAnswerArea or mcaa:MultipleChoiceAnswerArea or maa:MatchingAnswerArea or fintbaa:FillInTheBlankAnswerArea or saa:ShortAnswerArea or eaa:EssayAnswerArea or pcaa:ProgramCodeAnswerArea; description: (* Parent class of the different answer area types (for polymorphism). *); end AnswerArea; object TrueFalseAnswerArea is description: (* A True / False answer area. Contains no vomponents because it's content is inmutable. *); end TrueFalseAnswerArea; object MultipleChoiceAnswerArea is components: options:StringOption* and allowManyChoices:AllowManyChoices; description: (* A multiple choice answer that can also act as a multiple answer answer type. *); end MultipleChoiceAnswerArea; object StringOption is string description: (* An individual answer option. *); end StringOption; object AllowManyChoices is boolean description: (* If true, allows any number of selections in a MultipleChoiceAnswerArea, if false, allows one and only one choice. *); end AllowManyChoices; object MatchingAnswerArea is components: leftOptions:LeftSideOptions and rightOptions:RightSideOptions; description: (* A matching answer area. Contains two columns of options that can be "matched" (i.e. paired) to indicate connection. *); end MatchingAnswerArea; object RightSideOptions is components: options:StringOption*; description: (* The options on the right side of of a matching answer. *); end RightSideOptions; object LeftSideOptions is components: options:StringOption*; description: (* The options on the right side of of a matching answer. *); end LeftSideOptions; object FillInTheBlankAnswerArea is components: isCaseSens:IsCaseSensitive; description: (* A fill-in-the-blank answer area (an answer that consists of one word or a short phrase). *); end FillInTheBlankAnswerArea; object IsCaseSensitive is boolean description: (* If true, containing answer area is case-sensitive, if false, containing answer area is not case-sensitive. *); end IsCaseSensitive; object ShortAnswerArea is components: isMin:IsMinimum and txtQuantity:TextQuantity and limit:Limit; description: (* An answer area for text responses about a paragraph in length. The answer can only be manually graded. For text reponses that need to be automatically graded by an external program or script, an EssayAnswerArea should be used. The text response length is limited by 'Limit' number of 'TextQuantity' quantities of text. at a minimum or maximum. *); end ShortAnswerArea; object IsMinimum is boolean description: (* If true, IsMinimum represents "minimum", if false, IsMinimum represents "maximum". *); end isMinimum; object TextQuantity is components: chars:Characters or words:Words or lines:Lines; description: (* A quantity of text. *); end TextQuantity; object Characters is description: (* Uniti of measurement of text representing one character. *); end Characters; object Words is description: (* Unit of meadurement of text representing one word. *); end Words; object Lines is description: (* Unit of meadurement of text representing one line of text. *); end Lines; object Limit is integer description: (* The numeric value that represents a limit of text in the ShortAnswerArea object. *); end Limit; object EssayAnswerArea inherits from ShortAnswerArea components: manuallyGrade:ManuallyGrade and script:Script and scriptFileName:ScriptFileName; description: (* A long text answer area that can be automatically graded by passing the text response off to another program or script for grading. *); end EssayAnswerArea; object ManuallyGrade is boolean description: (* If true, the question is manually graded, if false, the question is automatically graded (by calling an user-specified outside program or script in the case of an Essay or program code answer area. *); end ManuallyGrade; object Script is string description: (* The script or program that will handle the automatic grading of a text answer. When the test is graded, the selected script or program is run and passed the path to a temporary text file containing the student's response. The script must then return a decimal number representing the percentage earned (i.e. ".834" = "83.4%") for the text response it was passed. The Test Tool will deterimine if the script needs to be run by examining the script's file name (ScriptFileName object) and looking at the file extension. For example, programs with a .pl extestion will be run with Perl. *); end Script; object ScriptFileName is string description: (* The file name of a script. *); end ScriptFileName; object ProgramCodeAnswerArea inherits from EssayAnswerArea description: (* Same as an essay answer area but uses a fixed-width font in the input area in a test suitable for code. *); end ProgramCodeAnswerArea; object TrueFalseAnswer is boolean description: (* A response to a True / False answer. If true, it represents the "True" option and if false, it represents the "False" option. *); end TrueFalseAnswer; object MultipleChoiceAnswer is components: options:StringOption*; description: (* The chosen answer(s) to a multiple choice question. *); end MultipleChoiceAnswer; object MatchingAnswer is components: matches:Match*; description: (* The matches choded as the correct responses to a matching answer area. *); end MatchingAnswer; object Match is components: leftOption:StringOption and rightOption:StringOption; description: (* A "match" between two StringOptions. *); end Match; object FillInTheBlankAnswer is string description: (* A text reponse to a fill-in-the-blank answer area. *); end FillInTheBlankAnswer; object TextAnswer is string description: (* The text enterd as a response to a short answer, essay answer, or program code answer area. *); end TextAnswer; object ShortAnswer inherits from TextAnswer description: (* The text response to a short answer area. *); end ShortAnswer; object EssayAnswer inherits from TextAnswer description: (* The text response to an essay answer area. *); end EssayAnswer; object ProgramCodeAnswer inherits from TextAnswer description: (* The text response to a program code answer area. *); end ProgramCodeAnswer; object Answer is components: tfa:TrueFalseAnswer or mca:MultipleChoiceAnswer or ma:MatchingAnswer or fitba:FillInTheBlankAnswer or sa:ShortAnswer or ea:EssayAnswer or pca:ProgramCodeAnswer; description: (* The parent class for the responses for the various answer areas. (Used for polymorphism) *); end Answer; object KeyAnswer inherits from Answer description: (* An instructor-supplied answer that is the correct answer for all the answer areas except for the ShortAnswerArea, EssayAnswerArea, and ProgramCodeAnswerArea object. For those three answer areas, the KeyAnswer object is the "example correct answer". *); end KeyAnswer; object StudentAnswer inherits from Answer description: (* The student's response to an answer area. *); end StudentAnswer; (* True if everything but the QuestionBody is the same between the two FullQuestions passed in, false if otherwise. *) function allEqualExceptQuestionBody(q1:FullQuestion, q2:FullQuestion)-> boolean = ( q1.class = q2.class and q1.keywords = q2.keywords and q1.difficulty = q2.difficulty and q1.timeToComplete = q2.timeToComplete and q1.dateAdded = q2.dateAdded and q1.dateModified = q2.dateModified and q1.dateLastUsed = q2.dateLastUsed and q1.owner = q2.owner and q1.keyAnswers = q2.keyAnswers and q1.id = q2.id ); operation insertImage is inputs: q:FullQuestion and position:integer and image:Image; outputs: q':FullQuestion; precondition: (* There exists a Question to add an iomage to and the Image exists *) (q != nil) and (image != nil) and (* The position to add the image is within the QuestionBody *) (position > 0) and (position <= #(q.questionBody)); postcondition: (* The image is inserted at 'position' position in the QuestionBody and the QuestionBody data after 'position' position is simply moved over. *) (* Nothing else is modified *) allEqualExceptQuestionBody(q, q'); end insertImage; operation insertTrueFalseAnswerArea is inputs: tfa:TrueFalseAnswerArea and q:FullQuestion and position:integer; outputs: q':FullQuestion; precondition: (* Question exists, answer area exists, and the position is within the confines of the question's QuestionBody *) (q != nil) and (tfa != nil) and (position > 0) and (position <= #(q.questionBody)); postcondition: (* The TrueFalseAnswer is inserted at 'position' position in the QuestionBody and the QuestionBody data after 'position' position is simply moved over. *) (* Nothing else is modified *) allEqualExceptQuestionBody(q, q') (* A KeyAnswer is also added. No formal spec written for that yet.*); end insertTrueFalseAnswerArea; operation insertMultipleChoiceAnswerArea is inputs: mcaa:MultipleChoiceAnswerArea and q:FullQuestion and position:integer; outputs: q':FullQuestion; precondition: (* Question exists, answer area exists, and the position is within the confines of the question's QuestionBody *) (q != nil) and (mcaa != nil) and (position > 0) and (position <= #(q.questionBody)) and (* Must be at least one option. *) (#(mcaa.options) > 0); postcondition: (* The MultipleChoiceAnswerArea is inserted at 'position' position in the QuestionBody and the QuestionBody data after 'position' position is simply moved over. *) (* Nothing else is modified *) allEqualExceptQuestionBody(q, q') (* A KeyAnswer is also added. No formal spec written for that yet.*); end insertMultipleChoiceAnswerArea; operation insertMatchingAnswerArea is inputs: maa:MatchingAnswerArea and q:FullQuestion and position:integer; outputs: q':FullQuestion; precondition: (* Question exists, answer area exists, and the position is within the confines of the question's QuestionBody*) (q != nil) and (maa != nil) and (position > 0) and (position <= #(q.questionBody)) and (* There must be at least one option on each side. *) (#(maa.leftOptions) > 0) and (#(maa.rightOptions) > 0); postcondition: (* The TrueFalseAnswer is inserted at 'position' position in the QuestionBody and the QuestionBody data after 'position' position is simply moved over. *) (* Nothing else is modified *) allEqualExceptQuestionBody(q, q') (* A KeyAnswer is also added. No formal spec written for that yet.*); end insertMatchingAnswerArea; operation insertFillInTheBlankAnswerArea is inputs: fitbaa:FillInTheBlankAnswerArea and q:FullQuestion and position:integer; outputs: q':FullQuestion; precondition: (* Question exists, answer area exists, and the position is within the confines of the question's QuestionBody*) (q != nil) and (fitbaa != nil) and (position > 0 and position <= #(q.questionBody)); postcondition: (* The TrueFalseAnswer is inserted at 'position' position in the QuestionBody and the QuestionBody data after 'position' position is simply moved over. *) (* Nothing else is modified *) allEqualExceptQuestionBody(q, q') (* A KeyAnswer is also added. No formal spec written for that yet.*); end insertFillInTheBlankAnswerArea; operation insertShortAnswerArea is inputs: saa:ShortAnswerArea and q:FullQuestion and position:integer; outputs: q':FullQuestion; precondition: (* Question exists, answer area exists, and the position is within the confines of the question's QuestionBody*) (q != nil) and (saa != nil) and (position > 0 and position <= #(q.questionBody)) and (* Options must be set and valid (i.e. no "maximum of 0 characters") *) (saa.isMin != nil and saa.txtQuantity != nil and saa.limit > 0); postcondition: (* The TrueFalseAnswer is inserted at 'position' position in the QuestionBody and the QuestionBody data after 'position' position is simply moved over. *) (* Nothing else is modified *) allEqualExceptQuestionBody(q, q') (* A KeyAnswer is also added. No formal spec written for that yet.*); end insertShortAnswerArea; operation insertEssayAnswerArea is inputs: eaa:EssayAnswerArea and q:FullQuestion and position:integer; outputs: q':FullQuestion; precondition: (* Question exists, answer area exists, and the position is within the confines of the question's QuestionBody*) (q != nil) and (eaa != nil) and (position > 0) and (position <= #(q.questionBody)) and (* Options must be set and valid (i.e. no "maximum of 0 characters") *) (eaa.isMin != nil) and (eaa.txtQuantity != nil) and (eaa.limit > 0) and (* The script must exist if we're to automatically grade it. *) (eaa.manuallyGrade = false) iff ((#(eaa.script) > 1) and (#(eaa.scriptFileName) > 1)); postcondition: (* The TrueFalseAnswer is inserted at 'position' position in the QuestionBody and the QuestionBody data after 'position' position is simply moved over. *) (* Nothing else is modified *) allEqualExceptQuestionBody(q, q') (* A KeyAnswer is also added. No formal spec written for that yet.*); end insertEssayAnswerArea; operation insertProgramCodeAnswerArea is inputs: pcaa:ProgramCodeAnswerArea and q:FullQuestion and position:integer; outputs: q':FullQuestion; precondition: (* Question exists, answer area exists, and the position is within the confines of the question's QuestionBody*) (q != nil) and (pcaa != nil) and (position > 0) and (position <= #(q.questionBody)) and (* Options must be set and valid (i.e. no "maximum of 0 characters") *) (pcaa.isMin != nil) and (pcaa.txtQuantity != nil) and (pcaa.limit > 0) and (* The script must exist if we're to automatically grade it. *) (pcaa.manuallyGrade = false) iff ((#(pcaa.script) > 1) and (#(pcaa.scriptFileName) > 1)); postcondition: (* The TrueFalseAnswer is inserted at 'position' position in the QuestionBody and the QuestionBody data after 'position' position is simply moved over. *) (* Nothing else is modified *) allEqualExceptQuestionBody(q, q') (* A KeyAnswer is also added. No formal spec written for that yet.*); end insertProgramCodeAnswerArea; end Question;