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