package edu.uncc.cs.watsonsim; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Collectors; import org.json.simple.JSONObject; /** * @author Phani Rahul * @author Sean Gallagher */ public class Answer extends Phrase implements Comparable<Answer> { public Score scores = Score.empty(); private double overall_score = 0.0; public List<Passage> passages = new ArrayList<>(); public List<String> lexical_types = new ArrayList<>(); private final Queue<Evidence> evidence = new ConcurrentLinkedQueue<>(); /** * Create an Answer with one implicitly defined Passage */ public Answer(Passage d) { super(d.title); this.passages.add(d); this.scores = d.scores; } public Answer(List<Passage> passages, Score scores, String candidate_text) { super(candidate_text); this.passages = passages; this.scores = scores; } /** * Create an Answer with one implicitly defined Passage */ public Answer(String engine, String title, String full_text, String reference) { this(new Passage(engine, title, full_text, reference)); } /** * Create an Answer without any passages */ public Answer(String candidate_text) { super(candidate_text); } /** * Return a *new answer* that is a copy of the original with new text */ public Answer withText(String text) { Answer a = new Answer(text); a.evidence.addAll(evidence); a.scores = scores.clone(); for (Passage p : passages) { a.passages.add(new Passage(p)); } return a; } /** * Return the answer text alone */ @Override public String toString() { return text; } /** * Return a more detailed version of the answer, including scores. */ public String toLongString() { // Make a short view of the engines as single-letter abbreviations String engines = ""; for (Passage e: this.passages) if (e.engine_name != null) engines += e.engine_name.substring(0, 1); // Should look like: [0.9998 gil] Flying Waterbuffalos ... return String.format("[%01f %-3s]%s %s", getOverallScore(), engines, scores.get("CORRECT") == 1 ? "!" : " ", text); } /** * Format an Answer for serialization (e.g. for a web frontend) */ @SuppressWarnings("unchecked") public JSONObject toJSON() { JSONObject jo = new JSONObject(); jo.put("score", getOverallScore()); jo.put("scores", Score.asMap(scores)); jo.put("text", text); jo.put("passages", passages.stream() .map(p -> p.toJSON()) .collect(Collectors.toList())); jo.put("evidence", evidence.stream() .map(e -> e.toJSON()) .collect(Collectors.toList())); return jo; } /** * Return the combined score for the answer, or null * */ public double getOverallScore() { return overall_score; } /** * Set the combined score for the answer, or null * */ public void setOverallScore(double s) { overall_score = s; } /** * Assign a score to this answer. If you want to automatically generate * models to go with this score, remember to call Score.registerAnswerScore * @param name The name of the score * @param score Double value of score (or NaN) */ public void score(String name, double score) { scores.put(name, score); } /** * Provide some evidence for this class. * @param source (any class) * @param note */ public void log(Object source, String note, Object... attachments) { note = String.format(note, attachments); evidence.add(new Evidence(source.getClass().getSimpleName(), note)); } /** * Explain why this answer was given (format and return the log) */ public String explain() { return evidence.stream() .map(e -> String.format("[%s: about \"%s\"] %s", e.source, text, e.note)) .reduce((x, y) -> x + "\n" + y) .orElse("No evidence recorded."); } @Override public int compareTo(Answer other) { return Double.compare(getOverallScore(), other.getOverallScore()); } /** Change this Answer to include all the information of others */ public static Answer merge(List<Answer> others) { List<Passage> passages = new ArrayList<>(); // Merge the passages for (Answer other : others) passages.addAll(other.passages); // Merge the scores Score scores = Score.empty(); for (Answer other : others) scores = Score.merge(scores, other.scores); // Merge the text String candidate_text = others.get(0).text; for (Answer a: others) { if (a.text.length() < candidate_text.length()) { candidate_text = a.text; } } // Merge the evidence Queue<Evidence> evidence = new ConcurrentLinkedQueue<>(); for (Answer a: others) { evidence.addAll(a.evidence); } // Make an answer and add evidence Answer a = new Answer(passages, scores, candidate_text); a.evidence.addAll(evidence); return a; } } class Evidence { public final String source; public final String note; public Evidence(String source, String note) { this.source = source; this.note = note; } public JSONObject toJSON() { JSONObject jo = new JSONObject(); jo.put("source", source); jo.put("note", note); return jo; } }