5.3. QuestionBank

(*
 * Module QuestionBank defines the objects and operations related to maintaining
 * a Question Bank and Question Bank management.
 *)
module QuestionBankModule;

from QuestionModule import Question, ID, Type, QuestionText, Difficulty, Time, CorrectAnswer, ClassName, Topic, Author, AverageScore, Created, Modified, LastUsed, Notes, Answer;
export    QuestionBank, LocalQuestionBank, SharedQuestionBank,
        QuestionBankSettings, LocalQuestionBankSettings, SharedQuestionBankSettings,
        LocalBankProfile, BankName, BankLocation,
        SharedBankProfile, BankUserList, BankAdministrator, BankUR, Username, Password;

object QuestionBank is
    components: questions:Question*;
    operations: AddQuestion, EditQuestion, DeleteQuestions, FilterQuestions;
    description: (*
    *);
end QuestionBank;

object LocalQuestionBank inherits from QuestionBank
    operations: UploadQuestion;
    description: (*
    *);
end LocalQuestionBank;

object SharedQuestionBank inherits from QuestionBank
    components: BankUserList;
    operations: DownloadQuestion;
    description: (*
    *);
end SharedQuestionBank;

(*---------------------- begin Kate's section -----------------------*)

operation DeleteQuestions is
    inputs: id: ID*, a: Author*, qb: QuestionBank;
    outputs: qb' : QuestionBank;
    description: (* This operation delets questions from the QuestionBank.  It return a QuestionBank without the deleted questions in it *);
    precondition:
        (forall (idx in id)
            (forall (authx in a)
            (forall (q: Question)
            exists (q in qb )((q.id = idx) and (q.author = authx) ) )));
    postcondition:
        (*The unwanted quesitons are deleted from the QuestionBank*)
        (forall (q': Question)
        (forall (authx in a)
        (forall (idx in id)
            (q' in qb') iff ((idx != q'.id) and (authx != q'.author) and (q' in qb)))));
end DeleteQuestions;

operation UploadQuestions is
    inputs: id: ID*, a: Author*, lqb: LocalQuestionBank, sqb: SharedQuestionBank;
    outputs: sqb' : SharedQuestionBank;       
    description: (*This operation uploads questions from the local QuestionBank to the shared QuestionBank; after
            the operation, the shared question bank will now contain the new questions*);
    precondition:
        (forall (q: Question)
        (forall (authx in a)
        (forall (idx in id)
        exists (q in lqb) ((q.id = idx) and (q.author = authx)))));   
    postcondition:
        (*The desired questions are uploaded from the shared QuestionBank to the local QuestionBank*)
        (forall (q' : Question)
        (forall (authx in a)
        (forall (idx in id)
            (q' in sqb'.questions) iff ((q' in sqb.questions) or ((idx =q'.id) and (authx = q'.author) and (q' in lqb.questions))))));
end UploadQuestions;

operation DownloadQuestions is
    inputs: id: ID*, a: Author*, lqb: LocalQuestionBank, sqb: SharedQuestionBank;
    outputs: lqb': LocalQuestionBank;
    description: (*This operation downloads questions from the shared QuestionBank to the local QuestionBank, after
            this operation is performed the local question bank will contain the new questions*);
    precondition:
        (forall (q: Question)
        (forall (authx in a)
        (forall (idx in id)
            exists (q in sqb.questions) ((q.id = idx) and (q.author = authx)))));
    postcondition:
        (*The desired questions are downloaded from the local QuestionBank to the shared QuestionBank*)
        (forall (q' : Question)
        (forall (authx in a)
        (forall (idx in id)
            (q' in lqb'.questions) iff ((q' in lqb.questions) or ((idx =q'.id) and (authx = q'.author) and (q' in sqb.questions))))));
end DownloadQuestions;

operation FilterQuestions is
    inputs: qb: QuestionBank, f: Filter;
    outputs: ql : QuestionList;
    description: (* Displays questions with certain selected attributes, while hiding others*);
    precondition:
        qb != nil;
    postcondition:
        (*Undesired questions are hidden, while the desired questions are displayed*)
        (forall(q: Question)
        exists (q in qb.questions)
            (q.cn in f.class) or
            (q.author in f.author) or
            (q.type in f.type) or
            ((*f.keyword in q.notes*) (f.keyword in q.topics)));
end FilterQuestions;

operation ShowDetails is
    inputs: q: Question;
    outputs: dp: DetailPanel;
    description: (*Clicking on the question row fills in the ShowDetails *);
    precondition:
        (forall(qb: QuestionBank)
            (q in qb));
    postcondition:
        (*The DetailPanel is filled in with the attributes of the selected question*)
        (dp.answer = q.ca) and
        (dp.class = q.cn) and
        (dp.topics = q.topics) and
        (dp.author = q.author) and
        (dp.as = q.as) and
        (dp.created = q.created) and
        (dp.modified = q.modified) and
        (dp.lu = q.lu) and
        (dp.notes = q.notes);
end ShowDetails;

operation SortedByTypeAscending is
    inputs: ql:QuestionList;
    outputs: ql':QuestionList;
    description: (* Sorts the list of questions by their type field *);
    precondition:
        #ql > 0;
    postcondition:
        (forall (q:Question) ((q in ql) iff (q in ql'))) and
        (forall (i:integer | i >= 1 and i < #ql)
            (ql'[i].type < ql'[i + 1].type));
end SortedByTypeAscending;

operation SortedByDifficultyAscending is
    inputs: ql:QuestionList;
    outputs: ql':QuestionList;
    description: (* Sorts the list of questions by their difficulty field *);
    precondition:
        #ql > 0;
    postcondition:
        (forall (q:Question) ((q in ql) iff (q in ql'))) and
        (forall (i:integer | i >= 1 and i < #ql)
            (ql'[i].difficulty < ql'[i + 1].difficulty));
end SortedByDifficultyAscending;

operation SortedByTimeAscending is
    inputs: ql:QuestionList;
    outputs: ql':QuestionList;
    description: (* Sorts the list of questions by their time field *);
    precondition:
        #ql > 0;
    postcondition:
        (forall (q:Question) ((q in ql) iff (q in ql'))) and
        (forall (i:integer | i >= 1 and i < #ql)
            (ql'[i].time < ql'[i + 1].time));
end SortedByTimeAscending;

operation SortedByQuestionAscending is
    inputs: ql:QuestionList;
    outputs: ql':QuestionList;
    description: (* Sorts the list of questions by their question text field *);
    precondition:
        #ql > 0;
    postcondition:
        (forall (q:Question) ((q in ql) iff (q in ql'))) and
        (forall (i:integer | i >= 1 and i < #ql)
            (ql'[i].text < ql'[i + 1].text));
end SortedByQuestionAscending;

object DetailPanel is answer:Answer, class:ClassName, topics:Topic*, author:Author, as:AverageScore, created:Created, modified:Modified, lu:LastUsed, notes:Notes
    description: (*The Detail Panel lists more detailed question attributes*);
end;

object Filter is type:TypeFilter, class:ClassFilter, author:AuthorFilter, keyword:KeywordFilter
    description: (* Each filter type allows the user to display or hide questions
            posessing that property*);
end;

object TypeFilter is Type*
    description: (* TypeFilter allows the user to hide or display questions based on
            what answer type is assosiated with the question*);
end;

object ClassFilter is ClassName*
    description: (* ClassFilter allows the user to hide or display questions based on
            what class name is assosiated with the question*);
end;

object AuthorFilter is Author*
    description: (* AuthorFilter allows the user to hide or display questions based on
            the author of the question*);
end;

object KeywordFilter is string
    description: (* KeywordFilter allows the user to hide or display questions based on
            what words are contained in the question text filed, the notes field,
            and the answer field*);
end;

object QuestionList is Question*
    description: (* This is the list of questions that is displayed in the table of the question bank view.
                    This way, you can filter and sort the questions without affecting the underlying Question Bank,
                    since those only affect how they are shown to the user. *);
end;
   
(*--------------------------- end Kate's section -----------------------*)


(*--------------------------- begin Adam's section ---------------------*)

object QuestionBankSettings is Lqbs:LocalQuestionBankSettings, Sqbs:SharedQuestionBankSettings;

object LocalQuestionBankSettings is
    components:  Lbp:LocalBankProfile*;
    operations:  AddLocalQB, EditLocalQB, DeleteSharedQB, MoveUp, MoveDown;
end LocalQuestionBankSettings;
object LocalBankProfile is bn:BankName, bl:BankLocation, lqb:LocalQuestionBank;

object SharedQuestionBankSettings is
    components:  sbp:SharedBankProfile*;
    operations:  AddSharedQB, EditSharedQB, DeleteSharedQB, MoveUp, MoveDown;
end SharedQuestionBankSettings;
object SharedBankProfile is bn:BankName, bl:BankLocation,  bul:BankUserList, un:Username, pw:Password, sqb:SharedQuestionBank;
object BankUserList is
    components: bur:BankUR*, ba:BankAdministrator;
    operations: AddUser, DeleteUser, SwitchAdmin;
end BankUserList;

object BankUR is un:Username, pw:Password;
object Username is string;
object ConfirmPassword is string;
object Password is string;
object BankName is string;
object BankLocation is string;
object BankAdministrator is string;

operation CreateLocal is
    inputs: Lqbs:LocalQuestionBankSettings, BankName, BankLocation;
    outputs: Lqbs':LocalQuestionBankSettings;
end CreateLocal;

operation AddSharedQB is
    inputs: Sqbs: SharedQuestionBankSettings, sbp:SharedBankProfile;
    outputs: Sqbs':SharedQuestionBankSettings;
    precondition:
        (* BankName, BankLocation, Username, and Password are non-empty  *)
        #(sbp.bn) >= 1 and
        #(sbp.bl) >= 1 and
        #(sbp.un) >= 1 and
        #(sbp.pw) >= 1;
    postcondition:
        (* Question Bank is added to the Shared Question Bank list *)
        sbp in Sqbs';
end AddSharedQB;

operation AddLocalQB is
    inputs: Lqbs:LocalQuestionBankSettings, lbp:LocalBankProfile;
    outputs: Lqbs':LocalQuestionBankSettings;
    precondition:
        (* BankName, BankLocation are non-empty *)
        #(lbp.bn) >= 1 and
        #(lbp.bl) >= 1;
    postcondition:
        (* Question Bank is added to the Local Question Bank list *)
        lbp in Lqbs';
end AddLOcalQB;

operation EditSharedQB is
    inputs: Sqbs:SharedQuestionBankSettings, sbp:SharedBankProfile;
    outputs: Sqbs':SharedQuestionBankSettings;
    precondition:
        (* BankName, BankLocation, Username, and Password are non-empty *)
        #(sbp.bn) >= 1 and
        #(sbp.bl) >= 1 and
        #(sbp.un) >= 1 and
        #(sbp.pw) >= 1;
    postcondition:
        (* Question Bank is updated in the Shared Question Bank list *)
        sbp in Sqbs';
end EditSharedQB;

operation EditLocalQB is
    inputs: Lqbs:LocalQuestionBankSettings, lbp:LocalBankProfile;
    outputs: Lqbs':LocalQuestionBankSettings;
    precondition:
        (* BankName, BankLocation are non-empty *)
        #(lbp.bn) >= 1 and
        #(lbp.bl) >= 1;
    postcondition:
        (* Question Bank is updated in the Shared Question Bank list *)
        lbp in Lqbs';
end EditLocalQB;

operation DeleteLocalQB is
    inputs: Lqbs:LocalQuestionBankSettings, Lbp:LocalBankProfile;
    outputs: Lqbs':LocalQuestionBankSettings;
    precondition:
        (* Question Bank must exist in Question Bank Settings *)
        Lbp != nil and
        Lbp in Lqbs;
    postcondition:
        (* Question Bank is deleted from Local Question Bank list *)
        not(Lbp in Lqbs');
end DeleteLocalQB;

operation DeleteSharedQB is
    inputs: Sqbs:SharedQuestionBankSettings, Sbp:SharedBankProfile;
    outputs:  Sqbs':SharedQuestionBankSettings;
    precondition:
        (* Question Bank must exist in Question Bank Settings *)
        Sbp != nil and
        Sbp in Sqbs;
    postcondition:
        (* Question Bank is deleted from Shared Question Bank list *)
        not(Sbp in Sqbs');
end DeleteSharedQB;
 
operation AddUser is
    inputs:  bul:BankUserList, un:Username, pw:Password, cpw:ConfirmPassword;
    outputs: bul':BankUserList;
    precondition:
        (* Username cannot exist in bank users list and password and confirm password must match *)
        (not (exists (bur:BankUR) (bur in bul.bur) and (bur.un = un))) and
        (#pw >= 1 and #cpw >=1 and pw = cpw);
    postcondition:
        (* BankUR has been added to bank user list *)
        forall(bur:BankUR)
        (bur in bul'.bur) iff (bur in bul.bur) or ((bur.un) = un and (bur.pw) = pw);
end AddUser;

operation DeleteUser is
    inputs: bul:BankUserList, un:Username;
    outputs: bul':BankUserList;
    precondition:
        (* User must exist in Bank Users List *)
        (un != nil) and (exists(r in bul.bur) (r.un = un));
    postcondition:
        (* User record with matching user name no longer exists in the bank user list *)
        forall(bur:BankUR)
        (bur in bul'.bur) iff ((bur in bul.bur) and (bur.un != un));
end DeleteUser;

operation SwitchAdmin is
    inputs: Bul:BankUserList, bur:BankUR;
    outputs: Bul':BankUserList;
    precondition:
        (* User record needs to be in user list *)
        (* (exists(r in Bul)) r.un = bur.un *);
    postcondition:
        (* User is now the administrator *)
        forall(ur:UserRecord)
            (ur in Bul'.bur) iff (ur in Bul.bur)
        and
        (Bul'.ba = bur.un);
end SwitchAdmin;

operation FileDirectory is
    inputs: qb:QuestionBank, ln:BankLocation;
    outputs: ln':BankLocation;
    precondition:
        (* Question Bank must exist in folder *);
    postcondition:
        (* Location is the path of the Question Bank *);
end FileDirectory;

operation MoveUp is
    inputs: sbp:SharedBankProfile, Sqbs:SharedQuestionBankSettings;
    outputs: Sqbs':SharedQuestionBankSettings;
    precondition:
        (* selected profile exists *)
        (exists (sbp' in Sqbs) (sbp.bn = sbp'.bn)) and
        Sqbs[1] != sbp;
    postcondition:
        (* Question Bank moves up on Question Bank list *)
        forall (profile:SharedBankProfile) ((profile in Sqbs) iff (profile in Sqbs'));
end Moveup;

(*----------------------------- end Adam's section --------------------------*)

end QuestionBankModule;


Prev:
5.2. Question

Up:
5. Formal Specifications
Next:
5.4. StudentInterface