package edu.cmu.cs.eyetrack.test;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import au.com.bytecode.opencsv.CSVWriter;
import edu.cmu.cs.eyetrack.helper.Util;
public class PilotOutputRecord {
/**
*
* @author Spook
* Hacky class made to parse data for the pilot runs in late 2011.
*/
public static enum TRIAL_TYPE {ALL_SAME_A, ALL_SAME_B, ALL_DIFF_A, ALL_DIFF_B, UNKNOWN;
public static TRIAL_TYPE inferTrialType(String rawFileName) {
String raw = rawFileName.toUpperCase();
if(raw.contains("DIFF_B") || raw.contains("DIFF B") || raw.contains("ALLDIFF_02")) {
return ALL_DIFF_B;
} else if(raw.contains("DIFF_A") || raw.contains("DIFF A") || raw.contains("ALLDIFF_01")) {
return ALL_DIFF_A;
} else if(raw.contains("SAME_B") || raw.contains("SAME B") || raw.contains("ALLSAME_02")) {
return ALL_SAME_B;
} else if(raw.contains("SAME_A") || raw.contains("SAME A") || raw.contains("ALLSAME_01")) {
return ALL_SAME_A;
} else {
return UNKNOWN;
}
}
};
// Random collection of strings->value pairs for header
private SortedMap<String, String> headerMap;
// Use SortedMaps all the way down so we can preserve order when printing out results
private SortedMap<TRIAL_TYPE, SortedMap<String, SortedMap<Integer, SingleRunScore>>> dataMap;
private SortedMap<TRIAL_TYPE, SortedSet<Integer>> seenTrialIDsMap;
public PilotOutputRecord() {
headerMap = new TreeMap<String, String>();
dataMap = new TreeMap<TRIAL_TYPE, SortedMap<String, SortedMap<Integer, SingleRunScore>>>();
seenTrialIDsMap = new TreeMap<TRIAL_TYPE, SortedSet<Integer>>();
for(TRIAL_TYPE t : TRIAL_TYPE.values()) {
dataMap.put(t, new TreeMap<String, SortedMap<Integer, SingleRunScore>>());
seenTrialIDsMap.put(t, new TreeSet<Integer>());
}
}
public void addHeader(String key, String value) {
headerMap.put(key, value);
}
public void addScore(TRIAL_TYPE trialType, String subjectID, Integer trialID, SingleRunScore record) {
SortedMap<String, SortedMap<Integer, SingleRunScore>> trialMap = dataMap.get(trialType);
// Initialize if we've never seen this subject for this trial type
if(!trialMap.containsKey(subjectID)) {
trialMap.put(subjectID, new TreeMap<Integer, SingleRunScore>());
}
// Add data for this specific trial of the specified trial type
trialMap.get(subjectID).put(trialID, record);
// Say that we've seen at least one of this trial ID for this specific trial type
seenTrialIDsMap.get(trialType).add(trialID);
}
public void addScore(TRIAL_TYPE trialType, String subjectID, Map<Integer, SingleRunScore> scoreMap) {
if(scoreMap == null) { return; }
for(Integer trialID : scoreMap.keySet()) {
addScore(trialType, subjectID, trialID, scoreMap.get(trialID));
}
}
public void fakeMissedTobiiTests(String subjectID) {
// For all known Trial types, if this Subject doesn't have a score for that trial type, fill it in with a nil
for( TRIAL_TYPE potential_miss : dataMap.keySet()) {
// If we've never seen this Subject for this Trial Type ...
if(null == dataMap.get(potential_miss).get(subjectID)) {
// ... add in NaN-scores for each Trial ID we've ever seen.
dataMap.get(potential_miss).put(subjectID, new TreeMap<Integer, SingleRunScore>());
for( int trialID : seenTrialIDsMap.get(potential_miss)){
dataMap.get(potential_miss).get(subjectID).put(trialID, new SingleRunScore().fakeTheScore());
}
}
}
}
public void writeToCSV(String filename) throws IOException {
CSVWriter writer = new CSVWriter(new FileWriter(new File(filename)), ',');
// Print overall header data at the top of the file
for(String header : headerMap.keySet()) {
writer.writeNext(new String[] {header, headerMap.get(header)});
}
writer.writeNext(new String[] {""});
writer.flush();
// Write the meaty data
// For each overall trial type (e.g., ALL DIFF A, ALL SAME B, ...) ...
for(TRIAL_TYPE trialType : dataMap.keySet()) {
writer.writeNext(new String[] {trialType.toString()});
// Add Header for this trial type
SortedSet<Integer> trialIDSet = seenTrialIDsMap.get(trialType);
int extraColCount = 3;
int numCSVColumns = 1 + trialIDSet.size() + extraColCount*(1 + trialIDSet.size());
String[] header = new String[numCSVColumns];
int idx=0;
header[idx++] = "Subject ID";
// First columns correspond to the scores
// Second columns correspond to time on task
// Third columns correspond to number of fixation points
// Fourth columns correpond to number of fixation points on a distractor, not on a target
for(int extraColIdx=0; extraColIdx<extraColCount+1; extraColIdx++) {
for(Integer trialID : trialIDSet) {
header[idx++] = "Trial " + trialID;
}
if(extraColIdx<extraColCount) {
header[idx++] = "";
}
}
writer.writeNext(header);
// Write data for this trial type, one row per subject ID
// TODO: map trial IDs to proper header's column; only an issue if we're missing data
SortedMap<String, SortedMap<Integer, SingleRunScore>> trialMap = dataMap.get(trialType);
int[] runningAvgCount = new int[numCSVColumns];
double[] runningAvgVal = new double[numCSVColumns];
for(String subjectID : trialMap.keySet()) {
SortedMap<Integer, SingleRunScore> records = trialMap.get(subjectID);
// If we don't have all data for this subject, skip (for now)
if(records.size() != trialIDSet.size()) {
Util.dPrintln("Skipping trial type " + trialType + ", subject " + subjectID + " because of incomplete data (" + records.size() + " / " + trialIDSet.size() + " possible trials found)");
continue;
}
// Otherwise, write a row of data for this subject and all of his trials' scores
String[] dataRow = new String[numCSVColumns];
idx=0;
dataRow[idx++] = subjectID;
for(Integer trialID : records.keySet()) {
Double score = records.get(trialID).getScore();
dataRow[idx] = String.valueOf( score );
if(score != null && !Double.isNaN(score) && !Double.isInfinite(score)) {
runningAvgCount[idx] += 1;
runningAvgVal[idx] += score;
}
idx++;
}
// And also the time-on-task for this subject across all trials
dataRow[idx++] = "";
for(Integer trialID : records.keySet()) {
double totalTaskLen = records.get(trialID).getFrameCountOverall();
double onTargetLen = records.get(trialID).getFrameCountOnTarget();
double timeOnTask = totalTaskLen > 0 ? onTargetLen/totalTaskLen : -1;
dataRow[idx++] = String.valueOf(timeOnTask);
}
// ... And the number of fixation points for this trial
dataRow[idx++] = "";
for(Integer trialID : records.keySet()) {
long fixationPtCount = records.get(trialID).getFixationPointCount();
dataRow[idx++] = String.valueOf(fixationPtCount);
}
// ... And the number of fixation points on a distractor and not the target
dataRow[idx++] = "";
for(Integer trialID : records.keySet()) {
long fixationPtOnDistractorCt = records.get(trialID).getFixationNearDistractorNotTargetCount();
dataRow[idx++] = String.valueOf(fixationPtOnDistractorCt);
}
writer.writeNext(dataRow);
}
String[] dataRow = new String[numCSVColumns];
dataRow[0] = "AVERAGE";
for(idx=1; idx<numCSVColumns; idx++) {
if(runningAvgCount[idx] != 0) {
runningAvgVal[idx] = runningAvgVal[idx] / (double) runningAvgCount[idx];
}
dataRow[idx] = String.valueOf(runningAvgVal[idx]);
}
writer.writeNext(dataRow);
writer.flush();
writer.writeNext(new String[] {""});
writer.writeNext(new String[] {""});
}
writer.close();
}
}