diff --git a/python/educational/course-creator/resources/META-INF/plugin.xml b/python/educational/course-creator/resources/META-INF/plugin.xml
index ed6c1ef33d23..0bc5838e6dca 100644
--- a/python/educational/course-creator/resources/META-INF/plugin.xml
+++ b/python/educational/course-creator/resources/META-INF/plugin.xml
@@ -83,6 +83,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java
index 42d3e08c9758..23ae0bed57a5 100644
--- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java
+++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java
@@ -173,6 +173,7 @@ public class CCProjectService implements PersistentStateComponent taskFileEntry) {
- final String name = taskFileEntry.getKey();
- final TaskFile taskFile = taskFileEntry.getValue();
- VirtualFile file = userFileDir.findChild(name);
- if (file != null) {
- try {
- file.delete(project);
- }
- catch (IOException e) {
- LOG.error(e);
- }
- }
- try {
- userFileDir.createChildData(project, name);
- }
- catch (IOException e) {
- LOG.error(e);
- }
-
- file = userFileDir.findChild(name);
- assert file != null;
- String answerFileName = file.getNameWithoutExtension() + ".answer." + file.getExtension();
- VirtualFile answerFile = answerFileDir.findChild(answerFileName);
- if (answerFile == null) {
- return;
- }
- final Document answerDocument = FileDocumentManager.getInstance().getDocument(answerFile);
- if (answerDocument == null) {
- return;
- }
- final Document document = FileDocumentManager.getInstance().getDocument(file);
- if (document == null) return;
-
- CommandProcessor.getInstance().executeCommand(project, new Runnable() {
- @Override
- public void run() {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- document.replaceString(0, document.getTextLength(), answerDocument.getCharsSequence());
- }
- });
- }
- }, "x", "qwe");
- EduDocumentListener listener = new EduDocumentListener(taskFile, false);
- document.addDocumentListener(listener);
- taskFile.sortAnswerPlaceholders();
- for (int i = taskFile.getAnswerPlaceholders().size() - 1; i >= 0; i--) {
- final AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholders().get(i);
- replaceAnswerPlaceholder(project, document, answerPlaceholder);
- }
- CommandProcessor.getInstance().executeCommand(project, new Runnable() {
- @Override
- public void run() {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- FileDocumentManager.getInstance().saveDocument(document);
- }
- });
- }
- }, "x", "qwe");
- document.removeDocumentListener(listener);
- }
-
- private static void replaceAnswerPlaceholder(@NotNull final Project project,
- @NotNull final Document document,
- @NotNull final AnswerPlaceholder answerPlaceholder) {
- final String taskText = answerPlaceholder.getTaskText();
- final int offset = answerPlaceholder.getRealStartOffset(document);
- CommandProcessor.getInstance().executeCommand(project, new Runnable() {
- @Override
- public void run() {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- document.replaceString(offset, offset + answerPlaceholder.getPossibleAnswerLength(), taskText);
- FileDocumentManager.getInstance().saveDocument(document);
- }
- });
- }
- }, "x", "qwe");
- }
-
private static void synchronize(@NotNull final Project project) {
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
ProjectView.getInstance(project).refresh();
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java
new file mode 100644
index 000000000000..c48d802150c8
--- /dev/null
+++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java
@@ -0,0 +1,49 @@
+package com.jetbrains.edu.coursecreator.actions;
+
+import com.intellij.ide.IdeView;
+import com.intellij.ide.util.DirectoryChooserUtil;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDirectory;
+import com.jetbrains.edu.courseFormat.Course;
+import com.jetbrains.edu.courseFormat.Lesson;
+import com.jetbrains.edu.coursecreator.CCProjectService;
+import com.jetbrains.edu.stepic.EduStepicConnector;
+import org.jetbrains.annotations.NotNull;
+
+public class CCPushLesson extends DumbAwareAction {
+ public CCPushLesson() {
+ super("Push lesson to Stepic", "Push lesson to Stepic", null);
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ CCProjectService.setCCActionAvailable(e);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ final IdeView view = e.getData(LangDataKeys.IDE_VIEW);
+ final Project project = e.getData(CommonDataKeys.PROJECT);
+ if (view == null || project == null) {
+ return;
+ }
+ final Course course = CCProjectService.getInstance(project).getCourse();
+ if (course == null) {
+ return;
+ }
+ PsiDirectory lessonDir = DirectoryChooserUtil.getOrChooseDirectory(view);;
+ if (lessonDir == null || !lessonDir.getName().contains("lesson")) {
+ return;
+ }
+ Lesson lesson = course.getLesson(lessonDir.getName());
+ if (lesson == null) {
+ return;
+ }
+ EduStepicConnector.postLesson(project, lesson);
+ }
+
+}
\ No newline at end of file
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java
index 1c69540c6945..f1d9d6cba063 100644
--- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java
+++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java
@@ -242,7 +242,7 @@ public abstract class CCRunTestsAction extends AnAction {
for (Map.Entry entry : task.getTaskFiles().entrySet()) {
TaskFile taskFileCopy = new TaskFile();
TaskFile.copy(entry.getValue(), taskFileCopy);
- CCCreateCourseArchive.createUserFile(project, taskResourceDir, taskDir,
+ EduUtils.createStudentFileFromAnswer(project, taskResourceDir, taskDir,
new AbstractMap.SimpleEntry(entry.getKey(), taskFileCopy));
}
}
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java
index f95938961a8b..8f24ed50a13c 100644
--- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java
+++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java
@@ -35,6 +35,7 @@ import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.intellij.ui.JBColor;
import com.jetbrains.edu.EduAnswerPlaceholderPainter;
+import com.jetbrains.edu.EduUtils;
import com.jetbrains.edu.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.courseFormat.Course;
import com.jetbrains.edu.courseFormat.TaskFile;
@@ -102,7 +103,7 @@ public class CCShowPreview extends DumbAwareAction {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
- CCCreateCourseArchive.createUserFile(project, taskDir.getVirtualFile(), taskDir.getVirtualFile(),
+ EduUtils.createStudentFileFromAnswer(project, taskDir.getVirtualFile(), taskDir.getVirtualFile(),
new AbstractMap.SimpleEntry(taskFileName, taskFileCopy));
}
});
diff --git a/python/educational/educational.iml b/python/educational/educational.iml
index 7e07d8259f41..4af7e0062118 100644
--- a/python/educational/educational.iml
+++ b/python/educational/educational.iml
@@ -12,5 +12,6 @@
+
\ No newline at end of file
diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyProjectComponent.java
index 0327f3d3663f..74a28b45de2b 100644
--- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyProjectComponent.java
+++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyProjectComponent.java
@@ -32,7 +32,6 @@ import com.jetbrains.edu.courseFormat.Lesson;
import com.jetbrains.edu.courseFormat.Task;
import com.jetbrains.edu.courseFormat.TaskFile;
import com.jetbrains.edu.learning.actions.*;
-import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener;
import com.jetbrains.edu.learning.ui.StudyCondition;
import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
@@ -303,7 +302,7 @@ public class StudyProjectComponent implements ProjectComponent {
if (StudyUtils.indexIsValid(taskIndex, tasks)) {
final Task task = tasks.get(taskIndex);
final TaskFile taskFile = new TaskFile();
- StudyGenerator.initTaskFile(taskFile, task, false);
+ EduUtils.initTaskFile(taskFile, task, false);
taskFile.setUserCreated(true);
task.getTaskFiles().put(createdFile.getName(), taskFile);
}
diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyTaskManager.java
index 549a6b7d2fff..7044951a9255 100644
--- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyTaskManager.java
+++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyTaskManager.java
@@ -12,10 +12,10 @@ import com.intellij.openapi.project.Project;
import com.intellij.ui.JBColor;
import com.intellij.util.containers.hash.HashMap;
import com.intellij.util.xmlb.XmlSerializerUtil;
+import com.jetbrains.edu.EduUtils;
import com.jetbrains.edu.courseFormat.*;
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.UserTest;
-import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -172,7 +172,7 @@ public class StudyTaskManager implements PersistentStateComponent taskList = lesson.getTaskList();
- for (Task task : taskList) {
- initTask(task, lesson, isRestarted);
- }
- }
-
- /**
- * Initializes state of task file
- *
- * @param lesson lesson which task belongs to
- */
- public static void initTask(@NotNull final Task task, final Lesson lesson, boolean isRestarted) {
- task.setLesson(lesson);
- for (TaskFile taskFile : task.getTaskFiles().values()) {
- initTaskFile(taskFile, task, isRestarted);
- }
- }
-
- public static void initTaskFile(@NotNull final TaskFile taskFile, final Task task, boolean isRestarted) {
- taskFile.setTask(task);
- final List answerPlaceholders = taskFile.getAnswerPlaceholders();
- for (AnswerPlaceholder answerPlaceholder : answerPlaceholders) {
- initAnswerPlaceholder(answerPlaceholder, taskFile, isRestarted);
- }
- Collections.sort(answerPlaceholders, new AnswerPlaceholderComparator());
- for (int i = 0; i < answerPlaceholders.size(); i++) {
- answerPlaceholders.get(i).setIndex(i);
- }
- }
-
- public static void initAnswerPlaceholder(@NotNull final AnswerPlaceholder placeholder, final TaskFile file, boolean isRestarted) {
- if (!isRestarted) {
- placeholder.setInitialState(new AnswerPlaceholder.MyInitialState(placeholder.getLine(), placeholder.getLength(),
- placeholder.getStart()));
- }
- placeholder.setTaskFile(file);
- }
-
}
diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java
index 3969c6977085..278c1d6d8fde 100644
--- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java
+++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java
@@ -20,11 +20,11 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.edu.EduNames;
+import com.jetbrains.edu.EduUtils;
import com.jetbrains.edu.courseFormat.Course;
import com.jetbrains.edu.courseFormat.Lesson;
import com.jetbrains.edu.courseFormat.Task;
import com.jetbrains.edu.courseFormat.TaskFile;
-import com.jetbrains.edu.learning.StudyLanguageManager;
import com.jetbrains.edu.learning.StudyProjectComponent;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
@@ -65,7 +65,7 @@ public class StudyProjectGenerator {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
- StudyGenerator.initCourse(course, false);
+ EduUtils.initCourse(course, false);
final File courseDirectory = new File(myCoursesDir, course.getName());
StudyGenerator.createCourse(course, baseDir, courseDirectory, project);
course.setCourseDirectory(new File(myCoursesDir, mySelectedCourseInfo.getName()).getAbsolutePath());
@@ -138,18 +138,16 @@ public class StudyProjectGenerator {
LOG.error("ERROR copying file " + name);
}
}
- StudyLanguageManager languageManager = StudyUtils.getLanguageManager(course);
- if (languageManager == null) {
- LOG.info("Language manager is null for " + course.getLanguageById().getDisplayName());
- return;
- }
- final File testsFile = new File(taskDirectory, languageManager.getTestFileName());
- FileUtil.createIfDoesntExist(testsFile);
- try {
- FileUtil.writeToFile(testsFile, task.getTestsText());
- }
- catch (IOException e) {
- LOG.error("ERROR copying tests file");
+ final Map testsText = task.getTestsText();
+ for (Map.Entry entry : testsText.entrySet()) {
+ final File testsFile = new File(taskDirectory, entry.getKey());
+ FileUtil.createIfDoesntExist(testsFile);
+ try {
+ FileUtil.writeToFile(testsFile, entry.getValue());
+ }
+ catch (IOException e) {
+ LOG.error("ERROR copying tests file");
+ }
}
final File taskText = new File(taskDirectory, "task.html");
FileUtil.createIfDoesntExist(taskText);
diff --git a/python/educational/src/com/jetbrains/edu/EduUtils.java b/python/educational/src/com/jetbrains/edu/EduUtils.java
index 184de8b94a4c..f1fc804c6dca 100644
--- a/python/educational/src/com/jetbrains/edu/EduUtils.java
+++ b/python/educational/src/com/jetbrains/edu/EduUtils.java
@@ -5,6 +5,7 @@ import com.intellij.ide.projectView.actions.MarkRootActionBase;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
@@ -17,8 +18,7 @@ import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
-import com.jetbrains.edu.courseFormat.AnswerPlaceholder;
-import com.jetbrains.edu.courseFormat.TaskFile;
+import com.jetbrains.edu.courseFormat.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -26,6 +26,9 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
public class EduUtils {
private EduUtils() {
@@ -133,4 +136,141 @@ public class EduUtils {
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
}
+ public static void createStudentFileFromAnswer(@NotNull final Project project,
+ @NotNull final VirtualFile userFileDir,
+ @NotNull final VirtualFile answerFileDir,
+ @NotNull final Map.Entry taskFileEntry) {
+ final String name = taskFileEntry.getKey();
+ final TaskFile taskFile = taskFileEntry.getValue();
+ VirtualFile file = userFileDir.findChild(name);
+ if (file != null) {
+ try {
+ file.delete(project);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ try {
+ userFileDir.createChildData(project, name);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+
+ file = userFileDir.findChild(name);
+ assert file != null;
+ String answerFileName = file.getNameWithoutExtension() + ".answer." + file.getExtension();
+ VirtualFile answerFile = answerFileDir.findChild(answerFileName);
+ if (answerFile == null) {
+ return;
+ }
+ final Document answerDocument = FileDocumentManager.getInstance().getDocument(answerFile);
+ if (answerDocument == null) {
+ return;
+ }
+ final Document document = FileDocumentManager.getInstance().getDocument(file);
+ if (document == null) return;
+
+ CommandProcessor.getInstance().executeCommand(project, new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ document.replaceString(0, document.getTextLength(), answerDocument.getCharsSequence());
+ }
+ });
+ }
+ }, "Create Student File", "Create Student File");
+ EduDocumentListener listener = new EduDocumentListener(taskFile, false);
+ document.addDocumentListener(listener);
+ taskFile.sortAnswerPlaceholders();
+ for (int i = taskFile.getAnswerPlaceholders().size() - 1; i >= 0; i--) {
+ final AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholders().get(i);
+ replaceAnswerPlaceholder(project, document, answerPlaceholder);
+ }
+ CommandProcessor.getInstance().executeCommand(project, new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ FileDocumentManager.getInstance().saveDocument(document);
+ }
+ });
+ }
+ }, "Create Student File", "Create Student File");
+ document.removeDocumentListener(listener);
+ }
+
+ private static void replaceAnswerPlaceholder(@NotNull final Project project,
+ @NotNull final Document document,
+ @NotNull final AnswerPlaceholder answerPlaceholder) {
+ final String taskText = answerPlaceholder.getTaskText();
+ final int offset = answerPlaceholder.getRealStartOffset(document);
+ CommandProcessor.getInstance().executeCommand(project, new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ document.replaceString(offset, offset + answerPlaceholder.getPossibleAnswerLength(), taskText);
+ FileDocumentManager.getInstance().saveDocument(document);
+ }
+ });
+ }
+ }, "Replace Answer Placeholders", "Replace Answer Placeholders");
+ }
+
+
+ /**
+ * Initializes state of course
+ */
+ public static void initCourse(@NotNull final Course course, boolean isRestarted) {
+ for (Lesson lesson : course.getLessons()) {
+ initLesson(lesson, course, isRestarted);
+ }
+ }
+
+ public static void initLesson(@NotNull final Lesson lesson, final Course course, boolean isRestarted) {
+ lesson.setCourse(course);
+ final List taskList = lesson.getTaskList();
+ for (Task task : taskList) {
+ initTask(task, lesson, isRestarted);
+ }
+ }
+
+ /**
+ * Initializes state of task file
+ *
+ * @param lesson lesson which task belongs to
+ */
+ public static void initTask(@NotNull final Task task, final Lesson lesson, boolean isRestarted) {
+ task.setLesson(lesson);
+ for (TaskFile taskFile : task.getTaskFiles().values()) {
+ initTaskFile(taskFile, task, isRestarted);
+ }
+ }
+
+ public static void initTaskFile(@NotNull final TaskFile taskFile, final Task task, boolean isRestarted) {
+ taskFile.setTask(task);
+ final List answerPlaceholders = taskFile.getAnswerPlaceholders();
+ for (AnswerPlaceholder answerPlaceholder : answerPlaceholders) {
+ initAnswerPlaceholder(answerPlaceholder, taskFile, isRestarted);
+ }
+ Collections.sort(answerPlaceholders, new AnswerPlaceholderComparator());
+ for (int i = 0; i < answerPlaceholders.size(); i++) {
+ answerPlaceholders.get(i).setIndex(i);
+ }
+ }
+
+ public static void initAnswerPlaceholder(@NotNull final AnswerPlaceholder placeholder, final TaskFile file, boolean isRestarted) {
+ if (!isRestarted) {
+ placeholder.setInitialState(new AnswerPlaceholder.MyInitialState(placeholder.getLine(), placeholder.getLength(),
+ placeholder.getStart()));
+ }
+ placeholder.setTaskFile(file);
+ }
+
}
diff --git a/python/educational/src/com/jetbrains/edu/courseFormat/Lesson.java b/python/educational/src/com/jetbrains/edu/courseFormat/Lesson.java
index e33ba3d397d5..f687fde80ca1 100644
--- a/python/educational/src/com/jetbrains/edu/courseFormat/Lesson.java
+++ b/python/educational/src/com/jetbrains/edu/courseFormat/Lesson.java
@@ -12,7 +12,7 @@ import java.util.List;
public class Lesson {
@Transient
- String id;
+ public int id;
@Transient
public List steps;
@Transient
@@ -20,6 +20,7 @@ public class Lesson {
@Transient
Boolean is_public;
@Expose
+ @SerializedName("title")
private String name;
@Expose
@SerializedName("task_list")
diff --git a/python/educational/src/com/jetbrains/edu/courseFormat/Task.java b/python/educational/src/com/jetbrains/edu/courseFormat/Task.java
index b398feb1e865..e381c6d61697 100644
--- a/python/educational/src/com/jetbrains/edu/courseFormat/Task.java
+++ b/python/educational/src/com/jetbrains/edu/courseFormat/Task.java
@@ -2,6 +2,8 @@ package com.jetbrains.edu.courseFormat;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.xmlb.annotations.Transient;
@@ -24,7 +26,7 @@ public class Task {
public Map taskFiles = new HashMap();
private String text;
- private String testsText;
+ private Map testsText = new HashMap();
@Transient private Lesson myLesson;
@@ -58,12 +60,12 @@ public class Task {
myIndex = index;
}
- public String getTestsText() {
+ public Map getTestsText() {
return testsText;
}
- public void setTestsText(@NotNull final String testsText) {
- this.testsText = testsText;
+ public void setTestsTexts(String name, String text) {
+ testsText.put(name, text);
}
public Map getTaskFiles() {
@@ -101,9 +103,9 @@ public class Task {
}
@Nullable
- public VirtualFile getTaskDir(Project project) {
- String lessonDirName = EduNames.LESSON + String.valueOf(myLesson.getIndex() + 1);
- String taskDirName = EduNames.TASK + String.valueOf(myIndex + 1);
+ public VirtualFile getTaskDir(@NotNull final Project project) {
+ String lessonDirName = EduNames.LESSON + String.valueOf(myLesson.getIndex());
+ String taskDirName = EduNames.TASK + String.valueOf(myIndex);
VirtualFile courseDir = project.getBaseDir();
if (courseDir != null) {
VirtualFile lessonDir = courseDir.findChild(lessonDirName);
@@ -114,6 +116,44 @@ public class Task {
return null;
}
+ @Nullable
+ public String getTaskText(@NotNull final Project project) {
+ final VirtualFile taskDir = getTaskDir(project);
+ if (taskDir != null) {
+ final VirtualFile file = taskDir.findChild(EduNames.TASK_HTML);
+ if (file == null) return null;
+ final Document document = FileDocumentManager.getInstance().getDocument(file);
+ if (document != null) {
+ return document.getImmutableCharSequence().toString();
+ }
+ }
+
+ return null;
+ }
+
+ @Nullable
+ public String getTestsText(@NotNull final Project project) {
+ final VirtualFile taskDir = getTaskDir(project);
+ if (taskDir != null) {
+ final VirtualFile file = taskDir.findChild("tests.py");
+ if (file == null) return null;
+ final Document document = FileDocumentManager.getInstance().getDocument(file);
+ if (document != null) {
+ return document.getImmutableCharSequence().toString();
+ }
+ }
+
+ return null;
+ }
+
+ public Document getDocument(Project project, String name) {
+ final VirtualFile taskDirectory = getTaskDir(project);
+ if (taskDirectory == null) return null;
+ final VirtualFile file = taskDirectory.findChild(name);
+ if (file == null) return null;
+ return FileDocumentManager.getInstance().getDocument(file);
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/python/educational/src/com/jetbrains/edu/courseFormat/TaskFile.java b/python/educational/src/com/jetbrains/edu/courseFormat/TaskFile.java
index 9ad9d96ab78e..1e6fa65bf5ab 100644
--- a/python/educational/src/com/jetbrains/edu/courseFormat/TaskFile.java
+++ b/python/educational/src/com/jetbrains/edu/courseFormat/TaskFile.java
@@ -18,12 +18,14 @@ import java.util.List;
*/
public class TaskFile {
- @SerializedName("task_windows")
+ @SerializedName("placeholders")
@Expose
private List myAnswerPlaceholders = new ArrayList();
private int myIndex = -1;
+ @Expose
public String name;
+ @Expose
public String text;
@Transient private Task myTask;
@Transient
diff --git a/python/educational/src/com/jetbrains/edu/stepic/EduStepicConnector.java b/python/educational/src/com/jetbrains/edu/stepic/EduStepicConnector.java
index c3e781e876d8..e91c633834eb 100644
--- a/python/educational/src/com/jetbrains/edu/stepic/EduStepicConnector.java
+++ b/python/educational/src/com/jetbrains/edu/stepic/EduStepicConnector.java
@@ -3,31 +3,52 @@ package com.jetbrains.edu.stepic;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.Expose;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.io.HttpRequests;
+import com.intellij.util.net.ssl.CertificateManager;
+import com.jetbrains.edu.EduUtils;
import com.jetbrains.edu.courseFormat.Course;
import com.jetbrains.edu.courseFormat.Lesson;
import com.jetbrains.edu.courseFormat.Task;
import com.jetbrains.edu.courseFormat.TaskFile;
+import org.apache.http.Header;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
public class EduStepicConnector {
private static final String stepicApiUrl = "https://stepic.org/api/";
private static final Logger LOG = Logger.getInstance(EduStepicConnector.class.getName());
+ private static final String ourDomain = "stepic.org";
+ private static String ourSessionId = "524iethiwju2tjywaqmf7tbwx0p0jk1b";
+ private static String ourCSRFToken = "LJ9n6OyLVA7hxU94dlYWUu65MF51Nx37";
- private EduStepicConnector() {}
+ private EduStepicConnector() {
+ }
@NotNull
public static List getCourses() {
try {
- return HttpRequests.request(stepicApiUrl + "courses/93").connect(new HttpRequests.RequestProcessor>() {
+ return HttpRequests.request(stepicApiUrl + "courses/99").connect(new HttpRequests.RequestProcessor>() {
@Override
public List process(@NotNull HttpRequests.Request request) throws IOException {
@@ -79,16 +100,16 @@ public class EduStepicConnector {
public static List getLessons(int sectionId) throws IOException {
final List lessons =
- HttpRequests.request(stepicApiUrl + "sections/" + String.valueOf(sectionId))
- .connect(new HttpRequests.RequestProcessor>() {
+ HttpRequests.request(stepicApiUrl + "sections/" + String.valueOf(sectionId))
+ .connect(new HttpRequests.RequestProcessor>() {
- @Override
- public List process(@NotNull HttpRequests.Request request) throws IOException {
- final BufferedReader reader = request.getReader();
- Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
- return gson.fromJson(reader, Section.class).lessons;
- }
- });
+ @Override
+ public List process(@NotNull HttpRequests.Request request) throws IOException {
+ final BufferedReader reader = request.getReader();
+ Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+ return gson.fromJson(reader, Course.class).getLessons();
+ }
+ });
for (Lesson lesson : lessons) {
lesson.taskList = new ArrayList();
for (Integer s : lesson.steps) {
@@ -96,7 +117,9 @@ public class EduStepicConnector {
final Task task = new Task();
task.setName(step.name);
task.setText(step.text);
- task.setTestsText(step.options.test);
+ for (TestFileWrapper wrapper : step.options.test) {
+ task.setTestsTexts(wrapper.name, wrapper.text);
+ }
task.taskFiles = new HashMap(); // TODO: it looks like we don't need taskFiles as map anymore
if (step.options.files != null) {
@@ -120,34 +143,181 @@ public class EduStepicConnector {
return gson.fromJson(reader, StepContainer.class).steps.get(0).block;
}
});
-
}
- private static class Section {
- public List lessons;
+
+
+ public static boolean postLesson(Project project, @NotNull final Lesson lesson) {
+ final HttpPost request = new HttpPost(stepicApiUrl + "lessons");
+ final ArrayList headers = getHeaders(request);
+ HttpClientBuilder builder = HttpClients.custom().setSslcontext(CertificateManager.getInstance().getSslContext());
+ final BasicCookieStore cookieStore = getCookies();
+ final CloseableHttpClient client = builder.setDefaultHeaders(headers).setDefaultCookieStore(cookieStore).build();
+
+ String requestBody = new Gson().toJson(new LessonWrapper(lesson));
+ request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
+
+ try {
+ final CloseableHttpResponse response = client.execute(request);
+ final String responseString = EntityUtils.toString(response.getEntity());
+ final StatusLine line = response.getStatusLine();
+ if (line.getStatusCode() != 201) {
+ LOG.error("Failed to push " + EntityUtils.toString(response.getEntity()));
+ return false;
+ }
+ final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
+ for (Task task : lesson.getTaskList()) {
+ postTask(project, task, postedLesson.id);
+ }
+ }
+ catch (IOException e) {
+ LOG.error(e.getMessage());
+ }
+ return false;
+ }
+
+ public static boolean postTask(Project project, @NotNull final Task task, int id) {
+ final HttpPost request = new HttpPost(stepicApiUrl + "step-sources");
+ final ArrayList headers = getHeaders(request);
+ HttpClientBuilder builder = HttpClients.custom().setSslcontext(CertificateManager.getInstance().getSslContext());
+ final BasicCookieStore cookieStore = getCookies();
+ final CloseableHttpClient client = builder.setDefaultHeaders(headers).setDefaultCookieStore(cookieStore).build();
+ final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
+ String requestBody = gson.toJson(new StepSourceWrapper(project, task, id));
+ request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
+
+ try {
+ final CloseableHttpResponse response = client.execute(request);
+ final StatusLine line = response.getStatusLine();
+ if (line.getStatusCode() != 201) {
+ LOG.error("Failed to push " + EntityUtils.toString(response.getEntity()));
+ }
+ return line.getStatusCode() == 201;
+ }
+ catch (IOException e) {
+ LOG.error(e.getMessage());
+ }
+
+ return false;
+ }
+
+ private static BasicCookieStore getCookies() {
+ final BasicCookieStore cookieStore = new BasicCookieStore();
+ final BasicClientCookie sessionid = new BasicClientCookie("sessionid", ourSessionId);
+ sessionid.setDomain(ourDomain);
+ sessionid.setPath("/");
+
+ cookieStore.addCookie(sessionid);
+ final BasicClientCookie csrfToken = new BasicClientCookie("csrftoken", ourCSRFToken);
+ csrfToken.setDomain(ourDomain);
+ csrfToken.setPath("/");
+
+ cookieStore.addCookie(csrfToken);
+ return cookieStore;
+ }
+
+ private static ArrayList getHeaders(HttpPost request) {
+ final ArrayList headers = new ArrayList();
+ headers.add(new BasicHeader("referer", "https://stepic.org"));
+ headers.add(new BasicHeader("X-CSRFToken", ourCSRFToken));
+ headers.add(new BasicHeader("content-type", "application/json"));
+ request.setHeaders(headers.toArray(new Header[headers.size()]));
+ return headers;
}
private static class StepContainer {
- List steps;
+ List steps;
}
private static class Step {
- StepOptions options;
- String text;
- String name;
+ @Expose StepOptions options;
+ @Expose String text;
+ @Expose String name = "pycharm";
+ @Expose StepOptions source;
+
+ public static Step fromTask(Project project, @NotNull final Task task) {
+ final Step step = new Step();
+ step.text = task.getTaskText(project);
+ step.source = StepOptions.fromTask(project, task);
+ return step;
+ }
}
private static class StepOptions {
- String test;
- List files;
- }
+ @Expose List test;
+ @Expose String title; //HERE
+ @Expose List files;
+ @Expose String text;
- private static class StepRaw {
- Step block;
+ public static StepOptions fromTask(final Project project, @NotNull final Task task) {
+ final StepOptions source = new StepOptions();
+
+ final String text = task.getTestsText(project);
+ source.test = Collections.singletonList(new TestFileWrapper("tests.py", text));
+ source.files = new ArrayList();
+ source.title = task.getName();
+ for (final Map.Entry entry : task.getTaskFiles().entrySet()) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ final VirtualFile taskDir = task.getTaskDir(project);
+ EduUtils.createStudentFileFromAnswer(project, taskDir, taskDir, entry);
+ }
+ });
+ final TaskFile taskFile = entry.getValue();
+ taskFile.name = entry.getKey();
+ final Document document = task.getDocument(project, taskFile.name);
+ if (document != null) {
+ source.text = document.getImmutableCharSequence().toString();
+ taskFile.text = document.getImmutableCharSequence().toString();
+ }
+ source.files.add(taskFile);
+ }
+ return source;
+ }
}
private static class CoursesContainer {
public List courses;
}
+ static class StepSourceWrapper {
+ @Expose
+ StepSource stepSource;
+
+ public StepSourceWrapper(Project project, Task task, int id) {
+ stepSource = new StepSource(project, task, id);
+ }
+ }
+
+ static class LessonWrapper {
+ Lesson lesson;
+
+ public LessonWrapper(Lesson lesson) {
+ this.lesson = lesson;
+ }
+ }
+
+ static class StepSource {
+ @Expose Step block;
+ @Expose int position = 0;
+ @Expose int lesson = 0;
+
+ public StepSource(Project project, Task task, int id) {
+ lesson = id;
+ position = task.getIndex();
+ block = Step.fromTask(project, task);
+ }
+ }
+
+ static class TestFileWrapper {
+ @Expose private final String name;
+ @Expose private final String text;
+
+ public TestFileWrapper(String name, String text) {
+ this.name = name;
+ this.text = text;
+ }
+ }
+
}