package crmdna.attendance;
import com.googlecode.objectify.Work;
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.counter.Counter;
import crmdna.counter.Counter.CounterType;
import crmdna.member.Member;
import crmdna.member.MemberLoader;
import crmdna.member.MemberProp;
import crmdna.member.MemberQueryCondition;
import crmdna.program.Program;
import crmdna.program.ProgramProp;
import crmdna.user.User;
import crmdna.user.User.GroupLevelPrivilege;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static crmdna.common.OfyService.ofy;
public class AttendanceDefaultImpl implements IAttendance {
protected String client;
AttendanceDefaultImpl(String client) {
Client.ensureValid(client);
this.client = client;
}
@Override
public int checkin(final long memberId, final long programId, final int sessionDateYYYYMMDD,
final int batchNo, final String login) {
ensureValidCheckInInputs(memberId, programId, sessionDateYYYYMMDD, batchNo, login);
final String key = getCheckInKey(programId, memberId);
CheckInProp checkInProp = ofy(client).transact(new Work<CheckInProp>() {
@Override
public CheckInProp run() {
CheckInEntity checkInEntity = ofy(client).load().type(CheckInEntity.class).id(key).now();
if (null == checkInEntity) {
checkInEntity = new CheckInEntity();
checkInEntity.key = key;
checkInEntity.programId = programId;
checkInEntity.memberId = memberId;
} else {
CheckInRecord existing = checkInEntity.toProp().getCheckInRecord(sessionDateYYYYMMDD);
if ((null != existing) && (existing.isCheckin)) {
String errMessage =
"Already checked in by "
+ existing.login
+ " "
+ DateUtils.getDateDiff(existing.timestamp.getTime() / 1000,
new Date().getTime() / 1000) + " ago";
throw new APIException().status(Status.ERROR_RESOURCE_ALREADY_EXISTS).message(
errMessage);
}
}
CheckInRecord record = new CheckInRecord();
record.batchNo = batchNo;
record.isCheckin = true;
record.login = login;
record.sessionDateYYYYMMDD = sessionDateYYYYMMDD;
record.timestamp = new Date();
checkInEntity.checkInRecords.add(record);
ofy(client).save().entity(checkInEntity).now();
return checkInEntity.toProp();
}
});
ProgramProp programProp = Program.safeGet(client, programId).toProp(client);
if (checkInProp.getNumCheckins() == programProp.getNumSessions())
Member.addOrDeleteProgram(client, memberId, programId, true, login);
else {
Member.addOrDeleteProgram(client, memberId, programId, false, login);
}
String counterKey = getCheckInCounterKey(programId, sessionDateYYYYMMDD, batchNo);
long numCheckIns = Counter.incrementAndGetCurrentCount(client, CounterType.CHECKIN, counterKey);
return (int) numCheckIns;
}
@Override
public int checkout(final long memberId, final long programId, final int sessionDateYYYYMMDD,
final String login) {
ensureValidCheckInInputs(memberId, programId, sessionDateYYYYMMDD, 1, login);
final String key = getCheckInKey(programId, memberId);
Integer batchNo = ofy(client).transact(new Work<Integer>() {
@Override
public Integer run() {
CheckInEntity checkInEntity = ofy(client).load().type(CheckInEntity.class).id(key).now();
if (null == checkInEntity)
throw new APIException().status(Status.ERROR_RESOURCE_NOT_FOUND).message(
"Member [" + memberId + "] is not checked in");
CheckInRecord existing = checkInEntity.toProp().getCheckInRecord(sessionDateYYYYMMDD);
if (null == existing)
throw new APIException().status(Status.ERROR_RESOURCE_NOT_FOUND).message(
"Member [" + memberId + "] is not checked in");
if (!existing.isCheckin) {
String errMessage =
"Already checked out by "
+ existing.login
+ " "
+ DateUtils.getDateDiff(existing.timestamp.getTime() / 1000,
new Date().getTime() / 1000) + " ago";
throw new APIException().status(Status.ERROR_RESOURCE_NOT_FOUND).message(errMessage);
}
CheckInRecord record = new CheckInRecord();
record.batchNo = existing.batchNo;
record.isCheckin = false;
record.login = login;
record.sessionDateYYYYMMDD = sessionDateYYYYMMDD;
record.timestamp = new Date();
checkInEntity.checkInRecords.add(record);
ofy(client).save().entity(checkInEntity);
return existing.batchNo;
}
});
// remove the program from member
Member.addOrDeleteProgram(client, memberId, programId, false, login);
String counterKey = getCheckInCounterKey(programId, sessionDateYYYYMMDD, batchNo);
long numCheckIns =
Counter.incrementAndGetCurrentCount(client, CounterType.CHECKIN, counterKey, -1);
return (int) numCheckIns;
}
@Override
public int getNumCheckins(long programId, int sessionDateYYYYMMDD, int batchNo) {
ensureValidCheckInInputs(programId, sessionDateYYYYMMDD, batchNo);
String counterKey = getCheckInCounterKey(programId, sessionDateYYYYMMDD, batchNo);
long numCheckIns = Counter.getCount(client, CounterType.CHECKIN, counterKey);
return (int) numCheckIns;
}
public List<CheckInStatusProp> getCheckInStatus(long programId, List<Long> memberIds,
int sessionDateYYYYMMDD) {
List<CheckInStatusProp> result = new ArrayList<>();
// result will have the same size as memberIds
List<String> checkInKeys = new ArrayList<>();
for (int i = 0; i < memberIds.size(); i++) {
CheckInStatusProp checkInStatusProp = new CheckInStatusProp();
checkInStatusProp.checkedIn = false;
checkInStatusProp.userFriendlyMessage = "Not checked in";
result.add(checkInStatusProp);
checkInKeys.add(getCheckInKey(programId, memberIds.get(i)));
}
Map<String, CheckInEntity> map = ofy(client).load().type(CheckInEntity.class).ids(checkInKeys);
for (int i = 0; i < memberIds.size(); i++) {
String checkinKey = checkInKeys.get(i);
if (map.containsKey(checkinKey)) {
CheckInEntity checkInEntity = map.get(checkinKey);
if (checkInEntity != null) {
CheckInRecord existing = checkInEntity.toProp().getCheckInRecord(sessionDateYYYYMMDD);
if ((existing != null) && existing.isCheckin) {
CheckInStatusProp checkInStatusProp = result.get(i);
checkInStatusProp.checkedIn = true;
checkInStatusProp.userFriendlyMessage =
"Checked in by "
+ existing.login
+ " "
+ DateUtils.getDateDiff(existing.timestamp.getTime() / 1000,
new Date().getTime() / 1000) + " ago";
}
}
}
}
return result;
}
@Override
public List<CheckInMemberProp> getMembersForCheckIn(String searchStr, long programId,
int sessionDateYYYYMMDD, int maxResultSize, String login) {
Client.ensureValid(client);
User.ensureValidUser(client, login);
if ((searchStr == null) || (searchStr.length() < 3))
Utils.throwIncorrectSpecException("Atleast 3 chars required for search");
Program.safeGet(client, programId).toProp(client);
MemberQueryCondition qc = new MemberQueryCondition(client, maxResultSize);
qc.searchStr = searchStr.toLowerCase();
List<MemberProp> memberProps = MemberLoader.querySortedProps(qc, login);
System.out.println("memberProps.size(): " + memberProps.size());
List<CheckInMemberProp> checkInMemberProps = new ArrayList<>();
List<Long> memberIds = new ArrayList<>();
for (MemberProp memberProp : memberProps) {
memberIds.add(memberProp.memberId);
}
List<CheckInStatusProp> checkInStatus =
getCheckInStatus(programId, memberIds, sessionDateYYYYMMDD);
for (int i = 0; i < memberIds.size(); i++) {
CheckInMemberProp checkInMemberProp = new CheckInMemberProp();
MemberProp memberProp = memberProps.get(i);
checkInMemberProp.memberId = memberProp.memberId;
checkInMemberProp.name = memberProp.contact.getName();
checkInMemberProp.email = memberProp.contact.email;
checkInMemberProp.phoneNos = memberProp.contact.getPhoneNos();
checkInMemberProp.practiceIds = memberProp.practiceIds;
checkInMemberProp.allow = true;
CheckInStatusProp checkInStatusProp = checkInStatus.get(i);
if (checkInStatusProp.checkedIn) {
checkInMemberProp.allow = false;
checkInMemberProp.notAllowingReason = checkInStatusProp.userFriendlyMessage;
}
checkInMemberProps.add(checkInMemberProp);
}
CheckInMemberProp.populatePractices(client, checkInMemberProps);
// TODO: handle the case where the program requires registration
// add fields bool requireRegistration and Set<Long>
// registeredProgramIds in member entity
return checkInMemberProps;
}
protected void ensureValidCheckInInputs(long memberId, long programId, int sessionDateYYYYMMDD,
int batchNo, String login) {
ProgramProp programProp = Program.safeGet(client, programId).toProp(client);
User.ensureGroupLevelPrivilege(client, programProp.groupProp.groupId, login,
GroupLevelPrivilege.CHECK_IN);
MemberLoader.safeGet(client, memberId, login);
DateUtils.ensureFormatYYYYMMDD(sessionDateYYYYMMDD);
if ((sessionDateYYYYMMDD < programProp.startYYYYMMDD)
|| (sessionDateYYYYMMDD > programProp.endYYYYMMDD))
Utils.throwIncorrectSpecException("session date [" + sessionDateYYYYMMDD
+ "] should be between program start [" + programProp.startYYYYMMDD + "] and end date ["
+ programProp.endYYYYMMDD + "]");
if (batchNo > programProp.numBatches)
Utils.throwIncorrectSpecException("Batch number [" + batchNo
+ "] is greater than total number of batches [" + programProp.numBatches + "]");
}
protected void ensureValidCheckInInputs(long programId, int sessionDateYYYYMMDD, int batchNo) {
ProgramProp programProp = Program.safeGet(client, programId).toProp(client);
DateUtils.ensureFormatYYYYMMDD(sessionDateYYYYMMDD);
if ((sessionDateYYYYMMDD < programProp.startYYYYMMDD)
|| (sessionDateYYYYMMDD > programProp.endYYYYMMDD))
Utils.throwIncorrectSpecException("session date [" + sessionDateYYYYMMDD
+ "] should be between program start [" + programProp.startYYYYMMDD + "] and end date ["
+ programProp.endYYYYMMDD + "]");
if (batchNo > programProp.numBatches)
Utils.throwIncorrectSpecException("Batch number [" + batchNo
+ "] is greater than total number of batches [" + programProp.numBatches + "]");
}
protected String getCheckInKey(long programId, long memberId) {
return programId + "_" + memberId;
}
protected String getCheckInCounterKey(long programId, long sessionDateYYYYMMDD, int batchNo) {
return programId + "_" + sessionDateYYYYMMDD + "_" + batchNo;
}
}