package crmdna.member; import com.google.appengine.api.datastore.Cursor; import com.google.appengine.api.datastore.QueryResultIterator; import com.googlecode.objectify.Key; import com.googlecode.objectify.cmd.Query; import crmdna.client.Client; import crmdna.common.DateUtils; import crmdna.common.Utils; import crmdna.common.api.APIException; import crmdna.common.api.APIResponse.Status; import crmdna.group.Group; import crmdna.group.GroupHelper; import crmdna.list.ListEntity; import crmdna.list.ListHelper; import crmdna.practice.PracticeHelper; import crmdna.program.Program; import crmdna.program.ProgramEntity; import crmdna.program.ProgramProp; import crmdna.programtype.ProgramTypeHelper; import crmdna.user.User; import java.util.*; import java.util.Map.Entry; import static crmdna.common.AssertUtils.ensure; import static crmdna.common.AssertUtils.ensureNotNull; import static crmdna.common.OfyService.ofy; public class MemberLoader { public static final int MAX_RESULT_SIZE = 2500; public static MemberEntity safeGet(String client, long memberId, String login) { ensure(memberId != 0, "memberId is 0"); Client.ensureValid(client); ensureNotNull(login, "login is null"); MemberEntity entity = ofy(client).load().type(MemberEntity.class).id(memberId).now(); if (null == entity) throw new APIException("There is no member with id [" + memberId + "]") .status(Status.ERROR_RESOURCE_NOT_FOUND); if ((entity.email == null) || !entity.email.equalsIgnoreCase(login)) User.ensureValidUser(client, login); return entity; } public static MemberEntity safeGetByIdOrEmail(String client, String memberIdOrEmail, String login) { if (Utils.canParseAsLong(memberIdOrEmail)) { return safeGet(client, Utils.safeParseAsLong(memberIdOrEmail), login); } else { String email = memberIdOrEmail.toLowerCase(); Utils.ensureValidEmail(email); MemberQueryCondition qc = new MemberQueryCondition(client, 100); qc.email = email; List<MemberEntity> entities = MemberLoader.queryEntities(qc, User.SUPER_USER); ensure(!entities.isEmpty(), "There is no member with email [" + email + "]"); ensure(entities.size() == 1, "There are [" + entities.size() + "] members with email [" + email + "]. Specify memberId"); ensure(email.equals(entities.get(0).email)); return entities.get(0); } } public static MemberEntity getByEmail(String client, String email) { Utils.ensureValidEmail(email); MemberQueryCondition qc = new MemberQueryCondition(client, 100); qc.email = email; List<MemberEntity> entities = MemberLoader.queryEntities(qc, User.SUPER_USER); if (entities.isEmpty()) return null; return entities.get(0); } public static List<MemberProp> quickSearch(String client, String searchStr, Set<Long> groupIds, int maxResultSize, String login) { ensure(maxResultSize > 0, "invalid maxResultSize [" + maxResultSize + "]"); Client.ensureValid(client); User.ensureValidUser(client, login); MemberQueryCondition qc = new MemberQueryCondition(client, maxResultSize); qc.searchStr = searchStr; qc.groupIds = groupIds; List<MemberProp> memberProps = MemberLoader.querySortedProps(qc, login); PracticeHelper.populateName(client, memberProps); GroupHelper.populateName(client, memberProps); // TODO: use projection query for (MemberProp memberProp : memberProps) { memberProp.contact.homeAddress = null; memberProp.contact.officeAddress = null; } return memberProps; } public static MemberProp safeGetDetailedInfo(String client, long memberId, String login) { Client.ensureValid(client); User.ensureValidUser(client, login); MemberEntity memberEntity = MemberLoader.safeGet(client, memberId, login); MemberProp memberProp = memberEntity.toProp(); populateDependents(client, memberProp, login); return memberProp; } public static void populateDependents(String client, MemberProp memberProp, String login) { Client.ensureValid(client); User.ensureValidUser(client, login); MemberEntity memberEntity = MemberLoader.safeGet(client, memberProp.memberId, login); // populate programProps and practices PracticeHelper.populateName(client, Utils.getList(memberProp)); GroupHelper.populateName(client, Utils.getList(memberProp)); // both verified and unverified programs go into memberprogramprop Map<Long, ProgramEntity> map = Program.getEntities(client, memberProp.programIds); for (Long programId : map.keySet()) { ProgramProp programProp = map.get(programId).toProp(client); MemberProgramProp memberProgramProp = new MemberProgramProp(); memberProgramProp.groupOrCity = programProp.groupProp.displayName; memberProgramProp.month = DateUtils.getMonthEnum(programProp.startYYYYMMDD); memberProgramProp.programTypeId = programProp.programTypeProp.programTypeId; memberProgramProp.teacher = programProp.teacherProp.firstName + " " + programProp.teacherProp.lastName; memberProgramProp.verified = true; memberProgramProp.year = programProp.startYYYYMMDD / 10000; memberProp.memberProgramProps.add(memberProgramProp); } for (Integer i : memberEntity.uvpMap.keySet()) { UnverifiedProgramProp unverifiedProgramProp = memberEntity.uvpMap.get(i); MemberProgramProp memberProgramProp = new MemberProgramProp(); memberProgramProp.groupOrCity = unverifiedProgramProp.city; memberProgramProp.month = unverifiedProgramProp.month; memberProgramProp.programTypeId = unverifiedProgramProp.programTypeId; memberProgramProp.teacher = unverifiedProgramProp.teacher; memberProgramProp.verified = false; memberProgramProp.year = unverifiedProgramProp.year; memberProgramProp.unverifiedProgramId = unverifiedProgramProp.unverifiedProgramId; memberProp.memberProgramProps.add(memberProgramProp); } ProgramTypeHelper.populateName(client, memberProp.memberProgramProps); Collections.sort(memberProp.memberProgramProps); Map<Long, ListEntity> listEntityMap = crmdna.list.List.get(client, memberProp.listIds); for (Entry<Long, ListEntity> entry : listEntityMap.entrySet()) { memberProp.listProps.add(entry.getValue().toProp()); } Collections.sort(memberProp.listProps); ListHelper.populateGroupName(client, memberProp.listProps); } public static Map<Long, MemberEntity> get(String client, Iterable<Long> memberIds, String login) { Client.ensureValid(client); User.ensureValidUser(client, login); return ofy(client).load().type(MemberEntity.class).ids(memberIds); } public static List<MemberProp> querySortedProps(MemberQueryCondition condition, String login) { List<MemberProp> props = queryProps(condition, login); Collections.sort(props); return props; } public static List<MemberProp> queryWithCursor(MemberQueryCondition condition, String login) { Client.ensureValid(condition.client); User.ensureValidUser(condition.client, login); boolean proceed = false; List<MemberProp> props = new ArrayList<>(); Query<MemberEntity> q = getQuery(condition); QueryResultIterator<MemberEntity> iterator = q.iterator(); while (iterator.hasNext()) { MemberEntity memberEntity = iterator.next(); MemberProp memberProp = memberEntity.toProp(); MemberLoader.populateDependents(condition.client, memberProp, login); props.add(memberProp); proceed = true; } condition.cursor = proceed ? iterator.getCursor().toWebSafeString() : null; Collections.sort(props); return props; } public static List<MemberProp> queryProps(MemberQueryCondition condition, String login) { Collection<MemberEntity> entities = queryEntities(condition, login); List<MemberProp> props = new ArrayList<>(entities.size()); for (MemberEntity memberEntity : entities) { props.add(memberEntity.toProp()); } return props; } public static List<MemberEntity> queryEntities(MemberQueryCondition condition, String login) { List<Key<MemberEntity>> memberKeys = queryKeys(condition, login); Map<Key<MemberEntity>, MemberEntity> map = ofy(condition.client).load().keys(memberKeys); return new ArrayList<>(map.values()); } static List<Key<MemberEntity>> queryKeys(MemberQueryCondition condition, String login) { Client.ensureValid(condition.client); User.ensureValidUser(condition.client, login); Query<MemberEntity> q = getQuery(condition); List<Key<MemberEntity>> memberKeys = q.keys().list(); if ((condition.maxResultSize != null) && (memberKeys.size() > condition.maxResultSize)) { throw new APIException().status(Status.ERROR_OVERFLOW).message( "Found [" + memberKeys.size() + "] matches. (Max allowed is [" + condition.maxResultSize + "])"); } return memberKeys; } public static int getCount(MemberQueryCondition condition, String login) { Client.ensureValid(condition.client); User.ensureValidUser(condition.client, login); Query<MemberEntity> q = getQuery(condition); return q.count(); } private static Query<MemberEntity> getQuery(MemberQueryCondition condition) { Client.ensureValid(condition.client); ensureNotNull(condition, "MemberLoadCondition cannot be null"); Query<MemberEntity> q = ofy(condition.client).load().type(MemberEntity.class); if ((null != condition.groupIds) && (condition.groupIds.size() != 0)) q = q.filter("groupIds in", condition.groupIds); if ((null != condition.practiceIds) && (condition.practiceIds.size() != 0)) q = q.filter("practiceIds in", condition.practiceIds); if ((null != condition.programTypeIds) && (condition.programTypeIds.size() != 0)) q = q.filter("programTypeIds in", condition.programTypeIds); if ((null != condition.programIds) && (condition.programIds.size() != 0)) q = q.filter("programIds in", condition.programIds); if ((null != condition.firstName3Chars) && (condition.firstName3Chars.size() != 0)) q = q.filter("firstName3Char in", condition.firstName3Chars); if (null != condition.email) q = q.filter("email", condition.email.toLowerCase()); if (null != condition.searchStr) { Set<String> searchTags = Utils.getQSTags(condition.searchStr); if (searchTags.isEmpty()) throw new APIException().status(Status.ERROR_RESOURCE_INCORRECT) .message("Atleast one word in the search string should be minimum 3 chars"); for (String tag : searchTags) { q = q.filter("qsTags", tag); } } if (null != condition.hasAccount) q = q.filter("hasAccount", condition.hasAccount); if ((null != condition.subscribedListIds) && !condition.subscribedListIds.isEmpty()) q = q.filter("subscribedListIds in", condition.subscribedListIds); if ((null != condition.unsubscribedListIds) && !condition.unsubscribedListIds.isEmpty()) q = q.filter("unsubscribedListIds in", condition.unsubscribedListIds); if ((null != condition.listIds) && !condition.listIds.isEmpty()) q = q.filter("listIds in", condition.listIds); if ((null != condition.subscribedGroupIds) && !condition.subscribedGroupIds.isEmpty()) q = q.filter("subscribedGroupIds in", condition.subscribedGroupIds); if ((null != condition.unsubscribedGroupIds) && !condition.unsubscribedGroupIds.isEmpty()) q = q.filter("unsubscribedGroupIds in", condition.unsubscribedGroupIds); if (null != condition.nameFirstChar && condition.nameFirstChar.length() != 0) q = q.filter("nameFirstChar", condition.nameFirstChar); if (null != condition.cursor) q = q.startAt(Cursor.fromWebSafeString(condition.cursor)); if (null != condition.maxResultSize) q = q.limit(condition.maxResultSize); return q; } public static TreeSet<String> getUnsubscribedEmails(String client, long groupId, String login) { Client.ensureValid(client); Group.safeGet(client, groupId); User.ensureValidUser(client, login); List<MemberEntity> memberEntities = ofy(client).load().type(MemberEntity.class) .filter("unsubscribedGroupIds", groupId) .project("email").list(); TreeSet<String> emails = new TreeSet<>(); for (MemberEntity memberEntity : memberEntities) { if (memberEntity.email != null) { emails.add(memberEntity.email); } } return emails; } }