diff --git a/python/educational-core/student/resources/META-INF/plugin.xml b/python/educational-core/student/resources/META-INF/plugin.xml
index 2d5b272bd2bb..5652e347f6d6 100644
--- a/python/educational-core/student/resources/META-INF/plugin.xml
+++ b/python/educational-core/student/resources/META-INF/plugin.xml
@@ -85,6 +85,7 @@
+
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java
index 74410d74ce84..07a3b31e8514 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java
@@ -69,6 +69,7 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur
Task task = getTask(file);
setTaskText(task, file.getParent());
}
+ toolWindow.setBottomComponent(null);
}
@Nullable
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java
index 29a4011c9306..61b7f3718596 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java
@@ -25,6 +25,7 @@ import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable {
@@ -75,21 +76,36 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
public void run(@NotNull ProgressIndicator indicator) {
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
if (course != null && course.isAdaptive()) {
- checkAdaptiveCourse(indicator);
+ checkForAdaptiveCourse(indicator);
}
else {
- if (checkCourse(indicator)) return;
+ checkForEduCourse(indicator);
}
- runAfterTaskSolvedActions();
}
- private boolean checkCourse(@NotNull ProgressIndicator indicator) {
+ private void checkForEduCourse(@NotNull ProgressIndicator indicator) {
+ final StudyTestsOutputParser.TestsOutput testsOutput = getTestOutput(indicator);
+
+ postAttemptToStepic(testsOutput);
+
+ if (testsOutput != null) {
+ if (testsOutput.isSuccess()) {
+ onTaskSolved(testsOutput.getMessage());
+ }
+ else {
+ onTaskFailed(testsOutput.getMessage());
+ }
+ runAfterTaskCheckedActions();
+ }
+ }
+
+ @Nullable
+ private StudyTestsOutputParser.TestsOutput getTestOutput(@NotNull ProgressIndicator indicator) {
final CapturingProcessHandler handler = new CapturingProcessHandler(myTestProcess, null, myCommandLine);
final ProcessOutput output = handler.runProcessWithProgressIndicator(indicator);
if (indicator.isCanceled()) {
ApplicationManager.getApplication().invokeLater(
() -> StudyCheckUtils.showTestResultPopUp("Check cancelled", MessageType.WARNING.getPopupBackground(), myProject));
- return true;
}
@@ -102,64 +118,64 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
myProject));
//log error output of tests
LOG.info("#educational " + stderr);
- return true;
+ return null;
}
-
- postAttemptToStepic(testsOutput);
-
-
- if (testsOutput.isSuccess()) {
- onTaskSolved(testsOutput.getMessage());
- }
- else {
- onTaskFailed(testsOutput.getMessage());
- }
- return false;
+ return testsOutput;
}
- private void checkAdaptiveCourse(ProgressIndicator indicator) {
+ private void checkForAdaptiveCourse(ProgressIndicator indicator) {
ProgressManager.getInstance().runProcessWithProgressAsynchronously(new Backgroundable(myProject, "Checking Task") {
@Override
public void run(@NotNull ProgressIndicator indicator) {
- final Pair pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
- if (pair != null) {
- final String checkMessage = pair.getSecond();
- if (pair.getFirst()) {
- onTaskSolved(checkMessage);
+ final StudyTestsOutputParser.TestsOutput testOutput = getTestOutput(indicator);
+ if (testOutput != null) {
+ if (testOutput.isSuccess()) {
+ final Pair pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
+ if (pair != null && !pair.getSecond().isEmpty()) {
+ final String checkMessage = pair.getSecond();
+ if (pair.getFirst()) {
+ onTaskSolved(checkMessage);
+ }
+ else {
+ onTaskFailed(checkMessage);
+ }
+ runAfterTaskCheckedActions();
+ }
+ else {
+ ApplicationManager.getApplication().invokeLater(() ->
+ StudyCheckUtils.showTestResultPopUp("Failed to launch checking",
+ MessageType.WARNING
+ .getPopupBackground(),
+ myProject));
+ }
}
else {
- onTaskFailed(checkMessage);
+ onTaskFailed(testOutput.getMessage());
}
}
- else {
- ApplicationManager.getApplication().invokeLater(() ->
- StudyCheckUtils.showTestResultPopUp("Failed to launch checking",
- MessageType.WARNING.getPopupBackground(),
- myProject));
- }
}
}, indicator);
-
}
- protected void onTaskFailed( String message) {
+ protected void onTaskFailed(String message) {
myTaskManger.setStatus(myTask, StudyStatus.Failed);
ApplicationManager.getApplication().invokeLater(
- () -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
+ () -> StudyCheckUtils.showTestResults(myProject, message));
}
protected void onTaskSolved(String message) {
myTaskManger.setStatus(myTask, StudyStatus.Solved);
+
ApplicationManager.getApplication().invokeLater(
- () -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject));
+ () -> StudyCheckUtils.showTestResults(myProject, message));
}
- private void runAfterTaskSolvedActions() {
+ private void runAfterTaskCheckedActions() {
StudyPluginConfigurator configurator = StudyUtils.getConfigurator(myProject);
if (configurator != null) {
StudyAfterCheckAction[] checkActions = configurator.getAfterCheckActions();
if (checkActions != null) {
- for (StudyAfterCheckAction action: checkActions) {
+ for (StudyAfterCheckAction action : checkActions) {
action.run(myProject, myTask, myStatusBeforeCheck);
}
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java
index 9fe0ae84ae6b..c719388b61ac 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java
@@ -15,23 +15,25 @@ import com.intellij.openapi.ui.popup.BalloonBuilder;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.wm.IdeFocusManager;
-import com.intellij.openapi.wm.IdeFrame;
-import com.intellij.openapi.wm.WindowManager;
+import com.intellij.openapi.wm.*;
import com.intellij.openapi.wm.ex.StatusBarEx;
import com.intellij.openapi.wm.ex.WindowManagerEx;
+import com.intellij.ui.content.Content;
+import com.jetbrains.edu.learning.StudyState;
+import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduDocumentListener;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import com.jetbrains.edu.learning.StudyState;
-import com.jetbrains.edu.learning.StudyTaskManager;
-import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.editor.StudyEditor;
import com.jetbrains.edu.learning.navigation.StudyNavigator;
+import com.jetbrains.edu.learning.ui.StudyTestResultsToolWindow;
+import com.jetbrains.edu.learning.ui.StudyTestResultsToolWindowFactory;
import org.jetbrains.annotations.NotNull;
+import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.List;
@@ -61,9 +63,9 @@ public class StudyCheckUtils {
}
public static void navigateToFailedPlaceholder(@NotNull final StudyState studyState,
- @NotNull final Task task,
- @NotNull final VirtualFile taskDir,
- @NotNull final Project project) {
+ @NotNull final Task task,
+ @NotNull final VirtualFile taskDir,
+ @NotNull final Project project) {
TaskFile selectedTaskFile = studyState.getTaskFile();
Editor editor = studyState.getEditor();
TaskFile taskFileToNavigate = selectedTaskFile;
@@ -109,10 +111,10 @@ public class StudyCheckUtils {
public static void runSmartTestProcess(@NotNull final VirtualFile taskDir,
- @NotNull final StudyTestRunner testRunner,
- final String taskFileName,
- @NotNull final TaskFile taskFile,
- @NotNull final Project project) {
+ @NotNull final StudyTestRunner testRunner,
+ final String taskFileName,
+ @NotNull final TaskFile taskFile,
+ @NotNull final Project project) {
final TaskFile answerTaskFile = new TaskFile();
answerTaskFile.name = taskFileName;
final VirtualFile virtualFile = taskDir.findChild(taskFileName);
@@ -135,11 +137,10 @@ public class StudyCheckUtils {
}
-
private static VirtualFile getCopyWithAnswers(@NotNull final VirtualFile taskDir,
- @NotNull final VirtualFile file,
- @NotNull final TaskFile source,
- @NotNull final TaskFile target) {
+ @NotNull final VirtualFile file,
+ @NotNull final TaskFile source,
+ @NotNull final TaskFile target) {
VirtualFile copy = null;
try {
@@ -193,4 +194,22 @@ public class StudyCheckUtils {
EduUtils.flushWindows(taskFile, virtualFile, true);
}
}
+
+ public static void showTestResults(@NotNull final Project project, @NotNull final String message) {
+ final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
+ ToolWindow window = toolWindowManager.getToolWindow("Test Results");
+ if (window == null) {
+ toolWindowManager.registerToolWindow("Test Results", true, ToolWindowAnchor.BOTTOM);
+ window = toolWindowManager.getToolWindow("Test Results");
+ new StudyTestResultsToolWindowFactory().createToolWindowContent(project, window);
+ }
+ final Content[] contents = window.getContentManager().getContents();
+ for (Content content : contents) {
+ final JComponent component = content.getComponent();
+ if (component instanceof StudyTestResultsToolWindow) {
+ ((StudyTestResultsToolWindow)component).setText(message);
+ component.setVisible(true);
+ }
+ }
+ }
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyTestsOutputParser.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyTestsOutputParser.java
index 55bfe7273bac..921ee4cba82c 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyTestsOutputParser.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyTestsOutputParser.java
@@ -4,7 +4,7 @@ import com.intellij.execution.process.ProcessOutput;
import org.jetbrains.annotations.NotNull;
public class StudyTestsOutputParser {
- private static final String ourStudyPrefix = "#educational_plugin";
+ private static final String STUDY_PREFIX = "#educational_plugin";
public static final String TEST_OK = "test OK";
private static final String TEST_FAILED = "FAILED + ";
private static final String CONGRATS_MESSAGE = "CONGRATS_MESSAGE ";
@@ -32,7 +32,7 @@ public class StudyTestsOutputParser {
public static TestsOutput getTestsOutput(@NotNull final ProcessOutput processOutput) {
String congratulations = CONGRATULATIONS;
for (String line : processOutput.getStdoutLines()) {
- if (line.startsWith(ourStudyPrefix)) {
+ if (line.startsWith(STUDY_PREFIX)) {
if (line.contains(TEST_OK)) {
continue;
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
index 9af4dc7af1a5..fe5cd4e65c40 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
@@ -4,13 +4,13 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import com.jetbrains.edu.learning.StudyTaskManager;
-import com.jetbrains.edu.learning.StudyUtils;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -23,6 +23,7 @@ public class StudyGenerator {
private StudyGenerator() {
}
+
private static final Logger LOG = Logger.getInstance(StudyGenerator.class.getName());
/**
@@ -107,34 +108,32 @@ public class StudyGenerator {
public static void createCourse(@NotNull final Course course, @NotNull final VirtualFile baseDir, @NotNull final File resourceRoot,
@NotNull final Project project) {
- try {
- final List lessons = course.getLessons();
- for (int i = 1; i <= lessons.size(); i++) {
- Lesson lesson = lessons.get(i - 1);
- lesson.setIndex(i);
- createLesson(lesson, baseDir, resourceRoot, project);
- }
- baseDir.createChildDirectory(project, EduNames.SANDBOX_DIR);
- File[] files = resourceRoot.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS);
- }
- });
- for (File file : files) {
- File dir = new File(baseDir.getPath(), file.getName());
- if (file.isDirectory()) {
- FileUtil.copyDir(file, dir);
- continue;
- }
+ try {
+ final List lessons = course.getLessons();
+ for (int i = 1; i <= lessons.size(); i++) {
+ Lesson lesson = lessons.get(i - 1);
+ lesson.setIndex(i);
+ createLesson(lesson, baseDir, resourceRoot, project);
+ }
+ baseDir.createChildDirectory(project, EduNames.SANDBOX_DIR);
+ File[] files = resourceRoot.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS);
+ }
+ });
+ for (File file : files) {
+ File dir = new File(baseDir.getPath(), file.getName());
+ if (file.isDirectory()) {
+ FileUtil.copyDir(file, dir);
+ continue;
+ }
- FileUtil.copy(file, dir);
-
- }
- }
- catch (IOException e) {
- LOG.error(e);
- }
+ FileUtil.copy(file, dir);
+ }
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
}
-
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
index ee80ac733d98..cad89ec26265 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
@@ -9,6 +9,7 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.jetbrains.edu.learning.StudyTaskManager;
@@ -42,6 +43,7 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getHttpClient;
@@ -57,6 +59,8 @@ public class EduAdaptiveStepicConnector {
private static final String RECOMMENDATION_REACTIONS_URL = "recommendation-reactions";
private static final String ATTEMPTS_URL = "attempts";
private static final String SUBMISSION_URL = "submissions";
+ private static final String PYTHON2 = "python2";
+ private static final String PYTHON3 = "python3";
@Nullable
public static Task getNextRecommendation(@NotNull final Project project, @NotNull Course course) {
@@ -88,9 +92,11 @@ public class EduAdaptiveStepicConnector {
for (int stepId : realLesson.steps) {
final StepicWrappers.Step step = EduStepicConnector.getStep(stepId);
if (step.name.equals("code")) {
- return getTaskFromStep(realLesson.getName(), stepId, step);
+ return getTaskFromStep(project, stepId, step, realLesson.getName());
}
}
+
+ LOG.warn("Got a lesson without code part as a recommendation");
}
}
else {
@@ -106,8 +112,8 @@ public class EduAdaptiveStepicConnector {
return null;
}
- public static boolean postRecommendationReaction(@NotNull final Project project, @NotNull final String lessonId,
- @NotNull final String user,int reaction) {
+ public static boolean postRecommendationReaction(@NotNull final Project project, @NotNull final String lessonId,
+ @NotNull final String user, int reaction) {
final HttpPost post = new HttpPost(STEPIC_API_URL + RECOMMENDATION_REACTIONS_URL);
final String json = new Gson()
@@ -132,7 +138,7 @@ public class EduAdaptiveStepicConnector {
// TODO: get user from settings
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
if (user != null &&
- postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().id),
+ postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().id),
String.valueOf(user.id), reaction)) {
final Task task = getNextRecommendation(project, course);
@@ -188,16 +194,37 @@ public class EduAdaptiveStepicConnector {
}
@NotNull
- private static Task getTaskFromStep(@NotNull String name, int lessonID, @NotNull final StepicWrappers.Step step) {
+ private static Task getTaskFromStep(Project project,
+ int lessonID,
+ @NotNull final StepicWrappers.Step step, @NotNull String name) {
final Task task = new Task();
task.setName(name);
task.setStepicId(lessonID);
task.setText(step.text);
+ if (step.options.samples != null) {
+ final StringBuilder builder = new StringBuilder();
+ for (List sample : step.options.samples) {
+ if (sample.size() == 2) {
+ builder.append("Sample Input:
");
+ builder.append(sample.get(0));
+ builder.append("
");
+ builder.append("Sample Output:
");
+ builder.append(sample.get(1));
+ builder.append("
");
+ }
+ }
+ task.setText(task.getText() + "
" + builder.toString());
+ }
if (step.options.test != null) {
for (StepicWrappers.TestFileWrapper wrapper : step.options.test) {
task.addTestsTexts(wrapper.name, wrapper.text);
}
}
+ else {
+ if (step.options.samples != null) {
+ createTestFileFromSamples(project, task, step.options.samples);
+ }
+ }
task.taskFiles = new HashMap(); // TODO: it looks like we don't need taskFiles as map anymore
if (step.options.files != null) {
@@ -218,37 +245,39 @@ public class EduAdaptiveStepicConnector {
public static Pair checkTask(@NotNull final Project project, @NotNull final Task task) {
try {
final int attemptId = getAttemptId(project, task, ATTEMPTS_URL);
- final Editor editor = StudyUtils.getSelectedEditor(project);
- String language = getLanguageString(task, project);
- if (editor != null && language != null) {
- final StepicWrappers.SubmissionToPostWrapper submissionToPostWrapper =
- new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), "python3", editor.getDocument().getText());
- final HttpPost httpPost = new HttpPost(STEPIC_API_URL + SUBMISSION_URL);
- httpPost.setEntity(new StringEntity(new Gson().toJson(submissionToPostWrapper)));
- final CloseableHttpClient client = getHttpClient(project);
- setHeaders(httpPost, CONTENT_TYPE_APPL_JSON);
- final CloseableHttpResponse execute = client.execute(httpPost);
- StepicWrappers.ResultSubmissionWrapper wrapper =
- new Gson().fromJson(EntityUtils.toString(execute.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
+ if (attemptId != -1) {
+ final Editor editor = StudyUtils.getSelectedEditor(project);
+ String language = getLanguageString(task, project);
+ if (editor != null && language != null) {
+ final StepicWrappers.SubmissionToPostWrapper submissionToPostWrapper =
+ new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), language, editor.getDocument().getText());
+ final HttpPost httpPost = new HttpPost(STEPIC_API_URL + SUBMISSION_URL);
+ httpPost.setEntity(new StringEntity(new Gson().toJson(submissionToPostWrapper)));
+ final CloseableHttpClient client = getHttpClient(project);
+ setHeaders(httpPost, CONTENT_TYPE_APPL_JSON);
+ final CloseableHttpResponse execute = client.execute(httpPost);
+ StepicWrappers.ResultSubmissionWrapper wrapper =
+ new Gson().fromJson(EntityUtils.toString(execute.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
- final StepicUser user = StudyTaskManager.getInstance(project).getUser();
- if (user != null) {
- final int id = user.getId();
- while (wrapper.submissions.length == 1 && wrapper.submissions[0].status.equals("evaluation")) {
- TimeUnit.MILLISECONDS.sleep(500);
- final URI submissionURI = new URIBuilder(STEPIC_API_URL + SUBMISSION_URL)
- .addParameter("attempt", String.valueOf(attemptId))
- .addParameter("order", "desc")
- .addParameter("user", String.valueOf(id))
- .build();
- final HttpGet httpGet = new HttpGet(submissionURI);
- setHeaders(httpGet, CONTENT_TYPE_APPL_JSON);
- final CloseableHttpResponse httpResponse = client.execute(httpGet);
- wrapper = new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
- }
- if (wrapper.submissions.length == 1) {
- final boolean isSolved = !wrapper.submissions[0].status.equals("wrong");
- return Pair.create(isSolved, wrapper.submissions[0].hint);
+ final StepicUser user = StudyTaskManager.getInstance(project).getUser();
+ if (user != null) {
+ final int id = user.getId();
+ while (wrapper.submissions.length == 1 && wrapper.submissions[0].status.equals("evaluation")) {
+ TimeUnit.MILLISECONDS.sleep(500);
+ final URI submissionURI = new URIBuilder(STEPIC_API_URL + SUBMISSION_URL)
+ .addParameter("attempt", String.valueOf(attemptId))
+ .addParameter("order", "desc")
+ .addParameter("user", String.valueOf(id))
+ .build();
+ final HttpGet httpGet = new HttpGet(submissionURI);
+ setHeaders(httpGet, CONTENT_TYPE_APPL_JSON);
+ final CloseableHttpResponse httpResponse = client.execute(httpGet);
+ wrapper = new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
+ }
+ if (wrapper.submissions.length == 1) {
+ final boolean isSolved = !wrapper.submissions[0].status.equals("wrong");
+ return Pair.create(isSolved, wrapper.submissions[0].hint);
+ }
}
}
}
@@ -272,11 +301,12 @@ public class EduAdaptiveStepicConnector {
private static String getLanguageString(@NotNull Task task, @NotNull Project project) {
final Sdk sdk = StudyUtils.findSdk(task, project);
if (sdk != null) {
- if (sdk.getVersionString() != null && sdk.getVersionString().startsWith("3")) {
- return "python3";
- }
- else {
- return "python2";
+ final String versionString = sdk.getVersionString();
+ if (versionString != null ) {
+ final List versionStringParts = StringUtil.split(versionString, " ");
+ if (versionStringParts.size() == 2) {
+ return versionStringParts.get(1).startsWith("2") ? PYTHON2 : PYTHON3;
+ }
}
}
else {
@@ -296,6 +326,15 @@ public class EduAdaptiveStepicConnector {
final CloseableHttpResponse httpResponse = client.execute(post);
final StepicWrappers.AttemptContainer container =
new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.AttemptContainer.class);
- return container.attempts.get(0).id;
+ return (container.attempts != null && !container.attempts.isEmpty()) ? container.attempts.get(0).id : -1;
+ }
+
+ private static void createTestFileFromSamples(@NotNull final Project project,
+ @NotNull final Task task,
+ @NotNull final List> samples) {
+ String testText = "from test_helper import check_samples\n\n" +
+ "if __name__ == '__main__':\n" +
+ " check_samples(samples=" + new GsonBuilder().create().toJson(samples) + ")";
+ task.addTestsTexts("tests.py", testText);
}
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java
index 8a02678c182c..69f1ea7d514a 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java
@@ -47,6 +47,8 @@ public class StepicWrappers {
@Expose String title;
@Expose List files;
@Expose String text;
+ @Expose List> samples;
+
public static StepOptions fromTask(final Project project, @NotNull final Task task) {
final StepOptions source = new StepOptions();
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java
index fd7a12a5e01e..75d6146c48c9 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java
@@ -35,7 +35,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
-class StudyBrowserWindow extends JFrame {
+public class StudyBrowserWindow extends JFrame {
private static final Logger LOG = Logger.getInstance(StudyToolWindow.class);
private static final String EVENT_TYPE_CLICK = "click";
private JFXPanel myPanel;
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyTestResultsToolWindow.kt b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyTestResultsToolWindow.kt
new file mode 100644
index 000000000000..17ec0d915507
--- /dev/null
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyTestResultsToolWindow.kt
@@ -0,0 +1,20 @@
+package com.jetbrains.edu.learning.ui
+
+import com.intellij.openapi.ui.SimpleToolWindowPanel
+import java.awt.BorderLayout
+import javax.swing.JPanel
+
+
+class StudyTestResultsToolWindow: SimpleToolWindowPanel(false) {
+ private val studyBrowserWindow = StudyBrowserWindow(false, false)
+ fun init() {
+ val panel = JPanel(BorderLayout())
+ studyBrowserWindow.loadContent("", null)
+ panel.add(studyBrowserWindow.panel, BorderLayout.CENTER)
+ setContent(panel)
+ }
+
+ fun setText(text: String) {
+ studyBrowserWindow.loadContent(text, null)
+ }
+}
\ No newline at end of file
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyTestResultsToolWindowFactory.kt b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyTestResultsToolWindowFactory.kt
new file mode 100644
index 000000000000..53d073ae4bbc
--- /dev/null
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyTestResultsToolWindowFactory.kt
@@ -0,0 +1,18 @@
+package com.jetbrains.edu.learning.ui
+
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.wm.ToolWindow
+
+
+class StudyTestResultsToolWindowFactory: StudyToolWindowFactory() {
+ override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
+ val testResultsToolWindow = StudyTestResultsToolWindow()
+ testResultsToolWindow.init()
+
+ val contentManager = toolWindow.contentManager
+ val content = contentManager.factory.createContent(testResultsToolWindow, null, false)
+ contentManager.addContent(content)
+ }
+}
+
+
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java
index 054b2d0a35ab..d8261baece92 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java
@@ -219,5 +219,5 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D
}
}
- public abstract void setTaskText(String text) ;
+ public abstract void setTaskText(String text);
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java
index db026cea6bad..23385fd077ce 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java
@@ -7,9 +7,9 @@ import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;
-import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.StudyProjectComponent;
import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.courseFormat.Course;
import icons.InteractiveLearningIcons;
import org.jetbrains.annotations.NotNull;
@@ -23,7 +23,6 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
final Course course = taskManager.getCourse();
if (course != null) {
-
final StudyToolWindow studyToolWindow;
if (StudyProjectComponent.getInstance(project).useJavaFx()) {
studyToolWindow = new StudyJavaFxToolWindow();
diff --git a/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft b/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft
index d27866c23f01..a0ee26851527 100644
--- a/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft
+++ b/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft
@@ -9,7 +9,7 @@ def get_file_text(path):
return text
-def get_file_output(encoding="utf-8", path=sys.argv[-1]):
+def get_file_output(encoding="utf-8", path=sys.argv[-1], arg_string=""):
"""
Returns answer file output
:param encoding: to decode output in python3
@@ -18,7 +18,13 @@ def get_file_output(encoding="utf-8", path=sys.argv[-1]):
"""
import subprocess
- proc = subprocess.Popen([sys.executable, path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ proc = subprocess.Popen([sys.executable, path], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ if arg_string:
+ for arg in arg_string.split("\n"):
+ proc.stdin.write(bytearray(str(arg) + "\n", encoding))
+ proc.stdin.flush()
+
return list(map(lambda x: str(x.decode(encoding)), proc.communicate()[0].splitlines()))
@@ -31,7 +37,8 @@ def test_file_importable():
parent = os.path.abspath(os.path.join(path, os.pardir))
python_files = [f for f in os.listdir(parent) if os.path.isfile(os.path.join(parent, f)) and f.endswith(".py")]
for python_file in python_files:
- if python_file == "tests.py": continue
+ if python_file == "tests.py":
+ continue
check_importable_path(os.path.join(parent, python_file))
return
check_importable_path(path)
@@ -187,7 +194,25 @@ def get_answer_placeholders():
return windows
-def run_common_tests(error_text="Please, reload file and try again"):
+def check_samples(samples=()):
+ """
+ Check script output for all samples. Sample is a two element list, where the first is input and
+ the second is output.
+ """
+ for sample in samples:
+ if len(sample) == 2:
+ output = get_file_output(arg_string=str(sample[0]))
+ if "\n".join(output) != sample[1]:
+ failed(
+ "Input:
{}
Expected:
{}
Your result:
{}".format(
+ sample[0].replace("\n", "
"), sample[1].replace("\n", "
"), "\n".join(output)))
+ return
+ set_congratulation_message("All test from samples passed. Now we are checking your solution on Stepic server.")
+
+ passed()
+
+
+def run_common_tests():
test_is_initial_text()
test_is_not_empty()
test_answer_placeholders_text_deleted()
diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java
index 1b238004fd57..34e59c4027b1 100644
--- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java
+++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java
@@ -7,7 +7,6 @@ import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.jetbrains.edu.learning.actions.StudyCheckAction;
@@ -15,10 +14,12 @@ import com.jetbrains.edu.learning.actions.StudyRunAction;
import com.jetbrains.edu.learning.checker.StudyCheckTask;
import com.jetbrains.edu.learning.checker.StudyCheckUtils;
import com.jetbrains.edu.learning.checker.StudyTestRunner;
+import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.editor.StudyEditor;
+import com.jetbrains.edu.learning.ui.StudyToolWindow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -40,8 +41,8 @@ public class PyStudyCheckAction extends StudyCheckAction {
}
if (StudyCheckUtils.hasBackgroundProcesses(project)) return;
-
- if (!runTask(project)) return;
+ final Course course = StudyTaskManager.getInstance(project).getCourse();
+ if (course != null && !course.isAdaptive() && !runTask(project)) return;
final Task task = studyState.getTask();
final VirtualFile taskDir = studyState.getTaskDir();
@@ -90,34 +91,37 @@ public class PyStudyCheckAction extends StudyCheckAction {
final Process testProcess,
final String commandLine) {
return new StudyCheckTask(project, studyState, myCheckInProgress, testProcess, commandLine) {
- @Override
- protected void onTaskFailed(String message) {
- ApplicationManager.getApplication().invokeLater(() -> {
- if (myTaskDir == null) return;
- myTaskManger.setStatus(myTask, StudyStatus.Failed);
- for (Map.Entry entry : myTask.getTaskFiles().entrySet()) {
- final String name = entry.getKey();
- final TaskFile taskFile = entry.getValue();
- if (taskFile.getAnswerPlaceholders().size() < 2) {
- myTaskManger.setStatus(taskFile, StudyStatus.Failed);
- continue;
- }
- CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
- StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
- }));
- }
- StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), project);
- StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
- });
+ @Override
+ protected void onTaskFailed(String message) {
+ ApplicationManager.getApplication().invokeLater(() -> {
+ if (myTaskDir == null) return;
+ myTaskManger.setStatus(myTask, StudyStatus.Failed);
+ for (Map.Entry entry : myTask.getTaskFiles().entrySet()) {
+ final String name = entry.getKey();
+ final TaskFile taskFile = entry.getValue();
+ if (taskFile.getAnswerPlaceholders().size() < 2) {
+ myTaskManger.setStatus(taskFile, StudyStatus.Failed);
+ continue;
}
- };
+ CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
+ StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
+ }));
+ }
+ final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
+ if (toolWindow != null) {
+ StudyCheckUtils.showTestResults(project, message);
+ StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
+ }
+ });
+ }
+ };
}
@Nullable
private static VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState,
- @NotNull final Task task,
- @NotNull final VirtualFile taskDir) {
+ @NotNull final Task task,
+ @NotNull final VirtualFile taskDir) {
VirtualFile taskVirtualFile = studyState.getVirtualFile();
for (Map.Entry entry : task.getTaskFiles().entrySet()) {
String name = entry.getKey();
@@ -131,7 +135,7 @@ public class PyStudyCheckAction extends StudyCheckAction {
}
return taskVirtualFile;
}
-
+
@NotNull
@Override
public String getActionId() {