package net.sf.egonet.persistence;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.time.Time;
import org.hibernate.Session;
import au.com.bytecode.opencsv.CSVWriter;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.sf.egonet.model.Alter;
import net.sf.egonet.model.Answer;
import net.sf.egonet.model.Expression;
import net.sf.egonet.model.Interview;
import net.sf.egonet.model.Question;
import net.sf.egonet.model.QuestionOption;
import net.sf.egonet.model.Study;
import net.sf.egonet.network.Network;
import net.sf.egonet.network.NetworkService;
import net.sf.egonet.network.Statistics;
import net.sf.egonet.persistence.Expressions.EvaluationContext;
import net.sf.egonet.web.component.NetworkImage;
import net.sf.functionalj.tuple.PairUni;
import net.sf.functionalj.tuple.TripleUni;
import net.sf.egonet.web.page.CheckIncludeID;
public class Analysis {
public static BufferedImage getImageForInterview(final Interview interview, final Expression connection) {
return new DB.Action<BufferedImage>() {
public BufferedImage get() {
return getImageForInterview(session, interview, connection);
}
}.execute();
}
public static BufferedImage getImageForInterview(Session session, Interview interview, Expression connection) {
return NetworkService.createImage(getNetworkForInterview(session,interview,connection),null,null,null);
}
public static class ImageResourceStream implements IResourceStream {
private byte[] imagedata;
public ImageResourceStream(BufferedImage image) {
imagedata = NetworkImage.getJPEGFromBufferedImage(image);
}
public void close() throws IOException {
}
public String getContentType() {
return "image/jpeg";
}
public InputStream getInputStream()
throws ResourceStreamNotFoundException {
return new ByteArrayInputStream(imagedata);
}
public Locale getLocale() {
return null;
}
public long length() {
return imagedata.length;
}
public void setLocale(Locale arg0) {
}
public Time lastModifiedTime() {
return Time.now();
}
}
public static Network<Alter>
getNetworkForInterview(final Interview interview, final Expression connection)
{
return new DB.Action<Network<Alter>>() {
public Network<Alter> get() {
return getNetworkForInterview(session, interview, connection);
}
}.execute();
}
public static Network<Alter> getNetworkForInterview(Session session, Interview interview, Expression connection) {
EvaluationContext context = Expressions.getContext(session, interview);
Set<Alter> alters = new HashSet<Alter>(Alters.getForInterview(session, interview.getId()));
Set<PairUni<Alter>> edges = Sets.newHashSet();
for(Alter alter1 : alters) {
for(Alter alter2 : alters) {
ArrayList<Alter> altersInPair = Lists.newArrayList(alter1,alter2);
if(alter1.getId() < alter2.getId() &&
(connection == null || Expressions.evaluateAsBool(connection, altersInPair, context)))
{
edges.add(new PairUni<Alter>(alter1,alter2));
}
}
}
return new Network<Alter>(alters,edges);
}
public static void writeEgoAndAlterDataForStudy(CSVWriter writer, Session session, Study study,
Expression connection, List<CheckIncludeID> checkIncludeIDList) {
List<Interview> interviews = Interviews.getInterviewsForStudy(session, study.getId());
List<Question> egoIdQuestions =
Questions.getQuestionsForStudy(session, study.getId(), Question.QuestionType.EGO_ID);
List<Question> egoQuestions =
Questions.getQuestionsForStudy(session, study.getId(), Question.QuestionType.EGO);
List<Question> alterQuestions =
Questions.getQuestionsForStudy(session, study.getId(), Question.QuestionType.ALTER);
Map<Long,String> optionIdToValue = Maps.newTreeMap();
List<Question> allQuestions = Lists.newArrayList();
allQuestions.addAll(egoIdQuestions);
allQuestions.addAll(egoQuestions);
allQuestions.addAll(alterQuestions);
for(Question question : allQuestions) {
if(question.getAnswerType().equals(Answer.AnswerType.SELECTION) ||
question.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION))
{
for(QuestionOption option : Options.getOptionsForQuestion(session, question.getId())) {
optionIdToValue.put(option.getId(), option.getValue());
}
}
}
// IF the user selected which interviews in this study to include in the analysis
// that data will be in List<CheckIncludeID> checkIncludeIDList.
// need to remove unwanted interviews the 'safe' way
if ( checkIncludeIDList != null ) {
// First, copy interviews to a temp structure and clear original array
List<Interview> oldInterviews = Lists.newArrayList(); // new ArrayList<Interview>();
oldInterviews.addAll(interviews);
interviews.clear();
// now reconstruct the list of interviews to include
for ( Interview interview : oldInterviews ) {
if ( CheckIncludeID.useThisID(checkIncludeIDList, interview.getId()))
interviews.add(interview);
}
}
List<String> header = Lists.newArrayList();
header.add("Interview number");
for(Question question : egoIdQuestions) {
header.add(question.getTitle());
}
for(Question question : egoQuestions) {
header.add(question.getTitle());
}
header.add("Density");
for(String centralityProperty : Statistics.centralityProperties) {
header.add("Max "+centralityProperty+" name");
header.add("Max "+centralityProperty+" value");
}
header.add("Cliques");
header.add("Components");
for(String centralityProperty : Statistics.centralityProperties) {
header.add(capitalizeFirstLetter(centralityProperty)+" mean");
}
for(String centralityProperty : Statistics.centralityProperties) {
header.add(capitalizeFirstLetter(centralityProperty)+" centralization");
}
header.add("Isolates");
header.add("Dyads");
header.add("Alter number");
header.add("Alter name");
for(Question question : alterQuestions) {
header.add(question.getTitle());
}
for(String centralityProperty : Statistics.centralityProperties) {
header.add(capitalizeFirstLetter(centralityProperty)+" centrality");
}
writer.writeNext(header.toArray(new String[]{}));
for(Integer interviewIndex = 1; interviewIndex < interviews.size()+1; interviewIndex++) {
Interview interview = interviews.get(interviewIndex-1);
Network<Alter> network = getNetworkForInterview(session,interview,connection);
Statistics<Alter> statistics = new Statistics<Alter>(network);
EvaluationContext context = Expressions.getContext(session, interview);
List<Alter> alters = Alters.getForInterview(interview.getId());
for(Integer alterIndex = 1;
alterIndex < (alters.isEmpty() ? 1 : alters.size())+1;
alterIndex++)
{
Alter alter = alters.isEmpty() ? null : alters.get(alterIndex-1);
List<String> output = Lists.newArrayList();
output.add(interviewIndex.toString());
for(Question question : egoIdQuestions) {
output.add(showAnswer(study, context, question,
context.qidToEgoAnswer.get(question.getId()),
null, null, optionIdToValue));
}
for(Question question : egoQuestions) {
output.add(showAnswer(study, context, question,
context.qidToEgoAnswer.get(question.getId()),
null, null, optionIdToValue));
}
output.add(statistics.density()+"");
for(String centralityProperty : Statistics.centralityProperties) {
output.add(alters.isEmpty() ? "" :
statistics.maxCentralityNode(centralityProperty)+"");
output.add(alters.isEmpty() ? "" :
statistics.maxCentrality(centralityProperty)+"");
}
output.add(statistics.cliques().size()+"");
output.add(statistics.components().size()+"");
for(String centralityProperty : Statistics.centralityProperties) {
output.add(statistics.centralityMean(centralityProperty)+"");
}
for(String centralityProperty : Statistics.centralityProperties) {
output.add(statistics.centralization(centralityProperty)+"");
}
output.add(statistics.isolates().size()+"");
output.add(statistics.dyads().size()+"");
output.add(alters.isEmpty() ? "" : alterIndex.toString());
output.add(alter == null ? "" : alter.getName());
for(Question question : alterQuestions) {
output.add(alter == null ? "" :
showAnswer(study, context, question,
context.qidAidToAlterAnswer.get(
new PairUni<Long>(question.getId(),alter.getId())),
alter, null, optionIdToValue));
}
for(String centralityProperty : Statistics.centralityProperties) {
output.add(alter == null ? "" :
statistics.centrality(centralityProperty, alter)+"");
}
writer.writeNext(output.toArray(new String[]{}));
}
}
}
private static String capitalizeFirstLetter(String string) {
return string.isEmpty() ? string : string.substring(0, 1).toUpperCase()+string.substring(1);
}
public static void writeAlterPairDataForStudy(CSVWriter writer, Session session, Study study,
Expression connection, List<CheckIncludeID> checkIncludeIDList)
{
List<Interview> interviews = Interviews.getInterviewsForStudy(session, study.getId());
List<Question> egoIdQuestions =
Questions.getQuestionsForStudy(session, study.getId(), Question.QuestionType.EGO_ID);
List<Question> alterPairQuestions =
Questions.getQuestionsForStudy(session, study.getId(), Question.QuestionType.ALTER_PAIR);
Map<Long,String> optionIdToValue = Maps.newTreeMap();
List<Question> allQuestions = Lists.newArrayList();
allQuestions.addAll(egoIdQuestions);
allQuestions.addAll(alterPairQuestions);
for(Question question : allQuestions) {
if(question.getAnswerType().equals(Answer.AnswerType.SELECTION) ||
question.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION))
{
for(QuestionOption option : Options.getOptionsForQuestion(session, question.getId())) {
optionIdToValue.put(option.getId(), option.getValue());
}
}
}
// IF the user selected which interviews in this study to include in the analysis
// that data will be in List<CheckIncludeID> checkIncludeIDList.
// need to remove unwanted interviews the 'safe' way
if ( checkIncludeIDList != null ) {
// First, copy interviews to a temp structure and clear original array
List<Interview> oldInterviews = Lists.newArrayList(); // new ArrayList<Interview>();
oldInterviews.addAll(interviews);
interviews.clear();
// now reconstruct the list of interviews to include
for ( Interview interview : oldInterviews ) {
if ( CheckIncludeID.useThisID(checkIncludeIDList, interview.getId()))
interviews.add(interview);
}
}
List<String> header = Lists.newArrayList();
header.add("Interview number");
for(Question question : egoIdQuestions) {
header.add(question.getTitle());
}
header.add("Alter 1 number");
header.add("Alter 1 name");
header.add("Alter 2 number");
header.add("Alter 2 name");
for(Question question : alterPairQuestions) {
header.add(question.getTitle());
}
header.add("Distance");
writer.writeNext(header.toArray(new String[]{}));
for(Integer interviewIndex = 1; interviewIndex < interviews.size()+1; interviewIndex++) {
Interview interview = interviews.get(interviewIndex-1);
EvaluationContext context = Expressions.getContext(session, interview);
Network<Alter> network = getNetworkForInterview(session,interview,connection);
List<Alter> alters = Alters.getForInterview(interview.getId());
for(Integer alter1Index = 1; alter1Index < alters.size()+1; alter1Index++) {
Alter alter1 = alters.get(alter1Index-1);
for(Integer alter2Index = alter1Index+1; alter2Index < alters.size()+1; alter2Index++) {
Alter alter2 = alters.get(alter2Index-1);
List<String> output = Lists.newArrayList();
output.add(interviewIndex.toString());
for(Question question : egoIdQuestions) {
output.add(showAnswer(study, context, question,
context.qidToEgoAnswer.get(question.getId()),
null, null, optionIdToValue));
}
output.add(alter1Index.toString());
output.add(alter1.getName());
output.add(alter2Index.toString());
output.add(alter2.getName());
for(Question question : alterPairQuestions) {
output.add(showAnswer(study,context,question,
context.qidA1idA2idToAlterPairAnswer.get(
new TripleUni<Long>(question.getId(),alter1.getId(),alter2.getId())),
alter1,alter2,optionIdToValue));
}
Integer distance = network.distance(alter1, alter2);
output.add(distance == null ? "Inf" : distance+"");
writer.writeNext(output.toArray(new String[]{}));
}
}
}
}
public static String getRawDataCSVForStudy(final Study study, final Expression connection) {
return new DB.Action<String>() {
public String get() {
return getRawDataCSVForStudy(session, study, connection);
}
}.execute();
}
public static String getRawDataCSVForStudy(Session session, Study study, Expression connection) {
try {
StringWriter stringWriter = new StringWriter();
CSVWriter writer = new CSVWriter(stringWriter);
writeEgoAndAlterDataForStudy(writer, session, study, connection, (List<CheckIncludeID>)(null));
writer.writeNext(new String[]{}); // blank line between tables
writeAlterPairDataForStudy(writer, session, study, connection, (List<CheckIncludeID>)(null));
writer.close();
return stringWriter.toString();
} catch(Exception ex) {
throw new RuntimeException("Unable to output raw data csv for study "+study.getName(),ex);
}
}
public static String getEgoAndAlterCSVForStudy(final Study study, final Expression connection,
final List<CheckIncludeID> checkIncludeIDList) {
return new DB.Action<String>() {
public String get() {
return getEgoAndAlterCSVForStudy(session, study, connection, checkIncludeIDList);
}
}.execute();
}
public static String getEgoAndAlterCSVForStudy(Session session, Study study,
Expression connection, List<CheckIncludeID> checkIncludeIDList) {
try {
StringWriter stringWriter = new StringWriter();
CSVWriter writer = new CSVWriter(stringWriter);
writeEgoAndAlterDataForStudy(writer, session, study, connection, checkIncludeIDList);
writer.close();
return stringWriter.toString();
} catch(Exception ex) {
throw new RuntimeException("Unable to output ego and alter csv for study "+study.getName(),ex);
}
}
public static String getAlterPairCSVForStudy(final Study study, final Expression connection,
final List<CheckIncludeID> checkIncludeIDList) {
return new DB.Action<String>() {
public String get() {
return getAlterPairCSVForStudy(session, study, connection,checkIncludeIDList);
}
}.execute();
}
public static String getAlterPairCSVForStudy(Session session, Study study, Expression connection,
List<CheckIncludeID> checkIncludeIDList) {
try {
StringWriter stringWriter = new StringWriter();
CSVWriter writer = new CSVWriter(stringWriter);
writeAlterPairDataForStudy(writer, session, study, connection, checkIncludeIDList);
writer.close();
return stringWriter.toString();
} catch(Exception ex) {
throw new RuntimeException("Unable to output alter pair csv for study "+study.getName(),ex);
}
}
private static String showAnswer(
Study study, EvaluationContext context,
Question question,
Answer answer, Alter alter1, Alter alter2,Map<Long,String> optionIdToValue)
{
ArrayList<Alter> alters = Lists.newArrayList();
if(alter1 != null) {
alters.add(alter1);
}
if(alter2 != null) {
alters.add(alter2);
}
Long exprId = question.getAnswerReasonExpressionId();
if(exprId != null) {
Expression expression = context.eidToExpression.get(exprId);
if(expression != null &&
! Expressions.evaluateAsBool(context.eidToExpression.get(exprId),alters,context))
{
return study.getValueLogicalSkip();
}
}
if(answer == null) {
return study.getValueNotYetAnswered();
} else if(answer.getSkipReason().equals(Answer.SkipReason.REFUSE)) {
return study.getValueRefusal();
} else if(answer.getSkipReason().equals(Answer.SkipReason.DONT_KNOW)) {
return study.getValueDontKnow();
} else {
return showAnswer(optionIdToValue, question, answer);
}
}
private static String showAnswer(Map<Long,String> optionIdToValue, Question question, Answer answer) {
if(answer == null) {
return null;
}
if(question.getAnswerType().equals(Answer.AnswerType.NUMERICAL) ||
question.getAnswerType().equals(Answer.AnswerType.TEXTUAL) ||
question.getAnswerType().equals(Answer.AnswerType.TEXTUAL_PP) ||
question.getAnswerType().equals(Answer.AnswerType.DATE) ||
question.getAnswerType().equals(Answer.AnswerType.TIME_SPAN))
{
return answer.getValue();
}
if(question.getAnswerType().equals(Answer.AnswerType.SELECTION) ||
question.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION))
{
String value = answer.getValue();
if(value == null || value.isEmpty()) {
return "0";
}
List<String> selectedOptions = Lists.newArrayList();
for(String optionIdString : value.split(",")) {
Long optionId = Long.parseLong(optionIdString);
String optionValue = optionIdToValue.get(optionId);
if(optionValue != null) {
selectedOptions.add(optionValue);
}
}
return Joiner.on("; ").join(selectedOptions);
}
throw new RuntimeException("Unable to answer for answer type: "+question.getAnswerType());
}
}