From f1e07a5d990970ef4017a019613afe6a9f563d5f Mon Sep 17 00:00:00 2001 From: Andras Eisenberger Date: Fri, 17 Apr 2015 13:06:20 +0200 Subject: [PATCH 1/7] Make it possible to add a custom evaluator to Python function problems --- .../app/client/page/EditProblemPage.java | 34 +++ .../EditOptionalStringFieldWithAceEditor.java | 195 ++++++++++++++++++ .../cloudcoder/app/client/view/ViewUtil.java | 21 ++ ...AddPythonFunctionScaffoldingBuildStep.java | 33 ++- .../app/shared/model/EditProblemAdapter.java | 11 + .../model/HashProblemAndTestCaseData.java | 4 +- .../app/shared/model/IProblemData.java | 11 + .../cloudcoder/app/shared/model/Problem.java | 12 +- .../app/shared/model/ProblemData.java | 38 +++- .../app/shared/model/RepoProblem.java | 12 +- .../app/server/persist/CreateSampleData.java | 1 + 11 files changed, 348 insertions(+), 24 deletions(-) create mode 100644 CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java diff --git a/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java b/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java index fd246d1b..58f49fc3 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java +++ b/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java @@ -31,6 +31,7 @@ import org.cloudcoder.app.client.view.EditDateTimeField; import org.cloudcoder.app.client.view.EditEnumField; import org.cloudcoder.app.client.view.EditModelObjectField; +import org.cloudcoder.app.client.view.EditOptionalStringFieldWithAceEditor; import org.cloudcoder.app.client.view.EditStringField; import org.cloudcoder.app.client.view.EditStringFieldWithAceEditor; import org.cloudcoder.app.client.view.PageNavPanel; @@ -278,6 +279,39 @@ private void setLanguage() { skeletonEditor.setEditorTheme(AceEditorTheme.VIBRANT_INK); problemFieldEditorList.add(skeletonEditor); + // In the editor for the evaluator, we keep the editor mode in sync + // with the problem type, like for the skeleton. + EditOptionalStringFieldWithAceEditor evaluatorEditor = + new EditOptionalStringFieldWithAceEditor("Evaluator code", ProblemData.EVALUATOR) { + @Override + public void update() { + setLanguage(); + super.update(); + setReadOnly(); + } + @Override + public void onModelObjectChange() { + setLanguage(); + setReadOnly(); + } + private void setLanguage() { + AceEditorMode editorMode = ViewUtil.getModeForLanguage(getModelObject().getProblemType().getLanguage()); + setEditorMode(editorMode); + } + private void setReadOnly() { + if(ViewUtil.isEvaluatorUsedForLanguage(getModelObject().getProblemType().getLanguage())) { + super.setEditorReadOnly(false); + super.setEditorTheme(AceEditorTheme.VIBRANT_INK); + } else { + super.setEditorReadOnly(true); + super.setEditorTheme(AceEditorTheme.SOLARIZED_DARK); + } + } + + }; + evaluatorEditor.setEditorTheme(AceEditorTheme.VIBRANT_INK); + problemFieldEditorList.add(evaluatorEditor); + // We don't need an editor for schema version - problems/testcases are // automatically converted to the latest version when they are imported. diff --git a/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java b/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java new file mode 100644 index 00000000..9279b17c --- /dev/null +++ b/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java @@ -0,0 +1,195 @@ +// CloudCoder - a web-based pedagogical programming environment +// Copyright (C) 2011-2012, Jaime Spacco +// Copyright (C) 2011-2012, David H. Hovemeyer +// Copyright (C) 2015, Andras Eisenberger +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package org.cloudcoder.app.client.view; + +import org.cloudcoder.app.shared.model.ModelObjectField; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.IsWidget; +import com.google.gwt.user.client.ui.Label; + +import edu.ycp.cs.dh.acegwt.client.ace.AceEditor; +import edu.ycp.cs.dh.acegwt.client.ace.AceEditorMode; +import edu.ycp.cs.dh.acegwt.client.ace.AceEditorTheme; + +/** + * Edit a string field of a model object using an {@link AceEditor}. + * + * @author David Hovemeyer + */ +public class EditOptionalStringFieldWithAceEditor + extends EditModelObjectField { + + private class UI extends EditModelObjectFieldUI { + private AceEditor editor; + private boolean editorStarted; + private AceEditorMode currentMode; + + public UI() { + FlowPanel panel = new FlowPanel(); + panel.setStyleName("cc-fieldEditor", true); + + Label label = new Label(getDescription()); + label.setStyleName("cc-fieldEditorLabel", true); + panel.add(label); + + panel.add(getErrorLabel()); + + editor = new AceEditor(); + editor.setSize("600px", "300px"); + panel.add(editor); + editorStarted = false; + + initWidget(panel); + } + + public boolean isEditorStarted() { + return editorStarted; + } + + public void startEditor() { + editor.startEditor(); + if (editorMode != null) { + editor.setMode(editorMode); + currentMode = editorMode; + } + editor.setTheme(editorTheme); + editor.setFontSize("14px"); + editorStarted = true; + } + + /** + * @param editorReadOnly whether the editor should be read-only now + */ + public void resetEditorReadOnly() { + editor.setReadOnly(editorReadOnly); + } + + public void setText(String text) { + editor.setText(text); + } + + public String getText() { + return editor.getText(); + } + + public void resetEditorMode() { + if (editorMode != null && editorMode != currentMode) { + editor.setMode(editorMode); + currentMode = editorMode; + GWT.log("Changing editor mode to " + editorMode); + } + } + + public void resetEditorTheme() { + if (editorTheme != null) { + editor.setTheme(editorTheme); + } + } + } + + private AceEditorMode editorMode; + private AceEditorTheme editorTheme; + private boolean editorReadOnly = false; + private UI ui; + + /** + * Constructor. + * + * @param desc human-readable description of field being edited + * @param field the {@link ModelObjectField} being edited + */ + public EditOptionalStringFieldWithAceEditor(String desc, ModelObjectField field) { + super(desc, field); + this.ui = new UI(); + } + + /** + * Set the editor mode. + * + * @param editorMode the editorMode to set + */ + public void setEditorMode(AceEditorMode editorMode) { + this.editorMode = editorMode; + if (ui.isEditorStarted()) { + ui.resetEditorMode(); + } + } + + /** + * Set the editor theme. + * + * @param editorTheme the editorTheme to set + */ + public void setEditorTheme(AceEditorTheme editorTheme) { + this.editorTheme = editorTheme; + if (ui.isEditorStarted()) { + ui.resetEditorTheme(); + } + } + + /** + * Set the editor theme. + * + * @param editorTheme the editorTheme to set + */ + public void setEditorReadOnly(boolean editorReadOnly) { + this.editorReadOnly = editorReadOnly; + if (ui.isEditorStarted()) { + ui.resetEditorReadOnly(); + } + } + + /* (non-Javadoc) + * @see org.cloudcoder.app.client.view.EditModelObjectField#getUI() + */ + @Override + public IsWidget getUI() { + return ui; + } + + /* (non-Javadoc) + * @see org.cloudcoder.app.client.view.EditModelObjectField#commit() + */ + @Override + public void commit() { + String text = ui.getText(); + if (text.length() > getModelObjectField().getSize()) { + setCommitError(true); + ui.setError("Value cannot be longer than " + getModelObjectField().getSize() + " characters"); + } else { + setCommitError(false); + ui.clearError(); + setField(text); + } + } + + @Override + public void update() { + if (!ui.isEditorStarted()) { + // At this point, we'll assume that the UI has been added to the page DOM, + // so it's safe to start the AceEditor. + ui.startEditor(); + } + + ui.setText(getField()); + } +} diff --git a/CloudCoder/src/org/cloudcoder/app/client/view/ViewUtil.java b/CloudCoder/src/org/cloudcoder/app/client/view/ViewUtil.java index f238ab09..79ea1914 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/view/ViewUtil.java +++ b/CloudCoder/src/org/cloudcoder/app/client/view/ViewUtil.java @@ -74,6 +74,27 @@ public static AceEditorMode getModeForLanguage(Language language) { // unknown Language return null; } + + /** + * Determine wherther custom evaluators can be used for {@link Language}. + * + * @param language the Language + * @return the AceEditorMode for the Language, or null if the Language is not known + */ + public static boolean isEvaluatorUsedForLanguage(Language language) { + switch (language) { + case PYTHON: + return true; + case JAVA: + case C: + case CPLUSPLUS: + case RUBY: + return false; + } + + // unknown Language + return false; + } /** * Create a Grid instance with a "loading" animated gif diff --git a/CloudCoderBuilder2/src/org/cloudcoder/builder2/pythonfunction/AddPythonFunctionScaffoldingBuildStep.java b/CloudCoderBuilder2/src/org/cloudcoder/builder2/pythonfunction/AddPythonFunctionScaffoldingBuildStep.java index d2d8e226..9e942840 100644 --- a/CloudCoderBuilder2/src/org/cloudcoder/builder2/pythonfunction/AddPythonFunctionScaffoldingBuildStep.java +++ b/CloudCoderBuilder2/src/org/cloudcoder/builder2/pythonfunction/AddPythonFunctionScaffoldingBuildStep.java @@ -70,7 +70,18 @@ private ProgramSource addScaffolding(ProgramSource programSource, Problem proble String programText = programSource.getProgramText(); test.append(programText + "\n"); programTextLength=StringUtil.countLines(programText); - int spaces=getIndentationIncrementFromPythonCode(programText); + + // Add code for evaluator + String evalText = problem.getEvaluator(); + if(evalText.trim().isEmpty()) { + // Default evaluator if no evaluator is given + evalText = "def _eval(_input, _expected):\n" + + " _output=" + problem.getTestname() + "(*_input)\n" + + " _result=(_expected == _output) if (type(_output) != float and type(_expected) != float) else (math.fabs(_output-_expected) < 0.00001)\n" + + " return (_result, _output)\n"; + } + + test.append(evalText + "\n"); for (TestCase t : testCaseList) { // each test case is a function that invokes the function being tested @@ -92,11 +103,8 @@ private ProgramSource addScaffolding(ProgramSource programSource, Problem proble // the test case passed, and a String containing the // actual output. // - test.append(indent(spaces)+"_output="+problem.getTestname() + - "(" +t.getInput()+ ")\n"); - test.append(indent(spaces)+"_expected=" + t.getOutput() + "\n"); - test.append(indent(spaces)+"_result=(_expected == _output) if (type(_output) != float and type(_expected) != float) else (math.fabs(_output-_expected) < 0.00001)\n"); - test.append(indent(spaces)+"return (_result, _output)\n"); + String in = t.getInput(); + test.append(" return _eval((" + in + (in.trim().isEmpty() ? "" : ",") + "), " + t.getOutput() + ")\n"); } // Convert to string, determine epilogue length @@ -107,17 +115,4 @@ private ProgramSource addScaffolding(ProgramSource programSource, Problem proble // Done! return new ProgramSource(result, prologueLength, epilogueLength); } - - private int getIndentationIncrementFromPythonCode(String programText) { - //TODO: Figure out the indentation scheme of the student submitted programTest - return 2; - } - - private String indent(int n) { - StringBuilder b=new StringBuilder(); - for (int i=0; i // Copyright (C) 2011-2013, David H. Hovemeyer // Copyright (C) 2013, York College of Pennsylvania +// Copyright (C) 2015, Eisenberger Andras // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -68,6 +69,16 @@ public interface IProblemData { */ public abstract String getSkeleton(); + /** + * @param evaluator the evaluator to set + */ + public abstract void setEvaluator(String evaluator); + + /** + * @return the evaluator, the code used to evaluate tests for a submission + */ + public abstract String getEvaluator(); + /** * Set the schema version. * diff --git a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/Problem.java b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/Problem.java index d815bb22..2970fa66 100644 --- a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/Problem.java +++ b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/Problem.java @@ -219,10 +219,20 @@ public class Problem extends ProblemData implements IProblem, IModelObject SCHEMA_V11 = ModelObjectSchema.basedOn(SCHEMA_V10) .finishDelta(); + /** + * Description of fields (schema version 12). + * This version incorporates schema changes from version 8 + * of {@link IProblemData}'s schema. + */ + public static final ModelObjectSchema SCHEMA_V12 = + ModelObjectSchema.basedOn(SCHEMA_V11) + .addDeltasFrom(ProblemData.SCHEMA_V8) + .finishDelta(); + /** * Description of fields (current schema version). */ - public static final ModelObjectSchema SCHEMA = SCHEMA_V11; + public static final ModelObjectSchema SCHEMA = SCHEMA_V12; /** * Number of fields. diff --git a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/ProblemData.java b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/ProblemData.java index d50d8810..d384f82a 100644 --- a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/ProblemData.java +++ b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/ProblemData.java @@ -50,6 +50,7 @@ public class ProblemData implements Serializable, IProblemData { private String parentHash; private String externalLibraryUrl; private String externalLibraryMD5; + private String evaluator; // Schema version 0 fields @@ -156,6 +157,14 @@ public class ProblemData implements Serializable, IProblemData { public String get(IProblemData obj) { return obj.getExternalLibraryMD5(); } }; + // Schema version 8 fields + + public static final ModelObjectField EVALUATOR = + new ModelObjectField("evaluator", String.class, 2000, ModelObjectIndexType.NONE, ModelObjectField.LITERAL) { + public void set(IProblemData obj, String value) { obj.setEvaluator(value); } + public String get(IProblemData obj) { return obj.getEvaluator(); } + }; + /** * Description of fields (version 0 schema). */ @@ -228,11 +237,18 @@ public class ProblemData implements Serializable, IProblemData { */ public static final ModelObjectSchema SCHEMA_V7 = ModelObjectSchema.basedOn(SCHEMA_V6) .finishDelta(); + + /** + * Description of fields (schema version 8). + */ + public static final ModelObjectSchema SCHEMA_V8 = ModelObjectSchema.basedOn(SCHEMA_V7) + .addAfter(EXTERNAL_LIBRARY_MD5, EVALUATOR) + .finishDelta(); /** * Description of fields (current schema). */ - public static final ModelObjectSchema SCHEMA = SCHEMA_V7; + public static final ModelObjectSchema SCHEMA = SCHEMA_V8; /** * Constructor. @@ -338,6 +354,22 @@ public boolean hasSkeleton() { return skeleton != null; } + /* (non-Javadoc) + * @see org.cloudcoder.app.shared.model.ProblemData#setEvaluator(java.lang.String) + */ + @Override + public void setEvaluator(String evaluator) { + this.evaluator = evaluator; + } + + /* (non-Javadoc) + * @see org.cloudcoder.app.shared.model.ProblemData#getEvaluator() + */ + @Override + public String getEvaluator() { + return evaluator; + } + /* (non-Javadoc) * @see org.cloudcoder.app.shared.model.ProblemData#setSchemaVersion(int) */ @@ -484,6 +516,7 @@ public void copyFrom(ProblemData other) { this.parentHash = other.parentHash; this.externalLibraryUrl = other.externalLibraryUrl; this.externalLibraryMD5 = other.externalLibraryMD5; + this.evaluator = other.evaluator; } @Override @@ -505,7 +538,8 @@ public boolean equals(Object obj) { && ModelObjectUtil.equals(this.license, other.license) && ModelObjectUtil.equals(this.parentHash, other.parentHash) && ModelObjectUtil.equals(this.externalLibraryUrl, other.externalLibraryUrl) - && ModelObjectUtil.equals(this.externalLibraryMD5, other.externalLibraryMD5); + && ModelObjectUtil.equals(this.externalLibraryMD5, other.externalLibraryMD5) + && ModelObjectUtil.equals(this.evaluator, other.evaluator); } /* diff --git a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/RepoProblem.java b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/RepoProblem.java index 5009beb4..edfceee1 100644 --- a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/RepoProblem.java +++ b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/RepoProblem.java @@ -127,10 +127,20 @@ public class RepoProblem extends ProblemData implements IModelObject SCHEMA_V7 = ModelObjectSchema.basedOn(SCHEMA_V6) .finishDelta(); + /** + * Description of fields (schema version 8). + * This version incorporates schema changes from version 8 + * of {@link IProblemData}'s schema. + */ + public static final ModelObjectSchema SCHEMA_V8 = + ModelObjectSchema.basedOn(SCHEMA_V7) + .addDeltasFrom(ProblemData.SCHEMA_V8) + .finishDelta(); + /** * Description of fields (current schema version). */ - public static final ModelObjectSchema SCHEMA = SCHEMA_V7; + public static final ModelObjectSchema SCHEMA = SCHEMA_V8; /** Number of fields. */ public static final int NUM_FIELDS = SCHEMA.getNumFields(); diff --git a/CloudCoderModelClassesPersistence/src/org/cloudcoder/app/server/persist/CreateSampleData.java b/CloudCoderModelClassesPersistence/src/org/cloudcoder/app/server/persist/CreateSampleData.java index 9b931582..3227e2af 100644 --- a/CloudCoderModelClassesPersistence/src/org/cloudcoder/app/server/persist/CreateSampleData.java +++ b/CloudCoderModelClassesPersistence/src/org/cloudcoder/app/server/persist/CreateSampleData.java @@ -105,6 +105,7 @@ public static void populateSampleProblemData(IProblemData problemData) { problemData.setParentHash(""); problemData.setExternalLibraryUrl(""); problemData.setExternalLibraryMD5(""); + problemData.setEvaluator(""); } public static void populateSampleCFunctionProblem(IProblem problem, int courseId) { From fb1c1d297d5b353efdb82a72c07dc67573d76a34 Mon Sep 17 00:00:00 2001 From: Andras Eisenberger Date: Sat, 18 Apr 2015 17:02:18 +0200 Subject: [PATCH 2/7] Add checkbox to optional string field --- .../app/client/page/EditProblemPage.java | 17 +-- .../EditOptionalStringFieldWithAceEditor.java | 105 ++++++++++++++---- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java b/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java index 58f49fc3..f98cdc9f 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java +++ b/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java @@ -282,34 +282,29 @@ private void setLanguage() { // In the editor for the evaluator, we keep the editor mode in sync // with the problem type, like for the skeleton. EditOptionalStringFieldWithAceEditor evaluatorEditor = - new EditOptionalStringFieldWithAceEditor("Evaluator code", ProblemData.EVALUATOR) { + new EditOptionalStringFieldWithAceEditor("Evaluator code", "Use custom evaluator", ProblemData.EVALUATOR) { @Override public void update() { - setLanguage(); super.update(); - setReadOnly(); + setLanguage(); } @Override public void onModelObjectChange() { setLanguage(); - setReadOnly(); } private void setLanguage() { AceEditorMode editorMode = ViewUtil.getModeForLanguage(getModelObject().getProblemType().getLanguage()); setEditorMode(editorMode); - } - private void setReadOnly() { + if(ViewUtil.isEvaluatorUsedForLanguage(getModelObject().getProblemType().getLanguage())) { - super.setEditorReadOnly(false); - super.setEditorTheme(AceEditorTheme.VIBRANT_INK); + setEnabled(true); } else { - super.setEditorReadOnly(true); - super.setEditorTheme(AceEditorTheme.SOLARIZED_DARK); + setEnabled(false); } } }; - evaluatorEditor.setEditorTheme(AceEditorTheme.VIBRANT_INK); + evaluatorEditor.setEditorThemes(AceEditorTheme.VIBRANT_INK, AceEditorTheme.SOLARIZED_DARK); problemFieldEditorList.add(evaluatorEditor); // We don't need an editor for schema version - problems/testcases are diff --git a/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java b/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java index 9279b17c..54836189 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java +++ b/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java @@ -21,6 +21,9 @@ import org.cloudcoder.app.shared.model.ModelObjectField; import com.google.gwt.core.client.GWT; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.IsWidget; @@ -39,11 +42,12 @@ public class EditOptionalStringFieldWithAceEditor extends EditModelObjectField { private class UI extends EditModelObjectFieldUI { + private CheckBox checkBox; private AceEditor editor; private boolean editorStarted; private AceEditorMode currentMode; - public UI() { + public UI(String checkboxLabel) { FlowPanel panel = new FlowPanel(); panel.setStyleName("cc-fieldEditor", true); @@ -53,8 +57,27 @@ public UI() { panel.add(getErrorLabel()); - editor = new AceEditor(); + this.checkBox = new CheckBox(checkboxLabel); + + this.editor = new AceEditor(); editor.setSize("600px", "300px"); + + checkBox.addValueChangeHandler(new ValueChangeHandler() { + + @Override + public void onValueChange(ValueChangeEvent event) { + if(event.getValue()) { + editor.setReadOnly(false); + editor.setTheme(editorEnabledTheme); + } else { + editor.setReadOnly(true); + editor.setTheme(editorDisabledTheme); + } + + } + }); + + panel.add(checkBox); panel.add(editor); editorStarted = false; @@ -71,7 +94,15 @@ public void startEditor() { editor.setMode(editorMode); currentMode = editorMode; } - editor.setTheme(editorTheme); + + resetEditorTheme(); + + if(checkBox.getValue()) { + editor.setReadOnly(false); + } else { + editor.setReadOnly(true); + } + editor.setFontSize("14px"); editorStarted = true; } @@ -79,8 +110,8 @@ public void startEditor() { /** * @param editorReadOnly whether the editor should be read-only now */ - public void resetEditorReadOnly() { - editor.setReadOnly(editorReadOnly); + public void resetEnabled() { + checkBox.setEnabled(enabled); } public void setText(String text) { @@ -91,6 +122,14 @@ public String getText() { return editor.getText(); } + public void setCheckBox(boolean value) { + checkBox.setValue(value, true); + } + + public boolean getCheckBox() { + return checkBox.getValue(); + } + public void resetEditorMode() { if (editorMode != null && editorMode != currentMode) { editor.setMode(editorMode); @@ -100,15 +139,22 @@ public void resetEditorMode() { } public void resetEditorTheme() { - if (editorTheme != null) { - editor.setTheme(editorTheme); + if(getCheckBox()) { + if (editorEnabledTheme != null) { + editor.setTheme(editorEnabledTheme); + } + } else { + if (editorDisabledTheme != null) { + editor.setTheme(editorDisabledTheme); + } } } } private AceEditorMode editorMode; - private AceEditorTheme editorTheme; - private boolean editorReadOnly = false; + private AceEditorTheme editorEnabledTheme; + private AceEditorTheme editorDisabledTheme; + private boolean enabled = true; private UI ui; /** @@ -117,9 +163,9 @@ public void resetEditorTheme() { * @param desc human-readable description of field being edited * @param field the {@link ModelObjectField} being edited */ - public EditOptionalStringFieldWithAceEditor(String desc, ModelObjectField field) { + public EditOptionalStringFieldWithAceEditor(String desc, String checkboxLabel, ModelObjectField field) { super(desc, field); - this.ui = new UI(); + this.ui = new UI(checkboxLabel); } /** @@ -139,22 +185,25 @@ public void setEditorMode(AceEditorMode editorMode) { * * @param editorTheme the editorTheme to set */ - public void setEditorTheme(AceEditorTheme editorTheme) { - this.editorTheme = editorTheme; + public void setEditorThemes(AceEditorTheme editorEnabledTheme, AceEditorTheme editorDisabledTheme) { + this.editorEnabledTheme = editorEnabledTheme; + this.editorDisabledTheme = editorDisabledTheme; if (ui.isEditorStarted()) { ui.resetEditorTheme(); } } - /** - * Set the editor theme. - * - * @param editorTheme the editorTheme to set - */ - public void setEditorReadOnly(boolean editorReadOnly) { - this.editorReadOnly = editorReadOnly; - if (ui.isEditorStarted()) { - ui.resetEditorReadOnly(); + public void setEnabled(boolean enabled) { + if(this.enabled != enabled) { + this.enabled = enabled; + ui.resetEnabled(); + if(enabled) { + if(!ui.getText().trim().isEmpty()) { + ui.setCheckBox(true); + } + } else { + ui.setCheckBox(false); + } } } @@ -171,7 +220,13 @@ public IsWidget getUI() { */ @Override public void commit() { - String text = ui.getText(); + String text; + if(ui.getCheckBox()) { + text = ui.getText(); + } else { + text = ""; + } + if (text.length() > getModelObjectField().getSize()) { setCommitError(true); ui.setError("Value cannot be longer than " + getModelObjectField().getSize() + " characters"); @@ -190,6 +245,10 @@ public void update() { ui.startEditor(); } + String field = getField(); ui.setText(getField()); + if(!field.isEmpty()) { + ui.setCheckBox(true); + } } } From 668b5ae2f60b22e1a6852047c7cb9d3ae63b357c Mon Sep 17 00:00:00 2001 From: Andras Eisenberger Date: Sun, 19 Apr 2015 15:30:36 +0200 Subject: [PATCH 3/7] A button to get the default evaluator to edit it. --- .../app/client/page/EditProblemPage.java | 40 +++++- .../app/client/view/EditDummyButton.java | 118 ++++++++++++++++++ .../EditOptionalStringFieldWithAceEditor.java | 10 +- .../cloudcoder/app/client/view/ViewUtil.java | 21 ---- ...AddPythonFunctionScaffoldingBuildStep.java | 6 +- .../app/shared/util/EvaluatorUtil.java | 65 ++++++++++ 6 files changed, 228 insertions(+), 32 deletions(-) create mode 100644 CloudCoder/src/org/cloudcoder/app/client/view/EditDummyButton.java create mode 100644 CloudCoderModelClasses/src/org/cloudcoder/app/shared/util/EvaluatorUtil.java diff --git a/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java b/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java index f98cdc9f..77d9cc3a 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java +++ b/CloudCoder/src/org/cloudcoder/app/client/page/EditProblemPage.java @@ -23,12 +23,13 @@ import org.cloudcoder.app.client.model.PageId; import org.cloudcoder.app.client.model.PageStack; import org.cloudcoder.app.client.model.Session; -import org.cloudcoder.app.client.model.StatusMessage; +import org.cloudcoder.app.client.model.StatusMessage; import org.cloudcoder.app.client.rpc.RPC; import org.cloudcoder.app.client.view.ChoiceDialogBox; import org.cloudcoder.app.client.view.EditBooleanField; import org.cloudcoder.app.client.view.EditDateField; import org.cloudcoder.app.client.view.EditDateTimeField; +import org.cloudcoder.app.client.view.EditDummyButton; import org.cloudcoder.app.client.view.EditEnumField; import org.cloudcoder.app.client.view.EditModelObjectField; import org.cloudcoder.app.client.view.EditOptionalStringFieldWithAceEditor; @@ -51,6 +52,7 @@ import org.cloudcoder.app.shared.model.ProblemLicense; import org.cloudcoder.app.shared.model.ProblemType; import org.cloudcoder.app.shared.model.TestCase; +import org.cloudcoder.app.shared.util.EvaluatorUtil; import org.cloudcoder.app.shared.util.SubscriptionRegistrar; import com.google.gwt.core.shared.GWT; @@ -96,6 +98,7 @@ private class UI extends ResizeComposite implements SessionObserver { private Button addTestCaseButton; private FlowPanel addTestCaseButtonPanel; private ProblemAndTestCaseList problemAndTestCaseListOrig; + EditOptionalStringFieldWithAceEditor evaluatorEditor; public UI() { this.dockLayoutPanel = new DockLayoutPanel(Unit.PX); @@ -281,8 +284,8 @@ private void setLanguage() { // In the editor for the evaluator, we keep the editor mode in sync // with the problem type, like for the skeleton. - EditOptionalStringFieldWithAceEditor evaluatorEditor = - new EditOptionalStringFieldWithAceEditor("Evaluator code", "Use custom evaluator", ProblemData.EVALUATOR) { + evaluatorEditor = + new EditOptionalStringFieldWithAceEditor("Evaluator code (only Python)", "Use custom evaluator", ProblemData.EVALUATOR) { @Override public void update() { super.update(); @@ -296,7 +299,7 @@ private void setLanguage() { AceEditorMode editorMode = ViewUtil.getModeForLanguage(getModelObject().getProblemType().getLanguage()); setEditorMode(editorMode); - if(ViewUtil.isEvaluatorUsedForLanguage(getModelObject().getProblemType().getLanguage())) { + if(EvaluatorUtil.isEvaluatorUsedForProblemType(getModelObject().getProblemType())) { setEnabled(true); } else { setEnabled(false); @@ -307,6 +310,35 @@ private void setLanguage() { evaluatorEditor.setEditorThemes(AceEditorTheme.VIBRANT_INK, AceEditorTheme.SOLARIZED_DARK); problemFieldEditorList.add(evaluatorEditor); + EditDummyButton defaultEvaluatorButton = + new EditDummyButton("Reset to default evaluator", ProblemData.EVALUATOR) { + @Override + public void onButtonClick() { + setField(EvaluatorUtil.getDefaultEvaluator(getModelObject())); + evaluatorEditor.update(); + } + + @Override + public void update() { + super.update(); + setLanguage(); + } + + @Override + public void onModelObjectChange() { + setLanguage(); + } + + private void setLanguage() { + if(EvaluatorUtil.isEvaluatorUsedForProblemType(getModelObject().getProblemType())) { + setEnabled(true); + } else { + setEnabled(false); + } + } + }; + + problemFieldEditorList.add(defaultEvaluatorButton); // We don't need an editor for schema version - problems/testcases are // automatically converted to the latest version when they are imported. diff --git a/CloudCoder/src/org/cloudcoder/app/client/view/EditDummyButton.java b/CloudCoder/src/org/cloudcoder/app/client/view/EditDummyButton.java new file mode 100644 index 00000000..82609f8a --- /dev/null +++ b/CloudCoder/src/org/cloudcoder/app/client/view/EditDummyButton.java @@ -0,0 +1,118 @@ +// CloudCoder - a web-based pedagogical programming environment +// Copyright (C) 2015, Andras Eisenberger +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package org.cloudcoder.app.client.view; + +import org.cloudcoder.app.client.page.EditProblemPage; +import org.cloudcoder.app.shared.model.ModelObjectField; + +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.IsWidget; + +/** + * Implementation of {@link EditModelObjectField} which does not edit any + * field. It extends {@link EditModelObjectField}, because only these can be + * on the {@link EditProblemPage} UI. + * + * @author Andras Eisenberger + */ +public abstract class EditDummyButton + extends EditModelObjectField { + + private class UI extends Composite { + private Button button; + + public UI() { + FlowPanel panel = new FlowPanel(); + panel.setStyleName("cc-fieldEditor"); + + this.button = new Button(getDescription(), new ClickHandler() { + public void onClick(ClickEvent event) { + onButtonClick(); + } + }); + button.setStyleName("cc-emphButton"); + panel.add(button); + + initWidget(panel); + } + + public void setEnabled(boolean enabled) { + button.setEnabled(enabled); + } + } + + private UI ui; + + /** + * Constructor. + * + * @param desc the human-readable description of the field, which is also + * the text of the button + * @param field any of the fields, it doesn't touch it by default + */ + public EditDummyButton(String desc, ModelObjectField field) { + super(desc, field); + ui = new UI(); + } + + /* (non-Javadoc) + * @see org.cloudcoder.app.client.view.EditModelObjectField#getUI() + */ + @Override + public IsWidget getUI() { + return ui; + } + + /* (non-Javadoc) + * @see org.cloudcoder.app.client.view.EditModelObjectField#commit() + */ + @Override + public void commit() { + } + + /* (non-Javadoc) + * @see org.cloudcoder.app.client.view.EditModelObjectField#update() + */ + @Override + public void update() { + } + + /* (non-Javadoc) + * @see org.cloudcoder.app.client.view.EditModelObjectField#isCommitError() + */ + @Override + public boolean isCommitError() { + // It's connected to no field, so no + return false; + } + + /** + * Do this when the button is clicked + */ + public abstract void onButtonClick(); + + /** + * @param enabled whether the button should be enabled + */ + public void setEnabled(boolean enabled) { + ui.setEnabled(enabled); + } +} diff --git a/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java b/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java index 54836189..4ec3916f 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java +++ b/CloudCoder/src/org/cloudcoder/app/client/view/EditOptionalStringFieldWithAceEditor.java @@ -24,7 +24,6 @@ import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Label; @@ -34,9 +33,9 @@ import edu.ycp.cs.dh.acegwt.client.ace.AceEditorTheme; /** - * Edit a string field of a model object using an {@link AceEditor}. + * Edit an optional string field of a model object using an {@link AceEditor}. * - * @author David Hovemeyer + * @author Andras Eisenberger */ public class EditOptionalStringFieldWithAceEditor extends EditModelObjectField { @@ -161,6 +160,7 @@ public void resetEditorTheme() { * Constructor. * * @param desc human-readable description of field being edited + * @param checkboxLabel label to display next to checkbox * @param field the {@link ModelObjectField} being edited */ public EditOptionalStringFieldWithAceEditor(String desc, String checkboxLabel, ModelObjectField field) { @@ -193,6 +193,10 @@ public void setEditorThemes(AceEditorTheme editorEnabledTheme, AceEditorTheme ed } } + /** + * @param enabled whether the editor should be enabled. Also unticks the + * checkbox when disabled. + */ public void setEnabled(boolean enabled) { if(this.enabled != enabled) { this.enabled = enabled; diff --git a/CloudCoder/src/org/cloudcoder/app/client/view/ViewUtil.java b/CloudCoder/src/org/cloudcoder/app/client/view/ViewUtil.java index 79ea1914..f238ab09 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/view/ViewUtil.java +++ b/CloudCoder/src/org/cloudcoder/app/client/view/ViewUtil.java @@ -74,27 +74,6 @@ public static AceEditorMode getModeForLanguage(Language language) { // unknown Language return null; } - - /** - * Determine wherther custom evaluators can be used for {@link Language}. - * - * @param language the Language - * @return the AceEditorMode for the Language, or null if the Language is not known - */ - public static boolean isEvaluatorUsedForLanguage(Language language) { - switch (language) { - case PYTHON: - return true; - case JAVA: - case C: - case CPLUSPLUS: - case RUBY: - return false; - } - - // unknown Language - return false; - } /** * Create a Grid instance with a "loading" animated gif diff --git a/CloudCoderBuilder2/src/org/cloudcoder/builder2/pythonfunction/AddPythonFunctionScaffoldingBuildStep.java b/CloudCoderBuilder2/src/org/cloudcoder/builder2/pythonfunction/AddPythonFunctionScaffoldingBuildStep.java index 9e942840..26d36600 100644 --- a/CloudCoderBuilder2/src/org/cloudcoder/builder2/pythonfunction/AddPythonFunctionScaffoldingBuildStep.java +++ b/CloudCoderBuilder2/src/org/cloudcoder/builder2/pythonfunction/AddPythonFunctionScaffoldingBuildStep.java @@ -23,6 +23,7 @@ import org.cloudcoder.app.shared.model.Problem; import org.cloudcoder.app.shared.model.ProblemType; import org.cloudcoder.app.shared.model.TestCase; +import org.cloudcoder.app.shared.util.EvaluatorUtil; import org.cloudcoder.builder2.model.BuilderSubmission; import org.cloudcoder.builder2.model.IBuildStep; import org.cloudcoder.builder2.model.InternalBuilderException; @@ -75,10 +76,7 @@ private ProgramSource addScaffolding(ProgramSource programSource, Problem proble String evalText = problem.getEvaluator(); if(evalText.trim().isEmpty()) { // Default evaluator if no evaluator is given - evalText = "def _eval(_input, _expected):\n" + - " _output=" + problem.getTestname() + "(*_input)\n" + - " _result=(_expected == _output) if (type(_output) != float and type(_expected) != float) else (math.fabs(_output-_expected) < 0.00001)\n" + - " return (_result, _output)\n"; + evalText = EvaluatorUtil.getDefaultPythonFunctionEvaluator(problem); } test.append(evalText + "\n"); diff --git a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/util/EvaluatorUtil.java b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/util/EvaluatorUtil.java new file mode 100644 index 00000000..5dc4581d --- /dev/null +++ b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/util/EvaluatorUtil.java @@ -0,0 +1,65 @@ +// CloudCoder - a web-based pedagogical programming environment +// Copyright (C) 2015, Andras Eisenberger +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package org.cloudcoder.app.shared.util; + +import org.cloudcoder.app.shared.model.IProblem; +import org.cloudcoder.app.shared.model.ProblemType; + +/** + * Utility methods problem evaluators + * + * @author Andras Eisenberger + */ +public class EvaluatorUtil { + /** + * Determine whether custom evaluators can be used for {@link ProblemType}. + * + * @param problemType the problem type + * @return the AceEditorMode for the Language, or null if the Language is not known + */ + public static boolean isEvaluatorUsedForProblemType(ProblemType problemType) { + switch (problemType) { + case PYTHON_FUNCTION: + return true; + default: + return false; + } + } + + /** + * Generate the default evaluator for the given problem. Returns empty + * {@link String} for problem types which don't use custom evaluators. + */ + public static String getDefaultEvaluator(IProblem problem) { + switch (problem.getProblemType()) { + case PYTHON_FUNCTION: + return getDefaultPythonFunctionEvaluator(problem); + default: + return ""; + } + } + + /** + * Generate the default evaluator for a python problem + */ + public static String getDefaultPythonFunctionEvaluator(IProblem problem) { + return "def _eval(_input, _expected):\n" + + " _output=" + problem.getTestname() + "(*_input)\n" + + " _result=(_expected == _output) if (type(_output) != float and type(_expected) != float) else (math.fabs(_output-_expected) < 0.00001)\n" + + " return (_result, _output)\n"; + } +} From 507fa02b9a3faeee2184337ef850e67fbb663660 Mon Sep 17 00:00:00 2001 From: Andras Eisenberger Date: Sun, 19 Apr 2015 15:51:13 +0200 Subject: [PATCH 4/7] Add new ProblemData field to tests --- .../junit/org/cloudcoder/builder2/tests/res/compute_sum.json | 2 +- .../tests/junit/org/cloudcoder/builder2/tests/res/gravity.json | 2 +- .../tests/junit/org/cloudcoder/builder2/tests/res/skip3.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/compute_sum.json b/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/compute_sum.json index bfadc332..bdaad343 100644 --- a/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/compute_sum.json +++ b/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/compute_sum.json @@ -1 +1 @@ -{"problem_data":{"problem_type":1,"testname":"compute_sum","brief_description":"compute the sum of two numbers","description":"

Write a function called compute_sum<\/code> that\n takes two parameters and returns their sum.<\/p>\n

Example calls:<\/p>\n

    \n
  • compute_sum(2, 3)<\/code> => 5<\/code><\/li>\n
  • compute_sum(4, -11)<\/code> => -7<\/code><\/li>\n
  • compute_sum(42, 0)<\/code> => 42<\/code><\/li>\n<\/ul>","skeleton":"","schema_version":4,"author_name":"David Hovemeyer","author_email":"dhovemey@ycp.edu","author_website":"http:\/\/faculty.ycp.edu\/~dhovemey","timestamp_utc":1377615525510,"license":1,"parent_hash":"","external_library_url":"","external_library_md5":""},"test_case_data_list":[{"test_case_name":"twoPlusThree","input":"2, 3","output":"5","secret":false},{"test_case_name":"fourPlusMinusEleven","input":"4, -11","output":"-7","secret":false},{"test_case_name":"fortyTwoPlusZero","input":"42, 0","output":"42","secret":false}]} \ No newline at end of file +{"problem_data":{"problem_type":1,"testname":"compute_sum","brief_description":"compute the sum of two numbers","description":"

    Write a function called compute_sum<\/code> that\n takes two parameters and returns their sum.<\/p>\n

    Example calls:<\/p>\n

      \n
    • compute_sum(2, 3)<\/code> => 5<\/code><\/li>\n
    • compute_sum(4, -11)<\/code> => -7<\/code><\/li>\n
    • compute_sum(42, 0)<\/code> => 42<\/code><\/li>\n<\/ul>","skeleton":"","schema_version":4,"author_name":"David Hovemeyer","author_email":"dhovemey@ycp.edu","author_website":"http:\/\/faculty.ycp.edu\/~dhovemey","timestamp_utc":1377615525510,"license":1,"parent_hash":"","external_library_url":"","external_library_md5":"","evaluator":""},"test_case_data_list":[{"test_case_name":"twoPlusThree","input":"2, 3","output":"5","secret":false},{"test_case_name":"fourPlusMinusEleven","input":"4, -11","output":"-7","secret":false},{"test_case_name":"fortyTwoPlusZero","input":"42, 0","output":"42","secret":false}]} \ No newline at end of file diff --git a/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/gravity.json b/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/gravity.json index 19cced23..3adf1054 100644 --- a/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/gravity.json +++ b/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/gravity.json @@ -1 +1 @@ -{"problem_data":{"problem_type":1,"testname":"gravity","brief_description":"How far do objects travel when thrown off a building?","description":"

      \nConsider the problem of trying to determine how far an object\nfalls when thrown from the top of a very tall building. Because\nthe building is on Earth, accelaration due to gravity should be \npart of your calculations (i.e., assume 9.8 m\/s^2 as the G<\/em> \nconstant). To make the calculation reasonable, assume the \nbuilding is infinitely tall and located in a vacuum on Earth\n(i.e., ignore any forces due to friction, air-resistance, etc.).\n<\/p>\n\n

      \nReturn the number of meters the object has fallen after \ntime<\/code> seconds have elapsed when the object is \nthrown with given initial velocity.\n<\/p>\n","skeleton":"def gravity (time, initalVelocity):\n \"\"\"\n return float indicating number of meters an object has fallen \n after being thrown with an initial velocity (given in meters per\n second) and after falling for time seconds\n \"\"\"\n # TODO: complete code here\n ","schema_version":4,"author_name":"Kannan Raju","author_email":"kr88@duke.edu","author_website":"","timestamp_utc":1378456148639,"license":1,"parent_hash":"","external_library_url":"","external_library_md5":""},"test_case_data_list":[{"test_case_name":"t0","input":"0, 0","output":"0","secret":false},{"test_case_name":"t1","input":"1, 1","output":"5.9","secret":false},{"test_case_name":"t2","input":"1, 5","output":"9.9","secret":false},{"test_case_name":"t3","input":"3, 5","output":"59.1","secret":false},{"test_case_name":"t4","input":"3, 0","output":"44.1","secret":false},{"test_case_name":"t5","input":"3200, 0","output":"50176000.0","secret":false},{"test_case_name":"t6","input":"86400, 0","output":"36578304000.0","secret":false},{"test_case_name":"t7","input":"12.5, 20","output":"1015.625","secret":false},{"test_case_name":"t8","input":"12.5, 200","output":"3265.625","secret":false},{"test_case_name":"t9","input":"12.5, 2000","output":"25765.625","secret":false}]} \ No newline at end of file +{"problem_data":{"problem_type":1,"testname":"gravity","brief_description":"How far do objects travel when thrown off a building?","description":"

      \nConsider the problem of trying to determine how far an object\nfalls when thrown from the top of a very tall building. Because\nthe building is on Earth, accelaration due to gravity should be \npart of your calculations (i.e., assume 9.8 m\/s^2 as the G<\/em> \nconstant). To make the calculation reasonable, assume the \nbuilding is infinitely tall and located in a vacuum on Earth\n(i.e., ignore any forces due to friction, air-resistance, etc.).\n<\/p>\n\n

      \nReturn the number of meters the object has fallen after \ntime<\/code> seconds have elapsed when the object is \nthrown with given initial velocity.\n<\/p>\n","skeleton":"def gravity (time, initalVelocity):\n \"\"\"\n return float indicating number of meters an object has fallen \n after being thrown with an initial velocity (given in meters per\n second) and after falling for time seconds\n \"\"\"\n # TODO: complete code here\n ","schema_version":4,"author_name":"Kannan Raju","author_email":"kr88@duke.edu","author_website":"","timestamp_utc":1378456148639,"license":1,"parent_hash":"","external_library_url":"","external_library_md5":"","evaluator":""},"test_case_data_list":[{"test_case_name":"t0","input":"0, 0","output":"0","secret":false},{"test_case_name":"t1","input":"1, 1","output":"5.9","secret":false},{"test_case_name":"t2","input":"1, 5","output":"9.9","secret":false},{"test_case_name":"t3","input":"3, 5","output":"59.1","secret":false},{"test_case_name":"t4","input":"3, 0","output":"44.1","secret":false},{"test_case_name":"t5","input":"3200, 0","output":"50176000.0","secret":false},{"test_case_name":"t6","input":"86400, 0","output":"36578304000.0","secret":false},{"test_case_name":"t7","input":"12.5, 20","output":"1015.625","secret":false},{"test_case_name":"t8","input":"12.5, 200","output":"3265.625","secret":false},{"test_case_name":"t9","input":"12.5, 2000","output":"25765.625","secret":false}]} \ No newline at end of file diff --git a/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/skip3.json b/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/skip3.json index fb4b5652..7ff1df8e 100644 --- a/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/skip3.json +++ b/CloudCoderBuilder2/tests/junit/org/cloudcoder/builder2/tests/res/skip3.json @@ -1 +1 @@ -{"problem_data":{"problem_type":3,"testname":"skip 3","brief_description":"print integers in range, skipping by increments of 3","description":"

      \n The program will receive two integer input values,\n start<\/i> and end<\/i>. The\n output of the program should be a line of text\n with all of the integer values between\n start<\/i> and end<\/i>, inclusive, such that\n each successive integer is 3 greater than the previous\n integer.\n<\/p>\n

      \n For example, if the input is 1 10<\/b>, then the output\n should be\n<\/p>\n

      1 4 7 10<\/pre><\/blockquote>\n

      \n Another example: if the input is 19 35<\/b>, then the\n output should be\n<\/p>\n

      19 22 25 28 31 34<\/pre><\/blockquote>\n

      Hints:<\/p>\n

        \n
      • The program already declares variables for start<\/i>\n and end<\/i>, and uses scanf<\/code> to read\n their values<\/li>\n
      • Make sure to print a space after each integer<\/li>\n
      • Think about how to generate only every third integer;\n planning<\/i> the loop will be helpful<\/li>\n<\/ul>","skeleton":"#include \n\nint main(void) {\n \/\/ Declare variables for input (start and end)\n int start, end;\n \n \/\/ Read input values\n scanf(\"%i\", &start);\n scanf(\"%i\", &end);\n \n \/\/ Use a loop to print the output values\n \/\/ TODO\n \n return 0;\n}","schema_version":1,"author_name":"David Hovemeyer","author_email":"dhovemey@ycp.edu","author_website":"http:\/\/faculty.ycp.edu\/~dhovemey\/","timestamp_utc":1360782949618,"license":1,"parent_hash":"","external_library_url":"","external_library_md5":""},"test_case_data_list":[{"test_case_name":"OneToTenByThree","input":"1 10","output":"^(.*[^0-9])?1\\s+4\\s+7\\s+10([^0-9]*)?$","secret":false},{"test_case_name":"OneToTenPartialCredit","input":"1 10","output":"^(.*[^0-9])?1\\s+(2(\\s+3\\s+)?)?4\\s+(5(\\s+6\\s+)?)?7\\s+(8(\\s+9\\s+)?)?10([^0-9]*)?$","secret":false},{"test_case_name":"NineteenToThirtyFive","input":"19 35","output":"^(.*[^0-9])?19\\s+22\\s+25\\s+28\\s+31\\s+34([^0-9]*)?$","secret":false},{"test_case_name":"NineteenToThirtyFivePartialCredit","input":"19 35","output":"^(.*[^0-9])?19\\s+(20(\\s+21\\s+)?)?22\\s+(23(\\s+24\\s+)?)?25\\s+(26(\\s+27\\s+)?)?28\\s+(29(\\s+30\\s+)?)?31\\s+(32(\\s+33\\s+)?)?34(\\s+35)?([^0-9]*)?$","secret":false},{"test_case_name":"Just42","input":"42 42","output":"^(.*[^0-9])?42([^0-9]*)$","secret":false}]} \ No newline at end of file +{"problem_data":{"problem_type":3,"testname":"skip 3","brief_description":"print integers in range, skipping by increments of 3","description":"

        \n The program will receive two integer input values,\n start<\/i> and end<\/i>. The\n output of the program should be a line of text\n with all of the integer values between\n start<\/i> and end<\/i>, inclusive, such that\n each successive integer is 3 greater than the previous\n integer.\n<\/p>\n

        \n For example, if the input is 1 10<\/b>, then the output\n should be\n<\/p>\n

        1 4 7 10<\/pre><\/blockquote>\n

        \n Another example: if the input is 19 35<\/b>, then the\n output should be\n<\/p>\n

        19 22 25 28 31 34<\/pre><\/blockquote>\n

        Hints:<\/p>\n

          \n
        • The program already declares variables for start<\/i>\n and end<\/i>, and uses scanf<\/code> to read\n their values<\/li>\n
        • Make sure to print a space after each integer<\/li>\n
        • Think about how to generate only every third integer;\n planning<\/i> the loop will be helpful<\/li>\n<\/ul>","skeleton":"#include \n\nint main(void) {\n \/\/ Declare variables for input (start and end)\n int start, end;\n \n \/\/ Read input values\n scanf(\"%i\", &start);\n scanf(\"%i\", &end);\n \n \/\/ Use a loop to print the output values\n \/\/ TODO\n \n return 0;\n}","schema_version":1,"author_name":"David Hovemeyer","author_email":"dhovemey@ycp.edu","author_website":"http:\/\/faculty.ycp.edu\/~dhovemey\/","timestamp_utc":1360782949618,"license":1,"parent_hash":"","external_library_url":"","external_library_md5":"","evaluator":""},"test_case_data_list":[{"test_case_name":"OneToTenByThree","input":"1 10","output":"^(.*[^0-9])?1\\s+4\\s+7\\s+10([^0-9]*)?$","secret":false},{"test_case_name":"OneToTenPartialCredit","input":"1 10","output":"^(.*[^0-9])?1\\s+(2(\\s+3\\s+)?)?4\\s+(5(\\s+6\\s+)?)?7\\s+(8(\\s+9\\s+)?)?10([^0-9]*)?$","secret":false},{"test_case_name":"NineteenToThirtyFive","input":"19 35","output":"^(.*[^0-9])?19\\s+22\\s+25\\s+28\\s+31\\s+34([^0-9]*)?$","secret":false},{"test_case_name":"NineteenToThirtyFivePartialCredit","input":"19 35","output":"^(.*[^0-9])?19\\s+(20(\\s+21\\s+)?)?22\\s+(23(\\s+24\\s+)?)?25\\s+(26(\\s+27\\s+)?)?28\\s+(29(\\s+30\\s+)?)?31\\s+(32(\\s+33\\s+)?)?34(\\s+35)?([^0-9]*)?$","secret":false},{"test_case_name":"Just42","input":"42 42","output":"^(.*[^0-9])?42([^0-9]*)$","secret":false}]} \ No newline at end of file From 8b290b511e9b5a3011a172aa5f4f9753afde13ef Mon Sep 17 00:00:00 2001 From: Andras Eisenberger Date: Sun, 21 Jun 2015 02:11:42 +0200 Subject: [PATCH 5/7] Fix places where old version of courses and problems page was displayed --- CloudCoder/src/org/cloudcoder/app/client/CloudCoder.java | 5 ++--- CloudCoder/src/org/cloudcoder/app/client/page/LoginPage.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CloudCoder/src/org/cloudcoder/app/client/CloudCoder.java b/CloudCoder/src/org/cloudcoder/app/client/CloudCoder.java index d425921a..810e5078 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/CloudCoder.java +++ b/CloudCoder/src/org/cloudcoder/app/client/CloudCoder.java @@ -23,7 +23,6 @@ import org.cloudcoder.app.client.model.Session; import org.cloudcoder.app.client.model.StatusMessage; import org.cloudcoder.app.client.page.CloudCoderPage; -import org.cloudcoder.app.client.page.CoursesAndProblemsPage2; import org.cloudcoder.app.client.page.CoursesAndProblemsPage3; import org.cloudcoder.app.client.page.DevelopmentPage; import org.cloudcoder.app.client.page.EditProblemPage; @@ -174,7 +173,7 @@ public void onSuccess(User result) { changePage(page); } else { // Default behavior: navigate to the home page - changePage(new CoursesAndProblemsPage2()); + changePage(createPageForPageId(PageId.COURSES_AND_PROBLEMS, "")); } } } @@ -271,7 +270,7 @@ private CloudCoderPage createPageForPageId(PageId pageId) { // This shouldn't happen (can't find page for Activity), // but if it does, go to the courses and problems page. GWT.log("Don't know what kind of page to create for " + pageId); - page = new CoursesAndProblemsPage2(); + page = new CoursesAndProblemsPage3(); break; } return page; diff --git a/CloudCoder/src/org/cloudcoder/app/client/page/LoginPage.java b/CloudCoder/src/org/cloudcoder/app/client/page/LoginPage.java index 26c9fb1e..4573036f 100644 --- a/CloudCoder/src/org/cloudcoder/app/client/page/LoginPage.java +++ b/CloudCoder/src/org/cloudcoder/app/client/page/LoginPage.java @@ -176,7 +176,7 @@ public void setPubTextInstitution(String result) { /** * Default constructor. - * Will take the user to the {@link CoursesAndProblemsPage2} + * Will take the user to the {@link CoursesAndProblemsPage3} * upon a successful login. */ public LoginPage() { From 36bfea55f1395ca6ded0428ff7571cac82710739 Mon Sep 17 00:00:00 2001 From: Andras Eisenberger Date: Sun, 21 Jun 2015 02:36:05 +0200 Subject: [PATCH 6/7] Fix creating a new problem, didn't work since evaluator added --- .../cloudcoder/builderwebservice/servlets/ProblemBuilder.java | 1 + .../src/org/cloudcoder/app/shared/model/ProblemData.java | 1 + .../src/org/cloudcoder/app/server/persist/CreateSampleData.java | 1 + 3 files changed, 3 insertions(+) diff --git a/CloudCoderBuilderWebService/src/org/cloudcoder/builderwebservice/servlets/ProblemBuilder.java b/CloudCoderBuilderWebService/src/org/cloudcoder/builderwebservice/servlets/ProblemBuilder.java index 77a93bea..84d4c8be 100644 --- a/CloudCoderBuilderWebService/src/org/cloudcoder/builderwebservice/servlets/ProblemBuilder.java +++ b/CloudCoderBuilderWebService/src/org/cloudcoder/builderwebservice/servlets/ProblemBuilder.java @@ -62,6 +62,7 @@ public Problem build() throws BadRequestException { problem.setTimestampUtc(0L); problem.setLicense(ProblemLicense.NOT_REDISTRIBUTABLE); problem.setParentHash(""); + problem.setEvaluator(""); // None of the Problem-specific fields are significant // Important: the builder must ignore the problem id. diff --git a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/ProblemData.java b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/ProblemData.java index d384f82a..b0c0a3b0 100644 --- a/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/ProblemData.java +++ b/CloudCoderModelClasses/src/org/cloudcoder/app/shared/model/ProblemData.java @@ -561,5 +561,6 @@ public static void initEmpty(ProblemData empty) { empty.setTimestampUtc(System.currentTimeMillis()); empty.setLicense(ProblemLicense.NOT_REDISTRIBUTABLE); empty.setParentHash(""); + empty.setEvaluator(""); } } \ No newline at end of file diff --git a/CloudCoderModelClassesPersistence/src/org/cloudcoder/app/server/persist/CreateSampleData.java b/CloudCoderModelClassesPersistence/src/org/cloudcoder/app/server/persist/CreateSampleData.java index 3227e2af..014a711c 100644 --- a/CloudCoderModelClassesPersistence/src/org/cloudcoder/app/server/persist/CreateSampleData.java +++ b/CloudCoderModelClassesPersistence/src/org/cloudcoder/app/server/persist/CreateSampleData.java @@ -141,6 +141,7 @@ private static void populateSampleCFunctionProblemData(IProblemData problemData) problemData.setParentHash(""); problemData.setExternalLibraryUrl(""); problemData.setExternalLibraryMD5(""); + problemData.setEvaluator(""); } public static void populateSampleCFunctionTestCases(ITestCase[] testCases, int problemId) { From 5372e89775e3a5b1750c5ca3d33e0b60d7159234 Mon Sep 17 00:00:00 2001 From: Andras Eisenberger Date: Thu, 9 Jul 2015 18:04:47 +0200 Subject: [PATCH 7/7] Upgrade Jython to version 2.7 --- CloudCoderBuilder2/.classpath | 2 +- default.deps | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CloudCoderBuilder2/.classpath b/CloudCoderBuilder2/.classpath index 59a773ea..29fc9517 100644 --- a/CloudCoderBuilder2/.classpath +++ b/CloudCoderBuilder2/.classpath @@ -17,7 +17,7 @@ - + diff --git a/default.deps b/default.deps index 3b779d6f..0a82d47e 100644 --- a/default.deps +++ b/default.deps @@ -21,8 +21,8 @@ http://repo1.maven.org/maven2/commons-io/commons-io/2.1/commons-io-2.1.jar CloudCoderLoadTester/lib/commons-io-2.1.jar CloudCoderLogging/lib/commons-io-2.1.jar CloudCoderAnalysis/lib/commons-io-2.1.jar -http://repo1.maven.org/maven2/org/python/jython-standalone/2.5.3/jython-standalone-2.5.3.jar - CloudCoderBuilder2/lib/jython-standalone-2.5.3.jar +http://repo1.maven.org/maven2/org/python/jython-standalone/2.7.0/jython-standalone-2.7.0.jar + CloudCoderBuilder2/lib/jython-standalone-2.7.0.jar https://s3.amazonaws.com/org.cloudcoder.daemon.binaries/daemon-${daemon_version}.jar CloudCoderJetty/lib/daemon/daemon-${daemon_version}.jar CloudCoderBuilder2/lib/daemon-${daemon_version}.jar