mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
push lesson to stepic
This commit is contained in:
@@ -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>
|
||||
+1
@@ -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) {
|
||||
|
||||
+6
-93
@@ -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();
|
||||
|
||||
+49
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
+1
-2
@@ -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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-51
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-14
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user