com.amazonaws.mturk.cmd.GetResults.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.mturk.cmd.GetResults.java

Source

/*
 * Copyright 2012 Amazon Technologies, Inc.
 * 
 * Licensed under the Amazon Software License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://aws.amazon.com/asl
 * 
 * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and
 * limitations under the License.
 */

package com.amazonaws.mturk.cmd;

import java.io.StringReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.amazonaws.mturk.addon.BatchItemCallback;
import com.amazonaws.mturk.addon.HITDataCSVReader;
import com.amazonaws.mturk.addon.HITDataInput;
import com.amazonaws.mturk.addon.HITProperties;
import com.amazonaws.mturk.addon.HITResults;
import com.amazonaws.mturk.requester.Assignment;
import com.amazonaws.mturk.requester.AssignmentStatus;
import com.amazonaws.mturk.util.HITResultProcessor;

public class GetResults extends AbstractCmd implements BatchItemCallback {

    private final String ARG_SUCCESSFILE = "successfile";
    private final String ARG_OUTPUTFILE = "outputfile";
    private final String ARG_KVP = "namevaluepairs";
    private final String ARG_ANSWER_PREFIX = "answerPrefix";
    private static final int SECONDS = 1000;
    private static final int MINUTES = SECONDS * 60;
    private static final int HOURS = MINUTES * 60;

    private HITResultProcessor resultProcessor = null;
    boolean outputAsNameValuePair = false;
    List<String> headerFields = null;
    private int resultsCount = 0;
    private int totalAssignments = 0;
    private int totalAssignmentsCompleted = 0;
    private int rowCount = 0;
    private long totalWorkTimeMillis = 0;
    private Calendar firstHitCreateTime = Calendar.getInstance();
    private Calendar lastAssignmentSubmitTime = Calendar.getInstance();

    public GetResults() {
        lastAssignmentSubmitTime.setTimeInMillis(0);
    }

    public static void main(String[] args) {
        GetResults lh = new GetResults();
        lh.run(args);
    }

    protected void initOptions() {
        opt.addOption(ARG_SUCCESSFILE, true,
                "(required) A path to the success file returned by the call to LoadHITs");
        opt.addOption(ARG_OUTPUTFILE, true, "(required) The file to which you'd like your results saved");
        opt.addOption(ARG_KVP, false,
                "(optional) Outputs the answer values as name-value-pairs instead of column-format");
        opt.addOption(ARG_ANSWER_PREFIX, true,
                "(optional) Answer prefix to use when outputting the answer in column-format (defaults to 'Answer.')");
    }

    protected void printHelp() {
        formatter.printHelp(GetResults.class.getName() + " -" + ARG_SUCCESSFILE + " [path to success file]" + " -"
                + ARG_OUTPUTFILE + " [path to output file]", opt);
    }

    protected void runCommand(CommandLine cmdLine) throws Exception {

        if (!cmdLine.hasOption(ARG_SUCCESSFILE)) {
            log.fatal("Missing: -" + ARG_SUCCESSFILE + " [path to success file] ");
            System.exit(-1);
        } else if (!cmdLine.hasOption(ARG_OUTPUTFILE)) {
            log.fatal("Missing: -" + ARG_OUTPUTFILE + " [path to output file]");
            System.exit(-1);
        }

        String outputFile = cmdLine.getOptionValue(ARG_OUTPUTFILE);
        outputAsNameValuePair = cmdLine.hasOption(ARG_KVP);
        getResults(cmdLine.getOptionValue(ARG_SUCCESSFILE), outputFile, cmdLine.getOptionValue(ARG_ANSWER_PREFIX));
    }

    public void getResults(String successFile, String outputFile) throws Exception {
        getResults(successFile, outputFile, null);
    }

    public void getResults(String successFile, String outputFile, String answerPrefix) throws Exception {
        HITDataInput success = new HITDataCSVReader(successFile);
        rowCount = success.getNumRows() - 1;

        resultProcessor = new HITResultProcessor(outputFile, !outputAsNameValuePair);
        if (answerPrefix != null) {
            resultProcessor.setAnswerPrefix(answerPrefix);
        }

        log.info("--[Retrieving Results]----------");

        service.getResults(success, this);

        log.info("--[Done Retrieving Results]----------");

        resultProcessor.close();
        if (totalAssignments == 0) {
            log.info("Could not retrieve work successfully");
        } else {
            log.info("\nResults have been written to file '" + outputFile + "'.\n");

            // The remaining log.infos are setup to align the colons (:)

            log.info(String.format("Assignments completed: %d/%d (%d%%)", totalAssignmentsCompleted,
                    totalAssignments, totalAssignmentsCompleted * 100 / totalAssignments));

            if (totalAssignmentsCompleted < totalAssignments) {
                log.info(String.format("         Time elapsed: %s (h:mm:ss)", getInProgressTimeElapsed()));
            } else {
                log.info(String.format("         Time elapsed: %s (h:mm:ss)", getCompletedTimeElapsed()));
            }

            log.info(String.format("  Average submit time: %.1f seconds", getAverageSecsPerAssignment()));
        }
    }

