Table of Contents

Roles

This page lists all the roles of the STAT System, which restrict access to the Procedures. The roles are manifested in the XCend Schema, under the section User Accounts and Roles.

Overview

Authentication

The basic idea is that the system restricts access to procedures based on accounts. Everyone can call guest procedures, which don't have the uid parameter.

With the read-only authenticate procedure, you can show the system your legitimation to call all the other methods with your username:

authenticate(ident username, string password) {
  assume exists /account[username];
 
  assume /account[username]/password = password;
}

The procedures then check for themselves, if your account has the necessary right in the form of matching roles.

Details

Admin Role

Administrators mostly have access to the procedures at the top level, i.e. The Whole Stat System, and those in the section User Accounts and Roles.

Access Rights

An admin in general could be allowed to see anything, but its unnecessary and redundant. What he has to be able to see though, are the User Accounts and Roles section in total and the top level elements with their attributes of the Exercise Management and Sheets and Exam Management and Grades sections.

Procedures

System Creation

To be able to list this procedure somewhere, it is listed here. However, it is not actually a procedure which you would call for an existing system, obviously.

createEmptyStats() {
  remove /stats; # procedures only operate on valid documents, so we have to get rid of "whatever was there" before
  insert / 
    <stats revision="0">
      <account username="admin" lastName="admin" firstName="admin" email="admin@stats.system" password="some hash of 'admin'">
        <admin />
      </account>
    </stats>;
}
Sub-System Creation
createExercise(ident uid, ident id, string lecture, string term) {
  assume exists /account[uid]/admin;
 
  assume not exists /exercise[id];
 
  insert / <exercise id=[id] lecture=[lecture] term=[term] open=[false] />
}
createExam(ident uid, ident id, ident eid, string title, string date, string time, string location) {
  assume exists /account[uid]/admin;
 
  assume not exists /exam[id];
  assume exists /exercise[eid];
 
  insert /
    <exam id=[id] title=[title] date=[date] time=[time] location=[location] free=[false] published=[false] exercise=[eid] />
}
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;
}
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;
}
deleteExercise(ident uid, ident id) {
  assume exists /account[uid]/admin;
 
  assume exists /exercise[id];
 
  assume size(/exercise[id]/assistant) = 0;
  assume size(/exercise[id]/group/tutor) = 0;
 
  assume size(/exercise[id]/student) = 0; # don't allow to delete if students are there
 
  remove /exercise[id];
}
deleteExam(ident uid, ident id) {
  assume exists /account[uid]/admin;
 
  assume exists /exam[id];
 
  assume size(/exam[id]/examiner) = 0;
 
  assume size(/exam[id]/participant) = 0; # don't allow to delete if participants are there
 
  remove /exam[id];
}
Account Management
# This doesn't take account validation into consideration at all. It probably has to be allowed to replace an unvalidated account.
createAccount(ident uid, ident username, string lastName, string firstName, string email, string password) {
  assume exists /account[uid]/admin;
 
  assume not exists /account[username];
 
  insert / <account username=[username] lastName=[lastName] firstName=[firstName] email=[email] password=[password] />
}
deleteAccount(ident uid, ident username) {
  assume exists /account[uid]/admin;
  assume uid != username; # or do we want to allow an admin to delete himself?
 
  assume exists /account[username];
 
  assume size(/account[username]/examiner) = 0;
  assume size(/account[username]/assistant) = 0;
  assume size(/account[username]/tutor) = 0;
 
  # not yet a constraint of the implementation, but is seems practically relevant :-)
  if exists /account[username]/admin then
    assume size(/account/admin) > 1;
  fi
 
  if exists /account[username]/student then
    assume size(/exercise/student[/account[username]/student/id]) = 0;
    assume size(/exam/participant[/account[username]/student/id]) = 0;
  fi
 
  remove /account[username];
}
Right Management
grantAdminRights(ident uid, ident username) {
  assume exists /account[uid]/admin;
 
  assume exists /account[username]; # implicitly true in OO implementations
  assume not exists /account[username]/admin;
 
  insert /account[username] <admin />;
}
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] />;
}
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] />;
}
revokeAdminRights(ident uid, ident username) {
  assume exists /account[uid]/admin;
 
  assume exists /account[username]/admin; # implies the account exists, which is implicitly true anyway
 
  assume size(/account/admin) > 1; # kind of a practical constraint
 
  remove /account[username]/admin;
}
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];
}
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];
}
Student Role

Normal accounts are created by admins, while students can create an account for themselves, which then has to have a student ID already. So there two procedures make only sense for admins, in the context of general right management. I put it into a separate section, however, to point out the oddity.

