5.4. Questions and Question Database (question.rsl)

(* define the question database and its operations *)
module Question;
	from Test import Test;
	
	export QuestionDB, Question, TestQuestion, AnsweredQuestion, GradedQuestion, QuestionText, Type, Class,
               LastUsed, Keywords, Author, Time, Difficulty, AutoGrade, CorrectAnswer, QuestionNumber, Response,
               Points, PointsEarned, Feedback;

	object QuestionDB is
	  components: Question*;
	  description: (*
		A QuestionDB is the highest level structure containing any number of questions.
		There are two kinds of databases, a local copy and a server copy.  When the 
		user first creates their local copy, it will become a replica of the 
		server QuestionDB.  After that, each QuestionDB remains separate, with operations
		available to edit each individualy.
	  *);
	end QuestionDB;

	object Question is
	  components: type:Type and class:Class and time:Time and difficulty:Difficulty and lastused:LastUsed and 
			keywords:Keywords and graphiclist:Graphic* and
			autograde:AutoGrade and questiontext:QuestionText and correctanswer:CorrectAnswer and author:Author;
	  description: (*
		A Question is the generic definition for the types of questions stored
		in a Test. Type determines what type of question (T/F, Multiple choice,
		Fill-in-the-blank, Matching, Essay, Code). The Class component is used to describe
		the general subject matter of the Question.  The Time component is an approximate
		time required to complete the Question.  The Difficulty component is a
		user defined approximation of the difficulty of the particular Question.
		The LastUsed component holds the date of when the question was last used on
		any exam.  The Keywords component contains a list of words describing the
		subject matter of the Question. The Graphic component holds any number of graphics
		associated with the Question.  The AutoGrade component is a boolean expression denoting
		whether or not the Question can be graded automatically. CorrectAnswer contains the correct
		answer to the question (for the autograder).

		*(subject to change)*
	  *);
	end Question;

	object Type is string;
	object Class is string;
	object Time is integer;
	object Difficulty is integer;
	object LastUsed is string;
	object Keywords is string;
	object Graphic;
	object AutoGrade is boolean;
	object QuestionText is string;
	object CorrectAnswer is string;
      object Author is string;

       object TestQuestion inherits from Question
          components: questionnumber:QuestionNumber, points:Points;
          description: (*
                QuestionNumber describes the position of the question within a Test.
                The Points component is used to hold the value of the Question.
                be stored when the Question is saved into a Database, but will only be used
                when the Question is saved into a Test.
          *);
        end TestQuestion;

	object QuestionNumber is integer;
	object Points is integer;

        object AnsweredQuestion inherits from TestQuestion
                components: response:Response;
          description: (*
                Response contains the student's choice of answer to the question.
          *);
        end AnsweredQuestion;

	object Response is string;

        object GradedQuestion inherits from AnsweredQuestion
          components: pointsearned:PointsEarned, feedback:Feedback;
          description: (*
                PointsEarned is used to mark how many points the student has earned for this
                question (set by autograder).
                Feedback contains a professors input back to the student regarding their response.
          *);
        end GradedQuestion;

	object PointsEarned is integer;
	object Feedback is string;
	object LocalDB is QuestionDB;
	object SharedDB is QuestionDB;	

	(* begin operations *)



