use of org.sagebionetworks.bridge.models.subpopulations.StudyConsentView in project BridgeServer2 by Sage-Bionetworks.
the class ConsentService method consentToResearch.
/**
* Consent this user to research. User will be updated to reflect consent. This method will ensure the
* user is not already consented to this subpopulation, but it does not validate that the user is a
* validate member of this subpopulation (that is checked in the controller). Will optionally send
* a signed copy of the consent to the user via email or phone (whichever is verified).
*
* @param sendSignedConsent
* if true, send the consent document to the user's email address
* @throws EntityNotFoundException
* if the subpopulation is not part of the app
* @throws InvalidEntityException
* if the user is not old enough to participate in the app (based on birthdate declared in signature)
* @throws EntityAlreadyExistsException
* if the user has already signed the consent for this subpopulation
*/
public void consentToResearch(App app, SubpopulationGuid subpopGuid, StudyParticipant participant, ConsentSignature consentSignature, SharingScope sharingScope, boolean sendSignedConsent) {
checkNotNull(app);
checkNotNull(subpopGuid);
checkNotNull(participant);
checkNotNull(consentSignature);
checkNotNull(sharingScope);
ConsentSignatureValidator validator = new ConsentSignatureValidator(app.getMinAgeOfConsent());
Validate.entityThrowingException(validator, consentSignature);
Subpopulation subpop = subpopService.getSubpopulation(app.getIdentifier(), subpopGuid);
StudyConsentView studyConsent = studyConsentService.getActiveConsent(subpop);
// If there's a signature to the current and active consent, user cannot consent again. They can sign
// any other consent, including more recent consents.
Account account = accountService.getAccount(AccountId.forId(app.getIdentifier(), participant.getId())).orElseThrow(() -> new EntityNotFoundException(Account.class));
ConsentSignature active = account.getActiveConsentSignature(subpopGuid);
if (active != null && active.getConsentCreatedOn() == studyConsent.getCreatedOn()) {
throw new EntityAlreadyExistsException(ConsentSignature.class, null);
}
// Add the consent creation timestamp and clear the withdrewOn timestamp, as some tests copy signatures
// that contain this. As with all builders, order of with* calls matters here.
ConsentSignature withConsentCreatedOnSignature = new ConsentSignature.Builder().withConsentSignature(consentSignature).withWithdrewOn(null).withConsentCreatedOn(studyConsent.getCreatedOn()).build();
// Add consent signature to the list of signatures, save account.
List<ConsentSignature> consentListCopy = new ArrayList<>(account.getConsentSignatureHistory(subpopGuid));
consentListCopy.add(withConsentCreatedOnSignature);
account.setConsentSignatureHistory(subpopGuid, consentListCopy);
account.setSharingScope(sharingScope);
account.getDataGroups().addAll(subpop.getDataGroupsAssignedWhileConsented());
// declare a study ID.
if (subpop.getStudyId() != null) {
Enrollment newEnrollment = Enrollment.create(app.getIdentifier(), subpop.getStudyId(), account.getId());
enrollmentService.addEnrollment(account, newEnrollment, true);
}
accountService.updateAccount(account);
// Administrative actions, almost exclusively for testing, will send no consent documents
if (sendSignedConsent) {
ConsentPdf consentPdf = new ConsentPdf(app, participant, withConsentCreatedOnSignature, sharingScope, studyConsent.getDocumentContent(), xmlTemplateWithSignatureBlock);
boolean verifiedEmail = (participant.getEmail() != null && Boolean.TRUE.equals(participant.getEmailVerified()));
boolean verifiedPhone = (participant.getPhone() != null && Boolean.TRUE.equals(participant.getPhoneVerified()));
// Send an email to the user if they have an email address and we're not suppressing the send,
// and/or to any app consent administrators.
Set<String> recipientEmails = Sets.newHashSet();
if (verifiedEmail && !subpop.isAutoSendConsentSuppressed()) {
recipientEmails.add(participant.getEmail());
}
addStudyConsentRecipients(app, recipientEmails);
if (!recipientEmails.isEmpty()) {
TemplateRevision revision = templateService.getRevisionForUser(app, EMAIL_SIGNED_CONSENT);
BasicEmailProvider.Builder consentEmailBuilder = new BasicEmailProvider.Builder().withApp(app).withParticipant(participant).withTemplateRevision(revision).withBinaryAttachment("consent.pdf", MimeType.PDF, consentPdf.getBytes()).withType(EmailType.SIGN_CONSENT);
for (String recipientEmail : recipientEmails) {
consentEmailBuilder.withRecipientEmail(recipientEmail);
}
sendMailService.sendEmail(consentEmailBuilder.build());
}
// Otherwise if there's no verified email but there is a phone and we're not suppressing, send it there
if (!subpop.isAutoSendConsentSuppressed() && !verifiedEmail && verifiedPhone) {
sendConsentViaSMS(app, subpop, participant, consentPdf);
}
}
}
use of org.sagebionetworks.bridge.models.subpopulations.StudyConsentView in project BridgeServer2 by Sage-Bionetworks.
the class StudyConsentService method addConsent.
/**
* Adds a new consent document to the study, and sets that consent document as active.
*
* @param subpopGuid
* the subpopulation associated with this consent
* @param form
* form filled out by researcher including the path to the consent document and the minimum age required
* to consent.
* @return the added consent document of type StudyConsent along with its document content
*/
public StudyConsentView addConsent(SubpopulationGuid subpopGuid, StudyConsentForm form) {
checkNotNull(subpopGuid);
checkNotNull(form);
String sanitizedContent = sanitizeHTML(form.getDocumentContent());
Validate.entityThrowingException(validator, new StudyConsentForm(sanitizedContent));
sanitizedContent = appendSignatureBlockIfNeeded(sanitizedContent);
long createdOn = DateUtils.getCurrentMillisFromEpoch();
String storagePath = subpopGuid.getGuid() + "." + createdOn;
logger.info("Accessing bucket: " + consentsBucket + " with storagePath: " + storagePath);
try {
Stopwatch stopwatch = Stopwatch.createStarted();
s3Helper.writeBytesToS3(consentsBucket, storagePath, sanitizedContent.getBytes(defaultCharset()));
logger.info("Finished writing consent to bucket " + consentsBucket + " storagePath " + storagePath + " (" + sanitizedContent.length() + " chars) in " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
StudyConsent consent = studyConsentDao.addConsent(subpopGuid, storagePath, createdOn);
return new StudyConsentView(consent, sanitizedContent);
} catch (Throwable t) {
throw new BridgeServiceException(t);
}
}
use of org.sagebionetworks.bridge.models.subpopulations.StudyConsentView in project BridgeServer2 by Sage-Bionetworks.
the class StudyConsentService method publishConsent.
/**
* Set the specified consent document as active, setting all other consent documents
* as inactive.
*
* @param app
* app for this consent
* @param subpop
* the subpopulation associated with this consent
* @param timestamp
* time the consent document was added to the database.
* @return the activated consent document along with its document content
*/
public StudyConsentView publishConsent(App app, Subpopulation subpop, long timestamp) {
checkNotNull(app);
checkNotNull(subpop);
checkArgument(timestamp > 0, "Timestamp is 0");
StudyConsent consent = studyConsentDao.getConsent(subpop.getGuid(), timestamp);
if (consent == null) {
throw new EntityNotFoundException(StudyConsent.class);
}
// Only if we can publish the document, do we mark it as published in the database.
String documentContent = loadDocumentContent(consent);
try {
publishFormatsToS3(app, subpop.getGuid(), documentContent);
subpop.setPublishedConsentCreatedOn(timestamp);
subpopService.updateSubpopulation(app, subpop);
} catch (IOException | DocumentException | XRRuntimeException e) {
throw new BridgeServiceException(e.getMessage());
}
return new StudyConsentView(consent, documentContent);
}
use of org.sagebionetworks.bridge.models.subpopulations.StudyConsentView in project BridgeServer2 by Sage-Bionetworks.
the class StudyConsentService method getMostRecentConsent.
/**
* Gets the most recently created consent document for the study.
*
* @param subpopGuid
* the subpopulation associated with this consent
* @return the most recent StudyConsent along with its document content
*/
public StudyConsentView getMostRecentConsent(SubpopulationGuid subpopGuid) {
checkNotNull(subpopGuid);
StudyConsent consent = studyConsentDao.getMostRecentConsent(subpopGuid);
if (consent == null) {
throw new EntityNotFoundException(StudyConsent.class);
}
String documentContent = loadDocumentContent(consent);
return new StudyConsentView(consent, documentContent);
}
use of org.sagebionetworks.bridge.models.subpopulations.StudyConsentView in project BridgeServer2 by Sage-Bionetworks.
the class SubpopulationService method createSubpopulation.
/**
* Create subpopulation.
* @param app
* @param subpop
* @return
*/
public Subpopulation createSubpopulation(App app, Subpopulation subpop) {
checkNotNull(app);
checkNotNull(subpop);
subpop.setGuidString(BridgeUtils.generateGuid());
subpop.setAppId(app.getIdentifier());
Set<String> studyIds = studyService.getStudyIds(app.getIdentifier());
Validator validator = new SubpopulationValidator(app.getDataGroups(), studyIds);
Validate.entityThrowingException(validator, subpop);
Subpopulation created = subpopDao.createSubpopulation(subpop);
// Create a default consent for this subpopulation.
StudyConsentView view = studyConsentService.addConsent(subpop.getGuid(), defaultConsentDocument);
studyConsentService.publishConsent(app, subpop, view.getCreatedOn());
cacheProvider.removeObject(CacheKey.subpopList(app.getIdentifier()));
return created;
}
Aggregations