0a9d5ca8539445c56c2ee05d8c2538335b6f309c
[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         ApplicationManager.getApplication().invokeLater(() ->
123                                                           StudyCheckUtils.showTestResultPopUp(FAILED_CHECK_LAUNCH,
124                                                                                               MessageType.WARNING.getPopupBackground(),
125                                                                                               myProject));
126         //log error output of tests
127         LOG.info("#educational " + stderr);
128         return null;
129       }
130       return testsOutput;
131     }
132     return null;
133   }
134
135   private void checkForAdaptiveCourse(ProgressIndicator indicator) {
136     final StudyTestsOutputParser.TestsOutput testOutput = getTestOutput(indicator);
137     if (testOutput != null) {
138       if (testOutput.isSuccess()) {
139         final Pair<Boolean, String> pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
140         if (pair != null && !(!pair.getFirst() && pair.getSecond().isEmpty())) {
141           if (pair.getFirst()) {
142             onTaskSolved("Congratulations! Remote tests passed.");
143             if (myStatusBeforeCheck != StudyStatus.Solved) {
144               EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, 2);
145             }
146           }
147           else {
148             final String checkMessage = pair.getSecond();
149             onTaskFailed(checkMessage);
150           }
151           runAfterTaskCheckedActions();
152         }
153         else {
154           ApplicationManager.getApplication().invokeLater(() -> StudyCheckUtils.showTestResultPopUp(FAILED_CHECK_LAUNCH,
155                                                                                                     MessageType.WARNING
156                                                                                                       .getPopupBackground(),
157                                                                                                     myProject));
158         }
159       }
160       else {
161         onTaskFailed(testOutput.getMessage());
162       }
163     }
164   }
165
166   protected void onTaskFailed(String message) {
167     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
168     myTask.setStatus(StudyStatus.Failed);
169     if (course != null) {
170       if (course.isAdaptive()) {
171         ApplicationManager.getApplication().invokeLater(
172           () -> {
173             StudyCheckUtils.showTestResultPopUp("Failed", MessageType.ERROR.getPopupBackground(), myProject);
174             StudyCheckUtils.showTestResultsToolWindow(myProject, message, false);
175           });
176       }
177       else {
178         ApplicationManager.getApplication()
179           .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
180       }
181     }
182   }
183
184   protected void onTaskSolved(String message) {
185     final Course course = StudyTaskManager.getInstance(myProject).getCourse();
186     myTask.setStatus(StudyStatus.Solved);
187     if (course != null) {
188       if (course.isAdaptive()) {
189         ApplicationManager.getApplication().invokeLater(
190           () -> {
191             StudyCheckUtils.showTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), myProject);
192             StudyCheckUtils.showTestResultsToolWindow(myProject, message, true);
193           });
194       }
195       else {
196         ApplicationManager.getApplication()
197           .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject));
198       }
199     }
200   }
201
202   private void runAfterTaskCheckedActions() {
203     StudyPluginConfigurator configurator = StudyUtils.getConfigurator(myProject);
204     if (configurator != null) {
205       StudyAfterCheckAction[] checkActions = configurator.getAfterCheckActions();
206       if (checkActions != null) {
207         for (StudyAfterCheckAction action : checkActions) {
208           action.run(myProject, myTask, myStatusBeforeCheck);
209         }
210       }
211     }
212     else {
213       LOG.warn("No configurator is provided for the plugin");
214     }
215   }
216
217   protected void postAttemptToStepic(@NotNull StudyTestsOutputParser.TestsOutput testsOutput) {
218     final StudyTaskManager studySettings = StudyTaskManager.getInstance(myProject);
219     final StepicUser user = studySettings.getUser();
220     if (user == null) return;
221     final String login = user.getEmail();
222     final String password = StringUtil.isEmptyOrSpaces(login) ? "" : user.getPassword();
223     EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), login, password);
224   }
225 }