1 /***
2 *
3 */
4 package org.telscenter.pas.steps;
5
6 import java.awt.BorderLayout;
7 import java.awt.Color;
8 import java.awt.event.ActionEvent;
9 import java.awt.event.ActionListener;
10 import java.awt.event.ComponentEvent;
11 import java.awt.event.ComponentListener;
12 import java.awt.Dimension;
13 import java.awt.FlowLayout;
14 import java.awt.GridBagConstraints;
15 import java.awt.GridBagLayout;
16 import java.io.InputStream;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Random;
22
23 import javax.swing.BorderFactory;
24 import javax.swing.BoxLayout;
25 import javax.swing.ButtonGroup;
26 import javax.swing.ImageIcon;
27 import javax.swing.JButton;
28 import javax.swing.JComponent;
29 import javax.swing.JLabel;
30 import javax.swing.JPanel;
31 import javax.swing.JRadioButton;
32 import javax.swing.JScrollPane;
33 import javax.swing.JTextArea;
34 import javax.swing.border.CompoundBorder;
35
36 import net.sf.sail.core.entity.ISock;
37
38 import java.util.logging.Logger;
39
40 import org.apache.commons.io.IOUtils;
41 import org.telscenter.pas.beans.PasProject;
42 import org.telscenter.pas.beans.PasStep;
43 import org.telscenter.pas.common.ui.CommonUI;
44 import org.telscenter.pas.common.ui.panel.SimpleGradientPanel;
45 import org.telscenter.pas.navigation.PasProjectNavigationTaskPanel;
46 import org.telscenter.pas.service.INavigationService;
47 import org.telscenter.pas.service.NavigateAction;
48 import org.telscenter.pas.steps.actions.LeaveStudentAssessmentAction;
49 import org.telscenter.pas.steps.domain.BlockInteraction;
50 import org.telscenter.pas.steps.domain.ChoiceInteraction;
51 import org.telscenter.pas.steps.domain.FeedbackInline;
52 import org.telscenter.pas.steps.domain.FeedbackModal;
53 import org.telscenter.pas.steps.domain.ResponseDeclaration;
54 import org.telscenter.pas.steps.actions.ChallengeQuestionCheckAction;
55 import org.telscenter.pas.steps.domain.SimpleChoice;
56 import org.telscenter.pas.ui.icons.PasCommonIconProvider;
57 import org.telscenter.pas.ui.util.PasColors;
58 import org.telscenter.pas.util.QTIUtils;
59 import org.xhtmlrenderer.simple.XHTMLPanel;
60
61 /***
62 * @author aperritano
63 *
64 */
65 public class ChallengeQuestionUI extends AssessmentUI {
66
67 /***
68 * Logger for this class
69 */
70 static final Logger logger = Logger
71 .getLogger(StudentAssessmentUI.class.getName());
72
73 private static final long serialVersionUID = 1L;
74
75 protected String introductionHtml = "";
76
77 protected JButton submitQuestionsButton;
78
79 protected String QUESTION_CARD = "QUESTION_CARD";
80
81 protected String RESULT_CARD = "RESULT_CARD";
82
83
84 private HashMap<String, JLabel> feedbackLabels;
85
86
87 private HashMap<String, JRadioButton> radiobuttons;
88
89
90 public boolean hasSubmited = true;
91
92 public boolean toLeaveStep = false;
93
94 protected JPanel submitButtonPanel;
95
96 protected LeaveStudentAssessmentAction leaveAction;
97
98
99
100 private JButton processAttemptButton;
101
102
103
104 private JButton gotoStepButton;
105
106
107 private ImageIcon redX;
108
109
110 private JLabel scoreLabel;
111
112
113 private JLabel inlineFbTextLabel;
114
115
116 private JRadioButton scButton;
117
118
119 private JPanel choicePanel;
120
121 private List<JRadioButton> choiceButtonList = new ArrayList<JRadioButton>();
122
123 private static final int CHOICE_CONTENT_WIDTH = 300;
124
125 private static final int INLINE_FEEDBACK_WIDTH = 300;
126
127 private static final int INLINE_FEEDBACK_HEIGHT = 100;
128
129
130
131
132
133 public static final String indexes[] = { "1st", "2nd", "3rd", "4th", "5th",
134 "6th", "7th", "8th", "9th", "10th" };
135
136 public ChallengeQuestionUI(Assessment assmt) {
137 super(assmt);
138 }
139
140 /***
141 *
142 * creates and integrates all the buttons, feedback icons, score label, prompt panel, choices panel, feedback panel for showing in VLE
143 * the final step for the actual VLE output itself is controlled by getComponent in ChallengeQuestion.java
144 *
145 * @return
146 */
147 @Override
148 protected void initUI() {
149 if( assessment.getAssessmentItem() == null || assessment.getAssessmentItem().getItemBody().getInteractions().size() == 0 ) {
150 this.setLayout(new FlowLayout(FlowLayout.CENTER));
151 this.setOpaque(false);
152 JLabel noLabel = new JLabel("This Note has no items.");
153 noLabel.setOpaque(false);
154 this.add(noLabel, BorderLayout.CENTER);
155 return;
156 }
157 List<BlockInteraction> interactions = assessment.getAssessmentItem().getItemBody().getInteractions();
158 radiobuttons = new HashMap<String, JRadioButton>();
159 feedbackLabels = new HashMap<String, JLabel>();
160 gotoStepButton = createGotoStepButton();
161 gotoStepButton.setVisible(false);
162 clearFeedbackInline();
163
164 JPanel titlePanel = createTitlePanel();
165 JPanel scorePanel = createScorePanel();
166 JPanel buttonPanel = createButtonPanel();
167 JPanel cqChoicePanel = cQ_choiceInteractionPanel(interactions, titlePanel, scorePanel, buttonPanel);
168
169
170 JPanel scrollerPanel = createScrollerPanel(cqChoicePanel);
171
172 cardPanel.add(scrollerPanel, QUESTION_CARD);
173
174 SimpleGradientPanel mainPanel = createSimpleGradientPanel();
175 GridBagConstraints c = createGridBagConstraint();
176 mainPanel.add(cardPanel, c);
177
178
179
180 Object correctAnswer = ((ChallengeQuestion)assessment).correctAnswer();
181 getCorrectFeedback(correctAnswer);
182
183 updateScoreLabelStatus();
184 mainPanel.validate();
185
186 this.setLayout(new BorderLayout(0,0));
187 this.add(mainPanel,BorderLayout.CENTER);
188
189 }
190
191 /**************************************** START OF LAYOUT ****************************************************/
192 /***
193 * @return c
194 * creates the position of the component to be shown via grid bag layout
195 */
196 private GridBagConstraints createGridBagConstraint() {
197 GridBagConstraints c = new GridBagConstraints();
198
199 c.fill = GridBagConstraints.BOTH;
200
201 c.weightx = 1;
202 c.gridx = 0;
203 c.gridy = 0;
204
205
206 c.gridx = 0;
207 c.gridy = 0;
208
209
210 c.weighty = 1;
211 c.gridx = 0;
212 c.gridy = 0;
213 c.fill = GridBagConstraints.BOTH;
214 return c;
215 }
216
217 /******************************** ENDING OF LAYOUT ********************************************************/
218 /******************************** THE MAIN PANELS **********************************************************
219 * TITLE, SCORE, BUTTON, PROMPT & CHOICE, CHOICE & FEEDBACK, CHOICE INTERACTION, GRADIENT, AND SCROLLING PANELS
220 */
221 /***
222 * creates the challenge question title panel
223 *
224 *
225 * @return titlePanel
226 */
227 private JPanel createTitlePanel(){
228 String labelValue = "<html><body>" + "<p style='font-size:16;font-weight:bold;text-align:left;'>" +
229 "Challenge Question" + "</body></html>";
230
231 JPanel titlePanel = new JPanel();
232 titlePanel.setLayout(new BorderLayout(0,0));
233
234 titlePanel.add(new JLabel(labelValue), BorderLayout.CENTER);
235 return titlePanel;
236 }
237
238 /***
239 * creates the challenge question score panel, with score report shown in blue text
240 *
241 *
242 * @return scorePanel
243 */
244 private JPanel createScorePanel(){
245 JPanel scorePanel = new JPanel();
246 scorePanel.setOpaque(false);
247 scoreLabel = new JLabel();
248 scoreLabel.setForeground(Color.BLUE);
249 scorePanel.add(scoreLabel, BorderLayout.CENTER);
250 return scorePanel;
251 }
252
253 /***
254 * creates the check action button panel
255 *
256 *
257 * @return buttonPanel
258 */
259 private JPanel createButtonPanel(){
260 ChallengeQuestionCheckAction checkAction = getCQCheckAction();
261 processAttemptButton = new JButton();
262 processAttemptButton.setAction(checkAction);
263 processAttemptButton.setEnabled(false);
264 CommonUI.makeEnhancedButton(processAttemptButton);
265 JPanel buttonPanel = new JPanel();
266 buttonPanel.add(processAttemptButton, BorderLayout.NORTH);
267 buttonPanel.setPreferredSize(new Dimension(buttonPanel.getPreferredSize().width, 20));
268 buttonPanel.setBackground(PasColors.challengeQuestionChoiceInteractionTextAreaColor);
269 return buttonPanel;
270 }
271
272 /***
273 * gets choice and feedback panel, adds the score panel, prompt panel, and the choice & feedback panel to a single panel to return
274 *
275 * @param scorePanel
276 * @param promptPanel
277 * @param choicePanel
278 * @param buttonPanel
279 * @return allPartsPanel
280 */
281 private JPanel getPromptAndChoicePanel(JPanel scorePanel, JComponent promptPanel, JPanel choicePanel, JPanel buttonPanel){
282 JPanel allPartsPanel = new JPanel();
283 allPartsPanel.setLayout(new BoxLayout(allPartsPanel, BoxLayout.Y_AXIS));
284 allPartsPanel.setBackground(Color.white);
285 allPartsPanel.putClientProperty(ITEM_COMPLETED, new Boolean(false));
286 JPanel choiceAndFeedbackPanel = getChoiceAndFeedbackPanel(choicePanel, buttonPanel);
287 allPartsPanel.setBackground(PasColors.challengeQuestionChoiceInteractionTextAreaColor);
288 allPartsPanel.add(scorePanel,BorderLayout.NORTH);
289 allPartsPanel.add(promptPanel, BorderLayout.CENTER);
290 allPartsPanel.add(choiceAndFeedbackPanel,BorderLayout.SOUTH);
291 return allPartsPanel;
292 }
293
294 /***
295 * adds the multiple choice panel, inline feedback text label, and check action button panel to
296 * choice and feedback panel
297 *
298 * @param choicePanel
299 * @param buttonPanel
300 * @return choiceAndFeedbackPanel
301 */
302 private JPanel getChoiceAndFeedbackPanel(JPanel choicePanel, JPanel buttonPanel) {
303 JPanel choiceAndFeedbackPanel = new JPanel();
304 choiceAndFeedbackPanel.setLayout(new BoxLayout(choiceAndFeedbackPanel, BoxLayout.Y_AXIS));
305 choiceAndFeedbackPanel.setPreferredSize(new Dimension(450,350));
306 choiceAndFeedbackPanel.setBackground(PasColors.challengeQuestionChoiceInteractionTextAreaColor);
307
308 JPanel inlineFbTextPanel = new JPanel();
309 inlineFbTextLabel = new JLabel();
310 inlineFbTextLabel.setPreferredSize(new Dimension(INLINE_FEEDBACK_WIDTH,INLINE_FEEDBACK_HEIGHT));
311 inlineFbTextPanel.add(inlineFbTextLabel);
312 inlineFbTextPanel.setBackground(PasColors.challengeQuestionChoiceInteractionTextAreaColor);
313 if(!inlineFbTextLabel.getText().equals("")){
314 inlineFbTextPanel.setPreferredSize(new Dimension(INLINE_FEEDBACK_WIDTH/2, INLINE_FEEDBACK_HEIGHT));
315
316 }else{
317 inlineFbTextPanel.setPreferredSize(new Dimension(0,0));
318 }
319 choiceAndFeedbackPanel.add(choicePanel);
320 choiceAndFeedbackPanel.add(inlineFbTextPanel);
321 choiceAndFeedbackPanel.add(buttonPanel);
322
323 return choiceAndFeedbackPanel;
324 }
325
326 /***
327 * creates a simple gradient panel for background
328 *
329 * @return mainPanel
330 */
331 private SimpleGradientPanel createSimpleGradientPanel() {
332 SimpleGradientPanel mainPanel = new SimpleGradientPanel();
333 mainPanel.setDirection(SimpleGradientPanel.VERTICAL);
334 mainPanel.setStartColor(PasColors.compactNavBackgroundStartColor);
335 mainPanel.setEndColor(PasColors.compactNavBackgroundEndColor);
336 mainPanel.setLayout(new GridBagLayout());
337 return mainPanel;
338 }
339
340 /***
341 * Creates all the interactions
342 *
343 * @param interactions
344 * @param titlePanel
345 * @param scorePanel
346 * @param buttonPanel
347 * @return a panel with all the parts needed for this challenge question - title, prompt and multiple choices, and the check action button panel
348 */
349 protected JPanel cQ_choiceInteractionPanel(List<BlockInteraction> interactions, JPanel titlePanel, JPanel scorePanel, JPanel buttonPanel) {
350 JPanel mainPanel = new JPanel();
351 mainPanel.setBackground(PasColors.pandaPanelBackgroundColor);
352 mainPanel.setLayout(new BorderLayout(0, 0));
353
354 JPanel tPane = new JPanel();
355 tPane.setBackground(PasColors.challengeQuestionChoiceInteractionTextAreaColor);
356 JPanel allPanel = null;
357
358 choicePanel = new JPanel();
359 choicePanel.setLayout(new BoxLayout(choicePanel, BoxLayout.Y_AXIS));
360 choicePanel.setBackground(PasColors.challengeQuestionChoiceInteractionTextAreaColor);
361
362 JComponent createPromptPanel=null;
363
364
365
366
367 for (int i = 0; i < interactions.size(); i++) {
368 ChoiceInteraction<String> choiceInteraction = (ChoiceInteraction<String>) interactions
369 .get(i);
370 choiceInteraction = reOrderChoices(choiceInteraction);
371 createPromptPanel = createPromptPanel(choiceInteraction);
372 createPromptPanel.setBackground(PasColors.challengeQuestionChoiceInteractionTextAreaColor);
373 setChoicesToButtons(choiceInteraction);
374 JPanel promptAndChoicePanel = getPromptAndChoicePanel(scorePanel, createPromptPanel, choicePanel, buttonPanel);
375 allPanel = new JPanel();
376 allPanel.setLayout(new BoxLayout(allPanel, BoxLayout.Y_AXIS));
377 allPanel.add(titlePanel);
378 allPanel.add(promptAndChoicePanel);
379 allPanel.add(gotoStepButton);
380 tPane.add(allPanel, BorderLayout.CENTER);
381 }
382
383 mainPanel.add(tPane, BorderLayout.CENTER);
384
385 return mainPanel;
386 }
387
388 /***
389 * Creates the scroller panel for adding the challenge question panel created
390 *
391 * @param questionsPanel
392 * @return scrollerPanel
393 */
394
395 private JPanel createScrollerPanel(JPanel questionsPanel) {
396 JScrollPane questionScroller = new JScrollPane(questionsPanel);
397 questionScroller
398 .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
399 questionScroller
400 .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
401 questionScroller.setBorder(BorderFactory.createEmptyBorder());
402 questionScroller.getVerticalScrollBar().setValue(
403 questionScroller.getVerticalScrollBar().getMaximum());
404 questionScroller.setOpaque(false);
405 JPanel scrollerPanel = new JPanel(new BorderLayout(0, 0));
406 scrollerPanel.add(questionScroller, BorderLayout.CENTER);
407 scrollerPanel.setOpaque(true);
408 scrollerPanel.setBackground(PasColors.pandaPanelBackgroundColor);
409 return scrollerPanel;
410 }
411
412 /*********************************CLOSING OF THE PANELS ********************************************************/
413 /*********************************** BUTTON CREATION ***********************************************************
414 * createGotoStepButton
415 * getChoiceButton
416 */
417 /***
418 * Creates goto step button
419 *
420 * @param thisPanel
421 * @param j
422 * @param step
423 * @return
424 */
425 public JButton createGotoStepButton() {
426 PasProjectNavigationTaskPanel navigationTaskPanel = ((ChallengeQuestion) assessment).getPasProject().getPanel();
427 PasStep gotoStep = ((ChallengeQuestion) assessment).getGotoStep();
428 INavigationService navigationService = ((ChallengeQuestion) assessment).getNavigationService();
429 JButton gotoStepButton = new JButton(new NavigateAction(gotoStep, navigationService, navigationTaskPanel));
430 gotoStepButton.setHorizontalTextPosition(JLabel.LEFT);
431 gotoStepButton.setContentAreaFilled(false);
432 gotoStepButton.setFont(gotoStepButton.getFont().deriveFont(16.0f));
433 gotoStepButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0));
434 gotoStepButton.setText("Go to Evidence Step");
435
436 gotoStepButton.addActionListener(new ActionListener() {
437 public void actionPerformed(ActionEvent e) {
438 PasProject pasProject = ((ChallengeQuestion) assessment).getPasProject();
439 ISock<String> navigationLogSock = pasProject.getNavigationLogSock();
440 if (navigationLogSock != null) {
441
442 navigationLogSock.add(pasProject.getItemLog("step_open", (((ChallengeQuestion) assessment).getGotoStep())));
443 }
444 }
445 });
446 CommonUI.makeLink(gotoStepButton);
447 return gotoStepButton;
448 }
449
450 /***
451 * Creates multiple choice radio button with action listener for setting student selection
452 *
453 * @param studentAnswer
454 * @param sc
455 * @param choiceInteraction
456 * @return button
457 */
458 private JRadioButton getChoiceButton(String studentAnswer, SimpleChoice<String> sc, ChoiceInteraction<String> choiceInteraction){
459 JRadioButton button = new JRadioButton();
460
461 if (studentAnswer != null
462 && studentAnswer.equals(sc.getIdentifier())) {
463 button.setSelected(true);
464 } else {
465 button.setSelected(false);
466 }
467 button.setOpaque(false);
468 button.putClientProperty(QTIUtils.SIMPLE_CHOICE, sc);
469 button.putClientProperty(RESPONSE_ID, choiceInteraction
470 .getResponseIdentifier());
471
472
473 button.addActionListener(new ActionListener() {
474
475 public void actionPerformed(ActionEvent e) {
476 JRadioButton radioButton = (JRadioButton) e.getSource();
477
478 if (radioButton.isSelected()) {
479 SimpleChoice<String> sc = ((SimpleChoice<String>) radioButton
480 .getClientProperty(QTIUtils.SIMPLE_CHOICE));
481
482 assessment.getResponses().put(
483 (String) radioButton
484 .getClientProperty(RESPONSE_ID),
485 sc.getIdentifier());
486
487 logger.info("responses: "
488 + assessment.getResponses().toString());
489 processAttemptButton.setEnabled(true);
490 }
491 }
492 });
493
494 return button;
495 }
496
497 /********************************************************************************************************/
498 /*********************************** BUTTON ACTION ***********************************************************/
499 /***
500 * disables the 'check answer' button after an attempt has been made (until the next time the cq step is visited)
501 * as long as one has more tries remaining or did not yet choose the correct answer
502 *
503 *
504 *
505 * @return
506 */
507
508 public void disableProcessAttemptButton() {
509 this.processAttemptButton.setEnabled(false);
510 }
511
512 /***
513 * Greys out SimpleChoice radio buttons
514 *
515 * @param buttonIdentifier
516 * @param isCorrect
517 * @return
518 */
519
520 public void disableRadioButtons(Object buttonIdentifier, boolean isCorrect) {
521 JRadioButton rb = radiobuttons.get(buttonIdentifier);
522
523 for (JRadioButton radioButton : radiobuttons.values()) {
524 if(!radioButton.equals(rb)){
525 radioButton.setEnabled(false);
526 }else{
527 if(isCorrect){
528 rb.setIcon(greenCheck);
529 }else{
530 rb.setIcon(redX);
531 }
532 }
533 }
534 }
535
536 /***
537 * selects the radio button and also sets the appropriate image icon
538 *
539 * @param buttonIdentifier
540 * @param isCorrect
541 */
542 public void selectRadioButton(Object buttonIdentifier, boolean isCorrect) {
543 JRadioButton rb = radiobuttons.get(buttonIdentifier);
544 if (isCorrect) {
545 rb.setIcon(greenCheck);
546 rb.setForeground(new Color(0,220,0));
547 } else {
548 rb.setIcon(redX);
549 rb.setForeground(Color.RED);
550 }
551 rb.setSelected(true);
552 }
553
554 /***
555 * Removes the icons associated with all of the radio buttons and reverts to
556 * default
557 *
558 * @return
559 */
560 public void clearRadioButtonIcons() {
561 for (JRadioButton radioButton : radiobuttons.values()) {
562 radioButton.setIcon(null);
563 radioButton.setForeground(Color.BLACK);
564 }
565 }
566
567 /************************************* END BUTTON ACTION ***************************************************/
568 /************************************ CHOICE INTERACTIONS **************************************************/
569 /***
570 * reorders the choices once the cq step is refreshed (revisited) as long as the correct answer was not chosen yet
571 * updates the score board and report
572 * clears all the inline feedbacks
573 * disables the process attempt button until the user chooses a choice
574 *
575 * @return
576 */
577
578 @SuppressWarnings("cast")
579 public void updateCqUI_Interactions(){
580 String correctAnswer = ((ChallengeQuestion)assessment).correctAnswer();
581 String studentAnswer = ((ChallengeQuestion)assessment).lastAnswer();
582 boolean correctAnswerChosen = correctAnswer.equals(studentAnswer);
583 int attemptNum = ((ChallengeQuestion)assessment).numStudentAttempts();
584 boolean outOfAttempts = attemptNum >= ((ChallengeQuestion)assessment).getMaxAttempts();
585
586 updateScoreLabelStatus();
587 processAttemptButton.setEnabled(false);
588
589
590 scoreLabel.setVisible(true);
591 this.inlineFbTextLabel.setText("");
592 clearRadioButtonIcons();
593 List<BlockInteraction> theInteractions = assessment.getAssessmentItem().getItemBody().getInteractions();
594 choiceButtonList = new ArrayList<JRadioButton>();
595
596
597
598 for (int i = 0; i < theInteractions.size(); i++) {
599 ChoiceInteraction<String> choiceInteraction = (ChoiceInteraction<String>) theInteractions.get(i);
600 choiceInteraction = reOrderChoices(choiceInteraction);
601 theInteractions.set(i, (BlockInteraction)choiceInteraction);
602 radiobuttons.clear();
603 choicePanel.removeAll();
604 inlineFbTextLabel.setText("");
605 setupChoiceButtonPanel(choiceInteraction, "", true);
606 }
607
608 assessment.getAssessmentItem().getItemBody().setInteractions(theInteractions);
609 }
610
611
612 /***
613 * assigns each choice content to a radio button for displaying as part of the multiple choice button
614 * panel
615 *
616 * @return
617 */
618 private void setupChoiceButtonPanel(ChoiceInteraction<String> ci, String answer, boolean feedbackAdjusted){
619 List<SimpleChoice<String>> theChoices = ci.getSimpleChoices();
620 ButtonGroup group = new ButtonGroup();
621
622
623 for (SimpleChoice<String> sc : theChoices) {
624 scButton = getChoiceButton(answer,sc,ci);
625
626 JPanel jp = new JPanel();
627 jp.setLayout(new BoxLayout(jp, BoxLayout.Y_AXIS));
628 jp.add(scButton);
629 jp.setOpaque(false);
630 if(!feedbackAdjusted)
631 adjustFeedback(sc);
632 String theContent = sc.getContent();
633
634
635
636 scButton.setText(theContent.trim());
637 scButton.setIcon(null);
638 choicePanel.add(jp, BorderLayout.NORTH);
639 radiobuttons.put(sc.getIdentifier(), scButton);
640 choiceButtonList.add(scButton);
641 group.add(scButton);
642 }
643 }
644
645 /***
646 * Randomizes the choices
647 * @param interaction
648 * @return
649 */
650 private ChoiceInteraction<String> reOrderChoices(ChoiceInteraction<String> interaction){
651 String prompt = interaction.getPrompt();
652 String promptAttrs = "<P style='font-size:20;text-align:center;'>";
653 String initHtml = "<html><body>";
654 String finalHtml = "</P></body></html>";
655 String updatedPrompt = initHtml + promptAttrs + extractBody(prompt) + finalHtml;
656 List<SimpleChoice<String>> choices = interaction.getSimpleChoices();
657 interaction.setPrompt(updatedPrompt);
658 List<SimpleChoice<String>> newChoices = new ArrayList<SimpleChoice<String>>(choices.size());
659 Random rand = new Random();
660 while (choices.size() > 0) {
661 newChoices.add(choices.remove(rand.nextInt(choices.size())));
662 }
663
664 interaction.setSimpleChoices(newChoices);
665 return interaction;
666 }
667
668
669 /***
670 * maps the student answer to the corresponding radio button, and maps the choice content to the radio buttons as well
671 *
672 * @param choiceInteraction
673 * @return
674 */
675 private void setChoicesToButtons(
676 ChoiceInteraction<String> choiceInteraction) {
677 choiceButtonList = new ArrayList<JRadioButton>();
678
679 String studentAnswer = Assessment.getStudentAnswer(assessment,
680 choiceInteraction);
681
682 setupChoiceButtonPanel(choiceInteraction, studentAnswer, false);
683 choiceInteractionToChoiceButtonLists.put(choiceInteraction,
684 choiceButtonList);
685 }
686
687 /*********************************************************************************************************/
688
689 /**************************** SCORE STATUS **************************************************
690 * This section determines what the score status should be - correct, wrong, or out of attempts
691 * the corresponding status is shown on the score label and also stored in the reportText for
692 * displaying in show all work
693 ********************************************************************************************/
694
695 /***
696 * Displays a status message on the bottom of the screen Either: -- What
697 * score the student can get -- What score the student has gotten
698 *
699 * @return
700 */
701 public void updateScoreLabelStatus() {
702 int sNumAttempts = ((ChallengeQuestion)assessment).numStudentAttempts();
703 List<Integer> scores = ((ChallengeQuestion) assessment).getScores();
704 String correctAnswer = ((ChallengeQuestion)assessment).correctAnswer();
705 String feedbackSize = "<p style='font-size:16;font-weight:bold;text-align:center;'>";
706 String origText = null;
707 String initHtml = "<html><body>";
708 String finalHtml = "</p></body></html>";
709
710
711
712
713
714 setScoreLabel(sNumAttempts, scores, correctAnswer);
715
716 origText = scoreLabel.getText();
717 scoreLabel.setText(initHtml + feedbackSize + origText + finalHtml);
718 scoreLabel.setVisible(false);
719 }
720
721 /***
722 * sets the score label outcome of the choice student selected and chose as their current answer,
723 * where the outcome is dependent on whether the most recent answer selected was correct,
724 * incorrect, or the number of attempts have been reached or exceeded
725 *
726 * @param sNumAttempts
727 * @param scores
728 * @param correctAnswer
729 * @return
730 */
731 private void setScoreLabel(int sNumAttempts, List<Integer> scores,
732 String correctAnswer) {
733 String lastAnswer = ((ChallengeQuestion)assessment).lastAnswer();
734
735 if (correctAnswer.equals(lastAnswer)){
736 int index = sNumAttempts-1;
737 String corrFeedback = indexes[index] + " try";
738 setCorrectAnswerLabel(index, corrFeedback, scores);
739
740 } else if (sNumAttempts >= scores.size()) {
741 scoreLabel.setText("Sorry, you're out of tries.");
742 } else {
743 String attmptFeedback = indexes[sNumAttempts] + " try";
744 setWrongAnswerLabel(sNumAttempts, attmptFeedback, scores);
745 }
746
747 }
748
749 /***
750 * updates the score label if the answer is wrong; if there are points, the points are shown in the label
751 * otherwise they are not shown
752 *
753 * @param sNumAttempts
754 * @param attmptFeedback
755 * @param scores
756 * @return
757 */
758 private void setWrongAnswerLabel(int sNumAttempts, String attmptFeedback, List<Integer> scores) {
759 if(scores.get(sNumAttempts) > 0){
760 scoreLabel.setText(attmptFeedback + " for "
761 + scores.get(sNumAttempts) + " points.");
762 }else{
763 scoreLabel.setText(attmptFeedback + ".");
764 }
765 }
766
767 /***
768 * updates the score label if the answer is correct; if there are points, the points are shown in the label
769 * otherwise they are not shown
770 *
771 * @param index
772 * @param corrFeedback
773 * @param scores
774 * @return
775 */
776 private void setCorrectAnswerLabel(int index, String corrFeedback, List<Integer> scores) {
777 if(scores.get(index) > 0){
778 scoreLabel.setText(corrFeedback + " for " + scores.get(index)
779 + " points.");
780 }else{
781 scoreLabel.setText(corrFeedback + ".");
782 }
783 }
784
785 /*********************************** ENDS SCORE STATUS **************************************************/
786 /************************************ FEEDBACK INLINE **************************************************/
787
788 /***
789 * set all inline feedback labels to be invisible
790 *
791 * @return
792 */
793 public void clearFeedbackInline() {
794 if (feedbackLabels == null)
795 return;
796
797 for (JLabel label : feedbackLabels.values()) {
798 label.setVisible(false);
799 }
800 }
801
802 /***
803 * disables the radio buttons, shows the correct inline feedback with corresponding radio button chosen
804 *
805 * @param correctAnswer
806 */
807 private void getCorrectFeedback(Object correctAnswer) {
808 int numStudentAttempts = ((ChallengeQuestion)assessment).numStudentAttempts();
809 int maxAttempts = ((ChallengeQuestion)assessment).getMaxAttempts();
810 String lastAnswer = ((ChallengeQuestion)assessment).lastAnswer();
811
812 if (numStudentAttempts >= maxAttempts
813 || lastAnswer != null && lastAnswer.equals(correctAnswer)) {
814 disableRadioButtons(correctAnswer,false);
815 displayCorrectFeedbackInline();
816 selectRadioButton(correctAnswer, true);
817
818 }
819 }
820
821 /***
822 * shows the correct inline feedback
823 *
824 * @return
825 */
826 public void displayCorrectFeedbackInline() {
827 String newFeedbackInline = ((ChallengeQuestion)assessment).getCorrectFeedbackInline();
828 displayInlineFeedbackLabel(newFeedbackInline);
829 }
830
831 /***
832 * show last attempted choice's feedback
833 *
834 * precondition: there is at least one previous answer in studentSock
835 * @return
836 */
837 public void displayLastFeedbackInline() {
838 String newFeedbackInline = ((ChallengeQuestion)assessment).getLastFeedbackInline(assessment);
839 displayInlineFeedbackLabel(newFeedbackInline);
840 }
841
842
843 private void displayInlineFeedbackLabel(String newFeedbackInline){
844 if(!newFeedbackInline.equals("")){
845 inlineFbTextLabel.setText(newFeedbackInline);
846 inlineFbTextLabel.setForeground(Color.BLUE);
847 inlineFbTextLabel.setVisible(true);
848 }
849 }
850
851
852 /***
853 * wraps the inline feedback, adds it to a feedback label and puts it to the list of feedbackLabels
854 *
855 * @param sc
856 * @return
857 */
858
859 private void adjustFeedback(SimpleChoice<String> sc){
860 int adjustedWidth = 50;
861 String inlineFeedbackText = null;
862 FeedbackInline inlineFeedback = sc.getFeedbackInline();
863
864 if (inlineFeedback != null) {
865 inlineFeedbackText = (String) inlineFeedback
866 .getContent();
867 inlineFeedbackText = wrapInlineText(inlineFeedbackText,adjustedWidth);
868 inlineFeedback.setContent(inlineFeedbackText);
869 }
870 JLabel fiLabel = new JLabel();
871
872
873 feedbackLabels.put(sc.getIdentifier(), fiLabel);
874 fiLabel.setLayout(new BoxLayout(fiLabel,BoxLayout.Y_AXIS));
875 fiLabel.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0));
876
877 }
878
879
880 /***
881 * @return label
882 */
883 protected JLabel createNoNoteTakenLabel() {
884 JLabel noNoteTakenLabel = new JLabel(Messages
885 .getString("Note.NOTE_NOT_TAKEN"));
886 noNoteTakenLabel.setFont(noNoteTakenLabel.getFont().deriveFont(
887 14.0f));
888 noNoteTakenLabel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0,
889 0));
890 noNoteTakenLabel.setOpaque(false);
891 return noNoteTakenLabel;
892 }
893
894 /***
895 * @return panel - an empty box layout panel with components to be aligned vertically
896 */
897 protected JPanel createVerticalBoxLayoutPanel(){
898 JPanel panel = new JPanel();
899 panel.setOpaque(false);
900 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
901 return panel;
902 }
903
904 /***
905 * Creates a simple, border-layout panel
906 * @return JPanel border layout
907 */
908 protected JPanel createBorderLayoutPanel() {
909 JPanel borderPanel = new JPanel();
910 borderPanel.setLayout(new BorderLayout(0, 0));
911 borderPanel.setOpaque(false);
912 return borderPanel;
913 }
914
915
916
917 /***
918 * @return CompoundBorder
919 */
920 protected CompoundBorder createCompoundBorder(){
921 return BorderFactory
922 .createCompoundBorder(BorderFactory.createEmptyBorder(
923 0, 8, 0, 0), BorderFactory.createLineBorder(
924 PasColors.noteReportLineBorderColor, 1));
925 }
926
927 /***
928 * @param answer - creates a textarea for the answer to be stored into,
929 * in this case red (if not answered)
930 * or black (if answered)
931 * @return answerArea
932 */
933 protected JTextArea createAnswerArea(String answer) {
934 JTextArea answerArea = new JTextArea();
935 answerArea.setOpaque(true);
936 answerArea.setBackground(PasColors.noteReportTextAreaColor);
937
938 if(answer.indexOf("NOT ANSWERED") > -1){
939 answerArea.setForeground(Color.RED);
940 }else{
941 answerArea.setForeground(Color.BLACK);
942 }
943
944 answerArea.setText(answer);
945 answerArea.setBorder(CommonUI.createEtchedBorder());
946 answerArea.setBorder(BorderFactory.createMatteBorder(0,0,1,0,Color.BLACK));
947 return answerArea;
948 }
949 /************************************END FEEDBACK INLINE **************************************************/
950 /************************************************** TEXT PROCESSING **************************************************/
951 /***
952 * wraps the inline text but cutting off each line to a specified width and continuing
953 * the remainder of the text, repeating this process until the string has been fully wrapped
954 */
955 public String wrapInlineText(String inlineFeedbackText, int adjustedWidth){
956 String initHtml = "<html><body>";
957 String finalHtml = "</body></html>";
958 String finalStr = "";
959 int lastIndex = 0;
960
961 inlineFeedbackText = inlineFeedbackText.trim();
962
963 while(inlineFeedbackText.length() > adjustedWidth){
964 String substri = inlineFeedbackText.substring(0,adjustedWidth);
965 lastIndex = substri.lastIndexOf(" ");
966 finalStr = finalStr + substri.substring(0,lastIndex) + "\n";
967 inlineFeedbackText = inlineFeedbackText.substring((int)lastIndex, inlineFeedbackText.length());
968
969 }
970 return initHtml + finalStr + inlineFeedbackText.substring(0,inlineFeedbackText.length()) + finalHtml;
971 }
972
973 /***
974 * extracts everything between the body tags
975 *
976 * @param prompt
977 * @return extractedBody
978 */
979
980 private String extractBody(String prompt) {
981 int start = prompt.indexOf("<body>");
982 int end = prompt.indexOf("</body>");
983
984 String extractedBody = "";
985
986 if (start != -1 && end != -1) {
987 extractedBody = prompt.substring(start+6, end);
988 }
989 return extractedBody;
990 }
991
992
993
994 /***********************************************END TEXT PROCESSING **************************************************/
995 /*********************************************************************************************************************/
996 /***********************************************GETTERS AND SETTERS **************************************************/
997
998 /***
999 * @return the check action for the check action button, the logic of which proceeds to
1000 * ChallengeQuestionCheckAction
1001 */
1002 protected ChallengeQuestionCheckAction getCQCheckAction() {
1003 ChallengeQuestionCheckAction checkAction = new ChallengeQuestionCheckAction(
1004 assessment.getAssessmentItem(), assessment.getResponses(),
1005 assessment.getResponseDeclarationToSocks(),
1006 (ChallengeQuestion) assessment);
1007 return checkAction;
1008 }
1009
1010 /***
1011 * @return the gotoStepButton
1012 */
1013 public JButton getGotoStepButton() {
1014 return gotoStepButton;
1015 }
1016
1017 /***
1018 * @param gotoStepButton the gotoStepButton to set
1019 */
1020 public void setGotoStepButton(JButton gotoStepButton) {
1021 this.gotoStepButton = gotoStepButton;
1022 }
1023
1024 /************************************************************************************************************************/
1025
1026 }