diff --git a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java index 4bee8f58103d..ac4df354a7c8 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java @@ -12,7 +12,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; -import com.intellij.util.Consumer; +import com.jetbrains.edu.learning.newproject.EduCourseProjectGenerator; import com.jetbrains.edu.learning.actions.*; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; @@ -133,7 +133,5 @@ public interface EduPluginConfigurator { return Collections.emptyList(); } - default boolean createCourseProject(@NotNull Course course, @Nullable Consumer onCreated) { - return false; - } + EduCourseProjectGenerator getEduCourseProjectGenerator(); } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerUtils.java index b69957f95d3b..2e9ea09bfc05 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerUtils.java @@ -19,21 +19,17 @@ import com.intellij.ide.RecentProjectsManagerBase; import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.TransactionGuard; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.util.Consumer; import com.intellij.util.xmlb.XmlSerializationException; -import com.jetbrains.edu.learning.EduPluginConfigurator; -import com.jetbrains.edu.learning.StudySettings; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.stepic.EduStepicConnector; -import com.jetbrains.edu.learning.stepic.StepicUser; +import com.jetbrains.edu.learning.newproject.ui.EduCreateNewProjectDialog; +import com.jetbrains.edu.learning.newproject.ui.EduCreateNewProjectListener; +import com.jetbrains.edu.learning.newproject.ui.EduCreateNewStepikProjectDialog; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; @@ -45,7 +41,6 @@ import java.io.File; import java.io.IOException; import java.util.List; -import static com.jetbrains.edu.learning.StudyUtils.execCancelable; import static com.jetbrains.edu.learning.core.EduNames.STUDY_PROJECT_XML_PATH; import static com.jetbrains.edu.learning.navigation.StudyNavigator.navigateToTask; @@ -53,8 +48,6 @@ import static com.jetbrains.edu.learning.navigation.StudyNavigator.navigateToTas * @author meanmail */ public class EduBuiltInServerUtils { - private static final Logger LOG = Logger.getInstance(EduBuiltInServerUtils.class); - public static boolean focusOpenProject(int courseId, int stepId) { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); for (Project project : openProjects) { @@ -163,26 +156,11 @@ public class EduBuiltInServerUtils { } public static boolean createProject(int courseId, int stepId) { - final Course[] course = new Course[1]; - Project defaultProject = ProjectManager.getInstance().getDefaultProject(); - StepicUser user = StudySettings.getInstance().getUser(); - ApplicationManager.getApplication().invokeAndWait(() -> - course[0] = ProgressManager.getInstance() - .runProcessWithProgressSynchronously(() -> { - ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - return execCancelable(() -> { - try { - return EduStepicConnector.getCourseFromStepik(user, courseId); - } - catch (IOException e) { - LOG.warn("Tried to create a project for course with id=" + courseId, e); - return null; - } - }); - }, "Getting Available Courses", true, defaultProject)); - - if (course[0] != null) { - Consumer onCreated = project -> + ApplicationManager.getApplication().invokeLater(() -> { + EduCreateNewProjectDialog createNewProjectDlg = new EduCreateNewStepikProjectDialog(courseId); + EduCreateNewProjectListener listener = new EduCreateNewProjectListener() { + @Override + public void created(@NotNull Project project) { ApplicationManager.getApplication().invokeLater(() -> { StudyTaskManager taskManager = StudyTaskManager.getInstance(project); Course targetCourse = taskManager.getCourse(); @@ -190,13 +168,13 @@ public class EduBuiltInServerUtils { navigateToStep(project, targetCourse, stepId); } }); - EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course[0].getLanguageById()); - if (configurator == null) { - return false; } - return configurator.createCourseProject(course[0], onCreated); - } - return false; + }; + createNewProjectDlg.addListener(listener); + createNewProjectDlg.show(); + }); + + return true; } private static void navigateToStep(@NotNull Project project, @NotNull Course course, int stepId) { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java index 1fed74118ab7..d26e77110c00 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java @@ -41,7 +41,7 @@ public class StudyGenerator { } else { String lessonDirName = EduNames.LESSON + Integer.toString(lesson.getIndex()); - VirtualFile lessonDir = courseDir.createChildDirectory(courseDir, lessonDirName); + VirtualFile lessonDir = VfsUtil.createDirectoryIfMissing(courseDir, lessonDirName); final List taskList = lesson.getTaskList(); for (int i = 1; i <= taskList.size(); i++) { Task task = taskList.get(i - 1); @@ -52,11 +52,12 @@ public class StudyGenerator { } public static void createTask(@NotNull final Task task, @NotNull final VirtualFile lessonDir) throws IOException { - VirtualFile taskDir = lessonDir.createChildDirectory(lessonDir, EduNames.TASK + Integer.toString(task.getIndex())); + String name = EduNames.TASK + Integer.toString(task.getIndex()); + VirtualFile taskDir = VfsUtil.createDirectoryIfMissing(lessonDir, name); createTaskContent(task, taskDir); } - public static void createTaskContent(@NotNull Task task, VirtualFile taskDir) throws IOException { + public static void createTaskContent(@NotNull Task task, @NotNull VirtualFile taskDir) throws IOException { int i = 0; for (Map.Entry taskFile : task.getTaskFiles().entrySet()) { TaskFile taskFileContent = taskFile.getValue(); @@ -73,25 +74,28 @@ public class StudyGenerator { createChildFile(taskDir, name, taskFile.text); } - private static void createDescriptions(VirtualFile taskDir, Task task) throws IOException { + private static void createDescriptions(@NotNull VirtualFile taskDir, @NotNull Task task) throws IOException { final Map texts = task.getTaskTexts(); + createFiles(taskDir, texts); + } + + private static void createTestFiles(@NotNull VirtualFile taskDir, @NotNull Task task) throws IOException { + final Map tests = task.getTestsText(); + createFiles(taskDir, tests); + } + + private static void createFiles(@NotNull VirtualFile taskDir, @NotNull Map texts) throws IOException { for (Map.Entry entry : texts.entrySet()) { final String name = entry.getKey(); - final VirtualFile virtualTaskFile = taskDir.createChildData(taskDir, name); + VirtualFile virtualTaskFile = taskDir.findChild(name); + if (virtualTaskFile == null) { + virtualTaskFile = taskDir.createChildData(taskDir, name); + } VfsUtil.saveText(virtualTaskFile, entry.getValue()); } } - private static void createTestFiles(VirtualFile taskDir, Task task) throws IOException { - final Map tests = task.getTestsText(); - for (Map.Entry entry : tests.entrySet()) { - final String name = entry.getKey(); - final VirtualFile virtualTaskFile = taskDir.createChildData(taskDir, name); - VfsUtil.saveText(virtualTaskFile, entry.getValue()); - } - } - - private static void createAdditionalFiles(Lesson lesson, VirtualFile courseDir) throws IOException { + private static void createAdditionalFiles(@NotNull Lesson lesson, @NotNull VirtualFile courseDir) throws IOException { final List taskList = lesson.getTaskList(); if (taskList.size() != 1) return; final Task task = taskList.get(0); @@ -100,8 +104,7 @@ public class StudyGenerator { } } - - public static void createChildFile(@NotNull VirtualFile taskDir, String name, String text) throws IOException { + public static void createChildFile(@NotNull VirtualFile taskDir, @NotNull String name, @NotNull String text) throws IOException { String newDirectories = null; String fileName = name; VirtualFile dir = taskDir; @@ -114,7 +117,10 @@ public class StudyGenerator { dir = VfsUtil.createDirectoryIfMissing(taskDir, newDirectories); } if (dir != null) { - final VirtualFile virtualTaskFile = dir.createChildData(taskDir, fileName); + VirtualFile virtualTaskFile = dir.findChild(fileName); + if (virtualTaskFile == null) { + virtualTaskFile = dir.createChildData(taskDir, fileName); + } if (EduUtils.isImage(name)) { virtualTaskFile.setBinaryContent(Base64.decodeBase64(text)); } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/newproject/EduCourseProjectGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/newproject/EduCourseProjectGenerator.java new file mode 100644 index 000000000000..2fb6524fa7ce --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/newproject/EduCourseProjectGenerator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.edu.learning.newproject; + +import com.intellij.facet.ui.ValidationResult; +import com.intellij.openapi.project.Project; +import com.intellij.platform.DirectoryProjectGenerator; +import com.jetbrains.edu.learning.courseFormat.Course; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author meanmail + */ +public interface EduCourseProjectGenerator { + @NotNull + DirectoryProjectGenerator getDirectoryProjectGenerator(); + + @Nullable + Object getProjectSettings(); + + void setCourse(@NotNull Course course); + + ValidationResult validate(@NotNull String path); + + boolean beforeProjectGenerated(); + + void afterProjectGenerated(@NotNull Project project); +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectDialog.java b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectDialog.java new file mode 100644 index 000000000000..2e8b6389e4ca --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectDialog.java @@ -0,0 +1,189 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.edu.learning.newproject.ui; + +import com.intellij.facet.ui.ValidationResult; +import com.intellij.ide.RecentProjectsManager; +import com.intellij.internal.statistic.UsageTrigger; +import com.intellij.internal.statistic.beans.ConvertUsagesUtil; +import com.intellij.lang.Language; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.DirectoryProjectGenerator; +import com.intellij.platform.PlatformProjectOpenProcessor; +import com.intellij.platform.templates.TemplateProjectDirectoryGenerator; +import com.intellij.projectImport.ProjectOpenedCallback; +import com.jetbrains.edu.learning.EduPluginConfigurator; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.newproject.EduCourseProjectGenerator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.io.File; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +/** + * @author meanmail + */ +public class EduCreateNewProjectDialog extends DialogWrapper { + private static final Logger LOG = Logger.getInstance(EduCreateNewProjectDialog.class); + private Project project; + private Course course; + private final List listeners = new ArrayList<>(); + private final EduCreateNewProjectPanel panel; + + public EduCreateNewProjectDialog() { + super(false); + setTitle("New project"); + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + panel = new EduCreateNewProjectPanel(defaultProject, this); + setOKButtonText("Create"); + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return panel; + } + + public void addListener(@NotNull EduCreateNewProjectListener listener) { + listeners.add(listener); + } + + public void setCourse(@Nullable Course course) { + this.course = course; + } + + @Override + protected void doOKAction() { + if (course == null) { + panel.setError("Selected course is null"); + return; + } + Language language = course.getLanguageById(); + if (language == null) { + String message = "Selected course don't have language"; + panel.setError(message); + LOG.warn(message); + return; + } + EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language); + if (configurator == null) { + String message = "A configurator for the selected course not found"; + panel.setError(message); + LOG.warn(message + ": " + language); + return; + } + EduCourseProjectGenerator projectGenerator = configurator.getEduCourseProjectGenerator(); + String errorMessage = createProject(projectGenerator); + if (errorMessage != null) { + panel.setError(errorMessage); + return; + } + if (project == null) { + panel.setError("Project did't created"); + return; + } + notifyCreated(); + super.doOKAction(); + } + + + /** + * @param projectGenerator + * @return error message if didn't create project else return null + */ + @Nullable + private String createProject(@NotNull final EduCourseProjectGenerator projectGenerator) { + String name = panel.getName(); + String path = FileUtil.join(FileUtil.toSystemDependentName(panel.getLocationPath()), name); + + ValidationResult result = projectGenerator.validate(path); + if (!result.isOk()) { + return result.getErrorMessage(); + } + + final File location = new File(path); + if (!location.exists() && !location.mkdirs()) { + String message = "Can't create a project directory"; + LOG.error(message + ": " + path); + return message; + } + + projectGenerator.setCourse(course); + + final VirtualFile baseDir = ApplicationManager.getApplication() + .runWriteAction((Computable)() -> + LocalFileSystem.getInstance().refreshAndFindFileByIoFile(location) + ); + + if (baseDir == null) { + LOG.error("Couldn't find '" + location + "' in VFS"); + return "Couldn't find in VFS"; + } + VfsUtil.markDirtyAndRefresh(false, true, true, baseDir); + + if (baseDir.getChildren().length > 0) { + String message = + String.format("Directory '%s' is not empty.\nFiles and directories will remove.\nDo you want continue?", location.getAbsolutePath()); + int rc = Messages.showYesNoDialog((Project)null, message, "New project", Messages.getQuestionIcon()); + if (rc != Messages.YES) { + panel.resetError(); + return "Canceled by user"; + } + } + + DirectoryProjectGenerator generator = projectGenerator.getDirectoryProjectGenerator(); + + String generatorName = ConvertUsagesUtil.ensureProperKey(generator.getName()); + UsageTrigger.trigger("AbstractNewProjectStep." + generatorName); + + RecentProjectsManager.getInstance().setLastProjectCreationLocation(location.getParent()); + + ProjectOpenedCallback callback = null; + if(generator instanceof TemplateProjectDirectoryGenerator){ + ((TemplateProjectDirectoryGenerator)generator).generateProject(baseDir.getName(), path); + } else { + callback = (project, module) -> { + if (projectGenerator.beforeProjectGenerated()) { + Object settings = projectGenerator.getProjectSettings(); + generator.generateProject(project, baseDir, settings, module); + projectGenerator.afterProjectGenerated(project); + } + }; + } + EnumSet options = EnumSet.noneOf(PlatformProjectOpenProcessor.Option.class); + project = PlatformProjectOpenProcessor.doOpenProject(baseDir, null, -1, callback, options); + return null; + } + + private void notifyCreated() { + listeners.forEach(listener -> listener.created(project)); + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectListener.java b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectListener.java new file mode 100644 index 000000000000..8c034be752e6 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectListener.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.edu.learning.newproject.ui; + +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +/** + * @author meanmail + */ +public interface EduCreateNewProjectListener { + void created(@NotNull Project project); +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectPanel.form b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectPanel.form new file mode 100644 index 000000000000..5365323d6369 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectPanel.form @@ -0,0 +1,74 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectPanel.java b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectPanel.java new file mode 100644 index 000000000000..b65a20d45884 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectPanel.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.edu.learning.newproject.ui; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.RecentProjectsManager; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.io.File; + +/** + * @author meanmail + */ +public class EduCreateNewProjectPanel extends JPanel { + private JPanel panel; + private JTextField nameField; + private TextFieldWithBrowseButton locationField; + private JLabel errorIcon; + private JLabel errorLabel; + + public EduCreateNewProjectPanel(@NotNull final Project project, @NotNull EduCreateNewProjectDialog dialog) { + setLayout(new BorderLayout()); + add(panel, BorderLayout.CENTER); + errorIcon.setIcon(AllIcons.Actions.Lightning); + resetError(); + nameField.setText("untitled"); + String location = RecentProjectsManager.getInstance().getLastProjectCreationLocation(); + locationField.setText(location); + FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); + locationField.addBrowseFolderListener("Choose Location Folder", null, project, descriptor); + locationField.addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + String location = FileUtil.toSystemDependentName(locationField.getText()); + File file = new File(location); + if (!FileUtil.ensureCanCreateFile(file)) { + dialog.setOKActionEnabled(false); + setError("Invalid location"); + } else { + dialog.setOKActionEnabled(true); + resetError(); + } + } + }); + } + + private void setState(boolean isVisible) { + errorIcon.setVisible(isVisible); + errorLabel.setVisible(isVisible); + } + + void setError(@NotNull String message) { + errorLabel.setText(message); + setState(true); + } + + public String getName() { + return nameField.getText(); + } + + public String getLocationPath() { + return locationField.getText(); + } + + public void resetError() { + setState(false); + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewStepikProjectDialog.java b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewStepikProjectDialog.java new file mode 100644 index 000000000000..f7bebcfd42c7 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewStepikProjectDialog.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.edu.learning.newproject.ui; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.stepic.EduStepicAuthorizedClient; +import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.StepicUser; + +import java.io.IOException; + +import static com.jetbrains.edu.learning.StudyUtils.execCancelable; + +/** + * @author meanmail + */ +public class EduCreateNewStepikProjectDialog extends EduCreateNewProjectDialog { + private static final Logger LOG = Logger.getInstance(EduCreateNewStepikProjectDialog.class); + + public EduCreateNewStepikProjectDialog() { + super(); + } + + public EduCreateNewStepikProjectDialog(int courseId) { + this(); + + StepicUser user = EduStepicAuthorizedClient.getCurrentUser(); + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + ApplicationManager.getApplication().invokeAndWait(() -> + ProgressManager.getInstance() + .runProcessWithProgressSynchronously(() -> { + ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); + execCancelable(() -> { + try { + Course course = EduStepicConnector.getCourseFromStepik(user, courseId); + if (course != null) { + setTitle("New project - " + course.getName()); + } + + setCourse(course); + } + catch (IOException e) { + LOG.warn("Tried to create a project for course with id=" + courseId, e); + } + return null; + }); + }, "Getting Available Courses", true, defaultProject) + ); + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java index f7eb72f4b607..6945d806c7c3 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java @@ -333,10 +333,6 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : ""); } - public JComboBox getCoursesComboBox() { - return myCoursesComboBox; - } - public JPanel getInfoPanel() { return myInfoPanel; } diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/common/newproject/PyEduCourseProjectGenerator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/common/newproject/PyEduCourseProjectGenerator.java new file mode 100644 index 000000000000..44a79b44979d --- /dev/null +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/common/newproject/PyEduCourseProjectGenerator.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.edu.common.newproject; + +import com.intellij.facet.ui.ValidationResult; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkAdditionalData; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; +import com.intellij.platform.DirectoryProjectGenerator; +import com.intellij.util.BooleanFunction; +import com.jetbrains.edu.learning.newproject.EduCourseProjectGenerator; +import com.jetbrains.edu.learning.PyStudyDirectoryProjectGenerator; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.python.newProject.PyNewProjectSettings; +import com.jetbrains.python.newProject.PythonProjectGenerator; +import com.jetbrains.python.sdk.PythonSdkAdditionalData; +import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author meanmail + */ +public class PyEduCourseProjectGenerator implements EduCourseProjectGenerator { + private final PyStudyDirectoryProjectGenerator generator; + + public PyEduCourseProjectGenerator() { + this.generator = new PyStudyDirectoryProjectGenerator(); + } + + @NotNull + @Override + public DirectoryProjectGenerator getDirectoryProjectGenerator() { + return generator; + } + + @Nullable + @Override + public Object getProjectSettings() { + return generator.getProjectSettings(); + } + + @Override + public void setCourse(@NotNull Course course) { + generator.setSelectedCourse(course); + } + + @Override + public ValidationResult validate(@NotNull String path) { + generator.setValidationResult(ValidationResult.OK); + return generator.validate(path); + } + + @Override + public boolean beforeProjectGenerated() { + BooleanFunction function = + generator.beforeProjectGenerated(null); + return function != null && function.fun(generator); + } + + @Override + public void afterProjectGenerated(@NotNull Project project) { + PyNewProjectSettings settings = (PyNewProjectSettings)generator.getProjectSettings(); + Sdk sdk = settings.getSdk(); + + if (sdk == null) { + generator.createAndAddVirtualEnv(project, settings); + sdk = settings.getSdk(); + } + + SdkConfigurationUtil.setDirectoryProjectSdk(project, sdk); + final List sdks = PythonSdkType.getAllSdks(); + for (Sdk s : sdks) { + final SdkAdditionalData additionalData = s.getSdkAdditionalData(); + if (additionalData instanceof PythonSdkAdditionalData) { + ((PythonSdkAdditionalData)additionalData).reassociateWithCreatedProject(project); + } + } + } +} diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java index 1972b5fefcbf..2d5be4f68221 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyEduPluginConfigurator.java @@ -5,8 +5,6 @@ import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.ide.fileTemplates.FileTemplateUtil; import com.intellij.ide.util.DirectoryUtil; -import com.intellij.ide.util.projectWizard.AbstractNewProjectDialog; -import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -16,11 +14,11 @@ import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; -import com.intellij.util.Consumer; import com.intellij.util.PathUtil; +import com.jetbrains.edu.common.newproject.PyEduCourseProjectGenerator; +import com.jetbrains.edu.learning.newproject.EduCourseProjectGenerator; import com.jetbrains.edu.coursecreator.settings.CCSettings; import com.jetbrains.edu.learning.actions.StudyCheckAction; -import com.jetbrains.edu.learning.builtInServer.EduBuiltInServerNewProjectStep; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.tasks.Task; @@ -150,16 +148,7 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator { } @Override - public boolean createCourseProject(@NotNull Course course, @Nullable Consumer onCreated) { - ApplicationManager.getApplication().invokeAndWait(() -> { - AbstractNewProjectDialog dlg = new AbstractNewProjectDialog() { - @Override - protected DefaultActionGroup createRootStep() { - return new EduBuiltInServerNewProjectStep(course, onCreated); - } - }; - dlg.show(); - }); - return true; + public EduCourseProjectGenerator getEduCourseProjectGenerator() { + return new PyEduCourseProjectGenerator(); } } diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index 96dd13b78740..dc5055e6d477 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -23,7 +23,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; import com.intellij.util.BooleanFunction; -import com.intellij.util.Consumer; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.RemoteCourse; @@ -57,17 +56,16 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator myOnCreated; + private final boolean isLocal; public ValidationResult myValidationResult = new ValidationResult("selected course is not valid"); - private final StudyNewProjectPanel mySettingsPanel; @SuppressWarnings("unused") // used on startup public PyStudyDirectoryProjectGenerator() { this(false); } - public PyStudyDirectoryProjectGenerator(boolean isLocal, @Nullable Consumer onCreated) { - myOnCreated = onCreated; + public PyStudyDirectoryProjectGenerator(boolean isLocal) { + this.isLocal = isLocal; myGenerator = new StudyProjectGenerator(); myGenerator.addSettingsStateListener(new StudyProjectGenerator.SettingsListener() { @Override @@ -75,47 +73,6 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator fireStateChanged()); - } - }); - - addErrorLabelMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - final Object selectedItem = mySettingsPanel.getCoursesComboBox().getSelectedItem(); - if (selectedItem != null && ((Course)selectedItem).isAdaptive() && !myGenerator.isLoggedIn()) { - mySettingsPanel.showLoginDialog(false, "Signing In"); - } - } - - @Override - public void mouseEntered(MouseEvent e) { - final Object selectedItem = mySettingsPanel.getCoursesComboBox().getSelectedItem(); - if (selectedItem != null && ((Course)selectedItem).isAdaptive() && !myGenerator.isLoggedIn()) { - e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - } - - @Override - public void mouseExited(MouseEvent e) { - final Course selectedItem = (Course)mySettingsPanel.getCoursesComboBox().getSelectedItem(); - if (selectedItem != null && selectedItem.isAdaptive() && !myGenerator.isLoggedIn()) { - e.getComponent().setCursor(Cursor.getDefaultCursor()); - } - } - }); - } - - public PyStudyDirectoryProjectGenerator(boolean isLocal) { - this(isLocal, null); } @Nls @@ -138,6 +95,10 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator fireStateChanged()); + } + }); + + addErrorLabelMouseListener(new MouseAdapter() { + private boolean isCourseAdaptiveAndNotLogged() { + Course course = myGenerator.getSelectedCourse(); + return course != null && course.isAdaptive() && !myGenerator.isLoggedIn(); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (isCourseAdaptiveAndNotLogged()) { + mySettingsPanel.showLoginDialog(false, "Signing In"); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + if (isCourseAdaptiveAndNotLogged()) { + e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + } + + @Override + public void mouseExited(MouseEvent e) { + if (isCourseAdaptiveAndNotLogged()) { + e.getComponent().setCursor(Cursor.getDefaultCursor()); + } + } + }); + return mySettingsPanel; } @@ -199,7 +191,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator beforeProjectGenerated(@Nullable Sdk sdk) { return generator -> { final List enrolledCoursesIds = myGenerator.getEnrolledCoursesIds(); - final Course course = (Course)mySettingsPanel.getCoursesComboBox().getSelectedItem(); + final Course course = myGenerator.getSelectedCourse(); if (course == null || !(course instanceof RemoteCourse)) return true; if (((RemoteCourse)course).getId() > 0 && !enrolledCoursesIds.contains(((RemoteCourse)course).getId())) { ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { @@ -207,7 +199,6 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator EduStepicConnector.enrollToCourse(((RemoteCourse)course).getId(), StudySettings.getInstance().getUser())); }, "Creating Course", true, ProjectManager.getInstance().getDefaultProject()); - } return true; }; diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerNewProjectStep.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerNewProjectStep.java deleted file mode 100644 index 0e79ddc210da..000000000000 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerNewProjectStep.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jetbrains.edu.learning.builtInServer; - -import com.intellij.openapi.project.Project; -import com.intellij.platform.DirectoryProjectGenerator; -import com.intellij.util.Consumer; -import com.jetbrains.edu.learning.PyStudyDirectoryProjectGenerator; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; -import com.jetbrains.python.newProject.steps.PyCharmNewProjectStep; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; - -/** - * @author meanmail - */ -public class EduBuiltInServerNewProjectStep extends PyCharmNewProjectStep { - public EduBuiltInServerNewProjectStep(@NotNull Course course, @Nullable Consumer onCreated) { - super(new EduBuiltInServerNewProjectStep.MyCustomization(course, onCreated)); - } - - protected static class MyCustomization extends PyCharmNewProjectStep.Customization { - private final Course myCourse; - private final PyStudyDirectoryProjectGenerator myGenerator; - - public MyCustomization(@NotNull Course course, @Nullable Consumer onCreated) { - myCourse = course; - myGenerator = new PyStudyDirectoryProjectGenerator(true, onCreated); - } - - @NotNull - @Override - protected DirectoryProjectGenerator[] getProjectGenerators() { - return new DirectoryProjectGenerator[] {}; - } - - @NotNull - @Override - protected DirectoryProjectGenerator createEmptyProjectGenerator() { - StudyProjectGenerator generator = myGenerator.getGenerator(); - List courses = Collections.singletonList(myCourse); - generator.setCourses(courses); - generator.setSelectedCourse(myCourse); - return myGenerator; - } - } -}