operation AddQuestionToLocalDB is
	inputs: ldb:LocalDB, q:Question;
	outputs: ldb':LocalDB;

    description: (*
        Add the given question to the LocalDB.  The properties
        of the question such as the questiontext, type, class
        time, difficulty, keywords, and correct answer must not
        all be the same as an existing question in the LocalDB.
    *);

    precondition:
        (*
         * There is no question in the local database with the same
         * properties as the input question, q.
         *)
       (not (exists (q' in ldb) q'.questiontext = q.questiontext))

            and

        (not (exists (q' in ldb) q'.type = q.type))

            and
    
        (not (exists (q' in ldb) q'.class = q.class))

            and

        (not (exists (q' in ldb) q'.time = q.time))

            and

        (not (exists (q' in ldb) q'.difficulty = q.difficulty))

            and

        (not (exists (q' in ldb) q'.keywords = q.keywords))

            and

        (not (exists (q' in ldb) q'.correctanswer = q.correctanswer))

	    and
	
	(not (exists (q' in ldb) q'.author = q.author));

    postcondition:
        (*
         * A question is in the database if and only if it is the new
         * question to be added or it is in the input database.
         *)
        
        forall (q':Question)
            (q' in ldb') iff ((q' = q) or (q' in ldb));

end AddQuestionToLocalDB;





operation RemoveQuestionFromLocalDB is
	inputs: ldb:LocalDB, q:Question;
	outputs: ldb':LocalDB;
    
    description:(*
        Delete the given question from the given local database.
        The given question must already be in the input local
        database.  The question to be deleted from the local
        database must match the input question in all properties.
    *);

    precondition:
        (*
         * The given Question is in the given LocalDB
         *)
        q in ldb;

    postcondition:
        (*
         * The question is in the output database if and only if it is not
         * the existing record to be deleted and it is in the input db.
         *)
        (forall (q':Question)
            (q' in ldb') iff ((q' != q) and (q' in ldb)));

end RemoveQuestionFromLocalDB;




operation ModifyQuestionInLocalDB is
	inputs: ldb:LocalDB, old_q:Question, new_q:Question;
	outputs: ldb':LocalDB;
    
    description:(*
        Change the given old question to the given new question.
        The old and new questions must not be the same.  The old question
        must already be in the input database.  The new question must meet
        the same conditions as for the input to the AddQuestionToLocalDB
        operation.
    *);

    precondition:
        (*
         * The old and new questions are not the same.
         *)
        (old_q != new_q)
    
            and

        (*
         * The old question is in the given database.
         *)
        (old_q in ldb)

            and

        (*
         * There is no question in the local database with the same
         * properties as the input question, q.
         *)
        (not (exists (new_q' in ldb) new_q'.questiontext = new_q.questiontext))

            and

        (not (exists (new_q' in ldb) new_q'.type = new_q.type))

            and
    
        (not (exists (new_q' in ldb) new_q'.class = new_q.class))

            and

        (not (exists (new_q' in ldb) new_q'.time = new_q.time))

            and

        (not (exists (new_q' in ldb) new_q'.difficulty = new_q.difficulty))

            and

        (not (exists (new_q' in ldb) new_q'.keywords = new_q.keywords))

            and

        (not (exists (new_q' in ldb) new_q'.correctanswer = new_q.correctanswer))

	    and

	(not (exists (new_q' in ldb) new_q'.author = new_q.author));

     postcondition:
        (*
         * A question is in the output database if and only if it is the new
         * question to be added or it is in the input database, an dit is not
         * the old question.
         *)
        forall (q':Question)
            (q' in ldb') iff (((q' = new_q) or (q' in ldb)) and (q' != old_q));   
    
        
end ModifyQuestionInLocalDB;



operation TransferQuestionToSharedDB is

	inputs: ldb:LocalDB, sdb:SharedDB, q:Question;
	outputs: sdb':SharedDB;

	description:(*
		Transfer the given Question from the local database to the shared
		database.  The Login operation for the server must occur before
		the transfer.  Before the question is transfered, the question
		should not already be in the shared database.  After the transfer,
		the shared database should contain all questions as the input
		shared database plus the question that was added from the local
		database.
	*);

	precondition:
	   (*
	    * The question to be transfered should not exist in the shared database.
	    *)

	  (not (exists (q' in sdb) q'.questiontext = q.questiontext))

            and

        (not (exists (q' in sdb) q'.type = q.type))

            and
    
        (not (exists (q' in sdb) q'.class = q.class))

            and

        (not (exists (q' in sdb) q'.time = q.time))

            and

        (not (exists (q' in sdb) q'.difficulty = q.difficulty))

            and

        (not (exists (q' in sdb) q'.keywords = q.keywords))

            and

        (not (exists (q' in sdb) q'.correctanswer = q.correctanswer))

	    and

	(not (exists (q' in sdb) q'.author = q.author));


	postcondition:
	  (*
         * The output shared database should only contain the question from
	   * the input shared database and the question added from the local
	   * database.
         *)
        forall (q':Question)
            (q' in sdb') iff (((q' = q) or (q' in sdb))); 

end TransferQuestionToSharedDB;



operation TransferSingleQuestionToLocalDB is

	inputs: ldb:LocalDB, sdb:SharedDB, q:Question;
	outputs: ldb':LocalDB;

	description:(*
		Transfer the given Question from the shared database to the local
		database.  The Login operation for the server must occur before
		the transfer.  Before the question is transfered, the question
		should not already be in the local database.  After the transfer,
		the local database should contain all questions as the input
		local database plus the question that was added from the shared
		database.
	*);

	precondition:
	   (*
	    * The question to be transfered should not exist in the local database.
	    *)

	  (not (exists (q' in ldb) q'.questiontext = q.questiontext))

            and

        (not (exists (q' in ldb) q'.type = q.type))

            and
    
        (not (exists (q' in ldb) q'.class = q.class))

            and

        (not (exists (q' in ldb) q'.time = q.time))

            and

        (not (exists (q' in ldb) q'.difficulty = q.difficulty))

            and

        (not (exists (q' in ldb) q'.keywords = q.keywords))

            and

        (not (exists (q' in ldb) q'.correctanswer = q.correctanswer))

	 	and

        (not (exists (q' in ldb) q'.author = q.author));


	postcondition:
	  (*
           * The output local database should only contain the question from
	   * the input local database and the question added from the shared
	   * database.
           *)
        forall (q':Question)
            (q' in ldb') iff (((q' = q) or (q' in ldb))); 

end TransferSingleQuestionToLocalDB;



operation TransferAllQuestionsToLocalDB is

	inputs: ldb:LocalDB, sdb:SharedDB;
	outputs: ldb':LocalDB;

	description:(*
		Transfer the all the given Questions from the shared database to the local
		database.  The Login operation for the server must occur before
		the transfer.  Before the questions are transfered, there should be
		no questions in the local database. After the transfer,
		the local database should contain all questions in the shared database.
	*);

	precondition:
	   (*
	    * Remove all questions in the local database.
	    *)

	  forall(q' in ldb)
		 (q' = nil);

	postcondition:
	  (*
           * The output local database should only contain the questions from
	   * the input shared database. 
           *)
        forall (q':Question)
            (q' in ldb') iff (q' in sdb); 

end TransferAllQuestionsToLocalDB;



operation ModifyQuestionInSharedDB is
	inputs: sdb:SharedDB, old_q:Question, new_q:Question;
	outputs: sdb':SharedDB;
    
    description:(*
        Change the given old question to the given new question.
        The old and new questions must not be the same.  The old question
        must already be in the input database.  The new question must meet
        the same conditions as for the input to the AddQuestionToLocalDB
        operation.
    *);

    precondition:
        (*
         * The old and new questions are not the same.
         *)
        (old_q != new_q)
    
            and

        (*
         * The old question is in the given database.
         *)
        (old_q in sdb)

            and

        (*
         * There is no question in the shared database with the same
         * properties as the input question, q.
         *)
        (not (exists (new_q' in sdb) new_q'.questiontext = new_q.questiontext))

            and

        (not (exists (new_q' in sdb) new_q'.type = new_q.type))

            and
    
        (not (exists (new_q' in sdb) new_q'.class = new_q.class))

            and

        (not (exists (new_q' in sdb) new_q'.time = new_q.time))

            and

        (not (exists (new_q' in sdb) new_q'.difficulty = new_q.difficulty))

            and

        (not (exists (new_q' in sdb) new_q'.keywords = new_q.keywords))

            and

        (not (exists (new_q' in sdb) new_q'.correctanswer = new_q.correctanswer))

		and

        (not (exists (new_q' in sdb) new_q'.author = new_q.author));


     postcondition:
        (*
         * A question is in the output database if and only if it is the new
         * question to be added or it is in the input database, and it is not
         * the old question.
         *)
        forall (q':Question)
            (q' in sdb') iff (((q' = new_q) or (q' in sdb)) and (q' != old_q));   
    
        
end ModifyQuestionInSharedDB;




operation RemoveQuestionFromSharedDB is
	inputs: sdb:SharedDB, q:Question;
	outputs: sdb':SharedDB;
    
    description:(*
        Delete the given question from the given shared database.
        The given question must already be in the input shared
        database.  The question to be deleted from the shared
        database must match the input question in all properties.
    *);

    precondition:
        (*
         * The given Question is in the given SharedDB
         *)
        q in sdb;

    postcondition:
        (*
         * The question is in the output database if and only if it is not
         * the existing record to be deleted and it is in the input db.
         *)
        (forall (q':Question)
            (q' in sdb') iff ((q' != q) and (q' in sdb)));

end RemoveQuestionFromSharedDB;

(* Start Nelson Bonilla's Section *)
operation SearchQuestionDatabase is
		inputs: qdb:QuestionDB, c:Class;
		outputs: ql:Question*;
		description: (*
			Find a question or questions by class.  If more than one is found, output list is sorted
			by keyword.
		*);
		precondition: (* None *);
		postcondition: 
		(*
		 * The output list consists of all questions of the given class in the input db.
		 *)
		(forall (q:Question)
			(q in ql) iff (q in qdb) and (q.class = c))
			
			and
			
		(* 
		 * The output list is sorted in alphabetical order
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].class < ql[i + 1].class);

	end SearchQuestionDatabase;

	operation SearchQuestionDatabase is
		inputs: qdb:QuestionDB, tp:Type;
		outputs: ql:Question*;
		description: (*
			Find a question or questions by type.  If more than one is found, output list is sorted
			by keyword.
		*);
		precondition: (* None*);
		postcondition: 
		(*
		 * The output list consists of all questions of the given type in the input db.
		 *)
		(forall (q:Question)
			(q in ql) iff (q in qdb) and (q.type = tp))
					
			and
			
		(* 
		 * The output list is sorted in alphabetical order by keyword.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].keywords < ql[i + 1].keywords);

	end SearchQuestionDatabase;

	operation SearchQuestionDatabase is
		inputs: qdb:QuestionDB, tm:Time;
		outputs: ql:Question*;
		description: (*
			Find a question or questions by time.  If more than one is found, output list is sorted
			by keyword.
		*);
		precondition: (* None *);
		postcondition: 
		(*
		 * The output list consists of all questions of the given time in the input db.
		 *)
		(forall (q:Question)
			(q in ql) iff (q in qdb) and (q.time = tm))
								
			and
			
		(* 
		 * The output list is sorted in alphabetical order by keyword.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].keywords < ql[i + 1].keywords);

	end SearchQuestionDatabase;

	operation SearchQuestionDatabase is
		inputs: qdb:QuestionDB, d:Difficulty;
		outputs: ql:Question*;
		description: (*
			Find a question or questions by difficulty.  If more than one is found, output list is sorted
			by keyword.
		*); 
		precondition: (* None *);
		postcondition:  
		(*
		 * The output list consists of all questions of the given difficulty in the input db.
		 *)
		(forall (q:Question)
			(q in ql) iff (q in qdb) and (q.difficulty = d))
											
			and
			
		(* 
		 * The output list is sorted in alphabetical order by keyword.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].keywords < ql[i + 1].keywords);

	end SearchQuestionDatabase;

	operation SearchQuestionDatabase is
		inputs: qdb:QuestionDB, l:LastUsed;
		outputs: ql:Question*;
		description: (*
			Find a question or questions by LastUse.  If more than one is found, output list is sorted
			by keyword.
		*);
		precondition: (* None *);
		postcondition: 
		(*
		 * The output list consists of all questions of the given Last Use in the input db.
		 *)
		(forall (q:Question)
			(q in ql) iff (q in qdb) and (q.lastused = l))
											
			and
			
		(* 
		 * The output list is sorted in alphabetical order by keyword.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].keywords < ql[i + 1].keywords);

	end SearchQuestionDatabase;

	operation SearchQuestionDatabase is
		inputs: qdb:QuestionDB, k:Keywords;
		outputs: ql:Question*;
		description: (*
			Find a question or questions by keyword.  If more than one is found, output list is sorted
			by keyword.
		*);
		precondition: (* None *);
		postcondition:  
		(*
		 * The output list consists of all questions of the given keyword in the input db.
		 *)
		(forall (q:Question)
			(q in ql) iff (q in qdb) and (q.keywords = k))
														
			and
			
		(* 
		 * The output list is sorted in alphabetical order by keyword.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].keywords < ql[i + 1].keywords);

	end SearchQuestionDatabase;

	operation SearchQuestionDatabase is
		inputs: qdb:QuestionDB, a:Author;
		outputs: ql:Question*;
		description: (*
			Find a question or questions by author.  If more than one is found, output list is sorted
			by keyword.
		*);
		precondition: (* None *);
		postcondition:  
		(*
		 * The output list consists of all questions of the given author in the input db.
		 *)
		(forall (q:Question)
			(q in ql) iff (q in qdb) and (q.author = a))
														
			and
			
		(* 
		 * The output list is sorted in alphabetical order by keyword.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].keywords < ql[i + 1].keywords);

	end SearchQuestionDatabase;




	operation SortQuestionDatabase is
		inputs: qdb:QuestionDB;
		outputs: ql:Question*;
		description: (*
			Sorts the column labeled class in alphabetical order.
		*);
		precondition: (* None *);
		postcondition:
		(* 
		 * The output list is sorted in alphabetical order by class.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].class < ql[i + 1].class);
	end SortQuestionDatabase;

	operation SortQuestionDatabase is
		inputs: qdb:QuestionDB;
		outputs: ql:Question*;
		description: (*
			Sorts the column labeled type in alphabetical order.
		*);
		precondition: (* None *);
		postcondition: 
		(* 
		 * The output list is sorted in alphabetical order by type.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].type < ql[i + 1].type);
	end SortQuestionDatabase;

	operation SortQuestionDatabase is
		inputs: qdb:QuestionDB;
		outputs: ql:Question*;
		description: (*
			Sort the column labeled time in descending numerical order.
		*);
		precondition: (* None *);
		postcondition: 
		(* 
		 * The output list is sorted in numerical order by time.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].time < ql[i + 1].time);
	end SortQuestionDatabase;

	operation SortQuestionDatabase is
		inputs: qdb:QuestionDB;
		outputs: ql:Question*;
		description: (*
			Sort the column labeled difficulty in descending numerical order.
		*); 
		precondition: (* None *);
		postcondition: 
		(* 
		 * The output list is sorted in numerical order by difficulty.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].difficulty < ql[i + 1].difficulty);

	end SortQuestionDatabase;

	operation SortQuestionDatabase is
		inputs: qdb:QuestionDB;
		outputs: ql:Question*;
		description: (*
			Sort the column labeled Last Use by date with the most recent at the top.
		*);
		precondition: (* None *);
		postcondition:
		(* 
		 * The output list is sorted in alphabetical order by last use.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].lastused < ql[i + 1].lastused);
	end SortQuestionDatabase;

	operation SortQuestionDatabase is
		inputs: qdb:QuestionDB;
		outputs: ql:Question*;
		description: (*
			Sort the column labeled keyword in alphabetical order.
		*);
		precondition: (* None *);
		postcondition:
		(* 
		 * The output list is sorted in alphabetical order by keyword.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].keywords < ql[i + 1].keywords);
	end SortQuestionDatabase;

	operation SortQuestionDatabase is
		inputs: qdb:QuestionDB;
		outputs: ql:Question*;
		description: (*
			Sort the column labeled author in alphabetical order.
		*);
		precondition: (* None *);
		postcondition:
		(* 
		 * The output list is sorted in alphabetical order by keyword.
		 *)
		(forall (i:integer | (i >= 1) and (i < #ql))
			ql[i].author < ql[i + 1].author);
	end SortQuestionDatabase;

(* End Nelson Bonilla's Section *)

end Question;

Prev: view.rsl | Next: test.rsl | Up: formal specification | Top: index