push lesson to stepic

This commit is contained in:
Ekaterina Tuzova
2015-02-16 21:14:44 +03:00
parent 374a3024dd
commit 28de7cdb8a
16 changed files with 476 additions and 203 deletions
@@ -83,6 +83,11 @@
<add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</action>
<action id="PushLesson" class="com.jetbrains.edu.coursecreator.actions.CCPushLesson">
<add-to-group group-id="MainToolBar" anchor="last" />
<add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</action>
</actions>
</idea-plugin>
@@ -173,6 +173,7 @@ public class CCProjectService implements PersistentStateComponent<CCProjectServi
@Override
public void loadState(CCProjectService state) {
XmlSerializerUtil.copyBean(state, this);
EduUtils.initCourse(myCourse, true);
}
public static CCProjectService getInstance(@NotNull Project project) {
@@ -7,10 +7,7 @@ import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
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;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
@@ -19,9 +16,12 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.io.ZipUtil;
import com.jetbrains.edu.EduDocumentListener;
import com.jetbrains.edu.EduNames;
import com.jetbrains.edu.courseFormat.*;
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.coursecreator.CCLanguageManager;
import com.jetbrains.edu.coursecreator.CCProjectService;
import com.jetbrains.edu.coursecreator.CCUtils;
@@ -86,7 +86,7 @@ public class CCCreateCourseArchive extends DumbAwareAction {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
createUserFile(project, taskDir, taskDir, entry);
EduUtils.createStudentFileFromAnswer(project, taskDir, taskDir, entry);
}
});
}
@@ -97,93 +97,6 @@ public class CCCreateCourseArchive extends DumbAwareAction {
synchronize(project);
}
public static void createUserFile(@NotNull final Project project,
@NotNull final VirtualFile userFileDir,
@NotNull final VirtualFile answerFileDir,
@NotNull final Map.Entry<String, TaskFile> 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();
@@ -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);
}
}
@@ -242,7 +242,7 @@ public abstract class CCRunTestsAction extends AnAction {
for (Map.Entry<String, TaskFile> 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<String, TaskFile>(entry.getKey(), taskFileCopy));
}
}
@@ -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<String, TaskFile>(taskFileName, taskFileCopy));
}
});
+1
View File
@@ -12,5 +12,6 @@
<orderEntry type="module" module-name="core-impl" />
<orderEntry type="module" module-name="platform-impl" />
<orderEntry type="module" module-name="lang-impl" />
<orderEntry type="library" name="http-client" level="project" />
</component>
</module>
@@ -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);
}
@@ -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<StudyTaskManag
public void loadState(StudyTaskManager state) {
XmlSerializerUtil.copyBean(state, this);
if (myCourse != null) {
StudyGenerator.initCourse(myCourse, true);
EduUtils.initCourse(myCourse, true);
}
}
@@ -6,13 +6,15 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.EduNames;
import com.jetbrains.edu.EduUtils;
import com.jetbrains.edu.courseFormat.*;
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.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -124,53 +126,4 @@ public class StudyGenerator {
}
}
/**
* 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<Task> 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<AnswerPlaceholder> 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);
}
}
@@ -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<String, String> testsText = task.getTestsText();
for (Map.Entry<String, String> 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);
@@ -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<String, TaskFile> 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<Task> 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<AnswerPlaceholder> 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);
}
}
@@ -12,7 +12,7 @@ import java.util.List;
public class Lesson {
@Transient
String id;
public int id;
@Transient
public List<Integer> steps;
@Transient
@@ -20,6 +20,7 @@ public class Lesson {
@Transient
Boolean is_public;
@Expose
@SerializedName("title")
private String name;
@Expose
@SerializedName("task_list")
@@ -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<String, TaskFile> taskFiles = new HashMap<String, TaskFile>();
private String text;
private String testsText;
private Map<String, String> testsText = new HashMap<String, String>();
@Transient private Lesson myLesson;
@@ -58,12 +60,12 @@ public class Task {
myIndex = index;
}
public String getTestsText() {
public Map<String, String> 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<String, TaskFile> 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;
@@ -18,12 +18,14 @@ import java.util.List;
*/
public class TaskFile {
@SerializedName("task_windows")
@SerializedName("placeholders")
@Expose
private List<AnswerPlaceholder> myAnswerPlaceholders = new ArrayList<AnswerPlaceholder>();
private int myIndex = -1;
@Expose
public String name;
@Expose
public String text;
@Transient private Task myTask;
@Transient
@@ -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<CourseInfo> getCourses() {
try {
return HttpRequests.request(stepicApiUrl + "courses/93").connect(new HttpRequests.RequestProcessor<List<CourseInfo>>() {
return HttpRequests.request(stepicApiUrl + "courses/99").connect(new HttpRequests.RequestProcessor<List<CourseInfo>>() {
@Override
public List<CourseInfo> process(@NotNull HttpRequests.Request request) throws IOException {
@@ -79,16 +100,16 @@ public class EduStepicConnector {
public static List<Lesson> getLessons(int sectionId) throws IOException {
final List<Lesson> lessons =
HttpRequests.request(stepicApiUrl + "sections/" + String.valueOf(sectionId))
.connect(new HttpRequests.RequestProcessor<List<Lesson>>() {
HttpRequests.request(stepicApiUrl + "sections/" + String.valueOf(sectionId))
.connect(new HttpRequests.RequestProcessor<List<Lesson>>() {
@Override
public List<Lesson> 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<Lesson> 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<Task>();
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<String, TaskFile>(); // 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<Lesson> lessons;
public static boolean postLesson(Project project, @NotNull final Lesson lesson) {
final HttpPost request = new HttpPost(stepicApiUrl + "lessons");
final ArrayList<Header> 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<Header> 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<Header> getHeaders(HttpPost request) {
final ArrayList<Header> headers = new ArrayList<Header>();
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<StepRaw> steps;
List<StepSource> 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<TaskFile> files;
}
@Expose List<TestFileWrapper> test;
@Expose String title; //HERE
@Expose List<TaskFile> 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<TaskFile>();
source.title = task.getName();
for (final Map.Entry<String, TaskFile> 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<CourseInfo> 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;
}
}
}