use util method to exclude generated files root
[idea/community.git] / python / educational-core / course-creator / src / com / jetbrains / edu / coursecreator / CCUtils.java
1 package com.jetbrains.edu.coursecreator;
2
3 import com.google.common.base.Predicate;
4 import com.google.common.collect.Collections2;
5 import com.intellij.lang.Language;
6 import com.intellij.openapi.actionSystem.AnActionEvent;
7 import com.intellij.openapi.actionSystem.Presentation;
8 import com.intellij.openapi.application.ApplicationManager;
9 import com.intellij.openapi.diagnostic.Logger;
10 import com.intellij.openapi.module.Module;
11 import com.intellij.openapi.project.DumbModePermission;
12 import com.intellij.openapi.project.DumbService;
13 import com.intellij.openapi.project.Project;
14 import com.intellij.openapi.roots.ModuleRootModificationUtil;
15 import com.intellij.openapi.roots.ProjectRootManager;
16 import com.intellij.openapi.util.Ref;
17 import com.intellij.openapi.util.io.FileUtil;
18 import com.intellij.openapi.vfs.LocalFileSystem;
19 import com.intellij.openapi.vfs.VirtualFile;
20 import com.intellij.psi.PsiDirectory;
21 import com.intellij.util.Function;
22 import com.jetbrains.edu.learning.StudyTaskManager;
23 import com.jetbrains.edu.learning.core.EduUtils;
24 import com.jetbrains.edu.learning.courseFormat.Course;
25 import com.jetbrains.edu.learning.courseFormat.StudyItem;
26 import com.jetbrains.edu.learning.courseFormat.Task;
27 import com.jetbrains.edu.learning.courseFormat.TaskFile;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 import java.io.File;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.Map;
37
38 public class CCUtils {
39   public static final String ANSWER_EXTENSION_DOTTED = ".answer.";
40   private static final Logger LOG = Logger.getInstance(CCUtils.class);
41   public static final String GENERATED_FILES_FOLDER = ".coursecreator";
42   public static final String COURSE_MODE = "Course Creator";
43
44   @Nullable
45   public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) {
46     Language language = Language.findLanguageByID(course.getLanguageID());
47     return language == null ? null : CCLanguageManager.INSTANCE.forLanguage(language);
48   }
49
50   /**
51    * This method decreases index and updates directory names of
52    * all tasks/lessons that have higher index than specified object
53    *
54    * @param dirs         directories that are used to get tasks/lessons
55    * @param getStudyItem function that is used to get task/lesson from VirtualFile. This function can return null
56    * @param threshold    index is used as threshold
57    * @param prefix       task or lesson directory name prefix
58    */
59   public static void updateHigherElements(VirtualFile[] dirs,
60                                           @NotNull final Function<VirtualFile, ? extends StudyItem> getStudyItem,
61                                           final int threshold,
62                                           final String prefix,
63                                           final int delta) {
64     ArrayList<VirtualFile> dirsToRename = new ArrayList<>
65       (Collections2.filter(Arrays.asList(dirs), new Predicate<VirtualFile>() {
66         @Override
67         public boolean apply(VirtualFile dir) {
68           final StudyItem item = getStudyItem.fun(dir);
69           if (item == null) {
70             return false;
71           }
72           int index = item.getIndex();
73           return index > threshold;
74         }
75       }));
76     Collections.sort(dirsToRename, (o1, o2) -> {
77       StudyItem item1 = getStudyItem.fun(o1);
78       StudyItem item2 = getStudyItem.fun(o2);
79       //if we delete some dir we should start increasing numbers in dir names from the end
80       return (-delta) * EduUtils.INDEX_COMPARATOR.compare(item1, item2);
81     });
82
83     for (final VirtualFile dir : dirsToRename) {
84       final StudyItem item = getStudyItem.fun(dir);
85       final int newIndex = item.getIndex() + delta;
86       item.setIndex(newIndex);
87       ApplicationManager.getApplication().runWriteAction(new Runnable() {
88         @Override
89         public void run() {
90           try {
91             dir.rename(this, prefix + newIndex);
92           }
93           catch (IOException e) {
94             LOG.error(e);
95           }
96         }
97       });
98     }
99   }
100
101   public static boolean isLessonDir(PsiDirectory sourceDirectory) {
102     if (sourceDirectory == null) {
103       return false;
104     }
105     Project project = sourceDirectory.getProject();
106     Course course = StudyTaskManager.getInstance(project).getCourse();
107     if (course != null && isCourseCreator(project) && course.getLesson(sourceDirectory.getName()) != null) {
108       return true;
109     }
110     return false;
111   }
112
113
114   public static VirtualFile getGeneratedFilesFolder(@NotNull Project project, @NotNull Module module) {
115     VirtualFile baseDir = project.getBaseDir();
116     VirtualFile folder = baseDir.findChild(GENERATED_FILES_FOLDER);
117     if (folder != null) {
118       return folder;
119     }
120     final Ref<VirtualFile> generatedRoot = new Ref<>();
121     DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() {
122       @Override
123       public void run() {
124         ApplicationManager.getApplication().runWriteAction(new Runnable() {
125           @Override
126           public void run() {
127             try {
128               generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER));
129               VirtualFile contentRootForFile =
130                 ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get());
131               if (contentRootForFile == null) {
132                 return;
133               }
134               ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), Collections.singletonList(generatedRoot.get().getUrl()));
135             }
136             catch (IOException e) {
137               LOG.info("Failed to create folder for generated files", e);
138             }
139           }
140         });
141       }
142     });
143     return generatedRoot.get();
144   }
145
146   @Nullable
147   public static VirtualFile generateFolder(@NotNull Project project, @NotNull Module module, String name) {
148     VirtualFile generatedRoot = getGeneratedFilesFolder(project, module);
149     if (generatedRoot == null) {
150       return null;
151     }
152
153     final Ref<VirtualFile> folder = new Ref<>(generatedRoot.findChild(name));
154     //need to delete old folder
155     ApplicationManager.getApplication().runWriteAction(() -> {
156       try {
157         if (folder.get() != null) {
158           folder.get().delete(null);
159         }
160         folder.set(generatedRoot.createChildDirectory(null, name));
161       }
162       catch (IOException e) {
163         LOG.info("Failed to generate folder " + name, e);
164       }
165     });
166     return folder.get();
167   }
168
169   public static boolean isCourseCreator(@NotNull Project project) {
170     Course course = StudyTaskManager.getInstance(project).getCourse();
171     if (course == null) {
172       return false;
173     }
174
175     return COURSE_MODE.equals(course.getCourseMode());
176   }
177
178   public static boolean isTestsFile(@NotNull Project project, @NotNull VirtualFile file) {
179     Course course = StudyTaskManager.getInstance(project).getCourse();
180     if (course == null) {
181       return false;
182     }
183     CCLanguageManager manager = getStudyLanguageManager(course);
184     if (manager == null) {
185       return false;
186     }
187     return manager.isTestFile(file);
188   }
189
190   public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) {
191     VirtualFile lessonVF = taskVF.getParent();
192     if (lessonVF == null) {
193       return;
194     }
195
196     String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName());
197     File taskResourceFile = new File(taskResourcesPath);
198     if (!taskResourceFile.exists()) {
199       if (!taskResourceFile.mkdirs()) {
200         LOG.info("Failed to create resources for task " + taskResourcesPath);
201       }
202     }
203     try {
204       File toFile = new File(taskResourceFile, createdFile.getName());
205       FileUtil.copy(new File(createdFile.getPath()), toFile);
206     }
207     catch (IOException e) {
208       LOG.info("Failed to copy created task file to resources " + createdFile.getPath());
209     }
210   }
211
212
213   public static void updateResources(Project project, Task task, VirtualFile taskDir) {
214     Course course = StudyTaskManager.getInstance(project).getCourse();
215     if (course == null) {
216       return;
217     }
218     VirtualFile lessonVF = taskDir.getParent();
219     if (lessonVF == null) {
220       return;
221     }
222
223     String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskDir.getName());
224     File taskResourceFile = new File(taskResourcesPath);
225     if (!taskResourceFile.exists()) {
226       if (!taskResourceFile.mkdirs()) {
227         LOG.info("Failed to create resources for task " + taskResourcesPath);
228       }
229     }
230     VirtualFile studentDir = LocalFileSystem.getInstance().findFileByIoFile(taskResourceFile);
231     if (studentDir == null) {
232       return;
233     }
234     for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
235       String name = entry.getKey();
236       VirtualFile answerFile = taskDir.findChild(name);
237       if (answerFile == null) {
238         continue;
239       }
240       ApplicationManager.getApplication().runWriteAction(() -> {
241         EduUtils.createStudentFile(CCUtils.class, project, answerFile, studentDir, null);
242       });
243     }
244   }
245
246   public static void updateActionGroup(AnActionEvent e) {
247     Presentation presentation = e.getPresentation();
248     Project project = e.getProject();
249     presentation.setEnabledAndVisible(project != null && isCourseCreator(project));
250   }
251 }