(****
*
*The following section provides specifications for the 'Roster' tool.
*
*)

module Roster;

export RosterDB;

object RosterDB is
	components: records:StudentRecord*;
	operations: FindGroupMembers, InitialMarkup, AddStudent, RemoveStudent, CheckStudent;
	description: (*
		This database keeps track of all the students the instructor has in the grading tool.
	*);
end RosterDB;

object StudentRecord is 
	components: name:StudentName and section:StudentSection and id:StudentID 
		    and level:ControlLevel and group:StudentGroup and
		    ic:IndividualControl and login:UserName;
	description: (*
		This is the object that goes into the Eclass student database.
	*);
end StudentRecord;

object StudentGroup is 
	components: group:Group;
	description: (*
		The StudentGroup is set to a Group. The GroupSection and StudentSection will be used as a checker.
	*);
end StudentGroup;

object Group is 
	components: name:GroupName and section:GroupSection;
	description: (*
		The group corresponds to a particular section the instructor teaches.
	*);
end Group;

object SelectedGroup is Group;

object Marked is 
	components: marks:Mark*;
	description: (*
		Marked is used for a marking list.
	*);
end Marked;

object Mark is 
	components: id:StudentID and check:Check;
	description: (*
		Mark is a student ID and a boolean check mark to determine whether a
		student record is accounted for or not in FindStudent or RemoveStudent.
		A Mark is very similar to StudentRecord.
	*);
end Mark;

object SelectedControlLevel is (NoControl or LimitedControl or FullControl);

object GroupMembers is StudentRecord*;

