View Javadoc

1   /*
2    * Created on Apr 27, 2005, Copyright UC Regents
3    */
4   package org.telscenter.pas.steps;
5   
6   import java.awt.BorderLayout;
7   import java.awt.Color;
8   import java.awt.Component;
9   import java.io.IOException;
10  import java.io.StringReader;
11  import java.util.ArrayList;
12  import java.util.Date;
13  import java.util.HashMap;
14  import java.util.Iterator;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.logging.Level;
18  import java.util.logging.Logger;
19  
20  import javax.swing.BorderFactory;
21  import javax.swing.JComponent;
22  import javax.swing.JLabel;
23  import javax.swing.JPanel;
24  import javax.swing.JTextArea;
25  import javax.swing.border.CompoundBorder;
26  
27  import net.sf.sail.core.entity.ISock;
28  import net.sf.sail.core.entity.ISockEntry;
29  
30  import org.telscenter.pas.beans.IWorkReporter;
31  import org.telscenter.pas.beans.PasProject;
32  import org.telscenter.pas.beans.PasStep;
33  import org.telscenter.pas.service.INavigationService;
34  import org.telscenter.pas.steps.actions.ChallengeQuestionCheckAction;
35  import org.telscenter.pas.steps.domain.BlockInteraction;
36  import org.telscenter.pas.steps.domain.ChoiceInteraction;
37  import org.telscenter.pas.steps.domain.ResponseDeclaration;
38  import org.telscenter.pas.steps.domain.SimpleChoice;
39  import org.telscenter.pas.ui.pdf.PDFUtil;
40  import org.telscenter.pas.ui.util.PasColors;
41  
42  import com.lowagie.text.Element;
43  import com.lowagie.text.Font;
44  import com.lowagie.text.Paragraph;
45  import com.lowagie.text.html.simpleparser.HTMLWorker;
46  import com.lowagie.text.html.simpleparser.StyleSheet;
47  import com.lowagie.text.pdf.PdfPCell;
48  import com.lowagie.text.pdf.PdfPTable;
49  
50  /***
51   * Implement of the Pas step type, <i>ChallengeQuestion</i>.
52   * 
53   * @author Jerry Cheung
54   * @author Hiroki Terashima
55   * @author turadg
56   */
57  public class ChallengeQuestion extends Assessment {
58  	/***
59  	 * Logger for this class
60  	 */
61  	private static final Logger logger = Logger
62  			.getLogger(ChallengeQuestion.class.getName());
63  
64  	// index of the gotostep within the same activity as this challenge question
65  	private int gotoStepIndex;
66  
67  	// step to display if the student gets the question wrong
68  	private PasStep gotoStep;
69  
70  	// keeps track of the scores used to assign points, where the length of the
71  	// scores array corresponds to the number of attempts the user is allotted
72  	// for the challenge question and each successive index is the value
73  	// assigned
74  	// for that particular try
75  	private List<Integer> scores = new ArrayList<Integer>();
76  
77  	// keeps track of whether the scoring mode is quantitative, qualitative, or
78  	// off
79  	private String scoringMode;
80  
81  	// current number of attempts is found by looking at the size of the
82  	// student's sock
83  	private int maxAttempts;
84  
85  	// keeps track of the parts of each step
86  	private List<JPanel> stepParts = new ArrayList<JPanel>();
87  
88  	// holds set of values for the attempt prefixes for reporting (e.g. 1st
89  	// attempt, 2nd attempt, etc)
90  	public static final String indexes[] = { "1st", "2nd", "3rd", "4th", "5th",
91  			"6th", "7th", "8th", "9th", "10th" };
92  
93  	// the challenge question interface for getting the gotostep button, etc.
94  	private ChallengeQuestionUI cqUI;
95  
96  	// the service required to navigate to the appropriate steps in the
97  	// navigation panel when clicked on
98  	private INavigationService navigationService;
99  
100 	// required to get access to the PasProjectNavigationTaskPanel, activities,
101 	// process the gotostep, etc.
102 	private PasProject pasProject;
103 
104 	// keeps track of whether the choices are to be reordered or not when cq is
105 	// revisited after visiting goto step
106 	private String choiceOrdering;
107 
108 	// empty constructor for challenge question
109 	public ChallengeQuestion() {
110 	}
111 
112 	// confirms that the step type is a challenge question
113 	@Override
114 	public String getType() {
115 		return "Challenge Question";
116 	}
117 
118 	/***
119 	 * The entry method to CQ. This method gets called when the step is visited
120 	 * If the evidence step has not been visited before visiting this CQ step,
121 	 * display a message to go back to the step
122 	 * 
123 	 * @return visual representation of the Challenge Question
124 	 */
125 	@Override
126 	public Component getComponent() {
127 		if (cqUI == null)
128 			cqUI = new ChallengeQuestionUI(this);
129 
130 		cqUI.getGotoStepButton().setVisible(false);
131 
132 		// If student has answered CQ at least once, either of the following has
133 		// occurred:
134 		// (1) last choice correct
135 		// (2) last choice incorrect
136 		// (3) ran out of attempts
137 		// here, only cases (1) and (3) are considered because the CQ is no
138 		// longer needed to be tried
139 		// and the final result is shown.
140 		ISock<String> lastAnswerSock = getLastAnswerSock();
141 		if (lastAnswerSock != null && !lastAnswerSock.isEmpty()) {
142 			if (isStudentAnswerCorrect(lastAnswerSock)
143 					|| (numStudentAttempts() >= scores.size())) {
144 				ChallengeQuestionCheckAction cqAction = cqUI.getCQCheckAction();
145 				cqAction.doCheckAndUpdate();
146 				return cqUI;
147 			}
148 		}
149 
150 		// check to see if student has visited the evidence step at all, or
151 		// since the last visit to the CQ step
152 		// if so, a panel should be returned with a message asking the student
153 		// to review the evidence step again
154 		// before looking at the CQ once more.
155 		JPanel panel = panelToReviewEvidencePage();
156 		if (panel != null)
157 			return panel;
158 
159 		// the last student answer was incorrect, so the choices are reordered
160 		// and the updated set of choice interaction
161 		// values are shown to the user.
162 		cqUI.updateCqUI_Interactions();
163 		return cqUI;
164 	}
165 
166 	/***
167 	 * This method determines whether the goto step (i.e. evidence step) for
168 	 * this challenge question has been visited at all, or has been visited
169 	 * since the last visit to that CQ step. If either of these has not
170 	 * occurred, a message will be displayed in the VLE asking the user to
171 	 * review the evidence step again before seeing the CQ again.
172 	 * 
173 	 * @return panel
174 	 */
175 	private JPanel panelToReviewEvidencePage() {
176 		JPanel panel = null;
177 
178 		// Display a message to the student to go back to the evidence step
179 		if (!isGotoStepEverVisited()) {
180 			panel = new JPanel();
181 			panel
182 					.add(new JLabel(
183 							"Please review earlier evidence steps in this project before starting this Challenge Question."));
184 			return panel;
185 		}
186 
187 		// if last answer exists and is incorrect, check to see if student has
188 		// visited
189 		// the gotostep before coming back to this step
190 		ISock<String> lastAnswerSock = getLastAnswerSock();
191 		if (lastAnswerSock != null && !lastAnswerSock.isEmpty()
192 				&& !isStudentAnswerCorrect(lastAnswerSock)) {
193 			if (!isGotoStepVisitedSinceLastWrongAnswer()) {
194 				// Display a message to the student to go back to the evidence
195 				// step
196 				panel = new JPanel();
197 				panel
198 						.add(new JLabel(
199 								"Please review the evidence page before trying this Challenge Question."));
200 				return panel;
201 			}
202 		}
203 		return null;
204 
205 	}
206 
207 	
208 
209 	
210 
211 	/***
212 	 * Returns a PDF-version of the report
213 	 * 
214 	 * @see org.telscenter.pas.steps.ChallengeQuestion#getReportForLearnerPDF()
215 	 */
216 	public PdfPCell getReportForLearnerPDF(boolean withAnnotations) {
217 		if (logger.isLoggable(Level.CONFIG)) {
218 			logger.config("start"); //$NON-NLS-1$
219 		}
220 
221 		int defaultFont = Font.TIMES_ROMAN;
222 		PdfPTable table = PDFUtil.createStepTitle(this.getTitle());
223 		PdfPTable questionAnswerTable = new PdfPTable(1);
224 
225 		List<ResponseDeclaration> reponseDeclarations = assessmentItem
226 				.getResponseDeclarations();
227 
228 		if (reponseDeclarations.isEmpty()) {
229 			PdfPCell noNoteTaken = new PdfPCell(new Paragraph(Messages
230 					.getString("Note.NOTE_NOT_TAKEN"))); //$NON-NLS-1$
231 			// table.addCell(noNoteTaken);
232 		} else {
233 			PdfPCell questionAnswerTableCell = null;
234 			Font font = new Font(defaultFont, 14, Font.NORMAL);
235 			font.setColor(PasColors.showAllWorkTextColor);
236 			for (ResponseDeclaration rd : reponseDeclarations) {
237 				BlockInteraction bi = getInteractionByResponseDeclaration(rd);
238 				String prompt = null;
239 				String answer = null;
240 
241 				if (bi == null) {
242 					prompt = null;
243 				} else {
244 					prompt = bi.getPrompt();
245 				}// if
246 
247 				PdfPCell noteQuestionCell = null;
248 				if (prompt == null) {
249 					prompt = Messages.getString("Note.ERROR_PROMPT_NOT_FOUND"); //$NON-NLS-1$
250 
251 					noteQuestionCell = new PdfPCell(new Paragraph(prompt, font));
252 					noteQuestionCell.setBorderWidthBottom(0.0f);
253 					noteQuestionCell.setGrayFill(0.8f);
254 				} else {
255 					//					
256 					StringReader stringReader = new StringReader(prompt);
257 					List<Element> p = null;
258 					try {
259 						StyleSheet st = new StyleSheet();
260 						st.loadTagStyle("body", "face", "Times New Roman");
261 						// st.loadTagStyle("body", "encoding", "Identity-H");
262 						st.loadTagStyle("body", "font-size", "6,0");
263 
264 						p = HTMLWorker.parseToList(stringReader, st);
265 
266 					} catch (IOException e) {
267 						e.printStackTrace();
268 					}// try
269 
270 					stringReader.close();
271 
272 					noteQuestionCell = new PdfPCell();
273 					for (Element e : p) {
274 						noteQuestionCell.addElement(e);
275 					}
276 
277 					noteQuestionCell.setBorderWidthBottom(0.0f);
278 					noteQuestionCell.setGrayFill(0.8f);
279 
280 					// PdfPCell noteQuestionCell = new PdfPCell(new
281 					// Paragraph(p.get(index),
282 					// font));
283 					// noteQuestionCell.setBorderWidthBottom(0.0f);
284 					// noteQuestionCell.setGrayFill(0.8f);
285 					// prompt = prompt.replaceAll(CommonUI.HTML_REGEX, "");
286 					// //$NON-NLS-1$
287 				}
288 
289 				// if (prompt == null) {
290 				// prompt = Messages.getString("Note.ERROR_PROMPT_NOT_FOUND");
291 				// //$NON-NLS-1$
292 				// } else {
293 				//					
294 				//					
295 
296 				// try {
297 				//						
298 				// StringExtractor ext = new StringExtractor(prompt);
299 				// prompt = ext.extractStrings(true);
300 				// } catch (ParserException e) {
301 				// // TODO Auto-generated catch block
302 				// e.printStackTrace();
303 				// }
304 				// prompt = prompt.replaceAll(CommonUI.HTML_REGEX, "");
305 				// prompt = prompt.trim();
306 				// prompt = prompt.replaceAll(CommonUI.HTML_REGEX, "");
307 				// //$NON-NLS-1$
308 				// }
309 				// PdfPCell noteQuestionCell = new PdfPCell(new
310 				// Paragraph(prompt,
311 				// font));
312 				// noteQuestionCell.setBorderWidthBottom(0.0f);
313 				// noteQuestionCell.setGrayFill(0.8f);
314 
315 				if (bi == null) {
316 					answer = null;
317 				} else {
318 					answer = lastAnswer(this.getResponseDeclarationToSocks(),
319 							rd);
320 				}// if
321 
322 				if (answer == null) {
323 					answer = Messages.getString("Note.NOT_ANSWERED"); //$NON-NLS-1$					
324 				} else if (bi instanceof ChoiceInteraction) {
325 					ChoiceInteraction<String> ci = (ChoiceInteraction<String>) bi;
326 
327 					List<SimpleChoice<String>> simpleChoices = ci.getSimpleChoices();
328 					for (SimpleChoice<String> choice : simpleChoices) {
329 						if (choice.getIdentifier().equals(answer)) {
330 							answer = choice.getContent();
331 						}
332 					}
333 				}
334 
335 				PdfPCell noteAnswerCell = new PdfPCell(new Paragraph(answer,
336 						font));
337 				noteAnswerCell.setBorderWidthTop(0.0f);
338 
339 				questionAnswerTable.addCell(noteQuestionCell);
340 				questionAnswerTable.addCell(noteAnswerCell);
341 
342 				questionAnswerTableCell = new PdfPCell(questionAnswerTable);
343 				questionAnswerTableCell.setPaddingLeft(9.0f);
344 				questionAnswerTableCell.setBorderWidth(0.0f);
345 
346 			}// for
347 			table.addCell(questionAnswerTableCell);
348 		}
349 		PdfPCell mainCell = new PdfPCell(table);
350 		// PdfPCell mainCell = new PdfPCell();
351 		mainCell.setBorder(0);
352 
353 		if (logger.isLoggable(Level.CONFIG)) {
354 			logger.config("end"); //$NON-NLS-1$
355 		}
356 
357 		// PdfPCell mainCell = new PdfPCell();
358 		return mainCell;
359 
360 		// TODO Auto-generated method stub
361 		// return super.getReportForLearnerPDF();
362 	}
363 
364 	/*** *********************************************************************************** */
365 	/***
366 	 * ******************************* GOTO EVIDENCE STEP
367 	 * **********************************************************
368 	 */
369 	/***
370 	 * Checks if gotoStep has ever been visited
371 	 * 
372 	 * @return true iff the gotoStep has ever been visited (at least once)
373 	 *         before visiting this Challenge Question step
374 	 */
375 	public boolean isGotoStepEverVisited() {
376 		if (getLastGotoStepVisitedSockEntry() == null) {
377 			return false;
378 		}
379 		return true;
380 	}
381 
382 	/***
383 	 * Checks if gotoStep has ever been visited
384 	 * 
385 	 * @return true iff the gotoStep has ever been visited (at least once)
386 	 *         before visiting this Challenge Question step
387 	 */
388 	public ISockEntry<String> getLastGotoStepVisitedSockEntry() {
389 		ISock<String> navigationLogSock = getPasProject()
390 				.getNavigationLogSock();
391 		ISockEntry<String> lastGotoStepVisitSockEntry = null;
392 		// now check if the gotoStep has been visited yet
393 		if (navigationLogSock != null) {
394 			Object gotoStepElement = getPasProject().getItemLog("step_open",
395 					getGotoStep());
396 			Iterator<ISockEntry<String>> iter = navigationLogSock
397 					.entryIterator();
398 			while (iter.hasNext()) {// make sure to go thru all of the iter to
399 									// get the LAST visit log
400 				ISockEntry<String> logElement = iter.next();
401 				if (logElement.getValue().equals(gotoStepElement)) {
402 					lastGotoStepVisitSockEntry = logElement;
403 				}
404 			}
405 		}
406 		return lastGotoStepVisitSockEntry;
407 	}
408 
409 	/***
410 	 * Checks if gotoStep has been visited between the last time this step was
411 	 * visited and now This must only be called if the last answer was wrong
412 	 * 
413 	 * @return true iff the gotoStep has been visited (at least once) between
414 	 *         the last time this ChallengeQuestion step was visited until now
415 	 */
416 	public boolean isGotoStepVisitedSinceLastWrongAnswer() {
417 
418 		ISock<String> lastWrongAnswerSock = getLastAnswerSock();
419 		Iterator<ISockEntry<String>> entryIterator = lastWrongAnswerSock.entryIterator();
420 		ISockEntry<String> lastSockEntry = null;
421 		while (entryIterator.hasNext()) {
422 			lastSockEntry = entryIterator.next();
423 		}
424 		Date lastWrongAnswerEntryDate = lastSockEntry.getDate();
425 
426 		ISockEntry<String> lastGotoStepVisitSockEntry = getLastGotoStepVisitedSockEntry();
427 		if (lastGotoStepVisitSockEntry == null) {
428 			return false;
429 		}
430 
431 		Date lastGotoStepVisitDate = lastGotoStepVisitSockEntry.getDate();
432 
433 		return lastWrongAnswerEntryDate.before(lastGotoStepVisitDate);
434 	}
435 
436 	/***
437 	 * ******************************* END GOTO EVIDENCE STEP
438 	 * ******************************************************
439 	 */
440 
441 	/***
442 	 * ************************************** ANSWER
443 	 * ******************************************
444 	 */
445 
446 	/***
447 	 * @return number of attempts that the student has made
448 	 */
449 	public int numStudentAttempts() {
450 
451 		ResponseDeclaration rd = assessmentItem.getResponseDeclarations()
452 				.get(0);
453 		ISock<String> studentSock = this.getResponseDeclarationToSocks().get(rd);
454 		if (studentSock == null) {
455 			logger.severe("no sock available for rim declaration " + rd
456 					+ "\n  so defaulting to ZERO for numStudentAttempts()");
457 			return 0;
458 		}
459 		return studentSock.size();
460 	}
461 
462 	/***
463 	 * @param rd
464 	 * @param bi
465 	 * @return answer - either stored in socks or whatever is retrieved from
466 	 *         choice interaction
467 	 */
468 	protected String getAnswer(ResponseDeclaration rd, BlockInteraction bi) {
469 		String answer = lastAnswer(this.getResponseDeclarationToSocks(), rd);
470 		if (answer == null) {
471 			answer = Messages.getString("Note.NOT_ANSWERED"); //$NON-NLS-1$
472 
473 		} else if (bi instanceof ChoiceInteraction) {
474 			ChoiceInteraction<String> ci = (ChoiceInteraction<String>) bi;
475 			String scoreReport = this.getScoreReport();
476 			if (scoreReport != null) {
477 				answer = scoreReport;
478 			} else {
479 				List<SimpleChoice<String>> simpleChoices = ci.getSimpleChoices();
480 				for (SimpleChoice<String> sc : simpleChoices) {
481 					if (sc.getIdentifier().equals(answer)) {
482 						answer = sc.getContent();
483 					}
484 				}
485 			}
486 		}
487 		return answer;
488 	}
489 
490 	/***
491 	 * TODO: refactor into utils along w/ lastAnswer
492 	 * 
493 	 * retrieves correct answer based on the inline feedback
494 	 * 
495 	 * @return corrAnswer
496 	 */
497 	public String correctAnswer() {
498 		ResponseDeclaration rd = assessmentItem.getResponseDeclarations()
499 				.get(0);
500 		return rd.getCorrectResponses().get(0);
501 	}
502 
503 	/***
504 	 * retrieves last answer based on the data from student sock
505 	 * 
506 	 */
507 	public String lastAnswer() {
508 		ResponseDeclaration rd = assessmentItem.getResponseDeclarations()
509 				.get(0);
510 		ISock<String> sock = this.getResponseDeclarationToSocks().get(rd);
511 
512 		if (sock == null) {
513 			String errMsg = "no sock available for rim declaration " + rd
514 					+ "\n  so returning null for lastAnswer()";
515 			logger.severe(errMsg);
516 			return null;
517 		}
518 
519 		if (sock.isEmpty())
520 			return null;
521 
522 		return sock.peek();
523 	}
524 
525 	/***
526 	 * @param rd
527 	 * @return sock last answer from sock
528 	 */
529 	public ISock<String> getLastAnswerSock() {
530 		ResponseDeclaration rd = getAssessmentItem().getResponseDeclarations()
531 				.get(0);
532 		ISock<String> sock = getResponseDeclarationToSocks().get(rd);
533 
534 		if (sock == null) {
535 			String errMsg = "no sock available for rim declaration " + rd
536 					+ "\n  so returning null for lastAnswer()";
537 			logger.severe(errMsg);
538 			return null;
539 		}
540 
541 		if (sock.isEmpty())
542 			return null;
543 
544 		return sock;
545 	}
546 
547 	/***
548 	 * Checks if student's answer in the provided sock is correct
549 	 * 
550 	 * @param studentAnswerSock
551 	 *            the Sock containing the student's answer
552 	 * @return true iff the first item in the sock contains a correct answer
553 	 *         response
554 	 */
555 	public boolean isStudentAnswerCorrect(ISock<String> studentAnswerSock) {
556 		String studentAnswer = studentAnswerSock.peek();
557 		Object correctAnswer = correctAnswer();
558 		return correctAnswer.equals(studentAnswer);
559 	}
560 
561 	/***
562 	 * ************************** ENDS ANSWER SECTION
563 	 * ************************************************
564 	 */
565 	/*** *********************************************************************************************** */
566 
567 	/****************************************************************************
568 	 * SETTERS AND GETTERS - ACCESSOR METHODS ********************
569 	 * 
570 	 * Methods: - getInteractionByResponseDeclaration - setCurrentStepPart,
571 	 * getStepParts - setScoringMode, getScoringMode - setScores, getScores -
572 	 * setGotoStepIndex, getGotoStepIndex - setGotoStep, getGotoStep -
573 	 * setScoreReport, getScoreReport - setMaxAttempts, getMaxAttempts -
574 	 * setCqUI, getCqUI - setNavigationService, getNavigationService -
575 	 * getPasProject, setPasProject - getChoiceOrdering, setChoiceOrdering -
576 	 * getCorrectFeedbackInline - getFeedbackContentFromChoices -
577 	 * getLastFeedbackInline
578 	 **************************************************************************/
579 
580 	/***
581 	 * @param rd
582 	 * @return
583 	 */
584 	protected BlockInteraction getInteractionByResponseDeclaration(
585 			ResponseDeclaration rd) {
586 		BlockInteraction bi = Assessment.getInteractionByResponseDeclaration(
587 				getAssessmentItem().getItemBody().getInteractions(), rd);
588 		return bi;
589 	}
590 
591 	/***
592 	 * @return stepParts
593 	 */
594 	public List<JPanel> getStepParts() {
595 		return this.stepParts;
596 	}
597 
598 	/***
599 	 * @param currentPart
600 	 *            adds the current step part to the list of steps' parts
601 	 */
602 	private void setCurrentStepPart(JPanel currentPart) {
603 		List<JPanel> stepParts = this.stepParts;
604 		stepParts.add(currentPart);
605 		this.stepParts = stepParts;
606 	}
607 
608 	/***
609 	 * @return scoringMode
610 	 */
611 	public String getScoringMode() {
612 		return scoringMode;
613 	}
614 
615 	/***
616 	 * @param scoringMode
617 	 *            how the scoring is done - quantitative, qualitative, or off
618 	 */
619 	public void setScoringMode(String scoringMode) {
620 		this.scoringMode = scoringMode;
621 	}
622 
623 	/***
624 	 * @return the scores
625 	 */
626 	public List<Integer> getScores() {
627 		return scores;
628 	}
629 
630 	/***
631 	 * @param scores
632 	 *            the scores to set
633 	 */
634 	public void setScores(List<Integer> scores) {
635 		logger.info("setScores");
636 		if (scores == null)
637 			throw new NullPointerException("null 'scores' argument'");
638 		this.scores = scores;
639 	}
640 
641 	/***
642 	 * @return the gotoStepIndex
643 	 */
644 	public int getGotoStepIndex() {
645 		return gotoStepIndex;
646 	}
647 
648 	/***
649 	 * @param gotoStepIndex
650 	 *            the gotoStepIndex to set
651 	 */
652 	public void setGotoStepIndex(int gotoStepIndex) {
653 		this.gotoStepIndex = gotoStepIndex;
654 	}
655 
656 	/***
657 	 * @return the gotoStep
658 	 */
659 	public PasStep getGotoStep() {
660 		return gotoStep;
661 	}
662 
663 	/***
664 	 * @param gotoStep
665 	 *            the step to go to when reviewing evidence
666 	 */
667 	public void setGotoStep(PasStep gotoStep) {
668 		if (gotoStep == null)
669 			throw new NullPointerException("null 'gotoStep' argument'");
670 		this.gotoStep = gotoStep;
671 	}
672 
673 	/***
674 	 * @return scoreReport
675 	 */
676 	public String getScoreReport() {
677 		Object correctAnswer = correctAnswer();
678 		String reportText = null;
679 		String lastAnswer = lastAnswer();
680 		int sNumAttempts = numStudentAttempts();
681 
682 		if (correctAnswer.equals(lastAnswer)) {
683 			reportText = getCorrectAnswerReport(sNumAttempts, scores, indexes);
684 		} else if (sNumAttempts >= scores.size()) {
685 			reportText = "Out of tries. 0 points.";
686 		} else {
687 			reportText = getWrongAnswerReport(sNumAttempts, scores, indexes);
688 		}
689 
690 		if (reportText == null) {
691 			String errMsg = "no score report available "
692 					+ "\n  so returning null";
693 			logger.severe(errMsg);
694 			return null;
695 		}
696 
697 		return reportText;
698 	}
699 
700 	/***
701 	 * provides scorereport to be shown for correct answer by stating how many
702 	 * attempts (tries) have been made and how many points have been earned
703 	 * 
704 	 * 
705 	 * @param sNumAttempts
706 	 * @param scores
707 	 * @param initHtml
708 	 * @return reportText
709 	 */
710 	protected String getCorrectAnswerReport(int sNumAttempts,
711 			List<Integer> scores, String[] indexes) {
712 		String reportText = null;
713 		int index = sNumAttempts - 1;
714 		String corrFeedback = indexes[index] + " try";
715 
716 		if (scores.get(index) > 0) {
717 			reportText = corrFeedback + " earned you " + scores.get(index)
718 					+ " points.";
719 		} else {
720 			reportText = "Correct on " + corrFeedback + ".";
721 		}
722 		return reportText;
723 	}
724 
725 	/***
726 	 * provides scorereport to be shown for wrong answer by stating how many
727 	 * attempts (tries) have been made and how many possible points are
728 	 * attainable (0 if that score attempt has a score of 0)
729 	 * 
730 	 * @param sNumAttempts
731 	 * @param scores
732 	 * @param initHtml
733 	 * @param attmptFeedback
734 	 * @return reportText
735 	 */
736 	private String getWrongAnswerReport(int sNumAttempts, List<Integer> scores,
737 			String[] indexes) {
738 		String reportText = null;
739 		if (scores.get(sNumAttempts) > 0) {
740 			// student hasn't gotten the question right, but can try again.
741 			reportText = indexes[sNumAttempts] + " try for "
742 					+ scores.get(sNumAttempts) + " points.";
743 		} else {
744 			reportText = indexes[sNumAttempts] + " try.";
745 		}
746 		return reportText;
747 	}
748 
749 	/***
750 	 * @return the indexes of the attempts
751 	 */
752 	public String[] getAttemptIndexes() {
753 		return indexes;
754 	}
755 
756 	/***
757 	 * @return the maxAttempts
758 	 */
759 	public int getMaxAttempts() {
760 		return maxAttempts;
761 	}
762 
763 	/***
764 	 * @param maxAttempts
765 	 *            the maxAttempts to set
766 	 */
767 	public void setMaxAttempts(int maxAttempts) {
768 		this.maxAttempts = maxAttempts;
769 	}
770 
771 	/***
772 	 * @return the cqUI
773 	 */
774 	public ChallengeQuestionUI getCqUI() {
775 		return this.cqUI;
776 	}
777 
778 	/***
779 	 * @param cqUI
780 	 *            the cqUI to set
781 	 */
782 	public void setCqUI(ChallengeQuestionUI cqUI) {
783 		this.cqUI = cqUI;
784 	}
785 
786 	/***
787 	 * @param navigationService
788 	 *            navigation service for being able to retrieve, visit, and
789 	 *            update steps in the navigation task panel
790 	 */
791 	public void setNavigationService(INavigationService navigationService) {
792 		this.navigationService = navigationService;
793 
794 	}
795 
796 	/***
797 	 * @return the navigationService
798 	 */
799 	public INavigationService getNavigationService() {
800 		return this.navigationService;
801 	}
802 
803 	/***
804 	 * @return the pasProject
805 	 */
806 	public PasProject getPasProject() {
807 		return this.pasProject;
808 	}
809 
810 	/***
811 	 * @param pasProject
812 	 *            the pasProject to set
813 	 */
814 	public void setPasProject(PasProject pasProject) {
815 		this.pasProject = pasProject;
816 	}
817 
818 	/***
819 	 * @param choiceOrdering -
820 	 *            sets the variable for whether to reorder choices or not
821 	 */
822 	public void setChoiceOrdering(String choiceOrdering) {
823 		this.choiceOrdering = choiceOrdering;
824 	}
825 
826 	/***
827 	 * @return choiceOrdering
828 	 */
829 	public String getChoiceOrdering() {
830 		return this.choiceOrdering;
831 	}
832 
833 	/***
834 	 * returns the inline feedback if the chosen answer is the correct one
835 	 * 
836 	 * @return newFeedbackInline
837 	 */
838 	public String getCorrectFeedbackInline() {
839 		List<BlockInteraction> theInteractions = assessmentItem.getItemBody()
840 				.getInteractions();
841 		ChoiceInteraction<String> interaction = (ChoiceInteraction<String>) theInteractions
842 				.get(0);
843 		List<SimpleChoice<String>> choices = interaction.getSimpleChoices();
844 		String newFeedbackInline = getFeedbackContentFromChoices(choices);
845 
846 		return newFeedbackInline;
847 	}
848 
849 	/***
850 	 * returns the inline feedback from the choices, choosing the one for the
851 	 * correct answer if the number of points for this attempt is greater than
852 	 * 0, the feedback would include saying that "you answered on the nth try
853 	 * for x points" where n is the number of attempts the user has made, and x
854 	 * is the number of points allotted for that attempt on a particular
855 	 * question, otherwise the part about the points is not included.
856 	 * 
857 	 * @return newFeedbackInline
858 	 */
859 	protected String getFeedbackContentFromChoices(List<SimpleChoice<String>> choices) {
860 		String newFeedbackInline = "";
861 
862 		for (int i = 0; i < choices.size(); i++) {
863 			String followupMessage = "You answered on the ";
864 			int numTry = numStudentAttempts();
865 			if (numTry == 0)
866 				break;
867 			List<Integer> scores = getScores();
868 			String correctAnswer = correctAnswer();
869 			if ((choices.get(i).getIdentifier()).equals(correctAnswer)) {
870 				String origFeedbackInline = extractBody(choices.get(i)
871 						.getFeedbackInline().getContent().toString());
872 				if (scores.get(numTry - 1) > 0) {
873 					newFeedbackInline = "<html><body>" + origFeedbackInline
874 							+ followupMessage + indexes[numTry - 1]
875 							+ " try for " + scores.get(numTry - 1) + " points."
876 							+ "</body></html>";
877 				} else {
878 					newFeedbackInline = "<html><body>" + origFeedbackInline
879 							+ followupMessage + indexes[numTry - 1]
880 							+ " try.</body></html>";
881 				}
882 			}
883 
884 		}
885 		return newFeedbackInline;
886 	}
887 
888 	/***
889 	 * returns the inline feedback for the last answer chosen
890 	 * 
891 	 * @return newFeedbackInline
892 	 */
893 	public String getLastFeedbackInline(Assessment assessment) {
894 		// TODO Auto-generated method stub
895 
896 		ResponseDeclaration rd = assessmentItem.getResponseDeclarations()
897 				.get(0);
898 		String lastAnswer = Assessment.lastAnswer(assessment
899 				.getResponseDeclarationToSocks(), rd);
900 		List<BlockInteraction> theInteractions = assessmentItem.getItemBody()
901 				.getInteractions();
902 		ChoiceInteraction<String> interaction = (ChoiceInteraction<String>) theInteractions
903 				.get(0);
904 		List<SimpleChoice<String>> choices = interaction.getSimpleChoices();
905 		String newFeedbackInline = "";
906 
907 		if (lastAnswer == null) {
908 			throw new IllegalStateException(
909 					"displayLastFeedbackInline() called while no lastAnswer() available");
910 		}
911 		for (int i = 0; i < choices.size(); i++) {
912 			String followupMessage = "Click the evidence button below and review the earlier evidence. Then return to this Challenge Question for a ";
913 			String finalFollowupMessage = "Sorry, you're out of tries. Move on to the next step.";
914 			int numTry = numStudentAttempts();
915 
916 			if ((choices.get(i).getIdentifier()).equals(lastAnswer)) {
917 				String origFeedbackInline = extractBody(choices.get(i)
918 						.getFeedbackInline().getContent().toString());
919 				if (numTry >= getMaxAttempts()) {
920 					newFeedbackInline = "<html><body>" + origFeedbackInline
921 							+ finalFollowupMessage + "</body></html>";
922 				} else {
923 					newFeedbackInline = "<html><body>" + origFeedbackInline
924 							+ followupMessage + indexes[numTry] + " try."
925 							+ "</body></html>";
926 				}
927 			}
928 		}
929 		return newFeedbackInline;
930 	}
931 
932 	/***
933 	 * @param responseDeclarations
934 	 * @param iterator
935 	 * @param filteredPrompt
936 	 * @param bi
937 	 */
938 	private void setBiPrompt(List<ResponseDeclaration> responseDeclarations,
939 			int iterator, BlockInteraction bi) {
940 		String oldPrompt = bi.getPrompt();
941 		int index = oldPrompt.indexOf("Part");
942 		int index2 = oldPrompt.indexOf("Partner");
943 		String filteredPrompt = extractBody(oldPrompt);
944 
945 		if (responseDeclarations.size() > 1) {
946 			if ((index < 0) || (index == index2)) {
947 				bi.setPrompt("<html><body> Part " + iterator + " : "
948 						+ filteredPrompt + "</body></html>");
949 			} else {
950 				bi
951 						.setPrompt("<html><body>" + filteredPrompt
952 								+ "</body></html>");
953 			}
954 		} else {
955 			bi.setPrompt("<html><body>" + filteredPrompt + "</body></html>");
956 		}
957 	}
958 
959 	/***
960 	 * Extracts the text in between the <body>...</body>
961 	 */
962 	@Override
963 	protected String extractBody(String prompt) {
964 		int start = prompt.indexOf("<body>");
965 		int end = prompt.indexOf("</body>");
966 
967 		String extractedBody = "";
968 
969 		if (start != -1 && end != -1) {
970 			extractedBody = prompt.substring(start + 6, end);
971 			return extractedBody;
972 		}
973 		return prompt;
974 	}
975 
976 	/***
977 	 * @see org.telscenter.pas.beans.IWorkReporter#getEntityToPromptMap()
978 	 */
979 	public Map<String, String> getEntityToPromptMap() {
980 		Map<String, String> rimToPromptMap = new HashMap<String, String>();
981 		List<ResponseDeclaration> responseDeclarations = assessmentItem
982 		    .getResponseDeclarations();
983 		for (ResponseDeclaration responseDeclaration : responseDeclarations) {
984 			String rimname = responseDeclaration.getRim().getName();
985 			BlockInteraction bi = getInteractionByResponseDeclaration(responseDeclaration);
986 			String prompt = bi.getPrompt();
987 
988 			rimToPromptMap.put(rimname, prompt);
989 		}
990 		return rimToPromptMap;
991 	}
992 
993 
994 }