[virtualcommons-svn] commit/irrigation: alllee: fixes issue 11, refactoring quiz processing logic a
Status: Beta
Brought to you by:
alllee
From: Bitbucket <com...@bi...> - 2012-03-06 00:14:37
|
1 new commit in irrigation: https://bitbucket.org/virtualcommons/irrigation/changeset/4f9d6b264634/ changeset: 4f9d6b264634 user: alllee date: 2012-03-06 01:14:37 summary: fixes issue 11, refactoring quiz processing logic and merging quiz questions into a single page affected #: 4 files diff -r 84f2fd3f32f88e70e3bd87b0285f7fb32ef55f9d -r 4f9d6b2646344773728e205e0c2e8d312944ce2d src/main/java/edu/asu/commons/irrigation/client/ExperimentGameWindow.java --- a/src/main/java/edu/asu/commons/irrigation/client/ExperimentGameWindow.java +++ b/src/main/java/edu/asu/commons/irrigation/client/ExperimentGameWindow.java @@ -10,22 +10,23 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.TreeMap; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.StyleSheet; import edu.asu.commons.irrigation.conf.RoundConfiguration; import edu.asu.commons.irrigation.conf.ServerConfiguration; @@ -69,30 +70,18 @@ private JEditorPane contributionInformationEditorPane; - private JButton nextButton; - - private JPanel instructionsNavigationPanel; - - private JButton previousButton; - private JPanel instructionsPanel; - private int currentQuizPageNumber = 1; - private JPanel submitTokenPanel; private HtmlEditorPane tokenInstructionsEditorPane; -// private int quizzesAnswered = 0; - private TokenContributionChartPanel tokenContributionChartPanel; // private CanalAnimationPanel canalAnimationPanel; private CardLayout cardLayout; - private Map<Integer, String> quizPageResponses = new HashMap<Integer, String>(); - private JLabel investedTokensLabel; public ExperimentGameWindow(IrrigationClient client) { @@ -163,79 +152,7 @@ } return instructionsPanel; } - - private JPanel getQuizNavigationPanel() { - if (instructionsNavigationPanel == null) { - instructionsNavigationPanel = new JPanel(); - instructionsNavigationPanel.setLayout(new BorderLayout()); - instructionsNavigationPanel.add(getPreviousButton(), BorderLayout.LINE_START); - instructionsNavigationPanel.add(getNextButton(), BorderLayout.LINE_END); - } - return instructionsNavigationPanel; - } - private JButton getPreviousButton() { - if (previousButton == null) { - previousButton = new JButton(); - previousButton.setText("Previous"); - previousButton.setEnabled(false); - previousButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - // getting the next instruction Number - if (currentQuizPageNumber > 1) { - currentQuizPageNumber--; - setInstructions(getQuizPage()); - } - previousButton.setEnabled(currentQuizPageNumber > 1); - nextButton.setEnabled(true); - } - }); - } - return previousButton; - } - - private JButton getNextButton() { - if (nextButton == null) { - nextButton = new JButton(); - nextButton.setText("Next"); - nextButton.setEnabled(false); - nextButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - previousButton.setEnabled(true); - currentQuizPageNumber++; - if (currentQuizPageNumber <= getServerConfiguration().getNumberOfQuizPages()) { - setInstructions(getQuizPage()); - } - else { - // this only works in between practice rounds #1 & #2 or after practice round 2 - // should just regenerate all the instructions... - nextButton.setEnabled(false); - disableQuiz(); - showDebriefing(false); -// setInstructions(instructionsBuilder.toString()); - } - } - }); - - } - return nextButton; - } - - // FIXME: replace with StringTemplate - private String getQuizPage() { - StringBuilder builder = new StringBuilder(); - String quizPage = getServerConfiguration().getQuizPage(currentQuizPageNumber); - String quizPageResponse = quizPageResponses.get(currentQuizPageNumber); - if (quizPageResponse == null) { - builder.append(quizPage); - } - else { - quizPage = quizPage.replace("<input type=\"submit\" name=\"submit\" value=\"Submit\">", ""); - builder.append(quizPage).append(quizPageResponse); - } - return builder.toString(); - } - private ServerConfiguration getServerConfiguration() { return clientDataModel.getServerConfiguration(); } @@ -333,65 +250,9 @@ instructionsBuilder.append(clientDataModel.getRoundConfiguration().generateClientDebriefing(clientDataModel, showExitInstructions)); setInstructions(instructionsBuilder.toString()); } - -// /** -// * FIXME: refactor to use StringTemplate -// * @param event -// */ -// private void addDebriefingText(EndRoundEvent event) { -// double showUpPayment = clientDataModel.getServerConfiguration().getShowUpPayment(); -// RoundConfiguration roundConfiguration = clientDataModel.getRoundConfiguration(); -// // FIXME: move this to RoundConfiguration instead and then templatize using -// // StringTemplate -// instructionsBuilder.delete(0, instructionsBuilder.length()); -// instructionsBuilder.append("<b>Results from the previous round</b>"); -// instructionsBuilder.append( -// "<table border='3' cellpadding='5'><thead><th>Position</th><th>Initial token endowment</th><th>Tokens invested</th><th>Tokens not invested</th>" + -// "<th>Tokens earned from growing crops</th><th>Total tokens earned during this round</th>" + -// "<th>Dollars earned during this round</th><th>Total dollars earned (including show-up bonus)</th></thead>" + -// "<tbody>"); -// -// for(ClientData clientData : clientDataModel.getClientDataSortedByPriority()) { -// String backgroundColor = clientData.getPriority() == clientDataModel.getPriority() ? "#FFFFCC" : "CCCCCC"; -// instructionsBuilder.append( -// String.format("<tr align='center' bgcolor='%s'><td>%s</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>$%3.2f</td><td>$%3.2f</td></tr>", -// backgroundColor, -// clientData.getPriorityString(), -// roundConfiguration.getMaximumTokenInvestment(), -// clientData.getInvestedTokens(), -// clientData.getUninvestedTokens(), -// clientData.getTokensEarnedFromWaterCollected(), -// clientData.getAllTokensEarnedThisRound(), -// clientData.getTotalDollarsEarnedThisRound(), -// clientData.getTotalDollarsEarned() + showUpPayment -// )); -// } -// -// ClientData clientData = clientDataModel.getClientData(); -// instructionsBuilder.append("</tbody></table><hr>"); -// instructionsBuilder.append(String.format("<h3>You (position %s) received $%3.2f this past round. Your total earnings are $%3.2f, including the $%3.2f show up bonus.</h3>", -// clientData.getPriorityString(), clientData.getTotalDollarsEarnedThisRound(), clientData.getTotalDollarsEarned()+showUpPayment, showUpPayment)); -// //append the added practice round instructions -// -// if (roundConfiguration.isPracticeRound()) { -// instructionsBuilder.append(roundConfiguration.getPracticeRoundPaymentInstructions()); -// } -// else if (event.isLastRound()) { -// instructionsBuilder.append(getServerConfiguration().getFinalInstructions()); -// } -// instructionsBuilder.append("<hr>"); -// displayInstructions(instructionsBuilder.toString()); -// } - - // adding the instructions into the instruction Panel - private void setInstructions(final String instructions) { - setInstructions(instructions, false); - } - private void setInstructions(String instructions, boolean caretToEnd) { + private void setInstructions(String instructions) { instructionsEditorPane.setText(instructions); - int caretPosition = caretToEnd ? instructionsEditorPane.getDocument().getLength() - 1 : 0; - instructionsEditorPane.setCaretPosition(caretPosition); } private void displayInstructions(final String instructions) { @@ -407,68 +268,41 @@ return new ActionListener() { private Map<String, String> quizAnswers = configuration.getQuizAnswers(); public synchronized void actionPerformed(ActionEvent e) { - if (quizPageResponses.containsKey(currentQuizPageNumber)) { - // this form has already been submit. - // shouldn't happen - // FIXME: report to user? - return; + FormActionEvent formEvent = (FormActionEvent) e; + Properties actualAnswers = formEvent.getData(); + List<String> incorrectQuestionNumbers = new ArrayList<String>(); + List<String> correctQuestionNumbers = new ArrayList<String>(); + for (Map.Entry<String, String> entry : quizAnswers.entrySet()) { + String questionNumber = entry.getKey(); + String number = questionNumber.substring(1); + String correctAnswer = entry.getValue(); + String actualAnswer = actualAnswers.getProperty(questionNumber); + if (actualAnswer == null) { + JOptionPane.showMessageDialog(ExperimentGameWindow.this, "Please enter a quiz answer for question " + number + "."); + return; + } + ((correctAnswer.equals(actualAnswer)) ? correctQuestionNumbers : incorrectQuestionNumbers).add(questionNumber); } - FormActionEvent formEvent = (FormActionEvent) e; - Properties responses = formEvent.getData(); - List<String> incorrectAnswers = new ArrayList<String>(); - responses.list(System.err); - StringBuilder builder = new StringBuilder(); - TreeMap<String, String> sortedResponses = new TreeMap<String, String>(); - // sort responses so we can put them in order. - for (Map.Entry<Object, Object> entry : responses.entrySet()) { - sortedResponses.put((String) entry.getKey(), (String) entry.getValue()); - } - builder.append("<hr><h2>Results</h2><hr>"); - for (Map.Entry<String, String> entry : sortedResponses.entrySet()) { - String questionNumber = (String) entry.getKey(); - if (questionNumber.charAt(0) == 'q') { - String number = questionNumber.substring(1, questionNumber.length()); - String response = (String) entry.getValue(); - if (response == null || response.trim().isEmpty()) { - // if any responses are empty, abort. - - return; - } - String correctAnswer = quizAnswers.get(questionNumber); - builder.append(String.format("<p><b>Question %s</b><br/>", number)); - String color = "blue"; - if (! response.equals(correctAnswer)) { - incorrectAnswers.add(questionNumber); - color = "red"; - } - builder.append(String.format("Your answer: <font color='%s'>%s</font><br/>", color, response)); - builder.append(String.format("Correct answer: %s<br/>", quizAnswers.get("a" + number))); - builder.append(quizAnswers.get( "explanation" + number )).append("</p>"); - } - } - if (incorrectAnswers.isEmpty()) { - builder.append("<p>Congratulations, you got all of the questions correct.</p>"); - } - else { - builder.append(String.format("<p>You answered %d questions incorrectly. Please review the correct answers.</p>", incorrectAnswers.size())); - } - builder.append("<p><b>Please click the 'Next' button at the bottom right of the screen to continue.</b></p>"); - quizPageResponses.put(currentQuizPageNumber, builder.toString()); - // no matter what we move on to the next question page - // tell them what was right and what was wrong. - if (currentQuizPageNumber <= getServerConfiguration().getNumberOfQuizPages()) { - nextButton.setEnabled(true); - } -// quizzesAnswered++; - QuizResponseEvent event = new QuizResponseEvent(client.getId(), currentQuizPageNumber, responses, incorrectAnswers); + setQuestionColors(correctQuestionNumbers, "blue"); + setQuestionColors(incorrectQuestionNumbers, "red"); + QuizResponseEvent event = new QuizResponseEvent(client.getId(), actualAnswers, incorrectQuestionNumbers); System.err.println("Correct answers: " + event.getNumberOfCorrectQuizAnswers()); clientDataModel.getClientData().addCorrectQuizAnswers(event.getNumberOfCorrectQuizAnswers()); client.transmit(event); - setInstructions(getQuizPage(), true); + setInstructions(getServerConfiguration().getQuizResults(incorrectQuestionNumbers, actualAnswers)); } }; } + + private void setQuestionColors(List<String> questionNumbers, String color) { + HTMLEditorKit editorKit = (HTMLEditorKit) instructionsEditorPane.getEditorKit(); + StyleSheet styleSheet = editorKit.getStyleSheet(); + for (String questionNumber : questionNumbers) { + String styleString = String.format(".%s { color: %s; }", questionNumber, color); + styleSheet.addRule(styleString); + } + } public void displayContributionInformation(final ClientData clientData) { final RoundConfiguration configuration = clientDataModel.getRoundConfiguration(); @@ -576,22 +410,12 @@ public void showQuiz() { SwingUtilities.invokeLater(new Runnable() { public void run() { - setInstructions(getQuizPage()); - getInstructionsPanel().add(getQuizNavigationPanel(), BorderLayout.PAGE_END); + setInstructions(getServerConfiguration().getQuizInstructions()); getInstructionsPanel().revalidate(); } }); } - /** - * Should only be invoked when the instructions navigation panel is done. - * How do we know when it's done? - */ - private void disableQuiz() { - getInstructionsPanel().remove(getQuizNavigationPanel()); - getInstructionsPanel().revalidate(); - } - public void showGameScreenshot() { displayInstructions(getServerConfiguration().getGameScreenshotInstructions()); } diff -r 84f2fd3f32f88e70e3bd87b0285f7fb32ef55f9d -r 4f9d6b2646344773728e205e0c2e8d312944ce2d src/main/java/edu/asu/commons/irrigation/conf/ServerConfiguration.java --- a/src/main/java/edu/asu/commons/irrigation/conf/ServerConfiguration.java +++ b/src/main/java/edu/asu/commons/irrigation/conf/ServerConfiguration.java @@ -2,6 +2,7 @@ import java.text.NumberFormat; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -169,8 +170,8 @@ return getProperty("general-instructionsq" + pageNumber); } - public String getQuizPage(int pageNumber) { - return getProperty("quiz-page"+pageNumber); + public String getQuizInstructions() { + return render(getProperty("quiz-instructions")); } public String getWaterCollectedToTokensTable() { @@ -261,4 +262,19 @@ } } + public String getQuizResults(List<String> incorrectQuestionNumbers, Map<Object, Object> actualAnswers) { + ST template = createStringTemplate(getProperty("quiz-results")); + // FIXME: actual answers includes the submit button, so there's an off-by-one that we need to deal with. + int totalQuestions = actualAnswers.size() - 1; + int numberCorrect = totalQuestions - incorrectQuestionNumbers.size(); + template.add("allCorrect", incorrectQuestionNumbers.isEmpty()); + template.add("numberCorrect", numberCorrect); + template.add("totalQuestions", totalQuestions); + template.add("totalQuizEarnings", toCurrencyString(getQuizCorrectAnswerReward() * numberCorrect)); + for (String incorrectQuestionNumber : incorrectQuestionNumbers) { + template.add("incorrect_" + incorrectQuestionNumber, String.format("Your answer, %s, was incorrect.", actualAnswers.get(incorrectQuestionNumber))); + } + return template.render(); + } + } diff -r 84f2fd3f32f88e70e3bd87b0285f7fb32ef55f9d -r 4f9d6b2646344773728e205e0c2e8d312944ce2d src/main/java/edu/asu/commons/irrigation/events/QuizResponseEvent.java --- a/src/main/java/edu/asu/commons/irrigation/events/QuizResponseEvent.java +++ b/src/main/java/edu/asu/commons/irrigation/events/QuizResponseEvent.java @@ -20,26 +20,16 @@ private static final long serialVersionUID = -7081410122722056083L; - private int quizPage; private Properties responses; private List<String> incorrectAnswers; - public QuizResponseEvent(Identifier id, int quizPage, Properties responses, List<String> incorrectAnswers) { + public QuizResponseEvent(Identifier id, Properties responses, List<String> incorrectAnswers) { super(id); - this.quizPage = quizPage; this.responses = responses; this.incorrectAnswers = incorrectAnswers; } - - public boolean isComplete() { - return quizPage == 2; - } - - public int getQuizPage(){ - return quizPage; - } public Properties getResponses() { return responses; @@ -50,7 +40,7 @@ } public String toString() { - return id + " quiz page response: " + responses + "\n\t incorrect: " + incorrectAnswers; + return id + " quiz responses: " + responses + "\n\t incorrect: " + incorrectAnswers; } public int getNumberOfCorrectQuizAnswers() { diff -r 84f2fd3f32f88e70e3bd87b0285f7fb32ef55f9d -r 4f9d6b2646344773728e205e0c2e8d312944ce2d src/main/resources/configuration/asu/2011/pretest/irrigation.xml --- a/src/main/resources/configuration/asu/2011/pretest/irrigation.xml +++ b/src/main/resources/configuration/asu/2011/pretest/irrigation.xml @@ -29,7 +29,6 @@ <entry key='token-endowment'>10</entry><entry key='field-of-vision'>1</entry> -<entry key='quiz-pages'>2</entry><entry key="wait-for-participants">true</entry><entry key="number-of-rounds">22</entry> @@ -151,13 +150,12 @@ ]]></entry> -<entry key="quiz-page1"> +<entry key="quiz-instructions"><![CDATA[ <p> The first two questions deal with irrigation infrastructure investment. <b>Please refer to Table 1 of your handout</b>. </p> - <form> Question 1:<br> Given an existing infrastructure efficiency of 20%, if the five participants invest @@ -193,15 +191,6 @@ E?<br><input type='text' name='q4' value=''> cubic feet per second <br><br> - -<input type="submit" name="submit" value="Submit"><br> -<br> - -]]> -</entry> - -<entry key="quiz-page2"> -<![CDATA[ <p> The final two questions cover the number of tokens you can earn in within a round. <b>Please refer to table 2 of your handout</b>. @@ -219,8 +208,9 @@ the round?<br><input type="text" name="q6" value="">tokens <br><br> -<input type="submit" name="submit" value="Submit"> -</form> + +<input type="submit" name="submit" value="Submit"><br> +<br> ]]></entry> @@ -505,4 +495,85 @@ {endif} ]]></entry> +<entry key='quiz-results'> + <![CDATA[ + <h2>Quiz Results</h2> + <hr> + <p> + {if (allCorrect)} + You have answered all the questions correctly and earned <b>{totalQuizEarnings}</b>. + {else} + You answered {numberCorrect} out of {totalQuestions} questions correctly and earned <b>{totalQuizEarnings}</b>. Questions you've answered + incorrectly are highlighted in red. Please see below for more details. + {endif} + </p> + <br> + <hr> + <span class='q1'>Question 1:</span><br> + Given an existing infrastructure efficiency of 20%, if the five participants invest + a <b>total of 29 additional tokens</b>, what is the new irrigation infrastructure + efficiency? + <br><b>{incorrect_q1} + An existing infrastructure efficiency of 20% + 29 tokens invested = 49% infrastructure efficiency. + </b> + <br> + + <span class='q2'>Question 2:</span><br> + Suppose the infrastructure efficiency in the last round was 60%. For the current + round, the efficiency will decline by an amount of 25% for a resulting + infrastructure efficiency of 35%. If the members of your group invest a total of 15 + tokens, what will be your group's resulting water delivery capacity?<br> + <b>{incorrect_q2} + An existing infrastructure efficiency of 35% + 15 tokens invested = 50% infrastructure efficiency for the current round. + An infrastructure efficiency of 50% corresponds to a water delivery capacity of 5 cubic feet per second.</b> + <br><br><br> + +<b>The next two questions deal with the capacity of the irrigation system in relation +to the actual amount of water available.</b> +<br><br> + <span class='q3'>Question 3:</span><br> + If the irrigation efficiency is between 71 and 80%, the water delivery capacity of + the irrigation system is 35 cubic feet per second. Suppose the water supply + available to your group is 30 cubic feet per second and A opens their gate, + diverting water at 25 cubic feet per second. What is the available water flow for + B? + <br> + <b>{incorrect_q3} + Since there is only 30 cubic feet per second of water available, the irrigation infrastructure capacity of 35 cubic feet + per second will not be fully used. When A opens their gate, they take 25 of the 30 cubic feet per second out of the + canal, leaving 5 cubic feet per second for B. + </b> + <br><br><br> + <span class='q4'>Question 4:</span> + If the available water delivery capacity is 25 cubic feet per second and A, B, C, + and D are not using water, how much cubic feet of water per second is available for + E?<br> + <b>{incorrect_q4} + If A has 25 cfps available and does not take any water, the same amount is available for the people + downstream. Since B, C and D do not take water, 25 cfps is available for E. + </b> + <br><br> +<p> +The final two questions cover the number of tokens you can earn in within a round. +<b>Please refer to table 2 of your handout</b>. +</p> +<span class='q5'>Question 5:</span><br> +If you invest 7 of the 10 tokens you start with and you apply 202 cubic feet of +water to your fields, what is the total number of tokens you will have earned at the +end of the round?<br> + <b>{incorrect_q5} + If you invest 7 out of 10 tokens, you keep 3 tokens for yourself. If you apply 202 cubic feet of water to your field you + will earn 4 tokens. 3 + 4 = 7 tokens + <br><br> +<span class='q6'>Question 6:</span><br> +If you invest all 10 tokens you start with and you apply 555 cubic feet of water to +your fields, what is the total number of tokens you will have earned at the end of +the round?<br> +<br> + <b>{incorrect_q6} + If you invest all 10 tokens, you keep 0 tokens from your initial endowment. If you apply 555 cubic feet of + water to your field you will earn 19 tokens. 0 + 19 = 19 tokens. + </b> + ]]> +</entry></properties> Repository URL: https://bitbucket.org/virtualcommons/irrigation/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. |