(* 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;