Java tutorial
/* Copyright "Open Digital Education", 2014 * * This program is published by "Open Digital Education". * You must indicate the name of the software and the company in any production /contribution * using the software and indicate on the home page of the software industry in question, * "powered by Open Digital Education" with a reference to the website: https://opendigitaleducation.com/. * * This program is free software, licensed under the terms of the GNU Affero General Public License * as published by the Free Software Foundation, version 3 of the License. * * You can redistribute this application and/or modify it since you respect the terms of the GNU Affero General Public License. * If you modify the source code and then use this modified source code in your creation, you must make available the source code of your modifications. * * You should have received a copy of the GNU Affero General Public License along with the software. * If not, please see : <http://www.gnu.org/licenses/>. Full compliance requires reading the terms of this license and following its directives. * */ package org.entcore.feeder.dictionary.structures; import org.entcore.common.events.EventStore; import org.entcore.common.events.EventStoreFactory; import org.entcore.common.notification.TimelineHelper; import org.entcore.common.user.UserInfos; import org.entcore.feeder.Feeder; import org.entcore.feeder.utils.TransactionHelper; import org.entcore.feeder.utils.TransactionManager; import io.vertx.core.Vertx; import io.vertx.core.Handler; import io.vertx.core.eventbus.EventBus; import io.vertx.core.eventbus.Message; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import static fr.wseduc.webutils.Utils.isNotEmpty; public class User { public static class DeleteTask implements Handler<Long> { private static final Logger log = LoggerFactory.getLogger(DeleteTask.class); private final long delay; private final EventBus eb; private final Vertx vertx; private EventStore eventStore = EventStoreFactory.getFactory().getEventStore(Feeder.class.getSimpleName()); private static final int LIMIT = 1000; private int page; public DeleteTask(long delay, EventBus eb, Vertx vertx) { this.delay = delay; this.eb = eb; this.vertx = vertx; } @Override public void handle(Long event) { log.info("Execute task delete user."); page = 1; delete(); } protected void delete() { try { TransactionHelper tx = TransactionManager.getInstance().begin(); User.getDelete(delay, LIMIT, tx); tx.commit(new Handler<Message<JsonObject>>() { @Override public void handle(Message<JsonObject> m) { JsonArray results = m.body().getJsonArray("results"); if ("ok".equals(m.body().getString("status")) && results != null) { final JsonArray r = results.getJsonArray(0); if (r != null && r.size() > 0) { final JsonArray deleteUsers = new fr.wseduc.webutils.collections.JsonArray(); for (Object o : r) { if (!(o instanceof JsonObject)) continue; deleteUsers.add(((JsonObject) o).getString("id")); } try { TransactionHelper tx = TransactionManager.getInstance().begin(); User.delete(deleteUsers, tx); tx.commit(new Handler<Message<JsonObject>>() { @Override public void handle(Message<JsonObject> m2) { if ("ok".equals(m2.body().getString("status"))) { log.info("Delete users : " + r.encode()); eb.publish(Feeder.USER_REPOSITORY, new JsonObject() .put("action", "delete-users").put("old-users", r)); eventStore.createAndStoreEvent( Feeder.FeederEvent.DELETE_USER.name(), (UserInfos) null, new JsonObject().put("old-users", r)); if (r.size() == LIMIT) { vertx.setTimer(LIMIT * 100l, new Handler<Long>() { @Override public void handle(Long event) { log.info("Delete page " + ++page); delete(); } }); } } else { log.error( "Error deleting user : " + m2.body().getString("message")); } } }); } catch (Exception e) { log.error("Delete task error"); log.error(e.getMessage(), e); } } else if (r == null) { log.error("User delete task return null array."); } } else { log.error(m.body().encode()); } } }); } catch (Exception e) { log.error("Delete task error"); log.error(e.getMessage(), e); } } } public static class PreDeleteTask implements Handler<Long> { private static final Logger log = LoggerFactory.getLogger(DeleteTask.class); private final long delay; private final String profile; private static final int LIMIT = 5000; private final TimelineHelper timeline; public PreDeleteTask(long delay) { this(delay, null, null); } public PreDeleteTask(long delay, TimelineHelper timeline) { this(delay, null, timeline); } public PreDeleteTask(Long delay, String profile, TimelineHelper timeline) { this.delay = delay; this.profile = profile; this.timeline = timeline; } @Override public void handle(Long event) { launchPreDelete(); } private void launchPreDelete() { log.info("Execute task pre-delete user."); JsonObject params = new JsonObject().put("date", System.currentTimeMillis() - delay).put("limit", LIMIT); String filter = ""; if (profile != null) { params.put("profile", profile); filter = "AND head(u.profiles) = {profile} "; } String query = "MATCH (u:User) " + "WHERE HAS(u.disappearanceDate) AND NOT(HAS(u.deleteDate)) AND u.disappearanceDate < {date} " + filter + "RETURN u.id as id " + "LIMIT {limit} "; TransactionManager.getInstance().getNeo4j().execute(query, params, new Handler<Message<JsonObject>>() { @Override public void handle(Message<JsonObject> message) { final JsonArray res = message.body().getJsonArray("result"); if ("ok".equals(message.body().getString("status")) && res != null && res.size() > 0) { preDeleteUsers(res, null); } else if (timeline != null && "ok".equals(message.body().getString("status"))) { notifyRemainingDays(); } } }); } private void notifyRemainingDays() { JsonObject params = new JsonObject(); String filter = ""; if (profile != null) { params.put("profile", profile); filter = "AND head(u.profiles) = {profile} "; } String query = "MATCH (u:User) " + "WHERE HAS(u.disappearanceDate) AND NOT(HAS(u.deleteDate)) " + filter + "RETURN u.id as id, u.disappearanceDate as disappearanceDate "; TransactionManager.getInstance().getNeo4j().execute(query, params, message -> { final JsonArray res = message.body().getJsonArray("result"); if ("ok".equals(message.body().getString("status")) && res != null && res.size() > 0) { final long now = System.currentTimeMillis(); for (Object o : res) { if (!(o instanceof JsonObject)) continue; JsonObject j = (JsonObject) o; JsonObject p = new JsonObject().put("remainingDays", Math.round((j.getLong("disappearanceDate") + delay - now) / 3600000.0 / 24)); final List<String> recipients = new ArrayList<>(); recipients.add(j.getString("id")); timeline.notifyTimeline(null, "userbook.predelete-delay", null, recipients, null, p); } } }); } public void findMissingUsersInStructure(String structureExternalId, String source, JsonArray existingUsers, JsonArray profiles, Handler<Message<JsonObject>> handler) { final String query = "MATCH (s:Structure {externalId : {structureExternalId}})<-[:DEPENDS]-(:ProfileGroup)<-[:IN]-(u:User) " + "WHERE u.source = {source} AND HEAD(u.profiles) IN {profiles} AND NOT(u.externalId IN {existingUsers}) " + "RETURN u.id as id, u.externalId as externalId, u.lastName as lastName, " + "u.firstName as firstName, HEAD(u.profiles) as profile"; final JsonObject params = new JsonObject().put("structureExternalId", structureExternalId) .put("source", source).put("existingUsers", existingUsers).put("profiles", profiles); TransactionManager.getInstance().getNeo4j().execute(query, params, handler); } public void preDeleteMissingUsersInStructure(String structureExternalId, String source, JsonArray existingUsers, JsonArray profiles, final Handler<Message<JsonObject>> handler) { findMissingUsersInStructure(structureExternalId, source, existingUsers, profiles, new Handler<Message<JsonObject>>() { @Override public void handle(Message<JsonObject> event) { final JsonArray res = event.body().getJsonArray("result"); if ("ok".equals(event.body().getString("status")) && res != null && res.size() > 0) { preDeleteUsers(res, handler); } else { handler.handle(event); } } }); } private void preDeleteUsers(final JsonArray users, final Handler<Message<JsonObject>> handler) { try { TransactionHelper tx = TransactionManager.getInstance().begin(); for (Object o : users) { if (!(o instanceof JsonObject)) continue; String userId = ((JsonObject) o).getString("id"); backupRelationship(userId, tx); preDelete(userId, tx); } tx.commit(new Handler<Message<JsonObject>>() { @Override public void handle(Message<JsonObject> m) { if ("ok".equals(m.body().getString("status"))) { log.info("PreDelete users : " + users.encode()); } else { log.error(m.body().encode()); } if (handler != null) { handler.handle(m); } else { launchPreDelete(); } } }); } catch (Exception e) { log.error("PreDelete task error"); log.error(e.getMessage(), e); } } } public static void backupRelationship(String userId, TransactionHelper transaction) { JsonObject params = new JsonObject().put("userId", userId); String query = "MATCH (u:User { id : {userId}})-[r:IN]->(n) " + "WHERE HAS(n.id) AND NOT(n:DeleteGroup) " + "WITH u, COLLECT(n.id) as ids " + "MERGE u-[:HAS_RELATIONSHIPS]->(b:Backup {userId: {userId}}) " + "SET b.IN_OUTGOING = coalesce(b.IN_OUTGOING, []) + ids "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}})-[r:COMMUNIQUE]->(n) " + "WHERE HAS(n.id) AND NOT(n:DeleteGroup) " + "WITH u, COLLECT(n.id) as ids " + "MERGE u-[:HAS_RELATIONSHIPS]->(b:Backup {userId: {userId}}) " + "SET b.COMMUNIQUE_OUTGOING = ids "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}})<-[r:COMMUNIQUE]-(n) " + "WHERE HAS(n.id) AND NOT(n:DeleteGroup) " + "WITH u, COLLECT(n.id) as ids " + "MERGE u-[:HAS_RELATIONSHIPS]->(b:Backup {userId: {userId}}) " + "SET b.COMMUNIQUE_INCOMING = ids "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}})-[r:COMMUNIQUE_DIRECT]->(n) " + "WHERE HAS(n.id) " + "WITH u, COLLECT(n.id) as ids " + "MERGE u-[:HAS_RELATIONSHIPS]->(b:Backup {userId: {userId}}) " + "SET b.COMMUNIQUE_DIRECT_OUTGOING = ids "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}})<-[r:COMMUNIQUE_DIRECT]-(n) " + "WHERE HAS(n.id) " + "WITH u, COLLECT(n.id) as ids " + "MERGE u-[:HAS_RELATIONSHIPS]->(b:Backup {userId: {userId}}) " + "SET b.COMMUNIQUE_DIRECT_INCOMING = ids "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}})-[r:RELATED]->(n) " + "WHERE HAS(n.id) " + "WITH u, COLLECT(n.id) as ids " + "MERGE u-[:HAS_RELATIONSHIPS]->(b:Backup {userId: {userId}}) " + "SET b.RELATED_OUTGOING = ids "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}})<-[r:RELATED]-(n) " + "WHERE HAS(n.id) " + "WITH u, COLLECT(n.id) as ids " + "MERGE u-[:HAS_RELATIONSHIPS]->(b:Backup {userId: {userId}}) " + "SET b.RELATED_INCOMING = ids "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}})-[:IN]->(pg: ProfileGroup)-[:DEPENDS]->(s: Structure), " + " (u)-[:HAS_RELATIONSHIPS]->(b: Backup) " + "WITH b, COLLECT(s.id) as sIds " + "SET b.structureIds = sIds"; transaction.add(query, params); } public static void preDelete(String userId, TransactionHelper transaction) { JsonObject params = new JsonObject().put("userId", userId); String mQuery = "MATCH (u:User { id : {userId}})<-[r:MERGED]-(um:User), " + "u<-[:RELATED]-(us:User) " + "WHERE has(us.relative) AND LENGTH(FILTER(eId IN us.relative WHERE eId STARTS WITH um.externalId)) > 0 " + "REMOVE um.mergedWith, u.mergedLogins " + "CREATE UNIQUE um<-[:RELATED]-us " + "DELETE r " + "WITH um, us " + "MATCH us-[:IN]->(scg:ProfileGroup)-[:DEPENDS]->(c:Structure)<-[:DEPENDS]-(rcg:ProfileGroup)-[:HAS_PROFILE]->(p:Profile) " + "WHERE p.name = head(um.profiles) " + "CREATE UNIQUE um-[:IN]->rcg "; transaction.add(mQuery, params); String query = "MATCH (u:User { id : {userId}}), (dg:DeleteGroup) " + "OPTIONAL MATCH u-[r:IN|COMMUNIQUE|COMMUNIQUE_DIRECT|RELATED|DUPLICATE|TEACHES_FOS|TEACHES]-() " + "SET u.deleteDate = timestamp(), u.IDPN = null " + "DELETE r " + "CREATE UNIQUE dg<-[:IN]-u"; transaction.add(query, params); } public static void restorePreDeleted(String userId, TransactionHelper transaction) { JsonObject params = new JsonObject().put("userId", userId); String query = "MATCH (u:User {id: {userId}})-[:HAS_RELATIONSHIPS]->(b:Backup) " + "MATCH (g:Group) WHERE g.id IN b.IN_OUTGOING CREATE UNIQUE u-[:IN]->g"; transaction.add(query, params); query = "MATCH (u:User {id: {userId}})-[:HAS_RELATIONSHIPS]->(b:Backup) " + "MATCH (g:Group) WHERE g.id IN b.COMMUNIQUE_OUTGOING CREATE UNIQUE u-[:COMMUNIQUE]->g"; transaction.add(query, params); query = "MATCH (u:User {id: {userId}})-[:HAS_RELATIONSHIPS]->(b:Backup) " + "MATCH (g:Group) WHERE g.id IN b.COMMUNIQUE_INCOMING CREATE UNIQUE u<-[:COMMUNIQUE]-g"; transaction.add(query, params); query = "MATCH (u:User {id: {userId}})-[r:IN]->(:DeleteGroup), u-[r2:HAS_RELATIONSHIPS]->(b:Backup) " + "REMOVE u.disappearanceDate, u.deleteDate " + "DELETE r, r2, b"; transaction.add(query, params); } public static void transition(String userId, TransactionHelper transaction) { JsonObject params = new JsonObject().put("userId", userId); String query = "MATCH (u:User { id : {userId}})-[r:IN|COMMUNIQUE]-(:ProfileGroup)-[:DEPENDS]->(c:Class) " + "DELETE r "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}})-[r:IN|COMMUNIQUE]-(:FunctionalGroup)-[:DEPENDS]->(c:Structure) " + "DELETE r "; transaction.add(query, params); query = "MATCH (u:User { id : {userId}}) WHERE HAS(u.headTeacherManual) " + "REMOVE u.headTeacherManual "; transaction.add(query, params); } public static void getDelete(long delay, int limit, TransactionHelper transactionHelper) { JsonObject params = new JsonObject().put("date", System.currentTimeMillis() - delay).put("limit", limit); String query = "MATCH (:DeleteGroup)<-[:IN]-(u:User)-[:HAS_RELATIONSHIPS]->(b:Backup) " + "WHERE HAS(u.deleteDate) AND u.deleteDate < {date} " + "OPTIONAL MATCH (fgroup:FunctionalGroup) " + "WHERE fgroup.externalId IN u.groups " + "OPTIONAL MATCH (mgroup:ManualGroup) " + "WHERE mgroup.id IN b.IN_OUTGOING " + "OPTIONAL MATCH (c:Class) " + "WHERE c.externalId IN u.classes " + "OPTIONAL MATCH (s:Structure) " + "WHERE s.externalId IN u.structures " + "RETURN DISTINCT u.id as id, u.firstName as firstName, u.lastName as lastName, " + "u.deleteDate as deleteDate, u.birthDate as birthDate," + "u.externalId as externalId, u.displayName as displayName, " + "HEAD(u.profiles) as type, " + "CASE WHEN c IS NULL THEN [] ELSE collect(distinct c.id) END as classIds, " + "CASE WHEN fgroup IS NULL THEN [] ELSE collect(distinct fgroup.id) END as functionalGroupsIds, " + "CASE WHEN mgroup IS NULL THEN [] ELSE collect(distinct mgroup.id) END as manualGroupsIds, " + "CASE WHEN s IS NULL THEN [] ELSE collect(distinct s.id) END as structureIds " + "LIMIT {limit} "; transactionHelper.add(query, params); } public static void delete(JsonArray deleteUsers, TransactionHelper transactionHelper) { JsonObject params = new JsonObject().put("deleteUsers", deleteUsers); final String query = "MATCH (:DeleteGroup)<-[:IN]-(u:User) " + "WHERE u.id IN {deleteUsers} " + "OPTIONAL MATCH u-[rb:HAS_RELATIONSHIPS]->(b:Backup) " + "OPTIONAL MATCH u-[r]-() " + "DELETE u,b,r,rb "; transactionHelper.add(query, params); } public static void addHeadTeacherManual(String userId, String structureExternalId, String classExternalId, TransactionHelper transactionHelper) { JsonObject params = new JsonObject().put("userId", userId).put("classExternalId", classExternalId) .put("structureExternalId", structureExternalId); String query = "MERGE (u:User { id: {userId} })" + "FOREACH(x in CASE WHEN {classExternalId} in u.headTeacherManual THEN [] ELSE [1] END | " + "SET u.headTeacherManual = coalesce(u.headTeacherManual,[]) + {classExternalId} " + ") " + "RETURN u.headTeacherManual"; transactionHelper.add(query, params); String query2 = "MATCH (u:User { id : {userId}}), (c:Class {externalId : {classExternalId}})<-[:DEPENDS]-(g:Group:HTGroup) " + "MERGE u-[:IN {source:'MANUAL'}]->g "; transactionHelper.add(query2, params); String query3 = "MATCH (u:User { id : {userId}}), (s:Structure {externalId : {structureExternalId}})<-[:DEPENDS]-(g:Group:HTGroup) " + "MERGE u-[:IN]->g " + "MERGE g-[:COMMUNIQUE]->u"; ; transactionHelper.add(query3, params); } public static void updateHeadTeacherManual(String userId, String structureExternalId, String classExternalId, TransactionHelper transactionHelper) { JsonObject params = new JsonObject().put("userId", userId).put("classExternalId", classExternalId) .put("structureExternalId", structureExternalId); String query = "MATCH (u:User) " + "WHERE HAS(u.headTeacherManual) AND u.id = {userId} " + "SET u.headTeacherManual = FILTER(x IN u.headTeacherManual WHERE x <> {classExternalId}) " + "RETURN u.headTeacherManual"; transactionHelper.add(query, params); String query2 = "MATCH (u:User { id : {userId}})-[r:IN]->(g:Group:HTGroup)-[:DEPENDS]->(c:Class {externalId : {classExternalId}}) " + "DELETE r "; transactionHelper.add(query2, params); String query3 = "MATCH (u:User { id : {userId}})-[r:IN |COMMUNIQUE]->(g:Group:HTGroup)-[:DEPENDS]->(s:Structure {externalId : {structureExternalId}}) " + "WHERE length(u.headTeacherManual) = 0 AND (u.headTeacher IS NULL OR length(u.headTeacher) = 0) " + "DELETE r "; ; transactionHelper.add(query3, params); } public static void addFunction(String userId, String functionCode, JsonArray s, TransactionHelper transactionHelper) { String query = "MATCH (u:User { id : {userId}}) " + "OPTIONAL MATCH (f1:Function { externalId : {functionCode}}) " + "OPTIONAL MATCH (f2:Functions { externalId : {functionCode}}) " + "WITH u, collect (f1) + collect(f2) as c " + "UNWIND c as f " + "MERGE u-[rf:HAS_FUNCTION]->f "; JsonArray scope = null; JsonObject params = new JsonObject().put("userId", userId).put("functionCode", functionCode); if (s != null) { query += "SET rf.scope = {scope} "; scope = new fr.wseduc.webutils.collections.JsonArray(new ArrayList<>(new HashSet<String>(s.getList()))); params.put("scope", scope); } transactionHelper.add(query, params); if (scope != null) { String query2 = "MATCH (u:User { id : {userId}}) " + "OPTIONAL MATCH (n1:Structure) WHERE n1.id IN {scope} " + "OPTIONAL MATCH (n2:Class) WHERE n2.id IN {scope} " + "OPTIONAL MATCH (f1:Function { externalId : {functionCode}}) " + "OPTIONAL MATCH (f2:Functions { externalId : {functionCode}}) " + "WITH u, collect (n1) + collect(n2) as c1 , collect (f1) + collect(f2) as c2 " + "UNWIND c1 as n " + "UNWIND c2 as f " + "MERGE (fg:Group:FunctionGroup { externalId : n.id + '-' + {functionCode}}) " + "ON CREATE SET fg.id = id(fg) + '-' + timestamp(), fg.name = n.name + '-' + f.name, fg.displayNameSearchField = lower(n.name) + lower(f.name), fg.filter = f.name\n" + "CREATE UNIQUE n<-[:DEPENDS]-fg<-[:IN {source:'MANUAL'}]-u " + "RETURN fg.id as groupId "; JsonObject p2 = new JsonObject().put("scope", scope).put("functionCode", functionCode).put("userId", userId); transactionHelper.add(query2, p2); countUsersInGroups(null, "FunctionGroup", transactionHelper); } } public static void removeFunction(String userId, String functionCode, TransactionHelper transactionHelper) { String query = "MATCH (u:User { id : {userId}})-[r:HAS_FUNCTION]->(f) " + "WHERE (f:Function OR f:Functions) AND f.externalId = {functionCode} " + "WITH r.scope as scope, r, u, f " + "DELETE r " + "WITH coalesce(scope, []) as ids, u, f " + "UNWIND ids as s " + "MATCH (fg:FunctionGroup {externalId : s + '-' + f.externalId})<-[r:IN|COMMUNIQUE]-u " + "DELETE r"; JsonObject params = new JsonObject().put("userId", userId).put("functionCode", functionCode); transactionHelper.add(query, params); countUsersInGroups(null, "FunctionGroup", transactionHelper); } public static void addGroup(String userId, String groupId, TransactionHelper transactionHelper) { String query = "MATCH (u:User { id : {userId}}), (f:Group {id : {groupId}}) " + "WHERE 'ManualGroup' IN labels(f) OR 'FunctionalGroup' IN labels(f) " + "CREATE UNIQUE u-[:IN {source:'MANUAL'}]->f " + "WITH f, u " + "WHERE 'FunctionalGroup' IN labels(f) " + "SET u.groups = FILTER(gId IN coalesce(u.groups, []) WHERE gId <> f.externalId) + f.externalId "; JsonObject params = new JsonObject().put("userId", userId).put("groupId", groupId); transactionHelper.add(query, params); countUsersInGroups(groupId, null, transactionHelper); } public static void removeGroup(String userId, String groupId, TransactionHelper transactionHelper) { String query = "MATCH (u:User { id : {userId}})-[r:IN|COMMUNIQUE]->(f:Group {id : {groupId}}) " + "WHERE 'ManualGroup' IN labels(f) OR 'FunctionalGroup' IN labels(f) " + "SET u.groups = FILTER(gId IN coalesce(u.groups, []) WHERE gId <> f.externalId) " + "DELETE r "; JsonObject params = new JsonObject().put("userId", userId).put("groupId", groupId); transactionHelper.add(query, params); countUsersInGroups(groupId, null, transactionHelper); } public static void count(String exportType, JsonArray profiles, TransactionHelper transactionHelper) { String query; JsonObject params = new JsonObject(); if (profiles != null && profiles.size() > 0) { query = "MATCH (u:User)-[:IN]->(:ProfileGroup)-[:DEPENDS]->(s:Structure) " + "WHERE HEAD(u.profiles) IN {profiles} "; params.put("profiles", profiles); if (isNotEmpty(exportType)) { query += "AND "; } } else { query = "MATCH (u:User)-[:IN]->(:ProfileGroup)-[:DEPENDS]->(s:Structure) "; if (isNotEmpty(exportType)) { query += "WHERE "; } } if (isNotEmpty(exportType)) { query += "HAS(s.exports) AND {exportType} IN s.exports "; params.put("exportType", exportType); } query += "RETURN count(distinct u) as nb"; transactionHelper.add(query, params); } public static void list(String exportType, JsonArray profiles, JsonArray attributes, Integer skip, Integer limit, TransactionHelper transactionHelper) { StringBuilder query = new StringBuilder(); JsonObject params = new JsonObject(); String filter = ""; String unionManualGroups = "as functionalGroups "; if (isNotEmpty(exportType)) { filter = "AND HAS(s0.exports) AND {exportType} IN s0.exports "; params.put("exportType", exportType); } if (attributes != null && attributes.contains("manualAndFunctionalGroups")) { unionManualGroups = " + COLLECT(DISTINCT { structureExternalId : s.externalId, id: g.id, name: g.name }) as manualAndFunctionalGroups "; } if (profiles != null && profiles.size() > 0) { query.append("MATCH (u:User)-[:IN]->(:ProfileGroup)-[:DEPENDS]->(s0:Structure) " + "WHERE HEAD(u.profiles) IN {profiles} AND NOT(HAS(u.deleteDate)) ").append(filter) .append("OPTIONAL MATCH u-[:IN]->(g:ManualGroup)-[:DEPENDS]->(s:Structure) ") .append((attributes != null && (attributes.contains("functionalGroups") || attributes.contains("manualAndFunctionalGroups"))) ? "OPTIONAL MATCH u-[:IN]->(fg:FunctionalGroup)-[:DEPENDS]->(s1:Structure) " : "") .append("WITH u, COLLECT(DISTINCT s.externalId + '$' + g.id + '$' + g.name) as manualGroups ") .append((attributes != null && (attributes.contains("functionalGroups") || attributes.contains("manualAndFunctionalGroups"))) ? ", COLLECT(DISTINCT { structureExternalId : s1.externalId, id: fg.id, externalId: fg.externalId, name: fg.name, idrgpmt : fg.idrgpmt, " + "idgpe : fg.idgpe, code_gep : fg.code_gep, code : fg.code, code_div : fg.code_div, usedInCourses : fg.usedInCourses }) " + unionManualGroups : ""); params.put("profiles", profiles); } else { query.append( "MATCH (u:User)-[:IN]->(:ProfileGroup)-[:DEPENDS]->(s0:Structure) WHERE NOT(HAS(u.deleteDate)) ") .append(filter); } if (attributes != null && attributes.size() > 0) { query.append("RETURN DISTINCT"); for (Object attribute : attributes) { if ("manualGroups".equals(attribute) || "manualAndFunctionalGroups".equals(attribute) || "functionalGroups".equals(attribute)) { query.append(" ").append(attribute); } else { query.append(" u.").append(attribute); } query.append(" as ").append(attribute).append(","); } query.deleteCharAt(query.length() - 1); query.append(" "); } else { query.append(" RETURN DISTINCT u "); } if (skip != null && limit != null) { query.append("ORDER BY externalId ASC " + "SKIP {skip} " + "LIMIT {limit} "); params.put("skip", skip); params.put("limit", limit); } transactionHelper.add(query.toString(), params); } public static void listByFunctions(String exportType, JsonArray functions, TransactionHelper transactionHelper) { JsonObject params = new JsonObject().put("functions", functions); String filter; if (isNotEmpty(exportType)) { filter = "-[:IN]->(:ProfileGroup)-[:DEPENDS]->(s:Structure) " + "WHERE f.externalId IN {functions} AND HAS(s.exports) AND {exportType} IN s.exports "; params.put("exportType", exportType); } else { filter = " WHERE f.externalId IN {functions} "; } String query = "MATCH (f:Function)<-[:CONTAINS_FUNCTION*0..1]-()<-[rf:HAS_FUNCTION]-u" + filter + "WITH DISTINCT u.externalId as externalId, rf.scope as scope, f " + "MATCH (s:Structure) " + "WHERE s.id in scope " + "WITH externalId, COLLECT(distinct s.externalId) as structs, f " + "RETURN externalId, COLLECT(distinct [f.externalId, structs]) as functions "; transactionHelper.add(query, params); } public static void relativeStudent(String relativeId, String studentId, TransactionHelper tx) { String query = "MATCH (r:User {id : {relativeId}})-[:IN]->(:ProfileGroup)-[:HAS_PROFILE]->(:Profile { name : 'Relative'}) " + "WITH DISTINCT r " + "MATCH (s:User {id : {studentId}})-[:IN]->(spg:ProfileGroup)-[:DEPENDS]->(st:Structure), " + "(spg)-[:HAS_PROFILE]->(:Profile { name : 'Student'}), " + "(st)<-[:DEPENDS]-(rspg:ProfileGroup)-[:HAS_PROFILE]->(:Profile { name : 'Relative'}) " + "MERGE s-[:RELATED]->r " + "WITH s, r, st, rspg " + "MERGE r-[:IN]->rspg " + "WITH s, r, st " + "SET s.relative = CASE WHEN r.externalId IN s.relative THEN " + "s.relative ELSE coalesce(s.relative, []) + (r.externalId + '$10$1$1$0$0') END " + "RETURN COLLECT(st.id) as structures "; String query2 = "MATCH (r:User {id : {relativeId}})-[:IN]->(:ProfileGroup)-[:HAS_PROFILE]->(:Profile { name : 'Relative'}) " + "WITH DISTINCT r " + "MATCH (s:User {id : {studentId}})-[:IN]->(:ProfileGroup)-[:DEPENDS]->(c:Class)-[:BELONGS]->(st:Structure), " + "(s)-[:IN]->(:ProfileGroup)-[:HAS_PROFILE]->(:Profile { name : 'Student'}), " + "(c)<-[:DEPENDS]-(rcpg:ProfileGroup)-[:DEPENDS]->(:ProfileGroup)-[:HAS_PROFILE]->(:Profile { name : 'Relative'}) " + "MERGE r-[:IN]->rcpg"; JsonObject params = new JsonObject().put("relativeId", relativeId).put("studentId", studentId); tx.add(query, params); tx.add(query2, params); } public static void unlinkRelativeStudent(String relativeId, String studentId, TransactionHelper tx) { String query = "MATCH (r:User {id : {relativeId}})-[:IN]->(:ProfileGroup)-[:HAS_PROFILE]->(:Profile { name : 'Relative'}) " + "WITH r " + "MATCH (s:User {id : {studentId}})-[:IN]->(:ProfileGroup)-[:HAS_PROFILE]->(:Profile { name : 'Student'}), " + "s-[relations]-r " + "SET s.relative = FILTER(rId IN s.relative WHERE NOT(rId =~ (r.externalId + '.*'))) " + "DELETE relations"; String query2 = "MATCH (s:User {id : {studentId}})-[:IN]->(:ProfileGroup)-[:DEPENDS]->(c:Class), " + "(r:User {id : {relativeId}})-[r2:IN]->(:ProfileGroup)-[:DEPENDS]->c " + "WHERE NOT(r<-[:RELATED]-(:User)-[:IN]->(:ProfileGroup)-[:DEPENDS]->c) " + "DELETE r2"; JsonObject params = new JsonObject().put("relativeId", relativeId).put("studentId", studentId); tx.add(query, params); tx.add(query2, params); } public static void countUsersInGroups(String groupId, String groupType, TransactionHelper tx) { JsonObject params = new JsonObject(); String filter = ""; String type = "Group"; if (isNotEmpty(groupId)) { filter = " {id: {id}}"; params.put("id", groupId); final String query0 = "MATCH (g:Group {id: {id}}) SET g.nbUsers = 0;"; tx.add(query0, params); } else if (isNotEmpty(groupType)) { type = groupType; final String query0 = "MATCH (g:" + type + ") SET g.nbUsers = 0;"; tx.add(query0, params); } final String query = "MATCH (g:" + type + filter + ")<-[:IN]-(u:User) WITH g, count(u) as cu SET g.nbUsers = cu;"; tx.add(query, params); } }