diff --git a/python/educational-core/student/resources/META-INF/edu-intellij.xml b/python/educational-core/student/resources/META-INF/edu-intellij.xml
index 7bb472051b3c..4430d18b2802 100644
--- a/python/educational-core/student/resources/META-INF/edu-intellij.xml
+++ b/python/educational-core/student/resources/META-INF/edu-intellij.xml
@@ -2,5 +2,8 @@
+
+
+
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/EduCourseConfigurator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/EduCourseConfigurator.java
index 0eb7fa75f7a7..1e091b2b97ac 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/EduCourseConfigurator.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/EduCourseConfigurator.java
@@ -1,8 +1,12 @@
package com.jetbrains.edu.learning.intellij;
import com.intellij.lang.LanguageExtension;
+import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.project.Project;
+import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
+import com.jetbrains.edu.learning.stepic.CourseInfo;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
@SuppressWarnings("unused") //used in other educational plugins that are stored in separate repository
public interface EduCourseConfigurator {
@@ -10,4 +14,10 @@ public interface EduCourseConfigurator {
LanguageExtension INSTANCE = new LanguageExtension<>(EP_NAME);
default void configureModule(@NotNull final Project project) {}
+
+ default void createCourseFromCourseInfo(@NotNull ModifiableModuleModel moduleModel,
+ @NotNull Project project,
+ @NotNull StudyProjectGenerator generator,
+ @NotNull CourseInfo selectedCourse,
+ @Nullable String moduleDir) {}
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java
new file mode 100644
index 000000000000..fe98b766ea7a
--- /dev/null
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/generation/EduProjectGenerator.java
@@ -0,0 +1,26 @@
+package com.jetbrains.edu.learning.intellij.generation;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.courseFormat.Course;
+import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+public class EduProjectGenerator extends StudyProjectGenerator {
+ private static final Logger LOG = Logger.getInstance(EduProjectGenerator.class);
+
+ @Override
+ public void generateProject(@NotNull Project project, @NotNull VirtualFile baseDir) {
+ final Course course = getCourse(project);
+ if (course == null) {
+ LOG.warn("Failed to get course");
+ return;
+ }
+ StudyTaskManager.getInstance(project).setCourse(course);
+ course.setCourseDirectory(new File(OUR_COURSES_DIR, mySelectedCourseInfo.getName()).getAbsolutePath());
+ }
+}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseModuleBuilder.java b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseModuleBuilder.java
new file mode 100644
index 000000000000..5a80f2c6089a
--- /dev/null
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseModuleBuilder.java
@@ -0,0 +1,102 @@
+package com.jetbrains.edu.learning.intellij.localCourses;
+
+import com.intellij.ide.util.projectWizard.*;
+import com.intellij.lang.Language;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleWithNameAlreadyExists;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.util.Conditions;
+import com.intellij.openapi.util.InvalidDataException;
+import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
+import com.jetbrains.edu.learning.intellij.EduCourseConfigurator;
+import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator;
+import com.jetbrains.edu.learning.stepic.CourseInfo;
+import org.jdom.JDOMException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.IOException;
+
+class EduCustomCourseModuleBuilder extends JavaModuleBuilder {
+ private EduProjectGenerator myGenerator = new EduProjectGenerator();
+ private CourseInfo mySelectedCourse;
+
+ @Nullable
+ @Override
+ public Module commitModule(@NotNull Project project, @Nullable ModifiableModuleModel model) {
+ String type = mySelectedCourse.getType();
+ String languageName = type.substring("pycharm ".length());
+ Language language = Language.findLanguageByID(languageName);
+ if (language != null) {
+ EduCourseConfigurator configurator = EduCourseConfigurator.INSTANCE.forLanguage(language);
+ if (configurator != null) {
+ configurator.configureModule(project);
+ }
+ }
+ return super.commitModule(project, model);
+ }
+
+
+ @Nullable
+ @Override
+ public String getBuilderId() {
+ return "custom.course.builder";
+ }
+
+ @Override
+ public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull ModulesProvider modulesProvider) {
+ return new ModuleWizardStep[]{new EduCourseSelectionSettingsStep(myGenerator)};
+ }
+
+
+ private class EduCourseSelectionSettingsStep extends ModuleWizardStep {
+
+ private final StudyProjectGenerator myGenerator;
+
+ EduCourseSelectionSettingsStep(StudyProjectGenerator generator) {
+ myGenerator = generator;
+ }
+
+ @Override
+ public JComponent getComponent() {
+ return new EduLocalCoursePanel(myGenerator, EduCustomCourseModuleBuilder.this).getContentPanel();
+ }
+
+ @Override
+ public void updateDataModel() {
+ }
+ }
+
+ @Nullable
+ @Override
+ public ModuleWizardStep modifySettingsStep(@NotNull SettingsStep settingsStep) {
+ return ProjectWizardStepFactory.getInstance().createJavaSettingsStep(settingsStep, this, Conditions.alwaysTrue());
+ }
+
+ void setSelectedCourse(CourseInfo selectedCourse) {
+ mySelectedCourse = selectedCourse;
+ }
+
+ @NotNull
+ @Override
+ public Module createModule(@NotNull ModifiableModuleModel moduleModel)
+ throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
+ Module baseModule = super.createModule(moduleModel);
+ if (mySelectedCourse != null) {
+ String type = mySelectedCourse.getType();
+ String languageName = type.substring("pycharm ".length());
+ Language language = Language.findLanguageByID(languageName);
+ if (language != null) {
+ EduCourseConfigurator configurator = EduCourseConfigurator.INSTANCE.forLanguage(language);
+ if (configurator != null) {
+ configurator.createCourseFromCourseInfo(moduleModel, baseModule.getProject(), myGenerator, mySelectedCourse, getModuleFileDirectory());
+ }
+ }
+ }
+ return baseModule;
+ }
+}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseProjectTemplate.java b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseProjectTemplate.java
new file mode 100644
index 000000000000..c80e8f4bc686
--- /dev/null
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduCustomCourseProjectTemplate.java
@@ -0,0 +1,41 @@
+package com.jetbrains.edu.learning.intellij.localCourses;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.util.projectWizard.AbstractModuleBuilder;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.jetbrains.edu.learning.intellij.EduIntelliJProjectTemplate;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+class EduCustomCourseProjectTemplate implements EduIntelliJProjectTemplate {
+ @NotNull
+ @Override
+ public String getName() {
+ return "Load an existing course";
+ }
+
+ @Nullable
+ @Override
+ public String getDescription() {
+ return "Custom Course Addition";
+ }
+
+ @Override
+ public Icon getIcon() {
+ return AllIcons.General.SecondaryGroup;
+ }
+
+ @NotNull
+ @Override
+ public AbstractModuleBuilder createModuleBuilder() {
+ return new EduCustomCourseModuleBuilder();
+ }
+
+ @Nullable
+ @Override
+ public ValidationInfo validateSettings() {
+ return null;
+ }
+}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.form b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.form
new file mode 100644
index 000000000000..19b885d3a4c6
--- /dev/null
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.form
@@ -0,0 +1,53 @@
+
+
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.java b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.java
new file mode 100644
index 000000000000..0fc56d48a2ab
--- /dev/null
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/intellij/localCourses/EduLocalCoursePanel.java
@@ -0,0 +1,56 @@
+package com.jetbrains.edu.learning.intellij.localCourses;
+
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.text.StringUtil;
+import com.jetbrains.edu.learning.courseFormat.Course;
+import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
+import com.jetbrains.edu.learning.stepic.CourseInfo;
+import com.jetbrains.edu.learning.ui.StudyNewProjectPanel;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class EduLocalCoursePanel {
+ private final StudyProjectGenerator myGenerator;
+ private EduCustomCourseModuleBuilder myBuilder;
+ private JPanel myContentPanel;
+ private JPanel myInfoPanel;
+ private TextFieldWithBrowseButton myCourseArchivePath;
+
+ public EduLocalCoursePanel(final StudyProjectGenerator generator, EduCustomCourseModuleBuilder builder) {
+ myGenerator = generator;
+ myBuilder = builder;
+ myCourseArchivePath.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ String coursePath = myCourseArchivePath.getText();
+ CourseInfo courseInfo = generator.addLocalCourse(coursePath);
+ if (courseInfo != null) {
+ //TODO: REMOVE THIS after API CHANGE
+ myBuilder.setSelectedCourse(courseInfo);
+ myGenerator.setSelectedCourse(courseInfo);
+ //TODO: change API to do this!!!
+// final String authorsString = Course.getAuthorsString(courseInfo.getAuthors());
+// ((JLabel)myInfoPanel.getComponent(0)).setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : "");
+// ((JTextPane)myInfoPanel.getComponent(1)).setText(courseInfo.getDescription());
+ }
+ else {
+ //TODO: validate!!!
+ }
+ }
+ });
+ myCourseArchivePath.addBrowseFolderListener("Select Course Archive", null, null,
+ FileChooserDescriptorFactory.createSingleFileDescriptor());
+ }
+
+ public JPanel getContentPanel() {
+ return myContentPanel;
+ }
+
+ private void createUIComponents() {
+ StudyNewProjectPanel panel = new StudyNewProjectPanel(myGenerator);
+ myInfoPanel = panel.getInfoPanel();
+ }
+}
diff --git a/python/educational-core/student/student.iml b/python/educational-core/student/student.iml
index 823dce83ff49..29d9b096e528 100644
--- a/python/educational-core/student/student.iml
+++ b/python/educational-core/student/student.iml
@@ -36,7 +36,8 @@
-
+
+
\ No newline at end of file