moved everything python dependent to language part of plugin
[idea/community.git] / python / educational / 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.icons.AllIcons;
6 import com.intellij.ide.projectView.ProjectView;
7 import com.intellij.openapi.actionSystem.AnActionEvent;
8 import com.intellij.openapi.actionSystem.CommonDataKeys;
9 import com.intellij.openapi.application.ApplicationManager;
10 import com.intellij.openapi.command.CommandProcessor;
11 import com.intellij.openapi.diagnostic.Logger;
12 import com.intellij.openapi.editor.Document;
13 import com.intellij.openapi.fileEditor.FileDocumentManager;
14 import com.intellij.openapi.project.DumbAwareAction;
15 import com.intellij.openapi.project.Project;
16 import com.intellij.openapi.ui.DialogWrapper;
17 import com.intellij.openapi.ui.Messages;
18 import com.intellij.openapi.util.io.FileUtil;
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.CCDocumentListener;
23 import com.jetbrains.edu.coursecreator.CCProjectService;
24 import com.jetbrains.edu.coursecreator.CCUtils;
25 import com.jetbrains.edu.coursecreator.StudyLanguageManager;
26 import com.jetbrains.edu.coursecreator.format.*;
27 import com.jetbrains.edu.coursecreator.ui.CreateCourseArchiveDialog;
28 import org.jetbrains.annotations.NotNull;
29
30 import java.io.*;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.Map;
34 import java.util.zip.ZipOutputStream;
35
36 public class CCCreateCourseArchive extends DumbAwareAction {
37   private static final Logger LOG = Logger.getInstance(CCCreateCourseArchive.class.getName());
38   private String myZipName;
39   private String myLocationDir;
40
41   public void setZipName(String zipName) {
42     myZipName = zipName;
43   }
44
45   public void setLocationDir(String locationDir) {
46     myLocationDir = locationDir;
47   }
48
49   public CCCreateCourseArchive() {
50     super("Generate Course Archive", "Generate Course Archive", AllIcons.FileTypes.Archive);
51   }
52
53   @Override
54   public void update(@NotNull AnActionEvent e) {
55     CCProjectService.setCCActionAvailable(e);
56   }
57
58   @Override
59   public void actionPerformed(@NotNull AnActionEvent e) {
60     final Project project = e.getData(CommonDataKeys.PROJECT);
61     if (project == null) {
62       return;
63     }
64     createCourseArchive(project);
65   }
66
67   private void createCourseArchive(final Project project) {
68     final CCProjectService service = CCProjectService.getInstance(project);
69     final Course course = service.getCourse();
70     if (course == null) return;
71     CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this);
72     dlg.show();
73     if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
74       return;
75     }
76     final VirtualFile baseDir = project.getBaseDir();
77     final Map<String, Lesson> lessons = course.getLessonsMap();
78     //map to store initial task file
79     final Map<TaskFile, TaskFile> taskFiles = new HashMap<TaskFile, TaskFile>();
80     for (Map.Entry<String, Lesson> lesson : lessons.entrySet()) {
81       final VirtualFile lessonDir = baseDir.findChild(lesson.getKey());
82       if (lessonDir == null) continue;
83       for (Map.Entry<String, Task> task : lesson.getValue().myTasksMap.entrySet()) {
84         final VirtualFile taskDir = lessonDir.findChild(task.getKey());
85         if (taskDir == null) continue;
86         for (final Map.Entry<String, TaskFile> entry : task.getValue().task_files.entrySet()) {
87           ApplicationManager.getApplication().runWriteAction(new Runnable() {
88             @Override
89             public void run() {
90               createUserFile(project, taskFiles, taskDir, taskDir, entry);
91             }
92           });
93         }
94       }
95     }
96     generateJson(project);
97     packCourse(baseDir, lessons, course);
98     resetTaskFiles(taskFiles);
99     synchronize(project);
100   }
101
102   public static void createUserFile(@NotNull final Project project,
103                                     @NotNull final Map<TaskFile, TaskFile> taskFilesCopy,
104                                     @NotNull final VirtualFile userFileDir,
105                                     @NotNull final VirtualFile answerFileDir,
106                                     @NotNull final Map.Entry<String, TaskFile> taskFiles) {
107     final String name = taskFiles.getKey();
108     VirtualFile file = userFileDir.findChild(name);
109     if (file != null) {
110       try {
111         file.delete(project);
112       }
113       catch (IOException e) {
114         LOG.error(e);
115       }
116     }
117     try {
118       userFileDir.createChildData(project, name);
119     }
120     catch (IOException e) {
121       LOG.error(e);
122     }
123
124     file = userFileDir.findChild(name);
125     assert file != null;
126     String answerFileName = file.getNameWithoutExtension() + ".answer." + file.getExtension();
127     VirtualFile answerFile = answerFileDir.findChild(answerFileName);
128     if (answerFile == null) {
129       return;
130     }
131     final Document answerDocument = FileDocumentManager.getInstance().getDocument(answerFile);
132     if (answerDocument == null) {
133       return;
134     }
135     final Document document = FileDocumentManager.getInstance().getDocument(file);
136     if (document == null) return;
137     final TaskFile taskFile = taskFiles.getValue();
138     TaskFile taskFileSaved = new TaskFile();
139     taskFile.copy(taskFileSaved);
140     for (AnswerPlaceholder answerPlaceholder : taskFile.getTaskWindows()) {
141       answerPlaceholder.setLength(answerPlaceholder.getReplacementLength());
142     }
143     CommandProcessor.getInstance().executeCommand(project, new Runnable() {
144       @Override
145       public void run() {
146         ApplicationManager.getApplication().runWriteAction(new Runnable() {
147           @Override
148           public void run() {
149             document.replaceString(0, document.getTextLength(), answerDocument.getCharsSequence());
150           }
151         });
152       }
153     }, "x", "qwe");
154     InsertionListener listener = new InsertionListener(taskFile);
155     document.addDocumentListener(listener);
156     taskFilesCopy.put(taskFile, taskFileSaved);
157     Collections.sort(taskFile.getTaskWindows());
158     for (int i = taskFile.getTaskWindows().size() - 1; i >= 0; i--) {
159       final AnswerPlaceholder answerPlaceholder = taskFile.getTaskWindows().get(i);
160       replaceTaskWindow(project, document, answerPlaceholder);
161     }
162     CommandProcessor.getInstance().executeCommand(project, new Runnable() {
163       @Override
164       public void run() {
165         ApplicationManager.getApplication().runWriteAction(new Runnable() {
166           @Override
167           public void run() {
168             FileDocumentManager.getInstance().saveDocument(document);
169           }
170         });
171       }
172     }, "x", "qwe");
173     document.removeDocumentListener(listener);
174   }
175
176   private static void replaceTaskWindow(@NotNull final Project project,
177                                         @NotNull final Document document,
178                                         @NotNull final AnswerPlaceholder answerPlaceholder) {
179     final String taskText = answerPlaceholder.getTaskText();
180     final int lineStartOffset = document.getLineStartOffset(answerPlaceholder.getLine());
181     final int offset = lineStartOffset + answerPlaceholder.getStart();
182     CommandProcessor.getInstance().executeCommand(project, new Runnable() {
183       @Override
184       public void run() {
185         ApplicationManager.getApplication().runWriteAction(new Runnable() {
186           @Override
187           public void run() {
188             document.replaceString(offset, offset + answerPlaceholder.getLength(), taskText);
189             FileDocumentManager.getInstance().saveDocument(document);
190           }
191         });
192       }
193     }, "x", "qwe");
194   }
195
196   private static void synchronize(@NotNull final Project project) {
197     VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
198     ProjectView.getInstance(project).refresh();
199   }
200
201   public static void resetTaskFiles(@NotNull final Map<TaskFile, TaskFile> taskFiles) {
202     for (Map.Entry<TaskFile, TaskFile> entry : taskFiles.entrySet()) {
203       TaskFile realTaskFile = entry.getKey();
204       TaskFile savedTaskFile = entry.getValue();
205       realTaskFile.update(savedTaskFile);
206     }
207   }
208
209   private void packCourse(@NotNull final VirtualFile baseDir, @NotNull final Map<String, Lesson> lessons, @NotNull final Course course) {
210     try {
211       File zipFile = new File(myLocationDir, myZipName + ".zip");
212       ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
213       final StudyLanguageManager manager = CCUtils.getStudyLanguageManager(course);
214       for (Map.Entry<String, Lesson> entry : lessons.entrySet()) {
215         final VirtualFile lessonDir = baseDir.findChild(entry.getKey());
216         if (lessonDir == null) continue;
217         ZipUtil.addFileOrDirRecursively(zos, null, new File(lessonDir.getPath()), lessonDir.getName(), new FileFilter() {
218           @Override
219           public boolean accept(File pathname) {
220             String name = pathname.getName();
221             String nameWithoutExtension = FileUtil.getNameWithoutExtension(pathname);
222             if (nameWithoutExtension.endsWith(".answer") || name.contains("_windows")) {
223               return false;
224             }
225             return manager == null || manager.packFile(pathname);
226           }
227         }, null);
228       }
229       packFile("hints", zos, baseDir);
230       packFile("course.json", zos, baseDir);
231       if (manager != null) {
232         String[] additionalFilesToPack = manager.getAdditionalFilesToPack();
233         for (String filename: additionalFilesToPack) {
234           packFile(filename, zos, baseDir);
235         }
236       }
237       zos.close();
238       Messages.showInfoMessage("Course archive was saved to " + zipFile.getPath(), "Course Archive Was Created Successfully");
239     }
240     catch (IOException e1) {
241       LOG.error(e1);
242     }
243   }
244
245   @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
246   private static void generateJson(@NotNull final Project project) {
247     final CCProjectService service = CCProjectService.getInstance(project);
248     final Course course = service.getCourse();
249     final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
250     final String json = gson.toJson(course);
251     final File courseJson = new File(project.getBasePath(), "course.json");
252     OutputStreamWriter outputStreamWriter = null;
253     try {
254       outputStreamWriter = new OutputStreamWriter(new FileOutputStream(courseJson), "UTF-8");
255       outputStreamWriter.write(json);
256     }
257     catch (Exception e) {
258       Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
259       LOG.info(e);
260     }
261     finally {
262       try {
263         if (outputStreamWriter != null) {
264           outputStreamWriter.close();
265         }
266       }
267       catch (IOException e1) {
268         //close silently
269       }
270     }
271   }
272
273   public static class InsertionListener extends CCDocumentListener {
274
275     public InsertionListener(TaskFile taskFile) {
276       super(taskFile);
277     }
278
279     @Override
280     protected boolean useLength() {
281       return true;
282     }
283   }
284
285   private static void packFile(@NotNull final String filename,
286                                @NotNull final ZipOutputStream zipOutputStream,
287                                @NotNull final VirtualFile baseDir) {
288     try {
289       File file = new File(baseDir.getPath(), filename);
290       if (!file.exists()) {
291         return;
292       }
293       ZipUtil.addFileOrDirRecursively(zipOutputStream, null, file, filename, null, null);
294     }
295     catch (IOException e) {
296       LOG.error(e);
297     }
298   }
299 }