/*******************************************************************************
* Copyright (c) 2014 antoniomariasanchez at gmail.com. All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0 which accompanies this distribution, and is
* available at http://www.gnu.org/licenses/gpl.html
*
* Contributors: antoniomaria - initial API and implementation
******************************************************************************/
package net.sf.gazpachoquest.services.core.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import net.sf.gazpachoquest.domain.core.AnonymousInvitation;
import net.sf.gazpachoquest.domain.core.MailMessage;
import net.sf.gazpachoquest.domain.core.MailMessageTemplate;
import net.sf.gazpachoquest.domain.core.PersonalInvitation;
import net.sf.gazpachoquest.domain.core.Questionnaire;
import net.sf.gazpachoquest.domain.core.QuestionnaireAnswers;
import net.sf.gazpachoquest.domain.core.QuestionnaireDefinition;
import net.sf.gazpachoquest.domain.core.Research;
import net.sf.gazpachoquest.domain.core.embeddables.MailMessageTemplateLanguageSettings;
import net.sf.gazpachoquest.domain.i18.MailMessageTemplateTranslation;
import net.sf.gazpachoquest.domain.permission.QuestionnairePermission;
import net.sf.gazpachoquest.domain.permission.ResearchPermission;
import net.sf.gazpachoquest.domain.user.Group;
import net.sf.gazpachoquest.domain.user.User;
import net.sf.gazpachoquest.qbe.SearchParameters;
import net.sf.gazpachoquest.repository.InvitationRepository;
import net.sf.gazpachoquest.repository.MailMessageRepository;
import net.sf.gazpachoquest.repository.QuestionnaireDefinitionRepository;
import net.sf.gazpachoquest.repository.QuestionnaireRepository;
import net.sf.gazpachoquest.repository.ResearchRepository;
import net.sf.gazpachoquest.repository.dynamic.QuestionnaireAnswersRepository;
import net.sf.gazpachoquest.repository.permission.QuestionnairePermissionRepository;
import net.sf.gazpachoquest.repository.permission.ResearchPermissionRepository;
import net.sf.gazpachoquest.repository.user.GroupRepository;
import net.sf.gazpachoquest.repository.user.UserRepository;
import net.sf.gazpachoquest.services.ResearchService;
import net.sf.gazpachoquest.types.EntityStatus;
import net.sf.gazpachoquest.types.InvitationStatus;
import net.sf.gazpachoquest.types.Language;
import net.sf.gazpachoquest.types.MailMessageTemplateType;
import net.sf.gazpachoquest.types.Perm;
import net.sf.gazpachoquest.types.ResearchAccessType;
import net.sf.gazpachoquest.util.RandomTokenGenerator;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.velocity.VelocityEngineFactoryBean;
import org.springframework.ui.velocity.VelocityEngineUtils;
import org.springframework.util.Assert;
@Service
public class ResearchServiceImpl extends AbstractPersistenceService<Research> implements ResearchService {
@Autowired
private InvitationRepository invitationRepository;
@Autowired
private MailMessageRepository mailMessageRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private GroupRepository groupRepository;
@Autowired
private QuestionnaireDefinitionRepository questionnaireDefinitionRepository;
@Autowired
private QuestionnaireRepository questionnaireRepository;
@Autowired
private RandomTokenGenerator tokenGenerator;
@Autowired
private VelocityEngineFactoryBean velocityFactory;
@Autowired
private ResearchPermissionRepository researchPermissionRepository;
@Autowired
private QuestionnaireAnswersRepository questionnaireAnswersRepository;
@Autowired
private QuestionnairePermissionRepository questionnairePermissionRepository;
@Autowired
public ResearchServiceImpl(final ResearchRepository repository) {
super(repository);
}
public Research saveOld(Research research) {
Research existing = null;
if (research.isNew()) {
research.setStatus(EntityStatus.DRAFT);
existing = repository.save(research);
} else {
existing = repository.findOne(research.getId());
existing.setStartDate(research.getStartDate());
existing.setExpirationDate(research.getExpirationDate());
}
return existing;
}
@Override
@Transactional(readOnly = false)
public Research save(Research research) {
Research existing = null;
if (research.isNew()) {
research.setStatus(EntityStatus.DRAFT);
existing = repository.save(research);
String token = tokenGenerator.generate();
AnonymousInvitation anonymousInvitation = AnonymousInvitation.with().research(research).token(token)
.status(InvitationStatus.ACTIVE).build();
invitationRepository.save(anonymousInvitation);
ResearchPermission permission = ResearchPermission.with().addPerm(Perm.READ).addPerm(Perm.UPDATE)
.addPerm(Perm.DELETE).user(getAuthenticatedUser()).target(research).build();
researchPermissionRepository.save(permission);
} else {
existing = repository.findOne(research.getId());
existing.setStartDate(research.getStartDate());
existing.setExpirationDate(research.getExpirationDate());
}
return existing;
}
@Override
@Transactional(readOnly = false)
public void changeStatus(Integer researchId, EntityStatus newStatus) {
Research research = repository.findOne(researchId);
if (research.getStatus().equals(newStatus)) {
return;
}
research.setStatus(newStatus);
for (Questionnaire questionnaire : research.getQuestionnaires()) {
questionnaire.setStatus(newStatus);
}
}
@Override
@Transactional(readOnly = false)
public void addRespondent(Integer researchId, User respondent) {
Assert.state(!respondent.isNew(), "Persist respondent before using inside a research.");
Research research = repository.findOne(researchId);
Assert.state(research.getType().equals(ResearchAccessType.BY_INVITATION),
"Tracked participants are not supported in anonymous researches");
QuestionnaireDefinition questionnaireDefinition = research.getQuestionnaireDefinition();
Questionnaire questionnaire = Questionnaire.with().status(research.getStatus()).research(research)
.questionnaireDefinition(questionnaireDefinition).respondent(respondent).build();
questionnaire = questionnaireRepository.save(questionnaire);
// Create answers holder
QuestionnaireAnswers questionnaireAnswers = new QuestionnaireAnswers();
questionnaireAnswers = questionnaireAnswersRepository.save(questionnaire.getQuestionnaireDefinition().getId(),
questionnaireAnswers);
questionnaire.setAnswersId(questionnaireAnswers.getId());
// Grant permissions over questionnaire to respondent
QuestionnairePermission permission = QuestionnairePermission.with().addPerm(Perm.READ).addPerm(Perm.UPDATE)
.user(respondent).target(questionnaire).build();
questionnairePermissionRepository.save(permission);
String token = tokenGenerator.generate();
PersonalInvitation personalInvitation = PersonalInvitation.with().research(research).token(token)
.status(InvitationStatus.ACTIVE).respondent(respondent).build();
invitationRepository.save(personalInvitation);
// Add the respondent to respondents groups
Group example = Group.with().name("Respondents").build();
Group respondentsGroup = groupRepository.findOneByExample(example, new SearchParameters()).orElseThrow(
() -> new EmptyResultDataAccessException(String.format("No %s entity with name %s found!", Group.class,
"Respondents"), 1));
if (groupRepository.isUserInGroup(respondent.getId(), "Respondents") == 0) {
respondentsGroup.assignUser(respondent);
}
}
@Override
@Transactional(readOnly = false)
public Research save(Research research, Set<QuestionnaireDefinition> questionnaireDefinitions, Set<User> respondents) {
research = saveOld(research);
if (ResearchAccessType.BY_INVITATION.equals(research.getType())) {
for (QuestionnaireDefinition questionnaireDefinition : questionnaireDefinitions) {
questionnaireDefinition = questionnaireDefinitionRepository.findOne(questionnaireDefinition.getId());
Map<MailMessageTemplateType, MailMessageTemplate> templates = questionnaireDefinition
.getMailTemplates();
MailMessageTemplate invitationTemplate = templates.get(MailMessageTemplateType.INVITATION);
Group example = Group.with().name("Respondents").build();
Group respondentsGroup = groupRepository.findOneByExample(example, new SearchParameters()).orElseThrow(
() -> new EmptyResultDataAccessException(String.format("No %s entity with name %s found!", Group.class,
"Respondents"), 1));
for (User respondent : respondents) {
Assert.state(!respondent.isNew(), "Persist all respondents before starting a research.");
Questionnaire questionnaire = Questionnaire.with().status(EntityStatus.CONFIRMED)
.research(research).questionnaireDefinition(questionnaireDefinition).respondent(respondent)
.build();
questionnaire = questionnaireRepository.save(questionnaire);
// Create answers holder
QuestionnaireAnswers questionnaireAnswers = new QuestionnaireAnswers();
questionnaireAnswers = questionnaireAnswersRepository.save(questionnaire
.getQuestionnaireDefinition().getId(), questionnaireAnswers);
questionnaire.setAnswersId(questionnaireAnswers.getId());
String token = tokenGenerator.generate();
respondent = userRepository.findOne(respondent.getId());
// Grant permissions over questionnaire to respondent
QuestionnairePermission permission = QuestionnairePermission.with().addPerm(Perm.READ)
.addPerm(Perm.UPDATE).user(respondent).target(questionnaire).build();
questionnairePermissionRepository.save(permission);
PersonalInvitation personalInvitation = PersonalInvitation.with().research(research).token(token)
.status(InvitationStatus.ACTIVE).respondent(respondent).build();
invitationRepository.save(personalInvitation);
MailMessage mailMessage = composeMailMessage(invitationTemplate, respondent, token);
mailMessageRepository.save(mailMessage);
if (groupRepository.isUserInGroup(respondent.getId(), "Respondents") == 0) {
respondentsGroup.assignUser(respondent);
}
}
}
} else {
Assert.notEmpty(questionnaireDefinitions, "questionnairDefinitions required");
Assert.state(questionnaireDefinitions.size() == 1,
"Only one questionnairDefinitions supported for Open Access researches");
String token = tokenGenerator.generate();
AnonymousInvitation anonymousInvitation = AnonymousInvitation.with().research(research).token(token)
.status(InvitationStatus.ACTIVE).build();
invitationRepository.save(anonymousInvitation);
}
ResearchPermission permission = ResearchPermission.with().addPerm(Perm.READ).addPerm(Perm.UPDATE)
.addPerm(Perm.DELETE).user(getAuthenticatedUser()).target(research).build();
researchPermissionRepository.save(permission);
return research;
}
private MailMessage composeMailMessage(final MailMessageTemplate mailMessageTemplate, final User respondent,
final String surveyLinkToken) {
Map<String, Object> model = new HashMap<>();
model.put("lastname", StringUtils.defaultIfBlank(respondent.getSurname(), ""));
model.put("firstname", StringUtils.defaultIfBlank(respondent.getGivenNames(), ""));
model.put("gender", respondent.getGender());
model.put("link", "http://localhost:8080/questionaires-ui/token=" + surveyLinkToken);
Language preferredLanguage = respondent.getPreferredLanguage();
StringBuilder templateLocation = new StringBuilder().append(mailMessageTemplate.getId());
if (preferredLanguage != null) {
templateLocation.append("/");
templateLocation.append(preferredLanguage);
}
VelocityEngine velocityEngine = velocityFactory.getObject();
String body = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templateLocation.toString(), "UTF-8",
model);
MailMessageTemplateLanguageSettings languageSettings = mailMessageTemplate.getLanguageSettings();
if (preferredLanguage != null && !preferredLanguage.equals(mailMessageTemplate.getLanguage())) {
MailMessageTemplateTranslation preferedTranslation = mailMessageTemplate.getTranslations().get(
preferredLanguage);
if (preferedTranslation != null) {
languageSettings = preferedTranslation.getLanguageSettings();
}
}
MailMessage mailMessage = MailMessage.with().subject(languageSettings.getSubject()).to(respondent.getEmail())
.replyTo(mailMessageTemplate.getReplyTo()).from(mailMessageTemplate.getFromAddress()).text(body)
.build();
return mailMessage;
}
}