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