package org.egonet.io;
import java.io.File;
import java.util.Arrays;
import org.egonet.exceptions.CorruptedInterviewException;
import org.egonet.exceptions.StudyIdMismatchException;
import org.egonet.model.Interview;
import org.egonet.model.Study;
import org.egonet.model.answer.*;
import org.egonet.model.question.AlterPromptQuestion;
import org.egonet.model.question.Question;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import electric.xml.Document;
import electric.xml.Element;
import electric.xml.Elements;
import electric.xml.ParseException;
public class InterviewReader {
final private static Logger logger = LoggerFactory.getLogger(InterviewReader.class);
private Study study;
private File interviewFile;
public InterviewReader(Study study, File interviewFile) {
this.study = study;
this.interviewFile = interviewFile;
}
public static File getNewInterviewPath(File studyPath, String [] name) {
if(studyPath.isFile()) // someone passed us the study file
throw new IllegalArgumentException("studyPath should be a path, not a file");
int i = 0;
boolean foundUniqueFile = false;
File ptr = null;
while(!foundUniqueFile) {
i++;
ptr = new File(studyPath, name[0].toLowerCase() + "_" + name[1].toLowerCase() + ".int");
if(!ptr.exists()) {
foundUniqueFile = true;
}
ptr = new File(studyPath, name[0].toLowerCase() + "_" + name[1].toLowerCase() + "-"+new java.util.Date().getTime()+".int");
if(!ptr.exists()) {
foundUniqueFile = true;
}
if(i > 20) {
throw new RuntimeException("Couldn't get a new, unique filename for new interview with " + Arrays.asList(name));
}
}
return ptr;
}
public Interview getInterview() throws CorruptedInterviewException {
return getInterview(false);
}
public Interview getInterview(boolean ignoreStudyId) throws CorruptedInterviewException {
String studyId = "";
try {
Document document = new Document(interviewFile);
studyId = document.getRoot().getAttribute("StudyId");
if (!studyId.equals(study.getStudyId())) {
if(ignoreStudyId)
document.getRoot().setAttribute(study.getStudyId(), studyId);
else
throw (new StudyIdMismatchException("study ID in study doesn't match study ID in interview file"));
}
Interview interview = readInterview(study, document.getRoot(), interviewFile.getName().replace(".int", ""));
logger.info("Completely parsed interview with study ID " + studyId + " and interview " + interview);
return interview;
} catch (NumberFormatException ex) {
throw new CorruptedInterviewException("Trouble parsing the study ID in this file: id=[" + studyId+"]",ex);
} catch (ParseException ex) {
throw new CorruptedInterviewException(ex);
}
}
private static Interview readInterview(Study study, Element e, String name) throws CorruptedInterviewException{
Element alterListElem = e.getElement("AlterList");
Element answerListElem = e.getElement("AnswerList");
/* Read the multiple alter list */
String[][] multipleAlterList = readAlters(alterListElem, study);
Interview interview = new Interview(study, name);
interview.setAlterQuestionPromptAnswers(multipleAlterList);
/* Get a list with no alter repetitions (repetitions
come from the apperance of alters in diferent questions)*/
String[] alterList = interview.getUnifiedAlterList();
interview.setAlterList(alterList);
/* Read answers */
if((alterList.length < study.getMinimumNumberOfAlters() || alterList.length > study.getMaximumNumberOfAlters())
&& !study.isUnlimitedAlterMode())
logger.warn("Study expected between " + study.getMinimumNumberOfAlters() + " and " +study.getMaximumNumberOfAlters() + " but interview file had " + alterList.length + " alters");
interview.setComplete(e.getBoolean("Complete"));
try {
interview.setFollowup(e.getBoolean("FollowUpProtocol"));
} catch (Exception ex) {
// no followup was found, set false
interview.setFollowup(false);
}
// TODO: Read new 'original alters' field
readAnswers(study, interview, answerListElem);
Element notes = e.getElement("notes");
if(notes != null) {
interview.setNotes(notes.getString());
}
return (interview);
}
public static boolean checkForCompleteness(Interview interview) {
boolean all = true;
Answer [] answers = interview.get_answers();
for(int i = 0 ; i < answers.length ; i++) {
//logger.info("\n---------------------------------------------------------------");
Answer answer = answers[i];
//logger.info("Found answer " + answer.getString());
// can't correctly find the linked question???
Question question = interview.getStudy().getQuestion(answer.getQuestionId());
//logger.info("\tFound question by answer " + question.getString());
if(!answer.isAnswered() && question.link.isActive()) { // if there's a real possibility this is linked
Answer linkedAnswer = answers[question.link.getAnswer().getIndex()];
if(!linkedAnswer.isAnswered()) {
//logger.info("\t!answer.answered && question.link.isActive()");
all = false;
}
}
else if(!answer.isAnswered() && !question.link.isActive()) {
//logger.info("\t!answer.answered && !question.link.isActive()");
all = false;
}
//logger.info("---------------------------------------------------------------\n");
}
logger.info("Checking interview for completeness bug - (" + all + ") "+ interview.toString());
return all;
}
private static String[][] readAlters(Element alterListElem, Study study) throws CorruptedInterviewException{
Elements alterPromptIter = alterListElem.getElements("QuestionPrompt");
String[][] lAlterList = null;
//New Egonet interviews, with multiple prompt questions
if(alterPromptIter.size() != 0)
{
int lNumPrompt;
int questionIndex = 0;
int alterIndex = 0;
lNumPrompt = study.getQuestionOrder(AlterPromptQuestion.class).size();
lAlterList = new String[lNumPrompt][];
while (alterPromptIter.hasMoreElements()) {
Elements alterNames = alterPromptIter.next().getElements("Name");
int sizePromptQuestion = alterNames.size();
lAlterList[questionIndex] = new String[sizePromptQuestion];
alterIndex = 0;
while(alterNames.hasMoreElements()){
lAlterList[questionIndex][alterIndex] = alterNames.next().getTextString();
alterIndex++;
}
questionIndex++;
}
}
//Old Egonet inteviews format, with only one alter question prompt.
else
{
Elements alterNames = alterListElem.getElements("Name");
lAlterList = new String[1][alterNames.size()];
int index = 0;
while(alterNames.hasMoreElements()){
lAlterList[0][index] = alterNames.next().getTextString();
index++;
}
}
return (lAlterList);
}
private static void readAnswers(Study study, Interview interview, Element e) throws CorruptedInterviewException {
Elements answerIter = e.getElements("Answer");
if (interview.get_numAnswers() != answerIter.size()) {
String err = "This interview file had " + answerIter.size() + " answered questions. I was expecting " + interview.get_numAnswers() + "!";
throw (new CorruptedInterviewException(err));
}
for(int index = 0; answerIter.hasMoreElements(); ) {
Element answerElement = answerIter.next();
Answer oldAnswer = interview.get_answerElement(index);
Answer newAnswer = readAnswer(study, answerElement);
if (oldAnswer.getQuestionId().equals(newAnswer.getQuestionId())) {
interview.set_answerElement(index++, newAnswer);
} else {
throw (new CorruptedInterviewException("mismatch question and answer id in datafile"));
}
}
}
private static Answer readAnswer(Study study, Element e) {
int qAlters[] = null;
Long qId = new Long(e.getLong("QuestionId"));
Question q = (Question) study.getQuestions().getQuestion(qId);
Element alterElem = e.getElement("Alters");
if (alterElem != null) {
Elements alterElems = alterElem.getElements("Index");
qAlters = new int[alterElems.size()];
for (int i = 0; i < alterElems.size(); i++) {
qAlters[i] = alterElems.next().getInt();
}
}
Answer r = Answer.newInstance(q.answerType);
r.setQuestionId(qId); r.setAlters(qAlters);
r.setAnswered(e.getBoolean("Answered"));
if (r.isAnswered()) {
r.string = e.getString("String");
r.setValue(e.getInt("Value"));
r.setIndex(e.getInt("Index"));
r.adjacent = q.selectionAdjacent(r.getValue());
} else {
r.string = null;
}
//logger.info("Read answer: " + r.getString());
return r;
}
}