5.5. Test (test.rsl)

5.5. Test (test.rsl)

(* describe the test objects and operations *)
module Test;
    from Question import QuestionDB, Question, TestQuestion, AnsweredQuestion, GradedQuestion, QuestionText, Type, AutoGrade, CorrectAnswer, Time, Author, Keywords, QuestionNumber;
    from File import File, RequiresSaving;
    
    export Test, TakenTest, GradedTest, TestTime, TestName, Difficulty, TotalPoints, AddQuestionToTest;

    (* objects *)
    object Test is
      components: class:Class, testname:TestName, totalpoints:TotalPoints, difficulty:Difficulty, testtime:TestTime, file:File, requires_saving:RequiresSaving, 
              testquestionlist:TestQuestion*;
      description: (*
        A Test is the container for questions, and as such, is the most commonly
        manipulated item in the Test Tool.  The TestQuestion component holds one of many
        different kind of Question types.  The TestName component is a brief
        description of what the Test is for.  The TotalPoints component is used to
        store the total points for each Question in the Test.  The Difficulty
        component is an average difficulty of all the Questions in the Test.
        The file component contains the test as is appears on disk, and requires_saving tracks
        whether the test has been modified from its contents on disk.
    *);
    end Test;

    object TestName is string;
    object TotalPoints is integer;
    object Difficulty is integer;
    object TestTime is integer;

    (* TakenTest - Added by Sujit Polpaya. *)
    obj TakenTest inherits from Test
      components: answeredquestionlist:AnsweredQuestion*;
      description: (*
                    A taken test contains answered questions.
                   *);
    end TakenTest;

    obj GradedTest inherits from TakenTest    
      components: gradedquestionlist:GradedQuestion*, totalpointsearned:TotalPointsEarned, overallfeedback:OverallFeedback;
      description: (*
        The grading operation takes in a TakenTest and spits out a GradedTest.
      *);
    end GradedTest;

    object TotalPointsEarned is integer;
    object OverallFeedback is integer;
    
    (* automatic test generation *)
    operation SimpleTestGenerator is
        inputs: QuestionDB, SimpleTestProperties;
        outputs: Test;
        pre:
            (* question db exists *)
            (* and is has enough questions in the database*);
        post:
            (* test has been successfully generated *)
            (* all criteria have been met*);
    end SimpleTestGenerator;

    (* SimpleTestProperties are the required inputs for the SimpleTestGenerator *)
    object SimpleTestProperties is class:Class and title:Title and duration:Duration and numberofquestions:NumberOfQuestions; 
    object Class is string;
    object Title is string;
    object Duration is integer;
    object NumberOfQuestions is integer;

    operation AdvancedTestGenerator is
        inputs: ldb:QuestionDB, atp:AdvancedTestProperties;
        outputs: t:Test;
    end AdvancedTestGenerator;

    (* AdvancedTestProperties are the required inputs for the AdvancedTestGenerator *)
    object AdvancedTestProperties inherits from SimpleTestProperties
        components: testdifficulty:TestDifficulty and testdifficultyrange:TestDifficultyRange and keywordlist:Keyword* and questiontypes:QuestionTypes;
    end AdvancedTestProperties;

    object TestDifficulty is integer;
    object TestDifficultyRange is integer;
    object Keyword is string;

    object QuestionTypes is
        components: TFCount and MultCount and FITBCount and MatchCount and CodeCount and ShortAnsCount;
    end QuestionTypes;
    
    object TFCount is integer;
    object MultCount is integer;
    object FITBCount is integer;
    object MatchCount is integer;
    object CodeCount is integer;
    object ShortAnsCount is integer;

    (* Begin Chris Noe's section *)
    (* Describe the operations about adding and removing questions from test *)
    function QuestionInTest(t:Test, q:Question) -> (boolean) = (
	forall (q' in t.testquestionlist) (
		(q'.questiontext != q.questiontext)
		and
		(q'.type != q.type)
		and
		(q'.class != q.class)
		and
		(q'.time != q.time)
		and
		(q'.questiontext != q.questiontext)
		and
		(q'.difficulty != q.difficulty)
		and
		(q'.keywords != q.keywords)
		and
		(q'.correctanswer != q.correctanswer)
		and
		(q'.author != q.author)
	)
    ) end QuestionInTest;

    operation AddQuestionToTest is
	inputs: t:Test, q:TestQuestion;
	outputs: t':Test;

	(* Ensure that the question is not already in the test *)
	pre:	not (QuestionInTest(t, q));

	(* Ensure that the given question in is the test, but test is otherwise unchanged. *)
	(* Make sure to set requires_saving *)
	post:	(forall (q':TestQuestion) ((q' in t'.testquestionlist) iff (q' = q)))
		and
		(t.requires_saving = true);
    end AddQuestionToTest;

    operation EditTestQuestion is
        inputs: t:Test, q:TestQuestion;
        outputs: t':Test;
	
	(* Ensure that the question is in the test *)
	pre:	QuestionInTest(t, q);

	(* All fields can change, I'm not sure how to verify this *)
	(* Make sure to set requires_saving*)
        post:	(t.requires_saving = true);
    end EditTestQuestion;

    (* Remove a question from the test. *)
    operation RemoveQuestionFromTest is
	inputs: t:Test, q:TestQuestion;
	outputs: t':Test;

	(* Ensure that the question is in the test *)
	pre: QuestionInTest(t, q);

	(* Ensure that the question has been removed, but the test is otherwise unchanged *)
	(* Make sure to set requires_saving *)
	post:	(forall (q':TestQuestion) ((q' in t'.testquestionlist) iff (q' != q)))
		and
		(t.requires_saving = true);
    end RemoveQuestionFromTest;

    operation ReplaceTestQuestion is
        inputs: t:Test, q:TestQuestion;
        outputs: t':Test;

	(* Ensure that the question is in the test *)
	pre: QuestionInTest(t, q);

	(* Ensure that the question has been replaced with same type, and that *)
	(* at least one of the other fields has been modified *)
        post:
		(t'.requires_saving = true)
		and
		(t'.testquestionlist[q.questionnumber].type = t.testquestionlist[q.questionnumber].type)
		and (
		(t'.testquestionlist[q.questionnumber].questiontext != t.testquestionlist[q.questionnumber].questiontext)
		or
		(t'.testquestionlist[q.questionnumber].class != t.testquestionlist[q.questionnumber].class)
		or
		(t'.testquestionlist[q.questionnumber].time != t.testquestionlist[q.questionnumber].time)
		or
		(t'.testquestionlist[q.questionnumber].difficulty != t.testquestionlist[q.questionnumber].difficulty)
		or
		(t'.testquestionlist[q.questionnumber].keywords != t.testquestionlist[q.questionnumber].keywords)
		or
		(t'.testquestionlist[q.questionnumber].correctanswer != t.testquestionlist[q.questionnumber].correctanswer)
		or
		(t'.testquestionlist[q.questionnumber].author != t.testquestionlist[q.questionnumber].author)
        );
    end EditQuestionInTest;

    (* End Chris Noe's section *)

    (* Begin David Myers' section *)

  object SearchOptions
    components: contains:SContains and class:SClass and time:STime and 
        diff:SDifficulty and keywords:SKeywords and type:SType;
    description: (*
        SearchOptions supplies the needed fields to search the question database 
        for a question.  The Contains field is used to search the question text.  The 
        Class field is used to search for questions based on class.  The Time 
        searches for a question of a given duration.  Difficulty is used to search 
        for questions of a certain difficulty.  Keywords seaches the keyword component 
        of a question.  The Type object includes boolean variables for each type 
        of question.
    *);
  end SearchOptions;

  object SContains is string;
  object SClass is string;
  object STime is integer;
  object SDifficulty is integer;
  object SKeywords is string;

  object SType
    components: tf:TF and mc:MC and fitb:FITB and 
        matching:Matching and coding:Coding and short:Short;
    description: (*
        Type holds a boolean for each type of question to be included in the search.
    *);
  end Type;

  object TF is boolean;
  object MC is boolean;
  object FITB is boolean;
  object Matching is boolean;
  object Coding is boolean;
  object Short is boolean;

  operation Search is
    inputs: qdb:QuestionDB, options:SearchOptions;
    outputs: qdb':QuestionDB;
    description: (*
        Search performs a search of the Question database with parameters included in SearchOptions. 
        The search results are shown as a subset of the question database, so it outputs a new database.
    *);
  end Search;

  operation AutoAssignPoints is
    inputs: test:Test;
    outputs: test':Test;
    description: (*
        Automatically assigns points to questions in the test based on the question 
        difficulty, duration, and type.  This operation modifies the TotalPoints field of a 
        test, and modifies each TestQuestion within the test to contain points.
    *);
  end AutoAssignPoints;

  operation QuestionAssignPoints is
    inputs: test:Test, ques:TestQuestion;
    outputs: test':Test, ques':TestQuestion;
    description: (*
        QuestionAssignPoints is used to manually assign points to an idividual question in a test.
        The inputs include a test as well because the TotalPoints field inside a test will automatically
        update when an individual question's points are changed.  The operation outputs the question
        with points added, as well as the updated test.
    *);
  end QuestionAssignPoints;

  operation ChangeQuestionNumber is
    inputs: test:Test, ques:TestQuestion;
    outputs: test':Test, ques':TestQuestion;
    description: (*
        ChangeQuestionNumber is used to move a question's position on a test, and to change it's number.
        The question will be updated to include the new number, and the test will change to show the result
        of moving the question.
    *);
  end ChangeQuestionNumber;

  (* End David Myers' Section *)

end Test;

Prev: question.rsl | Next: generate.rsl | Up: formal specification | Top: index