package fi.otavanopisto.muikku.plugins.workspace.rest;
import java.util.List;
import java.util.logging.Logger;
import javax.ejb.Stateful;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.lang3.StringUtils;
import fi.otavanopisto.muikku.controller.PluginSettingsController;
import fi.otavanopisto.muikku.model.users.UserSchoolDataIdentifier;
import fi.otavanopisto.muikku.model.workspace.WorkspaceEntity;
import fi.otavanopisto.muikku.model.workspace.WorkspaceRoleArchetype;
import fi.otavanopisto.muikku.model.workspace.WorkspaceRoleEntity;
import fi.otavanopisto.muikku.model.workspace.WorkspaceUserEntity;
import fi.otavanopisto.muikku.plugin.PluginRESTService;
import fi.otavanopisto.muikku.schooldata.RoleController;
import fi.otavanopisto.muikku.schooldata.SchoolDataIdentifier;
import fi.otavanopisto.muikku.schooldata.WorkspaceController;
import fi.otavanopisto.muikku.schooldata.WorkspaceEntityController;
import fi.otavanopisto.muikku.schooldata.entity.WorkspaceUser;
import fi.otavanopisto.muikku.session.SessionController;
import fi.otavanopisto.muikku.users.UserSchoolDataIdentifierController;
import fi.otavanopisto.muikku.users.WorkspaceUserEntityController;
import fi.otavanopisto.security.rest.RESTPermit;
import fi.otavanopisto.security.rest.RESTPermit.Handling;
@RequestScoped
@Stateful
@Produces("application/json")
@Path ("/system/workspace")
public class WorkspaceSystemRESTService extends PluginRESTService {
private static final long serialVersionUID = -9168862600590661002L;
@Inject
private Logger logger;
@Inject
private SessionController sessionController;
@Inject
private RoleController roleController;
@Inject
private WorkspaceController workspaceController;
@Inject
private WorkspaceEntityController workspaceEntityController;
@Inject
private WorkspaceUserEntityController workspaceUserEntityController;
@Inject
private UserSchoolDataIdentifierController userSchoolDataIdentifierController;
@Inject
private PluginSettingsController pluginSettingsController;
@GET
@Path("/syncworkspaceusers/{ID}")
@RESTPermit(handling = Handling.INLINE)
public Response synchronizeWorkspaceUsers(@PathParam("ID") Long workspaceEntityId, @Context Request request) {
// Admins only
if (!sessionController.isSuperuser()) {
return Response.status(Status.UNAUTHORIZED).build();
}
logger.info(String.format("Synchronizing users of workspace entity %d", workspaceEntityId));
// Workspace
WorkspaceEntity workspaceEntity = workspaceEntityController.findWorkspaceEntityById(workspaceEntityId);
if (workspaceEntity == null) {
return Response.status(Status.NOT_FOUND).build();
}
// Student role
WorkspaceRoleEntity workspaceStudentRole = roleController.findWorkspaceRoleEntityById(getWorkspaceStudentRoleId());
// Workspace students in Muikku
List<WorkspaceUserEntity> muikkuWorkspaceStudents = workspaceUserEntityController.listWorkspaceUserEntitiesByRole(workspaceEntity, workspaceStudentRole);
logger.info(String.format("Before synchronizing, Muikku course has %d active students", muikkuWorkspaceStudents.size()));
// Course students in Pyramus
List<WorkspaceUser> pyramusCourseStudents = workspaceController.listWorkspaceStudents(workspaceEntity);
logger.info(String.format("Before synchronizing, Pyramus course has %d active students", pyramusCourseStudents.size()));
// Loop through Pyramus students
for (WorkspaceUser pyramusCourseStudent : pyramusCourseStudents) {
String pyramusStudentId = pyramusCourseStudent.getUserIdentifier().getIdentifier();
String pyramusCourseStudentId = pyramusCourseStudent.getIdentifier().getIdentifier();
// Find Muikku student corresponding to Pyramus student
String muikkuWorkspaceStudentId = null;
WorkspaceUserEntity muikkuWorkspaceStudent = null;
for (int i = 0; i < muikkuWorkspaceStudents.size(); i++) {
muikkuWorkspaceStudentId = muikkuWorkspaceStudents.get(i).getIdentifier();
if (StringUtils.equals(muikkuWorkspaceStudentId, pyramusCourseStudentId)) {
muikkuWorkspaceStudent = muikkuWorkspaceStudents.get(i);
muikkuWorkspaceStudents.remove(i);
break;
}
}
if (muikkuWorkspaceStudent == null) {
// Restore an archived workspace student, if possible
muikkuWorkspaceStudent = workspaceUserEntityController.findWorkspaceUserEntityByWorkspaceUserIdentifierAndArchived(pyramusCourseStudent.getIdentifier(), Boolean.TRUE);
if (muikkuWorkspaceStudent != null) {
workspaceUserEntityController.unarchiveWorkspaceUserEntity(muikkuWorkspaceStudent);
logger.info(String.format("Unarchived workspace student %s", pyramusCourseStudentId));
SchoolDataIdentifier muikkuStudentIdentifier = new SchoolDataIdentifier(
muikkuWorkspaceStudent.getUserSchoolDataIdentifier().getIdentifier(),
muikkuWorkspaceStudent.getUserSchoolDataIdentifier().getDataSource().getIdentifier());
ensureCorrectWorkspaceStudent(
muikkuWorkspaceStudent,
muikkuStudentIdentifier,
pyramusCourseStudent.getUserIdentifier());
}
else {
// Workspace student with workspace student identifier still not found, even amongst archived workspace students
UserSchoolDataIdentifier userSchoolDataIdentifier = userSchoolDataIdentifierController.findUserSchoolDataIdentifierBySchoolDataIdentifier(
pyramusCourseStudent.getUserIdentifier());
if (userSchoolDataIdentifier == null) {
logger.severe(String.format("Unable to fix missing workspace student: UserSchoolDataIdentifier for Pyramus student %s not found", pyramusStudentId));
}
else {
// Try to find workspace student with workspace + student combo
muikkuWorkspaceStudent = workspaceUserEntityController.findWorkspaceUserEntityByWorkspaceAndUserSchoolDataIdentifierIncludeArchived(
workspaceEntity,
userSchoolDataIdentifier);
if (muikkuWorkspaceStudent != null) {
// Found. Might be archived but definitely has the wrong workspace student identifier
if (muikkuWorkspaceStudent.getArchived()) {
workspaceUserEntityController.unarchiveWorkspaceUserEntity(muikkuWorkspaceStudent);
}
if (!muikkuWorkspaceStudent.getIdentifier().equals(pyramusCourseStudent.getIdentifier().getIdentifier())) {
workspaceUserEntityController.updateIdentifier(muikkuWorkspaceStudent, pyramusCourseStudent.getIdentifier().getIdentifier());
}
}
else {
// Not found. Create a new workspace student
muikkuWorkspaceStudent = workspaceUserEntityController.createWorkspaceUserEntity(
userSchoolDataIdentifier,
workspaceEntity,
pyramusCourseStudentId,
workspaceStudentRole);
logger.info(String.format("Created workspace student %s", muikkuWorkspaceStudent.getIdentifier()));
}
}
}
}
else {
// Workspace student found with workspace student identifier. We still need to ensure that the underlying student is the same as in Pyramus
SchoolDataIdentifier muikkuStudentIdentifier = new SchoolDataIdentifier(
muikkuWorkspaceStudent.getUserSchoolDataIdentifier().getIdentifier(),
muikkuWorkspaceStudent.getUserSchoolDataIdentifier().getDataSource().getIdentifier());
ensureCorrectWorkspaceStudent(muikkuWorkspaceStudent, muikkuStudentIdentifier, pyramusCourseStudent.getUserIdentifier());
}
}
// The remaining Muikku students in muikkuWorkspaceStudents were not in Pyramus so archive them from Muikku
if (!muikkuWorkspaceStudents.isEmpty()) {
for (WorkspaceUserEntity muikkuWorkspaceStudent : muikkuWorkspaceStudents) {
workspaceUserEntityController.archiveWorkspaceUserEntity(muikkuWorkspaceStudent);
}
logger.info(String.format("Archived %d Muikku workspace students that were not present in Pyramus", muikkuWorkspaceStudents.size()));
}
// Student count in Muikku after synchronizing, which should be the same as in Pyramus before synchronization
muikkuWorkspaceStudents = workspaceUserEntityController.listWorkspaceUserEntitiesByRole(workspaceEntity, workspaceStudentRole);
logger.info(String.format("After synchronizing, Muikku course has %d active students", pyramusCourseStudents.size()));
// Course staff maintenance
// Teacher role
WorkspaceRoleEntity workspaceTeacherRole = roleController.findWorkspaceRoleEntityById(getWorkspaceTeacherRoleId());
// Workspace teachers in Muikku
List<WorkspaceUserEntity> muikkuWorkspaceTeachers = workspaceUserEntityController.listWorkspaceUserEntitiesByRole(workspaceEntity, workspaceTeacherRole);
logger.info(String.format("Before synchronizing, Muikku course has %d active teachers", muikkuWorkspaceTeachers.size()));
// Course teachers in Pyramus
List<WorkspaceUser> pyramusCourseTeachers = workspaceController.listWorkspaceStaffMembers(workspaceEntity);
logger.info(String.format("Before synchronizing, Pyramus course has %d active teachers", pyramusCourseTeachers.size()));
for (WorkspaceUser pyramusCourseTeacher : pyramusCourseTeachers) {
String pyramusCourseTeacherId = pyramusCourseTeacher.getIdentifier().getIdentifier();
String pyramusTeacherId = pyramusCourseTeacher.getUserIdentifier().getIdentifier();
WorkspaceUserEntity muikkuWorkspaceTeacher = null;
for (int i = 0; i < muikkuWorkspaceTeachers.size(); i++) {
String muikkuCourseTeacherId = muikkuWorkspaceTeachers.get(i).getIdentifier();
if (muikkuCourseTeacherId.equals(pyramusCourseTeacherId)) {
muikkuWorkspaceTeacher = muikkuWorkspaceTeachers.get(i);
muikkuWorkspaceTeachers.remove(i);
break;
}
}
if (muikkuWorkspaceTeacher == null) {
muikkuWorkspaceTeacher = workspaceUserEntityController.findWorkspaceUserEntityByWorkspaceUserIdentifierAndArchived(
pyramusCourseTeacher.getIdentifier(),
Boolean.TRUE);
if (muikkuWorkspaceTeacher != null) {
workspaceUserEntityController.unarchiveWorkspaceUserEntity(muikkuWorkspaceTeacher);
logger.info(String.format("Unarchived workspace teacher %s", pyramusCourseTeacherId));
if (!muikkuWorkspaceTeacher.getIdentifier().equals(pyramusCourseTeacher.getIdentifier().getIdentifier())) {
workspaceUserEntityController.updateIdentifier(muikkuWorkspaceTeacher, pyramusCourseTeacher.getIdentifier().getIdentifier());
}
}
else {
UserSchoolDataIdentifier userSchoolDataIdentifier = userSchoolDataIdentifierController.findUserSchoolDataIdentifierBySchoolDataIdentifier(
pyramusCourseTeacher.getUserIdentifier());
if (userSchoolDataIdentifier == null) {
logger.severe(String.format("Unable to fix missing workspace teacher: UserSchoolDataIdentifier for Pyramus teacher %s not found", pyramusTeacherId));
}
else {
muikkuWorkspaceTeacher = workspaceUserEntityController.findWorkspaceUserEntityByWorkspaceAndUserSchoolDataIdentifierIncludeArchived(
workspaceEntity,
userSchoolDataIdentifier);
if (muikkuWorkspaceTeacher != null) {
if (muikkuWorkspaceTeacher.getArchived()) {
workspaceUserEntityController.unarchiveWorkspaceUserEntity(muikkuWorkspaceTeacher);
}
if (!muikkuWorkspaceTeacher.getIdentifier().equals(pyramusCourseTeacher.getIdentifier().getIdentifier())) {
workspaceUserEntityController.updateIdentifier(muikkuWorkspaceTeacher, pyramusCourseTeacher.getIdentifier().getIdentifier());
}
}
else {
muikkuWorkspaceTeacher = workspaceUserEntityController.createWorkspaceUserEntity(
userSchoolDataIdentifier,
workspaceEntity,
pyramusCourseTeacherId,
workspaceTeacherRole);
logger.info(String.format("Created workspace teacher %", muikkuWorkspaceTeacher.getIdentifier()));
}
}
}
}
}
// The remaining Muikku teachers in muikkuWorkspaceTeachers were not in Pyramus so archive them from Muikku
if (!muikkuWorkspaceTeachers.isEmpty()) {
for (WorkspaceUserEntity muikkuWorkspaceTeacher : muikkuWorkspaceTeachers) {
workspaceUserEntityController.archiveWorkspaceUserEntity(muikkuWorkspaceTeacher);
}
logger.info(String.format("Archived %d Muikku workspace teachers that were not present in Pyramus", muikkuWorkspaceTeachers.size()));
}
return null;
}
private void ensureCorrectWorkspaceStudent(WorkspaceUserEntity muikkuWorkspaceStudent, SchoolDataIdentifier muikkuStudentIdentifier, SchoolDataIdentifier pyramusStudentIdentifier) {
if (!pyramusStudentIdentifier.equals(muikkuStudentIdentifier)) {
logger.warning(String.format("Muikku workspace student points to student %s and Pyramus to student %s", muikkuStudentIdentifier, pyramusStudentIdentifier));
UserSchoolDataIdentifier userSchoolDataIdentifier = userSchoolDataIdentifierController.findUserSchoolDataIdentifierBySchoolDataIdentifier(pyramusStudentIdentifier);
if (userSchoolDataIdentifier == null) {
logger.severe(String.format("Unable to fix: UserSchoolDataIdentifier for Pyramus student %s not found", pyramusStudentIdentifier));
}
else if (!userSchoolDataIdentifier.getUserEntity().getId().equals(muikkuWorkspaceStudent.getUserSchoolDataIdentifier().getUserEntity().getId())) {
logger.severe("Unable to fix: UserSchoolDataIdentifiers in Muikku and Pyramus point to different users");
}
else {
// Muikku and Pyramus students are not the same. Let's see if we can find a workspace student with workspace + student combo
WorkspaceUserEntity existingStudent = workspaceUserEntityController.findWorkspaceUserEntityByWorkspaceAndUserIdentifierIncludeArchived(
muikkuWorkspaceStudent.getWorkspaceEntity(),
pyramusStudentIdentifier);
if (existingStudent != null && !existingStudent.getId().equals(muikkuWorkspaceStudent.getId())) {
// We can, so delete the workspace student we had and use the one we found instead, unarchiving it if needed
if (existingStudent.getArchived()) {
workspaceUserEntityController.unarchiveWorkspaceUserEntity(existingStudent);
}
workspaceUserEntityController.updateIdentifier(existingStudent, muikkuWorkspaceStudent.getIdentifier());
workspaceUserEntityController.deleteWorkspaceUserEntity(muikkuWorkspaceStudent);
muikkuWorkspaceStudent = existingStudent;
}
// Set workspace student to point at the same student as in Pyramus
workspaceUserEntityController.updateUserSchoolDataIdentifier(muikkuWorkspaceStudent, userSchoolDataIdentifier);
logger.info("Muikku workspace student UserSchoolDataIdentifier updated");
}
}
}
private Long getWorkspaceStudentRoleId() {
List<WorkspaceRoleEntity> workspaceStudentRoles = roleController.listWorkspaceRoleEntitiesByArchetype(WorkspaceRoleArchetype.STUDENT);
if (workspaceStudentRoles.size() == 1) {
return workspaceStudentRoles.get(0).getId();
} else {
// TODO: How to choose correct workspace student role?
throw new RuntimeException("Multiple workspace student roles found.");
}
}
private Long getWorkspaceTeacherRoleId() {
String teacherRoleSetting = pluginSettingsController.getPluginSetting("school-data-pyramus", "roles.workspace.TEACHER");
if (StringUtils.isNumeric(teacherRoleSetting)) {
return Long.parseLong(teacherRoleSetting);
}
else {
throw new RuntimeException("school-data-pyramus/roles.workspace.TEACHER not set");
}
}
}