This page describes a major part of the STAT System XCend Schema, namely the exam definition by tasks, grade definition and student results. It is part of The Whole Stat System.
element exam * id { attribute title { string } attribute date { string } attribute time { string } attribute location { string } attribute free { boolean } attribute published { boolean } attribute exercise { ident [ exists /exercise[.] ]} element examiner * account {[ /account[./account]/examiner[..exam/id] ]} element task * id { attribute maxPoints { double [ . >= 0 ]} } element grade * id { attribute name { string } [count(./name, ../grade/name) = 1] attribute value { double } attribute minPoints { double } } [ {exists ./grade[x], exists ./grade[y], x != y} ./grade[x]/value != ./grade[y]/value ] [ {exists ./grade[x], exists ./grade[y], x != y} ./grade[x]/minPoints != ./grade[y]/minPoints ] [ {exists ./grade[x], exists ./grade[y], ./grade[x]/value < ./grade[y]/value} ./grade[x]/minPoints > ./grade[y]/minPoints ] element participant * id { [ count (./id, /account/student/id) = 1 ] element result * task { [ exists ..exam/task[./task] ] attribute points { double [ . >= 0 && . <= ..exam/task[../task]/maxPoints ]} } } }
changeAttributes(ident uid, ident id, string title, string date, string time, string location) { assume exists /account[uid]/admin || exists /account[uid]/examiner[id]; assume exists /exam[id]; # implicitly true in OO implementations update /exam[id]/title title; update /exam[id]/date date; update /exam[id]/time time; update /exam[id]/location location; }
grantExaminerRights(ident uid, ident username, ident examId) { assume exists /account[uid]/admin; assume exists /account[username]; # implicitly true in OO implementations assume exists /exam[examId]; assume not exists /account[username]/examiner[examId]); assume not exists /exam[examId]/examiner[username]); # implied by integrity and the assumption before insert /account[username] <examiner exam=[examId] />; insert /exam[examId] <examiner account=[username] />; }
revokeExaminerRights(ident uid, ident username, ident examId) { assume exists /account[uid]/admin; assume exists /account[username]/examiner[examId]; # implies the account exists, which is implicitly true anyway assume exists /exam[examId]/examiner[username]; # implied by integrity and the assumption before remove /account[username]/examiner[examId]; remove /exam[examId]/examiner[username]; }
Results depend on tasks, so deleting them is impossible, if they are still referenced. If this is really necessary, the host language has to use the deleteResult
procedures first.
createTask(ident uid, ident id, ident taskId, double maxPoints) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]; # implicitly true in OO implementations assume not exists /exam[id]/task[taskId]; assume maxPoints >= 0; insert /exam[id] <task id=[taskId] maxPoints=[maxPoints] />; }
deleteTask(ident uid, ident id, ident taskId) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]/task[taskId]; # implies the exam exists, which is implicitly true anyway assume size(/exam[id]/participant/result[taskId]) = 0; remove /exam[id]/task[taskId]; }
The grades only have to be consistent within themselves, nothing depends on them from the outside.
Changing the values of a grade is a method currently present in the implementation, which can be done by removing the grade and adding it. An additional procedure for this might make sense though.
createGrade(ident uid, ident id, ident gradeId, string name, double value, double minPoints) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]; # implicitly true in OO implementations assume not exists /exam[id]/grade[gradeId]; assume count(value, /exam[id]/grade/value) = 0; assume count(minPoints, /exam[id]/grade/minPoints) = 0; assume exists /exam[id]/grade[x] && /exam[id]/grade[x]/value < value -> /exam[id]/grade[x]/minPoints > minPoints; assume exists /exam[id]/grade[x] && value < /exam[id]/grade[x]/value -> minPoints > /exam[id]/grade[x]/minPoints; insert /exam[id] <grade id=[gradeId] name=[name] value=[value] minPoints=[minPoints] />; }
deleteGrade(ident uid, ident id, ident gradeId) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]/grade[gradeId]; # implies the exam exists, which is implicitly true anyway remove /exam[id]/grade[gradeId]; }
Currently, the participants are not hardwired to an account, I just changed that fact for the schema. Besides that, participants stand for themselves, so deleting one does not affect anything.
openRegistration(ident uid, ident id) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]; assume not /exam[id]/free; update /exam[id]/free true; }
closeRegistration(ident uid, ident id) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]; assume /exam[id]/free; update /exam[id]/free false; }
addParticipant(ident uid, ident id, ident studentId) { assume exists /account[uid]/student && /account[uid]/student/id = studentId && /exam[id]/free || exists /account[uid]/examiner[id]; assume exists /exam[id]; # implicitly true in OO implementations assume count (studentId, /account/student/id) = 1; assume not exists /exam[id]/participant[studentId]; insert /exam[id]/participant[studentId]; }
removeParticipant(ident uid, ident id, ident studentId) { assume exists /account[uid]/student && /account[uid]/student/id = studentId && /exam[id]/free || exists /account[uid]/examiner[id]; assume exists /exam[id]/participant[studentId]; # implies the exam exists, which is implicitly true anyway assume size(/exam[id]/participant[studentId]/result) = 0; # don't allow to remove if results are there remove /exam[id]/participant[studentId]; }
Results depend on tasks and have numeric constraints. The procedures can be (and currently are) associated with the Participant
class, rather than the Exam
class. Basically this means we now have two actually implicit parameters.
Again, changing the values can be done by adding and removing, which also invokes most of the checks. One could offer a change Method of course, which can actually make sense, as the precondition will be easier.
addResult(ident uid, ident id, ident studentId, ident taskId, double points) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]/participant[studentId]; # can also be implicitly true in an OO language assume exists /exam[id]/task[taskId]; assume not exists /exam[id]/participant[studentId]/result[taskId]; assume points >= 0 && points <= /exam[id]/task[taskId]/maxPoints; insert /exam[id]/participant[studentId] <result task=[taskId] points=[points] />; }
changeResult(ident uid, ident id, ident studentId, ident taskId, double points) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]/participant[studentId]; assume exists /exam[id]/task[taskId]; assume points >= 0 && points <= /exam[id]/task[taskId]/maxPoints; assume exists /exam[id]/participant[studentId]/result[taskId]; update /exam[id]/participant[studentId]/result[taskId]/points points; }
removeResult(ident uid, ident id, ident studentId, ident taskId) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]/participant[studentId]/result[taskId]; # implies the exam, account, student role and task exist by integrity remove /exam[id]/participant[studentId]/result[taskId]; }
publishResults(ident uid, ident id) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]; assume not /exam[id]/published; update /exam[id]/published true; }
hideResults(ident uid, ident id) { assume exists /account[uid]/examiner[id]; assume exists /exam[id]; assume /exam[id]/published; update /exam[id]/published false; }