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