From 669a3e5a67f9fff3082abaa1ddafbf7164af75b2 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 7 Apr 2017 19:49:48 +0300 Subject: [PATCH] full support for stepik in kotlin-java courses --- .../com/jetbrains/edu/learning/Stepik.png | Bin 0 -> 228 bytes .../com/jetbrains/edu/learning/Stepik@2x.png | Bin 0 -> 290 bytes .../learning/courseFormat/RemoteCourse.java | 13 ++- .../StudyProjectGenerator.java | 2 +- .../intellij/EduIntelliJTemplateFactory.java | 28 +++++- .../stepik/EduRemoteCourseModuleBuilder.java | 90 ++++++++++++++++++ .../stepik/EduRemoteCourseTemplate.java | 48 ++++++++++ .../learning/stepic/EduStepicConnector.java | 35 ++++--- .../src/icons/EducationalCoreIcons.java | 1 + 9 files changed, 198 insertions(+), 19 deletions(-) create mode 100644 python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik.png create mode 100644 python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik@2x.png create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseModuleBuilder.java create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseTemplate.java diff --git a/python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik.png b/python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik.png new file mode 100644 index 0000000000000000000000000000000000000000..299f0d9797194cf89db52ce8041f344a34f6d79a GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDURdQ8IJCl}lJn8@c|Ciri z?!4X!l)(d@ZPAhiN;8%O`2{mLJiCzwckjvoxzpWJ7|%vXU|`b0LR+SiyYBc<9wH$6O#=0-TTpV)vubM v?4CSRO=jcgB@YjMJU98-&rj!4)!FLz@V1NXn#)i*3*<6SS3j3^P6Z*$CLj5|9|=Y z<<9G!wdd@Q0A(3Vg8YIR9G=}s196hP-CY0cR)MV6txt;Ds#X-8 ds(NF>-?&sc^xxCmxj;uTc)I$ztaD0e0suFJWFP " - @SerializedName("course_format") private String myType = "pycharm" + EduStepicConnector.CURRENT_VERSION + " " + getLanguageID(); + @SerializedName("course_format") private String myType = + String.format("%s%d %s", EduStepicNames.PYCHARM_PREFIX, EduStepicConnector.CURRENT_VERSION, getLanguageID()); @SerializedName("is_idea_compatible") private boolean isCompatible = true; List sections; List instructors = new ArrayList<>(); @@ -31,6 +33,11 @@ public class RemoteCourse extends Course { return myType; } + public void setLanguage(@NotNull final String language) { + super.setLanguage(language); + updateType(language); + } + public List getSections() { return sections; } @@ -107,8 +114,10 @@ public class RemoteCourse extends Course { public void copyCourseParameters(RemoteCourse course) { setName(course.getName()); - setUpdateDate(course.getUpdateDate()); + } + private void updateType(String language) { + myType = String.format("%s%d %s", EduStepicNames.PYCHARM_PREFIX, EduStepicConnector.CURRENT_VERSION, language); } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index b294e5d29dad..6b25be65eef2 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -144,7 +144,7 @@ public class StudyProjectGenerator { } @NotNull - public List getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @NotNull final Project project) { + public List getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @Nullable final Project project) { try { return ProgressManager.getInstance() .runProcessWithProgressSynchronously(() -> { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/EduIntelliJTemplateFactory.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/EduIntelliJTemplateFactory.java index 48e9ce41923b..19535068e1c1 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/intellij/EduIntelliJTemplateFactory.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/EduIntelliJTemplateFactory.java @@ -3,16 +3,35 @@ package com.jetbrains.edu.learning.intellij; import com.intellij.icons.AllIcons; import com.intellij.ide.util.projectWizard.WizardContext; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.platform.ProjectTemplate; import com.intellij.platform.ProjectTemplatesFactory; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; +import com.jetbrains.edu.learning.intellij.stepik.EduRemoteCourseTemplate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; @SuppressWarnings("unused") //used in other educational plugins that are stored in separate repository public class EduIntelliJTemplateFactory extends ProjectTemplatesFactory { private static final String GROUP_NAME = "Education"; + private static List ourRemoteCourses; + + static { + ProgressManager.getInstance().run(new Task.Backgroundable(null, "Updating Course") { + @Override + public void run(@NotNull ProgressIndicator indicator) { + ourRemoteCourses = new StudyProjectGenerator().getCourses(true); + } + }); + } @NotNull @Override @@ -23,7 +42,14 @@ public class EduIntelliJTemplateFactory extends ProjectTemplatesFactory { @NotNull @Override public ProjectTemplate[] createTemplates(@Nullable String group, WizardContext context) { - return ApplicationManager.getApplication().getExtensions(EduIntelliJProjectTemplate.EP_NAME); + final ArrayList templates = new ArrayList<>(); + if (ourRemoteCourses != null) { + for (Course course : ourRemoteCourses) { + templates.add(new EduRemoteCourseTemplate(course)); + } + } + Collections.addAll(templates, ApplicationManager.getApplication().getExtensions(EduIntelliJProjectTemplate.EP_NAME)); + return templates.toArray(new ProjectTemplate[templates.size()]); } @Override diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseModuleBuilder.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseModuleBuilder.java new file mode 100644 index 000000000000..9aa0282e7985 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseModuleBuilder.java @@ -0,0 +1,90 @@ +package com.jetbrains.edu.learning.intellij.stepik; + +import com.intellij.ide.util.projectWizard.JavaModuleBuilder; +import com.intellij.ide.util.projectWizard.ModuleWizardStep; +import com.intellij.ide.util.projectWizard.ProjectWizardStepFactory; +import com.intellij.ide.util.projectWizard.SettingsStep; +import com.intellij.lang.Language; +import com.intellij.openapi.diagnostic.Logger; +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.util.Conditions; +import com.intellij.openapi.util.InvalidDataException; +import com.jetbrains.edu.learning.EduPluginConfigurator; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator; +import org.jdom.JDOMException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +class EduRemoteCourseModuleBuilder extends JavaModuleBuilder { + private static final Logger LOG = Logger.getInstance(EduRemoteCourseModuleBuilder.class); + private final Course myCourse; + private EduProjectGenerator myGenerator = new EduProjectGenerator(); + + public EduRemoteCourseModuleBuilder(Course course) { + myCourse = course; + } + + @Nullable + @Override + public Module commitModule(@NotNull Project project, @Nullable ModifiableModuleModel model) { + Module module = super.commitModule(project, model); + if (module == null) { + return null; + } + String languageName = myCourse.getLanguageID(); + Language language = Language.findLanguageByID(languageName); + if (language == null) { + return module; + } + EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language); + if (configurator == null) { + return module; + } + configurator.configureModule(module); + return module; + } + + @Nullable + @Override + public String getBuilderId() { + return "remote.course.builder." + myCourse.getName(); + } + + @Nullable + @Override + public ModuleWizardStep modifySettingsStep(@NotNull SettingsStep settingsStep) { + return ProjectWizardStepFactory.getInstance().createJavaSettingsStep(settingsStep, this, Conditions.alwaysTrue()); + } + + @NotNull + @Override + public Module createModule(@NotNull ModifiableModuleModel moduleModel) + throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException { + Module baseModule = super.createModule(moduleModel); + String languageName = myCourse.getLanguageID(); + Language language = Language.findLanguageByID(languageName); + if (language != null) { + EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language); + if (configurator != null) { + Project project = baseModule.getProject(); + myGenerator.setSelectedCourse(myCourse); + myGenerator.generateProject(project, project.getBaseDir()); + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + LOG.info("failed to generate course"); + return baseModule; + } + configurator.createCourseModuleContent(moduleModel, project, course, getModuleFileDirectory()); + } + } + return baseModule; + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseTemplate.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseTemplate.java new file mode 100644 index 000000000000..b3f1bd994e10 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseTemplate.java @@ -0,0 +1,48 @@ +package com.jetbrains.edu.learning.intellij.stepik; + +import com.intellij.ide.util.projectWizard.AbstractModuleBuilder; +import com.intellij.openapi.ui.ValidationInfo; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.intellij.EduIntelliJProjectTemplate; +import icons.EducationalCoreIcons; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class EduRemoteCourseTemplate implements EduIntelliJProjectTemplate { + private final Course myCourse; + + public EduRemoteCourseTemplate(Course course) { + myCourse = course; + } + + @NotNull + @Override + public String getName() { + return myCourse.getName(); + } + + @Nullable + @Override + public String getDescription() { + return myCourse.getDescription(); + } + + @Override + public Icon getIcon() { + return EducationalCoreIcons.Stepik; + } + + @NotNull + @Override + public AbstractModuleBuilder createModuleBuilder() { + return new EduRemoteCourseModuleBuilder(myCourse); + } + + @Nullable + @Override + public ValidationInfo validateSettings() { + return null; + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 1a3adeb35fa2..5ae616d81654 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -152,6 +152,8 @@ public class EduStepicConnector { final List courses = coursesContainer.courses; for (RemoteCourse info : courses) { if (!info.isAdaptive() && StringUtil.isEmptyOrSpaces(info.getType())) continue; + setCourseLanguage(info); + if (canBeOpened(info)) { final ArrayList authors = new ArrayList<>(); for (Integer instructor : info.getInstructors()) { @@ -169,12 +171,19 @@ public class EduStepicConnector { } } - static boolean canBeOpened(RemoteCourse courseInfo) { - if (courseInfo.isAdaptive()) { - return true; + private static void setCourseLanguage(RemoteCourse info) { + if (info.isAdaptive()) { + info.setLanguage("Python"); // adaptive courses available only in PyCharm now + return; } - String courseType = courseInfo.getType(); + String courseType = info.getType(); + final int separator = courseType.indexOf(" "); + assert separator != -1; + final String language = courseType.substring(separator + 1); + info.setLanguage(language); + } + static boolean canBeOpened(RemoteCourse courseInfo) { final ArrayList supportedLanguages = new ArrayList<>(); final LanguageExtensionPoint[] extensions = Extensions.getExtensions(EduPluginConfigurator.EP_NAME, null); for (LanguageExtensionPoint extension : extensions) { @@ -182,12 +191,14 @@ public class EduStepicConnector { supportedLanguages.add(languageId); } + if (courseInfo.isAdaptive()) { + return supportedLanguages.contains(courseInfo.getLanguageID()); + } + + String courseType = courseInfo.getType(); final List typeLanguage = StringUtil.split(courseType, " "); String prefix = typeLanguage.get(0); - if (typeLanguage.size() > 1) { - final String courseLanguage = typeLanguage.get(typeLanguage.size() - 1); - if (!supportedLanguages.contains(courseLanguage)) return false; - } + if (!supportedLanguages.contains(courseInfo.getLanguageID())) return false; if (typeLanguage.size() < 2 || !prefix.startsWith(PYCHARM_PREFIX)) { return false; } @@ -205,14 +216,8 @@ public class EduStepicConnector { } } - public static RemoteCourse getCourse(@NotNull final Project project, @NotNull final RemoteCourse course) { - final RemoteCourse remoteCourse = (RemoteCourse)course.copy(); + public static RemoteCourse getCourse(@NotNull final Project project, @NotNull final RemoteCourse remoteCourse) { if (!remoteCourse.isAdaptive()) { - String courseType = remoteCourse.getType(); - final int separator = courseType.indexOf(" "); - assert separator != -1; - final String language = courseType.substring(separator + 1); - remoteCourse.setLanguage(language); try { for (Integer section : remoteCourse.getSections()) { remoteCourse.addLessons(getLessons(section)); diff --git a/python/educational-core/src/icons/EducationalCoreIcons.java b/python/educational-core/src/icons/EducationalCoreIcons.java index c089db48d76c..73c105a44024 100644 --- a/python/educational-core/src/icons/EducationalCoreIcons.java +++ b/python/educational-core/src/icons/EducationalCoreIcons.java @@ -22,6 +22,7 @@ public class EducationalCoreIcons { public static final Icon ResetTaskFile = load("/icons/com/jetbrains/edu/learning/ResetTaskFile.png"); // 16x16 public static final Icon Sandbox = load("/icons/com/jetbrains/edu/learning/Sandbox.png"); // 16x16 public static final Icon ShowHint = load("/icons/com/jetbrains/edu/learning/showHint.png"); // 16x16 + public static final Icon Stepik = load("/icons/com/jetbrains/edu/learning/Stepik.png"); // 16x16 public static final Icon Task = load("/icons/com/jetbrains/edu/learning/Task.png"); // 16x16 public static final Icon TaskCompl = load("/icons/com/jetbrains/edu/learning/TaskCompl.png"); // 16x16 public static final Icon TaskDescription = load("/icons/com/jetbrains/edu/learning/TaskDescription.png"); // 13x13