(* describes the student related portion of testtool *)
module Student;
    from Question import QuestionDB, Question, TestQuestion, AnsweredQuestion, GradedQuestion, QuestionNumber;
    from Test import Test, TakenTest, TestTime, Difficulty, TotalPoints, TestName;
    export Student, TestServer, GradesServer;

(* 'students present' table in administer tools *)
object StudentsPresentTable is
	components: studentlist:Student*;
	description: (*
		The students present table is a collection of Students. Each student takes 1 row of the table.
	*);
end StudentsPresentTable;

object Student is
	components: studentname:StudentName, studentid:StudentID, studentpassword:StudentPassword;
	description: (*
		A Student object represents a student. Consists of a Name, ID, and password unique to every student.
	*);
end Student;

object StudentName is string;
object StudentID is string;
object StudentPassword is string;

operation StudentBeginsTest is
	inputs: spt:StudentsPresentTable, s:Student;
	outputs: spt':StudentsPresentTable;
	precondition: (* student enters the testing environment *);
	postcondition: (* output table consists of every student present in input table + entering student, 
				sorted by student ID *)
			    (spt' = spt + s)
			     and
			    (forall (i:integer | (i >= 1) and ( i < #(spt'.studentlist)))
			       spt'.studentlist[i].studentid < spt'.studentlist[i+1].studentid);
end StudentBeginsTest;


(* administration: starting and extending test time management details *)
object TimeRemaining is integer;
object TimeExtension is integer;

operation BeginTest is
	inputs: test:Test;
	outputs: tr:TimeRemaining;
	precondition: ;
	postcondition: (tr = test.testtime);
end BeginTest;

operation ExtendTime is
	inputs: tr:TimeRemaining, te:TimeExtension;
	outputs: tr':TimeRemaining;
	precondition: ((tr > 0) and (te > 0));
	postcondition: (tr' = tr + te);
end ExtendTime;


(* Students log in to take test*)
operation StudentLogin is
	inputs: s:TestServer, studentid:StudentID, studentpassword:StudentPassword;
	outputs: test:Test;
	precondition: exists (stu in s.studentlist) (stu.studentid = studentid) and (stu.studentpassword = studentpassword);
	postcondition: (test = s.test);
	description: (*
		Login to the server to access test. If the students' username and password 
		exist in the server's user list, return test, otherwise nil.
	*);
end StudentLogin;

object TestServer is
	components: studentlist:Student*, test:Test;
	description: (*
		A test server consists of a list of authentic users and a test. Access to the server 
		must be granted to take a test.
	*);
end TestServer;



(* Students log in to check test results *)
operation StudentGradesLogin is
	inputs: s:GradesServer, studentid:StudentID, studentpassword:StudentPassword;
	outputs: tests:Test*;
	precondition: exists (stu in s.studentlist) (stu.studentid = studentid) and (stu.studentpassword = studentpassword);
	postcondition: (tests = s.tests);
	description: (*
		Login to the server to access grades. If the students' username and password exist 
		in the server's user list, returns the graded tests associated with that student.
		*);
end StudentGradesLogin;

object GradesServer is
	components: studentlist:Student*, tests:Test*;
	description: (*
		The grades server consists of a list of authentic users and the graded tests accosiated with those 
		users. Access to the server must be granted to view any graded material.
	*);
end GradesServer;


(* question navigation in seperate questions view mode *)
object CurrentQuestion is integer;

operation NavBeginningOfTest is
	inputs:;
	outputs: cq:CurrentQuestion;
	precondition:;
	postcondition: (cq = 1);
end NavBeginningOfTest;

operation NavPrev is
	inputs: cq:CurrentQuestion;
	outputs: cq':CurrentQuestion;
	precondition: (cq > 1);
	postcondition: (cq' = cq - 1);
end NavPrev;

operation NavNext is
	inputs: test:Test, cq:CurrentQuestion;
	outputs: cq':CurrentQuestion;
	precondition: (cq < #(test.testquestionlist));
	postcondition: (cq' = cq - 1);
end NavNext;

operation NavEndOfTest is
	inputs: test:Test;
	outputs: cq:CurrentQuestion;
	precondition:;
	postcondition: (cq = #(test.testquestionlist));
end NavEndOfTest;


(* student responce and test submission *)operation AnswerQuestion is
	inputs: tq:TestQuestion;
	outputs: aq:AnsweredQuestion;
	precondition: (* Student enters an answer *);
	postcondition: (* aq.response = student input *);
end AnswerQuestion;

operation SubmitTest is
	inputs: test:Test, answeredquestionlist:AnsweredQuestion*;
	outputs: takentest:TakenTest;
	precondition: (* for all AnsweredQuestion objects, question numbers must match test question numbers *)
			(forall (i:integer | (i >= 1) and ( i < #(answeredquestionlist)))
				answeredquestionlist[i].questionnumber = test.testquestionlist[i].questionnumber);

	postcondition: (* test names are the same, total points are the same, difficulty is the same, and
			   every question in the TakenTest has the same question number as in the Test *)
			(takentest.testname = test.testname)
			and (takentest.totalpoints = test.totalpoints)
			and (takentest.difficulty = test.difficulty)
			and
 			(forall (i:integer | (i >= 1) and ( i < #(test.testquestionlist)))
			    takentest.answeredquestionlist[i].questionnumber = answeredquestionlist[i].questionnumber );
end SubmitTest;
end Student;