diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml
index d1523e23db1d..941567b8c6ee 100644
--- a/python/educational-core/resources/META-INF/plugin.xml
+++ b/python/educational-core/resources/META-INF/plugin.xml
@@ -198,7 +198,6 @@
-
\ No newline at end of file
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCCheckListener.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCCheckListener.java
deleted file mode 100644
index 12aa92541496..000000000000
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCCheckListener.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.jetbrains.edu.coursecreator;
-
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.edu.learning.StudyCheckListener;
-import com.jetbrains.edu.learning.courseFormat.tasks.Task;
-import org.jetbrains.annotations.NotNull;
-
-public class CCCheckListener implements StudyCheckListener {
-
- @Override
- public void beforeCheck(@NotNull Project project, @NotNull Task task) {
- if (!CCUtils.isCourseCreator(project)) {
- return;
- }
- VirtualFile taskDir = task.getTaskDir(project);
- if (taskDir == null) {
- return;
- }
- CCUtils.updateResources(project, task, taskDir);
- }
-}
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java
index f5c081335c40..61d0323f968c 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java
@@ -1,7 +1,6 @@
package com.jetbrains.edu.coursecreator;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -15,12 +14,11 @@ import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Lesson;
-import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
import org.jetbrains.annotations.NotNull;
-import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -47,9 +45,6 @@ public class CCProjectComponent extends AbstractProjectComponent {
CCProjectService.getInstance(myProject).setCourse(null);
oldCourse.initCourse(true);
oldCourse.setCourseMode(CCUtils.COURSE_MODE);
- File coursesDir = new File(PathManager.getConfigPath(), "courses");
- File courseDir = new File(coursesDir, oldCourse.getName() + "-" + myProject.getName());
- oldCourse.setCourseDirectory(courseDir.getPath());
StudyUtils.registerStudyToolWindow(oldCourse, myProject);
transformFiles(oldCourse, myProject);
}
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java
index b9d46aa47d06..a917b185b31c 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java
@@ -15,10 +15,7 @@
*/
package com.jetbrains.edu.coursecreator;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -30,16 +27,14 @@ import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Lesson;
-import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.IOException;
import java.util.Map;
public class CCRefactoringElementListenerProvider implements RefactoringElementListenerProvider {
- private static final Logger LOG = Logger.getInstance(CCRefactoringElementListenerProvider.class);
@Nullable
@Override
@@ -94,21 +89,8 @@ public class CCRefactoringElementListenerProvider implements RefactoringElementL
if (taskFile == null) {
return;
}
- ApplicationManager.getApplication().runWriteAction(() -> {
- VirtualFile patternFile = StudyUtils.getPatternFile(taskFile, oldName);
- if (patternFile != null) {
- try {
- patternFile.delete(CCRefactoringElementListenerProvider.class);
- }
- catch (IOException e) {
- LOG.info(e);
- }
- }
- });
-
taskFiles.remove(oldName);
taskFiles.put(StudyUtils.pathRelativeToTask(file.getVirtualFile()), taskFile);
- CCUtils.createResourceFile(file.getVirtualFile(), course, taskDir.getVirtualFile());
}
@Override
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java
index c12b680a292b..7ec7d188972b 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java
@@ -8,34 +8,37 @@ import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
-import com.intellij.openapi.project.DumbModePermission;
-import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
-import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.PsiDirectory;
import com.intellij.util.Function;
+import com.intellij.util.containers.hash.HashMap;
import com.jetbrains.edu.learning.EduPluginConfigurator;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
+import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.StudyItem;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
-import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
+import org.apache.commons.codec.binary.Base64;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.File;
import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
public class CCUtils {
public static final String ANSWER_EXTENSION_DOTTED = ".answer.";
@@ -137,27 +140,22 @@ public class CCUtils {
return folder;
}
final Ref generatedRoot = new Ref<>();
- DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- try {
- generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER));
- VirtualFile contentRootForFile =
- ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get());
- if (contentRootForFile == null) {
- return;
- }
- ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(),
- Collections.singletonList(generatedRoot.get().getUrl()));
- }
- catch (IOException e) {
- LOG.info("Failed to create folder for generated files", e);
- }
+ try {
+ generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER));
+ VirtualFile contentRootForFile =
+ ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get());
+ if (contentRootForFile == null) {
+ return;
}
- });
+ ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(),
+ Collections.singletonList(generatedRoot.get().getUrl()));
+ }
+ catch (IOException e) {
+ LOG.info("Failed to create folder for generated files", e);
+ }
}
});
return generatedRoot.get();
@@ -211,63 +209,6 @@ public class CCUtils {
return configurator.isTestFile(file);
}
- public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) {
- VirtualFile lessonVF = taskVF.getParent();
- if (lessonVF == null) {
- return;
- }
-
- String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName());
- File taskResourceFile = new File(taskResourcesPath);
- if (!taskResourceFile.exists()) {
- if (!taskResourceFile.mkdirs()) {
- LOG.info("Failed to create resources for task " + taskResourcesPath);
- }
- }
- try {
- File toFile = new File(taskResourceFile, createdFile.getName());
- FileUtil.copy(new File(createdFile.getPath()), toFile);
- }
- catch (IOException e) {
- LOG.info("Failed to copy created task file to resources " + createdFile.getPath());
- }
- }
-
-
- public static void updateResources(Project project, Task task, VirtualFile taskDir) {
- Course course = StudyTaskManager.getInstance(project).getCourse();
- if (course == null) {
- return;
- }
- VirtualFile lessonVF = taskDir.getParent();
- if (lessonVF == null) {
- return;
- }
-
- String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskDir.getName());
- File taskResourceFile = new File(taskResourcesPath);
- if (!taskResourceFile.exists()) {
- if (!taskResourceFile.mkdirs()) {
- LOG.info("Failed to create resources for task " + taskResourcesPath);
- }
- }
- VirtualFile studentDir = LocalFileSystem.getInstance().findFileByIoFile(taskResourceFile);
- if (studentDir == null) {
- return;
- }
- for (Map.Entry entry : task.getTaskFiles().entrySet()) {
- String name = entry.getKey();
- VirtualFile answerFile = taskDir.findFileByRelativePath(name);
- if (answerFile == null) {
- continue;
- }
- ApplicationManager.getApplication().runWriteAction(() -> {
- EduUtils.createStudentFile(CCUtils.class, project, answerFile, studentDir, null,
- task instanceof TaskWithSubtasks ? ((TaskWithSubtasks)task).getActiveSubtaskIndex() : 0);
- });
- }
- }
-
public static void updateActionGroup(AnActionEvent e) {
Presentation presentation = e.getPresentation();
Project project = e.getProject();
@@ -315,4 +256,58 @@ public class CCUtils {
}
});
}
+
+ @Nullable
+ public static Lesson createAdditionalLesson(Course course, Project project) {
+ final VirtualFile baseDir = project.getBaseDir();
+ EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById());
+
+ final Lesson lesson = new Lesson();
+ lesson.setName(EduNames.PYCHARM_ADDITIONAL);
+ final Task task = new Task();
+ task.setLesson(lesson);
+ task.setName(EduNames.PYCHARM_ADDITIONAL);
+ task.setIndex(1);
+
+ VfsUtilCore.visitChildrenRecursively(baseDir, new VirtualFileVisitor(VirtualFileVisitor.NO_FOLLOW_SYMLINKS) {
+ @Override
+ public boolean visitFile(@NotNull VirtualFile file) {
+ final String name = file.getName();
+ if (name.equals(EduNames.COURSE_META_FILE) || name.equals(EduNames.HINTS) || name.startsWith(".")) return false;
+ String sanitizedName = FileUtil.sanitizeFileName(course.getName());
+ final String archiveName = sanitizedName.startsWith("_") ? EduNames.COURSE : sanitizedName;
+ if (name.equals(archiveName + ".zip")) return false;
+ if (GENERATED_FILES_FOLDER.equals(name) || Project.DIRECTORY_STORE_FOLDER.equals(name)) {
+ return false;
+ }
+ if (file.isDirectory()) return true;
+
+ if (StudyUtils.isTaskDescriptionFile(name) || StudyUtils.isTestsFile(project, name)) return true;
+
+ if (name.contains(".iml") || (configurator != null && configurator.excludeFromArchive(name))) {
+ return false;
+ }
+ final TaskFile taskFile = StudyUtils.getTaskFile(project, file);
+ if (taskFile == null) {
+ final String path = VfsUtilCore.getRelativePath(file, baseDir);
+ try {
+ if (EduUtils.isImage(file.getName())) {
+ task.addTestsTexts(path, Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream())));
+ }
+ else {
+ task.addTestsTexts(path, FileUtil.loadTextAndClose(file.getInputStream()));
+ }
+ }
+ catch (IOException e) {
+ LOG.error("Can't find file " + path);
+ }
+ }
+ return true;
+ }
+ });
+ if (task.getTestsText().isEmpty()) return null;
+ lesson.addTask(task);
+ lesson.setIndex(1);
+ return lesson;
+ }
}
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java
index a45193c712ca..52b81d8f222c 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java
@@ -48,7 +48,7 @@ public class CCVirtualFileListener implements VirtualFileListener {
String taskRelativePath = StudyUtils.pathRelativeToTask(createdFile);
EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById());
- if (configurator != null && configurator.excludeFromArchive(new File(createdFile.getPath()))) {
+ if (configurator != null && configurator.excludeFromArchive(createdFile.getName())) {
return;
}
@@ -68,8 +68,6 @@ public class CCVirtualFileListener implements VirtualFileListener {
return;
}
- CCUtils.createResourceFile(createdFile, course, taskVF);
-
task.addTaskFile(taskRelativePath, 1);
}
@@ -89,7 +87,7 @@ public class CCVirtualFileListener implements VirtualFileListener {
return;
}
Course course = StudyTaskManager.getInstance(project).getCourse();
- if (course == null || path.contains(FileUtil.toSystemIndependentName(course.getCourseDirectory()))) {
+ if (course == null) {
return;
}
final TaskFile taskFile = StudyUtils.getTaskFile(project, removedFile);
@@ -112,7 +110,7 @@ public class CCVirtualFileListener implements VirtualFileListener {
}
VirtualFile courseDir = project.getBaseDir();
CCUtils.updateHigherElements(courseDir.getChildren(), file -> course.getLesson(file.getName()), removedLesson.getIndex(), EduNames.LESSON, -1);
- course.getLessons().remove(removedLesson);
+ course.removeLesson(removedLesson);
}
private static void deleteTask(@NotNull final Course course, @NotNull final VirtualFile removedTask) {
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java
index a1bf760c5a6a..9b9904b0f883 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java
@@ -9,8 +9,8 @@ import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
-import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.courseFormat.tasks.Task;
public class CCAddAsTaskFile extends CCTaskFileActionBase {
public static final String ACTION_NAME = "Make Visible to Student";
@@ -21,7 +21,7 @@ public class CCAddAsTaskFile extends CCTaskFileActionBase {
protected void performAction(VirtualFile file, Task task, Course course, Project project) {
- EduUtils.runUndoableAction(project, ACTION_NAME, new AddTaskFile(file, null, course, project, task));
+ EduUtils.runUndoableAction(project, ACTION_NAME, new AddTaskFile(file, null, project, task));
}
protected boolean isAvailable(Project project, VirtualFile file) {
@@ -31,21 +31,20 @@ public class CCAddAsTaskFile extends CCTaskFileActionBase {
private static class AddTaskFile extends BasicUndoableAction {
private final VirtualFile myFile;
private TaskFile myTaskFile;
- private final Course myCourse;
private final Project myProject;
private final Task myTask;
- public AddTaskFile(VirtualFile file, TaskFile taskFile, Course course, Project project, Task task) {
+ public AddTaskFile(VirtualFile file, TaskFile taskFile, Project project, Task task) {
super(file);
myFile = file;
myTaskFile = taskFile;
- myCourse = course;
myProject = project;
myTask = task;
}
@Override
public void undo() throws UnexpectedUndoException {
+ if (myTaskFile == null) return;
CCHideFromStudent.hideFromStudent(myFile, myProject, myTask.getTaskFiles(), myTaskFile);
ProjectView.getInstance(myProject).refresh();
}
@@ -59,7 +58,6 @@ public class CCAddAsTaskFile extends CCTaskFileActionBase {
myTask.addTaskFile(taskRelativePath, myTask.getTaskFiles().size());
myTaskFile = myTask.getTaskFile(taskRelativePath);
}
- CCUtils.createResourceFile(myFile, myCourse, StudyUtils.getTaskDir(myFile));
ProjectView.getInstance(myProject).refresh();
}
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java
index a199f2d0ebc1..8703e3612117 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java
@@ -14,30 +14,31 @@ import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.Condition;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
-import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.hash.HashMap;
import com.intellij.util.io.ZipUtil;
import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.coursecreator.ui.CreateCourseArchiveDialog;
import com.jetbrains.edu.learning.StudySerializationUtils;
-import com.jetbrains.edu.learning.EduPluginConfigurator;
import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Lesson;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
-import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
import org.jetbrains.annotations.NotNull;
import java.io.*;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;
public class CCCreateCourseArchive extends DumbAwareAction {
@@ -90,34 +91,26 @@ public class CCCreateCourseArchive extends DumbAwareAction {
return;
}
- EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById());
- if (configurator == null) {
- return;
- }
- FileFilter filter = pathname -> !configurator.excludeFromArchive(pathname);
-
- for (VirtualFile child : baseDir.getChildren()) {
- String name = child.getName();
- File fromFile = new File(child.getPath());
- if (CCUtils.GENERATED_FILES_FOLDER.equals(name) || Project.DIRECTORY_STORE_FOLDER.equals(name)
- || name.contains("iml") || configurator.excludeFromArchive(fromFile)) {
- continue;
- }
- copyChild(archiveFolder, filter, child, fromFile);
- }
-
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
archiveFolder.refresh(false, true);
Course courseCopy = course.copy();
replaceAnswerFilesWithTaskFiles(courseCopy);
+ additionalFiles(courseCopy);
generateJson(archiveFolder, courseCopy);
VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
packCourse(archiveFolder, locationDir, zipName, showMessage);
synchronize(project);
}
+ private void additionalFiles(Course course) {
+ final Lesson lesson = CCUtils.createAdditionalLesson(course, project);
+ if (lesson != null) {
+ course.addLesson(lesson);
+ }
+ }
+
private void replaceAnswerFilesWithTaskFiles(Course courseCopy) {
for (Lesson lesson : courseCopy.getLessons()) {
String lessonDirName = EduNames.LESSON + String.valueOf(lesson.getIndex());
@@ -126,61 +119,76 @@ public class CCCreateCourseArchive extends DumbAwareAction {
for (Task task : lesson.getTaskList()) {
final VirtualFile taskDir = task.getTaskDir(project);
if (taskDir == null) continue;
- String taskDirName = EduNames.TASK + String.valueOf(task.getIndex());
- VirtualFile studentFileDir = VfsUtil.findRelativeFile(archiveFolder, lessonDirName, taskDirName);
- if (studentFileDir == null) {
- continue;
- }
- VirtualFile srcDir = studentFileDir.findChild(EduNames.SRC);
- if (srcDir != null) {
- studentFileDir = srcDir;
- }
- if (task instanceof TaskWithSubtasks) {
- transformSubtaskTestsToTextFiles(studentFileDir);
- }
- for (String taskFile : task.getTaskFiles().keySet()) {
- VirtualFile answerFile = taskDir.findFileByRelativePath(taskFile);
- if (answerFile == null) {
- continue;
- }
- EduUtils.createStudentFile(this, project, answerFile, studentFileDir, task, 0);
- }
+ convertToStudentTaskFiles(task, taskDir);
+ addTestsToTask(task);
+ addDescriptions(task);
}
}
}
- private void transformSubtaskTestsToTextFiles(VirtualFile studentFileDir) {
- Condition isSubtaskTestFile =
- file -> CCUtils.isTestsFile(project, file) && file.getName().contains(EduNames.SUBTASK_MARKER);
- List subtaskTests = ContainerUtil.filter(Arrays.asList(studentFileDir.getChildren()), isSubtaskTestFile);
- for (VirtualFile subtaskTest : subtaskTests) {
+ private void convertToStudentTaskFiles(Task task, VirtualFile taskDir) {
+ final HashMap studentTaskFiles = new HashMap<>();
+ for (Map.Entry entry : task.getTaskFiles().entrySet()) {
+ VirtualFile answerFile = taskDir.findFileByRelativePath(entry.getKey());
+ if (answerFile == null) {
+ continue;
+ }
+ final TaskFile studentFile = EduUtils.createStudentFile(project, answerFile, task, 0);
+ if (studentFile != null) {
+ studentTaskFiles.put(entry.getKey(), studentFile);
+ }
+ }
+ task.taskFiles = studentTaskFiles;
+ }
+
+ private void addDescriptions(@NotNull final Task task) {
+ final List descriptions = getDescriptionFiles(task, project);
+ for (VirtualFile file : descriptions) {
try {
- subtaskTest.rename(this, subtaskTest.getNameWithoutExtension() + ".txt");
+ task.addTaskText(file.getName(), VfsUtilCore.loadText(file));
}
catch (IOException e) {
- LOG.error(e);
+ LOG.warn("Failed to load text " + file.getName());
}
}
}
- });
- }
- private static void copyChild(VirtualFile archiveFolder, FileFilter filter, VirtualFile child, File fromFile) {
- File toFile = new File(archiveFolder.getPath(), child.getName());
-
- try {
- if (child.isDirectory()) {
- FileUtil.copyDir(fromFile, toFile, filter);
- }
- else {
- if (filter.accept(fromFile)) {
- FileUtil.copy(fromFile, toFile);
+ private void addTestsToTask(Task task) {
+ final List testFiles = getTestFiles(task, project);
+ for (VirtualFile file : testFiles) {
+ try {
+ task.addTestsTexts(file.getName(), VfsUtilCore.loadText(file));
+ }
+ catch (IOException e) {
+ LOG.warn("Failed to load text " + file.getName());
+ }
}
}
- }
- catch (IOException e) {
- LOG.info("Failed to copy" + fromFile.getPath(), e);
- }
+
+ private List getTestFiles(@NotNull Task task, @NotNull Project project) {
+ List testFiles = new ArrayList<>();
+ VirtualFile taskDir = task.getTaskDir(project);
+ if (taskDir == null) {
+ return testFiles;
+ }
+ testFiles.addAll(Arrays.stream(taskDir.getChildren())
+ .filter(file -> StudyUtils.isTestsFile(project, file.getName()))
+ .collect(Collectors.toList()));
+ return testFiles;
+ }
+
+ private List getDescriptionFiles(@NotNull Task task, @NotNull Project project) {
+ List testFiles = new ArrayList<>();
+ VirtualFile taskDir = task.getTaskDir(project);
+ if (taskDir == null) {
+ return testFiles;
+ }
+ testFiles.addAll(Arrays.stream(taskDir.getChildren())
+ .filter(file -> StudyUtils.isTaskDescriptionFile(file.getName()))
+ .collect(Collectors.toList()));
+ return testFiles;
+ }
+ });
}
private static void synchronize(@NotNull final Project project) {
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java
index 6949ff558c5f..11334cf12bdf 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java
@@ -27,7 +27,10 @@ import com.jetbrains.edu.learning.StudySerializationUtils;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.core.EduDocumentListener;
import com.jetbrains.edu.learning.core.EduNames;
-import com.jetbrains.edu.learning.courseFormat.*;
+import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
+import com.jetbrains.edu.learning.courseFormat.Course;
+import com.jetbrains.edu.learning.courseFormat.Lesson;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import org.jetbrains.annotations.NotNull;
@@ -35,8 +38,6 @@ import java.io.*;
import java.util.ArrayList;
import java.util.Map;
-import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.OUR_COURSES_DIR;
-
public class CCFromCourseArchive extends DumbAwareAction {
private static final Logger LOG = Logger.getInstance(CCFromCourseArchive.class.getName());
@@ -80,8 +81,6 @@ public class CCFromCourseArchive extends DumbAwareAction {
}
StudyTaskManager.getInstance(project).setCourse(course);
- File courseDir = new File(OUR_COURSES_DIR, course.getName() + "-" + project.getName());
- course.setCourseDirectory(courseDir.getPath());
course.setCourseMode(CCUtils.COURSE_MODE);
project.getBaseDir().refresh(false, true);
int index = 1;
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java
index 5c16882a183a..957e47954511 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java
@@ -15,25 +15,22 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
-import com.jetbrains.edu.learning.courseFormat.Lesson;
-import com.jetbrains.edu.learning.courseFormat.tasks.Task;
-import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
-import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
+import com.jetbrains.edu.learning.courseFormat.Lesson;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.courseFormat.tasks.Task;
+import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import org.jetbrains.annotations.NotNull;
-import java.io.File;
import java.util.Map;
import static com.jetbrains.edu.coursecreator.actions.CCFromCourseArchive.createAnswerFile;
-import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.OUR_COURSES_DIR;
-import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.flushCourse;
public class CCGetCourseFromStepic extends DumbAwareAction {
@@ -66,21 +63,17 @@ public class CCGetCourseFromStepic extends DumbAwareAction {
final Course course = EduStepicConnector.getCourse(project, info);
if (course != null) {
- flushCourse(course);
- final File courseDirectory = StudyUtils.getCourseDirectory(course);
ApplicationManager.getApplication().invokeAndWait(() -> ApplicationManager.getApplication().runWriteAction(() -> {
final VirtualFile[] children = baseDir.getChildren();
for (VirtualFile child : children) {
StudyUtils.deleteFile(child);
}
- StudyGenerator.createCourse(course, baseDir, courseDirectory, project);
+ StudyGenerator.createCourse(course, baseDir);
}));
StudyTaskManager.getInstance(project).setCourse(course);
- File courseDir = new File(OUR_COURSES_DIR, course.getName() + "-" + project.getName());
- course.setCourseDirectory(courseDir.getPath());
course.setCourseMode(CCUtils.COURSE_MODE);
project.getBaseDir().refresh(false, true);
int index = 1;
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java
index 7e13155c0425..accb05906ebf 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java
@@ -1,29 +1,24 @@
package com.jetbrains.edu.coursecreator.actions;
import com.intellij.ide.projectView.ProjectView;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.BasicUndoableAction;
import com.intellij.openapi.command.undo.UnexpectedUndoException;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
-import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.courseFormat.tasks.Task;
+import org.jetbrains.annotations.NotNull;
-import java.io.IOException;
import java.util.Map;
public class CCHideFromStudent extends CCTaskFileActionBase {
-
- private static final Logger LOG = Logger.getInstance(CCHideFromStudent.class);
private static final String ACTION_NAME = "Hide from Student";
public CCHideFromStudent() {
@@ -36,21 +31,19 @@ public class CCHideFromStudent extends CCTaskFileActionBase {
if (taskFile == null) {
return;
}
- EduUtils.runUndoableAction(project, ACTION_NAME, new HideTaskFile(project, course, file, task, taskFile));
+ EduUtils.runUndoableAction(project, ACTION_NAME, new HideTaskFile(project, file, task, taskFile));
}
private static class HideTaskFile extends BasicUndoableAction {
private final Project myProject;
- private final Course myCourse;
private final VirtualFile myFile;
private final Task myTask;
private final TaskFile myTaskFile;
- public HideTaskFile(Project project, Course course, VirtualFile file, Task task, TaskFile taskFile) {
+ public HideTaskFile(Project project, VirtualFile file, Task task, TaskFile taskFile) {
super(file);
myProject = project;
- myCourse = course;
myFile = file;
myTask = task;
myTaskFile = taskFile;
@@ -59,7 +52,6 @@ public class CCHideFromStudent extends CCTaskFileActionBase {
@Override
public void undo() throws UnexpectedUndoException {
myTask.getTaskFiles().put(StudyUtils.pathRelativeToTask(myFile), myTaskFile);
- CCUtils.createResourceFile(myFile, myCourse, StudyUtils.getTaskDir(myFile));
if (!myTaskFile.getAnswerPlaceholders().isEmpty() && FileEditorManager.getInstance(myProject).isFileOpen(myFile)) {
for (FileEditor fileEditor : FileEditorManager.getInstance(myProject).getEditors(myFile)) {
if (fileEditor instanceof TextEditor) {
@@ -83,7 +75,7 @@ public class CCHideFromStudent extends CCTaskFileActionBase {
}
}
- public static void hideFromStudent(VirtualFile file, Project project, Map taskFiles, TaskFile taskFile) {
+ public static void hideFromStudent(VirtualFile file, Project project, Map taskFiles, @NotNull final TaskFile taskFile) {
if (!taskFile.getAnswerPlaceholders().isEmpty() && FileEditorManager.getInstance(project).isFileOpen(file)) {
for (FileEditor fileEditor : FileEditorManager.getInstance(project).getEditors(file)) {
if (fileEditor instanceof TextEditor) {
@@ -93,17 +85,6 @@ public class CCHideFromStudent extends CCTaskFileActionBase {
}
}
String taskRelativePath = StudyUtils.pathRelativeToTask(file);
- VirtualFile patternFile = StudyUtils.getPatternFile(taskFile, taskRelativePath);
- ApplicationManager.getApplication().runWriteAction(() -> {
- if (patternFile != null) {
- try {
- patternFile.delete(CCHideFromStudent.class);
- }
- catch (IOException e) {
- LOG.info(e);
- }
- }
- });
taskFiles.remove(taskRelativePath);
}
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java
index 2ac39ca62623..156d2f477c4a 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java
@@ -11,12 +11,12 @@ import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.courseFormat.Course;
+import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
-import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
-import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -59,11 +59,11 @@ public class CCPushCourse extends DumbAwareAction {
public void run(@NotNull ProgressIndicator indicator) {
for (Lesson lesson : course.getLessons()) {
if (lesson.getId() > 0) {
- CCStepicConnector.updateLesson(project, lesson, indicator);
+ CCStepicConnector.updateLesson(project, lesson);
}
else {
final CourseInfo info = CourseInfo.fromCourse(course);
- final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator);
+ final int lessonId = CCStepicConnector.postLesson(project, lesson);
if (lessonId != -1) {
final List sections = info.getSections();
final Integer sectionId = sections.get(sections.size() - 1);
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java
index 3eda00f494fa..1c203cf6e85c 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java
@@ -12,11 +12,11 @@ import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.courseFormat.Course;
-import com.jetbrains.edu.learning.courseFormat.Lesson;
-import com.jetbrains.edu.coursecreator.stepik.CCStepicConnector;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
+import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.stepic.EduStepicNames;
import org.jetbrains.annotations.NotNull;
@@ -77,11 +77,11 @@ public class CCPushLesson extends DumbAwareAction {
public void run(@NotNull ProgressIndicator indicator) {
indicator.setText("Uploading lesson to " + EduStepicNames.STEPIC_URL);
if (lesson.getId() > 0) {
- CCStepicConnector.updateLesson(project, lesson, indicator);
+ CCStepicConnector.updateLesson(project, lesson);
}
else {
final CourseInfo info = CourseInfo.fromCourse(course);
- final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator);
+ final int lessonId = CCStepicConnector.postLesson(project, lesson);
final List sections = info.getSections();
final Integer sectionId = sections.get(sections.size()-1);
CCStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId);
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java
index 896aa1e5d2fb..067a0b47d443 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java
@@ -31,10 +31,10 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.FrameWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
+import com.intellij.testFramework.LightVirtualFile;
import com.intellij.ui.JBColor;
import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.learning.StudyTaskManager;
@@ -109,33 +109,24 @@ public class CCShowPreview extends DumbAwareAction {
return;
}
-
if (taskFile.getActivePlaceholders().isEmpty()) {
Messages.showInfoMessage("Preview is available for task files with answer placeholders only", "No Preview for This File");
return;
}
- VirtualFile generatedFilesFolder = CCUtils.getGeneratedFilesFolder(project, module);
-
- if (generatedFilesFolder == null) {
- return;
- }
final Task task = taskFile.getTask();
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- Pair pair =
- EduUtils.createStudentFile(this, project, virtualFile, generatedFilesFolder, null,
- task instanceof TaskWithSubtasks ? ((TaskWithSubtasks)task).getActiveSubtaskIndex() : 0);
- if (pair != null) {
- showPreviewDialog(project, pair.getFirst(), pair.getSecond());
- }
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ TaskFile studentTaskFile = EduUtils.createStudentFile(project, virtualFile, task.copy(),
+ task instanceof TaskWithSubtasks ? ((TaskWithSubtasks)task).getActiveSubtaskIndex() : 0);
+ if (studentTaskFile != null) {
+ showPreviewDialog(project, studentTaskFile);
}
});
}
- private static void showPreviewDialog(@NotNull Project project, @NotNull VirtualFile userFile, @NotNull TaskFile taskFile) {
+ private static void showPreviewDialog(@NotNull Project project, @NotNull TaskFile taskFile) {
final FrameWrapper showPreviewFrame = new FrameWrapper(project);
+ final LightVirtualFile userFile = new LightVirtualFile(taskFile.name, taskFile.text);
showPreviewFrame.setTitle(userFile.getName());
LabeledEditor labeledEditor = new LabeledEditor(null);
final EditorFactory factory = EditorFactory.getInstance();
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/intellij/EduCCModuleBuilder.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/intellij/EduCCModuleBuilder.java
index 927cb26a4548..b8fc3e83c486 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/intellij/EduCCModuleBuilder.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/intellij/EduCCModuleBuilder.java
@@ -28,7 +28,6 @@ import com.jetbrains.edu.coursecreator.ui.CCNewProjectPanel;
import com.jetbrains.edu.learning.EduPluginConfigurator;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.courseFormat.Course;
-import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.intellij.generation.EduCourseModuleBuilder;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NotNull;
@@ -36,7 +35,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.text.JTextComponent;
-import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.function.Function;
@@ -93,8 +91,6 @@ class EduCCModuleBuilder extends EduCourseModuleBuilder {
Language language = wrapper.getLanguage();
course.setLanguage(language.getID());
course.setCourseMode(CCUtils.COURSE_MODE);
- File courseDir = new File(StudyProjectGenerator.OUR_COURSES_DIR, myPanel.getName() + "-" + project.getName());
- course.setCourseDirectory(courseDir.getPath());
StudyTaskManager.getInstance(project).setCourse(course);
EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language);
String languageName = language.getDisplayName();
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java
index 6f5e3cd7768b..85b018c7df81 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java
@@ -19,15 +19,13 @@ public class CCOptions implements StudyOptionsProvider {
if (!StudySettings.getInstance().isCourseCreatorEnabled()) return null;
if (CCSettings.getInstance().useHtmlAsDefaultTaskFormat()) {
myHtmlRadioButton.setSelected(true);
- IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> {
- IdeFocusManager.getGlobalInstance().requestFocus(myHtmlRadioButton, true);
- });
+ IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(
+ () -> IdeFocusManager.getGlobalInstance().requestFocus(myHtmlRadioButton, true));
}
else {
myMarkdownRadioButton.setSelected(true);
- IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> {
- IdeFocusManager.getGlobalInstance().requestFocus(myMarkdownRadioButton, true);
- });
+ IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(
+ () -> IdeFocusManager.getGlobalInstance().requestFocus(myMarkdownRadioButton, true));
}
return myPanel;
}
diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/stepik/CCStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/stepik/CCStepicConnector.java
index 1b45fb108668..d052e1bbbe51 100644
--- a/python/educational-core/src/com/jetbrains/edu/coursecreator/stepik/CCStepicConnector.java
+++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/stepik/CCStepicConnector.java
@@ -8,13 +8,9 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.VfsUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileFilter;
+import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.learning.StudySerializationUtils;
import com.jetbrains.edu.learning.core.EduNames;
-import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
@@ -24,7 +20,6 @@ import com.jetbrains.edu.learning.stepic.EduStepicAuthorizedClient;
import com.jetbrains.edu.learning.stepic.EduStepicNames;
import com.jetbrains.edu.learning.stepic.StepicUser;
import com.jetbrains.edu.learning.stepic.StepicWrappers;
-import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
@@ -65,13 +60,16 @@ public class CCStepicConnector {
ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Uploading Course", true) {
@Override
public void run(@NotNull final ProgressIndicator indicator) {
- postCourse(project, course, indicator);
+ postCourse(project, course);
}
});
}
- private static void postCourse(final Project project, @NotNull Course course, @NotNull final ProgressIndicator indicator) {
- indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL);
+ private static void postCourse(final Project project, @NotNull Course course) {
+ final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
+ if (indicator != null) {
+ indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL);
+ }
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses");
final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser();
@@ -110,55 +108,25 @@ public class CCStepicConnector {
final int sectionId = postModule(postedCourse.getId(), 1, String.valueOf(postedCourse.getName()));
int position = 1;
for (Lesson lesson : course.getLessons()) {
- indicator.checkCanceled();
- final int lessonId = postLesson(project, lesson, indicator);
+ if (indicator != null) {
+ indicator.checkCanceled();
+ }
+ final int lessonId = postLesson(project, lesson);
postUnit(lessonId, position, sectionId);
position += 1;
}
- ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.getId(), indicator));
+ ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(course, project, postedCourse.getId()));
}
catch (IOException e) {
LOG.error(e.getMessage());
}
}
- private static void postAdditionalFiles(@NotNull final Project project, int id, ProgressIndicator indicator) {
- final VirtualFile baseDir = project.getBaseDir();
- final List files = VfsUtil.getChildren(baseDir, new VirtualFileFilter() {
- @Override
- public boolean accept(VirtualFile file) {
- final String name = file.getName();
- return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS) &&
- !"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith(".");
- }
- });
-
- if (!files.isEmpty()) {
+ private static void postAdditionalFiles(Course course, @NotNull final Project project, int id) {
+ final Lesson lesson = CCUtils.createAdditionalLesson(course, project);
+ if (lesson != null) {
final int sectionId = postModule(id, 2, EduNames.PYCHARM_ADDITIONAL);
- final Lesson lesson = new Lesson();
- lesson.setName(EduNames.PYCHARM_ADDITIONAL);
- final Task task = new Task();
- task.setLesson(lesson);
- task.setName(EduNames.PYCHARM_ADDITIONAL);
- task.setIndex(1);
- for (VirtualFile file : files) {
- try {
- if (file != null) {
- if (EduUtils.isImage(file.getName())) {
- task.addTestsTexts(file.getName(), Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream())));
- }
- else {
- task.addTestsTexts(file.getName(), FileUtil.loadTextAndClose(file.getInputStream()));
- }
- }
- }
- catch (IOException e) {
- LOG.error("Can't find file " + file.getPath());
- }
- }
- lesson.addTask(task);
- lesson.setIndex(1);
- final int lessonId = postLesson(project, lesson, indicator);
+ final int lessonId = postLesson(project, lesson);
postUnit(lessonId, 1, sectionId);
}
}
@@ -255,7 +223,7 @@ public class CCStepicConnector {
return -1;
}
- public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) {
+ public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson) {
final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + EduStepicNames.LESSONS + String.valueOf(lesson.getId()));
String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
@@ -279,7 +247,10 @@ public class CCStepicConnector {
}
for (Task task : lesson.getTaskList()) {
- indicator.checkCanceled();
+ final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
+ if (indicator != null) {
+ indicator.checkCanceled();
+ }
postTask(project, task, lesson.getId());
}
return lesson.getId();
@@ -290,7 +261,7 @@ public class CCStepicConnector {
return -1;
}
- public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) {
+ public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson) {
final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/lessons");
String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson));
@@ -311,7 +282,10 @@ public class CCStepicConnector {
final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0);
lesson.setId(postedLesson.getId());
for (Task task : lesson.getTaskList()) {
- indicator.checkCanceled();
+ final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
+ if (indicator != null) {
+ indicator.checkCanceled();
+ }
postTask(project, task, postedLesson.getId());
}
return postedLesson.getId();
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java
index da549bc885e8..74dd51a872c8 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java
@@ -21,8 +21,6 @@ import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.File;
-
public interface EduPluginConfigurator {
String EP_NAME = "Edu.pluginConfigurator";
LanguageExtension INSTANCE = new LanguageExtension<>(EP_NAME);
@@ -64,7 +62,7 @@ public interface EduPluginConfigurator {
/**
* Used in educator plugin to filter files to be packed into course archive
*/
- boolean excludeFromArchive(@NotNull File pathname);
+ boolean excludeFromArchive(@NotNull String name);
/**
* @return true for all the test files including tests for subtasks
@@ -128,4 +126,7 @@ public interface EduPluginConfigurator {
@Nullable String moduleDir) {
}
+ default String getBundledCoursePath() {
+ return null;
+ }
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java
index 8173ff07d68c..335857dfc4a0 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java
@@ -39,6 +39,7 @@ import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.*;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
+import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
@@ -49,14 +50,12 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
-import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static com.jetbrains.edu.learning.StudyUtils.execCancelable;
-import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.flushCourse;
public class StudyProjectComponent implements ProjectComponent {
@@ -150,15 +149,8 @@ public class StudyProjectComponent implements ProjectComponent {
final CourseInfo info = CourseInfo.fromCourse(currentCourse);
if (info == null) return;
- final File resourceDirectory = new File(currentCourse.getCourseDirectory());
- if (resourceDirectory.exists()) {
- FileUtil.delete(resourceDirectory);
- }
-
final Course course = EduStepicConnector.getCourse(myProject, info);
-
if (course == null) return;
- flushCourse(course);
course.initCourse(false);
EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById());
@@ -175,14 +167,14 @@ public class StudyProjectComponent implements ProjectComponent {
Lesson studentLesson = currentCourse.getLesson(lesson.getId());
final String lessonDirName = EduNames.LESSON + String.valueOf(lessonIndex);
- final File lessonDir = new File(myProject.getBasePath(), lessonDirName);
- if (!lessonDir.exists()){
- final File fromLesson = new File(resourceDirectory, lessonDirName);
+ final VirtualFile baseDir = myProject.getBaseDir();
+ final VirtualFile lessonDir = baseDir.findChild(lessonDirName);
+ if (lessonDir == null) {
try {
- FileUtil.copyDir(fromLesson, lessonDir);
+ StudyGenerator.createLesson(lesson, baseDir);
}
catch (IOException e) {
- LOG.warn("Failed to copy lesson " + fromLesson.getPath());
+ LOG.error("Failed to create lesson");
}
lesson.setIndex(lessonIndex);
lesson.initLesson(currentCourse, false);
@@ -210,15 +202,14 @@ public class StudyProjectComponent implements ProjectComponent {
task.setIndex(index);
final String taskDirName = EduNames.TASK + String.valueOf(index);
- final File toTask = new File(lessonDir, taskDirName);
+ final VirtualFile taskDir = lessonDir.findChild(taskDirName);
- final String taskPath = FileUtil.join(resourceDirectory.getPath(), lessonDirName, taskDirName);
- final File taskDir = new File(taskPath);
- if (!taskDir.exists()) return;
- final File[] taskFiles = taskDir.listFiles();
- if (taskFiles == null) continue;
- for (File fromFile : taskFiles) {
- copyFile(fromFile, new File(toTask, fromFile.getName()));
+ if (taskDir != null) return;
+ try {
+ StudyGenerator.createTask(task, lessonDir);
+ }
+ catch (IOException e) {
+ LOG.error("Failed to create task");
}
tasks.add(task);
}
@@ -231,17 +222,6 @@ public class StudyProjectComponent implements ProjectComponent {
notification.notify(myProject);
}
- private static void copyFile(@NotNull final File from, @NotNull final File to) {
- if (from.exists()) {
- try {
- FileUtil.copyFileOrDir(from, to);
- }
- catch (IOException e) {
- LOG.warn("Failed to copy " + from.getName());
- }
- }
- }
-
private void addShortcut(@NotNull final String actionIdString, @NotNull final String[] shortcuts) {
KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
for (Keymap keymap : keymapManager.getAllKeymaps()) {
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java
index e6b1c052afb2..1a8165ebcc16 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java
@@ -528,9 +528,9 @@ public class StudySerializationUtils {
stepOptionsJson = convertToSecondVersion(stepOptionsJson);
case 2:
stepOptionsJson = convertToThirdVersion(stepOptionsJson);
- // uncomment for future versions
- //case 3:
- // stepOptionsJson = convertToFourthVersion(stepOptionsJson);
+ // uncomment for future versions
+ //case 3:
+ // stepOptionsJson = convertToFourthVersion(stepOptionsJson);
}
convertSubtaskInfosToMap(stepOptionsJson);
StepicWrappers.StepOptions stepOptions =
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java
index 9d74279928a7..8b981f97cc74 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java
@@ -13,14 +13,12 @@ import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.Transient;
import com.jetbrains.edu.learning.courseFormat.*;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
-import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.ui.StudyToolWindow;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -39,7 +37,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb
public int VERSION = 5;
public Map> myUserTests = new HashMap<>();
- public List myInvisibleFiles = new ArrayList<>();
public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx();
private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT;
@@ -150,12 +147,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb
VERSION = CURRENT_VERSION;
if (myCourse != null) {
myCourse.initCourse(true);
- if (version != VERSION) {
- final File updatedCourse = new File(StudyProjectGenerator.OUR_COURSES_DIR, myCourse.getName());
- if (updatedCourse.exists()) {
- myCourse.setCourseDirectory(updatedCourse.getAbsolutePath());
- }
- }
}
}
catch (StudySerializationUtils.StudyUnrecognizedFormatException e) {
@@ -167,14 +158,6 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb
return ServiceManager.getService(project, StudyTaskManager.class);
}
- public void addInvisibleFiles(String filePath) {
- myInvisibleFiles.add(filePath);
- }
-
- public boolean isInvisibleFile(String path) {
- return myInvisibleFiles.contains(path);
- }
-
public boolean shouldUseJavaFx() {
return myShouldUseJavaFx;
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java
index 94651eecc62a..8909c6936fd7 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java
@@ -7,6 +7,7 @@ import com.intellij.ide.IdeView;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
+import com.intellij.ide.projectView.ProjectView;
import com.intellij.ide.util.EditorHelper;
import com.intellij.lang.Language;
import com.intellij.openapi.actionSystem.AnActionEvent;
@@ -40,8 +41,9 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
+import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
@@ -49,6 +51,7 @@ import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
import com.intellij.ui.JBColor;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.content.Content;
@@ -72,9 +75,7 @@ import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask;
-import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.editor.StudyEditor;
-import com.jetbrains.edu.learning.stepic.StepicUser;
import com.jetbrains.edu.learning.ui.StudyToolWindow;
import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
import com.petebevin.markdown.MarkdownProcessor;
@@ -84,9 +85,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.io.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
+import java.util.*;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
@@ -238,20 +237,6 @@ public class StudyUtils {
}
}
- public static File copyResourceFile(@NotNull final String sourceName, @NotNull final String copyName, @NotNull final Project project,
- @NotNull final Task task)
- throws IOException {
- final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
- final Course course = taskManager.getCourse();
- int taskNum = task.getIndex();
- int lessonNum = task.getLesson().getIndex();
- assert course != null;
- final String pathToResource = FileUtil.join(course.getCourseDirectory(), EduNames.LESSON + lessonNum, EduNames.TASK + taskNum);
- final File resourceFile = new File(pathToResource, copyName);
- FileUtil.copy(new File(pathToResource, sourceName), resourceFile);
- return resourceFile;
- }
-
@Nullable
public static Sdk findSdk(@NotNull final Task task, @NotNull final Project project) {
final Language language = task.getLesson().getCourse().getLanguageById();
@@ -413,34 +398,6 @@ public class StudyUtils {
}
}
-
- @Nullable
- public static VirtualFile getPatternFile(@NotNull TaskFile taskFile, String name) {
- Task task = taskFile.getTask();
- String lessonDir = EduNames.LESSON + String.valueOf(task.getLesson().getIndex());
- String taskDir = EduNames.TASK + String.valueOf(task.getIndex());
- Course course = task.getLesson().getCourse();
- File resourceFile = new File(course.getCourseDirectory());
- if (!resourceFile.exists()) {
- return null;
- }
- String patternPath = FileUtil.join(resourceFile.getPath(), lessonDir, taskDir, name);
- VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true);
- if (patternFile == null) {
- return null;
- }
- return patternFile;
- }
-
- @Nullable
- public static Document getPatternDocument(@NotNull final TaskFile taskFile, String name) {
- VirtualFile patternFile = getPatternFile(taskFile, name);
- if (patternFile == null) {
- return null;
- }
- return FileDocumentManager.getInstance().getDocument(patternFile);
- }
-
public static boolean isRenameableOrMoveable(@NotNull final Project project, @NotNull final Course course, @NotNull final PsiElement element) {
if (element instanceof PsiFile) {
VirtualFile virtualFile = ((PsiFile)element).getVirtualFile();
@@ -623,23 +580,6 @@ public class StudyUtils {
return course != null && EduNames.STUDY.equals(course.getCourseMode());
}
- @NotNull
- public static File getCourseDirectory(Course course) {
- final File courseDirectory;
- if (course.isAdaptive()) {
- StepicUser user = StudySettings.getInstance().getUser();
- String email = user == null ? "" : user.getEmail();
-
- courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR,
- StudyProjectGenerator.ADAPTIVE_COURSE_PREFIX + course.getName()
- + "_" + email);
- }
- else {
- courseDirectory = new File(StudyProjectGenerator.OUR_COURSES_DIR, course.getName());
- }
- return courseDirectory;
- }
-
public static boolean hasJavaFx() {
try {
Class.forName("javafx.application.Platform");
@@ -880,4 +820,48 @@ public class StudyUtils {
LOG.error(e);
}
}
+ public static void openFirstTask(@NotNull final Course course, @NotNull final Project project) {
+ LocalFileSystem.getInstance().refresh(false);
+ final Lesson firstLesson = getFirst(course.getLessons());
+ if (firstLesson == null) return;
+ final Task firstTask = getFirst(firstLesson.getTaskList());
+ if (firstTask == null) return;
+ final VirtualFile taskDir = firstTask.getTaskDir(project);
+ if (taskDir == null) return;
+ final Map taskFiles = firstTask.getTaskFiles();
+ VirtualFile activeVirtualFile = null;
+ for (Map.Entry entry : taskFiles.entrySet()) {
+ final String relativePath = entry.getKey();
+ final TaskFile taskFile = entry.getValue();
+ taskDir.refresh(false, true);
+ final VirtualFile virtualFile = taskDir.findFileByRelativePath(relativePath);
+ if (virtualFile != null) {
+ if (!taskFile.getActivePlaceholders().isEmpty()) {
+ activeVirtualFile = virtualFile;
+ }
+ }
+ }
+ if (activeVirtualFile != null) {
+ final PsiFile file = PsiManager.getInstance(project).findFile(activeVirtualFile);
+ ProjectView.getInstance(project).select(file, activeVirtualFile, false);
+ final FileEditor[] editors = FileEditorManager.getInstance(project).openFile(activeVirtualFile, true);
+ if (editors.length == 0) {
+ return;
+ }
+ final FileEditor studyEditor = editors[0];
+ if (studyEditor instanceof StudyEditor) {
+ selectFirstAnswerPlaceholder((StudyEditor)studyEditor, project);
+ }
+ FileEditorManager.getInstance(project).openFile(activeVirtualFile, true);
+ }
+ else {
+ String first = getFirst(taskFiles.keySet());
+ if (first != null) {
+ NewVirtualFile firstFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(first);
+ if (firstFile != null) {
+ FileEditorManager.getInstance(project).openFile(firstFile, true);
+ }
+ }
+ }
+ }
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java
index 85f3d3869ae3..8c6ea0bd36cf 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java
@@ -3,13 +3,9 @@ package com.jetbrains.edu.learning.actions;
import com.intellij.icons.AllIcons;
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.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.TextRange;
import com.jetbrains.edu.learning.StudyState;
import com.jetbrains.edu.learning.StudySubtaskUtils;
import com.jetbrains.edu.learning.StudyTaskManager;
@@ -17,8 +13,8 @@ import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.learning.courseFormat.Course;
+import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
import com.jetbrains.edu.learning.editor.StudyEditor;
import org.jetbrains.annotations.Nullable;
@@ -41,24 +37,12 @@ public class StudyRefreshAnswerPlaceholder extends DumbAwareAction {
return;
}
StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project);
- final StudyState studyState = new StudyState(studyEditor);
- if (answerPlaceholder.getTaskFile().getTask() instanceof TaskWithSubtasks) {
- StudySubtaskUtils.refreshPlaceholder(studyState.getEditor(), answerPlaceholder);
- return;
+ if (studyEditor != null) {
+ StudySubtaskUtils.refreshPlaceholder(studyEditor.getEditor(), answerPlaceholder);
+ final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+ answerPlaceholder.reset();
+ taskManager.setStatus(answerPlaceholder, StudyStatus.Unchecked);
}
- Document patternDocument = StudyUtils.getPatternDocument(answerPlaceholder.getTaskFile(), studyState.getVirtualFile().getName());
- if (patternDocument == null) {
- return;
- }
- AnswerPlaceholder.MyInitialState initialState = answerPlaceholder.getInitialState();
- int startOffset = initialState.getOffset();
- final String text = patternDocument.getText(new TextRange(startOffset, startOffset + initialState.getLength()));
- CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
- Document document = studyState.getEditor().getDocument();
- int offset = answerPlaceholder.getOffset();
- document.deleteString(offset, offset + answerPlaceholder.getRealLength());
- document.insertString(offset, text);
- }), NAME, null);
}
@Override
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java
index 08fb50fcfb43..5cbb7c749ecd 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java
@@ -76,7 +76,7 @@ public class StudyRefreshTaskFileAction extends StudyActionWithShortcut {
}
}
else {
- if (!resetTaskFile(editor.getDocument(), project, taskFile, studyState.getVirtualFile().getName())) {
+ if (!resetTaskFile(editor.getDocument(), project, taskFile)) {
Messages.showInfoMessage("The initial text of task file is unavailable", "Failed to Refresh Task File");
return;
}
@@ -95,16 +95,13 @@ public class StudyRefreshTaskFileAction extends StudyActionWithShortcut {
() -> IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true));
StudyNavigator.navigateToFirstAnswerPlaceholder(editor, taskFile);
- showBalloon(project, "You can start again now", MessageType.INFO);
+ showBalloon(project, MessageType.INFO);
}
private static boolean resetTaskFile(@NotNull final Document document,
@NotNull final Project project,
- TaskFile taskFile,
- String name) {
- if (!resetDocument(document, taskFile, name)) {
- return false;
- }
+ TaskFile taskFile) {
+ resetDocument(document, taskFile);
final Task task = taskFile.getTask();
task.setStatus(StudyStatus.Unchecked);
if (task instanceof ChoiceTask) {
@@ -116,9 +113,9 @@ public class StudyRefreshTaskFileAction extends StudyActionWithShortcut {
return true;
}
- private static void showBalloon(@NotNull final Project project, String text, @NotNull final MessageType messageType) {
+ private static void showBalloon(@NotNull final Project project, @NotNull final MessageType messageType) {
BalloonBuilder balloonBuilder =
- JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, messageType, null);
+ JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can start again now", messageType, null);
final Balloon balloon = balloonBuilder.createBalloon();
StudyEditor selectedStudyEditor = StudyUtils.getSelectedStudyEditor(project);
assert selectedStudyEditor != null;
@@ -135,19 +132,14 @@ public class StudyRefreshTaskFileAction extends StudyActionWithShortcut {
}
- private static boolean resetDocument(@NotNull final Document document,
- @NotNull final TaskFile taskFile,
- String fileName) {
- final Document patternDocument = StudyUtils.getPatternDocument(taskFile, fileName);
- if (patternDocument == null) {
- return false;
- }
+ private static void resetDocument(@NotNull final Document document,
+ @NotNull final TaskFile taskFile) {
StudyUtils.deleteGuardedBlocks(document);
taskFile.setTrackChanges(false);
clearDocument(document);
- document.setText(patternDocument.getCharsSequence());
+
+ document.setText(taskFile.text);
taskFile.setTrackChanges(true);
- return true;
}
private static void clearDocument(@NotNull final Document document) {
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java
index 3604bd8665c0..daa1d3491eb5 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java
@@ -21,7 +21,6 @@ import com.jetbrains.edu.learning.courseFormat.StudyStatus;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import org.jetbrains.annotations.NotNull;
-import java.io.File;
import java.io.IOException;
public class StudySmartChecker {
@@ -40,7 +39,6 @@ public class StudySmartChecker {
@NotNull final VirtualFile virtualFile,
@NotNull final Document usersDocument) {
VirtualFile fileWindows = null;
- File resourceFile = null;
VirtualFile windowCopy = null;
try {
final int index = placeholder.getIndex();
@@ -49,7 +47,6 @@ public class StudySmartChecker {
final FileDocumentManager documentManager = FileDocumentManager.getInstance();
final Document windowDocument = documentManager.getDocument(windowCopy);
if (windowDocument != null) {
- resourceFile = StudyUtils.copyResourceFile(virtualFile.getName(), windowCopy.getName(), project, usersTaskFile.getTask());
TaskFile windowTaskFile = answerTaskFile.getTask().copy().getTaskFile(StudyUtils.pathRelativeToTask(virtualFile));
if (windowTaskFile == null) {
return;
@@ -81,9 +78,6 @@ public class StudySmartChecker {
finally {
StudyUtils.deleteFile(windowCopy);
StudyUtils.deleteFile(fileWindows);
- if (resourceFile != null && resourceFile.exists() && !resourceFile.delete()) {
- LOG.error("failed to delete", resourceFile.getPath());
- }
}
}
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/core/EduUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/core/EduUtils.java
index 0f6a8e5fe719..0ec946b2aefe 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/core/EduUtils.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/core/EduUtils.java
@@ -13,18 +13,20 @@ import com.intellij.openapi.command.undo.UndoableAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
-import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiDirectory;
+import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.courseFormat.*;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
+import org.apache.commons.codec.binary.Base64;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -120,68 +122,52 @@ public class EduUtils {
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
}
-
- public static VirtualFile copyFile(Object requestor, VirtualFile toDir, VirtualFile file) {
- Document document = FileDocumentManager.getInstance().getDocument(file);
- if (document != null) {
- FileDocumentManager.getInstance().saveDocument(document);
- }
- String taskRelativePath = StudyUtils.pathRelativeToTask(file);
- try {
- VirtualFile userFile = toDir.findFileByRelativePath(taskRelativePath);
- if (userFile != null) {
- userFile.delete(requestor);
- }
- return VfsUtil.copyFileRelative(requestor, file, toDir, taskRelativePath);
- }
- catch (IOException e) {
- LOG.info("Failed to create file " + taskRelativePath + " in folder " + toDir.getPath(), e);
- }
- return null;
- }
-
@Nullable
- public static Pair createStudentFile(Object requestor,
- Project project,
- VirtualFile answerFile,
- VirtualFile parentDir,
- @Nullable Task task,
- int targetSubtaskIndex) {
-
- VirtualFile studentFile = copyFile(requestor, parentDir, answerFile);
- if (studentFile == null) {
- return null;
- }
- Document studentDocument = FileDocumentManager.getInstance().getDocument(studentFile);
- if (studentDocument == null) {
- return null;
- }
- if (task == null) {
- task = StudyUtils.getTaskForFile(project, answerFile);
+ public static TaskFile createStudentFile(Project project, VirtualFile answerFile, @Nullable Task task, int targetSubtaskIndex) {
+ try {
if (task == null) {
+ task = StudyUtils.getTaskForFile(project, answerFile);
+ if (task == null) {
+ return null;
+ }
+ task = task.copy();
+ }
+ TaskFile taskFile = task.getTaskFile(StudyUtils.pathRelativeToTask(answerFile));
+ if (taskFile == null) {
return null;
}
- task = task.copy();
- }
- TaskFile taskFile = task.getTaskFile(StudyUtils.pathRelativeToTask(answerFile));
- if (taskFile == null) {
- return null;
- }
- EduDocumentListener listener = new EduDocumentListener(taskFile, false);
- studentDocument.addDocumentListener(listener);
- taskFile.setTrackLengths(false);
- for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
- if (task instanceof TaskWithSubtasks) {
- int fromSubtask = ((TaskWithSubtasks)task).getActiveSubtaskIndex();
- placeholder.switchSubtask(studentDocument, fromSubtask, targetSubtaskIndex);
+ if (isImage(taskFile.name)) {
+ taskFile.text = Base64.encodeBase64String(answerFile.contentsToByteArray());
+ return taskFile;
}
+ final LightVirtualFile studentFile = new LightVirtualFile("student_task", PlainTextFileType.INSTANCE,
+ VfsUtilCore.loadText(answerFile));
+ Document studentDocument = FileDocumentManager.getInstance().getDocument(studentFile);
+ if (studentDocument == null) {
+ return null;
+ }
+ EduDocumentListener listener = new EduDocumentListener(taskFile, false);
+ studentDocument.addDocumentListener(listener);
+ taskFile.setTrackLengths(false);
+ for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
+ if (task instanceof TaskWithSubtasks) {
+ int fromSubtask = ((TaskWithSubtasks)task).getActiveSubtaskIndex();
+ placeholder.switchSubtask(studentDocument, fromSubtask, targetSubtaskIndex);
+ }
+ }
+ for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
+ replaceWithTaskText(studentDocument, placeholder, targetSubtaskIndex);
+ }
+ taskFile.setTrackChanges(true);
+ studentDocument.removeDocumentListener(listener);
+ taskFile.text = studentDocument.getImmutableCharSequence().toString();
+ return taskFile;
}
- for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
- replaceWithTaskText(studentDocument, placeholder, targetSubtaskIndex);
+ catch (IOException e) {
+ LOG.error("Failed to convert answer file to student one");
}
- taskFile.setTrackChanges(true);
- studentDocument.removeDocumentListener(listener);
- return Pair.create(studentFile, taskFile);
+
+ return null;
}
private static void replaceWithTaskText(Document studentDocument, AnswerPlaceholder placeholder, int toSubtaskIndex) {
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Course.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Course.java
index 392ce6b4c6b2..44976a6078dc 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Course.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Course.java
@@ -18,13 +18,13 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.stream.Collectors;
public class Course {
@Expose private List lessons = new ArrayList<>();
@Expose private List authors = new ArrayList<>();
@Expose private String description;
@Expose private String name;
- private String myCourseDirectory = "";
@Expose private int id;
@Expose @SerializedName("update_date") private Date myUpdateDate;
@Expose private boolean isAdaptive = false;
@@ -35,8 +35,7 @@ public class Course {
private String courseType = EduNames.PYCHARM;
private String courseMode = EduNames.STUDY; //this field is used to distinguish study and course creator modes
- public Course() {
- }
+ public Course() {}
/**
* Initializes state of course
@@ -48,12 +47,18 @@ public class Course {
}
public List getLessons() {
- return lessons;
+ return getLessons(false);
+ }
+
+ public List getLessons(boolean withAadditional) {
+ return withAadditional ? lessons
+ : lessons.stream().filter(lesson -> !EduNames.PYCHARM_ADDITIONAL.equals(lesson.getName())).collect(Collectors.toList());
}
public void setLessons(List lessons) {
this.lessons = lessons;
}
+
public void addLessons(List lessons) {
this.lessons.addAll(lessons);
}
@@ -62,6 +67,10 @@ public class Course {
lessons.add(lesson);
}
+ public void removeLesson(Lesson lesson) {
+ lessons.remove(lesson);
+ }
+
public Lesson getLesson(@NotNull final String name) {
int lessonIndex = EduUtils.getIndex(name, EduNames.LESSON);
List lessons = getLessons();
@@ -118,14 +127,6 @@ public class Course {
this.name = name;
}
- public String getCourseDirectory() {
- return myCourseDirectory;
- }
-
- public void setCourseDirectory(@NotNull final String courseDirectory) {
- myCourseDirectory = courseDirectory;
- }
-
public String getDescription() {
return description;
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/CourseInfo.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/CourseInfo.java
index acac3c5f951d..89fe65b8941e 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/CourseInfo.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/CourseInfo.java
@@ -1,7 +1,6 @@
package com.jetbrains.edu.learning.courseFormat;
import com.google.gson.annotations.SerializedName;
-import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.stepic.StepicUser;
import org.jetbrains.annotations.NotNull;
@@ -10,7 +9,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import java.util.stream.Collectors;
/**
* Implementation of class which contains information to be shawn in course description in tool window
@@ -152,10 +150,11 @@ public class CourseInfo {
public static CourseInfo fromCourse(@Nullable final Course course) {
if (course == null) return null;
- final List infos = StudyProjectGenerator.getCoursesFromCache().stream().
- filter(info -> info.id == course.getId()).collect(Collectors.toList());
- if (infos.isEmpty()) return null;
- return infos.get(0);
+ CourseInfo courseInfo = new CourseInfo();
+ courseInfo.setName(course.getName());
+ courseInfo.setDescription(course.getDescription());
+ courseInfo.setAuthors(course.getAuthors());
+ return courseInfo;
}
public List getSections() {
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java
index d5b56e06dd01..f5467dddee04 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java
@@ -21,6 +21,11 @@ import java.util.Map;
/**
* Implementation of task which contains task files, tests, input file for tests
+ *
+ * To implement new task there are 3 steps to be done:
+ * - extend Task class
+ * - go to Lesson and update elementTypes in taskList AbstractCollection. Needed for proper xml serialization
+ * - Update TaskSerializer and TaskDeserializer in StudySerializationUtil to handle json serialization
*/
public class Task implements StudyItem {
@Expose private String name;
@@ -35,8 +40,10 @@ public class Task implements StudyItem {
@SerializedName("task_files")
@Expose public Map taskFiles = new HashMap<>();
- protected Map testsText = new HashMap<>();
- protected Map taskTexts = new HashMap<>();
+ @SerializedName("test_files")
+ @Expose protected Map testsText = new HashMap<>();
+ @SerializedName("task_texts")
+ @Expose protected Map taskTexts = new HashMap<>();
@Transient private Lesson myLesson;
@Expose @SerializedName("update_date") private Date myUpdateDate;
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
index e19a67d25430..91e5eb7523c8 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
@@ -1,125 +1,33 @@
package com.jetbrains.edu.learning.courseGeneration;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.edu.learning.StudyTaskManager;
-import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
+import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Lesson;
-import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.courseFormat.tasks.Task;
+import org.apache.commons.codec.binary.Base64;
import org.jetbrains.annotations.NotNull;
-import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class StudyGenerator {
- private StudyGenerator() {
-
- }
private static final Logger LOG = Logger.getInstance(StudyGenerator.class.getName());
- /**
- * Creates task files in its task folder in project user created
- *
- * @param taskDir project directory of task which task file belongs to
- * @param resourceRoot directory where original task file stored
- * @throws IOException
- */
- public static void createTaskFile(@NotNull final VirtualFile taskDir, @NotNull final File resourceRoot,
- @NotNull final String name) throws IOException {
- String systemIndependentName = FileUtil.toSystemIndependentName(name);
- File resourceFile = new File(resourceRoot, name);
- File fileInProject = new File(taskDir.getPath(), systemIndependentName);
- FileUtil.copyFileOrDir(resourceFile, fileInProject);
- }
-
- /**
- * Creates task directory in its lesson folder in project user created
- *
- * @param lessonDir project directory of lesson which task belongs to
- * @param resourceRoot directory where original task file stored
- * @throws IOException
- */
- public static void createTask(@NotNull final Task task, @NotNull final VirtualFile lessonDir, @NotNull final File resourceRoot,
- @NotNull final Project project) throws IOException {
- VirtualFile taskDir = lessonDir.createChildDirectory(project, EduNames.TASK + Integer.toString(task.getIndex()));
- File newResourceRoot = new File(resourceRoot, taskDir.getName());
- int i = 0;
- for (Map.Entry taskFile : task.getTaskFiles().entrySet()) {
- TaskFile taskFileContent = taskFile.getValue();
- taskFileContent.setIndex(i);
- i++;
- createTaskFile(taskDir, newResourceRoot, taskFile.getKey());
- }
- File[] filesInTask = newResourceRoot.listFiles();
- if (filesInTask != null) {
- for (File file : filesInTask) {
- String fileName = file.getName();
- if (!task.isTaskFile(fileName)) {
- File resourceFile = new File(newResourceRoot, fileName);
- File fileInProject = new File(taskDir.getCanonicalPath(), fileName);
- FileUtil.copyFileOrDir(resourceFile, fileInProject);
- if (!StudyUtils.isTestsFile(project, fileName) && !StudyUtils.isTaskDescriptionFile(fileName)) {
- StudyTaskManager.getInstance(project).addInvisibleFiles(FileUtil.toSystemIndependentName(fileInProject.getPath()));
- }
- }
- }
- }
- }
-
- /**
- * Creates lesson directory in its course folder in project user created
- *
- * @param courseDir project directory of course
- * @param resourceRoot directory where original lesson stored
- * @throws IOException
- */
- public static void createLesson(@NotNull final Lesson lesson, @NotNull final VirtualFile courseDir, @NotNull final File resourceRoot,
- @NotNull final Project project) throws IOException {
- if (EduNames.PYCHARM_ADDITIONAL.equals(lesson.getName())) return;
- String lessonDirName = EduNames.LESSON + Integer.toString(lesson.getIndex());
- VirtualFile lessonDir = courseDir.createChildDirectory(project, lessonDirName);
- final List taskList = lesson.getTaskList();
- for (int i = 1; i <= taskList.size(); i++) {
- Task task = taskList.get(i - 1);
- task.setIndex(i);
- createTask(task, lessonDir, new File(resourceRoot, lessonDir.getName()), project);
- }
- }
-
- /**
- * Creates course directory in project user created
- *
- * @param baseDir project directory
- * @param resourceRoot directory where original course is stored
- */
- public static void createCourse(@NotNull final Course course, @NotNull final VirtualFile baseDir, @NotNull final File resourceRoot,
- @NotNull final Project project) {
+ private StudyGenerator() {}
+ public static void createCourse(@NotNull final Course course, @NotNull final VirtualFile baseDir) {
try {
- final List lessons = course.getLessons();
+ final List lessons = course.getLessons(true);
for (int i = 1; i <= lessons.size(); i++) {
Lesson lesson = lessons.get(i - 1);
lesson.setIndex(i);
- createLesson(lesson, baseDir, resourceRoot, project);
- }
- File[] files = resourceRoot.listFiles(
- (dir, name) -> !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS));
- if (files != null) {
- for (File file : files) {
- File dir = new File(baseDir.getPath(), file.getName());
- if (file.isDirectory()) {
- FileUtil.copyDir(file, dir);
- continue;
- }
- FileUtil.copy(file, dir);
- }
+ createLesson(lesson, baseDir);
}
}
catch (IOException e) {
@@ -127,4 +35,88 @@ public class StudyGenerator {
}
}
+ public static void createLesson(@NotNull final Lesson lesson, @NotNull final VirtualFile courseDir) throws IOException {
+ if (EduNames.PYCHARM_ADDITIONAL.equals(lesson.getName())) {
+ createAdditionalFiles(lesson, courseDir);
+ }
+ else {
+ String lessonDirName = EduNames.LESSON + Integer.toString(lesson.getIndex());
+ VirtualFile lessonDir = courseDir.createChildDirectory(courseDir, lessonDirName);
+ final List taskList = lesson.getTaskList();
+ for (int i = 1; i <= taskList.size(); i++) {
+ Task task = taskList.get(i - 1);
+ task.setIndex(i);
+ createTask(task, lessonDir);
+ }
+ }
+ }
+
+ public static void createTask(@NotNull final Task task, @NotNull final VirtualFile lessonDir) throws IOException {
+ VirtualFile taskDir = lessonDir.createChildDirectory(lessonDir, EduNames.TASK + Integer.toString(task.getIndex()));
+ int i = 0;
+ for (Map.Entry taskFile : task.getTaskFiles().entrySet()) {
+ TaskFile taskFileContent = taskFile.getValue();
+ taskFileContent.setIndex(i);
+ i++;
+ createTaskFile(taskDir, taskFile.getValue());
+ }
+ createTestFiles(taskDir, task);
+ createDescriptions(taskDir, task);
+ }
+
+ public static void createTaskFile(@NotNull final VirtualFile taskDir, @NotNull final TaskFile taskFile) throws IOException {
+ final String name = taskFile.name;
+ createChildFile(taskDir, name, taskFile.text);
+ }
+
+ private static void createDescriptions(VirtualFile taskDir, Task task) throws IOException {
+ final Map texts = task.getTaskTexts();
+ for (Map.Entry entry : texts.entrySet()) {
+ final String name = entry.getKey();
+ final VirtualFile virtualTaskFile = taskDir.createChildData(taskDir, name);
+ VfsUtil.saveText(virtualTaskFile, entry.getValue());
+ }
+ }
+
+ private static void createTestFiles(VirtualFile taskDir, Task task) throws IOException {
+ final Map tests = task.getTestsText();
+ for (Map.Entry entry : tests.entrySet()) {
+ final String name = entry.getKey();
+ final VirtualFile virtualTaskFile = taskDir.createChildData(taskDir, name);
+ VfsUtil.saveText(virtualTaskFile, entry.getValue());
+ }
+ }
+
+ private static void createAdditionalFiles(Lesson lesson, VirtualFile courseDir) throws IOException {
+ final List taskList = lesson.getTaskList();
+ if (taskList.size() != 1) return;
+ final Task task = taskList.get(0);
+ for (Map.Entry entry : task.getTestsText().entrySet()) {
+ createChildFile(courseDir, entry.getKey(), entry.getValue());
+ }
+ }
+
+
+ private static void createChildFile(@NotNull VirtualFile taskDir, String name, String text) throws IOException {
+ String newDirectories = null;
+ String fileName = name;
+ VirtualFile dir = taskDir;
+ if (name.contains("/")) {
+ int pos = name.lastIndexOf("/");
+ fileName = name.substring(pos + 1);
+ newDirectories = name.substring(0, pos);
+ }
+ if (newDirectories != null) {
+ dir = VfsUtil.createDirectoryIfMissing(taskDir, newDirectories);
+ }
+ if (dir != null) {
+ final VirtualFile virtualTaskFile = dir.createChildData(taskDir, fileName);
+ if (EduUtils.isImage(name)) {
+ virtualTaskFile.setBinaryContent(Base64.decodeBase64(text));
+ }
+ else {
+ VfsUtil.saveText(virtualTaskFile, text);
+ }
+ }
+ }
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java
index 84f63d113e7a..3bd94efe7e30 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java
@@ -1,62 +1,48 @@
package com.jetbrains.edu.learning.courseGeneration;
-import com.google.gson.*;
-import com.google.gson.stream.JsonReader;
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
import com.intellij.facet.ui.ValidationResult;
-import com.intellij.ide.projectView.ProjectView;
+import com.intellij.lang.LanguageExtensionPoint;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileManager;
-import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
-import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl;
-import com.intellij.platform.templates.github.ZipUtil;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.PsiManager;
import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.io.zip.JBZipEntry;
+import com.intellij.util.io.zip.JBZipFile;
+import com.jetbrains.edu.learning.EduPluginConfigurator;
import com.jetbrains.edu.learning.StudySerializationUtils;
import com.jetbrains.edu.learning.StudySettings;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
-import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
-import com.jetbrains.edu.learning.courseFormat.Lesson;
-import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
-import com.jetbrains.edu.learning.editor.StudyEditor;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.stepic.StepicUser;
-import org.apache.commons.codec.binary.Base64;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.*;
-import java.util.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import static com.jetbrains.edu.learning.StudyUtils.execCancelable;
public class StudyProjectGenerator {
- public static final String AUTHOR_ATTRIBUTE = "authors";
- public static final String LANGUAGE_ATTRIBUTE = "language";
- public static final String ADAPTIVE_COURSE_PREFIX = "__AdaptivePyCharmPython__";
- public static final File OUR_COURSES_DIR = new File(PathManager.getConfigPath(), "courses");
private static final Logger LOG = Logger.getInstance(StudyProjectGenerator.class.getName());
- private static final String COURSE_NAME_ATTRIBUTE = "name";
- private static final String COURSE_DESCRIPTION = "description";
- private static final String CACHE_NAME = "courseNames.txt";
private final List myListeners = ContainerUtil.newArrayList();
private List myCourses = new ArrayList<>();
+ private List myLocalCourses = new ArrayList<>();
private List myEnrolledCoursesIds = new ArrayList<>();
protected CourseInfo mySelectedCourseInfo;
@@ -97,39 +83,24 @@ public class StudyProjectGenerator {
Messages.showWarningDialog("There is no recommended tasks for this adaptive course", "Error in Course Creation");
return;
}
- final File courseDirectory = StudyUtils.getCourseDirectory(course);
StudyTaskManager.getInstance(project).setCourse(course);
ApplicationManager.getApplication().runWriteAction(() -> {
- StudyGenerator.createCourse(course, baseDir, courseDirectory, project);
- course.setCourseDirectory(courseDirectory.getAbsolutePath());
- VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
+ StudyGenerator.createCourse(course, baseDir);
StudyUtils.registerStudyToolWindow(course, project);
- openFirstTask(course, project);
+ StudyUtils.openFirstTask(course, project);
EduUsagesCollector.projectTypeCreated(course.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY);
});
}
@Nullable
public Course getCourse(@NotNull final Project project) {
-
- final File courseFile = new File(new File(OUR_COURSES_DIR, mySelectedCourseInfo.getName()), EduNames.COURSE_META_FILE);
- if (courseFile.exists()) {
- final Course course = readCourseFromCache(courseFile, false);
- if (course != null && course.isUpToDate()) {
+ final CourseInfo info = mySelectedCourseInfo;
+ for (Course course : myLocalCourses) {
+ if (course.getName().equals(info.getName())){
+ course.initCourse(false);
return course;
}
- return getCourseFromStepic(project);
- }
- else {
- final StepicUser user = StudySettings.getInstance().getUser();
- if (user != null) {
- final File adaptiveCourseFile = new File(new File(OUR_COURSES_DIR, ADAPTIVE_COURSE_PREFIX +
- mySelectedCourseInfo.getName() + "_" +
- user.getEmail()), EduNames.COURSE_META_FILE);
- if (adaptiveCourseFile.exists()) {
- return readCourseFromCache(adaptiveCourseFile, true);
- }
- }
+
}
return getCourseFromStepic(project);
}
@@ -140,7 +111,6 @@ public class StudyProjectGenerator {
return execCancelable(() -> {
final Course course = EduStepicConnector.getCourse(project, mySelectedCourseInfo);
if (StudyUtils.isCourseValid(course)) {
- flushCourse(course);
course.initCourse(false);
}
return course;
@@ -148,267 +118,12 @@ public class StudyProjectGenerator {
}, "Creating Course", true, project);
}
- @Nullable
- private static Course readCourseFromCache(@NotNull File courseFile, boolean isAdaptive) {
- Reader reader = null;
- try {
- reader = new InputStreamReader(new FileInputStream(courseFile), "UTF-8");
- Gson gson =
- new GsonBuilder().registerTypeAdapter(Course.class, new StudySerializationUtils.Json.CourseTypeAdapter(courseFile))
- .registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskDeserializer()).create();
- final Course course = gson.fromJson(reader, Course.class);
- course.initCourse(isAdaptive);
- return course;
- }
- catch (UnsupportedEncodingException | FileNotFoundException e) {
- LOG.warn(e.getMessage());
- }
- finally {
- StudyUtils.closeSilently(reader);
- }
- return null;
- }
-
- public static void openFirstTask(@NotNull final Course course, @NotNull final Project project) {
- LocalFileSystem.getInstance().refresh(false);
- final Lesson firstLesson = StudyUtils.getFirst(course.getLessons());
- if (firstLesson == null) return;
- final Task firstTask = StudyUtils.getFirst(firstLesson.getTaskList());
- if (firstTask == null) return;
- final VirtualFile taskDir = firstTask.getTaskDir(project);
- if (taskDir == null) return;
- final Map taskFiles = firstTask.getTaskFiles();
- VirtualFile activeVirtualFile = null;
- for (Map.Entry entry : taskFiles.entrySet()) {
- final String relativePath = entry.getKey();
- final TaskFile taskFile = entry.getValue();
- taskDir.refresh(false, true);
- final VirtualFile virtualFile = taskDir.findFileByRelativePath(relativePath);
- if (virtualFile != null) {
- if (!taskFile.getActivePlaceholders().isEmpty()) {
- activeVirtualFile = virtualFile;
- }
- }
- }
- if (activeVirtualFile != null) {
- final PsiFile file = PsiManager.getInstance(project).findFile(activeVirtualFile);
- ProjectView.getInstance(project).select(file, activeVirtualFile, false);
- final FileEditor[] editors = FileEditorManager.getInstance(project).openFile(activeVirtualFile, true);
- if (editors.length == 0) {
- return;
- }
- final FileEditor studyEditor = editors[0];
- if (studyEditor instanceof StudyEditor) {
- StudyUtils.selectFirstAnswerPlaceholder((StudyEditor)studyEditor, project);
- }
- FileEditorManager.getInstance(project).openFile(activeVirtualFile, true);
- }
- else {
- String first = StudyUtils.getFirst(taskFiles.keySet());
- if (first != null) {
- NewVirtualFile firstFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(first);
- if (firstFile != null) {
- FileEditorManager.getInstance(project).openFile(firstFile, true);
- }
- }
- }
- }
-
- public static void flushCourse(@NotNull final Course course) {
- final File courseDirectory = StudyUtils.getCourseDirectory(course);
- FileUtil.createDirectory(courseDirectory);
- flushCourseJson(course, courseDirectory);
-
- int lessonIndex = 1;
- List additionalLessons = new ArrayList<>();
- for (Lesson lesson : course.getLessons()) {
- if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) {
- flushAdditionalFiles(courseDirectory, lesson);
- additionalLessons.add(lesson);
- }
- else {
- final File lessonDirectory = new File(courseDirectory, EduNames.LESSON + String.valueOf(lessonIndex));
- flushLesson(lessonDirectory, lesson);
- lessonIndex += 1;
- }
- }
- for (Lesson lesson : additionalLessons) {
- course.getLessons().remove(lesson);
- }
- }
-
- private static void flushAdditionalFiles(File courseDirectory, Lesson lesson) {
- final List taskList = lesson.getTaskList();
- if (taskList.size() != 1) return;
- final Task task = taskList.get(0);
- for (Map.Entry entry : task.getTestsText().entrySet()) {
- final String name = entry.getKey();
- final String text = entry.getValue();
- final File file = new File(courseDirectory, name);
- FileUtil.createIfDoesntExist(file);
- try {
- if (EduUtils.isImage(name)) {
- FileUtil.writeToFile(file, Base64.decodeBase64(text));
- }
- else {
- FileUtil.writeToFile(file, text);
- }
- }
- catch (IOException e) {
- LOG.error("ERROR copying file " + name);
- }
- }
- }
-
- public static void flushLesson(@NotNull final File lessonDirectory, @NotNull final Lesson lesson) {
- FileUtil.createDirectory(lessonDirectory);
- int taskIndex = 1;
- for (Task task : lesson.taskList) {
- final File taskDirectory = new File(lessonDirectory, EduNames.TASK + String.valueOf(taskIndex));
- flushTask(task, taskDirectory);
- taskIndex += 1;
- }
- }
-
- public static void flushTask(@NotNull final Task task, @NotNull final File taskDirectory) {
- FileUtil.createDirectory(taskDirectory);
- for (Map.Entry taskFileEntry : task.taskFiles.entrySet()) {
- final String name = FileUtil.toSystemDependentName(taskFileEntry.getKey());
- final TaskFile taskFile = taskFileEntry.getValue();
- final File file = new File(taskDirectory, name);
- FileUtil.createIfDoesntExist(file);
-
- try {
- if (EduUtils.isImage(taskFile.name)) {
- FileUtil.writeToFile(file, Base64.decodeBase64(taskFile.text));
- }
- else {
- FileUtil.writeToFile(file, taskFile.text);
- }
- }
- catch (IOException e) {
- LOG.error(e);
- }
- }
- createFiles(taskDirectory, task.getTestsText());
- createFiles(taskDirectory, task.getTaskTexts());
- }
-
- public static void createFiles(@NotNull File taskDirectory, Map files) {
- for (Map.Entry entry : files.entrySet()) {
- final File file = new File(taskDirectory, entry.getKey());
- if (file.exists()) {
- FileUtil.delete(file);
- }
- FileUtil.createIfDoesntExist(file);
- try {
- FileUtil.writeToFile(file, entry.getValue());
- }
- catch (IOException e) {
- LOG.error(e);
- }
- }
- }
-
- public static void flushCourseJson(@NotNull final Course course, @NotNull final File courseDirectory) {
- final Gson gson = new GsonBuilder().setPrettyPrinting().
- registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskSerializer()).
- excludeFieldsWithoutExposeAnnotation().create();
- final String json = gson.toJson(course);
- final File courseJson = new File(courseDirectory, EduNames.COURSE_META_FILE);
- final FileOutputStream fileOutputStream;
- try {
- fileOutputStream = new FileOutputStream(courseJson);
- OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
- try {
- outputStreamWriter.write(json);
- }
- catch (IOException e) {
- Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
- LOG.info(e);
- }
- finally {
- try {
- outputStreamWriter.close();
- }
- catch (IOException e) {
- LOG.info(e);
- }
- }
- }
- catch (FileNotFoundException | UnsupportedEncodingException e) {
- LOG.info(e);
- }
- }
-
- /**
- * Writes courses to cache file {@link StudyProjectGenerator#CACHE_NAME}
- */
- @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
- public static void flushCache(List courses) {
- flushCache(courses, true);
- }
-
- public static void flushCache(List courses, boolean preserveOld) {
- File cacheFile = new File(OUR_COURSES_DIR, CACHE_NAME);
- PrintWriter writer = null;
- try {
- if (!createCacheFile(cacheFile)) return;
- Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
-
- final Set courseInfos = new HashSet<>();
- courseInfos.addAll(courses);
- if (preserveOld) {
- courseInfos.addAll(getCoursesFromCache());
- }
-
- writer = new PrintWriter(cacheFile, "UTF-8");
- try {
- for (CourseInfo courseInfo : courseInfos) {
- final String json = gson.toJson(courseInfo);
- writer.println(json);
- }
- }
- finally {
- StudyUtils.closeSilently(writer);
- }
- }
- catch (IOException e) {
- LOG.error(e);
- }
- finally {
- StudyUtils.closeSilently(writer);
- }
- }
-
- private static boolean createCacheFile(File cacheFile) throws IOException {
- if (!OUR_COURSES_DIR.exists()) {
- final boolean created = OUR_COURSES_DIR.mkdirs();
- if (!created) {
- LOG.error("Cannot flush courses cache. Can't create courses directory");
- return false;
- }
- }
- if (!cacheFile.exists()) {
- final boolean created = cacheFile.createNewFile();
- if (!created) {
- LOG.error("Cannot flush courses cache. Can't create " + CACHE_NAME + " file");
- return false;
- }
- }
- return true;
- }
-
// Supposed to be called under progress
public List getCourses(boolean force) {
- if (OUR_COURSES_DIR.exists() && !force) {
- myCourses = getCoursesFromCache();
+ if (force) {
+ myCourses = execCancelable(() -> EduStepicConnector.getCourses(StepicUpdateSettings.getInstance().getUser()));
}
- if (force || myCourses.isEmpty()) {
- myCourses = execCancelable(() -> EduStepicConnector.getCourses(StudySettings.getInstance().getUser()));
- flushCache(myCourses);
- }
- if (myCourses.isEmpty() || (myCourses.size() == 1 && myCourses.contains(CourseInfo.INVALID_COURSE))) {
+ if (force || myCourses == null || myCourses.isEmpty() || (myCourses.size() == 1 && myCourses.contains(CourseInfo.INVALID_COURSE))) {
myCourses = Collections.singletonList(getBundledIntro());
}
sortCourses(myCourses);
@@ -461,78 +176,45 @@ public class StudyProjectGenerator {
}
}
- public static CourseInfo getBundledIntro() {
- final File introCourse = new File(OUR_COURSES_DIR, "Introduction to Python");
- if (introCourse.exists()) {
- return getCourseInfo(introCourse);
+ @Nullable
+ public CourseInfo getBundledIntro() {
+ final LanguageExtensionPoint[] extensions = Extensions.getExtensions(EduPluginConfigurator.EP_NAME, null);
+ for (LanguageExtensionPoint extension : extensions) {
+ final EduPluginConfigurator configurator = extension.getInstance();
+ final String path = configurator.getBundledCoursePath();
+ if (path != null) {
+ return getCourseInfo(path);
+ }
}
return null;
}
- public static List getCoursesFromCache() {
- List courses = new ArrayList<>();
- final File cacheFile = new File(OUR_COURSES_DIR, CACHE_NAME);
- if (!cacheFile.exists()) {
- return courses;
- }
- try {
- final FileInputStream inputStream = new FileInputStream(cacheFile);
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
- try {
- String line;
- while ((line = reader.readLine()) != null) {
- Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
- final CourseInfo courseInfo = gson.fromJson(line, CourseInfo.class);
- courses.add(courseInfo);
- }
- }
- catch (IOException | JsonSyntaxException e) {
- LOG.error(e.getMessage());
- }
- finally {
- StudyUtils.closeSilently(reader);
- }
- }
- catch (UnsupportedEncodingException e) {
- LOG.error(e.getMessage());
- }
- finally {
- StudyUtils.closeSilently(inputStream);
- }
- }
- catch (FileNotFoundException e) {
- LOG.error(e.getMessage());
- }
- return courses;
- }
-
- /**
- * Adds course from zip archive to courses
- *
- * @return added course name or null if course is invalid
- */
@Nullable
public CourseInfo addLocalCourse(String zipFilePath) {
- File file = new File(zipFilePath);
+ final CourseInfo courseInfo = getCourseInfo(zipFilePath);
+ if (courseInfo != null) {
+ myCourses.add(0, courseInfo);
+ }
+ return courseInfo;
+ }
+
+ @Nullable
+ public CourseInfo getCourseInfo(String zipFilePath) {
try {
- String fileName = file.getName();
- String unzippedName = fileName.substring(0, fileName.indexOf("."));
- File courseDir = new File(OUR_COURSES_DIR, unzippedName);
- ZipUtil.unzip(null, courseDir, file, null, null, true);
- CourseInfo courseName = addCourse(myCourses, courseDir);
- flushCache(myCourses);
- if (courseName != null && !courseName.getName().equals(unzippedName)) {
- //noinspection ResultOfMethodCallIgnored
- File dest = new File(OUR_COURSES_DIR, courseName.getName());
- if (dest.exists()) {
- FileUtil.delete(dest);
- }
- courseDir.renameTo(dest);
- //noinspection ResultOfMethodCallIgnored
- courseDir.delete();
+ final JBZipFile zipFile = new JBZipFile(zipFilePath);
+ final JBZipEntry entry = zipFile.getEntry(EduNames.COURSE_META_FILE);
+ byte[] bytes = entry.getData();
+ final String jsonText = new String(bytes, CharsetToolkit.UTF8_CHARSET);
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Task.class, new StudySerializationUtils.Json.TaskDeserializer())
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .create();
+ Course course = gson.fromJson(jsonText, Course.class);
+ final CourseInfo courseInfo = CourseInfo.fromCourse(course);
+ if (courseInfo != null) {
+ myLocalCourses.add(course);
}
- return courseName;
+ return courseInfo;
}
catch (IOException e) {
LOG.error("Failed to unzip course archive");
@@ -540,80 +222,4 @@ public class StudyProjectGenerator {
}
return null;
}
-
- /**
- * Adds course to courses specified in params
- *
- * @param courses
- * @param courseDir must be directory containing course file
- * @return added course name or null if course is invalid
- */
- @Nullable
- private static CourseInfo addCourse(List courses, File courseDir) {
- if (courseDir.isDirectory()) {
- File[] courseFiles = courseDir.listFiles((dir, name) -> name.equals(EduNames.COURSE_META_FILE));
- if (courseFiles == null || courseFiles.length != 1) {
- LOG.info("User tried to add course with more than one or without course files");
- return null;
- }
- File courseFile = courseFiles[0];
- CourseInfo courseInfo = getCourseInfo(courseFile);
- if (courseInfo != null) {
- courses.add(0, courseInfo);
- }
- return courseInfo;
- }
- return null;
- }
-
- /**
- * Parses course json meta file and finds course name
- *
- * @return information about course or null if course file is invalid
- */
- @Nullable
- private static CourseInfo getCourseInfo(File courseFile) {
- if (courseFile.isDirectory()) {
- File[] courseFiles = courseFile.listFiles((dir, name) -> name.equals(EduNames.COURSE_META_FILE));
- if (courseFiles == null || courseFiles.length != 1) {
- LOG.info("More than one or without course files");
- return null;
- }
- courseFile = courseFiles[0];
- }
- CourseInfo courseInfo = null;
- BufferedReader reader = null;
- try {
- if (courseFile.getName().equals(EduNames.COURSE_META_FILE)) {
- reader = new BufferedReader(new InputStreamReader(new FileInputStream(courseFile), "UTF-8"));
- JsonReader r = new JsonReader(reader);
- JsonParser parser = new JsonParser();
- JsonElement el = parser.parse(r);
- String courseName = el.getAsJsonObject().get(COURSE_NAME_ATTRIBUTE).getAsString();
- String courseDescription = el.getAsJsonObject().get(COURSE_DESCRIPTION).getAsString();
- JsonArray courseAuthors = el.getAsJsonObject().get(AUTHOR_ATTRIBUTE).getAsJsonArray();
- String language = el.getAsJsonObject().get(LANGUAGE_ATTRIBUTE).getAsString();
- courseInfo = new CourseInfo();
- courseInfo.setName(courseName);
- courseInfo.setDescription(courseDescription);
- courseInfo.setType("pycharm " + language);
- final ArrayList authors = new ArrayList<>();
- for (JsonElement author : courseAuthors) {
- final JsonObject authorAsJsonObject = author.getAsJsonObject();
- final StepicUser stepicUser = StepicUser.createEmptyUser();
- stepicUser.setFirstName(authorAsJsonObject.get("first_name").getAsString());
- stepicUser.setLastName(authorAsJsonObject.get("last_name").getAsString());
- authors.add(stepicUser);
- }
- courseInfo.setAuthors(authors);
- }
- }
- catch (Exception e) {
- //error will be shown in UI
- }
- finally {
- StudyUtils.closeSilently(reader);
- }
- return courseInfo;
- }
-}
+}
\ No newline at end of file
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java
index fe98b766ea7a..6cac20d04ace 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java
@@ -8,8 +8,6 @@ import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import org.jetbrains.annotations.NotNull;
-import java.io.File;
-
public class EduProjectGenerator extends StudyProjectGenerator {
private static final Logger LOG = Logger.getInstance(EduProjectGenerator.class);
@@ -21,6 +19,5 @@ public class EduProjectGenerator extends StudyProjectGenerator {
return;
}
StudyTaskManager.getInstance(project).setCourse(course);
- course.setCourseDirectory(new File(OUR_COURSES_DIR, mySelectedCourseInfo.getName()).getAbsolutePath());
}
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java b/python/educational-core/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java
index f500a1ee179e..c4db5d3caf2b 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/statistics/EduUsagesCollector.java
@@ -23,7 +23,6 @@ import com.intellij.openapi.components.ServiceManager;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.Set;
@@ -31,7 +30,6 @@ public class EduUsagesCollector extends UsagesCollector {
private static final String GROUP_ID = "educational";
private final FactoryMap myUsageDescriptors = new FactoryMap() {
- @Nullable
@Override
protected UsageDescriptor create(String key) {
return new UsageDescriptor(key, 0);
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
index 1e159afd7aa3..5bda69ff6384 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java
@@ -7,16 +7,17 @@ import com.intellij.ide.projectView.ProjectView;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
-import com.jetbrains.edu.learning.StudySettings;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.checker.StudyExecutor;
@@ -27,7 +28,6 @@ import com.jetbrains.edu.learning.courseFormat.tasks.CodeTask;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask;
import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
-import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.editor.StudyEditor;
import com.jetbrains.edu.learning.navigation.StudyNavigator;
import com.jetbrains.edu.learning.ui.StudyToolWindow;
@@ -45,12 +45,14 @@ import org.apache.http.util.EntityUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getStep;
@@ -67,7 +69,6 @@ public class EduAdaptiveStepicConnector {
private static final String CODE_TASK_TYPE = "code";
private static final String CHOICE_TYPE_TEXT = "choice";
private static final String TEXT_STEP_TYPE = "text";
- private static final String DEFAULT_TASK_NAME = "code.py";
@Nullable
public static Task getNextRecommendation(@NotNull Project project, @NotNull Course course) {
@@ -78,7 +79,7 @@ public class EduAdaptiveStepicConnector {
return null;
}
- StepicUser user = StudySettings.getInstance().getUser();
+ StepicUser user = StepicUpdateSettings.getInstance().getUser();
if (user == null) {
LOG.warn("User is null");
return null;
@@ -128,7 +129,16 @@ public class EduAdaptiveStepicConnector {
}
catch (IOException e) {
LOG.warn(e.getMessage());
- ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, "Connection problems, Please, try again"));
+
+ final String connectionMessages = "Connection problems, Please, try again";
+ final Balloon balloon =
+ JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(connectionMessages, MessageType.ERROR, null)
+ .createBalloon();
+ ApplicationManager.getApplication().invokeLater(() -> {
+ if (StudyUtils.getSelectedEditor(project) != null) {
+ StudyUtils.showCheckPopUp(project, balloon);
+ }
+ });
}
catch (URISyntaxException e) {
LOG.warn(e.getMessage());
@@ -194,8 +204,8 @@ public class EduAdaptiveStepicConnector {
private static void createMockTaskFile(@NotNull Task task, String editorText) {
final TaskFile taskFile = new TaskFile();
taskFile.text = editorText;
- taskFile.name = "code";
- task.taskFiles.put(DEFAULT_TASK_NAME, taskFile);
+ taskFile.name = "code.py";
+ task.taskFiles.put("code.py", taskFile);
}
@Nullable
@@ -252,7 +262,9 @@ public class EduAdaptiveStepicConnector {
request.setConfig(requestConfig);
}
- public static boolean postRecommendationReaction(@NotNull String lessonId, @NotNull String user, int reaction) {
+ public static boolean postRecommendationReaction(@NotNull String lessonId,
+ @NotNull String user,
+ int reaction) {
final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.RECOMMENDATION_REACTIONS_URL);
final String json = new Gson()
.toJson(new StepicWrappers.RecommendationReactionWrapper(new StepicWrappers.RecommendationReaction(reaction, user, lessonId)));
@@ -276,100 +288,74 @@ public class EduAdaptiveStepicConnector {
}
catch (IOException e) {
LOG.warn(e.getMessage());
- return false;
}
+ return false;
}
- public static void addNextRecommendedTask(@NotNull Project project, @NotNull ProgressIndicator indicator, int reactionToPost) {
+ public static void addNextRecommendedTask(@NotNull Project project,
+ @NotNull ProgressIndicator indicator,
+ int reaction) {
final StudyEditor editor = StudyUtils.getSelectedStudyEditor(project);
final Course course = StudyTaskManager.getInstance(project).getCourse();
- if (course != null && editor != null && editor.getTaskFile() != null) {
- indicator.checkCanceled();
- final StepicUser user = StudySettings.getInstance().getUser();
- if (user == null) {
- LOG.warn("Can't get next recommendation: user is null");
- ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project,
- "Can't get next recommendation: you're not authorized"));
- return;
- }
-
- final Lesson adaptive = editor.getTaskFile().getTask().getLesson();
- final boolean reactionPosted = postRecommendationReaction(String.valueOf(adaptive.getId()), String.valueOf(user.getId()), reactionToPost);
-
- if (!reactionPosted) {
- LOG.warn("Recommendation reaction wasn't posted");
- ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, "Couldn't post your reactionToPost"));
- return;
- }
-
+ if (course == null || editor == null || editor.getTaskFile() == null) {
+ return;
+ }
+ indicator.checkCanceled();
+ final StepicUser user = StepicUpdateSettings.getInstance().getUser();
+ if (user == null) {
+ LOG.warn("Can't get next recommendation: user is null");
+ ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project,
+ "Can't get next recommendation: you're not authorized"));
+ return;
+ }
+ final Lesson lesson = course.getLessons().get(0);
+ if (lesson == null) return;
+ final boolean recommendationReaction = postRecommendationReaction(String.valueOf(lesson.getId()),
+ String.valueOf(user.getId()), reaction);
+ if (recommendationReaction) {
indicator.checkCanceled();
final Task task = getNextRecommendation(project, course);
- if (task == null) {
- ApplicationManager
- .getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, "Couldn't load a new recommendation"));
- return;
- }
-
- task.initTask(adaptive, false);
- boolean replaceCurrentTask = reactionToPost == TOO_HARD_RECOMMENDATION_REACTION || reactionToPost == TOO_BORING_RECOMMENDATION_REACTION;
- if (replaceCurrentTask) {
- replaceCurrentTask(project, editor, task);
+ if (task != null) {
+ task.initTask(lesson, false);
+ if (reaction == TOO_HARD_RECOMMENDATION_REACTION || reaction == TOO_BORING_RECOMMENDATION_REACTION) {
+ replaceCurrentTask(project, task, lesson);
+ }
+ else {
+ addNewTask(project, course, task, lesson);
+ }
}
else {
- addAsNextTask(project, editor, task);
+ ApplicationManager.getApplication().invokeLater(() -> {
+ final Balloon balloon = JBPopupFactory.getInstance().
+ createHtmlTextBalloonBuilder("Couldn't load a new recommendation", MessageType.ERROR, null).createBalloon();
+ StudyUtils.showCheckPopUp(project, balloon);
+ });
}
-
ApplicationManager.getApplication().invokeLater(() -> {
VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
ProjectView.getInstance(project).refresh();
- StudyNavigator.navigateToTask(project, task);
});
}
+ else {
+ LOG.warn("Recommendation reactions weren't posted");
+ ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, "Couldn't post your reaction"));
+ }
}
- private static void replaceCurrentTask(@NotNull Project project, @NotNull StudyEditor editor, @NotNull Task task) {
- Course course = StudyTaskManager.getInstance(project).getCourse();
- assert course != null;
-
- final Lesson adaptive = editor.getTaskFile().getTask().getLesson();
- int taskIndex = adaptive.getTaskList().size();
-
- task.setLesson(adaptive);
- task.setIndex(taskIndex);
- adaptive.getTaskList().set(taskIndex - 1, task);
-
- copyTaskFileParameters(editor, task);
-
- final String lessonName = EduNames.LESSON + adaptive.getIndex();
- updateCache(course, task, lessonName);
- updateProjectFiles(project, task, lessonName, taskIndex);
- setToolWindowText(project, task);
- }
-
- private static void addAsNextTask(@NotNull Project project, @NotNull StudyEditor editor, @NotNull Task task) {
- Course course = StudyTaskManager.getInstance(project).getCourse();
- assert course != null;
-
- final Lesson adaptive = editor.getTaskFile().getTask().getLesson();
- adaptive.addTask(task);
- task.setIndex(adaptive.getTaskList().size());
- adaptive.initLesson(course, true);
-
- final String lessonName = EduNames.LESSON + adaptive.getIndex();
- updateCache(course, task, lessonName);
- createFilesForNewTask(project, task, lessonName, course.getCourseDirectory());
- }
-
- private static void createFilesForNewTask(@NotNull Project project,
- @NotNull Task task,
- @NotNull String lessonName,
- @NotNull String courseDirectory) {
+ private static void addNewTask(@NotNull Project project, Course course, Task task, Lesson lesson) {
+ final String lessonName = EduNames.LESSON + String.valueOf(lesson.getIndex());
+ lesson.addTask(task);
+ task.setIndex(lesson.getTaskList().size());
final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName);
+
if (lessonDir != null) {
ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> {
try {
- StudyGenerator.createTask(task, lessonDir, new File(courseDirectory, lessonName), project);
+ final String taskName = EduNames.TASK + String.valueOf(task.getIndex());
+ StudyGenerator.createTask(task, lessonDir);
+ lesson.initLesson(course, true);
+ StudyNavigator.navigateToTask(project, lessonName, taskName);
}
catch (IOException e) {
LOG.warn(e.getMessage());
@@ -378,56 +364,30 @@ public class EduAdaptiveStepicConnector {
}
}
- private static void copyTaskFileParameters(@NotNull StudyEditor editor, @NotNull Task task) {
- final Map taskFiles = task.getTaskFiles();
- if (taskFiles.size() == 1) {
- TaskFile newTaskFile = (TaskFile)taskFiles.values().toArray()[0];
- setTaskFileParameters(editor, task, newTaskFile);
- updateEditorText(editor, newTaskFile);
- }
- else {
- LOG.warn("Got task without unexpected number of task files: " + taskFiles.size());
- }
- }
+ private static void replaceCurrentTask(@NotNull Project project, Task task, Lesson lesson) {
+ final Task unsolvedTask = lesson.getTaskList().get(lesson.getTaskList().size() - 1);
+ final String lessonName = EduNames.LESSON + String.valueOf(lesson.getIndex());
+ task.setLesson(unsolvedTask.getLesson());
+ task.setIndex(unsolvedTask.getIndex());
+ lesson.getTaskList().set(lesson.getTaskList().size() - 1, task);
+ ApplicationManager.getApplication().invokeLater(() -> StudyNavigator.navigateToTask(project, task));
- private static void setToolWindowText(@NotNull Project project, @NotNull Task task) {
+ final String taskName = EduNames.TASK + String.valueOf(lesson.getTaskList().size());
+ final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName);
+
+ if (lessonDir != null) {
+ try {
+ StudyGenerator.createTask(task, lessonDir);
+ }
+ catch (IOException e) {
+ LOG.warn(e.getMessage());
+ }
+ }
final StudyToolWindow window = StudyUtils.getStudyToolWindow(project);
if (window != null) {
- window.setTaskText(StudyUtils.wrapTextToDisplayLatex(task.getTaskDescription()), task.getTaskDir(project), project);
+ window.setTaskText(StudyUtils.wrapTextToDisplayLatex(unsolvedTask.getTaskDescription()), unsolvedTask.getTaskDir(project), project);
}
- }
-
- private static void updateProjectFiles(@NotNull Project project, @NotNull Task task, @NotNull String lessonName, int taskIndex) {
- final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName);
- if (lessonDir != null) {
- final File taskResourceRoot = new File(lessonDir.getCanonicalPath(), EduNames.TASK + taskIndex);
- StudyProjectGenerator.createFiles(taskResourceRoot, task.getTaskTexts());
- StudyProjectGenerator.createFiles(taskResourceRoot, task.getTestsText());
- }
- }
-
- private static void updateCache(@NotNull Course course, @NotNull Task task, @NotNull String lessonName) {
- String taskName = EduNames.TASK + task.getIndex();
- final File lessonDirectory = new File(course.getCourseDirectory(), lessonName);
- final File taskDir = new File(lessonDirectory, taskName);
- StudyProjectGenerator.flushTask(task, taskDir);
- StudyProjectGenerator.flushCourseJson(course, new File(course.getCourseDirectory()));
- }
-
- private static void updateEditorText(@NotNull StudyEditor editor, @NotNull TaskFile newTaskFile) {
- ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> {
- final Document document = editor.getEditor().getDocument();
- document.setText(newTaskFile.text);
- }));
- }
-
- private static void setTaskFileParameters(@NotNull StudyEditor editor, @NotNull Task task, @NotNull TaskFile newTaskFile) {
- TaskFile currentTaskFile = editor.getTaskFile();
- currentTaskFile.text = newTaskFile.text;
- currentTaskFile.name = newTaskFile.name;
- currentTaskFile.setTask(task);
- task.getTaskFiles().clear();
- task.taskFiles.put(DEFAULT_TASK_NAME, currentTaskFile);
+ StudyNavigator.navigateToTask(project, lessonName, taskName);
}
@NotNull
@@ -482,7 +442,7 @@ public class EduAdaptiveStepicConnector {
taskFile.name = CODE_TASK_TYPE;
final String templateForTask = getCodeTemplateForTask(project, task, step.options.codeTemplates);
taskFile.text = templateForTask == null ? "# write your answer here \n" : templateForTask;
- task.taskFiles.put(DEFAULT_TASK_NAME, taskFile);
+ task.taskFiles.put("code.py", taskFile);
}
return task;
}
@@ -543,7 +503,6 @@ public class EduAdaptiveStepicConnector {
return answer;
}
- @NotNull
public static Pair checkCodeTask(@NotNull Project project, @NotNull Task task, @NotNull StepicUser user) {
int attemptId = -1;
try {
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicUpdater.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicUpdater.java
index ed8afb093f92..f511b8547d25 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicUpdater.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicUpdater.java
@@ -12,9 +12,10 @@ import com.intellij.util.Alarm;
import com.intellij.util.text.DateFormatUtil;
import com.jetbrains.edu.learning.StudySettings;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
-import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
public class EduStepicUpdater {
@@ -50,24 +51,27 @@ public class EduStepicUpdater {
ActionCallback callback = new ActionCallback();
ApplicationManager.getApplication().executeOnPooledThread(() -> {
final List courses = EduStepicConnector.getCourses(null);
- final List cachedCourses = StudyProjectGenerator.getCoursesFromCache();
- StudyProjectGenerator.flushCache(courses);
StudySettings.getInstance().setLastTimeChecked(System.currentTimeMillis());
- courses.removeAll(cachedCourses);
- if (!courses.isEmpty() && !cachedCourses.isEmpty()) {
+ if (!courses.isEmpty()) {
+ List updated = new ArrayList<>();
+ for (CourseInfo course : courses) {
+ if (course.getUpdateDate().after(new Date(StepicUpdateSettings.getInstance().getLastTimeChecked()))) {
+ updated.add(course);
+ }
+ }
+ if (updated.isEmpty()) return;
final String message;
final String title;
- if (courses.size() == 1) {
- message = courses.get(0).getName();
+ if (updated.size() == 1) {
+ message = updated.get(0).getName();
title = "New course available";
}
else {
title = "New courses available";
- message = StringUtil.join(courses, CourseInfo::getName, ", ");
+ message = StringUtil.join(updated, CourseInfo::getName, ", ");
}
- final Notification notification =
- new Notification("New.course", title, message, NotificationType.INFORMATION);
+ final Notification notification = new Notification("New.course", title, message, NotificationType.INFORMATION);
notification.notify(null);
}
});
@@ -79,8 +83,7 @@ public class EduStepicUpdater {
}
private static boolean checkNeeded() {
- final List courses = StudyProjectGenerator.getCoursesFromCache();
- long timeToNextCheck = StudySettings.getInstance().getLastTimeChecked() + CHECK_INTERVAL - System.currentTimeMillis();
- return courses.isEmpty() || timeToNextCheck <= 0;
+ long timeToNextCheck = StepicUpdateSettings.getInstance().getLastTimeChecked() + CHECK_INTERVAL - System.currentTimeMillis();
+ return timeToNextCheck <= 0;
}
}
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java
index b5a2065c3135..e3c20424e020 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java
@@ -5,8 +5,6 @@ import com.google.gson.annotations.SerializedName;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.learning.StudyUtils;
@@ -18,12 +16,10 @@ import com.jetbrains.edu.learning.courseFormat.Lesson;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
-import org.apache.commons.codec.binary.Base64;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
-import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
@@ -89,21 +85,11 @@ public class StepicWrappers {
}
String name = entry.getKey();
VirtualFile answerFile = taskDir.findFileByRelativePath(name);
- Pair pair = EduUtils.createStudentFile(StepicWrappers.class, project, answerFile, stepicDir, null, 0);
- if (pair == null) {
+ TaskFile studentTaskFile = EduUtils.createStudentFile(project, answerFile, null, 0);
+ if (studentTaskFile == null) {
return;
}
- VirtualFile virtualFile = pair.getFirst();
- TaskFile taskFile = pair.getSecond();
- try {
- InputStream stream = virtualFile.getInputStream();
- taskFile.text =
- EduUtils.isImage(name) ? Base64.encodeBase64URLSafeString(FileUtil.loadBytes(stream)) : FileUtil.loadTextAndClose(stream);
- }
- catch (IOException e) {
- LOG.error("Can't find file " + virtualFile.getPath());
- }
- source.files.add(taskFile);
+ source.files.add(studentTaskFile);
});
}
return source;
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java
index e236b5487fa3..ea53e5725f80 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java
@@ -290,7 +290,6 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
myGenerator.setCourses(courses);
myAvailableCourses = courses;
- StudyProjectGenerator.flushCache(myAvailableCourses, false);
}
private void addCoursesToCombobox(@NotNull List courses) {
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java
index 5a855a007786..9a2358a625a9 100644
--- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java
+++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java
@@ -324,7 +324,6 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D
int taskNum = 0;
int taskSolved = 0;
for (Lesson lesson : lessons) {
- if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) continue;
taskNum += lesson.getTaskList().size();
taskSolved += getSolvedTasks(lesson);
}
diff --git a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java
index 69d9b4eb311d..b327a074f6b6 100644
--- a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java
+++ b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java
@@ -81,7 +81,6 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase {
super.setUp();
Course course = new Course();
course.setName("test course");
- course.setCourseDirectory(getProject().getBasePath());
StudyTaskManager.getInstance(getProject()).setCourse(course);
Lesson lesson = new Lesson();
@@ -125,7 +124,6 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase {
}
taskFile.sortAnswerPlaceholders();
StudyUtils.drawAllAnswerPlaceholders(myFixture.getEditor(), taskFile);
- CCUtils.createResourceFile(file, StudyTaskManager.getInstance(getProject()).getCourse(), file.getParent());
return file;
}
diff --git a/python/educational-python/Edu-Python/resources/courses/Introduction to Python.zip b/python/educational-python/Edu-Python/resources/courses/Introduction to Python.zip
index 5542a7925fc0..a6293bc906a3 100644
Binary files a/python/educational-python/Edu-Python/resources/courses/Introduction to Python.zip and b/python/educational-python/Edu-Python/resources/courses/Introduction to Python.zip differ
diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java
index 08b023c7d613..558c12db1091 100644
--- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java
+++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java
@@ -33,10 +33,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
-import java.io.File;
-
-import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.OUR_COURSES_DIR;
-
public class PyCCProjectGenerator extends PythonProjectGenerator {
private static final Logger LOG = Logger.getInstance(PyCCProjectGenerator.class);
@@ -60,11 +56,7 @@ public class PyCCProjectGenerator extends PythonProjectGenerator StudyCheckUtils.flushWindows(myTask, myTaskDir));
return super.execute(executor, processStarter, patchers);
diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java
index eee62abd65a9..8a8d37ebc5b0 100644
--- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java
+++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java
@@ -14,12 +14,14 @@ import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiManager;
+import com.intellij.util.PathUtil;
import com.jetbrains.edu.coursecreator.settings.CCSettings;
import com.jetbrains.edu.learning.actions.StudyCheckAction;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
+import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -65,9 +67,8 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator {
}
@Override
- public boolean excludeFromArchive(@NotNull File pathname) {
- String name = pathname.getName();
- return name.contains("__pycache__") || name.contains(".pyc");
+ public boolean excludeFromArchive(@NotNull String name) {
+ return name.contains("__pycache__") || name.endsWith(".pyc");
}
@Override
@@ -126,4 +127,17 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator {
public StudyCheckAction getCheckAction() {
return new PyStudyCheckAction();
}
+
+ @Override
+ public String getBundledCoursePath() {
+ @NonNls String jarPath = PathUtil.getJarPathForClass(PyEduPluginConfigurator.class);
+ if (jarPath.endsWith(".jar")) {
+ final File jarFile = new File(jarPath);
+
+ File pluginBaseDir = jarFile.getParentFile();
+ return new File(new File(pluginBaseDir, "courses"), "Introduction to Python.zip").getPath();
+ }
+
+ return new File(new File(jarPath, "courses"), "Introduction to Python.zip").getPath();
+ }
}
diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java
index 443c80f20c5e..264596c26b46 100644
--- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java
+++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java
@@ -94,9 +94,9 @@ public class PyStudyCheckAction extends StudyCheckAction {
}
final Course course = myTaskManger.getCourse();
if (course != null && EduNames.STUDY.equals(course.getCourseMode())) {
- CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
- StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
- }));
+ CommandProcessor.getInstance().runUndoTransparentAction(
+ () -> ApplicationManager.getApplication().runWriteAction(
+ () -> StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project)));
}
}
final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java
index a9d7e496138f..7393a5f192ba 100644
--- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java
+++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java
@@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiManager;
import com.intellij.util.BooleanFunction;
+import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
@@ -125,7 +126,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator getCourses() {
- return myGenerator.getCoursesUnderProgress(false, "Getting Courses", ProjectManager.getInstance().getDefaultProject());
- }
-
public void setSelectedCourse(CourseInfo course) {
myGenerator.setSelectedCourse(course);
}
diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyInitialConfigurator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyInitialConfigurator.java
index 922fd3bc2f1b..dc230cb66239 100644
--- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyInitialConfigurator.java
+++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyInitialConfigurator.java
@@ -2,24 +2,18 @@ package com.jetbrains.edu.learning;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.ide.util.PropertiesComponent;
-import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.platform.templates.github.ZipUtil;
-import com.intellij.util.PathUtil;
import com.intellij.util.messages.MessageBus;
-import com.jetbrains.edu.learning.actions.PyStudyIntroductionCourseAction;
-import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import org.jetbrains.annotations.NonNls;
import java.io.File;
-import java.io.IOException;
@SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UtilityClassWithPublicConstructor"})
public class PyStudyInitialConfigurator {
- private static final Logger LOG = Logger.getInstance(PyStudyInitialConfigurator.class.getName());
- @NonNls private static final String CONFIGURED_V35 = "StudyPyCharm.InitialConfiguration35";
+ @NonNls private static final String CONFIGURED_V40 = "StudyPyCharm.InitialConfiguration40";
/**
* @noinspection UnusedParameters
@@ -29,42 +23,9 @@ public class PyStudyInitialConfigurator {
final PropertiesComponent propertiesComponent,
FileTypeManager fileTypeManager,
final ProjectManagerEx projectManager) {
- final File file = new File(getCoursesRoot(), "Introduction to Python.zip");
- if (!propertiesComponent.getBoolean(CONFIGURED_V35) && file.exists()) {
- try {
- File[] children = StudyProjectGenerator.OUR_COURSES_DIR.listFiles(
- (dir, name) -> name.equals(PyStudyIntroductionCourseAction.INTRODUCTION_TO_PYTHON));
- if (children != null) {
- for (File child : children) {
- FileUtil.delete(child);
- }
- }
- copyCourse(file, StudyProjectGenerator.OUR_COURSES_DIR);
- propertiesComponent.setValue(CONFIGURED_V35, "true");
- }
- catch (IOException e) {
- LOG.warn("Couldn't copy bundled courses " + e);
- }
+ if (!propertiesComponent.getBoolean(CONFIGURED_V40)) {
+ final File courses = new File(PathManager.getConfigPath(), "courses");
+ FileUtil.delete(courses);
}
}
-
- private static void copyCourse(File bundledCourse, File userCourseDir) throws IOException {
- String fileName = bundledCourse.getName();
- String unzippedName = fileName.substring(0, fileName.indexOf("."));
- File courseDir = new File(userCourseDir, unzippedName);
- ZipUtil.unzip(null, courseDir, bundledCourse, null, null, true);
- }
-
- private static File getCoursesRoot() {
- @NonNls String jarPath = PathUtil.getJarPathForClass(PyStudyInitialConfigurator.class);
- if (jarPath.endsWith(".jar")) {
- final File jarFile = new File(jarPath);
-
-
- File pluginBaseDir = jarFile.getParentFile();
- return new File(pluginBaseDir, "courses");
- }
-
- return new File(jarPath, "courses");
- }
}
diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyShowTutorial.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyShowTutorial.java
index 9adf0f45dfb5..0539ea134b28 100644
--- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyShowTutorial.java
+++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyShowTutorial.java
@@ -23,32 +23,25 @@ public class PyStudyShowTutorial extends AbstractProjectComponent {
@Override
public void projectOpened() {
- ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() {
- @Override
- public void run() {
- ApplicationManager.getApplication().runWriteAction(new DumbAwareRunnable() {
- @Override
- public void run() {
- if (PropertiesComponent.getInstance().getBoolean(ourShowPopup, true)) {
- final String content = "If you'd like to learn more about PyCharm Edu, " +
- "click here to watch a tutorial";
- final Notification notification = new Notification("Watch Tutorials!", "", content, NotificationType.INFORMATION,
- new NotificationListener.UrlOpeningListener(true));
- Notifications.Bus.notify(notification);
- Balloon balloon = notification.getBalloon();
- if (balloon != null) {
- balloon.addListener(new JBPopupAdapter() {
- @Override
- public void onClosed(LightweightWindowEvent event) {
- notification.expire();
- }
- });
+ ApplicationManager.getApplication().invokeLater((DumbAwareRunnable)() -> ApplicationManager.getApplication().runWriteAction(
+ (DumbAwareRunnable)() -> {
+ if (PropertiesComponent.getInstance().getBoolean(ourShowPopup, true)) {
+ final String content = "If you'd like to learn more about PyCharm Edu, " +
+ "click here to watch a tutorial";
+ final Notification notification = new Notification("Watch Tutorials!", "", content, NotificationType.INFORMATION,
+ new NotificationListener.UrlOpeningListener(true));
+ Notifications.Bus.notify(notification);
+ Balloon balloon = notification.getBalloon();
+ if (balloon != null) {
+ balloon.addListener(new JBPopupAdapter() {
+ @Override
+ public void onClosed(LightweightWindowEvent event) {
+ notification.expire();
}
- notification.whenExpired(() -> PropertiesComponent.getInstance().setValue(ourShowPopup, false, true));
- }
+ });
}
- });
- }
- });
+ notification.whenExpired(() -> PropertiesComponent.getInstance().setValue(ourShowPopup, false, true));
+ }
+ }));
}
}
diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java
index a24db818717f..6720691d37f6 100644
--- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java
+++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyTestRunner.java
@@ -10,7 +10,6 @@ import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.learning.checker.StudyTestRunner;
import com.jetbrains.edu.learning.core.EduNames;
-import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
import com.jetbrains.python.sdk.PythonSdkType;
@@ -28,7 +27,6 @@ public class PyStudyTestRunner extends StudyTestRunner {
public Process createCheckProcess(@NotNull final Project project, @NotNull final String executablePath) throws ExecutionException {
final Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
- Course course = myTask.getLesson().getCourse();
PyEduPluginConfigurator configurator = new PyEduPluginConfigurator();
String testsFileName = configurator.getTestFileName();
if (myTask instanceof TaskWithSubtasks) {
@@ -50,8 +48,6 @@ public class PyStudyTestRunner extends StudyTestRunner {
if (pythonPath != null) {
commandLine.setExePath(pythonPath);
commandLine.addParameter(testRunner.getPath());
- File resourceFile = new File(course.getCourseDirectory());
- commandLine.addParameter(resourceFile.getPath());
commandLine.addParameter(FileUtil.toSystemDependentName(executablePath));
return commandLine.createProcess();
}
diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java
index 1d44f500627d..578bb361df37 100644
--- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java
+++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java
@@ -19,9 +19,10 @@ import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
+import com.jetbrains.edu.learning.EduPluginConfigurator;
import com.jetbrains.edu.learning.PyStudyDirectoryProjectGenerator;
-import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.courseFormat.CourseInfo;
+import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.newProject.steps.ProjectSpecificSettingsStep;
import com.jetbrains.python.newProject.steps.PythonGenerateProjectCallback;
import icons.InteractiveLearningPythonIcons;
@@ -44,7 +45,8 @@ public class PyStudyIntroductionCourseAction extends AnAction {
if (projectDir.exists()) {
return;
}
- if (StudyProjectGenerator.getBundledIntro() != null) {
+ final EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(PythonLanguage.getInstance());
+ if (configurator.getBundledCoursePath() != null) {
return;
}
Presentation presentation = e.getPresentation();
@@ -60,7 +62,9 @@ public class PyStudyIntroductionCourseAction extends AnAction {
}
else {
final PyStudyDirectoryProjectGenerator generator = new PyStudyDirectoryProjectGenerator();
- CourseInfo introCourse = StudyProjectGenerator.getBundledIntro();
+ final EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(PythonLanguage.getInstance());
+ final String bundledCoursePath = configurator.getBundledCoursePath();
+ CourseInfo introCourse = generator.getGenerator().getCourseInfo(bundledCoursePath);
if (introCourse == null) {
return;
}
diff --git a/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft b/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft
index 6a3be496eb18..42bdaa245c70 100644
--- a/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft
+++ b/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft
@@ -120,39 +120,6 @@ def test_is_not_empty():
failed("The file is empty. Please, reload the task and try again.")
-def test_is_initial_text(error_text="You should modify the file"):
- """
- Checks that file was modified
- """
- if not get_answer_placeholders():
- passed()
- return
- path = sys.argv[-1]
- text = get_initial_text(path)
- file_text = get_file_text(path)
-
- if file_text.strip() == text.strip():
- failed(error_text)
- else:
- passed()
-
-
-def get_initial_text(path):
- """
- Returns the initial task text
- """
- course_lib = sys.argv[-2]
- test_dir = sys.argv[-3]
- import os
- lesson_task = os.sep.join(test_dir.split(os.sep)[-3:-1])
- task_dir = os.sep.join(test_dir.split(os.sep)[:-1])
- relative_path = lesson_task + os.sep + path[len(task_dir):]
-
- # path format is "project_root/lessonX/taskY/.../file.py"
- initial_file_path = os.path.join(course_lib, relative_path)
- return get_file_text(initial_file_path)
-
-
def test_text_equals(text, error_text):
"""
Checks that answer equals text.
@@ -259,7 +226,6 @@ def do_not_run_on_check():
def run_common_tests(error_text="Please, reload file and try again"):
- test_is_initial_text()
test_is_not_empty()
test_answer_placeholders_text_deleted()
test_file_importable()
diff --git a/python/educational-python/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java b/python/educational-python/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java
index 8a5292e57cc9..ffc8c6372f42 100644
--- a/python/educational-python/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java
+++ b/python/educational-python/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java
@@ -71,7 +71,7 @@ public class PyExecuteFileLineMarkerProvider implements LineMarkerProvider {
return PlatformUtils.isPyCharmEducational() ? text : actions.get(0).getTemplatePresentation().getText();
}, null,
GutterIconRenderer.Alignment.RIGHT) {
- @Nullable
+
@Override
public GutterIconRenderer createGutterRenderer() {
return new LineMarkerGutterIconRenderer(this) {
diff --git a/python/educational-python/src/com/jetbrains/python/edu/debugger/PyEduConsoleInputFilterProvider.java b/python/educational-python/src/com/jetbrains/python/edu/debugger/PyEduConsoleInputFilterProvider.java
index 1f191c949548..1e348cf533eb 100644
--- a/python/educational-python/src/com/jetbrains/python/edu/debugger/PyEduConsoleInputFilterProvider.java
+++ b/python/educational-python/src/com/jetbrains/python/edu/debugger/PyEduConsoleInputFilterProvider.java
@@ -7,7 +7,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.jetbrains.python.debugger.PyRunCythonExtensionsFilter;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
@@ -17,7 +16,6 @@ public class PyEduConsoleInputFilterProvider implements ConsoleInputFilterProvid
@Override
public InputFilter[] getDefaultFilters(@NotNull Project project) {
return new InputFilter[]{new InputFilter() {
- @Nullable
@Override
public List> applyFilter(String text, ConsoleViewContentType outputType) {
if (outputType.equals(ConsoleViewContentType.SYSTEM_OUTPUT) && !text.contains("exit code")) {