This page describes a major part of the STAT System XCend Schema, namely the exercise management, including groups, sheets and student results. It is a part of The Whole Stat System and contains Registered Students as well as Exercise Groups.
element exercise * id {
attribute lecture { string }
attribute term { string }
attribute open { boolean }
element assistant * account {[ exists /account[./account]/assistant[..exercise/id] ]}
element group * id { [ count(./id, ..exercise/student/group) <= ./maxSize ]
attribute day { "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday" }
attribute time { string }
attribute location { string }
attribute curSize { integer [ . = count(../id, ..exercise/student/group) ]}
attribute maxSize { integer [ . >= 0 ]}
element tutor * account {[ exists /account[./account]/tutor[..exercise/id]/group[..group/id] ]}
}
element sheet * id {
attribute maxPoints { double [ . >= 0 ]}
}
element student * id { [ count (./id, /account/student/id) = 1 ]
attribute group ? { ident [ exists ..exercise/group[.] ]}
attribute team ? { ident [ exists ../group ]}
element result * sheet { [ exists ..exercise/sheet[./sheet] ]
attribute points { double [ . >= 0 && . <= ..exercise/sheet[../sheet]/maxPoints ]}
}
}
}
changeAttributes(ident uid, ident id, string lecture, string term) {
assume exists /account[uid]/admin || exists /account[uid]/assistant[id];
assume exists /exercise[id]; # implicitly true in OO implementations
update /exercise[id]/lecture lecture;
update /exercise[id]/term term;
}
The procedures from User Accounts and Roles for the assistant role can be directly reused.
grantAssistantRights(ident uid, ident username, ident exerciseId) {
assume exists /account[uid]/admin;
assume exists /account[username]; # implicitly true in OO implementations
assume exists /exercise[exerciseId];
assume not exists /account[username]/assistant[exerciseId]);
assume not exists /exercise[exerciseId]/assistant[username]; # implied by integrity and the assumption before
insert /account[username] <assistant exercise=[exerciseId] />;
insert /exercise[exerciseId] <assistant account=[username] />;
}
revokeAssistantRights(ident uid, ident username, ident exerciseId) {
assume exists /account[uid]/admin;
assume exists /account[username]/assistant[exerciseId]; # implies the account exists, which is implicitly true anyway
assume exists /exercise[exerciseId]/assistant[username]; # implied by integrity and the assumption before
remove /account[username]/assistant[exerciseId];
remove /exercise[exerciseId]/assistant[username];
}
The tutor role from section User Accounts and Roles and the students depend on groups, which restricts deletion. The Exercise Groups are a large enough parts of the schema to have their own section and associated procedures.
createGroup(ident uid, ident id, ident groupId, string day, string time, string location, integer maxSize) {
assume exists /account[uid]/assistant[id];
assume exists /exercise[id];
assume not exists /exercise[id]/group[groupId];
assume maxSize >= 0;
assume day = "Monday" || day = "Tuesday" || ...;
insert /exercise[id] <group id=[groupId] day=[day] time=[time] location=[location] curSize=[0] maxSize=[maxSize] />;
}
deleteGroup(ident uid, ident id, ident groupId) {
assume exists /account[uid]/assistant[id];
assume exists /exercise[id]/group[groupId];
assume size(/exercise[id]/group[groupId]/tutor) = 0;
assume count(groupId, /exercise[id]/student/group)) = 0;
remove /exercise[id]/group[groupId];
}
The student results depend on sheets.
createSheet(ident uid, ident id, ident sheetId, double maxPoints) {
assume exists /account[uid]/assistant[id];
assume exists /exercise[id];
assume not exists /exercise[id]/sheet[sheetId];
assume maxPoints >= 0;
insert /exercise[id] <sheet id=[sheetId] maxPoints=[maxPoints] />;
}
deleteSheet(ident uid, ident id, ident sheetId) {
assume exists /account[uid]/assistant[id];
assume exists /exercise[id]/sheet[sheetId]; # implies existence of the exercise
assume size(/exercise[id]/student/result[sheetId]) = 0;
remove /exercise[id]/sheet[sheetId];
}
Students themselves depend on an account, but nothing depends on them; Their results stand for themselves. As with Exercise Groups, Registered Students are large enough to warrant their own section and associated procedures.
registerStudent(ident uid, ident id, ident studentId) {
assume count (studentId, /account/student/id) = 1;
assume exists /exercise[id] && /exercise[id]/open &&
exists /account[uid]/student && /account[uid]/student/id = studentId
|| exists /account[uid]/assistant[id];
assume exists /exercise[id];
assume not exists /exercise[id]/student[studentId];
insert /exercise[id] <student id=[studentId] />;
}
unregisterStudent(ident uid, ident id, ident studentId) {
assume exists /exercise[id] && /exercise[id]/open &&
exists /account[uid]/student && /account[uid]/student/id = studentId
|| exists /account[uid]/assistant[id];
assume exists /exercise[id]/student[studentId]; # implies the exercise exists
assume size(/exercise[id]/student[studentId]/result) = 0; # don't allow unregistering if results are there
assume not exists /exercise[id]/student[studentId]/group; # don't allow if signed in a group
remove /exercise[id]/student[studentId];
}
openSignUp(ident uid, ident id) {
assume exists /account[uid]/assistant[id];
assume exists /exercise[id];
assume not /exercise[id]/open;
update /exercise[id]/open true;
}
closeSignUp(ident uid, ident id) {
assume exists /account[uid]/assistant[id];
assume exists /exercise[id];
assume /exercise[id]/open;
update /exercise[id]/open false;
}