check for PyCharm tasks (EDU-882)

This commit is contained in:
Liana.Bakradze
2017-04-18 17:11:17 +03:00
parent 4e9d785e20
commit d996b96ea1
13 changed files with 174 additions and 344 deletions
@@ -45,7 +45,6 @@
<action id="StudyWatchTutorial" class="com.jetbrains.edu.learning.actions.PyStudyWatchTutorialAction">
<add-to-group group-id="HelpMenu" anchor="before" relative-to-action="HelpTopics"/>
</action>
<action class="com.jetbrains.edu.learning.PyStudyCheckAction" id="PyCheckAction"/>
<!--course creator-->
<action class="com.jetbrains.edu.coursecreator.PyCCCreateProjectFromArchive" id="PyCC.NewEdu">
<add-to-group group-id="CCFileGroup" anchor="last"/>
@@ -19,9 +19,10 @@ import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiManager;
import com.intellij.util.PathUtil;
import com.jetbrains.edu.coursecreator.settings.CCSettings;
import com.jetbrains.edu.learning.actions.StudyCheckAction;
import com.jetbrains.edu.learning.checker.StudyTaskChecker;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
import com.jetbrains.python.PythonModuleTypeBase;
@@ -131,8 +132,9 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator {
}
@Override
public StudyCheckAction getCheckAction() {
return new PyStudyCheckAction();
@NotNull
public StudyTaskChecker<PyCharmTask> getPyCharmTaskChecker(@NotNull PyCharmTask task, @NotNull Project project) {
return new PyStudyTaskChecker(task, project);
}
@Override
@@ -1,126 +0,0 @@
package com.jetbrains.edu.learning;
import com.intellij.execution.ExecutionException;
import com.intellij.openapi.application.ApplicationManager;
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.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.jetbrains.edu.learning.actions.StudyCheckAction;
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.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.editor.StudyEditor;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class PyStudyCheckAction extends StudyCheckAction {
private static final Logger LOG = Logger.getInstance(PyStudyCheckAction.class);
public static final String ACTION_ID = "PyCheckAction";
public void check(@NotNull Project project) {
EduUsagesCollector.taskChecked();
ApplicationManager.getApplication().runWriteAction(() -> CommandProcessor.getInstance().runUndoTransparentAction(() -> {
final StudyEditor selectedEditor = StudyUtils.getSelectedStudyEditor(project);
if (selectedEditor == null) return;
final StudyState studyState = new StudyState(selectedEditor);
if (!studyState.isValid()) {
LOG.info("StudyCheckAction was invoked outside study editor");
return;
}
if (StudyCheckUtils.hasBackgroundProcesses(project)) return;
final Task task = studyState.getTask();
final VirtualFile taskDir = studyState.getTaskDir();
StudyCheckUtils.flushWindows(task, taskDir);
ApplicationManager.getApplication().invokeLater(
() -> IdeFocusManager.getInstance(project).requestFocus(studyState.getEditor().getComponent(), true));
final StudyTestRunner testRunner = new PyStudyTestRunner(task, taskDir);
Process testProcess = null;
String commandLine = "";
try {
final VirtualFile executablePath = getTaskVirtualFile(studyState, task, taskDir);
if (executablePath != null) {
commandLine = executablePath.getPath();
testProcess = testRunner.createCheckProcess(project, commandLine);
}
}
catch (ExecutionException e) {
LOG.error(e);
}
if (testProcess == null) {
return;
}
myCheckInProgress.set(true);
StudyCheckTask checkTask = getCheckTask(project, studyState, testRunner, testProcess, commandLine);
ProgressManager.getInstance().run(checkTask);
}));
}
@NotNull
private StudyCheckTask getCheckTask(@NotNull final Project project,
final StudyState studyState,
final StudyTestRunner testRunner,
final Process testProcess,
final String commandLine) {
return new StudyCheckTask(project, studyState, myCheckInProgress, testProcess, commandLine) {
@Override
protected void onTaskFailed(@NotNull String message) {
super.onTaskFailed(message);
ApplicationManager.getApplication().invokeLater(() -> {
if (myTaskDir == null) return;
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
final String name = entry.getKey();
final TaskFile taskFile = entry.getValue();
if (taskFile.getActivePlaceholders().size() < 2) {
continue;
}
final Course course = myTaskManger.getCourse();
if (course != null && EduNames.STUDY.equals(course.getCourseMode())) {
CommandProcessor.getInstance().runUndoTransparentAction(
() -> ApplicationManager.getApplication().runWriteAction(
() -> StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project)));
}
}
StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
});
}
};
}
@Nullable
private static VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState,
@NotNull final Task task,
@NotNull final VirtualFile taskDir) {
VirtualFile taskVirtualFile = studyState.getVirtualFile();
for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
String name = entry.getKey();
TaskFile taskFile = entry.getValue();
VirtualFile virtualFile = taskDir.findFileByRelativePath(name);
if (virtualFile != null) {
if (!taskFile.getActivePlaceholders().isEmpty()) {
taskVirtualFile = virtualFile;
}
}
}
return taskVirtualFile;
}
@NotNull
@Override
public String getActionId() {
return ACTION_ID;
}
}
@@ -20,7 +20,7 @@ public class PyStudyInstructionPainter extends EditorEmptyTextPainter {
appendAction(painter, "Navigate between tasks", getActionShortcutText(StudyPreviousTaskAction.ACTION_ID) + separator +
getActionShortcutText(StudyNextTaskAction.ACTION_ID));
appendAction(painter, "Reset current task file", getActionShortcutText(StudyRefreshTaskFileAction.ACTION_ID));
appendAction(painter, "Check task", getActionShortcutText(PyStudyCheckAction.ACTION_ID));
appendAction(painter, "Check task", getActionShortcutText(StudyCheckAction.ACTION_ID));
appendAction(painter, "Get hint for the answer placeholder", getActionShortcutText(StudyShowHintAction.ACTION_ID));
}
}
@@ -0,0 +1,114 @@
package com.jetbrains.edu.learning;
import com.intellij.execution.ExecutionException;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.learning.actions.StudyCheckAction;
import com.jetbrains.edu.learning.checker.*;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.stepic.StepicUser;
import one.util.streamex.EntryStream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class PyStudyTaskChecker extends StudyTaskChecker<PyCharmTask> {
private static final Logger LOG = Logger.getInstance(PyStudyTaskChecker.class);
public PyStudyTaskChecker(PyCharmTask task, Project project) {
super(task, project);
}
@Override
public StudyCheckResult check() {
VirtualFile taskDir = myTask.getTaskDir(myProject);
if (taskDir == null) {
LOG.info("taskDir is null for task " + myTask.getName());
return new StudyCheckResult(StudyStatus.Unchecked, "Task is broken");
}
ApplicationManager.getApplication()
.invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> StudyCheckUtils.flushWindows(myTask, taskDir)));
final StudyTestRunner testRunner = new PyStudyTestRunner(myTask, taskDir);
try {
final VirtualFile fileToCheck = getTaskVirtualFile(myTask, taskDir);
if (fileToCheck != null) {
Process testProcess = testRunner.createCheckProcess(myProject, fileToCheck.getPath());
StudyTestsOutputParser.TestsOutput output =
StudyCheckUtils
.getTestOutput(testProcess, testRunner.getCommandLine().getCommandLineString(), myTask.getLesson().getCourse().isAdaptive());
return new StudyCheckResult(output.isSuccess() ? StudyStatus.Solved : StudyStatus.Failed, output.getMessage());
}
}
catch (ExecutionException e) {
LOG.error(e);
}
return new StudyCheckResult(StudyStatus.Unchecked, StudyCheckAction.FAILED_CHECK_LAUNCH);
}
@Override
public void clearState() {
ApplicationManager.getApplication().invokeLater(() -> {
StudyCheckUtils.drawAllPlaceholders(myProject, myTask);
VirtualFile taskDir = myTask.getTaskDir(myProject);
if (taskDir != null) {
EduUtils.deleteWindowDescriptions(myTask, taskDir);
}
});
}
@Override
public void onTaskFailed(@NotNull String message) {
super.onTaskFailed(message);
ApplicationManager.getApplication().invokeLater(() -> {
VirtualFile taskDir = myTask.getTaskDir(myProject);
if (taskDir == null) return;
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
final String name = entry.getKey();
final TaskFile taskFile = entry.getValue();
if (taskFile.getActivePlaceholders().size() < 2) {
continue;
}
final Course course = myTask.getLesson().getCourse();
if (course != null && EduNames.STUDY.equals(course.getCourseMode())) {
CommandProcessor.getInstance().runUndoTransparentAction(
() -> ApplicationManager.getApplication().runWriteAction(
() -> StudyCheckUtils.runSmartTestProcess(taskDir, new PyStudyTestRunner(myTask, taskDir), name, taskFile, myProject)));
}
}
StudyCheckUtils.navigateToFailedPlaceholder(new StudyState(StudyUtils.getSelectedStudyEditor(myProject)), myTask, taskDir, myProject);
});
}
@Nullable
private static VirtualFile getTaskVirtualFile(@NotNull final Task task,
@NotNull final VirtualFile taskDir) {
Map<TaskFile, VirtualFile> fileMap =
EntryStream.of(task.getTaskFiles()).invert().mapValues(name -> taskDir.findFileByRelativePath(name)).nonNullValues().toMap();
Map.Entry<TaskFile, VirtualFile> entry = EntryStream.of(fileMap).findAny(e -> !e.getKey().getActivePlaceholders().isEmpty())
.orElse(fileMap.entrySet().stream().findFirst().orElse(null));
return entry == null ? null : entry.getValue();
}
@Override
public StudyCheckResult checkOnRemote(@NotNull StepicUser user) {
StudyCheckResult result = check();
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
StudyStatus status = result.getStatus();
if (course != null && EduNames.STUDY.equals(course.getCourseMode()) && status != StudyStatus.Unchecked) {
EduStepicConnector.postSolution(myTask, status == StudyStatus.Solved, myProject);
}
return result;
}
}
@@ -20,6 +20,7 @@ import java.util.Map;
public class PyStudyTestRunner extends StudyTestRunner {
private static final String PYTHONPATH = "PYTHONPATH";
private GeneralCommandLine myCommandLine;
PyStudyTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
super(task, taskDir);
@@ -35,9 +36,9 @@ public class PyStudyTestRunner extends StudyTestRunner {
testsFileName += EduNames.SUBTASK_MARKER + index + "." + FileUtilRt.getExtension(configurator.getTestFileName());
}
final File testRunner = new File(myTaskDir.getPath(), testsFileName);
final GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.withWorkDirectory(myTaskDir.getPath());
final Map<String, String> env = commandLine.getEnvironment();
myCommandLine = new GeneralCommandLine();
myCommandLine.withWorkDirectory(myTaskDir.getPath());
final Map<String, String> env = myCommandLine.getEnvironment();
final VirtualFile courseDir = project.getBaseDir();
if (courseDir != null) {
@@ -46,12 +47,17 @@ public class PyStudyTestRunner extends StudyTestRunner {
if (sdk != null) {
String pythonPath = sdk.getHomePath();
if (pythonPath != null) {
commandLine.setExePath(pythonPath);
commandLine.addParameter(testRunner.getPath());
commandLine.addParameter(FileUtil.toSystemDependentName(executablePath));
return commandLine.createProcess();
myCommandLine.setExePath(pythonPath);
myCommandLine.addParameter(testRunner.getPath());
myCommandLine.addParameter(FileUtil.toSystemDependentName(executablePath));
return myCommandLine.createProcess();
}
}
return null;
}
@Override
public GeneralCommandLine getCommandLine() {
return myCommandLine;
}
}