    /**
     * Callback passed to the SDK which is invoked after a HIT result has been retrieved
     * or an error occurred
     */
    public synchronized void processItemResult(Object itemId, boolean succeeded, Object result,
            Exception itemException) {

        try {
            resultsCount++;
            if (succeeded) {
                HITResults r = (HITResults) result;

                if (headerFields == null) {
                    resultProcessor.setFieldNames(getHeaderRow(r));
                }

                r.writeResults(resultProcessor);
                log.info(String.format("Retrieved HIT %d/%d, %s", resultsCount, rowCount, itemId));
                updateStatistics(r);
            } else {
                log.error(String.format("Error retrieving HIT results for HIT %d/%d (%s): %s", resultsCount,
                        rowCount, itemId, itemException.getMessage()), itemException);
            }
        } catch (Exception ex) {
            log.error(String.format("Error processing HIT results for HIT %s: %s", itemId, ex.getMessage()), ex);
        }
    }

    /**
     * Gets the HIT and assignment headers and adds additional rows
     * for each question identifier in a HIT in case the ARG_KVP
     * flag is not set
     */
    private String[] getHeaderRow(HITResults r) {
        if (headerFields == null) {
            headerFields = new ArrayList<String>();

            for (HITProperties.HITField field : HITProperties.HIT_FIELDS) {
                headerFields.add(field.getFieldName());
            }

            for (HITProperties.AssignmentField field : HITProperties.ASSIGNMENT_FIELDS) {
                headerFields.add(field.getFieldName());
            }
        }

        if (!outputAsNameValuePair) {
            headerFields.remove(HITProperties.AssignmentField.Answers.getFieldName());
            // parse question for first hit to write out the header based on the question IDs
            try {
                DOMParser p = new DOMParser();
                p.parse(new InputSource(new StringReader(r.getHIT().getQuestion())));
                Document doc = p.getDocument();
                NodeList nodes = doc.getElementsByTagName("QuestionIdentifier");
                for (int i = 0; i < nodes.getLength(); i++) {
                    headerFields
                            .add(resultProcessor.getAnswerPrefix() + nodes.item(i).getFirstChild().getNodeValue());
                }
            } catch (Exception e) {
                log.error("Failed to parse HIT question to produce proper header information: "
                        + e.getLocalizedMessage(), e);
            }
        }

        return headerFields.toArray(new String[] {});
    }

    /**
     * Updates the statistics for the result that are displayed after
     * all results have been retrieved
     */
    private void updateStatistics(HITResults r) {
        totalAssignments += r.getHIT().getMaxAssignments();

        if (r.getHIT().getCreationTime().before(firstHitCreateTime)) {
            firstHitCreateTime = r.getHIT().getCreationTime();
        }

        Assignment[] assignments = r.getAssignments();
        if (assignments != null) {

            for (Assignment a : assignments) {
                AssignmentStatus status = a.getAssignmentStatus();
                Calendar acceptTime = a.getAcceptTime();
                Calendar submitTime = a.getSubmitTime();

                if (status == AssignmentStatus.Submitted || status == AssignmentStatus.Approved
                        || status == AssignmentStatus.Rejected) {
                    totalAssignmentsCompleted++;
                }

                if (submitTime != null && submitTime.after(lastAssignmentSubmitTime)) {
                    lastAssignmentSubmitTime = submitTime;
                }

                if (acceptTime != null && submitTime != null) {
                    totalWorkTimeMillis += (submitTime.getTimeInMillis() - acceptTime.getTimeInMillis());
                }

            }
        }
    }

    /**
     * Returns the time since the creation date of the first HIT (in format h:mm:ss)
     */
    private String getInProgressTimeElapsed() {
        long millis = System.currentTimeMillis() - firstHitCreateTime.getTimeInMillis();

        return millisToTimeElapsedString(millis);
    }

    /**
     * Returns the time between the creation of the first HIT and the submit of the last assignment (in format h:mm:ss)
     */
    private String getCompletedTimeElapsed() {
        long millis = lastAssignmentSubmitTime.getTimeInMillis() - firstHitCreateTime.getTimeInMillis();

        return millisToTimeElapsedString(millis);
    }

    private String millisToTimeElapsedString(long millis) {
        long hours = millis / HOURS;
        millis %= HOURS;
        long minutes = millis / MINUTES;
        millis %= MINUTES;
        long seconds = millis / SECONDS;

        return String.format("%d:%02d:%02d", hours, minutes, seconds);
    }

    /**
     * Returns the average work time per assignment completed in seconds
     * @return
     */
    private double getAverageSecsPerAssignment() {
        return this.totalWorkTimeMillis == 0 || this.totalAssignmentsCompleted == 0 ? (double) 0.0
                : (double) (this.totalWorkTimeMillis / this.totalAssignmentsCompleted) / (double) 1000.0;
    }

}