EDU-649 PyCharm fails to launch checker for educational plugin
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / checker / StudyCheckTask.java
1 package com.jetbrains.edu.learning.checker;
2
3 import com.intellij.execution.process.CapturingProcessHandler;
4 import com.intellij.execution.process.ProcessOutput;
5 import com.intellij.ide.projectView.ProjectView;
6 import com.intellij.openapi.application.ApplicationManager;
7 import com.intellij.openapi.diagnostic.Logger;
8 import com.intellij.openapi.progress.ProgressIndicator;
9 import com.intellij.openapi.project.Project;
10 import com.intellij.openapi.ui.MessageType;
11 import com.intellij.openapi.util.Pair;
12 import com.intellij.openapi.util.Ref;
13 import com.intellij.openapi.util.text.StringUtil;
14 import com.intellij.openapi.vfs.VirtualFile;
15 import com.jetbrains.edu.learning.StudyPluginConfigurator;
16 import com.jetbrains.edu.learning.StudyState;
17 import com.jetbrains.edu.learning.StudyTaskManager;
18 import com.jetbrains.edu.learning.StudyUtils;
19 import com.jetbrains.edu.learning.actions.StudyAfterCheckAction;
20 import com.jetbrains.edu.learning.core.EduNames;
21 import com.jetbrains.edu.learning.core.EduUtils;
22 import com.jetbrains.edu.learning.courseFormat.Course;
23 import com.jetbrains.edu.learning.courseFormat.StudyStatus;
24 import com.jetbrains.edu.learning.courseFormat.Task;
25 import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
26 import com.jetbrains.edu.learning.stepic.EduStepicConnector;
27 import com.jetbrains.edu.learning.stepic.StepicUser;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable {
32
33   private static final Logger LOG = Logger.getInstance(StudyCheckTask.class);
34   private final Project myProject;
35   protected final StudyState myStudyState;
36   protected final Task myTask;
37   protected final VirtualFile myTaskDir;
38   protected final StudyTaskManager myTaskManger;
39   private final StudyStatus myStatusBeforeCheck;
40   private final Ref<Boolean> myCheckInProcess;
41   private final Process myTestProcess;
42   private final String myCommandLine;
43   private final String FAILED_CHECK_LAUNCH = "Failed to launch checking";
44
45   public StudyCheckTask(Project project, StudyState studyState, Ref<Boolean> checkInProcess, Process testProcess, String commandLine) {
46     super(project, "Checking Task");
47     myProject = project;
48     myStudyState = studyState;
49     myCheckInProcess = checkInProcess;
50     myTestProcess = testProcess;
51     myCommandLine = commandLine;
52     myTask = studyState.getTask();
53     myTaskDir = studyState.getTaskDir();
54     myTaskManger = StudyTaskManager.getInstance(myProject);
55     myStatusBeforeCheck = myTask.getStatus();
56   }
57
58   @Override
59   public void onSuccess() {
60     StudyUtils.updateToolWindows(myProject);
61     StudyCheckUtils.drawAllPlaceholders(myProject, myTask, myTaskDir);
62     ProjectView.getInstance(myProject).refresh();
63     clearState();
64   }
65
66   protected void clearState() {
67     EduUtils.deleteWindowDescriptions(myTask, myTaskDir);
68     myCheckInProcess.set(false);
69   }
70
71   @Override
72   public void onCancel() {
73     myTask.setStatus(myStatusBeforeCheck);
74     clearState();
75   }
76
77   @Override
78   public void run(@NotNull ProgressIndicator indicator) {
79     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
80     if (course != null) {
81       if (course.isAdaptive()) {
82         checkForAdaptiveCourse(indicator);
83       }
84       else {
85         checkForEduCourse(indicator);
86       }
87     }
88   }
89
90   private void checkForEduCourse(@NotNull ProgressIndicator indicator) {
91     final StudyTestsOutputParser.TestsOutput testsOutput = getTestOutput(indicator);
92     
93     if (testsOutput != null) {
94       if (testsOutput.isSuccess()) {
95         onTaskSolved(testsOutput.getMessage());
96       }
97       else {
98         onTaskFailed(testsOutput.getMessage());
99       }
100       runAfterTaskCheckedActions();
101       final Course course = StudyTaskManager.getInstance(myProject).getCourse();
102       if (course != null && EduNames.STUDY.equals(course.getCourseMode())) {
103         postAttemptToStepic(testsOutput);
104       }
105     }
106   }
107
108   @Nullable
109   private StudyTestsOutputParser.TestsOutput getTestOutput(@NotNull ProgressIndicator indicator) {
110     final CapturingProcessHandler handler = new CapturingProcessHandler(myTestProcess, null, myCommandLine);
111     final ProcessOutput output = handler.runProcessWithProgressIndicator(indicator);
112     if (indicator.isCanceled()) {
113       ApplicationManager.getApplication().invokeLater(
114         () -> StudyCheckUtils.showTestResultPopUp("Check cancelled", MessageType.WARNING.getPopupBackground(), myProject));
115     }
116
117     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
118     if (course != null) {
119       final StudyTestsOutputParser.TestsOutput testsOutput = StudyTestsOutputParser.getTestsOutput(output, course.isAdaptive());
120       String stderr = output.getStderr();
121       if (!stderr.isEmpty()) {
122         //log error output of tests
123         LOG.info("#educational " + stderr);
124       }
125       return testsOutput;
126     }
127     return null;
128   }
129
130   private void checkForAdaptiveCourse(ProgressIndicator indicator) {
131     final StudyTestsOutputParser.TestsOutput testOutput = getTestOutput(indicator);
132     if (testOutput != null) {
133       if (testOutput.isSuccess()) {
134         final Pair<Boolean, String> pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
135         if (pair != null && !(!pair.getFirst() && pair.getSecond().isEmpty())) {
136           if (pair.getFirst()) {
137             onTaskSolved("Congratulations! Remote tests passed.");
138             if (myStatusBeforeCheck != StudyStatus.Solved) {
139               EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, 2);
140             }
141           }
142           else {
143             final String checkMessage = pair.getSecond();
144             onTaskFailed(checkMessage);
145           }
146           runAfterTaskCheckedActions();
147         }
148         else {
149           ApplicationManager.getApplication().invokeLater(() -> StudyCheckUtils.showTestResultPopUp(FAILED_CHECK_LAUNCH,
150                                                                                                     MessageType.WARNING
151                                                                                                       .getPopupBackground(),
152                                                                                                     myProject));
153         }
154       }
155       else {
156         onTaskFailed(testOutput.getMessage());
157       }
158     }
159   }
160
161   protected void onTaskFailed(String message) {
162     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
163     myTask.setStatus(StudyStatus.Failed);
164     if (course != null) {
165       if (course.isAdaptive()) {
166         ApplicationManager.getApplication().invokeLater(
167           () -> {
168             StudyCheckUtils.showTestResultPopUp("Failed", MessageType.ERROR.getPopupBackground(), myProject);
169             StudyCheckUtils.showTestResultsToolWindow(myProject, message, false);
170           });
171       }
172       else {
173         ApplicationManager.getApplication()
174           .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
175       }
176     }
177   }
178
179   protected void onTaskSolved(String message) {
180     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
181     myTask.setStatus(StudyStatus.Solved);
182     if (course != null) {
183       if (course.isAdaptive()) {
184         ApplicationManager.getApplication().invokeLater(
185           () -> {
186             StudyCheckUtils.showTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), myProject);
187             StudyCheckUtils.showTestResultsToolWindow(myProject, message, true);
188           });
189       }
190       else {
191         ApplicationManager.getApplication()
192           .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject));
193       }
194     }
195   }
196
197   private void runAfterTaskCheckedActions() {
198     StudyPluginConfigurator configurator = StudyUtils.getConfigurator(myProject);
199     if (configurator != null) {
200       StudyAfterCheckAction[] checkActions = configurator.getAfterCheckActions();
201       if (checkActions != null) {
202         for (StudyAfterCheckAction action : checkActions) {
203           action.run(myProject, myTask, myStatusBeforeCheck);
204         }
205       }
206     }
207     else {
208       LOG.warn("No configurator is provided for the plugin");
209     }
210   }
211
212   protected void postAttemptToStepic(@NotNull StudyTestsOutputParser.TestsOutput testsOutput) {
213     final StudyTaskManager studySettings = StudyTaskManager.getInstance(myProject);
214     final StepicUser user = studySettings.getUser();
215     if (user == null) return;
216     final String login = user.getEmail();
217     final String password = StringUtil.isEmptyOrSpaces(login) ? "" : user.getPassword();
218     EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), login, password);
219   }
220 }