05e9e87c60f8961c9f50fa74ea69386aa5a1dcae
[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.io.FileUtil;
18 import com.intellij.openapi.vfs.VfsUtil;
19 import com.intellij.openapi.vfs.VirtualFile;
20 import com.intellij.openapi.vfs.VirtualFileManager;
21 import com.intellij.util.containers.HashMap;
22 import com.intellij.util.io.ZipUtil;
23 import com.jetbrains.edu.coursecreator.CCLanguageManager;
24 import com.jetbrains.edu.coursecreator.CCUtils;
25 import com.jetbrains.edu.coursecreator.ui.CreateCourseArchiveDialog;
26 import com.jetbrains.edu.learning.StudyTaskManager;
27 import com.jetbrains.edu.learning.core.EduNames;
28 import com.jetbrains.edu.learning.core.EduUtils;
29 import com.jetbrains.edu.learning.courseFormat.Course;
30 import com.jetbrains.edu.learning.courseFormat.Lesson;
31 import com.jetbrains.edu.learning.courseFormat.Task;
32 import com.jetbrains.edu.learning.courseFormat.TaskFile;
33 import org.jetbrains.annotations.NotNull;
34
35 import java.io.*;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.zip.ZipOutputStream;
39
40 public class CCCreateCourseArchive extends DumbAwareAction {
41   private static final Logger LOG = Logger.getInstance(CCCreateCourseArchive.class.getName());
42   private String myZipName;
43   private String myLocationDir;
44
45   public void setZipName(String zipName) {
46     myZipName = zipName;
47   }
48
49   public void setLocationDir(String locationDir) {
50     myLocationDir = locationDir;
51   }
52
53   public CCCreateCourseArchive() {
54     super("Generate Course Archive", "Generate Course Archive", null);
55   }
56
57   @Override
58   public void update(@NotNull AnActionEvent e) {
59     Presentation presentation = e.getPresentation();
60     Project project = e.getProject();
61     presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project));
62   }
63
64   @Override
65   public void actionPerformed(@NotNull AnActionEvent e) {
66     final Project project = e.getData(CommonDataKeys.PROJECT);
67     final Module module = e.getData(LangDataKeys.MODULE);
68     if (project == null || module == null) {
69       return;
70     }
71     CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this);
72     dlg.show();
73     if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
74       return;
75     }
76     createCourseArchive(project, module, myZipName, myLocationDir, true);
77   }
78
79   public static void createCourseArchive(final Project project, Module module, String zipName, String locationDir, boolean showMessage) {
80     final Course course = StudyTaskManager.getInstance(project).getCourse();
81     if (course == null) return;
82     final VirtualFile baseDir = project.getBaseDir();
83     VirtualFile archiveFolder = CCUtils.generateFolder(project, module, null, zipName);
84     if (archiveFolder == null) {
85       return;
86     }
87
88     CCLanguageManager manager = CCUtils.getStudyLanguageManager(course);
89     if (manager == null) {
90       return;
91     }
92     FileFilter filter = pathname -> !manager.doNotPackFile(pathname);
93
94     for (VirtualFile child : baseDir.getChildren()) {
95       String name = child.getName();
96       File fromFile = new File(child.getPath());
97       if (CCUtils.GENERATED_FILES_FOLDER.equals(name) || ".idea".equals(name)
98           || name.contains("iml") || manager.doNotPackFile(fromFile)) {
99         continue;
100       }
101       copyChild(archiveFolder, filter, child, fromFile);
102     }
103
104     final List<Lesson> lessons = course.getLessons();
105
106     ApplicationManager.getApplication().runWriteAction(new Runnable() {
107       @Override
108       public void run() {
109         final Map<TaskFile, TaskFile> savedTaskFiles = new HashMap<TaskFile, TaskFile>();
110         replaceAnswerFilesWithTaskFiles(savedTaskFiles);
111         generateJson(project, archiveFolder);
112         resetTaskFiles(savedTaskFiles);
113         VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
114         packCourse(archiveFolder, locationDir, zipName, showMessage);
115         synchronize(project);
116       }
117
118       private void replaceAnswerFilesWithTaskFiles(Map<TaskFile, TaskFile> savedTaskFiles) {
119         for (Lesson lesson : lessons) {
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 = lessonDir.findChild(EduNames.TASK + String.valueOf(task.getIndex()));
124             if (taskDir == null) continue;
125             for (final Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
126               TaskFile taskFileCopy = new TaskFile();
127               TaskFile taskFile = entry.getValue();
128               TaskFile.copy(taskFile, taskFileCopy);
129               savedTaskFiles.put(taskFile, taskFileCopy);
130
131               VirtualFile userFileDir = VfsUtil.findRelativeFile(archiveFolder, lessonDir.getName(), taskDir.getName());
132               if (userFileDir == null) {
133                 continue;
134               }
135               String taskFileName = entry.getKey();
136               EduUtils.createStudentFileFromAnswer(project, userFileDir, taskDir, taskFileName, taskFile);
137             }
138           }
139         }
140       }
141     });
142
143
144   }
145
146   private static void copyChild(VirtualFile archiveFolder, FileFilter filter, VirtualFile child, File fromFile) {
147     File toFile = new File(archiveFolder.getPath(), child.getName());
148
149     try {
150       if (child.isDirectory()) {
151         FileUtil.copyDir(fromFile, toFile, filter);
152       }
153       else {
154         if (filter.accept(fromFile)) {
155           FileUtil.copy(fromFile, toFile);
156         }
157       }
158     }
159     catch (IOException e) {
160       LOG.info("Failed to copy" + fromFile.getPath(), e);
161     }
162   }
163
164   private static void resetTaskFiles(Map<TaskFile, TaskFile> savedTaskFiles) {
165     for (Map.Entry<TaskFile, TaskFile> entry : savedTaskFiles.entrySet()) {
166       entry.getKey().setAnswerPlaceholders(entry.getValue().getAnswerPlaceholders());
167     }
168   }
169
170   private static void synchronize(@NotNull final Project project) {
171     VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
172     ProjectView.getInstance(project).refresh();
173   }
174
175   private static void packCourse(@NotNull final VirtualFile baseDir, String locationDir, String zipName, boolean showMessage) {
176     try {
177       final File zipFile = new File(locationDir, zipName + ".zip");
178       ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
179       VirtualFile[] courseFiles = baseDir.getChildren();
180       for (VirtualFile file : courseFiles) {
181         ZipUtil.addFileOrDirRecursively(zos, null, new File(file.getPath()), file.getName(), null, null);
182       }
183       zos.close();
184       if (showMessage) {
185         Messages.showInfoMessage("Course archive was saved to " + zipFile.getPath(), "Course Archive Was Created Successfully");
186       }
187     }
188     catch (IOException e1) {
189       LOG.error(e1);
190     }
191   }
192
193   @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
194   private static void generateJson(@NotNull final Project project, VirtualFile parentDir) {
195     final Course course = StudyTaskManager.getInstance(project).getCourse();
196     final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
197     final String json = gson.toJson(course);
198     final File courseJson = new File(parentDir.getPath(), EduNames.COURSE_META_FILE);
199     OutputStreamWriter outputStreamWriter = null;
200     try {
201       outputStreamWriter = new OutputStreamWriter(new FileOutputStream(courseJson), "UTF-8");
202       outputStreamWriter.write(json);
203     }
204     catch (Exception e) {
205       Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
206       LOG.info(e);
207     }
208     finally {
209       try {
210         if (outputStreamWriter != null) {
211           outputStreamWriter.close();
212         }
213       }
214       catch (IOException e1) {
215         //close silently
216       }
217     }
218   }
219 }