addStudentId(ident uid, ident username, ident id) {
  assume exists /account[uid]/admin;
 
  assume exists /account[username]; # implicitly true in OO implementations
  assume not exists /account[username]/student;
  assume count(id, /account/student/id) = 0;
 
  insert /account[username] <student id=[id] />;
}
removeStudentId(ident uid, ident username) {
  assume exists /account[uid]/admin;
 
  assume exists /account[username]/student; # implies the account exists, which is implicitly true anyway
 
  assume size(/exercise/student[/account[username]/student/id]) = 0;
  assume size(/exam/participant[/account[username]/student/id]) = 0;
 
  remove /account[username]/student;
}

Assistant Role

Assistants have access to many procedures in the Exercise Management and Sheets section.

Parameter: Exercise Identifier

Access Rights

The assistant right grants access to the complete exercise tree with the corresponding id, from the Exercise Management and Sheets section.

Procedures

Exercise Management
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;
}
Sheet and Group Management
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] />;
}
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] />;
}
changeAttributes(ident uid, ident id, ident sheetId, double maxPoints) {
  assume exists /account[uid]/assistant[id];
 
  assume exists /exercise[id]/sheet[sheetId]; # implies the existence of the exercise
 
  assume maxPoints >= 0;
  assume /exercise[id]/student[x]/result[sheetId]/points <= maxPoints;
 
  update /exercise[id]/sheet[sheetId]/maxPoints maxPoints;
}
changeAttributes(ident uid, ident id, ident groupId, string day, string time, string location, integer maxSize) {
  assume exists /account[uid]/assistant[id];
 
  assume exists /exercise[id]/group[groupId]; # implicitly true in OO implementations
 
  assume maxSize >= 0;
  assume /exercise[id]/group[groupId]/curSize <= maxSize;
 
  update /exercise[id]/group[groupId]/day day;
  update /exercise[id]/group[groupId]/time time;
  update /exercise[id]/group[groupId]/location location;
  update /exercise[id]/group[groupId]/maxSize maxSize;
}
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];
}
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];
}
Tutor Management
grantTutorRights(ident uid, ident username, ident exerciseId, ident groupId) {
  assume exists /account[uid]/assistant[exerciseId];
 
  assume exists /account[username]; # implicitly true in OO implementations
  assume exists /exercise[exerciseId]/group[groupId];
 
  assume not exists /account[username]/tutor[exerciseId]/group[groupId]);
  assume not exists /exercise[exerciseId]/group[groupId]/tutor[username];
    # implied by integrity and the assumption before
 
  if not exists /account[username]/tutor[exerciseId] then # we could split the tutor rights here
    insert /account[username] <tutor exercise=[exerciseId] />;
  fi
  insert /account[username]/tutor[exerciseId] <group id=[groupId] />;
  insert /exercise[exerciseId]/group[groupId] <tutor account=[account] />;
}
revokeTutorRights(ident uid, ident username, ident exerciseId, ident groupId) {
  assume exists /account[uid]/assistant[exerciseId];
 
  assume exists /account[username]/tutor[exerciseId]/group[groupId];
  assume exists /exercise[exerciseId]/group[groupId]/tutor[username];
    # implied by integrity and the assumption before
 
  remove /account[username]/tutor[exerciseId]/group[groupId];
  if size(/account[username]/tutor[exerciseId]/group) = 0 then # should this be an additional procedure?
    remove /account[username]/tutor[exerciseId];
  fi
  remove /exercise[exerciseId]/group[groupId]/tutor[username];
}
Sign-Up Management
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;
}
signUpGroup(ident uid, ident id, ident studentId, ident groupId) {
  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 existence of the exercise
  assume exists /exercise[id]/group[groupId]; # also implies it, but once is really enough
 
  assume not exists /exercise[id]/student[studentId]/group;
  assume /exercise[id]/group[groupId]/curSize < /exercise[id]/group[groupId]/maxSize;
 
  insert /exercise[id]/student[studentId]/group groupId;
  update /exercise[id]/group[groupId]/curSize (/exercise[id]/group[groupId]/curSize + 1);
}
signOutGroup(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]/group;
    # implies existence of the exercise and student, even the group
 
 
  update /exercise[id]/group[/exercise[id]/student[studentId]/group]/curSize
    (/exercise[id]/group[/exercise[id]/student[studentId]/group]/curSize - 1)
  remove /exercise[id]/student[studentId]/group;
}
Student Management
signUpGroup(ident uid, ident id, ident studentId, ident groupId) {
  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 existence of the exercise
  assume exists /exercise[id]/group[groupId]; # also implies it, but once is really enough
 
  assume not exists /exercise[id]/student[studentId]/group;
  assume /exercise[id]/group[groupId]/curSize < /exercise[id]/group[groupId]/maxSize;
 
  insert /exercise[id]/student[studentId]/group groupId;
  update /exercise[id]/group[groupId]/curSize (/exercise[id]/group[groupId]/curSize + 1);
}
signOutGroup(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]/group;
    # implies existence of the exercise and student, even the group
 
 
  update /exercise[id]/group[/exercise[id]/student[studentId]/group]/curSize
    (/exercise[id]/group[/exercise[id]/student[studentId]/group]/curSize - 1)
  remove /exercise[id]/student[studentId]/group;
}
addResult(ident uid, ident id, ident studentId, ident sheetId, double points) {
  assume exists /account[uid]/assistant[id] 
      || exists /account[uid]/tutor[id]/group[/exercise[id]/student[studentId]/group];
 
  assume exists /exercise[id]/student[studentId]; # implies the existence of the exercise
  assume exists /exercise[id]/sheet[sheetId]; # dito
 
  assume not exists /exercise[id]/student[studentId]/result[sheetId];
 
  assume points >= 0 && points <= /exercise[id]/sheet[sheetId]/maxPoints;
 
  insert /exercise[id]/student[studentId] <result sheet=[sheetId] points=[points] />;
}
changeResult(ident uid, ident id, ident studentId, ident sheetId, double points) {
  assume exists /account[uid]/assistant[id]
      || exists /account[uid]/tutor[id]/group[/exercise[id]/student[studentId]/group];
 
  assume exists /exercise[id]/student[studentId]; # implies the existence of the exercise
  assume exists /exercise[id]/sheet[sheetId]; # dito
 
  assume points >= 0 && points <= /exercise[id]/sheet[sheetId]/maxPoints;
 
  assume exists /exercise[id]/student[studentId]/result[sheetId];
 
  update /exercise[id]/student[studentId]/result[sheetId]/points points;
}
removeResult(ident uid, ident id, ident studentId, ident sheetId) {
  assume exists /account[uid]/assistant[id]
      || exists /account[uid]/tutor[id]/group[/exercise[id]/student[studentId]/group];
 
  assume exists /exercise[id]/student[studentId]/result[sheetId]; # implies the existence of pretty much everything
 
  remove /exercise[id]/student[studentId]/result[sheetId];
}

