module Student; from Admin import Accounts; from Login import LoginCookie; from Data import all; from Page import all; from tutorial import all; export all; (*****************************************************************************) object TutorialBrowser is components: browseTab:BrowseTab and searchTab:SearchTab and historyTab:HistoryTab; description: (* Highest-level model of the Tutorials window in the Student interface. Consists of three tabs used in tutorial location. *); end TutorialBrowser; (*****************************************************************************) object BrowseTab is components: departmentGroups:DepartmentGroup*; description: (* The main Tutorials tab, this contains all tutorials in the database, arranged by department. *); end BrowseTab; object DepartmentGroup is components: departmentName:DepartmentName and courseGroups:CourseGroup*; description: (* Further subdivides the tutorials in a department by course. *); end DepartmentGroup; object CourseGroup is components: courseNumber:CourseNumber and authorGroups:AuthorGroup*; description: (* The CourseGroup is used to group tutorials by author. It consists of a CourseNumber and a collection of AuthorGroups. *); end Course; object AuthorGroup is components: authorID:UserID and tutorialGroups:TutorialGroup* and tutorials:TutorialRecord*; description: (* The AuthorGroup contains all tutorials for a particular class written by the same author. It consists of an authorID and a collection of TutorialGroups and TutorialRecords. *); end AuthorGroup; object TutorialGroup is components: tutorialGroups:TutorialGroup* and tutorials:TutorialRecord*; description: (* A TutorialGroup is used to subdivide tutorials written by a particular author into additional categories. It is analogous to a file folder, consiting of a collection of TutorialGroups and TutorialRecords. *); end TutorialGroup; (*****************************************************************************) object SearchTab is components: searchName:SearchString and searchKeywords:SearchString and searchDepartmentName:SearchString and searchCourseNumber:SearchInteger and searchAuthorFirstName:SearchString and searchAuthorLastName:SearchString; description: (* This Tutorials tab allows the user to search for tutorials by specifying certain search parameters. *); end SearchTab; object SearchString is string description: (* A generic string used for a parameter in the tutorial search operation. *); end SearchString; object SearchInteger is integer description: (* A generic integer used for a parameter in the tutorial search operation. *); end SearchInteger; operation FindTutorial is inputs: cookie:LoginCookie and accounts:Accounts and tutorialDB:TutorialDB and pageDB:PageDB and searchName:SearchString and searchKeywords:SearchString and searchDepartmentName:SearchString and searchCourseNumber:SearchInteger and searchAuthorFirstName:SearchString and searchAuthorLastName:SearchString; outputs: matchingTutorials:TutorialRecord*; precondition: cookie.isConnected; postcondition: (* Skip a parameter if it is not supplied, otherwise assert that the parameter matches the corresponding aspect of the file *) forall (tutorialRecord in matchingTutorials) ( (searchName = nil or StringContains(tutorialRecord.tutorial.title, searchName)) and (searchKeywords = nil or TutorialContains(tutorialRecord, searchKeywords)) and (searchDepartmentName = nil or StringContains(tutorialRecord.class.departmentName, searchDepartmentName)) and (searchCourseNumber = nil or tutorialRecord.class.courseNumber = searchCourseNumber) and (searchAuthorFirstName = nil or GetFirstName(accounts, tutorialRecord.authorID) = searchAuthorFirstName) and (searchAuthorLastName = nil or GetLastName(accounts, tutorialRecord.authorID) = searchAuthorLastName) ); description: (* Given some identifying properties for tutorials, FindTutorial retrieves zero or more tutorials that match the specified parameters. Any parameter that is left blank will be ignored. *); end FindTutorial; (* Returns true if s2 is a substring of s1, false otherwise *) function StringContains(s1:string, s2:string)->boolean = (exists (x:integer | (x >= 1) and (x + #s2 <= #s1) ) s1[x : (x + #s2)] = s2); (* Returns true if any of the pages in the tutorial contains all of the space- separated keywords, false otherwise *) function TutorialContains(t:TutorialRecord, k:string)->boolean = ...; (* Returns the first name of the user in Accounts with the specified UserID, or nil if no such user exists *) function GetFirstName(a:Accounts, uid:UserID)->string = ...; (* Returns the last name of the user in Accounts with the specified UserID, or nil if no such user exists *) function GetLastName(a:Accounts, uid:UserID)->string = ...; (*****************************************************************************) object HistoryTab is components: favoriteTutorials:FavoriteTutorials and tutorialsInProgress:TutorialsInProgress and completedTutorials:CompletedTutorials; description: (* This Tutorials tab allows the user to browse their tutorial history, including completed, incomplete, and bookmarked tutorials. *); end HistoryTab; object FavoriteTutorials is components: tutorialIDs:TutorialID*; description: (* This subfolder of the History tab holds all tutorials that the user has bookmarked as 'Favorites'. *); end FavoriteTutorials; operation AddFavorite is inputs: cookie:LoginCookie and ft:FavoriteTutorials and newID:TutorialID; outputs: ft':FavoriteTutorials; precondition: cookie.isConnected and cookie.isAuthenticated and (not exists (tID in ft.tutorialIDs) tID = newID); postcondition: forall (tID':TutorialID) (tID' in ft') iff ((tID' = newID) or (tID' in ft)); description: (* Adds a tutorial to the Favorites folder if it is not already there. *); end AddFavorite; operation RemoveFavorite is inputs: cookie:LoginCookie and ft:FavoriteTutorials and pageID:PageID; outputs: ft':FavoriteTutorials; precondition: cookie.isConnected and cookie.isAuthenticated and pageID in ft; postcondition: forall (pID':PageID) (pID' in ft') iff ((pID' != pageID) or (pID' in ft)); description: (* Removes a tutorial from the Favorites folder. *); end RemoveFavorite; object TutorialsInProgress is components: pageIDs:PageID*; description: (* This subfolder of the History tab holds all tutorials that the user has begun but has not navigated to completion. *); end TutorialsInProgress; object CompletedTutorials is components: pageIDs:PageID*; description: (* This subfolder of the History tab holds all tutorials that the user has fully completed. *); end CompletedTutorials; operation CompleteTutorial is inputs: cookie:LoginCookie and tip:TutorialsInProgress and ct:CompletedTutorials and pID:PageID; outputs: tip':TutorialsInProgress and ct':CompletedTutorials; precondition: cookie.isConnected and cookie.isAuthenticated; postcondition: (forall (pID':PageID) (pID' in tip') iff ((pID' != pID) or (pID' in tip))) and (forall (pID':PageID) (pID' in ct' ) iff ((pID' = pID) or (pID' in ct ))); description: (* Moves a tutorial from the Tutorials In Progress list to the Completed Tutorials list. Precondition has been left open because it is not necessary for a tutorial to be on the Tutorials In Progress list when completed, even though this should always be the case. *); end CompleteTutorial; (*****************************************************************************) end Student;