remote check (EDU-882)

This commit is contained in:
Liana.Bakradze
2017-04-18 17:11:17 +03:00
parent 3ef6f8face
commit 4e9d785e20
9 changed files with 166 additions and 22 deletions
@@ -55,6 +55,7 @@
<action id="Edu.NewScratchFile" class="com.intellij.ide.scratch.ScratchFileActions$NewFileAction" text="New Scratch File...">
<add-to-group group-id="FileOpenGroup" relative-to-action="OpenFile" anchor="before"/>
</action>
<action id="Edu.Check" class="com.jetbrains.edu.learning.actions.StudyCheckAction"/>
<action id="PrevWindowAction" class="com.jetbrains.edu.learning.actions.StudyPrevWindowAction" text="Previous Answer Placeholder"
description="Navigate to the previous answer placeholder">
</action>
@@ -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<Boolean> 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};
@@ -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;
}
}
@@ -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<T extends Task> {
@@ -24,4 +26,16 @@ public class StudyTaskChecker<T extends Task> {
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() {
}
}
@@ -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<ChoiceTask>(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);
@@ -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);
}
};
}
}
@@ -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<TheoryTask>(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, "");
}
};
}
}
@@ -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<Boolean, String> 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<Boolean, String> 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<Boolean, String> 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<Boolean, String> 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
@@ -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);
}
});