Examiner Role

Examiners have access to most procedures from the Exam Management and Grades section.

Parameter: Exam Identifier

Access Rights

The examiner right grants access to the complete exam tree with the corresponding id, from the Exam Management and Grades section. Nothing else really has to be bound to this role.

Procedures

Exam Management
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;
}
Task and Grade Management
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] />;
}
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] />;
}
changeAttributes(ident uid, ident id, ident taskId, double maxPoints) {
  assume exists /account[uid]/examiner[id];
 
  assume exists /exam[id]/task[taskId]; # implies the existence of the exam
 
  assume maxPoints >= 0;
  assume /exam[id]/participant[x]/result[taskId]/points <= maxPoints;
 
  update /exam[id]/task[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];
}
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];
}
Registration and Participant Management
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];
}
Result Management
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;
}

Guest Role

A guest is basically everyone who does not have an account, i.e. no other associated role. Guests probably can't do much, but they can create an account.

Access Rights

Guests really don't need to see anything, just that the system “is there” and a screen to login and register. Registration is free to students and no one else needs to see anything.

The registration is completed by validating the email address and the user can now authenticate, to get access to all the methods of his Roles.

Procedures

Account Creation
createStudentAccount(ident username, ident studentId, string lastName, string firstName, string email, string password, string code) {
  assume not exists /account[username];
  assume count(studentId, /account/student/id) = 0;
 
  insert /
    <account username=[username] lastName=[lastName] firstName=[firstName] email=[email] password=[password] code=[code]>
      <student id=[studentId] />
    </account>
}
validateAccount(ident username, string code) {
  assume exists /account[username]/code; # implies existence of the account
 
  assume /account[username]/code = code;
 
  remove /account[username]/code;
}
authenticate(ident username, string password) {
  assume exists /account[username];
 
  assume /account[username]/password = password;
}

Student Role

Students can not change much in the system, they can essentially just sign up or out for exercise, i.e. use procedures from Exercise Management and Sheets.

Parameter: Student ID

Access Rights

A student should see all the exercises from the Exercise Management and Sheets section, so he can sign up. He should also see all the exams in the Exam Management and Grades section, so he knows their details, but he doesn't sign up for them in our system.

Once he has a corresponding student or participant element in either, he can see his results and all the generic information, like sheets of exercises. In the Exercise Management and Sheets section, he can then also see all the groups. Registering in a group does not change visibility, however.

Procedures

