diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml
index 7745070d43b6..370643525c1d 100644
--- a/python/educational-core/resources/META-INF/plugin.xml
+++ b/python/educational-core/resources/META-INF/plugin.xml
@@ -55,6 +55,7 @@
+
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java
index abf32c6c97e5..f8d160ee8891 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java
@@ -1,5 +1,6 @@
package com.jetbrains.edu.learning.actions;
+import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.actionSystem.Presentation;
@@ -8,25 +9,37 @@ import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.keymap.KeymapUtil;
+import com.intellij.openapi.progress.PerformInBackgroundOption;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.edu.learning.checker.StudyCheckListener;
+import com.jetbrains.edu.learning.StudySettings;
import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.checker.StudyCheckListener;
+import com.jetbrains.edu.learning.checker.StudyCheckResult;
import com.jetbrains.edu.learning.checker.StudyCheckUtils;
+import com.jetbrains.edu.learning.checker.StudyTaskChecker;
+import com.jetbrains.edu.learning.courseFormat.RemoteCourse;
+import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask;
import com.jetbrains.edu.learning.editor.StudyEditor;
+import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
+import com.jetbrains.edu.learning.stepic.StepicUser;
import icons.EducationalCoreIcons;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
-public abstract class StudyCheckAction extends StudyActionWithShortcut {
+public class StudyCheckAction extends StudyActionWithShortcut {
public static final String SHORTCUT = "ctrl alt pressed ENTER";
+ private static final String ACTION_ID = "Edu.Check";
private static final String TEXT = "Check Task";
+ public static final String FAILED_CHECK_LAUNCH = "Failed to launch checking";
protected final Ref myCheckInProgress = new Ref<>(false);
@@ -40,10 +53,9 @@ public abstract class StudyCheckAction extends StudyActionWithShortcut {
return text + "(" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")";
}
- public abstract void check(@NotNull final Project project);
-
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
+ EduUsagesCollector.taskChecked();
Project project = e.getProject();
if (project == null) {
return;
@@ -69,7 +81,55 @@ public abstract class StudyCheckAction extends StudyActionWithShortcut {
for (StudyCheckListener listener : Extensions.getExtensions(StudyCheckListener.EP_NAME)) {
listener.beforeCheck(project, task);
}
- check(project);
+ StudyTaskChecker checker = task.getChecker(project);
+ boolean isRemote = task.getLesson().getCourse() instanceof RemoteCourse;
+ ProgressManager.getInstance()
+ .run(new com.intellij.openapi.progress.Task.Backgroundable(project, "Checking Task", true, PerformInBackgroundOption.DEAF) {
+ private StudyCheckResult myResult;
+
+ @Override
+ public void onSuccess() {
+ String message = myResult.getMessage();
+ StudyStatus status = myResult.getStatus();
+ task.setStatus(status);
+ switch (status) {
+ case Failed:
+ checker.onTaskFailed(message);
+ break;
+ case Solved:
+ checker.onTaskSolved(message);
+ break;
+ default:
+ StudyCheckUtils.showTestResultPopUp(message, MessageType.WARNING.getPopupBackground(), project);
+ }
+ StudyUtils.updateToolWindows(myProject);
+ ProjectView.getInstance(myProject).refresh();
+
+ for (StudyCheckListener listener : StudyCheckListener.EP_NAME.getExtensions()) {
+ listener.afterCheck(project, task);
+ }
+ checker.clearState();
+ }
+
+ @Override
+ public void onCancel() {
+ checker.clearState();
+ }
+
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ indicator.setIndeterminate(true);
+ myResult = isRemote ? checkOnRemote() : checker.check();
+ }
+
+ private StudyCheckResult checkOnRemote() {
+ final StepicUser user = StudySettings.getInstance().getUser();
+ if (user == null) {
+ return new StudyCheckResult(StudyStatus.Unchecked, "Failed to launch checking: you're not authorized");
+ }
+ return checker.checkOnRemote(user);
+ }
+ });
}
@Override
@@ -99,6 +159,12 @@ public abstract class StudyCheckAction extends StudyActionWithShortcut {
}
}
+ @NotNull
+ @Override
+ public String getActionId() {
+ return ACTION_ID;
+ }
+
@Override
public String[] getShortcuts() {
return new String[]{SHORTCUT};
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckResult.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckResult.java
new file mode 100644
index 000000000000..3c1c5f62c942
--- /dev/null
+++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckResult.java
@@ -0,0 +1,21 @@
+package com.jetbrains.edu.learning.checker;
+
+import com.jetbrains.edu.learning.courseFormat.StudyStatus;
+
+public class StudyCheckResult {
+ private StudyStatus myStatus;
+ private String myMessage;
+
+ public StudyCheckResult(StudyStatus status, String message) {
+ myStatus = status;
+ myMessage = message;
+ }
+
+ public StudyStatus getStatus() {
+ return myStatus;
+ }
+
+ public String getMessage() {
+ return myMessage;
+ }
+}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyTaskChecker.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyTaskChecker.java
index 879621a1a725..7fcd1715b2d3 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyTaskChecker.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyTaskChecker.java
@@ -3,7 +3,9 @@ package com.jetbrains.edu.learning.checker;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
+import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
+import com.jetbrains.edu.learning.stepic.StepicUser;
import org.jetbrains.annotations.NotNull;
public class StudyTaskChecker {
@@ -24,4 +26,16 @@ public class StudyTaskChecker {
ApplicationManager.getApplication()
.invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
}
+
+ public StudyCheckResult check() {
+ return new StudyCheckResult(StudyStatus.Unchecked, "Check for " + myTask.getTaskType() + " task isn't available");
+ }
+
+ public StudyCheckResult checkOnRemote(@NotNull StepicUser user) {
+ return new StudyCheckResult(StudyStatus.Unchecked, "Remote check for " + myTask.getTaskType() + " task isn't available");
+ }
+
+ public void clearState() {
+
+ }
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/ChoiceTask.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/ChoiceTask.java
index 476af7826116..bc4804f56973 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/ChoiceTask.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/ChoiceTask.java
@@ -4,8 +4,11 @@ import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.intellij.openapi.project.Project;
import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.checker.StudyCheckResult;
import com.jetbrains.edu.learning.checker.StudyTaskChecker;
import com.jetbrains.edu.learning.editor.StudyChoiceVariantsPanel;
+import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
+import com.jetbrains.edu.learning.stepic.StepicUser;
import com.jetbrains.edu.learning.ui.StudyToolWindow;
import org.jetbrains.annotations.NotNull;
@@ -57,6 +60,11 @@ public class ChoiceTask extends Task {
@Override
public StudyTaskChecker getChecker(@NotNull Project project) {
return new StudyTaskChecker(this, project) {
+ @Override
+ public StudyCheckResult checkOnRemote(@NotNull StepicUser user) {
+ return EduAdaptiveStepicConnector.checkChoiceTask(myProject, myTask, user);
+ }
+
@Override
public void onTaskFailed(@NotNull String message) {
super.onTaskFailed(message);
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/CodeTask.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/CodeTask.java
index b82c478c43d9..cf1595a561e7 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/CodeTask.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/CodeTask.java
@@ -1,8 +1,11 @@
package com.jetbrains.edu.learning.courseFormat.tasks;
import com.intellij.openapi.project.Project;
+import com.jetbrains.edu.learning.checker.StudyCheckResult;
import com.jetbrains.edu.learning.checker.StudyCheckUtils;
import com.jetbrains.edu.learning.checker.StudyTaskChecker;
+import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
+import com.jetbrains.edu.learning.stepic.StepicUser;
import org.jetbrains.annotations.NotNull;
public class CodeTask extends Task {
@@ -26,6 +29,11 @@ public class CodeTask extends Task {
super.onTaskFailed("Wrong solution");
StudyCheckUtils.showTestResultsToolWindow(myProject, message);
}
+
+ @Override
+ public StudyCheckResult checkOnRemote(@NotNull StepicUser user) {
+ return EduAdaptiveStepicConnector.checkCodeTask(myProject, myTask, user);
+ }
};
}
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/TheoryTask.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/TheoryTask.java
index 4d0d6b0071ab..db60c2c6b3a9 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/TheoryTask.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/TheoryTask.java
@@ -1,5 +1,10 @@
package com.jetbrains.edu.learning.courseFormat.tasks;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.edu.learning.checker.StudyCheckResult;
+import com.jetbrains.edu.learning.checker.StudyTaskChecker;
+import com.jetbrains.edu.learning.courseFormat.StudyStatus;
+import com.jetbrains.edu.learning.stepic.StepicUser;
import org.jetbrains.annotations.NotNull;
public class TheoryTask extends Task {
@@ -14,4 +19,23 @@ public class TheoryTask extends Task {
public String getTaskType() {
return "theory";
}
+
+ @Override
+ public StudyTaskChecker getChecker(@NotNull Project project) {
+ return new StudyTaskChecker(this, project) {
+ @Override
+ public void onTaskSolved(@NotNull String message) {
+ }
+
+ @Override
+ public StudyCheckResult check() {
+ return new StudyCheckResult(StudyStatus.Solved, "");
+ }
+
+ @Override
+ public StudyCheckResult checkOnRemote(@NotNull StepicUser user) {
+ return new StudyCheckResult(StudyStatus.Solved, "");
+ }
+ };
+ }
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
index 28e68911c615..2a6c9ac41d70 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
@@ -13,13 +13,14 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootManager;
-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.StudySettings;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.actions.StudyCheckAction;
+import com.jetbrains.edu.learning.checker.StudyCheckResult;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.*;
import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask;
@@ -475,8 +476,8 @@ public class EduAdaptiveStepicConnector {
return null;
}
- public static Pair checkChoiceTask(@NotNull Project project, @NotNull ChoiceTask task, @NotNull StepicUser user) {
- if (task.getSelectedVariants().isEmpty()) return Pair.create(null, "No variants selected");
+ public static StudyCheckResult checkChoiceTask(@NotNull Project project, @NotNull ChoiceTask task, @NotNull StepicUser user) {
+ if (task.getSelectedVariants().isEmpty()) return new StudyCheckResult(StudyStatus.Failed, "No variants selected");
final StepicWrappers.AdaptiveAttemptWrapper.Attempt attempt = getAttemptForStep(task.getStepId(), user.getId());
if (attempt != null) {
@@ -484,12 +485,11 @@ public class EduAdaptiveStepicConnector {
final boolean isActiveAttempt = task.getSelectedVariants().stream()
.allMatch(index -> attempt.dataset.options.get(index).equals(task.getChoiceVariants().get(index)));
- if (!isActiveAttempt) return Pair.create(false, "Your solution is out of date. Please try again");
+ if (!isActiveAttempt) return new StudyCheckResult(StudyStatus.Failed, "Your solution is out of date. Please try again");
final StepicWrappers.SubmissionToPostWrapper wrapper = new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId),
createChoiceTaskAnswerArray(task));
- final Pair pair = doAdaptiveCheck(wrapper, attemptId, user.getId());
- Boolean result = pair.getFirst();
- if (result != null && !result) {
+ final StudyCheckResult result = doAdaptiveCheck(wrapper, attemptId, user.getId());
+ if (result.getStatus() == StudyStatus.Failed) {
try {
createNewAttempt(task.getStepId());
final Task updatedTask = getTask(project, task.getName(), task.getStepId(), user.getId());
@@ -503,10 +503,10 @@ public class EduAdaptiveStepicConnector {
LOG.warn(e.getMessage());
}
}
- return pair;
+ return result;
}
- return new Pair<>(null, "Failed to launch checking");
+ return new StudyCheckResult(StudyStatus.Unchecked, StudyCheckAction.FAILED_CHECK_LAUNCH);
}
private static boolean[] createChoiceTaskAnswerArray(@NotNull ChoiceTask task) {
@@ -518,7 +518,7 @@ public class EduAdaptiveStepicConnector {
return answer;
}
- public static Pair checkCodeTask(@NotNull Project project, @NotNull Task task, @NotNull StepicUser user) {
+ public static StudyCheckResult checkCodeTask(@NotNull Project project, @NotNull Task task, @NotNull StepicUser user) {
int attemptId = -1;
try {
attemptId = getAttemptId(task);
@@ -539,10 +539,10 @@ public class EduAdaptiveStepicConnector {
else {
LOG.warn("Got an incorrect attempt id: " + attemptId);
}
- return Pair.create(null, "Failed to launch checking");
+ return new StudyCheckResult(StudyStatus.Unchecked, StudyCheckAction.FAILED_CHECK_LAUNCH);
}
- private static Pair doAdaptiveCheck(@NotNull StepicWrappers.SubmissionToPostWrapper submission,
+ private static StudyCheckResult doAdaptiveCheck(@NotNull StepicWrappers.SubmissionToPostWrapper submission,
int attemptId, int userId) {
final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient();
if (client != null) {
@@ -553,7 +553,7 @@ public class EduAdaptiveStepicConnector {
final String status = wrapper.submissions[0].status;
final String hint = wrapper.submissions[0].hint;
final boolean isSolved = !status.equals("wrong");
- return Pair.create(isSolved, hint.isEmpty() ? StringUtil.capitalize(status) + " solution" : hint);
+ return new StudyCheckResult(isSolved ? StudyStatus.Solved : StudyStatus.Failed, hint.isEmpty() ? StringUtil.capitalize(status) + " solution" : hint);
}
else {
LOG.warn("Got a submission wrapper with incorrect submissions number: " + wrapper.submissions.length);
@@ -561,11 +561,10 @@ public class EduAdaptiveStepicConnector {
}
else {
LOG.warn("Can't do adaptive check: " + "wrapper is null");
- return Pair.create(null, "Can't get check results for Stepik");
+ return new StudyCheckResult(StudyStatus.Unchecked, "Can't get check results for Stepik");
}
}
-
- return Pair.create(null, "Failed to launch checking");
+ return new StudyCheckResult(StudyStatus.Unchecked, StudyCheckAction.FAILED_CHECK_LAUNCH);
}
@Nullable
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduNextRecommendationCheckListener.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduNextRecommendationCheckListener.java
index f1c7ef7677b5..0e0034e651df 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduNextRecommendationCheckListener.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduNextRecommendationCheckListener.java
@@ -1,5 +1,6 @@
package com.jetbrains.edu.learning.stepic;
+import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
@@ -32,9 +33,11 @@ public class EduNextRecommendationCheckListener implements StudyCheckListener {
if (statusAfterCheck != StudyStatus.Solved) {
return;
}
- ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Backgroundable(project, EduAdaptiveStepicConnector.LOADING_NEXT_RECOMMENDATION) {
+ ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Backgroundable(project, EduAdaptiveStepicConnector.LOADING_NEXT_RECOMMENDATION, false,
+ PerformInBackgroundOption.DEAF) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
+ indicator.setIndeterminate(true);
EduAdaptiveStepicConnector.addNextRecommendedTask(project, task.getLesson(), indicator, EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION);
}
});