c8efdd55af3b4d1fa3c42d6219ab7966dd48be0a
[idea/community.git] / python / educational-core / course-creator / src / com / jetbrains / edu / coursecreator / actions / CCCreateCourseArchive.java
1 package com.jetbrains.edu.coursecreator.actions;
2
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;
35
36 import java.io.*;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.zip.ZipOutputStream;
40
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;
46
47   public void setZipName(String zipName) {
48     myZipName = zipName;
49   }
50
51   public void setLocationDir(String locationDir) {
52     myLocationDir = locationDir;
53   }
54
55   public CCCreateCourseArchive() {
56     super(GENERATE_COURSE_ARCHIVE, GENERATE_COURSE_ARCHIVE, null);
57   }
58
59   @Override
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));
64   }
65
66   @Override
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) {
71       return;
72     }
73     CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this);
74     dlg.show();
75     if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
76       return;
77     }
78     createCourseArchive(project, module, myZipName, myLocationDir, true);
79     EduUsagesCollector.createdCourseArchive();
80   }
81
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) {
88       return;
89     }
90
91     CCLanguageManager manager = CCUtils.getStudyLanguageManager(course);
92     if (manager == null) {
93       return;
94     }
95     FileFilter filter = pathname -> !manager.doNotPackFile(pathname);
96
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)) {
102         continue;
103       }
104       copyChild(archiveFolder, filter, child, fromFile);
105     }
106
107     ApplicationManager.getApplication().runWriteAction(new Runnable() {
108       @Override
109       public void run() {
110         Course courseCopy = course.copy();
111         replaceAnswerFilesWithTaskFiles(courseCopy);
112         generateJson(archiveFolder, courseCopy);
113         VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
114         packCourse(archiveFolder, locationDir, zipName, showMessage);
115         synchronize(project);
116       }
117
118       private void replaceAnswerFilesWithTaskFiles(Course courseCopy) {
119         for (Lesson lesson : courseCopy.getLessons()) {
120           final VirtualFile lessonDir = baseDir.findChild(EduNames.LESSON + String.valueOf(lesson.getIndex()));
121           if (lessonDir == null) continue;
122           for (Task task : lesson.getTaskList()) {
123             final VirtualFile taskDir = task.getTaskDir(project);
124             if (taskDir == null) continue;
125             String taskDirName = EduNames.TASK + String.valueOf(task.getIndex());
126             VirtualFile studentFileDir = VfsUtil.findRelativeFile(archiveFolder, lessonDir.getName(), taskDirName);
127             if (studentFileDir == null) {
128               continue;
129             }
130             VirtualFile srcDir = studentFileDir.findChild(EduNames.SRC);
131             if (srcDir != null) {
132               studentFileDir = srcDir;
133             }
134             if (task.hasSubtasks()) {
135               transformSubtaskTestsToTextFiles(studentFileDir);
136             }
137             for (String taskFile : task.getTaskFiles().keySet()) {
138               VirtualFile answerFile = taskDir.findChild(taskFile);
139               if (answerFile == null) {
140                 continue;
141               }
142               EduUtils.createStudentFile(this, project, answerFile, studentFileDir, task, 0);
143             }
144           }
145         }
146       }
147
148       private void transformSubtaskTestsToTextFiles(VirtualFile studentFileDir) {
149         Condition<VirtualFile> isSubtaskTestFile =
150           file -> CCUtils.isTestsFile(project, file) && file.getName().contains(EduNames.SUBTASK_MARKER);
151         List<VirtualFile> subtaskTests = ContainerUtil.filter(Arrays.asList(studentFileDir.getChildren()), isSubtaskTestFile);
152         for (VirtualFile subtaskTest : subtaskTests) {
153           try {
154             subtaskTest.rename(this, subtaskTest.getNameWithoutExtension() + ".txt");
155           }
156           catch (IOException e) {
157             LOG.error(e);
158           }
159         }
160       }
161     });
162   }
163
164   private static void copyChild(VirtualFile archiveFolder, FileFilter filter, VirtualFile child, File fromFile) {
165     File toFile = new File(archiveFolder.getPath(), child.getName());
166
167     try {
168       if (child.isDirectory()) {
169         FileUtil.copyDir(fromFile, toFile, filter);
170       }
171       else {
172         if (filter.accept(fromFile)) {
173           FileUtil.copy(fromFile, toFile);
174         }
175       }
176     }
177     catch (IOException e) {
178       LOG.info("Failed to copy" + fromFile.getPath(), e);
179     }
180   }
181
182   private static void synchronize(@NotNull final Project project) {
183     VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
184     ProjectView.getInstance(project).refresh();
185   }
186
187   private static void packCourse(@NotNull final VirtualFile baseDir, String locationDir, String zipName, boolean showMessage) {
188     try {
189       final File zipFile = new File(locationDir, zipName + ".zip");
190       ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
191       VirtualFile[] courseFiles = baseDir.getChildren();
192       for (VirtualFile file : courseFiles) {
193         ZipUtil.addFileOrDirRecursively(zos, null, new File(file.getPath()), file.getName(), null, null);
194       }
195       zos.close();
196       if (showMessage) {
197         Messages.showInfoMessage("Course archive was saved to " + zipFile.getPath(), "Course Archive Was Created Successfully");
198       }
199     }
200     catch (IOException e1) {
201       LOG.error(e1);
202     }
203   }
204
205   @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
206   private static void generateJson(VirtualFile parentDir, Course course) {
207     final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
208     final String json = gson.toJson(course);
209     final File courseJson = new File(parentDir.getPath(), EduNames.COURSE_META_FILE);
210     OutputStreamWriter outputStreamWriter = null;
211     try {
212       outputStreamWriter = new OutputStreamWriter(new FileOutputStream(courseJson), "UTF-8");
213       outputStreamWriter.write(json);
214     }
215     catch (Exception e) {
216       Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
217       LOG.info(e);
218     }
219     finally {
220       try {
221         if (outputStreamWriter != null) {
222           outputStreamWriter.close();
223         }
224       }
225       catch (IOException e1) {
226         //close silently
227       }
228     }
229   }
230 }