Register for Exercise
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];
}
Group Sign-Up and Sign-Out
signUpGroup(ident uid, ident id, ident studentId, ident groupId) {
  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 existence of the exercise
  assume exists /exercise[id]/group[groupId]; # also implies it, but once is really enough
 
  assume not exists /exercise[id]/student[studentId]/group;
  assume /exercise[id]/group[groupId]/curSize < /exercise[id]/group[groupId]/maxSize;
 
  insert /exercise[id]/student[studentId]/group groupId;
  update /exercise[id]/group[groupId]/curSize (/exercise[id]/group[groupId]/curSize + 1);
}
signOutGroup(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]/group;
    # implies existence of the exercise and student, even the group
 
 
  update /exercise[id]/group[/exercise[id]/student[studentId]/group]/curSize
    (/exercise[id]/group[/exercise[id]/student[studentId]/group]/curSize - 1)
  remove /exercise[id]/student[studentId]/group;
}

Tutor Role

Tutors mainly add results for sheets, i.e. contribute to the Exercise Management and Sheets section.

Parameter: Exercise Identifier and Group Identifier

Access Rights

With the plain tutor right, a tutor can see all the groups in the corresponding exercise of the Exercise Management and Sheets section, as well as the sheets and other generic information. For each group right he can also see all the students which are signed up and their results.

Procedures

Team Assignment
assignTeam(ident uid, ident id, ident studentId, ident teamId) {
  assume exists /account[uid]/tutor[id]/group[/exercise[id]/student[studentId]/group];
    # implies the existence of the student, the exercise, the group and enough rights by a tutor account
 
  assume not exists /exercise[id]/student[studentId]/team;
 
  insert /exercise[id]/student[studentId]/team teamId;
}
leaveTeam(ident uid, ident id, ident studentId) {
  assume exists /account[uid]/tutor[id]/group[/exercise[id]/student[studentId]/group];
    # implies the existence of the student, the exercise, the group and enough rights by a tutor account
 
  assume exists /exercise[id]/student[studentId]/team;
 
  remove /exercise[id]/student[studentId]/team;
}
Result Management
addResult(ident uid, ident id, ident studentId, ident sheetId, double points) {
  assume exists /account[uid]/assistant[id] 
      || exists /account[uid]/tutor[id]/group[/exercise[id]/student[studentId]/group];
 
  assume exists /exercise[id]/student[studentId]; # implies the existence of the exercise
  assume exists /exercise[id]/sheet[sheetId]; # dito
 
  assume not exists /exercise[id]/student[studentId]/result[sheetId];
 
  assume points >= 0 && points <= /exercise[id]/sheet[sheetId]/maxPoints;
 
  insert /exercise[id]/student[studentId] <result sheet=[sheetId] points=[points] />;
}
changeResult(ident uid, ident id, ident studentId, ident sheetId, double points) {
  assume exists /account[uid]/assistant[id]
      || exists /account[uid]/tutor[id]/group[/exercise[id]/student[studentId]/group];
 
  assume exists /exercise[id]/student[studentId]; # implies the existence of the exercise
  assume exists /exercise[id]/sheet[sheetId]; # dito
 
  assume points >= 0 && points <= /exercise[id]/sheet[sheetId]/maxPoints;
 
  assume exists /exercise[id]/student[studentId]/result[sheetId];
 
  update /exercise[id]/student[studentId]/result[sheetId]/points points;
}
removeResult(ident uid, ident id, ident studentId, ident sheetId) {
  assume exists /account[uid]/assistant[id]
      || exists /account[uid]/tutor[id]/group[/exercise[id]/student[studentId]/group];
 
  assume exists /exercise[id]/student[studentId]/result[sheetId]; # implies the existence of pretty much everything
 
  remove /exercise[id]/student[studentId]/result[sheetId];
}

User Role

Although not a manifested role by itself, anyone having an account can most likely invoke a few procedures from User Accounts and Roles.

Parameter: Username

Access right

A user should be able to see his own account details and what rights he has, from the User Accounts and Roles section. It might also be a good idea to show all the exercises from Exercise Management and Sheets and exams from Exam Management and Grades to every user, so visibility of these basic top elements and theirs attributes is not bound to any specific role and these roles only grant more rights for individual elements.

Procedures

Account Management
changePassword(ident uid, ident username, string password) {
  assume exists /account[uid]/admin || uid = username;
 
  assume exists /account[username]; # implicitly true in OO implementations
 
  update /account[username]/password password;
}
requestReset(ident username, string reset) {
  assume exists /account[username];
 
  assume not exists /account[username]/code; # account is already validated
 
  if not exists /account[username]/reset then
    insert /account[username]/reset;
  fi
  update /account[username]/reset reset;
}
resetPassword(ident username, string reset, string password) {
  assume exists /account[username]/reset; # reset code was requested before, implies account exists
  assume /account[username]/reset = reset; 
 
  remove /account[username]/reset;
  update /account[username]/password password;
}