object StudentName is string; (*Imported from Grader's database.*)

object StudentSection is integer; (*Imported from Grader's database.*)

object StudentID is integer; (*Imported from Grader's database.*)

object UserName is string;

object GroupName is string;

object GroupSection is integer;

object ControlLevel is integer;

object IndividualControl is boolean;

object NoControl is integer;

object LimitedControl is integer;

object FullControl is integer;

object Check is boolean;

operation InitialMarkUp is
	inputs: rdb:RosterDB and list:Marked;
	outputs: list':Marked;
	preconditions: ;
	postconditions: 
		(*
		 * The Marked list has student ID for all students in roster db and
		 * all marks are set to 'false', unless the RosterDB is empty, in 
		 * which case Marked list is created by all imported ID numbers from.
		 *)
		if (rdb != nil)
			forall (mark':Mark in list')
				(mark' in rdb) and (mark'.check = false)
		else
			(* Make list of all imported ID numbers from Grader.  Use this
			 * initial list and mark all of them false to add them all to
			 * the RosterDB during the AddStudent operation.
			 *); 
	description: (*
		Marks all those IDs in the roster db to false, in order to keep track of
		who has been accounted for in the update of the db from the Grader db. 
		This will be used to determine who can be removed from the roster db when 
		roster is updated.
	*);
end InitialMarkUp;

operation CheckStudent is
	inputs: rdb:RosterDB and list:Marked and record:StudentRecord;
	outputs: list':Marked;
	preconditions: 
		(* The record is in the roster db and there is a mark in the list with 
		 * the same ID as the record.
		 *)
		(record in rdb)

		and
		
		exists (mark':Mark in list)
			mark.id = record.id;
	postconditions: 
		(*
		 * For all the Mark records in the output list, the list checks are
		 * switched if they are found in the roster db.
		 *)
		 forall (mark':Mark in list')
			if (mark'.id in rdb)
				mark'.check = true
			else
				mark'.check = false;
	description: (*
		The operation goes through the roster db and marks all the student records 
		in the Marked list to true if they are found in the list.  This is used in
		conjunction with RemoveStudent and Add Student.  Once determined which students are in the
		roster db through this operation, RemoveStudent deletes the records with the
		IDs marked false in the Marked list.  This is all done because roster is 
		automatically updated to match with the Grader.
	*);
end CheckStudent;

operation AddStudent is
	inputs: rdb:RosterDB and record:StudentRecord;
	outputs: rdb':RosterDB;
	preconditions:
		(* There is no student secord in the input RosterDB with the same id as the
		 * record to be added.
		 *)
		not (exists (record':StudentRecord in rdb) record'.id = record.id); 
	postconditions:
		(*
		 * A student record is in the output db if and only if it is the new
		 * record to be added or it is the input db.  There are also no duplicate
		 * records in the output db to the input db.
		 *)
		(not (exists (record':StudentRecord in rdb') record'.id = record.id) )
		
		and

		forall (record':StudentRecord in rdb')
			(record' in rdb') iff ((record' = record) or (record' in rdb));
	description: (*
		Add the student into the database.
	*);
end AddStudent;

operation RemoveStudent is
	inputs: rdb:RosterDB and record:StudentRecord and list:Marked;
	outputs: rdb':RosterDB and list':Marked;
	preconditions: 
		(* The record is in the roster db and there is also a mark in the list
		 * that corresponds to the record by the student ID.
		 *)
		 (record in rdb)
		 
		 and
		 
		 exists (mark:Mark in list)
			mark.id = record.id;
	postconditions: 
		(*
		 * A student record is not in the output db if its corresponding ID
		 * is in the list of student IDs marked false in the list.  If the 
		 * ID of the record is false, then the record has been determined to
		 * already be in the list.
		 *)
		forall (mark':Mark in list)
		(
			if (mark'.check = false)
			(
				(* If mark is false, remove both mark and record from
				 * their respective lists.  The record that matches 
				 * with the mark is found and also deleted from the
				 * rdb.
				 *)
				list' = list - mark'
				rdb' = rdb - (*StudentRecord from FindRecord*)
			) else (
				(* If mark is true, mark and record remain in their
				 * respective lists.
				 *)
				(record in rdb') and (mark' in list')
			)
		);
	description: (*
		Removes the students that are in the Marked list from the roster db.
		This is performed after CheckStudent run to determine which students
		Should be deleted from the roster db that are already there.  All the 
		marks that are false will be deleted.
	*);
end RemoveStudent;

operation FindRecord is
	inputs: rdb:RosterDB and mark:Mark;
	outputs: record:StudentRecord;
	preconditions:
		exists (record':StudentRecord in rdb)
			record'.id = mark.id;
	postconditions: ;
	description: (*
		This operation takes a mark and searches the roster db for the
		record with the same student ID as the mark.  This mark is then
		used in the operation that calls it.
	*);
end FindRecord;

operation CreateGroup is
	inputs: name:GroupName and section:GroupSection;
	outputs: group:Group;
	preconditions: name != nil;
	postconditions: group.name != nil;
	description: (*
		This is the operation that creates the groups as long as the name is
		not blank.
	*);
end CreateGroup;

operation SetGroup is
	inputs: rdb:RosterDB and group:Group and record:StudentRecord;
	outputs: rdb':RosterDB;
	preconditions: group.section = record.group.section;
	postconditions: (record.group = group) and (record in (rdb and rdb'));
	descriptions: (* 
		This is where a student is assigned to a group.
	*);
end SetGroup;

operation SetControlLevel is
	inputs: rdb:RosterDB and level:SelectedControlLevel and record:StudentRecord;
	outputs: rdb':RosterDB;
	preconditions: (record in rdb);
	postconditions: (record.level = level) and (record in (rdb and rdb'));
	description: (*
		Sets ControlLevel of each selected person to the desired control level. 
		If a group is being altered, FindGroupMembers is called.
	*);
end SetControlLevel;

operation SetIndividualControl is
	inputs: rdb:RosterDB and record:StudentRecord;
	outputs: rdb':RosterDB;
	preconditions: (record.level = (LimitedControl or FullControl)) and (record in rdb);
	postconditions: 
		(*
		 * There exists a record in the output db that has the same ID as the
		 * input record, but its IC level is opposite from the input IC.
		 *)
		exists (record':StudentRecord in rdb')
			(record'.id = record.id) and (record'.ic = not (record.ic));
	description: (*
		When individual student selected only, IC simply switched. For 
		groups, FindGroupMembers is run and all the members of the group 
		selected are then switched to the opposite value (it is up to the 
		instructor to account for students already switched on when making 
		switches.
	*);
end SetIndividualControl;

operation FindGroupMembers is
	inputs: rdb:RosterDB and group:SelectedGroup;
	outputs: members:GroupMembers;
	preconditions: ;
	postconditions: 
		forall (record:StudentRecord in members)
			record.group = group;
	description: (*
		Searches the roster db to find all the members of the selected group.
	*);
end FindGroupMembers;

end Roster;