1 package com.jetbrains.edu.coursecreator.actions;
3 import com.google.gson.Gson;
4 import com.google.gson.GsonBuilder;
5 import com.intellij.ide.projectView.ProjectView;
6 import com.intellij.openapi.actionSystem.AnActionEvent;
7 import com.intellij.openapi.actionSystem.CommonDataKeys;
8 import com.intellij.openapi.actionSystem.LangDataKeys;
9 import com.intellij.openapi.actionSystem.Presentation;
10 import com.intellij.openapi.application.ApplicationManager;
11 import com.intellij.openapi.diagnostic.Logger;
12 import com.intellij.openapi.module.Module;
13 import com.intellij.openapi.project.DumbAwareAction;
14 import com.intellij.openapi.project.Project;
15 import com.intellij.openapi.ui.DialogWrapper;
16 import com.intellij.openapi.ui.Messages;
17 import com.intellij.openapi.util.Condition;
18 import com.intellij.openapi.util.io.FileUtil;
19 import com.intellij.openapi.vfs.VfsUtil;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.openapi.vfs.VirtualFileManager;
22 import com.intellij.util.containers.ContainerUtil;
23 import com.intellij.util.io.ZipUtil;
24 import com.jetbrains.edu.coursecreator.CCLanguageManager;
25 import com.jetbrains.edu.coursecreator.CCUtils;
26 import com.jetbrains.edu.coursecreator.ui.CreateCourseArchiveDialog;
27 import com.jetbrains.edu.learning.StudyTaskManager;
28 import com.jetbrains.edu.learning.core.EduNames;
29 import com.jetbrains.edu.learning.core.EduUtils;
30 import com.jetbrains.edu.learning.courseFormat.Course;
31 import com.jetbrains.edu.learning.courseFormat.Lesson;
32 import com.jetbrains.edu.learning.courseFormat.Task;
33 import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
34 import org.jetbrains.annotations.NotNull;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.zip.ZipOutputStream;
41 public class CCCreateCourseArchive extends DumbAwareAction {
42 private static final Logger LOG = Logger.getInstance(CCCreateCourseArchive.class.getName());
43 public static final String GENERATE_COURSE_ARCHIVE = "Generate Course Archive";
44 private String myZipName;
45 private String myLocationDir;
47 public void setZipName(String zipName) {
51 public void setLocationDir(String locationDir) {
52 myLocationDir = locationDir;
55 public CCCreateCourseArchive() {
56 super(GENERATE_COURSE_ARCHIVE, GENERATE_COURSE_ARCHIVE, null);
60 public void update(@NotNull AnActionEvent e) {
61 Presentation presentation = e.getPresentation();
62 Project project = e.getProject();
63 presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project));
67 public void actionPerformed(@NotNull AnActionEvent e) {
68 final Project project = e.getData(CommonDataKeys.PROJECT);
69 final Module module = e.getData(LangDataKeys.MODULE);
70 if (project == null || module == null) {
73 CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this);
75 if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
78 createCourseArchive(project, module, myZipName, myLocationDir, true);
79 EduUsagesCollector.createdCourseArchive();
82 public static void createCourseArchive(final Project project, Module module, String zipName, String locationDir, boolean showMessage) {
83 final Course course = StudyTaskManager.getInstance(project).getCourse();
84 if (course == null) return;
85 final VirtualFile baseDir = project.getBaseDir();
86 VirtualFile archiveFolder = CCUtils.generateFolder(project, module, zipName);
87 if (archiveFolder == null) {
91 CCLanguageManager manager = CCUtils.getStudyLanguageManager(course);
92 if (manager == null) {
95 FileFilter filter = pathname -> !manager.doNotPackFile(pathname);
97 for (VirtualFile child : baseDir.getChildren()) {
98 String name = child.getName();
99 File fromFile = new File(child.getPath());
100 if (CCUtils.GENERATED_FILES_FOLDER.equals(name) || ".idea".equals(name)
101 || name.contains("iml") || manager.doNotPackFile(fromFile)) {
104 copyChild(archiveFolder, filter, child, fromFile);
107 ApplicationManager.getApplication().runWriteAction(new Runnable() {
110 archiveFolder.refresh(false, true);
111 Course courseCopy = course.copy();
112 replaceAnswerFilesWithTaskFiles(courseCopy);
113 generateJson(archiveFolder, courseCopy);
114 VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
115 packCourse(archiveFolder, locationDir, zipName, showMessage);
116 synchronize(project);
119 private void replaceAnswerFilesWithTaskFiles(Course courseCopy) {
120 for (Lesson lesson : courseCopy.getLessons()) {
121 String lessonDirName = EduNames.LESSON + String.valueOf(lesson.getIndex());
122 final VirtualFile lessonDir = baseDir.findChild(lessonDirName);
123 if (lessonDir == null) continue;
124 for (Task task : lesson.getTaskList()) {
125 final VirtualFile taskDir = task.getTaskDir(project);
126 if (taskDir == null) continue;
127 String taskDirName = EduNames.TASK + String.valueOf(task.getIndex());
128 VirtualFile studentFileDir = VfsUtil.findRelativeFile(archiveFolder, lessonDirName, taskDirName);
129 if (studentFileDir == null) {
132 VirtualFile srcDir = studentFileDir.findChild(EduNames.SRC);
133 if (srcDir != null) {
134 studentFileDir = srcDir;
136 if (task.hasSubtasks()) {
137 transformSubtaskTestsToTextFiles(studentFileDir);
139 for (String taskFile : task.getTaskFiles().keySet()) {
140 VirtualFile answerFile = taskDir.findChild(taskFile);
141 if (answerFile == null) {
144 EduUtils.createStudentFile(this, project, answerFile, studentFileDir, task, 0);
150 private void transformSubtaskTestsToTextFiles(VirtualFile studentFileDir) {
151 Condition<VirtualFile> isSubtaskTestFile =
152 file -> CCUtils.isTestsFile(project, file) && file.getName().contains(EduNames.SUBTASK_MARKER);
153 List<VirtualFile> subtaskTests = ContainerUtil.filter(Arrays.asList(studentFileDir.getChildren()), isSubtaskTestFile);
154 for (VirtualFile subtaskTest : subtaskTests) {
156 subtaskTest.rename(this, subtaskTest.getNameWithoutExtension() + ".txt");
158 catch (IOException e) {
166 private static void copyChild(VirtualFile archiveFolder, FileFilter filter, VirtualFile child, File fromFile) {
167 File toFile = new File(archiveFolder.getPath(), child.getName());
170 if (child.isDirectory()) {
171 FileUtil.copyDir(fromFile, toFile, filter);
174 if (filter.accept(fromFile)) {
175 FileUtil.copy(fromFile, toFile);
179 catch (IOException e) {
180 LOG.info("Failed to copy" + fromFile.getPath(), e);
184 private static void synchronize(@NotNull final Project project) {
185 VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
186 ProjectView.getInstance(project).refresh();
189 private static void packCourse(@NotNull final VirtualFile baseDir, String locationDir, String zipName, boolean showMessage) {
191 final File zipFile = new File(locationDir, zipName + ".zip");
192 ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
193 VirtualFile[] courseFiles = baseDir.getChildren();
194 for (VirtualFile file : courseFiles) {
195 ZipUtil.addFileOrDirRecursively(zos, null, new File(file.getPath()), file.getName(), null, null);
199 Messages.showInfoMessage("Course archive was saved to " + zipFile.getPath(), "Course Archive Was Created Successfully");
202 catch (IOException e1) {
207 @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
208 private static void generateJson(VirtualFile parentDir, Course course) {
209 final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
210 final String json = gson.toJson(course);
211 final File courseJson = new File(parentDir.getPath(), EduNames.COURSE_META_FILE);
212 OutputStreamWriter outputStreamWriter = null;
214 outputStreamWriter = new OutputStreamWriter(new FileOutputStream(courseJson), "UTF-8");
215 outputStreamWriter.write(json);
217 catch (Exception e) {
218 Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
223 if (outputStreamWriter != null) {
224 outputStreamWriter.close();
227 catch (IOException e1) {