EDU-657 Initial implementation of subtasks
[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.io.ZipUtil;
22 import com.jetbrains.edu.coursecreator.CCLanguageManager;
23 import com.jetbrains.edu.coursecreator.CCUtils;
24 import com.jetbrains.edu.coursecreator.ui.CreateCourseArchiveDialog;
25 import com.jetbrains.edu.learning.StudyTaskManager;
26 import com.jetbrains.edu.learning.core.EduNames;
27 import com.jetbrains.edu.learning.core.EduUtils;
28 import com.jetbrains.edu.learning.courseFormat.Course;
29 import com.jetbrains.edu.learning.courseFormat.Lesson;
30 import com.jetbrains.edu.learning.courseFormat.Task;
31 import com.jetbrains.edu.learning.courseFormat.TaskFile;
32 import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
33 import org.jetbrains.annotations.NotNull;
34
35 import java.io.*;
36 import java.util.zip.ZipOutputStream;
37
38 public class CCCreateCourseArchive extends DumbAwareAction {
39   private static final Logger LOG = Logger.getInstance(CCCreateCourseArchive.class.getName());
40   public static final String GENERATE_COURSE_ARCHIVE = "Generate Course Archive";
41   private String myZipName;
42   private String myLocationDir;
43
44   public void setZipName(String zipName) {
45     myZipName = zipName;
46   }
47
48   public void setLocationDir(String locationDir) {
49     myLocationDir = locationDir;
50   }
51
52   public CCCreateCourseArchive() {
53     super(GENERATE_COURSE_ARCHIVE, GENERATE_COURSE_ARCHIVE, null);
54   }
55
56   @Override
57   public void update(@NotNull AnActionEvent e) {
58     Presentation presentation = e.getPresentation();
59     Project project = e.getProject();
60     presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project));
61   }
62
63   @Override
64   public void actionPerformed(@NotNull AnActionEvent e) {
65     final Project project = e.getData(CommonDataKeys.PROJECT);
66     final Module module = e.getData(LangDataKeys.MODULE);
67     if (project == null || module == null) {
68       return;
69     }
70     CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this);
71     dlg.show();
72     if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
73       return;
74     }
75     createCourseArchive(project, module, myZipName, myLocationDir, true);
76     EduUsagesCollector.createdCourseArchive();
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, 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     ApplicationManager.getApplication().runWriteAction(new Runnable() {
105       @Override
106       public void run() {
107         Course courseCopy = course.copy();
108         replaceAnswerFilesWithTaskFiles(courseCopy);
109         generateJson(archiveFolder, courseCopy);
110         VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
111         packCourse(archiveFolder, locationDir, zipName, showMessage);
112         synchronize(project);
113       }
114
115       private void replaceAnswerFilesWithTaskFiles(Course courseCopy) {
116         for (Lesson lesson : courseCopy.getLessons()) {
117           final VirtualFile lessonDir = baseDir.findChild(EduNames.LESSON + String.valueOf(lesson.getIndex()));
118           if (lessonDir == null) continue;
119           for (Task task : lesson.getTaskList()) {
120             final VirtualFile taskDir = lessonDir.findChild(EduNames.TASK + String.valueOf(task.getIndex()));
121             if (taskDir == null) continue;
122             VirtualFile studentFileDir = VfsUtil.findRelativeFile(archiveFolder, lessonDir.getName(), taskDir.getName());
123             if (studentFileDir == null) {
124               continue;
125             }
126             for (TaskFile taskFile : task.getTaskFiles().values()) {
127               VirtualFile answerFile = taskDir.findChild(taskFile.name);
128               if (answerFile == null) {
129                 continue;
130               }
131               EduUtils.createStudentFile(this, project, answerFile, -1, studentFileDir, task);
132             }
133         }
134       }
135     }
136     });
137   }
138
139   private static void copyChild(VirtualFile archiveFolder, FileFilter filter, VirtualFile child, File fromFile) {
140     File toFile = new File(archiveFolder.getPath(), child.getName());
141
142     try {
143       if (child.isDirectory()) {
144         FileUtil.copyDir(fromFile, toFile, filter);
145       }
146       else {
147         if (filter.accept(fromFile)) {
148           FileUtil.copy(fromFile, toFile);
149         }
150       }
151     }
152     catch (IOException e) {
153       LOG.info("Failed to copy" + fromFile.getPath(), e);
154     }
155   }
156
157   private static void synchronize(@NotNull final Project project) {
158     VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
159     ProjectView.getInstance(project).refresh();
160   }
161
162   private static void packCourse(@NotNull final VirtualFile baseDir, String locationDir, String zipName, boolean showMessage) {
163     try {
164       final File zipFile = new File(locationDir, zipName + ".zip");
165       ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
166       VirtualFile[] courseFiles = baseDir.getChildren();
167       for (VirtualFile file : courseFiles) {
168         ZipUtil.addFileOrDirRecursively(zos, null, new File(file.getPath()), file.getName(), null, null);
169       }
170       zos.close();
171       if (showMessage) {
172         Messages.showInfoMessage("Course archive was saved to " + zipFile.getPath(), "Course Archive Was Created Successfully");
173       }
174     }
175     catch (IOException e1) {
176       LOG.error(e1);
177     }
178   }
179
180   @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
181   private static void generateJson(VirtualFile parentDir, Course course) {
182     final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
183     final String json = gson.toJson(course);
184     final File courseJson = new File(parentDir.getPath(), EduNames.COURSE_META_FILE);
185     OutputStreamWriter outputStreamWriter = null;
186     try {
187       outputStreamWriter = new OutputStreamWriter(new FileOutputStream(courseJson), "UTF-8");
188       outputStreamWriter.write(json);
189     }
190     catch (Exception e) {
191       Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
192       LOG.info(e);
193     }
194     finally {
195       try {
196         if (outputStreamWriter != null) {
197           outputStreamWriter.close();
198         }
199       }
200       catch (IOException e1) {
201         //close silently
202       }
203     }
204   }
205 }