Search in sources :

Example 1 with StudyConsentView

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);
        }
    }
}
Also used : Account(org.sagebionetworks.bridge.models.accounts.Account) BasicEmailProvider(org.sagebionetworks.bridge.services.email.BasicEmailProvider) EntityAlreadyExistsException(org.sagebionetworks.bridge.exceptions.EntityAlreadyExistsException) ConsentSignatureValidator(org.sagebionetworks.bridge.validators.ConsentSignatureValidator) StudyConsentView(org.sagebionetworks.bridge.models.subpopulations.StudyConsentView) ArrayList(java.util.ArrayList) TemplateRevision(org.sagebionetworks.bridge.models.templates.TemplateRevision) EntityNotFoundException(org.sagebionetworks.bridge.exceptions.EntityNotFoundException) ConsentSignature(org.sagebionetworks.bridge.models.subpopulations.ConsentSignature) Subpopulation(org.sagebionetworks.bridge.models.subpopulations.Subpopulation) Enrollment(org.sagebionetworks.bridge.models.studies.Enrollment)

Example 2 with StudyConsentView

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);
    }
}
Also used : StudyConsent(org.sagebionetworks.bridge.models.subpopulations.StudyConsent) StudyConsentView(org.sagebionetworks.bridge.models.subpopulations.StudyConsentView) Stopwatch(com.google.common.base.Stopwatch) BridgeServiceException(org.sagebionetworks.bridge.exceptions.BridgeServiceException) StudyConsentForm(org.sagebionetworks.bridge.models.subpopulations.StudyConsentForm)

Example 3 with StudyConsentView

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);
}
Also used : StudyConsent(org.sagebionetworks.bridge.models.subpopulations.StudyConsent) XRRuntimeException(org.xhtmlrenderer.util.XRRuntimeException) DocumentException(com.lowagie.text.DocumentException) StudyConsentView(org.sagebionetworks.bridge.models.subpopulations.StudyConsentView) BridgeServiceException(org.sagebionetworks.bridge.exceptions.BridgeServiceException) EntityNotFoundException(org.sagebionetworks.bridge.exceptions.EntityNotFoundException) IOException(java.io.IOException)

Example 4 with StudyConsentView

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);
}
Also used : StudyConsent(org.sagebionetworks.bridge.models.subpopulations.StudyConsent) StudyConsentView(org.sagebionetworks.bridge.models.subpopulations.StudyConsentView) EntityNotFoundException(org.sagebionetworks.bridge.exceptions.EntityNotFoundException)

Example 5 with StudyConsentView

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;
}
Also used : Subpopulation(org.sagebionetworks.bridge.models.subpopulations.Subpopulation) StudyConsentView(org.sagebionetworks.bridge.models.subpopulations.StudyConsentView) SubpopulationValidator(org.sagebionetworks.bridge.validators.SubpopulationValidator) Validator(org.springframework.validation.Validator) SubpopulationValidator(org.sagebionetworks.bridge.validators.SubpopulationValidator)

Aggregations

StudyConsentView (org.sagebionetworks.bridge.models.subpopulations.StudyConsentView)24 StudyConsent (org.sagebionetworks.bridge.models.subpopulations.StudyConsent)16 Test (org.testng.annotations.Test)16 StudyConsentForm (org.sagebionetworks.bridge.models.subpopulations.StudyConsentForm)9 Subpopulation (org.sagebionetworks.bridge.models.subpopulations.Subpopulation)6 DynamoStudyConsent1 (org.sagebionetworks.bridge.dynamodb.DynamoStudyConsent1)5 EntityNotFoundException (org.sagebionetworks.bridge.exceptions.EntityNotFoundException)4 DynamoApp (org.sagebionetworks.bridge.dynamodb.DynamoApp)2 BridgeServiceException (org.sagebionetworks.bridge.exceptions.BridgeServiceException)2 App (org.sagebionetworks.bridge.models.apps.App)2 SubpopulationGuid (org.sagebionetworks.bridge.models.subpopulations.SubpopulationGuid)2 ObjectMetadata (com.amazonaws.services.s3.model.ObjectMetadata)1 PutObjectRequest (com.amazonaws.services.s3.model.PutObjectRequest)1 AmazonSimpleEmailServiceClient (com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient)1 SendRawEmailRequest (com.amazonaws.services.simpleemail.model.SendRawEmailRequest)1 Stopwatch (com.google.common.base.Stopwatch)1 DocumentException (com.lowagie.text.DocumentException)1 FileInputStream (java.io.FileInputStream)1 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1