package net.sf.egonet.persistence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import net.sf.egonet.model.Alter;
import net.sf.egonet.model.Answer;
import net.sf.egonet.model.Interview;
import net.sf.egonet.model.Question;
import net.sf.egonet.model.Question.QuestionType;
import net.sf.egonet.persistence.Expressions.EvaluationContext;
import net.sf.egonet.web.page.InterviewingAlterPage;
import net.sf.egonet.web.page.InterviewingAlterPairPage;
import net.sf.egonet.web.page.InterviewingNetworkPage;
import net.sf.egonet.web.panel.InterviewNavigationPanel;
import net.sf.egonet.web.panel.InterviewNavigationPanel.InterviewLink;
import net.sf.functionalj.tuple.Pair;
import net.sf.functionalj.tuple.PairUni;
import net.sf.functionalj.tuple.Triple;
import net.sf.functionalj.tuple.TripleUni;
import org.hibernate.Session;
import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
public class Interviewing {
public static Interview findOrCreateMatchingInterviewForStudy(
final Long studyId, final List<Answer> egoIdAnswers)
{
return DB.withTx(new Function<Session,Interview>(){
public Interview apply(Session session) {
Multimap<Long,Answer> answersByInterview = ArrayListMultimap.create();
for(Answer answer : Answers.getAnswersForStudy(session,studyId,QuestionType.EGO_ID)) {
answersByInterview.put(answer.getInterviewId(), answer);
}
for(Long interviewId : answersByInterview.keySet()) {
Collection<Answer> interviewAnswers = answersByInterview.get(interviewId);
if(answersMatch(egoIdAnswers,interviewAnswers)) {
return Interviews.getInterview(session, interviewId);
}
}
// If reach this point without finding a match, time to start a new interview.
Interview interview = new Interview(Studies.getStudy(studyId));
Long interviewId = (Long) session.save(interview);
interview.setId(interviewId);
List<Question> egoIdQuestions =
Questions.getQuestionsForStudy(session, studyId, QuestionType.EGO_ID);
for(Question question : egoIdQuestions) {
for(Answer answer : egoIdAnswers) {
if(answer.getQuestionId().equals(question.getId())) {
DB.save(new Answer(interview,question,answer.getValue()));
}
}
}
return interview;
}
private Boolean answersMatch(Collection<Answer> ego1Answers, Collection<Answer> ego2Answers) {
if (ego1Answers.isEmpty() && !ego2Answers.isEmpty())
{
return false;
}
if (!ego1Answers.isEmpty() && ego2Answers.isEmpty())
{
return false;
}
for(Answer ego1Answer : ego1Answers) {
for(Answer ego2Answer : ego2Answers) {
if(ego1Answer.getQuestionId().equals(ego2Answer.getQuestionId()) &&
! ego1Answer.getValue().equals(ego2Answer.getValue())) {
return false;
}
}
}
return true;
}
});
}
public static Question nextEgoQuestionForInterview(
final Long interviewId, final Question current,
final Boolean forward, final Boolean unansweredOnly)
{
return DB.withTx(new Function<Session,Question>() {
public Question apply(Session session) {
return nextEgoQuestionForInterview(session,interviewId,current,forward,unansweredOnly);
}
});
}
public static Question nextEgoQuestionForInterview(
Session session, Long interviewId, Question current, Boolean forward, Boolean unansweredOnly)
{
Interview interview = Interviews.getInterview(session, interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.EGO);
if(! forward) {
Collections.reverse(questions);
}
List<Answer> answers =
Answers.getAnswersForInterview(session, interviewId, QuestionType.EGO);
Boolean passedCurrent = current == null;
EvaluationContext context = Expressions.getContext(session, interview);
for(Question question : questions) {
Boolean foundAnswer = false;
for(Answer answer : answers) {
if(answer.getQuestionId()!=null && answer.getQuestionId().equals(question.getId())) {
foundAnswer = true;
}
}
if(unansweredOnly && foundAnswer) {
// Looking for unanswered. This one is answered. Not the question we're looking for.
} else if(passedCurrent) {
Long reasonId = question.getAnswerReasonExpressionId();
Boolean shouldAnswer =
reasonId == null ||
Expressions.evaluateAsBool(
context.eidToExpression.get(reasonId),
new ArrayList<Alter>(),
context);
if(shouldAnswer) {
return question;
}
}
if(current != null && question.getId().equals(current.getId())) {
passedCurrent = true;
}
}
return null;
}
public static List<Question> answeredEgoQuestionsForInterview(
Session session, EvaluationContext context, Long interviewId)
{
Interview interview = Interviews.getInterview(session, interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.EGO);
List<Question> answeredQuestions = Lists.newArrayList();
for(Question question : questions) {
Long reasonId = question.getAnswerReasonExpressionId();
Boolean shouldAnswer =
reasonId == null ||
Expressions.evaluateAsBool(
context.eidToExpression.get(reasonId),
new ArrayList<Alter>(),
context);
if(shouldAnswer && context.qidToEgoAnswer.get(question.getId()) != null) {
answeredQuestions.add(question);
}
}
return answeredQuestions;
}
public static InterviewingAlterPage.Subject nextAlterPageForInterview(final Long interviewId,
final InterviewingAlterPage.Subject currentPage, final Boolean forward, final Boolean unansweredOnly)
{
return new DB.Action<InterviewingAlterPage.Subject>() {
public InterviewingAlterPage.Subject get() {
return nextAlterPageForInterview(
session, interviewId, currentPage,
forward, unansweredOnly);
}
}.execute();
}
public static InterviewingAlterPage.Subject nextAlterPageForInterview(Session session, Long interviewId,
InterviewingAlterPage.Subject currentPage, Boolean forward, Boolean unansweredOnly)
{
InterviewingAlterPage.Subject previousPage = currentPage;
EvaluationContext context =
Expressions.getContext(session, Interviews.getInterview(session, interviewId));
TreeSet<InterviewingAlterPage.Subject> pages =
alterPagesForInterview(session, interviewId);
while(true) {
InterviewingAlterPage.Subject nextPage;
if(pages.isEmpty()) {
return null;
}
if(previousPage == null) {
nextPage =
forward ? pages.first() : pages.last();
} else {
nextPage =
forward ? pages.higher(previousPage) : pages.lower(previousPage);
}
if(nextPage == null) {
return null;
}
if(! unansweredOnly) {
return nextPage;
}
for(Alter alter : nextPage.alters) {
Boolean answered =
context.qidAidToAlterAnswer.containsKey(
new PairUni<Long>(nextPage.question.getId(),alter.getId()));
if(! answered) {
return nextPage;
}
}
previousPage = nextPage;
}
}
public static InterviewingAlterPairPage.Subject nextAlterPairPageForInterview(
final Long interviewId, final InterviewingAlterPairPage.Subject currentPage,
final Boolean forward, final Boolean unansweredOnly)
{
return new DB.Action<InterviewingAlterPairPage.Subject>() {
public InterviewingAlterPairPage.Subject get() {
return nextAlterPairPageForInterview(
session, interviewId, currentPage,
forward, unansweredOnly);
}
}.execute();
}
public static InterviewingAlterPairPage.Subject nextAlterPairPageForInterview(Session session,
Long interviewId, InterviewingAlterPairPage.Subject currentPage,
Boolean forward, Boolean unansweredOnly)
{
InterviewingAlterPairPage.Subject previousPage = currentPage;
EvaluationContext context =
Expressions.getContext(session, Interviews.getInterview(session, interviewId));
TreeSet<InterviewingAlterPairPage.Subject> pages =
alterPairPagesForInterview(session, interviewId);
while(true) {
InterviewingAlterPairPage.Subject nextPage;
if(pages.isEmpty()) {
return null;
}
if(previousPage == null) {
nextPage =
forward ? pages.first() : pages.last();
} else {
nextPage =
forward ? pages.higher(previousPage) : pages.lower(previousPage);
}
if(nextPage == null) {
return null;
}
if(! unansweredOnly) {
return nextPage;
}
for(Alter secondAlter : nextPage.secondAlters) {
Boolean answered =
context.qidA1idA2idToAlterPairAnswer.containsKey(
new TripleUni<Long>(
nextPage.question.getId(),
nextPage.firstAlter.getId(),
secondAlter.getId()));
if(! answered) {
return nextPage;
}
}
previousPage = nextPage;
}
}
public static Question nextNetworkQuestionForInterview(
final Long interviewId, final Question current,
final Boolean forward, final Boolean unansweredOnly)
{
return DB.withTx(new Function<Session,Question>() {
public Question apply(Session session) {
return nextNetworkQuestionForInterview(session,interviewId,current,forward,unansweredOnly);
}
});
}
public static Question nextNetworkQuestionForInterview(
Session session, Long interviewId, Question current, Boolean forward, Boolean unansweredOnly)
{
Interview interview = Interviews.getInterview(session, interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.NETWORK);
if(! forward) {
Collections.reverse(questions);
}
List<Answer> answers =
Answers.getAnswersForInterview(session, interviewId, QuestionType.NETWORK);
Boolean passedCurrent = current == null;
EvaluationContext context = Expressions.getContext(session, interview);
for(Question question : questions) {
Boolean foundAnswer = false;
for(Answer answer : answers) {
if(answer.getQuestionId().equals(question.getId())) {
foundAnswer = true;
}
}
if(unansweredOnly && foundAnswer) {
// Looking for unanswered. This one is answered. Not the question we're looking for.
} else if(passedCurrent) {
Long reasonId = question.getAnswerReasonExpressionId();
Boolean shouldAnswer =
reasonId == null ||
Expressions.evaluateAsBool(
context.eidToExpression.get(reasonId),
new ArrayList<Alter>(),
context);
if(shouldAnswer) {
return question;
}
}
if(current != null && question.getId().equals(current.getId())) {
passedCurrent = true;
}
}
return null;
}
public static List<Question> answeredNetworkQuestionsForInterview(
Session session, EvaluationContext context, Long interviewId)
{
Interview interview = Interviews.getInterview(session, interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.NETWORK);
List<Question> answeredQuestions = Lists.newArrayList();
for(Question question : questions) {
Long reasonId = question.getAnswerReasonExpressionId();
Boolean shouldAnswer =
reasonId == null ||
Expressions.evaluateAsBool(
context.eidToExpression.get(reasonId),
new ArrayList<Alter>(),
context);
if(shouldAnswer && context.qidToNetworkAnswer.get(question.getId()) != null) {
answeredQuestions.add(question);
}
}
return answeredQuestions;
}
// Note: sections only relevant for alter and alter pair questions... for now...
private static Map<Long,TreeSet<Question>>
createQuestionIdToSectionMap(Collection<Question> questions)
{
Map<Long,TreeSet<Question>> questionIdToSection = Maps.newTreeMap();
TreeSet<Question> section = null;
Question previousQuestion = null;
for(Question question : new TreeSet<Question>(questions)) {
if(section == null || // reasons to start a new section
question.hasPreface() ||
question.getAskingStyleList() ||
previousQuestion.getAskingStyleList())
{
section = new TreeSet<Question>();
}
previousQuestion = question;
section.add(question);
questionIdToSection.put(question.getId(), section);
}
return questionIdToSection;
}
public static String getPrefaceBetweenQuestions(final Question early, final Question late) {
return new DB.Action<String>() {
public String get() {
return getPrefaceBetweenQuestions(session,early,late);
}
}.execute();
}
public static String getPrefaceBetweenQuestions(
Session session, Question early, Question late)
{
if(late == null) {
return null;
}
Set<Question> section =
createQuestionIdToSectionMap(
Questions.getQuestionsForStudy(late.getStudyId(), late.getType()))
.get(late.getId());
if(early != null && section.contains(early)) {
return null;
}
for(Question question : section) {
if(question.hasPreface()) {
return question.getPreface();
}
}
return null;
}
public static ArrayList<InterviewingAlterPage.Subject>
answeredAlterPagesForInterview(Session session, EvaluationContext context, Long interviewId)
{
ArrayList<InterviewingAlterPage.Subject> results = Lists.newArrayList();
for(InterviewingAlterPage.Subject subject : alterPagesForInterview(session,interviewId)) {
Boolean answered = false;
for(Alter alter : subject.alters) {
if(context.qidAidToAlterAnswer.containsKey(
new PairUni<Long>(
subject.question.getId(),
alter.getId())))
{
answered = true;
}
}
if(answered) {
results.add(subject);
}
}
return results;
}
private static TreeSet<InterviewingAlterPage.Subject>
alterPagesForInterview(Session session, Long interviewId)
{
ArrayList<Pair<Question,ArrayList<Alter>>> questionAlterListPairs =
alterQuestionsForInterview(session,interviewId);
Interview interview = Interviews.getInterview(interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.ALTER);
Map<Long,TreeSet<Question>> qIdToSection =
createQuestionIdToSectionMap(new TreeSet<Question>(questions));
TreeSet<InterviewingAlterPage.Subject> results = Sets.newTreeSet();
for(Pair<Question,ArrayList<Alter>> questionAndAlters : questionAlterListPairs) {
Question question = questionAndAlters.getFirst();
ArrayList<Alter> alters = questionAndAlters.getSecond();
if(question.getAskingStyleList()) {
InterviewingAlterPage.Subject subject = new InterviewingAlterPage.Subject();
subject.alters = alters;
subject.interviewId = interviewId;
subject.question = question;
subject.sectionQuestions = qIdToSection.get(question.getId());
results.add(subject);
} else {
for(Alter alter : alters) {
InterviewingAlterPage.Subject subject = new InterviewingAlterPage.Subject();
subject.alters = Lists.newArrayList(alter);
subject.interviewId = interviewId;
subject.question = question;
subject.sectionQuestions = qIdToSection.get(question.getId());
results.add(subject);
}
}
}
return results;
}
public static ArrayList<InterviewingAlterPairPage.Subject>
answeredAlterPairPagesForInterview(Session session, EvaluationContext context, Long interviewId)
{
ArrayList<InterviewingAlterPairPage.Subject> results = Lists.newArrayList();
for(InterviewingAlterPairPage.Subject subject : alterPairPagesForInterview(session,interviewId)) {
Boolean answered = false;
for(Alter secondAlter : subject.secondAlters) {
if(context.qidA1idA2idToAlterPairAnswer.containsKey(
new TripleUni<Long>(
subject.question.getId(),
subject.firstAlter.getId(),
secondAlter.getId())))
{
answered = true;
}
}
if(answered) {
results.add(subject);
}
}
return results;
}
private static TreeSet<InterviewingAlterPairPage.Subject>
alterPairPagesForInterview(Session session, Long interviewId)
{
ArrayList<Triple<Question,Alter,ArrayList<Alter>>> questionAlterSecondAltersList =
alterPairQuestionFirstAlterAndSecondAlters(session, interviewId);
Interview interview = Interviews.getInterview(interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.ALTER_PAIR);
Map<Long,TreeSet<Question>> qIdToSection =
createQuestionIdToSectionMap(new TreeSet<Question>(questions));
TreeSet<InterviewingAlterPairPage.Subject> results = Sets.newTreeSet();
for(Triple<Question,Alter,ArrayList<Alter>> questionAlterSecondAlters : questionAlterSecondAltersList)
{
Question question = questionAlterSecondAlters.getFirst();
Alter firstAlter = questionAlterSecondAlters.getSecond();
ArrayList<Alter> secondAlters = questionAlterSecondAlters.getThird();
if(question.getAskingStyleList()) {
InterviewingAlterPairPage.Subject subject = new InterviewingAlterPairPage.Subject();
subject.interviewId = interviewId;
subject.question = question;
subject.firstAlter = firstAlter;
subject.secondAlters = secondAlters;
subject.sectionQuestions = qIdToSection.get(question.getId());
results.add(subject);
} else {
for(Alter secondAlter : secondAlters) {
InterviewingAlterPairPage.Subject subject = new InterviewingAlterPairPage.Subject();
subject.interviewId = interviewId;
subject.question = question;
subject.firstAlter = firstAlter;
subject.secondAlters = Lists.newArrayList(secondAlter);
subject.sectionQuestions = qIdToSection.get(question.getId());
results.add(subject);
}
}
}
return results;
}
/**
* alterQuestionsForInterview is response for getting the set of
* questions to ask in the alter section. Here is an example that
* explains how the questions are grouped:
* Q1 non list question with preface text
* Q2 non list question (no preface text)
* Q3 non list question with preface text
* Q4 non list question (no preface text)
* Q5 list question
* Q6 non list question
* Q7 non list question
* Q8 list question
*
* It should ask:
*
* 1. Q1 for alter 1, Q2 for alter 1, Q1 for alter 2, Q2 for alter 2 and so on for each alter
* 2. Q3 for alter 1, Q4 for alter 1, Q3 for alter 2, Q4 for alter 2 and so on for each alter
* 3. Q5 in list format
* 4. Q6 for alter 1, Q7 for alter 1, Q6 for alter 2, Q7 for alter 2 and so on for each alter
* 5. Q8 in list format
*/
private static ArrayList<Pair<Question,ArrayList<Alter>>> alterQuestionsForInterview(
Session session, Long interviewId)
{
Interview interview = Interviews.getInterview(session, interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.ALTER);
Multimap<Long,Answer> questionIdToAnswers = ArrayListMultimap.create();
for(Answer answer : Answers.getAnswersForInterview(session, interviewId, QuestionType.ALTER)) {
questionIdToAnswers.put(answer.getQuestionId(), answer);
}
List<Alter> alters = Alters.getForInterview(session, interviewId);
EvaluationContext context = Expressions.getContext(session, interview);
ArrayList<Pair<Question,ArrayList<Alter>>> results = Lists.newArrayList();
for(Question question : questions) {
Set<Long> answeredAlterIds = Sets.newHashSet();
for(Answer answer : questionIdToAnswers.get(question.getId())) {
answeredAlterIds.add(answer.getAlterId1());
}
ArrayList<Alter> resultAlters = Lists.newArrayList();
for(Alter alter : alters) {
Long reasonId = question.getAnswerReasonExpressionId();
// Boolean shouldAnswer =
// reasonId == null ||
// Expressions.evaluateAsBool(
// context.eidToExpression.get(reasonId),
// Lists.newArrayList(alter),
// context);
Boolean shouldAnswer;
if ( reasonId==null ) {
shouldAnswer = true;
} else {
shouldAnswer = Expressions.evaluateAsBool(
context.eidToExpression.get(reasonId),
Lists.newArrayList(alter),
context);
}
if(shouldAnswer) {
resultAlters.add(alter);
}
}
if(! resultAlters.isEmpty()) {
results.add(new Pair<Question,ArrayList<Alter>>(question,resultAlters));
}
}
return results;
}
private static ArrayList<Triple<Question,Alter,ArrayList<Alter>>>
alterPairQuestionFirstAlterAndSecondAlters(Session session, Long interviewId) {
ArrayList<Triple<Question,Alter,ArrayList<Alter>>> results = Lists.newArrayList();
Interview interview = Interviews.getInterview(session, interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.ALTER_PAIR);
List<Alter> alters = Alters.getForInterview(session, interviewId);
EvaluationContext context = Expressions.getContext(session, interview);
for(Question question : questions) {
for(Alter alter1 : alters) {
ArrayList<Alter> secondAlters = Lists.newArrayList();
for(Alter alter2 : alters) {
if( ( !question.getSymmetric() && alter1.getId()!=alter2.getId()) ||
alter1.getId() < alter2.getId()) {
Long reasonId = question.getAnswerReasonExpressionId();
Boolean shouldAnswer =
reasonId == null ||
Expressions.evaluateAsBool(
context.eidToExpression.get(reasonId),
Lists.newArrayList(alter1,alter2),
context);
if( shouldAnswer ) {
secondAlters.add(alter2);
}
}
}
if(! secondAlters.isEmpty()) {
results.add(new Triple<Question,Alter,ArrayList<Alter>>(
question,alter1,secondAlters));
}
}
}
return results;
}
public static List<InterviewNavigationPanel.InterviewLink>
getAnsweredPagesForInterview(final Long interviewId) {
return new DB.Action<List<InterviewNavigationPanel.InterviewLink>>() {
public List<InterviewNavigationPanel.InterviewLink> get() {
return getAnsweredPagesForInterview(session, interviewId);
}
}.execute();
}
public static List<InterviewNavigationPanel.InterviewLink>
getAnsweredPagesForInterview(Session session, Long interviewId) {
Interview interview = Interviews.getInterview(session, interviewId);
EvaluationContext context = Expressions.getContext(session, interview);
List<InterviewLink> links = Lists.newArrayList();
for(Question egoQuestion : answeredEgoQuestionsForInterview(session,context,interviewId)) {
links.add(new InterviewNavigationPanel.EgoLink(interviewId,egoQuestion));
}
Integer alters = Alters.getForInterview(session,interviewId).size();
if(alters > 0) {
links.add(new InterviewNavigationPanel.AlterPromptLink(interviewId,alters));
}
for(InterviewingAlterPage.Subject alterSubject :
answeredAlterPagesForInterview(session,context,interviewId))
{
links.add(new InterviewNavigationPanel.AlterLink(alterSubject));
}
for(InterviewingAlterPairPage.Subject alterPairSubject :
answeredAlterPairPagesForInterview(session,context,interviewId))
{
links.add(new InterviewNavigationPanel.AlterPairLink(alterPairSubject));
}
for(Question networkQuestion : answeredNetworkQuestionsForInterview(session,context,interviewId)) {
links.add(new InterviewNavigationPanel.NetworkLink(interviewId,networkQuestion));
}
return links;
}
/**
* simple convenience function for alterQuestionsForInterview
* reverses a list of questions
* @param questionList list to be reverse
* @return the input lists data but in reverse order
*/
private static List<Question> reverse ( List<Question> questionList) {
int ix;
ArrayList<Question> returnList = new ArrayList<Question>(questionList.size());
for ( ix=questionList.size()-1 ; ix>=0 ; --ix )
returnList.add(questionList.get(ix));
return(returnList);
}
/**
* simple convenience function for alterQuestionsForInterview
* starting from the start on the left, remove all questions up to
* and including prevQuestion (if present)
* @param questionList list to remove items from
* @param prevQuestion question we will trim up to
* @return the input list possibly shortened
*/
private static List<Question> trimLeading ( List<Question> questionList, Question prevQuestion ) {
int ix;
int questionIndex = -1;
Question removed;
if ( prevQuestion==null )
return(questionList);
questionIndex = questionList.indexOf(prevQuestion);
if ( questionIndex<1 )
return(questionList);
for ( ix=questionIndex-1 ; ix>=0 ; --ix ) {
removed = questionList.remove(ix);
System.out.println ("Skipping " + removed);
}
return(questionList);
}
/**
* this version of alterQuestionsForInterview will return just ONE question,
* the next questions to ask
* @param session database object
* @param interviewId IDs this interview
* @param eContext EvaluationContext that will check for answered questions
* @param prevQuestion the question just asked
* @param forward if true, advancing
* @param unansweredOnly if true, only want unanswered questions
* @return a ArrayList with just one question
*/
private static ArrayList<Pair<Question,ArrayList<Alter>>> alterQuestionsForInterview(
Session session, Long interviewId, EvaluationContext eContext,
Question prevQuestion, Boolean forward, Boolean unansweredOnly)
{
long onFunctionEntry = System.currentTimeMillis();
long onFunctionExit;
long onSkipLogicEntry;
long onUseIfEntry;
long timeInSetup = 0;
long timeInFunction = 0;
long timeInSkipLogic = 0;
long timeInUseIf = 0;
int skipLogicCount = 0;
int useIfCount = 0;
Interview interview = Interviews.getInterview(session, interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.ALTER);
List<Alter> alters = Alters.getForInterview(session, interviewId);
EvaluationContext context = Expressions.getContext(session, interview);
ArrayList<Pair<Question,ArrayList<Alter>>> results = Lists.newArrayList();
System.out.println ( "========================");
System.out.println ("Moving " + (forward ? "forward" : "backward") + " looking for " +
(unansweredOnly ? "UNanswered only" : "answered OR unanswered"));
// to make things simpler, reverse the order of the list if we
// are going backward
if ( !forward ) {
questions = reverse(questions);
}
// also to help keep things simple, get rid of questions we
// know we have absolutely no use for.
// that is, all questions up to and including the previous one.
// ( if we are going backwards we don't want questions IN FRONT
// of the previous one, but the reverse took care of that
if ( prevQuestion!=null) {
System.out.println ("prevquestion=" + prevQuestion.getTitle());
questions = trimLeading(questions, prevQuestion);
} else {
System.out.println ("prevquestion=null");
}
timeInSetup = System.currentTimeMillis() - onFunctionEntry;
for(Question question : questions) {
System.out.println ( "examining " + question.getTitle());
ArrayList<Alter> resultAlters = Lists.newArrayList();
Long reasonId = question.getAnswerReasonExpressionId();
Boolean answered = true;
Boolean shouldAnswerSkipLogic = true;
Boolean shouldAnswerUseIf = true;
// if we are looking for unanswered only and any one
// alter has answered the question we are not interested
// in the question
// if ( unansweredOnly ) {
// for ( Alter alter : alters ) {
// if ( !context.qidAidToAlterAnswer.containsKey(
// new PairUni<Long>(question.getId(),alter.getId())))
// answered = false;
// }
// if ( answered )
// System.out.println ( "Skipping, looking for unanswered and this has an answer");
// }
if ( true /* !unansweredOnly || !answered */ ) {
for(Alter alter : alters) {
int iEvaluate;
shouldAnswerSkipLogic = shouldAnswerUseIf = true;
ArrayList<Alter> singleAlterList = Lists.newArrayList(alter);
if ( reasonId!=null ) {
++skipLogicCount;
onSkipLogicEntry = System.currentTimeMillis();
shouldAnswerSkipLogic = Expressions.evaluateAsBool(
context.eidToExpression.get(reasonId),
singleAlterList,
context);
System.out.println ( "skip logic says to " + (shouldAnswerSkipLogic ? "ask " : "SKIP " ) + question.getTitle() + " with " + alter.getName());
timeInSkipLogic += System.currentTimeMillis() - onSkipLogicEntry;
} // end if skip logic reasonID != null
if ( shouldAnswerSkipLogic ) {
String strUseIf = question.getUseIfExpression();
if ( strUseIf!=null && strUseIf.trim().length()>0 ) {
++useIfCount;
onUseIfEntry = System.currentTimeMillis();
strUseIf = strUseIf.trim();
strUseIf = question.answerCountInsertion(strUseIf, interviewId);
strUseIf = question.questionContainsAnswerInsertion(strUseIf, interviewId, singleAlterList);
strUseIf = question.calculationInsertion(strUseIf, interviewId, singleAlterList);
strUseIf = question.variableInsertion(strUseIf, interviewId, singleAlterList);
iEvaluate = SimpleLogicMgr.createSimpleExpressionAndEvaluate (
strUseIf, interviewId,
question.getType(), question.getStudyId(), singleAlterList);
if ( SimpleLogicMgr.hasError()) {
System.out.println ("USE IF error in " + question.getTitle());
System.out.println ("USE IF =" + question.getUseIfExpression());
}
if (iEvaluate==0)
shouldAnswerUseIf = false;
System.out.println ( "use-if says to " + (shouldAnswerUseIf ? "ask " : "SKIP " ) + question.getTitle() + " with " + alter.getName());
timeInUseIf += System.currentTimeMillis() - onUseIfEntry;
} // end if strUseIf != null
} // end if shouldAnswerSkipLogic
if(shouldAnswerSkipLogic && shouldAnswerUseIf) {
System.out.println ( " adding " + alter.getName() + " to list ");
resultAlters.add(alter);
} // end of for ( Alter alters
if( !resultAlters.isEmpty()) {
results.add(new Pair<Question,ArrayList<Alter>>(question,resultAlters));
} // end of !resultAlters.isEmpty()
} // end off for ( Alter alters... loop
} // end if ( !unansweredOnly || !answered )
if ( false /* !results.isEmpty() */ ) {
onFunctionExit = System.currentTimeMillis();
timeInFunction = onFunctionExit - onFunctionEntry;
System.out.println ( "Time in NEW alterQuestionsForInterview=" + timeInFunction);
System.out.println ( "Time in setup =" + timeInSetup);
if ( skipLogicCount>0 )
System.out.println ( "Time in skip-logic=" + timeInSkipLogic + " (" + skipLogicCount + ") @ " + timeInSkipLogic/skipLogicCount);
if ( useIfCount>0 )
System.out.println ( "Time in use-if=" + timeInUseIf + " (" + useIfCount + ") @ " + timeInUseIf/useIfCount);
System.out.println ("returning " + results.size());
return results;
}
}
// if we get to this point results is empty, no questions were found
onFunctionExit = System.currentTimeMillis();
timeInFunction = onFunctionExit - onFunctionEntry;
System.out.println ( "Exit from bottom Time in NEW alterQuestionsForInterview=" + timeInFunction);
System.out.println ( "Time in setup =" + timeInSetup);
if ( skipLogicCount>0 )
System.out.println ( "Time in skip-logic=" + timeInSkipLogic + " (" + skipLogicCount + ") @ " + timeInSkipLogic/skipLogicCount);
if ( useIfCount>0 )
System.out.println ( "Time in use-if=" + timeInUseIf + " (" + useIfCount + ") @ " + timeInUseIf/useIfCount);
System.out.println ("returning " + results.size());
return results;
}
/**
* duplicate of the existing alterPagesForInterview above but
* has the four additional parameters to pass to the new version
* of alterQuestionsForInterview
* @param session
* @param interviewId
* @param eContext
* @param prevQuestion
* @param forward
* @param unansweredOnly
* @return
*/
private static TreeSet<InterviewingAlterPage.Subject>
alterPagesForInterview(Session session, Long interviewId,
EvaluationContext eContext,
Question prevQuestion, Boolean forward, Boolean unansweredOnly)
{
ArrayList<Pair<Question,ArrayList<Alter>>> questionAlterListPairs =
alterQuestionsForInterview(session,interviewId,
eContext, prevQuestion, forward, unansweredOnly );
Interview interview = Interviews.getInterview(interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.ALTER);
Map<Long,TreeSet<Question>> qIdToSection =
createQuestionIdToSectionMap(new TreeSet<Question>(questions));
TreeSet<InterviewingAlterPage.Subject> results = Sets.newTreeSet();
for(Pair<Question,ArrayList<Alter>> questionAndAlters : questionAlterListPairs) {
Question question = questionAndAlters.getFirst();
ArrayList<Alter> alters = questionAndAlters.getSecond();
if(question.getAskingStyleList()) {
InterviewingAlterPage.Subject subject = new InterviewingAlterPage.Subject();
subject.alters = alters;
subject.interviewId = interviewId;
subject.question = question;
subject.sectionQuestions = qIdToSection.get(question.getId());
results.add(subject);
} else {
for(Alter alter : alters) {
InterviewingAlterPage.Subject subject = new InterviewingAlterPage.Subject();
subject.alters = Lists.newArrayList(alter);
subject.interviewId = interviewId;
subject.question = question;
subject.sectionQuestions = qIdToSection.get(question.getId());
results.add(subject);
}
}
}
return results;
}
/**
* new version of nextAlterPageForInterview
* @param interviewId this interview
* @param currentPage 'subject' if this is null we are 'entering' the alter section,
* and will want the first or the last question
* @param forward if false user pressed the << button, searching backwards
* @param unansweredOnly if true looking for unanswered questions only
* @return
*/
// public static InterviewingAlterPage.Subject nextAlterPageForInterviewNEW(final Long interviewId,
// final InterviewingAlterPage.Subject currentPage, final Boolean forward, final Boolean unansweredOnly)
// {
// return new DB.Action<InterviewingAlterPage.Subject>() {
// public InterviewingAlterPage.Subject get() {
// return nextAlterPageForInterviewNEW(
// session, interviewId, currentPage,
// forward, unansweredOnly);
// }
// }.execute();
// }
/**
* new version of nextAlterPageForInterview
* @param session the database
* @param interviewId this interview
* @param currentPage 'subject' if this is null we are 'entering' the alter section,
* and will want the first or the last question
* @param forward if true searching forward to next question
* @param unansweredOnly if true only want unanswered questions
* @return
*/
// public static InterviewingAlterPage.Subject nextAlterPageForInterviewNEW(Session session, Long interviewId,
// InterviewingAlterPage.Subject currentPage, Boolean forward, Boolean unansweredOnly)
// {
// InterviewingAlterPage.Subject previousPage = currentPage;
// Question previousQuestion = (currentPage==null) ? null : currentPage.question;
// EvaluationContext context =
// Expressions.getContext(session, Interviews.getInterview(session, interviewId));
// TreeSet<InterviewingAlterPage.Subject> pages =
// alterPagesForInterview(session, interviewId, context, previousQuestion, forward, unansweredOnly);
//
// while(true) {
// InterviewingAlterPage.Subject nextPage;
// if(pages.isEmpty()) {
// return null;
// }
// if(previousPage == null) {
// nextPage =
// forward ? pages.first() : pages.last();
// } else {
// nextPage =
// forward ? pages.higher(previousPage) : pages.lower(previousPage);
// }
// if(nextPage == null) {
// return null;
// }
// if(! unansweredOnly) {
// return nextPage;
// }
// for(Alter alter : nextPage.alters) {
// Boolean answered =
// context.qidAidToAlterAnswer.containsKey(
// new PairUni<Long>(nextPage.question.getId(),alter.getId()));
// if(! answered) {
// return nextPage;
// }
// }
// previousPage = nextPage;
// }
// }
/**
* next couple of functions deal with putting multiple questions on a
* page in the ego section.
*/
public static List<Question> EgoQuestionListForInterview(
final Long interviewId, final Question current )
{
return DB.withTx(new Function<Session,List<Question>>() {
public List<Question> apply(Session session) {
return EgoQuestionListForInterview(session,interviewId,current);
}
});
}
/**
* this function is specific to putting multiple questions on one page in
* the ego section. it creates a list of contiguous questions bounded by
* preface questions ( or the bounds of the ego section )
* and possibly connected via the keep-on-one-page variable
* @param session - database structure
* @param interviewId - uniquely identifies the interview we are dealing with
* @param current - the question to ask the ego
* @return a list of question that should appear on one page.
* could easily be only one question in the list
*/
public static List<Question> EgoQuestionListForInterview(
Session session, Long interviewId, Question current)
{
Interview interview = Interviews.getInterview(session, interviewId);
List<Question> questions =
Questions.getQuestionsForStudy(session, interview.getStudyId(), QuestionType.EGO);
Iterator<Question> iter;
EvaluationContext context = Expressions.getContext(session, interview);
ArrayList<Question> workList = Lists.newArrayList();
ArrayList<Question> retList = Lists.newArrayList();
boolean hit = false;
boolean done = false;
Question workQuestion;
// Step 1: create a list of all questions that need to be answered
// and are bounded by questions with a preface. Only the first question
// within a group can have a preface. So if we hit a second (or third... )
// preface question before hitting the current question clear the list.
// also, if we hit a preface AFTER the current question we are done, thats
// a new group.
// This process duplicates similar work done for alter and alter_pair sections.
for(Question question : questions) {
if ( question.hasPreface()) {
if ( hit ) {
break;
}
}
if ( question.getId().equals(current.getId()))
hit = true;
Long reasonId = question.getAnswerReasonExpressionId();
Boolean shouldAnswer =
reasonId == null ||
Expressions.evaluateAsBool(
context.eidToExpression.get(reasonId),
new ArrayList<Alter>(),
context);
if(shouldAnswer) {
workList.add(question);
}
}
// now further reduce this list to questions connected by the
// keep-on-same-page option
hit = done = false;
iter = workList.iterator();
while ( iter.hasNext() && !done ) {
workQuestion = iter.next();
if ( workQuestion.equals(current)) {
retList.add(workQuestion);
hit = true;
} else if (hit) {
if ( workQuestion.getKeepOnSamePage()) {
retList.add(workQuestion);
} else {
done = true;
}
}
}
// in a similar manner, if we are going backwards and are on
// a question that needs to stay on the same page as the previous one,
// we will need to go back
if ( current.getKeepOnSamePage() && !current.hasPreface()) {
Collections.reverse(workList);
iter = workList.iterator();
hit = done = false;
while ( iter.hasNext() && !done ) {
workQuestion = iter.next();
if ( workQuestion.equals(current)) {
hit = true;
} else if (hit) {
if ( workQuestion.hasPreface()) {
retList.add(0,workQuestion);
done = true;
} else {
if ( workQuestion.getKeepOnSamePage()) {
retList.add(0,workQuestion);
} else {
retList.add(0,workQuestion);
done = true;
}
}
}
}
}
return retList;
}
}