package com.secretpal.model; import java.security.SecureRandom; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSMutableArray; import er.extensions.eof.ERXEC; import er.extensions.eof.ERXEOControlUtilities; public class SPEvent extends _SPEvent { private static Logger log = LoggerFactory.getLogger(SPEvent.class); public boolean hasAssignedSecretPals() { return secretPals().count() > 0; } public boolean hasNotAssignedSecretPals() { return !hasAssignedSecretPals(); } public void reassignSecretPals() { // MS: deleting all the existing pals means that this implementation can be a lot easier, but it was originally // written to NOT do this, which is why it's so weird and non-deterministic. deleteAllSecretPalsRelationships(); NSArray<SPPerson> allPeople = SPMembership.PERSON.arrayValueInObject(group().memberships()); NSArray<SPPerson> eligibleGivers = _eligibleGivers(allPeople); NSMutableArray<SPPerson> consumedReceivers = new NSMutableArray<>(); log.info("{} eligible givers out of {} people", eligibleGivers.count(), allPeople.count()); SecureRandom random = new SecureRandom(); long a = System.currentTimeMillis(); int numberOfAttempts = 0; int maxNumberOfAttempts = 100; boolean doneWithSecretPals = false; while (!doneWithSecretPals && numberOfAttempts < maxNumberOfAttempts) { log.info("attempt #{} of {}", numberOfAttempts, maxNumberOfAttempts); EOEditingContext nestedEditingContext = ERXEC.newEditingContext(editingContext()); nestedEditingContext.lock(); SPEvent localEvent = localInstanceIn(nestedEditingContext); try { for (SPPerson selectedGiver : eligibleGivers) { NSArray<SPPerson> eligibleReceivers = _eligibleReceiversForPerson(selectedGiver, allPeople, consumedReceivers); if (eligibleReceivers.count() == 0) { throw new IllegalStateException(" Try again -- no receivers for " + selectedGiver.name()); } int selectedReceiverNum = Math.abs(random.nextInt()) % eligibleReceivers.count(); SPPerson selectedReceiver = eligibleReceivers.objectAtIndex(selectedReceiverNum); log.info(" {}=>{}, {}, {}", selectedGiver.name(), selectedReceiver.name(), selectedGiver, selectedReceiver); SPSecretPal.createSPSecretPal(nestedEditingContext, localEvent, selectedGiver.localInstanceIn(nestedEditingContext), selectedReceiver.localInstanceIn(nestedEditingContext)); consumedReceivers.addObject(selectedReceiver); } nestedEditingContext.saveChanges(); doneWithSecretPals = true; } catch (IllegalStateException e) { numberOfAttempts ++; } finally { nestedEditingContext.unlock(); nestedEditingContext.dispose(); } } log.info("done in {}ms", System.currentTimeMillis() - a); if (!doneWithSecretPals) { throw new IllegalStateException("Failed to assign secret pals after " + numberOfAttempts + " attempts."); } } public boolean canEdit(SPPerson currentPerson) { return currentPerson.admin().booleanValue() || ERXEOControlUtilities.eoEquals(group().owner(), currentPerson); } public NSArray<SPPerson> _eligibleGivers(NSArray<SPPerson> allPeople) { NSMutableArray<SPPerson> eligibleGivers = new NSMutableArray<>(); for (SPPerson possibleGiver : allPeople) { NSArray<SPPerson> noNoPeople = SPNoNoPal.RECEIVER.arrayValueInObject(noNoPalsForPerson(possibleGiver)); // If someone has NO possible receivers right at the start, just leave them out ... This is to support // defining babies that can't buy for anyone if (noNoPeople.count() != allPeople.count() - 1) { eligibleGivers.addObject(possibleGiver); } } return eligibleGivers; } public NSArray<SPPerson> _eligibleReceiversForPerson(SPPerson giver, NSArray<SPPerson> allPeople, NSArray<SPPerson> consumedReceivers) { NSMutableArray<SPPerson> eligibleReceivers = new NSMutableArray<>(); NSArray<SPPerson> noNoPeople = SPNoNoPal.RECEIVER.arrayValueInObject(noNoPalsForPerson(giver)); for (SPPerson possibleReceiver : allPeople) { if (!giver.equals(possibleReceiver) && !consumedReceivers.containsObject(possibleReceiver) && !noNoPeople.containsObject(possibleReceiver)) { eligibleReceivers.addObject(possibleReceiver); } } return eligibleReceivers; } public NSArray<SPSecretPal> secretPalsForPerson(SPPerson person) { return SPSecretPal.GIVER.is(person).filtered(secretPals()); } public NSArray<SPNoNoPal> noNoPalsForPerson(SPPerson person) { return SPNoNoPal.GIVER.is(person).filtered(noNoPals()); } public NSArray<SPPerson> noNoPersonPossibilitiesForPerson(SPPerson person) { NSMutableArray<SPPerson> noNoPalPossibilities = new NSMutableArray<>(); NSArray<SPPerson> noNoPals = SPNoNoPal.RECEIVER.arrayValueInObject(noNoPalsForPerson(person)); for (SPMembership membership : group().memberships()) { if (!membership.person().equals(person) && !noNoPals.containsObject(membership.person())) { noNoPalPossibilities.addObject(membership.person()); } } return noNoPalPossibilities; } }