mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Add tests from samples provided for step and show test result in separate tool window
This commit is contained in:
@@ -85,6 +85,7 @@
|
||||
|
||||
<toolWindow id="Task Description" anchor="right" factoryClass="com.jetbrains.edu.learning.ui.StudyToolWindowFactory" conditionClass="com.jetbrains.edu.learning.ui.StudyCondition"/>
|
||||
<toolWindow id="Course Progress" anchor="left" factoryClass="com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory" conditionClass="com.jetbrains.edu.learning.ui.StudyCondition"/>
|
||||
<toolWindow id="Test Results" anchor="bottom" factoryClass="com.jetbrains.edu.learning.ui.StudyTestResultsToolWindowFactory" conditionClass="com.jetbrains.edu.learning.ui.StudyCondition"/>
|
||||
<fileEditorProvider implementation="com.jetbrains.edu.learning.editor.StudyFileEditorProvider"/>
|
||||
<treeStructureProvider implementation="com.jetbrains.edu.learning.projectView.StudyTreeStructureProvider"/>
|
||||
<highlightErrorFilter implementation="com.jetbrains.edu.learning.editor.StudyHighlightErrorFilter"/>
|
||||
|
||||
+1
@@ -69,6 +69,7 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur
|
||||
Task task = getTask(file);
|
||||
setTaskText(task, file.getParent());
|
||||
}
|
||||
toolWindow.setBottomComponent(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+52
-36
@@ -25,6 +25,7 @@ import com.jetbrains.edu.learning.courseFormat.Task;
|
||||
import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable {
|
||||
|
||||
@@ -75,21 +76,36 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
|
||||
if (course != null && course.isAdaptive()) {
|
||||
checkAdaptiveCourse(indicator);
|
||||
checkForAdaptiveCourse(indicator);
|
||||
}
|
||||
else {
|
||||
if (checkCourse(indicator)) return;
|
||||
checkForEduCourse(indicator);
|
||||
}
|
||||
runAfterTaskSolvedActions();
|
||||
}
|
||||
|
||||
private boolean checkCourse(@NotNull ProgressIndicator indicator) {
|
||||
private void checkForEduCourse(@NotNull ProgressIndicator indicator) {
|
||||
final StudyTestsOutputParser.TestsOutput testsOutput = getTestOutput(indicator);
|
||||
|
||||
postAttemptToStepic(testsOutput);
|
||||
|
||||
if (testsOutput != null) {
|
||||
if (testsOutput.isSuccess()) {
|
||||
onTaskSolved(testsOutput.getMessage());
|
||||
}
|
||||
else {
|
||||
onTaskFailed(testsOutput.getMessage());
|
||||
}
|
||||
runAfterTaskCheckedActions();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private StudyTestsOutputParser.TestsOutput getTestOutput(@NotNull ProgressIndicator indicator) {
|
||||
final CapturingProcessHandler handler = new CapturingProcessHandler(myTestProcess, null, myCommandLine);
|
||||
final ProcessOutput output = handler.runProcessWithProgressIndicator(indicator);
|
||||
if (indicator.isCanceled()) {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> StudyCheckUtils.showTestResultPopUp("Check cancelled", MessageType.WARNING.getPopupBackground(), myProject));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,64 +118,64 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
myProject));
|
||||
//log error output of tests
|
||||
LOG.info("#educational " + stderr);
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
|
||||
postAttemptToStepic(testsOutput);
|
||||
|
||||
|
||||
if (testsOutput.isSuccess()) {
|
||||
onTaskSolved(testsOutput.getMessage());
|
||||
}
|
||||
else {
|
||||
onTaskFailed(testsOutput.getMessage());
|
||||
}
|
||||
return false;
|
||||
return testsOutput;
|
||||
}
|
||||
|
||||
private void checkAdaptiveCourse(ProgressIndicator indicator) {
|
||||
private void checkForAdaptiveCourse(ProgressIndicator indicator) {
|
||||
ProgressManager.getInstance().runProcessWithProgressAsynchronously(new Backgroundable(myProject, "Checking Task") {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
final Pair<Boolean, String> pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
|
||||
if (pair != null) {
|
||||
final String checkMessage = pair.getSecond();
|
||||
if (pair.getFirst()) {
|
||||
onTaskSolved(checkMessage);
|
||||
final StudyTestsOutputParser.TestsOutput testOutput = getTestOutput(indicator);
|
||||
if (testOutput != null) {
|
||||
if (testOutput.isSuccess()) {
|
||||
final Pair<Boolean, String> pair = EduAdaptiveStepicConnector.checkTask(myProject, myTask);
|
||||
if (pair != null && !pair.getSecond().isEmpty()) {
|
||||
final String checkMessage = pair.getSecond();
|
||||
if (pair.getFirst()) {
|
||||
onTaskSolved(checkMessage);
|
||||
}
|
||||
else {
|
||||
onTaskFailed(checkMessage);
|
||||
}
|
||||
runAfterTaskCheckedActions();
|
||||
}
|
||||
else {
|
||||
ApplicationManager.getApplication().invokeLater(() ->
|
||||
StudyCheckUtils.showTestResultPopUp("Failed to launch checking",
|
||||
MessageType.WARNING
|
||||
.getPopupBackground(),
|
||||
myProject));
|
||||
}
|
||||
}
|
||||
else {
|
||||
onTaskFailed(checkMessage);
|
||||
onTaskFailed(testOutput.getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
ApplicationManager.getApplication().invokeLater(() ->
|
||||
StudyCheckUtils.showTestResultPopUp("Failed to launch checking",
|
||||
MessageType.WARNING.getPopupBackground(),
|
||||
myProject));
|
||||
}
|
||||
}
|
||||
}, indicator);
|
||||
|
||||
}
|
||||
|
||||
protected void onTaskFailed( String message) {
|
||||
protected void onTaskFailed(String message) {
|
||||
myTaskManger.setStatus(myTask, StudyStatus.Failed);
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
|
||||
() -> StudyCheckUtils.showTestResults(myProject, message));
|
||||
}
|
||||
|
||||
protected void onTaskSolved(String message) {
|
||||
myTaskManger.setStatus(myTask, StudyStatus.Solved);
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject));
|
||||
() -> StudyCheckUtils.showTestResults(myProject, message));
|
||||
}
|
||||
|
||||
private void runAfterTaskSolvedActions() {
|
||||
private void runAfterTaskCheckedActions() {
|
||||
StudyPluginConfigurator configurator = StudyUtils.getConfigurator(myProject);
|
||||
if (configurator != null) {
|
||||
StudyAfterCheckAction[] checkActions = configurator.getAfterCheckActions();
|
||||
if (checkActions != null) {
|
||||
for (StudyAfterCheckAction action: checkActions) {
|
||||
for (StudyAfterCheckAction action : checkActions) {
|
||||
action.run(myProject, myTask, myStatusBeforeCheck);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-17
@@ -15,23 +15,25 @@ import com.intellij.openapi.ui.popup.BalloonBuilder;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import com.intellij.openapi.wm.IdeFrame;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.openapi.wm.*;
|
||||
import com.intellij.openapi.wm.ex.StatusBarEx;
|
||||
import com.intellij.openapi.wm.ex.WindowManagerEx;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.jetbrains.edu.learning.StudyState;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.core.EduDocumentListener;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
|
||||
import com.jetbrains.edu.learning.courseFormat.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.StudyState;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.editor.StudyEditor;
|
||||
import com.jetbrains.edu.learning.navigation.StudyNavigator;
|
||||
import com.jetbrains.edu.learning.ui.StudyTestResultsToolWindow;
|
||||
import com.jetbrains.edu.learning.ui.StudyTestResultsToolWindowFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -61,9 +63,9 @@ public class StudyCheckUtils {
|
||||
}
|
||||
|
||||
public static void navigateToFailedPlaceholder(@NotNull final StudyState studyState,
|
||||
@NotNull final Task task,
|
||||
@NotNull final VirtualFile taskDir,
|
||||
@NotNull final Project project) {
|
||||
@NotNull final Task task,
|
||||
@NotNull final VirtualFile taskDir,
|
||||
@NotNull final Project project) {
|
||||
TaskFile selectedTaskFile = studyState.getTaskFile();
|
||||
Editor editor = studyState.getEditor();
|
||||
TaskFile taskFileToNavigate = selectedTaskFile;
|
||||
@@ -109,10 +111,10 @@ public class StudyCheckUtils {
|
||||
|
||||
|
||||
public static void runSmartTestProcess(@NotNull final VirtualFile taskDir,
|
||||
@NotNull final StudyTestRunner testRunner,
|
||||
final String taskFileName,
|
||||
@NotNull final TaskFile taskFile,
|
||||
@NotNull final Project project) {
|
||||
@NotNull final StudyTestRunner testRunner,
|
||||
final String taskFileName,
|
||||
@NotNull final TaskFile taskFile,
|
||||
@NotNull final Project project) {
|
||||
final TaskFile answerTaskFile = new TaskFile();
|
||||
answerTaskFile.name = taskFileName;
|
||||
final VirtualFile virtualFile = taskDir.findChild(taskFileName);
|
||||
@@ -135,11 +137,10 @@ public class StudyCheckUtils {
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static VirtualFile getCopyWithAnswers(@NotNull final VirtualFile taskDir,
|
||||
@NotNull final VirtualFile file,
|
||||
@NotNull final TaskFile source,
|
||||
@NotNull final TaskFile target) {
|
||||
@NotNull final VirtualFile file,
|
||||
@NotNull final TaskFile source,
|
||||
@NotNull final TaskFile target) {
|
||||
VirtualFile copy = null;
|
||||
try {
|
||||
|
||||
@@ -193,4 +194,22 @@ public class StudyCheckUtils {
|
||||
EduUtils.flushWindows(taskFile, virtualFile, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void showTestResults(@NotNull final Project project, @NotNull final String message) {
|
||||
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
|
||||
ToolWindow window = toolWindowManager.getToolWindow("Test Results");
|
||||
if (window == null) {
|
||||
toolWindowManager.registerToolWindow("Test Results", true, ToolWindowAnchor.BOTTOM);
|
||||
window = toolWindowManager.getToolWindow("Test Results");
|
||||
new StudyTestResultsToolWindowFactory().createToolWindowContent(project, window);
|
||||
}
|
||||
final Content[] contents = window.getContentManager().getContents();
|
||||
for (Content content : contents) {
|
||||
final JComponent component = content.getComponent();
|
||||
if (component instanceof StudyTestResultsToolWindow) {
|
||||
((StudyTestResultsToolWindow)component).setText(message);
|
||||
component.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import com.intellij.execution.process.ProcessOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class StudyTestsOutputParser {
|
||||
private static final String ourStudyPrefix = "#educational_plugin";
|
||||
private static final String STUDY_PREFIX = "#educational_plugin";
|
||||
public static final String TEST_OK = "test OK";
|
||||
private static final String TEST_FAILED = "FAILED + ";
|
||||
private static final String CONGRATS_MESSAGE = "CONGRATS_MESSAGE ";
|
||||
@@ -32,7 +32,7 @@ public class StudyTestsOutputParser {
|
||||
public static TestsOutput getTestsOutput(@NotNull final ProcessOutput processOutput) {
|
||||
String congratulations = CONGRATULATIONS;
|
||||
for (String line : processOutput.getStdoutLines()) {
|
||||
if (line.startsWith(ourStudyPrefix)) {
|
||||
if (line.startsWith(STUDY_PREFIX)) {
|
||||
if (line.contains(TEST_OK)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+29
-30
@@ -4,13 +4,13 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
@@ -23,6 +23,7 @@ public class StudyGenerator {
|
||||
private StudyGenerator() {
|
||||
|
||||
}
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(StudyGenerator.class.getName());
|
||||
|
||||
/**
|
||||
@@ -107,34 +108,32 @@ public class StudyGenerator {
|
||||
public static void createCourse(@NotNull final Course course, @NotNull final VirtualFile baseDir, @NotNull final File resourceRoot,
|
||||
@NotNull final Project project) {
|
||||
|
||||
try {
|
||||
final List<Lesson> lessons = course.getLessons();
|
||||
for (int i = 1; i <= lessons.size(); i++) {
|
||||
Lesson lesson = lessons.get(i - 1);
|
||||
lesson.setIndex(i);
|
||||
createLesson(lesson, baseDir, resourceRoot, project);
|
||||
}
|
||||
baseDir.createChildDirectory(project, EduNames.SANDBOX_DIR);
|
||||
File[] files = resourceRoot.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS);
|
||||
}
|
||||
});
|
||||
for (File file : files) {
|
||||
File dir = new File(baseDir.getPath(), file.getName());
|
||||
if (file.isDirectory()) {
|
||||
FileUtil.copyDir(file, dir);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
final List<Lesson> lessons = course.getLessons();
|
||||
for (int i = 1; i <= lessons.size(); i++) {
|
||||
Lesson lesson = lessons.get(i - 1);
|
||||
lesson.setIndex(i);
|
||||
createLesson(lesson, baseDir, resourceRoot, project);
|
||||
}
|
||||
baseDir.createChildDirectory(project, EduNames.SANDBOX_DIR);
|
||||
File[] files = resourceRoot.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS);
|
||||
}
|
||||
});
|
||||
for (File file : files) {
|
||||
File dir = new File(baseDir.getPath(), file.getName());
|
||||
if (file.isDirectory()) {
|
||||
FileUtil.copyDir(file, dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
FileUtil.copy(file, dir);
|
||||
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
FileUtil.copy(file, dir);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+80
-41
@@ -9,6 +9,7 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
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.StudyTaskManager;
|
||||
@@ -42,6 +43,7 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getHttpClient;
|
||||
@@ -57,6 +59,8 @@ public class EduAdaptiveStepicConnector {
|
||||
private static final String RECOMMENDATION_REACTIONS_URL = "recommendation-reactions";
|
||||
private static final String ATTEMPTS_URL = "attempts";
|
||||
private static final String SUBMISSION_URL = "submissions";
|
||||
private static final String PYTHON2 = "python2";
|
||||
private static final String PYTHON3 = "python3";
|
||||
|
||||
@Nullable
|
||||
public static Task getNextRecommendation(@NotNull final Project project, @NotNull Course course) {
|
||||
@@ -88,9 +92,11 @@ public class EduAdaptiveStepicConnector {
|
||||
for (int stepId : realLesson.steps) {
|
||||
final StepicWrappers.Step step = EduStepicConnector.getStep(stepId);
|
||||
if (step.name.equals("code")) {
|
||||
return getTaskFromStep(realLesson.getName(), stepId, step);
|
||||
return getTaskFromStep(project, stepId, step, realLesson.getName());
|
||||
}
|
||||
}
|
||||
|
||||
LOG.warn("Got a lesson without code part as a recommendation");
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -106,8 +112,8 @@ public class EduAdaptiveStepicConnector {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean postRecommendationReaction(@NotNull final Project project, @NotNull final String lessonId,
|
||||
@NotNull final String user,int reaction) {
|
||||
public static boolean postRecommendationReaction(@NotNull final Project project, @NotNull final String lessonId,
|
||||
@NotNull final String user, int reaction) {
|
||||
|
||||
final HttpPost post = new HttpPost(STEPIC_API_URL + RECOMMENDATION_REACTIONS_URL);
|
||||
final String json = new Gson()
|
||||
@@ -132,7 +138,7 @@ public class EduAdaptiveStepicConnector {
|
||||
// TODO: get user from settings
|
||||
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
|
||||
if (user != null &&
|
||||
postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().id),
|
||||
postRecommendationReaction(project, String.valueOf(editor.getTaskFile().getTask().getLesson().id),
|
||||
String.valueOf(user.id), reaction)) {
|
||||
final Task task = getNextRecommendation(project, course);
|
||||
|
||||
@@ -188,16 +194,37 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Task getTaskFromStep(@NotNull String name, int lessonID, @NotNull final StepicWrappers.Step step) {
|
||||
private static Task getTaskFromStep(Project project,
|
||||
int lessonID,
|
||||
@NotNull final StepicWrappers.Step step, @NotNull String name) {
|
||||
final Task task = new Task();
|
||||
task.setName(name);
|
||||
task.setStepicId(lessonID);
|
||||
task.setText(step.text);
|
||||
if (step.options.samples != null) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (List<String> sample : step.options.samples) {
|
||||
if (sample.size() == 2) {
|
||||
builder.append("<b>Sample Input:</b><br>");
|
||||
builder.append(sample.get(0));
|
||||
builder.append("<br>");
|
||||
builder.append("<b>Sample Output:</b><br>");
|
||||
builder.append(sample.get(1));
|
||||
builder.append("<br><br>");
|
||||
}
|
||||
}
|
||||
task.setText(task.getText() + "<br>" + builder.toString());
|
||||
}
|
||||
if (step.options.test != null) {
|
||||
for (StepicWrappers.TestFileWrapper wrapper : step.options.test) {
|
||||
task.addTestsTexts(wrapper.name, wrapper.text);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (step.options.samples != null) {
|
||||
createTestFileFromSamples(project, task, step.options.samples);
|
||||
}
|
||||
}
|
||||
|
||||
task.taskFiles = new HashMap<String, TaskFile>(); // TODO: it looks like we don't need taskFiles as map anymore
|
||||
if (step.options.files != null) {
|
||||
@@ -218,37 +245,39 @@ public class EduAdaptiveStepicConnector {
|
||||
public static Pair<Boolean, String> checkTask(@NotNull final Project project, @NotNull final Task task) {
|
||||
try {
|
||||
final int attemptId = getAttemptId(project, task, ATTEMPTS_URL);
|
||||
final Editor editor = StudyUtils.getSelectedEditor(project);
|
||||
String language = getLanguageString(task, project);
|
||||
if (editor != null && language != null) {
|
||||
final StepicWrappers.SubmissionToPostWrapper submissionToPostWrapper =
|
||||
new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), "python3", editor.getDocument().getText());
|
||||
final HttpPost httpPost = new HttpPost(STEPIC_API_URL + SUBMISSION_URL);
|
||||
httpPost.setEntity(new StringEntity(new Gson().toJson(submissionToPostWrapper)));
|
||||
final CloseableHttpClient client = getHttpClient(project);
|
||||
setHeaders(httpPost, CONTENT_TYPE_APPL_JSON);
|
||||
final CloseableHttpResponse execute = client.execute(httpPost);
|
||||
StepicWrappers.ResultSubmissionWrapper wrapper =
|
||||
new Gson().fromJson(EntityUtils.toString(execute.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
|
||||
if (attemptId != -1) {
|
||||
final Editor editor = StudyUtils.getSelectedEditor(project);
|
||||
String language = getLanguageString(task, project);
|
||||
if (editor != null && language != null) {
|
||||
final StepicWrappers.SubmissionToPostWrapper submissionToPostWrapper =
|
||||
new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), language, editor.getDocument().getText());
|
||||
final HttpPost httpPost = new HttpPost(STEPIC_API_URL + SUBMISSION_URL);
|
||||
httpPost.setEntity(new StringEntity(new Gson().toJson(submissionToPostWrapper)));
|
||||
final CloseableHttpClient client = getHttpClient(project);
|
||||
setHeaders(httpPost, CONTENT_TYPE_APPL_JSON);
|
||||
final CloseableHttpResponse execute = client.execute(httpPost);
|
||||
StepicWrappers.ResultSubmissionWrapper wrapper =
|
||||
new Gson().fromJson(EntityUtils.toString(execute.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
|
||||
|
||||
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
|
||||
if (user != null) {
|
||||
final int id = user.getId();
|
||||
while (wrapper.submissions.length == 1 && wrapper.submissions[0].status.equals("evaluation")) {
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
final URI submissionURI = new URIBuilder(STEPIC_API_URL + SUBMISSION_URL)
|
||||
.addParameter("attempt", String.valueOf(attemptId))
|
||||
.addParameter("order", "desc")
|
||||
.addParameter("user", String.valueOf(id))
|
||||
.build();
|
||||
final HttpGet httpGet = new HttpGet(submissionURI);
|
||||
setHeaders(httpGet, CONTENT_TYPE_APPL_JSON);
|
||||
final CloseableHttpResponse httpResponse = client.execute(httpGet);
|
||||
wrapper = new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
|
||||
}
|
||||
if (wrapper.submissions.length == 1) {
|
||||
final boolean isSolved = !wrapper.submissions[0].status.equals("wrong");
|
||||
return Pair.create(isSolved, wrapper.submissions[0].hint);
|
||||
final StepicUser user = StudyTaskManager.getInstance(project).getUser();
|
||||
if (user != null) {
|
||||
final int id = user.getId();
|
||||
while (wrapper.submissions.length == 1 && wrapper.submissions[0].status.equals("evaluation")) {
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
final URI submissionURI = new URIBuilder(STEPIC_API_URL + SUBMISSION_URL)
|
||||
.addParameter("attempt", String.valueOf(attemptId))
|
||||
.addParameter("order", "desc")
|
||||
.addParameter("user", String.valueOf(id))
|
||||
.build();
|
||||
final HttpGet httpGet = new HttpGet(submissionURI);
|
||||
setHeaders(httpGet, CONTENT_TYPE_APPL_JSON);
|
||||
final CloseableHttpResponse httpResponse = client.execute(httpGet);
|
||||
wrapper = new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.ResultSubmissionWrapper.class);
|
||||
}
|
||||
if (wrapper.submissions.length == 1) {
|
||||
final boolean isSolved = !wrapper.submissions[0].status.equals("wrong");
|
||||
return Pair.create(isSolved, wrapper.submissions[0].hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,11 +301,12 @@ public class EduAdaptiveStepicConnector {
|
||||
private static String getLanguageString(@NotNull Task task, @NotNull Project project) {
|
||||
final Sdk sdk = StudyUtils.findSdk(task, project);
|
||||
if (sdk != null) {
|
||||
if (sdk.getVersionString() != null && sdk.getVersionString().startsWith("3")) {
|
||||
return "python3";
|
||||
}
|
||||
else {
|
||||
return "python2";
|
||||
final String versionString = sdk.getVersionString();
|
||||
if (versionString != null ) {
|
||||
final List<String> versionStringParts = StringUtil.split(versionString, " ");
|
||||
if (versionStringParts.size() == 2) {
|
||||
return versionStringParts.get(1).startsWith("2") ? PYTHON2 : PYTHON3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -296,6 +326,15 @@ public class EduAdaptiveStepicConnector {
|
||||
final CloseableHttpResponse httpResponse = client.execute(post);
|
||||
final StepicWrappers.AttemptContainer container =
|
||||
new Gson().fromJson(EntityUtils.toString(httpResponse.getEntity()), StepicWrappers.AttemptContainer.class);
|
||||
return container.attempts.get(0).id;
|
||||
return (container.attempts != null && !container.attempts.isEmpty()) ? container.attempts.get(0).id : -1;
|
||||
}
|
||||
|
||||
private static void createTestFileFromSamples(@NotNull final Project project,
|
||||
@NotNull final Task task,
|
||||
@NotNull final List<List<String>> samples) {
|
||||
String testText = "from test_helper import check_samples\n\n" +
|
||||
"if __name__ == '__main__':\n" +
|
||||
" check_samples(samples=" + new GsonBuilder().create().toJson(samples) + ")";
|
||||
task.addTestsTexts("tests.py", testText);
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -47,6 +47,8 @@ public class StepicWrappers {
|
||||
@Expose String title;
|
||||
@Expose List<TaskFile> files;
|
||||
@Expose String text;
|
||||
@Expose List<List<String>> samples;
|
||||
|
||||
|
||||
public static StepOptions fromTask(final Project project, @NotNull final Task task) {
|
||||
final StepOptions source = new StepOptions();
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
class StudyBrowserWindow extends JFrame {
|
||||
public class StudyBrowserWindow extends JFrame {
|
||||
private static final Logger LOG = Logger.getInstance(StudyToolWindow.class);
|
||||
private static final String EVENT_TYPE_CLICK = "click";
|
||||
private JFXPanel myPanel;
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.jetbrains.edu.learning.ui
|
||||
|
||||
import com.intellij.openapi.ui.SimpleToolWindowPanel
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JPanel
|
||||
|
||||
|
||||
class StudyTestResultsToolWindow: SimpleToolWindowPanel(false) {
|
||||
private val studyBrowserWindow = StudyBrowserWindow(false, false)
|
||||
fun init() {
|
||||
val panel = JPanel(BorderLayout())
|
||||
studyBrowserWindow.loadContent("", null)
|
||||
panel.add(studyBrowserWindow.panel, BorderLayout.CENTER)
|
||||
setContent(panel)
|
||||
}
|
||||
|
||||
fun setText(text: String) {
|
||||
studyBrowserWindow.loadContent(text, null)
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.jetbrains.edu.learning.ui
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.wm.ToolWindow
|
||||
|
||||
|
||||
class StudyTestResultsToolWindowFactory: StudyToolWindowFactory() {
|
||||
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
|
||||
val testResultsToolWindow = StudyTestResultsToolWindow()
|
||||
testResultsToolWindow.init()
|
||||
|
||||
val contentManager = toolWindow.contentManager
|
||||
val content = contentManager.factory.createContent(testResultsToolWindow, null, false)
|
||||
contentManager.addContent(content)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -219,5 +219,5 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void setTaskText(String text) ;
|
||||
public abstract void setTaskText(String text);
|
||||
}
|
||||
|
||||
+1
-2
@@ -7,9 +7,9 @@ import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.openapi.wm.ToolWindowFactory;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.ui.content.ContentManager;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.StudyProjectComponent;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import icons.InteractiveLearningIcons;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -23,7 +23,6 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
|
||||
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
|
||||
final Course course = taskManager.getCourse();
|
||||
if (course != null) {
|
||||
|
||||
final StudyToolWindow studyToolWindow;
|
||||
if (StudyProjectComponent.getInstance(project).useJavaFx()) {
|
||||
studyToolWindow = new StudyJavaFxToolWindow();
|
||||
|
||||
@@ -9,7 +9,7 @@ def get_file_text(path):
|
||||
return text
|
||||
|
||||
|
||||
def get_file_output(encoding="utf-8", path=sys.argv[-1]):
|
||||
def get_file_output(encoding="utf-8", path=sys.argv[-1], arg_string=""):
|
||||
"""
|
||||
Returns answer file output
|
||||
:param encoding: to decode output in python3
|
||||
@@ -18,7 +18,13 @@ def get_file_output(encoding="utf-8", path=sys.argv[-1]):
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
proc = subprocess.Popen([sys.executable, path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
proc = subprocess.Popen([sys.executable, path], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
if arg_string:
|
||||
for arg in arg_string.split("\n"):
|
||||
proc.stdin.write(bytearray(str(arg) + "\n", encoding))
|
||||
proc.stdin.flush()
|
||||
|
||||
return list(map(lambda x: str(x.decode(encoding)), proc.communicate()[0].splitlines()))
|
||||
|
||||
|
||||
@@ -31,7 +37,8 @@ def test_file_importable():
|
||||
parent = os.path.abspath(os.path.join(path, os.pardir))
|
||||
python_files = [f for f in os.listdir(parent) if os.path.isfile(os.path.join(parent, f)) and f.endswith(".py")]
|
||||
for python_file in python_files:
|
||||
if python_file == "tests.py": continue
|
||||
if python_file == "tests.py":
|
||||
continue
|
||||
check_importable_path(os.path.join(parent, python_file))
|
||||
return
|
||||
check_importable_path(path)
|
||||
@@ -187,7 +194,25 @@ def get_answer_placeholders():
|
||||
return windows
|
||||
|
||||
|
||||
def run_common_tests(error_text="Please, reload file and try again"):
|
||||
def check_samples(samples=()):
|
||||
"""
|
||||
Check script output for all samples. Sample is a two element list, where the first is input and
|
||||
the second is output.
|
||||
"""
|
||||
for sample in samples:
|
||||
if len(sample) == 2:
|
||||
output = get_file_output(arg_string=str(sample[0]))
|
||||
if "\n".join(output) != sample[1]:
|
||||
failed(
|
||||
"<b>Input</b>:<br> {} <br><br><b>Expected:</b><br> {} <br><br> <b>Your result</b>:<br> {}".format(
|
||||
sample[0].replace("\n", "<br>"), sample[1].replace("\n", "<br>"), "\n".join(output)))
|
||||
return
|
||||
set_congratulation_message("All test from samples passed. Now we are checking your solution on Stepic server.")
|
||||
|
||||
passed()
|
||||
|
||||
|
||||
def run_common_tests():
|
||||
test_is_initial_text()
|
||||
test_is_not_empty()
|
||||
test_answer_placeholders_text_deleted()
|
||||
|
||||
+30
-26
@@ -7,7 +7,6 @@ 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.ui.MessageType;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import com.jetbrains.edu.learning.actions.StudyCheckAction;
|
||||
@@ -15,10 +14,12 @@ import com.jetbrains.edu.learning.actions.StudyRunAction;
|
||||
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.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
|
||||
import com.jetbrains.edu.learning.courseFormat.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.editor.StudyEditor;
|
||||
import com.jetbrains.edu.learning.ui.StudyToolWindow;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -40,8 +41,8 @@ public class PyStudyCheckAction extends StudyCheckAction {
|
||||
}
|
||||
if (StudyCheckUtils.hasBackgroundProcesses(project)) return;
|
||||
|
||||
|
||||
if (!runTask(project)) return;
|
||||
final Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
if (course != null && !course.isAdaptive() && !runTask(project)) return;
|
||||
|
||||
final Task task = studyState.getTask();
|
||||
final VirtualFile taskDir = studyState.getTaskDir();
|
||||
@@ -90,34 +91,37 @@ public class PyStudyCheckAction extends StudyCheckAction {
|
||||
final Process testProcess,
|
||||
final String commandLine) {
|
||||
return new StudyCheckTask(project, studyState, myCheckInProgress, testProcess, commandLine) {
|
||||
@Override
|
||||
protected void onTaskFailed(String message) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (myTaskDir == null) return;
|
||||
myTaskManger.setStatus(myTask, StudyStatus.Failed);
|
||||
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
final TaskFile taskFile = entry.getValue();
|
||||
if (taskFile.getAnswerPlaceholders().size() < 2) {
|
||||
myTaskManger.setStatus(taskFile, StudyStatus.Failed);
|
||||
continue;
|
||||
}
|
||||
CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
|
||||
}));
|
||||
}
|
||||
StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), project);
|
||||
StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
|
||||
});
|
||||
@Override
|
||||
protected void onTaskFailed(String message) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (myTaskDir == null) return;
|
||||
myTaskManger.setStatus(myTask, StudyStatus.Failed);
|
||||
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
final TaskFile taskFile = entry.getValue();
|
||||
if (taskFile.getAnswerPlaceholders().size() < 2) {
|
||||
myTaskManger.setStatus(taskFile, StudyStatus.Failed);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
|
||||
}));
|
||||
}
|
||||
final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
|
||||
if (toolWindow != null) {
|
||||
StudyCheckUtils.showTestResults(project, message);
|
||||
StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState,
|
||||
@NotNull final Task task,
|
||||
@NotNull final VirtualFile taskDir) {
|
||||
@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();
|
||||
@@ -131,7 +135,7 @@ public class PyStudyCheckAction extends StudyCheckAction {
|
||||
}
|
||||
return taskVirtualFile;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getActionId() {
|
||||
|
||||
Reference in New Issue
Block a user