From c941b6ab815de5fe4c01bd4418244d799f664b7d Mon Sep 17 00:00:00 2001 From: Petrov Alexander Date: Wed, 5 Apr 2017 17:42:54 +0700 Subject: [PATCH 01/24] Added StepikRestService --- python/educational-core/educational-core.iml | 1 + .../resources/META-INF/plugin.xml | 20 ++++--- .../builtInServer/StepikRestService.java | 55 +++++++++++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java diff --git a/python/educational-core/educational-core.iml b/python/educational-core/educational-core.iml index 058f88e071e4..811dc90aff11 100644 --- a/python/educational-core/educational-core.iml +++ b/python/educational-core/educational-core.iml @@ -33,5 +33,6 @@ + \ No newline at end of file diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml index d1523e23db1d..1ff292d678d4 100644 --- a/python/educational-core/resources/META-INF/plugin.xml +++ b/python/educational-core/resources/META-INF/plugin.xml @@ -40,14 +40,16 @@ - + - + @@ -70,8 +72,9 @@ description="Navigate to the next task"/> - - + + @@ -157,13 +160,15 @@ + serviceImplementation="com.jetbrains.edu.learning.StudyDocumentationManager" overrides="true"/> - - + + @@ -177,6 +182,7 @@ + diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java new file mode 100644 index 000000000000..5cb9c402f33a --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -0,0 +1,55 @@ +/* + * 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.diagnostic.Logger; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.QueryStringDecoder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.ide.RestService; + +import java.io.IOException; + +/** + * @author meanmail + */ +public class StepikRestService extends RestService { + private static final Logger LOG = Logger.getInstance(StepikRestService.class.getName()); + private static final String SERVICE_NAME = "stepik"; + + @NotNull + @Override + protected String getServiceName() { + return SERVICE_NAME; + } + + @Override + protected boolean isMethodSupported(@NotNull HttpMethod method) { + return method.equals(HttpMethod.GET); + } + + @Nullable + @Override + public String execute(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) + throws IOException { + LOG.info("Request: " + urlDecoder.path()); + RestService.sendOk(request, context); + return null; + } +} From 809fc64a039387fc95d4d99cbb3eeccb7731c102 Mon Sep 17 00:00:00 2001 From: meanmail Date: Thu, 6 Apr 2017 04:52:34 +0700 Subject: [PATCH 02/24] Open project by link from recent project list --- .../builtInServer/StepikRestService.java | 99 ++++++++++++++++++- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index 5cb9c402f33a..427f5c8fa5de 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -15,23 +15,54 @@ */ package com.jetbrains.edu.learning.builtInServer; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.intellij.ide.RecentProjectsManager; +import com.intellij.ide.RecentProjectsManagerBase; +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.platform.ProjectSetReader; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.courseFormat.Course; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.QueryStringDecoder; +import org.jdom.Document; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.jdom.input.SAXBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.ide.RestService; +import java.io.File; import java.io.IOException; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author meanmail */ public class StepikRestService extends RestService { + public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; private static final Logger LOG = Logger.getInstance(StepikRestService.class.getName()); private static final String SERVICE_NAME = "stepik"; + private static final Pattern OPEN_COURSE = Pattern.compile("/stepik/course/[^/]*-(\\d+)(?:$|\\?|/.*)"); + + private static void openProject(String path) { + String descriptor = String.format("{\"project\": \"%s\"}", path); + JsonObject jsonObject = new JsonParser().parse(descriptor).getAsJsonObject(); + + ApplicationManager.getApplication().invokeAndWait(() -> { + new ProjectSetReader().readDescriptor(jsonObject, null); + activateLastFocusedFrame(); + }); + } @NotNull @Override @@ -41,7 +72,12 @@ public class StepikRestService extends RestService { @Override protected boolean isMethodSupported(@NotNull HttpMethod method) { - return method.equals(HttpMethod.GET); + return method == HttpMethod.GET; + } + + @Override + protected boolean isPrefixlessAllowed() { + return true; } @Nullable @@ -49,7 +85,64 @@ public class StepikRestService extends RestService { public String execute(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) throws IOException { LOG.info("Request: " + urlDecoder.path()); - RestService.sendOk(request, context); - return null; + + String path = urlDecoder.path(); + Matcher matcher = OPEN_COURSE.matcher(path); + if (matcher.matches()) { + int course_id = Integer.parseInt(matcher.group(1)); + LOG.info("Open course: " + course_id); + + RecentProjectsManagerBase recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); + + if (recentProjectsManager == null) { + return null; + } + + RecentProjectsManagerBase.State state = recentProjectsManager.getState(); + + if (state == null) { + return null; + } + + List recentPaths = state.recentPaths; + + LOG.info(recentPaths.toString()); + + Project project = ProjectManager.getInstance().getDefaultProject(); + StudyTaskManager taskManager = new StudyTaskManager(project); + + SAXBuilder parser = new SAXBuilder(); + Document xmlDoc; + + for (String projectPath : recentPaths) { + try { + String studyProjectXML = projectPath + STUDY_PROJECT_XML_PATH; + xmlDoc = parser.build(new File(studyProjectXML)); + Element root = xmlDoc.getRootElement(); + Element component = root.getChild("component"); + if (component == null) { + continue; + } + + taskManager.loadState(component); + Course course = taskManager.getCourse(); + + if (course == null) { + continue; + } + + if (course.getId() == course_id) { + openProject(projectPath); + RestService.sendOk(request, context); + return null; + } + } + catch (JDOMException | IllegalStateException | IOException ignored) { + } + } + } + + RestService.sendStatus(HttpResponseStatus.NOT_FOUND, false, context.channel()); + return "Unknown command"; } } From 452860a697dbf07a55e39f8ceb768d5f06b94bad Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 7 Apr 2017 02:54:27 +0700 Subject: [PATCH 03/24] Use RemoteCourse class --- .../edu/learning/StudyTaskManager.java | 16 +++-- .../builtInServer/StepikRestService.java | 69 +++++++++++-------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java index d64896ca4bb8..519a56396258 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -46,9 +46,9 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb private static final Logger LOG = Logger.getInstance(StudyTaskManager.class); public static final int CURRENT_VERSION = 5; private Course myCourse; - public int VERSION = 5; + public int VERSION = CURRENT_VERSION; - public Map> myUserTests = new HashMap<>(); + public final Map> myUserTests = new HashMap<>(); public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT; @@ -191,14 +191,18 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb private void updateTestHelper() { StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> ApplicationManager.getApplication().runWriteAction(() -> { - final VirtualFile testHelper = myProject.getBaseDir().findChild(EduNames.TEST_HELPER); + final VirtualFile baseDir = myProject.getBaseDir(); + if (baseDir == null) { + return; + } + final VirtualFile testHelper = baseDir.findChild(EduNames.TEST_HELPER); if (testHelper != null) { StudyUtils.deleteFile(testHelper); } final FileTemplate template = FileTemplateManager.getInstance(myProject).getInternalTemplate(FileUtil.getNameWithoutExtension(EduNames.TEST_HELPER)); try { - final PsiDirectory projectDir = PsiManager.getInstance(myProject).findDirectory(myProject.getBaseDir()); + final PsiDirectory projectDir = PsiManager.getInstance(myProject).findDirectory(baseDir); if (projectDir != null) { FileTemplateUtil.createFromTemplate(template, EduNames.TEST_HELPER, null, projectDir); } @@ -211,8 +215,9 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb private void deserialize(Element state) throws StudySerializationUtils.StudyUnrecognizedFormatException { final Element taskManagerElement = state.getChild(StudySerializationUtils.Xml.MAIN_ELEMENT); - if (taskManagerElement == null) + if (taskManagerElement == null) { throw new StudySerializationUtils.StudyUnrecognizedFormatException(); + } XmlSerializer.deserializeInto(this, taskManagerElement); final Element xmlCourse = StudySerializationUtils.Xml.getChildWithName(taskManagerElement, StudySerializationUtils.COURSE); final Element remoteCourseElement = xmlCourse.getChild(REMOTE_COURSE); @@ -250,5 +255,4 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public void setTurnEditingMode(boolean turnEditingMode) { myTurnEditingMode = turnEditingMode; } - } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index 427f5c8fa5de..eadbc6fdfe10 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -26,6 +26,7 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.platform.ProjectSetReader; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpMethod; @@ -89,8 +90,8 @@ public class StepikRestService extends RestService { String path = urlDecoder.path(); Matcher matcher = OPEN_COURSE.matcher(path); if (matcher.matches()) { - int course_id = Integer.parseInt(matcher.group(1)); - LOG.info("Open course: " + course_id); + int targetCourseId = Integer.parseInt(matcher.group(1)); + LOG.info("Open course: " + targetCourseId); RecentProjectsManagerBase recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); @@ -106,38 +107,21 @@ public class StepikRestService extends RestService { List recentPaths = state.recentPaths; - LOG.info(recentPaths.toString()); - Project project = ProjectManager.getInstance().getDefaultProject(); StudyTaskManager taskManager = new StudyTaskManager(project); - SAXBuilder parser = new SAXBuilder(); - Document xmlDoc; for (String projectPath : recentPaths) { - try { - String studyProjectXML = projectPath + STUDY_PROJECT_XML_PATH; - xmlDoc = parser.build(new File(studyProjectXML)); - Element root = xmlDoc.getRootElement(); - Element component = root.getChild("component"); - if (component == null) { - continue; - } - - taskManager.loadState(component); - Course course = taskManager.getCourse(); - - if (course == null) { - continue; - } - - if (course.getId() == course_id) { - openProject(projectPath); - RestService.sendOk(request, context); - return null; - } + Element component = readComponent(parser, projectPath); + if (component == null) { + continue; } - catch (JDOMException | IllegalStateException | IOException ignored) { + int courseId = getCourseId(taskManager, component); + + if (courseId == targetCourseId) { + openProject(projectPath); + RestService.sendOk(request, context); + return null; } } } @@ -145,4 +129,33 @@ public class StepikRestService extends RestService { RestService.sendStatus(HttpResponseStatus.NOT_FOUND, false, context.channel()); return "Unknown command"; } + + @Nullable + private static Element readComponent(@NotNull SAXBuilder parser, @NotNull String projectPath) { + Element component = null; + try { + String studyProjectXML = projectPath + STUDY_PROJECT_XML_PATH; + Document xmlDoc = parser.build(new File(studyProjectXML)); + Element root = xmlDoc.getRootElement(); + component = root.getChild("component"); + } + catch (JDOMException | IOException ignored) { + } + + return component; + } + + private static int getCourseId(@NotNull StudyTaskManager taskManager, @NotNull Element component) { + try { + taskManager.loadState(component); + Course course = taskManager.getCourse(); + + if ((course instanceof RemoteCourse)) { + return ((RemoteCourse)course).getId(); + } + } + catch (IllegalStateException ignored) { + } + return 0; + } } From e8b8f16d1359f972b05f41156fe47e8f90f3b271 Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 7 Apr 2017 06:03:53 +0700 Subject: [PATCH 04/24] Rename service --- .../edu/learning/builtInServer/StepikRestService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index eadbc6fdfe10..86384f465f1f 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -52,8 +52,8 @@ import java.util.regex.Pattern; public class StepikRestService extends RestService { public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; private static final Logger LOG = Logger.getInstance(StepikRestService.class.getName()); - private static final String SERVICE_NAME = "stepik"; - private static final Pattern OPEN_COURSE = Pattern.compile("/stepik/course/[^/]*-(\\d+)(?:$|\\?|/.*)"); + private static final String SERVICE_NAME = "edu/stepik"; + private static final Pattern OPEN_COURSE = Pattern.compile("/" + SERVICE_NAME + "/course/[^/]*-(\\d+)(?:$|\\?|/.*)"); private static void openProject(String path) { String descriptor = String.format("{\"project\": \"%s\"}", path); From 0dfd5888d017c63602c48e46665cb384edbf8b6e Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 7 Apr 2017 07:20:32 +0700 Subject: [PATCH 05/24] Finds a opened project before find in a recent --- .../builtInServer/StepikRestService.java | 53 ++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index 86384f465f1f..ffd4a1e0fc4c 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -15,15 +15,15 @@ */ package com.jetbrains.edu.learning.builtInServer; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.intellij.ide.RecentProjectsManager; 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.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.platform.ProjectSetReader; +import com.intellij.openapi.wm.IdeFocusManager; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.RemoteCourse; @@ -55,14 +55,21 @@ public class StepikRestService extends RestService { private static final String SERVICE_NAME = "edu/stepik"; private static final Pattern OPEN_COURSE = Pattern.compile("/" + SERVICE_NAME + "/course/[^/]*-(\\d+)(?:$|\\?|/.*)"); - private static void openProject(String path) { - String descriptor = String.format("{\"project\": \"%s\"}", path); - JsonObject jsonObject = new JsonParser().parse(descriptor).getAsJsonObject(); - + private static boolean openProject(String projectPath) { + final boolean[] opened = {false}; ApplicationManager.getApplication().invokeAndWait(() -> { - new ProjectSetReader().readDescriptor(jsonObject, null); - activateLastFocusedFrame(); + final Project[] project = new Project[1]; + TransactionGuard.getInstance().submitTransactionAndWait(() -> { + project[0] = ProjectUtil.openProject(projectPath, null, true); + opened[0] = project[0] != null; + }); + requestDefaultFocus(project[0]); }); + return opened[0]; + } + + private static void requestDefaultFocus(@NotNull Project project) { + IdeFocusManager.getInstance(project).requestDefaultFocus(true); } @NotNull @@ -93,6 +100,11 @@ public class StepikRestService extends RestService { int targetCourseId = Integer.parseInt(matcher.group(1)); LOG.info("Open course: " + targetCourseId); + if (findOpenedProjectAndFocus(targetCourseId)) { + RestService.sendOk(request, context); + return null; + } + RecentProjectsManagerBase recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); if (recentProjectsManager == null) { @@ -118,18 +130,35 @@ public class StepikRestService extends RestService { } int courseId = getCourseId(taskManager, component); - if (courseId == targetCourseId) { - openProject(projectPath); + if (courseId == targetCourseId && openProject(projectPath)) { RestService.sendOk(request, context); return null; } } + RestService.sendStatus(HttpResponseStatus.NOT_FOUND, false, context.channel()); + return "Didn't found or create a project"; } - RestService.sendStatus(HttpResponseStatus.NOT_FOUND, false, context.channel()); + RestService.sendStatus(HttpResponseStatus.BAD_REQUEST, false, context.channel()); return "Unknown command"; } + private static boolean findOpenedProjectAndFocus(int targetCourseId) { + Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); + for (Project project : openProjects) { + StudyTaskManager taskManager = StudyTaskManager.getInstance(project); + if (taskManager != null) { + Course course = taskManager.getCourse(); + RemoteCourse remoteCourse = course != null && course instanceof RemoteCourse ? (RemoteCourse)course : null; + if (remoteCourse != null && remoteCourse.getId() == targetCourseId) { + ApplicationManager.getApplication().invokeLater(() -> requestDefaultFocus(project)); + return true; + } + } + } + return false; + } + @Nullable private static Element readComponent(@NotNull SAXBuilder parser, @NotNull String projectPath) { Element component = null; From c67832fc2db1aaa8b511b2ba97ff86f5b7d1937e Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 7 Apr 2017 07:31:14 +0700 Subject: [PATCH 06/24] Merge plugin.xml --- .../resources/META-INF/plugin.xml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml index 1f517fe20d7b..5890a75bf8fd 100644 --- a/python/educational-core/resources/META-INF/plugin.xml +++ b/python/educational-core/resources/META-INF/plugin.xml @@ -40,16 +40,14 @@ - + - + @@ -72,9 +70,8 @@ description="Navigate to the next task"/> - - + + @@ -160,15 +157,13 @@ + serviceImplementation="com.jetbrains.edu.learning.StudyDocumentationManager" overrides="true"/> - - + + From 0b62b0df93efa41d67b53b92bd0f0564cc96fa10 Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 7 Apr 2017 07:56:49 +0700 Subject: [PATCH 07/24] Corrects a focus request --- .../builtInServer/StepikRestService.java | 92 +++++++++++-------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index ffd4a1e0fc4c..a7dccdb18fe3 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -23,7 +23,8 @@ import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.WindowManager; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.RemoteCourse; @@ -40,6 +41,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.ide.RestService; +import java.awt.*; import java.io.File; import java.io.IOException; import java.util.List; @@ -63,13 +65,16 @@ public class StepikRestService extends RestService { project[0] = ProjectUtil.openProject(projectPath, null, true); opened[0] = project[0] != null; }); - requestDefaultFocus(project[0]); + requestFocus(project[0]); }); return opened[0]; } - private static void requestDefaultFocus(@NotNull Project project) { - IdeFocusManager.getInstance(project).requestDefaultFocus(true); + private static void requestFocus(@NotNull Project project) { + IdeFrame frame = WindowManager.getInstance().getIdeFrame(project); + if (frame instanceof Window) { + ((Window)frame).toFront(); + } } @NotNull @@ -97,44 +102,14 @@ public class StepikRestService extends RestService { String path = urlDecoder.path(); Matcher matcher = OPEN_COURSE.matcher(path); if (matcher.matches()) { - int targetCourseId = Integer.parseInt(matcher.group(1)); - LOG.info("Open course: " + targetCourseId); + int courseId = Integer.parseInt(matcher.group(1)); + LOG.info("Open course: " + courseId); - if (findOpenedProjectAndFocus(targetCourseId)) { + if (findOpenProjectAndFocus(courseId) || findRecentProjectAndOpen(courseId) || createProjectAndOpen(courseId)) { RestService.sendOk(request, context); return null; } - RecentProjectsManagerBase recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); - - if (recentProjectsManager == null) { - return null; - } - - RecentProjectsManagerBase.State state = recentProjectsManager.getState(); - - if (state == null) { - return null; - } - - List recentPaths = state.recentPaths; - - Project project = ProjectManager.getInstance().getDefaultProject(); - StudyTaskManager taskManager = new StudyTaskManager(project); - SAXBuilder parser = new SAXBuilder(); - - for (String projectPath : recentPaths) { - Element component = readComponent(parser, projectPath); - if (component == null) { - continue; - } - int courseId = getCourseId(taskManager, component); - - if (courseId == targetCourseId && openProject(projectPath)) { - RestService.sendOk(request, context); - return null; - } - } RestService.sendStatus(HttpResponseStatus.NOT_FOUND, false, context.channel()); return "Didn't found or create a project"; } @@ -143,15 +118,15 @@ public class StepikRestService extends RestService { return "Unknown command"; } - private static boolean findOpenedProjectAndFocus(int targetCourseId) { + private static boolean findOpenProjectAndFocus(int courseId) { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); for (Project project : openProjects) { StudyTaskManager taskManager = StudyTaskManager.getInstance(project); if (taskManager != null) { Course course = taskManager.getCourse(); RemoteCourse remoteCourse = course != null && course instanceof RemoteCourse ? (RemoteCourse)course : null; - if (remoteCourse != null && remoteCourse.getId() == targetCourseId) { - ApplicationManager.getApplication().invokeLater(() -> requestDefaultFocus(project)); + if (remoteCourse != null && remoteCourse.getId() == courseId) { + ApplicationManager.getApplication().invokeLater(() -> requestFocus(project)); return true; } } @@ -159,6 +134,39 @@ public class StepikRestService extends RestService { return false; } + private static boolean findRecentProjectAndOpen(int targetCourseId) { + RecentProjectsManagerBase recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); + + if (recentProjectsManager == null) { + return false; + } + + RecentProjectsManagerBase.State state = recentProjectsManager.getState(); + + if (state == null) { + return false; + } + + List recentPaths = state.recentPaths; + + Project project = ProjectManager.getInstance().getDefaultProject(); + StudyTaskManager taskManager = new StudyTaskManager(project); + SAXBuilder parser = new SAXBuilder(); + + for (String projectPath : recentPaths) { + Element component = readComponent(parser, projectPath); + if (component == null) { + continue; + } + int courseId = getCourseId(taskManager, component); + + if (courseId == targetCourseId && openProject(projectPath)) { + return true; + } + } + return false; + } + @Nullable private static Element readComponent(@NotNull SAXBuilder parser, @NotNull String projectPath) { Element component = null; @@ -187,4 +195,8 @@ public class StepikRestService extends RestService { } return 0; } + + private static boolean createProjectAndOpen(int courseId) { + return false; + } } From cd47c962ed2945e41e10765d550928d50a02d345 Mon Sep 17 00:00:00 2001 From: meanmail Date: Sat, 8 Apr 2017 00:44:47 +0700 Subject: [PATCH 08/24] The extension point for a project creator --- .../resources/META-INF/plugin.xml | 1 + .../builtInServer/EduProjectCreator.java | 48 +++++++++++++++++++ .../builtInServer/StepikRestService.java | 15 ++++++ 3 files changed, 64 insertions(+) create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml index 5890a75bf8fd..630f26649fc4 100644 --- a/python/educational-core/resources/META-INF/plugin.xml +++ b/python/educational-core/resources/META-INF/plugin.xml @@ -52,6 +52,7 @@ + diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java new file mode 100644 index 000000000000..b1d5fe692292 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java @@ -0,0 +1,48 @@ +/* + * 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.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.Extensions; +import com.jetbrains.edu.learning.courseFormat.Course; +import org.jetbrains.annotations.NotNull; + +/** + * @author meanmail + */ +public class EduProjectCreator { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.eduProjectCreator"); + + public static boolean createProject(@NotNull Course course) { + EduProjectCreator[] extensions = Extensions.getExtensions(EP_NAME); + + for (EduProjectCreator projectCreator : extensions) { + if (projectCreator.canCreateProject(course)) { + return projectCreator.createCourseProject(course); + } + } + + return false; + } + + private boolean createCourseProject(@NotNull Course course) { + return false; + } + + private boolean canCreateProject(@NotNull Course course) { + return false; + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index a7dccdb18fe3..1bc202028d9f 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -28,6 +28,7 @@ import com.intellij.openapi.wm.WindowManager; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.RemoteCourse; +import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpMethod; @@ -44,6 +45,7 @@ import org.jetbrains.ide.RestService; import java.awt.*; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -197,6 +199,19 @@ public class StepikRestService extends RestService { } private static boolean createProjectAndOpen(int courseId) { + EduProjectGenerator generator = new EduProjectGenerator(); + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + String title = "Getting Available Courses"; + List availableCourses = new ArrayList<>(); + ApplicationManager.getApplication().invokeAndWait(() -> { + List courses = generator.getCoursesUnderProgress(true, title, defaultProject); + availableCourses.addAll(courses); + }); + for (Course course : availableCourses) { + if (course instanceof RemoteCourse && ((RemoteCourse)course).getId() == courseId) { + return EduProjectCreator.createProject(course); + } + } return false; } } From 4307fbefedb004ea9401c09acf3d4ed523823255 Mon Sep 17 00:00:00 2001 From: meanmail Date: Sat, 8 Apr 2017 04:36:41 +0700 Subject: [PATCH 09/24] Implementation for a project creator --- .../builtInServer/EduProjectCreator.java | 4 +- .../Edu-Python/Edu-Python.iml | 1 + .../Edu-Python/resources/META-INF/plugin.xml | 1 + .../BuiltInServerNewProjectStep.java | 61 +++++++++++++++++++ .../EduPythonProjectCreator.java | 47 ++++++++++++++ 5 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerNewProjectStep.java create mode 100644 python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java index b1d5fe692292..224ff03ef6f2 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java @@ -38,11 +38,11 @@ public class EduProjectCreator { return false; } - private boolean createCourseProject(@NotNull Course course) { + public boolean createCourseProject(@NotNull Course course) { return false; } - private boolean canCreateProject(@NotNull Course course) { + public boolean canCreateProject(@NotNull Course course) { return false; } } diff --git a/python/educational-python/Edu-Python/Edu-Python.iml b/python/educational-python/Edu-Python/Edu-Python.iml index 815befd49b4b..3460c943ef69 100644 --- a/python/educational-python/Edu-Python/Edu-Python.iml +++ b/python/educational-python/Edu-Python/Edu-Python.iml @@ -15,5 +15,6 @@ + \ No newline at end of file diff --git a/python/educational-python/Edu-Python/resources/META-INF/plugin.xml b/python/educational-python/Edu-Python/resources/META-INF/plugin.xml index 35419980703f..b7a8fb0120f6 100644 --- a/python/educational-python/Edu-Python/resources/META-INF/plugin.xml +++ b/python/educational-python/Edu-Python/resources/META-INF/plugin.xml @@ -37,6 +37,7 @@ + courses = new ArrayList<>(); + courses.add(myCourse); + generator.setCourses(courses); + generator.setSelectedCourse(myCourse); + return myGenerator; + } + } +} diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java new file mode 100644 index 000000000000..cac9d2333c32 --- /dev/null +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java @@ -0,0 +1,47 @@ +/* + * 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.ide.util.projectWizard.AbstractNewProjectDialog; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.application.ApplicationManager; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.python.PythonLanguage; +import org.jetbrains.annotations.NotNull; + +/** + * @author meanmail + */ +public class EduPythonProjectCreator extends EduProjectCreator { + @Override + public boolean canCreateProject(@NotNull Course course) { + return course.getLanguageById() == PythonLanguage.getInstance(); + } + + @Override + public boolean createCourseProject(@NotNull Course course) { + ApplicationManager.getApplication().invokeAndWait(() -> { + AbstractNewProjectDialog dlg = new AbstractNewProjectDialog() { + @Override + protected DefaultActionGroup createRootStep() { + return new BuiltInServerNewProjectStep(course); + } + }; + dlg.show(); + }); + return true; + } +} From a2aecb5daf75f7cccd757333e0a95d16f39dd3c9 Mon Sep 17 00:00:00 2001 From: meanmail Date: Sat, 8 Apr 2017 04:54:57 +0700 Subject: [PATCH 10/24] Extract utils --- .../builtInServer/StepikRestService.java | 148 +--------------- .../edu/learning/builtInServer/Utils.java | 165 ++++++++++++++++++ 2 files changed, 174 insertions(+), 139 deletions(-) create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index 1bc202028d9f..50c51dd0de1e 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -15,70 +15,30 @@ */ package com.jetbrains.edu.learning.builtInServer; -import com.intellij.ide.RecentProjectsManager; -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.project.Project; -import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.wm.IdeFrame; -import com.intellij.openapi.wm.WindowManager; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.RemoteCourse; -import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.QueryStringDecoder; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.input.SAXBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.ide.RestService; -import java.awt.*; -import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.jetbrains.edu.learning.builtInServer.Utils.*; + /** * @author meanmail */ public class StepikRestService extends RestService { - public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; private static final Logger LOG = Logger.getInstance(StepikRestService.class.getName()); private static final String SERVICE_NAME = "edu/stepik"; private static final Pattern OPEN_COURSE = Pattern.compile("/" + SERVICE_NAME + "/course/[^/]*-(\\d+)(?:$|\\?|/.*)"); - private static boolean openProject(String projectPath) { - final boolean[] opened = {false}; - ApplicationManager.getApplication().invokeAndWait(() -> { - final Project[] project = new Project[1]; - TransactionGuard.getInstance().submitTransactionAndWait(() -> { - project[0] = ProjectUtil.openProject(projectPath, null, true); - opened[0] = project[0] != null; - }); - requestFocus(project[0]); - }); - return opened[0]; - } - - private static void requestFocus(@NotNull Project project) { - IdeFrame frame = WindowManager.getInstance().getIdeFrame(project); - if (frame instanceof Window) { - ((Window)frame).toFront(); - } - } - @NotNull @Override protected String getServiceName() { @@ -109,109 +69,19 @@ public class StepikRestService extends RestService { if (findOpenProjectAndFocus(courseId) || findRecentProjectAndOpen(courseId) || createProjectAndOpen(courseId)) { RestService.sendOk(request, context); + LOG.info("Course opened: " + courseId); return null; } RestService.sendStatus(HttpResponseStatus.NOT_FOUND, false, context.channel()); - return "Didn't found or create a project"; + String message = "A project didn't found or created"; + LOG.info(message); + return message; } RestService.sendStatus(HttpResponseStatus.BAD_REQUEST, false, context.channel()); - return "Unknown command"; - } - - private static boolean findOpenProjectAndFocus(int courseId) { - Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); - for (Project project : openProjects) { - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - if (taskManager != null) { - Course course = taskManager.getCourse(); - RemoteCourse remoteCourse = course != null && course instanceof RemoteCourse ? (RemoteCourse)course : null; - if (remoteCourse != null && remoteCourse.getId() == courseId) { - ApplicationManager.getApplication().invokeLater(() -> requestFocus(project)); - return true; - } - } - } - return false; - } - - private static boolean findRecentProjectAndOpen(int targetCourseId) { - RecentProjectsManagerBase recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); - - if (recentProjectsManager == null) { - return false; - } - - RecentProjectsManagerBase.State state = recentProjectsManager.getState(); - - if (state == null) { - return false; - } - - List recentPaths = state.recentPaths; - - Project project = ProjectManager.getInstance().getDefaultProject(); - StudyTaskManager taskManager = new StudyTaskManager(project); - SAXBuilder parser = new SAXBuilder(); - - for (String projectPath : recentPaths) { - Element component = readComponent(parser, projectPath); - if (component == null) { - continue; - } - int courseId = getCourseId(taskManager, component); - - if (courseId == targetCourseId && openProject(projectPath)) { - return true; - } - } - return false; - } - - @Nullable - private static Element readComponent(@NotNull SAXBuilder parser, @NotNull String projectPath) { - Element component = null; - try { - String studyProjectXML = projectPath + STUDY_PROJECT_XML_PATH; - Document xmlDoc = parser.build(new File(studyProjectXML)); - Element root = xmlDoc.getRootElement(); - component = root.getChild("component"); - } - catch (JDOMException | IOException ignored) { - } - - return component; - } - - private static int getCourseId(@NotNull StudyTaskManager taskManager, @NotNull Element component) { - try { - taskManager.loadState(component); - Course course = taskManager.getCourse(); - - if ((course instanceof RemoteCourse)) { - return ((RemoteCourse)course).getId(); - } - } - catch (IllegalStateException ignored) { - } - return 0; - } - - private static boolean createProjectAndOpen(int courseId) { - EduProjectGenerator generator = new EduProjectGenerator(); - Project defaultProject = ProjectManager.getInstance().getDefaultProject(); - String title = "Getting Available Courses"; - List availableCourses = new ArrayList<>(); - ApplicationManager.getApplication().invokeAndWait(() -> { - List courses = generator.getCoursesUnderProgress(true, title, defaultProject); - availableCourses.addAll(courses); - }); - for (Course course : availableCourses) { - if (course instanceof RemoteCourse && ((RemoteCourse)course).getId() == courseId) { - return EduProjectCreator.createProject(course); - } - } - return false; + String message = "Unknown command: " + path; + LOG.info(message); + return message; } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java new file mode 100644 index 000000000000..00fb0156c556 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java @@ -0,0 +1,165 @@ +/* + * 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.ide.RecentProjectsManager; +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.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.WindowManager; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.RemoteCourse; +import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator; +import org.jdom.Document; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.jdom.input.SAXBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author meanmail + */ +public class Utils { + public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; + + public static boolean findOpenProjectAndFocus(int courseId) { + Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); + for (Project project : openProjects) { + StudyTaskManager taskManager = StudyTaskManager.getInstance(project); + if (taskManager != null) { + Course course = taskManager.getCourse(); + RemoteCourse remoteCourse = course instanceof RemoteCourse ? (RemoteCourse)course : null; + if (remoteCourse != null && remoteCourse.getId() == courseId) { + ApplicationManager.getApplication().invokeLater(() -> requestFocus(project)); + return true; + } + } + } + return false; + } + + private static boolean openProject(@NotNull String projectPath) { + final boolean[] opened = {false}; + ApplicationManager.getApplication().invokeAndWait(() -> { + final Project[] project = new Project[1]; + TransactionGuard.getInstance().submitTransactionAndWait(() -> { + project[0] = ProjectUtil.openProject(projectPath, null, true); + opened[0] = project[0] != null; + }); + requestFocus(project[0]); + }); + return opened[0]; + } + + private static void requestFocus(@NotNull Project project) { + IdeFrame frame = WindowManager.getInstance().getIdeFrame(project); + if (frame instanceof Window) { + ((Window)frame).toFront(); + } + } + + public static boolean findRecentProjectAndOpen(int targetCourseId) { + RecentProjectsManagerBase recentProjectsManager; + recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); + + if (recentProjectsManager == null) { + return false; + } + + RecentProjectsManagerBase.State state = recentProjectsManager.getState(); + + if (state == null) { + return false; + } + + List recentPaths = state.recentPaths; + + Project project = ProjectManager.getInstance().getDefaultProject(); + StudyTaskManager taskManager = new StudyTaskManager(project); + SAXBuilder parser = new SAXBuilder(); + + for (String projectPath : recentPaths) { + Element component = readComponent(parser, projectPath); + if (component == null) { + continue; + } + int courseId = getCourseId(taskManager, component); + + if (courseId == targetCourseId && openProject(projectPath)) { + return true; + } + } + return false; + } + + @Nullable + private static Element readComponent(@NotNull SAXBuilder parser, @NotNull String projectPath) { + Element component = null; + try { + String studyProjectXML = projectPath + STUDY_PROJECT_XML_PATH; + Document xmlDoc = parser.build(new File(studyProjectXML)); + Element root = xmlDoc.getRootElement(); + component = root.getChild("component"); + } + catch (JDOMException | IOException ignored) { + } + + return component; + } + + private static int getCourseId(@NotNull StudyTaskManager taskManager, @NotNull Element component) { + try { + taskManager.loadState(component); + Course course = taskManager.getCourse(); + + if (course instanceof RemoteCourse) { + return ((RemoteCourse)course).getId(); + } + } + catch (IllegalStateException ignored) { + } + return 0; + } + + public static boolean createProjectAndOpen(int courseId) { + EduProjectGenerator generator = new EduProjectGenerator(); + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + String title = "Getting Available Courses"; + List availableCourses = new ArrayList<>(); + ApplicationManager.getApplication().invokeAndWait(() -> { + List courses = generator.getCoursesUnderProgress(true, title, defaultProject); + availableCourses.addAll(courses); + }); + for (Course course : availableCourses) { + if (course instanceof RemoteCourse && ((RemoteCourse)course).getId() == courseId) { + return EduProjectCreator.createProject(course); + } + } + return false; + } +} From 3aff09380ddbd2da9187bb373eb36863e9d53dcb Mon Sep 17 00:00:00 2001 From: meanmail Date: Sat, 8 Apr 2017 09:12:32 +0700 Subject: [PATCH 11/24] Navigation to a target task --- .../builtInServer/EduProjectCreator.java | 9 +- .../builtInServer/StepikRestService.java | 18 ++- .../edu/learning/builtInServer/Utils.java | 104 +++++++++++++----- .../learning/navigation/StudyNavigator.java | 7 +- .../PyStudyDirectoryProjectGenerator.java | 15 ++- .../BuiltInServerNewProjectStep.java | 11 +- .../EduPythonProjectCreator.java | 7 +- 7 files changed, 125 insertions(+), 46 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java index 224ff03ef6f2..3e71263cc13f 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java @@ -17,8 +17,11 @@ package com.jetbrains.edu.learning.builtInServer; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.project.Project; +import com.intellij.util.Consumer; import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author meanmail @@ -26,19 +29,19 @@ import org.jetbrains.annotations.NotNull; public class EduProjectCreator { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.eduProjectCreator"); - public static boolean createProject(@NotNull Course course) { + public static boolean createProject(@NotNull Course course, @Nullable Consumer callback) { EduProjectCreator[] extensions = Extensions.getExtensions(EP_NAME); for (EduProjectCreator projectCreator : extensions) { if (projectCreator.canCreateProject(course)) { - return projectCreator.createCourseProject(course); + return projectCreator.createCourseProject(course, callback); } } return false; } - public boolean createCourseProject(@NotNull Course course) { + public boolean createCourseProject(@NotNull Course course, @Nullable Consumer callback) { return false; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index 50c51dd0de1e..f07ec33d9163 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.ide.RestService; import java.io.IOException; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -37,7 +38,8 @@ import static com.jetbrains.edu.learning.builtInServer.Utils.*; public class StepikRestService extends RestService { private static final Logger LOG = Logger.getInstance(StepikRestService.class.getName()); private static final String SERVICE_NAME = "edu/stepik"; - private static final Pattern OPEN_COURSE = Pattern.compile("/" + SERVICE_NAME + "/course/[^/]*-(\\d+)(?:$|\\?|/.*)"); + public static final String STEP_ID = "step_id"; + private static final Pattern OPEN_COURSE = Pattern.compile("/" + SERVICE_NAME + "/course/(\\d+)"); @NotNull @Override @@ -59,15 +61,23 @@ public class StepikRestService extends RestService { @Override public String execute(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) throws IOException { - LOG.info("Request: " + urlDecoder.path()); + LOG.info("Request: " + urlDecoder.uri()); String path = urlDecoder.path(); Matcher matcher = OPEN_COURSE.matcher(path); if (matcher.matches()) { int courseId = Integer.parseInt(matcher.group(1)); - LOG.info("Open course: " + courseId); + List stepIds = urlDecoder.parameters().get(STEP_ID); + int stepId = 0; + if (stepIds != null && !stepIds.isEmpty()) { + try { + stepId = Integer.parseInt(stepIds.get(0)); + } catch (NumberFormatException ignored) { + } + } + LOG.info(String.format("Try to open a course: courseId=%s, stepId=%s", courseId, stepId)); - if (findOpenProjectAndFocus(courseId) || findRecentProjectAndOpen(courseId) || createProjectAndOpen(courseId)) { + if (focusOpenProject(courseId, stepId) || openRecentProject(courseId, stepId) || createProject(courseId, stepId)) { RestService.sendOk(request, context); LOG.info("Course opened: " + courseId); return null; diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java index 00fb0156c556..40f1f319827d 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java @@ -22,11 +22,12 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.wm.IdeFrame; -import com.intellij.openapi.wm.WindowManager; +import com.intellij.util.Consumer; 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.intellij.generation.EduProjectGenerator; import org.jdom.Document; import org.jdom.Element; @@ -35,11 +36,13 @@ import org.jdom.input.SAXBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.*; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; + +import static com.jetbrains.edu.learning.navigation.StudyNavigator.navigateToTask; /** * @author meanmail @@ -47,43 +50,43 @@ import java.util.List; public class Utils { public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; - public static boolean findOpenProjectAndFocus(int courseId) { + public static boolean focusOpenProject(int courseId, int stepId) { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); for (Project project : openProjects) { - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - if (taskManager != null) { - Course course = taskManager.getCourse(); - RemoteCourse remoteCourse = course instanceof RemoteCourse ? (RemoteCourse)course : null; - if (remoteCourse != null && remoteCourse.getId() == courseId) { - ApplicationManager.getApplication().invokeLater(() -> requestFocus(project)); - return true; + if (!project.isDefault()) { + StudyTaskManager taskManager = StudyTaskManager.getInstance(project); + if (taskManager != null) { + Course course = taskManager.getCourse(); + RemoteCourse remoteCourse = course instanceof RemoteCourse ? (RemoteCourse)course : null; + if (remoteCourse != null && remoteCourse.getId() == courseId) { + ApplicationManager.getApplication().invokeLater(() -> { + requestFocus(project); + navigateToStep(project, course, stepId); + }); + return true; + } } } } return false; } - private static boolean openProject(@NotNull String projectPath) { - final boolean[] opened = {false}; + @Nullable + private static Project openProject(@NotNull String projectPath) { + final Project[] project = {null}; ApplicationManager.getApplication().invokeAndWait(() -> { - final Project[] project = new Project[1]; - TransactionGuard.getInstance().submitTransactionAndWait(() -> { - project[0] = ProjectUtil.openProject(projectPath, null, true); - opened[0] = project[0] != null; - }); + TransactionGuard.getInstance().submitTransactionAndWait(() -> + project[0] = ProjectUtil.openProject(projectPath, null, true)); requestFocus(project[0]); }); - return opened[0]; + return project[0]; } private static void requestFocus(@NotNull Project project) { - IdeFrame frame = WindowManager.getInstance().getIdeFrame(project); - if (frame instanceof Window) { - ((Window)frame).toFront(); - } + ProjectUtil.focusProjectWindow(project, false); } - public static boolean findRecentProjectAndOpen(int targetCourseId) { + public static boolean openRecentProject(int targetCourseId, int stepId) { RecentProjectsManagerBase recentProjectsManager; recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); @@ -99,8 +102,8 @@ public class Utils { List recentPaths = state.recentPaths; - Project project = ProjectManager.getInstance().getDefaultProject(); - StudyTaskManager taskManager = new StudyTaskManager(project); + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + StudyTaskManager taskManager = new StudyTaskManager(defaultProject); SAXBuilder parser = new SAXBuilder(); for (String projectPath : recentPaths) { @@ -110,8 +113,17 @@ public class Utils { } int courseId = getCourseId(taskManager, component); - if (courseId == targetCourseId && openProject(projectPath)) { - return true; + if (courseId == targetCourseId) { + Project project = openProject(projectPath); + if (project != null) { + Course course = taskManager.getCourse(); + if (course != null) { + ApplicationManager.getApplication().invokeLater(() -> + navigateToStep(project, course, stepId) + ); + } + return true; + } } } return false; @@ -146,7 +158,7 @@ public class Utils { return 0; } - public static boolean createProjectAndOpen(int courseId) { + public static boolean createProject(int courseId, int stepId) { EduProjectGenerator generator = new EduProjectGenerator(); Project defaultProject = ProjectManager.getInstance().getDefaultProject(); String title = "Getting Available Courses"; @@ -157,9 +169,41 @@ public class Utils { }); for (Course course : availableCourses) { if (course instanceof RemoteCourse && ((RemoteCourse)course).getId() == courseId) { - return EduProjectCreator.createProject(course); + Consumer callback = project -> + ApplicationManager.getApplication().invokeLater(() -> { + StudyTaskManager taskManager = StudyTaskManager.getInstance(project); + Course targetCourse = taskManager.getCourse(); + if (targetCourse != null) { + navigateToStep(project, targetCourse, stepId); + } + }); + return EduProjectCreator.createProject(course, callback); } } return false; } + + private static void navigateToStep(@NotNull Project project, @NotNull Course course, int stepId) { + if (stepId == 0 || course.isAdaptive()) { + return; + } + Task task = getTask(course, stepId); + if (task != null) { + navigateToTask(project, task); + } + } + + @Nullable + private static Task getTask(@NotNull Course course, int stepId) { + List lessons = course.getLessons(); + for (Lesson lesson : lessons) { + Optional optionalTask = lesson.getTaskList().stream() + .filter(task -> task.getStepId() == stepId) + .findFirst(); + if (optionalTask.isPresent()) { + return optionalTask.get(); + } + } + return null; + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java b/python/educational-core/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java index 85b709a9dc63..d5b42cd248eb 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java @@ -1,6 +1,7 @@ package com.jetbrains.edu.learning.navigation; import com.intellij.ide.projectView.ProjectView; +import com.intellij.ide.projectView.impl.AbstractProjectViewPane; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; @@ -190,7 +191,11 @@ public class StudyNavigator { } private static void updateProjectView(@NotNull Project project, @NotNull VirtualFile fileToActivate) { - JTree tree = ProjectView.getInstance(project).getCurrentProjectViewPane().getTree(); + AbstractProjectViewPane viewPane = ProjectView.getInstance(project).getCurrentProjectViewPane(); + if (viewPane == null) { + return; + } + JTree tree = viewPane.getTree(); ProjectView.getInstance(project).selectCB(fileToActivate, fileToActivate, false).doWhenDone(() -> { List paths = TreeUtil.collectExpandedPaths(tree); List toCollapse = new ArrayList<>(); 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 7b4b40317bea..297984784aa8 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,6 +23,7 @@ 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; @@ -56,15 +57,17 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator myCallback; public ValidationResult myValidationResult = new ValidationResult("selected course is not valid"); - private StudyNewProjectPanel mySettingsPanel; + private final StudyNewProjectPanel mySettingsPanel; @SuppressWarnings("unused") // used on startup public PyStudyDirectoryProjectGenerator() { this(false); } - public PyStudyDirectoryProjectGenerator(boolean isLocal) { + public PyStudyDirectoryProjectGenerator(boolean isLocal, @Nullable Consumer callback) { + myCallback = callback; myGenerator = new StudyProjectGenerator(); myGenerator.addSettingsStateListener(new StudyProjectGenerator.SettingsListener() { @Override @@ -111,6 +114,10 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator callback) { + super(new BuiltInServerNewProjectStep.MyCustomization(course, callback)); } protected static class MyCustomization extends PyCharmNewProjectStep.Customization { private final Course myCourse; private final PyStudyDirectoryProjectGenerator myGenerator; - public MyCustomization(@NotNull Course course) { + public MyCustomization(@NotNull Course course, @Nullable Consumer callback) { myCourse = course; - myGenerator = new PyStudyDirectoryProjectGenerator(true); + myGenerator = new PyStudyDirectoryProjectGenerator(true, callback); } @NotNull diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java index cac9d2333c32..405f23416948 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java @@ -18,9 +18,12 @@ package com.jetbrains.edu.learning.builtInServer; import com.intellij.ide.util.projectWizard.AbstractNewProjectDialog; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.util.Consumer; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.python.PythonLanguage; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author meanmail @@ -32,12 +35,12 @@ public class EduPythonProjectCreator extends EduProjectCreator { } @Override - public boolean createCourseProject(@NotNull Course course) { + public boolean createCourseProject(@NotNull Course course, @Nullable Consumer callback) { ApplicationManager.getApplication().invokeAndWait(() -> { AbstractNewProjectDialog dlg = new AbstractNewProjectDialog() { @Override protected DefaultActionGroup createRootStep() { - return new BuiltInServerNewProjectStep(course); + return new BuiltInServerNewProjectStep(course, callback); } }; dlg.show(); From d0b08b2d675cba39ff7462f8b7e7c37cfca95908 Mon Sep 17 00:00:00 2001 From: meanmail Date: Sat, 8 Apr 2017 23:16:39 +0700 Subject: [PATCH 12/24] Minor refactoring --- .../learning/builtInServer/EduProjectCreator.java | 6 +++--- .../edu/learning/builtInServer/Utils.java | 4 ++-- .../learning/PyStudyDirectoryProjectGenerator.java | 14 +++++++++----- .../builtInServer/BuiltInServerNewProjectStep.java | 8 ++++---- .../builtInServer/EduPythonProjectCreator.java | 4 ++-- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java index 3e71263cc13f..6f1bc099fb84 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java @@ -29,19 +29,19 @@ import org.jetbrains.annotations.Nullable; public class EduProjectCreator { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.eduProjectCreator"); - public static boolean createProject(@NotNull Course course, @Nullable Consumer callback) { + public static boolean createProject(@NotNull Course course, @Nullable Consumer onCreated) { EduProjectCreator[] extensions = Extensions.getExtensions(EP_NAME); for (EduProjectCreator projectCreator : extensions) { if (projectCreator.canCreateProject(course)) { - return projectCreator.createCourseProject(course, callback); + return projectCreator.createCourseProject(course, onCreated); } } return false; } - public boolean createCourseProject(@NotNull Course course, @Nullable Consumer callback) { + public boolean createCourseProject(@NotNull Course course, @Nullable Consumer onCreated) { return false; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java index 40f1f319827d..df670f9d353c 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java @@ -169,7 +169,7 @@ public class Utils { }); for (Course course : availableCourses) { if (course instanceof RemoteCourse && ((RemoteCourse)course).getId() == courseId) { - Consumer callback = project -> + Consumer onCreated = project -> ApplicationManager.getApplication().invokeLater(() -> { StudyTaskManager taskManager = StudyTaskManager.getInstance(project); Course targetCourse = taskManager.getCourse(); @@ -177,7 +177,7 @@ public class Utils { navigateToStep(project, targetCourse, stepId); } }); - return EduProjectCreator.createProject(course, callback); + return EduProjectCreator.createProject(course, onCreated); } } return false; 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 297984784aa8..96dd13b78740 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 @@ -57,7 +57,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator myCallback; + private final Consumer myOnCreated; public ValidationResult myValidationResult = new ValidationResult("selected course is not valid"); private final StudyNewProjectPanel mySettingsPanel; @@ -66,8 +66,8 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator callback) { - myCallback = callback; + public PyStudyDirectoryProjectGenerator(boolean isLocal, @Nullable Consumer onCreated) { + myOnCreated = onCreated; myGenerator = new StudyProjectGenerator(); myGenerator.addSettingsStateListener(new StudyProjectGenerator.SettingsListener() { @Override @@ -150,8 +150,12 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator callback) { - super(new BuiltInServerNewProjectStep.MyCustomization(course, callback)); + public BuiltInServerNewProjectStep(@NotNull Course course, @Nullable Consumer onCreated) { + super(new BuiltInServerNewProjectStep.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 callback) { + public MyCustomization(@NotNull Course course, @Nullable Consumer onCreated) { myCourse = course; - myGenerator = new PyStudyDirectoryProjectGenerator(true, callback); + myGenerator = new PyStudyDirectoryProjectGenerator(true, onCreated); } @NotNull diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java index 405f23416948..ca927450bfe6 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java @@ -35,12 +35,12 @@ public class EduPythonProjectCreator extends EduProjectCreator { } @Override - public boolean createCourseProject(@NotNull Course course, @Nullable Consumer callback) { + public boolean createCourseProject(@NotNull Course course, @Nullable Consumer onCreated) { ApplicationManager.getApplication().invokeAndWait(() -> { AbstractNewProjectDialog dlg = new AbstractNewProjectDialog() { @Override protected DefaultActionGroup createRootStep() { - return new BuiltInServerNewProjectStep(course, callback); + return new BuiltInServerNewProjectStep(course, onCreated); } }; dlg.show(); From 48f89ccc7a66945be75607607ebbefa3ea25eb2c Mon Sep 17 00:00:00 2001 From: meanmail Date: Tue, 11 Apr 2017 14:42:36 +0700 Subject: [PATCH 13/24] Refactoring edu/built-in-server utils --- .../{Utils.java => BuiltInServerUtils.java} | 62 +++++++++++-------- .../builtInServer/StepikRestService.java | 2 +- .../jetbrains/edu/learning/core/EduNames.java | 2 + .../learning/stepic/EduStepicConnector.java | 39 +++++++++--- 4 files changed, 70 insertions(+), 35 deletions(-) rename python/educational-core/src/com/jetbrains/edu/learning/builtInServer/{Utils.java => BuiltInServerUtils.java} (77%) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerUtils.java similarity index 77% rename from python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java rename to python/educational-core/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerUtils.java index df670f9d353c..3f704ee5a336 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/Utils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerUtils.java @@ -15,20 +15,24 @@ */ package com.jetbrains.edu.learning.builtInServer; -import com.intellij.ide.RecentProjectsManager; 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.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.intellij.generation.EduProjectGenerator; +import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.StepicUser; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; @@ -38,17 +42,17 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.List; -import java.util.Optional; +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; /** * @author meanmail */ -public class Utils { - public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; +public class BuiltInServerUtils { + private static final Logger LOG = Logger.getInstance(BuiltInServerUtils.class); public static boolean focusOpenProject(int courseId, int stepId) { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); @@ -87,8 +91,7 @@ public class Utils { } public static boolean openRecentProject(int targetCourseId, int stepId) { - RecentProjectsManagerBase recentProjectsManager; - recentProjectsManager = (RecentProjectsManagerBase)RecentProjectsManager.getInstance(); + RecentProjectsManagerBase recentProjectsManager = RecentProjectsManagerBase.getInstanceEx(); if (recentProjectsManager == null) { return false; @@ -153,22 +156,31 @@ public class Utils { return ((RemoteCourse)course).getId(); } } - catch (IllegalStateException ignored) { + catch (IllegalStateException | XmlSerializationException ignored) { } return 0; } public static boolean createProject(int courseId, int stepId) { - EduProjectGenerator generator = new EduProjectGenerator(); + final Course[] course = new Course[1]; Project defaultProject = ProjectManager.getInstance().getDefaultProject(); - String title = "Getting Available Courses"; - List availableCourses = new ArrayList<>(); - ApplicationManager.getApplication().invokeAndWait(() -> { - List courses = generator.getCoursesUnderProgress(true, title, defaultProject); - availableCourses.addAll(courses); - }); - for (Course course : availableCourses) { - if (course instanceof RemoteCourse && ((RemoteCourse)course).getId() == courseId) { + 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(() -> { StudyTaskManager taskManager = StudyTaskManager.getInstance(project); @@ -177,9 +189,8 @@ public class Utils { navigateToStep(project, targetCourse, stepId); } }); - return EduProjectCreator.createProject(course, onCreated); + return EduProjectCreator.createProject(course[0], onCreated); } - } return false; } @@ -195,13 +206,10 @@ public class Utils { @Nullable private static Task getTask(@NotNull Course course, int stepId) { - List lessons = course.getLessons(); - for (Lesson lesson : lessons) { - Optional optionalTask = lesson.getTaskList().stream() - .filter(task -> task.getStepId() == stepId) - .findFirst(); - if (optionalTask.isPresent()) { - return optionalTask.get(); + for (Lesson lesson : course.getLessons()) { + Task task = lesson.getTask(stepId); + if (task != null) { + return task; } } return null; diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index f07ec33d9163..f3a64daf698f 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -30,7 +30,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.jetbrains.edu.learning.builtInServer.Utils.*; +import static com.jetbrains.edu.learning.builtInServer.BuiltInServerUtils.*; /** * @author meanmail diff --git a/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java b/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java index c2c35a63b217..db66e50205b5 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java @@ -50,6 +50,8 @@ public class EduNames { public static final String SUBTASK_MARKER = "_subtask"; public static final String SUBTASK = "subtask"; + public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; + private EduNames() { } 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 5ae616d81654..388e6bc7009b 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 @@ -127,6 +127,17 @@ public class EduStepicConnector { return null; } + private static StepicWrappers.CoursesContainer getCoursesFromStepik(@Nullable StepicUser user, URI url) throws IOException { + final StepicWrappers.CoursesContainer coursesContainer; + if (user != null) { + coursesContainer = EduStepicAuthorizedClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class, user); + } + else { + coursesContainer = EduStepicClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class); + } + return coursesContainer; + } + private static boolean addCoursesFromStepic(@Nullable StepicUser user, List result, int pageNumber) throws IOException { final URI url; try { @@ -137,17 +148,31 @@ public class EduStepicConnector { LOG.error(e.getMessage()); return false; } - final StepicWrappers.CoursesContainer coursesContainer; - if (user != null) { - coursesContainer = EduStepicAuthorizedClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class, user); - } - else { - coursesContainer = EduStepicClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class); - } + final StepicWrappers.CoursesContainer coursesContainer = getCoursesFromStepik(user, url); addAvailableCourses(result, coursesContainer); return coursesContainer.meta.containsKey("has_next") && coursesContainer.meta.get("has_next") == Boolean.TRUE; } + @Nullable + public static Course getCourseFromStepik(@Nullable StepicUser user, int courseId) throws IOException { + final URI url; + try { + url = new URIBuilder(EduStepicNames.COURSES + "/" + courseId).addParameter("is_idea_compatible", "true") + .build(); + } + catch (URISyntaxException e) { + LOG.error(e.getMessage()); + return null; + } + final StepicWrappers.CoursesContainer coursesContainer = getCoursesFromStepik(user, url); + + if (coursesContainer!= null && !coursesContainer.courses.isEmpty()) { + return coursesContainer.courses.get(0); + } else { + return null; + } + } + static void addAvailableCourses(List result, StepicWrappers.CoursesContainer coursesContainer) throws IOException { final List courses = coursesContainer.courses; for (RemoteCourse info : courses) { From a4ee6d7bc20b488a872c2ff06106ee7c15410ae0 Mon Sep 17 00:00:00 2001 From: meanmail Date: Tue, 11 Apr 2017 15:00:17 +0700 Subject: [PATCH 14/24] Log a wrong request parameter --- .../edu/learning/builtInServer/StepikRestService.java | 11 +++++++---- .../src/com/jetbrains/edu/learning/core/EduNames.java | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java index f3a64daf698f..e9bcc6ea8544 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java @@ -31,14 +31,14 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.jetbrains.edu.learning.builtInServer.BuiltInServerUtils.*; +import static com.jetbrains.edu.learning.core.EduNames.SERVICE_NAME; +import static com.jetbrains.edu.learning.core.EduNames.STEP_ID; /** * @author meanmail */ public class StepikRestService extends RestService { private static final Logger LOG = Logger.getInstance(StepikRestService.class.getName()); - private static final String SERVICE_NAME = "edu/stepik"; - public static final String STEP_ID = "step_id"; private static final Pattern OPEN_COURSE = Pattern.compile("/" + SERVICE_NAME + "/course/(\\d+)"); @NotNull @@ -70,9 +70,12 @@ public class StepikRestService extends RestService { List stepIds = urlDecoder.parameters().get(STEP_ID); int stepId = 0; if (stepIds != null && !stepIds.isEmpty()) { + String firstStepId = ""; try { - stepId = Integer.parseInt(stepIds.get(0)); - } catch (NumberFormatException ignored) { + firstStepId = stepIds.get(0); + stepId = Integer.parseInt(firstStepId); + } catch (NumberFormatException e) { + LOG.warn("Wrong a request parameter: step_id=" + firstStepId, e); } } LOG.info(String.format("Try to open a course: courseId=%s, stepId=%s", courseId, stepId)); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java b/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java index db66e50205b5..40c7cea89ad5 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java @@ -51,6 +51,8 @@ public class EduNames { public static final String SUBTASK = "subtask"; public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; + public static final String SERVICE_NAME = "edu/stepik"; + public static final String STEP_ID = "step_id"; private EduNames() { } From c3b513c1fb6d45ea865a5207fc5c51729e6ba83f Mon Sep 17 00:00:00 2001 From: meanmail Date: Tue, 11 Apr 2017 15:21:26 +0700 Subject: [PATCH 15/24] Removed an extension point 'eduProjectCreator' --- .../resources/META-INF/plugin.xml | 3 +- .../edu/learning/EduPluginConfigurator.java | 5 ++ ...rUtils.java => EduBuiltInServerUtils.java} | 11 ++-- .../builtInServer/EduProjectCreator.java | 51 ------------------- ...Service.java => EduStepikRestService.java} | 6 +-- .../Edu-Python/resources/META-INF/plugin.xml | 1 - .../edu/learning/PyEduPluginConfigurator.java | 18 +++++++ ...va => EduBuiltInServerNewProjectStep.java} | 6 +-- .../EduPythonProjectCreator.java | 50 ------------------ 9 files changed, 38 insertions(+), 113 deletions(-) rename python/educational-core/src/com/jetbrains/edu/learning/builtInServer/{BuiltInServerUtils.java => EduBuiltInServerUtils.java} (94%) delete mode 100644 python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java rename python/educational-core/src/com/jetbrains/edu/learning/builtInServer/{StepikRestService.java => EduStepikRestService.java} (93%) rename python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/{BuiltInServerNewProjectStep.java => EduBuiltInServerNewProjectStep.java} (88%) delete mode 100644 python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml index 249be31cb07f..b18e496e6292 100644 --- a/python/educational-core/resources/META-INF/plugin.xml +++ b/python/educational-core/resources/META-INF/plugin.xml @@ -49,7 +49,6 @@ - @@ -173,7 +172,7 @@ - + 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 c41739bef591..4bee8f58103d 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java @@ -12,6 +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.actions.*; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; @@ -131,4 +132,8 @@ public interface EduPluginConfigurator { default List getBundledCoursePaths() { return Collections.emptyList(); } + + default boolean createCourseProject(@NotNull Course course, @Nullable Consumer onCreated) { + return false; + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerUtils.java similarity index 94% rename from python/educational-core/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerUtils.java rename to python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerUtils.java index 3f704ee5a336..b69957f95d3b 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerUtils.java @@ -25,6 +25,7 @@ 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; @@ -51,8 +52,8 @@ import static com.jetbrains.edu.learning.navigation.StudyNavigator.navigateToTas /** * @author meanmail */ -public class BuiltInServerUtils { - private static final Logger LOG = Logger.getInstance(BuiltInServerUtils.class); +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(); @@ -189,7 +190,11 @@ public class BuiltInServerUtils { navigateToStep(project, targetCourse, stepId); } }); - return EduProjectCreator.createProject(course[0], onCreated); + EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course[0].getLanguageById()); + if (configurator == null) { + return false; + } + return configurator.createCourseProject(course[0], onCreated); } return false; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java deleted file mode 100644 index 6f1bc099fb84..000000000000 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduProjectCreator.java +++ /dev/null @@ -1,51 +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.extensions.ExtensionPointName; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.project.Project; -import com.intellij.util.Consumer; -import com.jetbrains.edu.learning.courseFormat.Course; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author meanmail - */ -public class EduProjectCreator { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.eduProjectCreator"); - - public static boolean createProject(@NotNull Course course, @Nullable Consumer onCreated) { - EduProjectCreator[] extensions = Extensions.getExtensions(EP_NAME); - - for (EduProjectCreator projectCreator : extensions) { - if (projectCreator.canCreateProject(course)) { - return projectCreator.createCourseProject(course, onCreated); - } - } - - return false; - } - - public boolean createCourseProject(@NotNull Course course, @Nullable Consumer onCreated) { - return false; - } - - public boolean canCreateProject(@NotNull Course course) { - return false; - } -} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java similarity index 93% rename from python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java rename to python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java index e9bcc6ea8544..5a2ccad1dc3b 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/StepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java @@ -30,15 +30,15 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.jetbrains.edu.learning.builtInServer.BuiltInServerUtils.*; +import static com.jetbrains.edu.learning.builtInServer.EduBuiltInServerUtils.*; import static com.jetbrains.edu.learning.core.EduNames.SERVICE_NAME; import static com.jetbrains.edu.learning.core.EduNames.STEP_ID; /** * @author meanmail */ -public class StepikRestService extends RestService { - private static final Logger LOG = Logger.getInstance(StepikRestService.class.getName()); +public class EduStepikRestService extends RestService { + private static final Logger LOG = Logger.getInstance(EduStepikRestService.class.getName()); private static final Pattern OPEN_COURSE = Pattern.compile("/" + SERVICE_NAME + "/course/(\\d+)"); @NotNull diff --git a/python/educational-python/Edu-Python/resources/META-INF/plugin.xml b/python/educational-python/Edu-Python/resources/META-INF/plugin.xml index e39f149ef86e..1d4b1055bfba 100644 --- a/python/educational-python/Edu-Python/resources/META-INF/plugin.xml +++ b/python/educational-python/Edu-Python/resources/META-INF/plugin.xml @@ -36,7 +36,6 @@ - onCreated) { + ApplicationManager.getApplication().invokeAndWait(() -> { + AbstractNewProjectDialog dlg = new AbstractNewProjectDialog() { + @Override + protected DefaultActionGroup createRootStep() { + return new EduBuiltInServerNewProjectStep(course, onCreated); + } + }; + dlg.show(); + }); + return true; + } } diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerNewProjectStep.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerNewProjectStep.java similarity index 88% rename from python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerNewProjectStep.java rename to python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerNewProjectStep.java index 4b329ffbc3c6..305f88c0cca8 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/BuiltInServerNewProjectStep.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerNewProjectStep.java @@ -30,9 +30,9 @@ import java.util.ArrayList; /** * @author meanmail */ -public class BuiltInServerNewProjectStep extends PyCharmNewProjectStep { - public BuiltInServerNewProjectStep(@NotNull Course course, @Nullable Consumer onCreated) { - super(new BuiltInServerNewProjectStep.MyCustomization(course, onCreated)); +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 { diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java deleted file mode 100644 index ca927450bfe6..000000000000 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduPythonProjectCreator.java +++ /dev/null @@ -1,50 +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.ide.util.projectWizard.AbstractNewProjectDialog; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.intellij.util.Consumer; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.python.PythonLanguage; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author meanmail - */ -public class EduPythonProjectCreator extends EduProjectCreator { - @Override - public boolean canCreateProject(@NotNull Course course) { - return course.getLanguageById() == PythonLanguage.getInstance(); - } - - @Override - public boolean createCourseProject(@NotNull Course course, @Nullable Consumer onCreated) { - ApplicationManager.getApplication().invokeAndWait(() -> { - AbstractNewProjectDialog dlg = new AbstractNewProjectDialog() { - @Override - protected DefaultActionGroup createRootStep() { - return new BuiltInServerNewProjectStep(course, onCreated); - } - }; - dlg.show(); - }); - return true; - } -} From e8a4074457201670f7518d1c45ad2c79f89990db Mon Sep 17 00:00:00 2001 From: meanmail Date: Tue, 11 Apr 2017 21:14:08 +0700 Subject: [PATCH 16/24] Removed a dependency 'python-community-ide' --- python/educational-python/Edu-Python/Edu-Python.iml | 1 - 1 file changed, 1 deletion(-) diff --git a/python/educational-python/Edu-Python/Edu-Python.iml b/python/educational-python/Edu-Python/Edu-Python.iml index 69c8e7ca7d1b..919047b0072a 100644 --- a/python/educational-python/Edu-Python/Edu-Python.iml +++ b/python/educational-python/Edu-Python/Edu-Python.iml @@ -14,6 +14,5 @@ - \ No newline at end of file From f8be70c91691df852e90f9afcee4ffebd735b2c3 Mon Sep 17 00:00:00 2001 From: meanmail Date: Tue, 11 Apr 2017 21:42:34 +0700 Subject: [PATCH 17/24] Minor refactoring --- .../edu/learning/builtInServer/EduStepikRestService.java | 8 ++++---- .../src/com/jetbrains/edu/learning/core/EduNames.java | 2 -- .../com/jetbrains/edu/learning/stepic/EduStepicNames.java | 3 +++ .../builtInServer/EduBuiltInServerNewProjectStep.java | 6 +++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java index 5a2ccad1dc3b..1e0f94ee8b83 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java @@ -31,20 +31,20 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.jetbrains.edu.learning.builtInServer.EduBuiltInServerUtils.*; -import static com.jetbrains.edu.learning.core.EduNames.SERVICE_NAME; -import static com.jetbrains.edu.learning.core.EduNames.STEP_ID; +import static com.jetbrains.edu.learning.stepic.EduStepicNames.EDU_STEPIK_SERVICE_NAME; +import static com.jetbrains.edu.learning.stepic.EduStepicNames.STEP_ID; /** * @author meanmail */ public class EduStepikRestService extends RestService { private static final Logger LOG = Logger.getInstance(EduStepikRestService.class.getName()); - private static final Pattern OPEN_COURSE = Pattern.compile("/" + SERVICE_NAME + "/course/(\\d+)"); + private static final Pattern OPEN_COURSE = Pattern.compile("/" + EDU_STEPIK_SERVICE_NAME + "/course/(\\d+)"); @NotNull @Override protected String getServiceName() { - return SERVICE_NAME; + return EDU_STEPIK_SERVICE_NAME; } @Override diff --git a/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java b/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java index 40c7cea89ad5..db66e50205b5 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/core/EduNames.java @@ -51,8 +51,6 @@ public class EduNames { public static final String SUBTASK = "subtask"; public static final String STUDY_PROJECT_XML_PATH = "/.idea/study_project.xml"; - public static final String SERVICE_NAME = "edu/stepik"; - public static final String STEP_ID = "step_id"; private EduNames() { } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java index 5ee99dc6cde8..e2e8ef92ec1f 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java @@ -23,4 +23,7 @@ public class EduStepicNames { public static final String STEPIC_REGISTRATION_LINK = "https://stepic.org/registration?next=%2Fexplore%2Fcourses"; public static final String PYCHARM_PREFIX = "pycharm"; + + public static final String EDU_STEPIK_SERVICE_NAME = "edu/stepik"; + public static final String STEP_ID = "step_id"; } 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 index 305f88c0cca8..0e79ddc210da 100644 --- 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 @@ -25,7 +25,8 @@ import com.jetbrains.python.newProject.steps.PyCharmNewProjectStep; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * @author meanmail @@ -54,8 +55,7 @@ public class EduBuiltInServerNewProjectStep extends PyCharmNewProjectStep { @Override protected DirectoryProjectGenerator createEmptyProjectGenerator() { StudyProjectGenerator generator = myGenerator.getGenerator(); - ArrayList courses = new ArrayList<>(); - courses.add(myCourse); + List courses = Collections.singletonList(myCourse); generator.setCourses(courses); generator.setSelectedCourse(myCourse); return myGenerator; From 8667ae60ab8827c3fc72bd9a9e4743534719d5b2 Mon Sep 17 00:00:00 2001 From: meanmail Date: Wed, 12 Apr 2017 22:45:58 +0700 Subject: [PATCH 18/24] Added a project create dialog --- .../edu/learning/EduPluginConfigurator.java | 6 +- .../builtInServer/EduBuiltInServerUtils.java | 50 ++--- .../courseGeneration/StudyGenerator.java | 42 ++-- .../newproject/EduCourseProjectGenerator.java | 42 ++++ .../ui/EduCreateNewProjectDialog.java | 189 ++++++++++++++++++ .../ui/EduCreateNewProjectListener.java | 26 +++ .../ui/EduCreateNewProjectPanel.form | 74 +++++++ .../ui/EduCreateNewProjectPanel.java | 90 +++++++++ .../ui/EduCreateNewStepikProjectDialog.java | 68 +++++++ .../edu/learning/ui/StudyNewProjectPanel.java | 4 - .../PyEduCourseProjectGenerator.java | 96 +++++++++ .../edu/learning/PyEduPluginConfigurator.java | 19 +- .../PyStudyDirectoryProjectGenerator.java | 103 +++++----- .../EduBuiltInServerNewProjectStep.java | 64 ------ 14 files changed, 676 insertions(+), 197 deletions(-) create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/newproject/EduCourseProjectGenerator.java create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectDialog.java create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectListener.java create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectPanel.form create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectPanel.java create mode 100644 python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewStepikProjectDialog.java create mode 100644 python/educational-python/Edu-Python/src/com/jetbrains/edu/common/newproject/PyEduCourseProjectGenerator.java delete mode 100644 python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/builtInServer/EduBuiltInServerNewProjectStep.java 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; - } - } -} From 35bc629c480462baf87575e8830fb0c84fef3041 Mon Sep 17 00:00:00 2001 From: meanmail Date: Wed, 12 Apr 2017 23:08:20 +0700 Subject: [PATCH 19/24] Autonaming for new project --- .../edu/learning/newproject/ui/EduCreateNewProjectPanel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index b65a20d45884..dbc7ed938a32 100644 --- 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 @@ -17,6 +17,7 @@ package com.jetbrains.edu.learning.newproject.ui; import com.intellij.icons.AllIcons; import com.intellij.ide.RecentProjectsManager; +import com.intellij.ide.util.projectWizard.ProjectWizardUtil; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.project.Project; @@ -45,9 +46,10 @@ public class EduCreateNewProjectPanel extends JPanel { add(panel, BorderLayout.CENTER); errorIcon.setIcon(AllIcons.Actions.Lightning); resetError(); - nameField.setText("untitled"); String location = RecentProjectsManager.getInstance().getLastProjectCreationLocation(); locationField.setText(location); + String name = ProjectWizardUtil.findNonExistingFileName(location, "course", ""); + nameField.setText(name); FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); locationField.addBrowseFolderListener("Choose Location Folder", null, project, descriptor); locationField.addFocusListener(new FocusAdapter() { From 5aba0901296d41895040ec5b417d2f5156319f28 Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 14 Apr 2017 15:19:38 +0700 Subject: [PATCH 20/24] Apply the code style --- .../builtInServer/EduBuiltInServerUtils.java | 3 -- .../builtInServer/EduStepikRestService.java | 7 +-- .../newproject/EduCourseProjectGenerator.java | 3 -- .../ui/EduCreateNewProjectDialog.java | 51 +++++++++---------- .../ui/EduCreateNewProjectListener.java | 3 -- .../ui/EduCreateNewProjectPanel.form | 10 ++-- .../ui/EduCreateNewProjectPanel.java | 37 +++++++------- .../ui/EduCreateNewStepikProjectDialog.java | 5 +- .../PyEduCourseProjectGenerator.java | 25 ++++----- .../PyStudyDirectoryProjectGenerator.java | 1 - 10 files changed, 60 insertions(+), 85 deletions(-) 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 2e9ea09bfc05..d3b912b6e6ee 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 @@ -44,9 +44,6 @@ import java.util.List; import static com.jetbrains.edu.learning.core.EduNames.STUDY_PROJECT_XML_PATH; import static com.jetbrains.edu.learning.navigation.StudyNavigator.navigateToTask; -/** - * @author meanmail - */ public class EduBuiltInServerUtils { public static boolean focusOpenProject(int courseId, int stepId) { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java index 1e0f94ee8b83..245797f796aa 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/builtInServer/EduStepikRestService.java @@ -34,12 +34,9 @@ import static com.jetbrains.edu.learning.builtInServer.EduBuiltInServerUtils.*; import static com.jetbrains.edu.learning.stepic.EduStepicNames.EDU_STEPIK_SERVICE_NAME; import static com.jetbrains.edu.learning.stepic.EduStepicNames.STEP_ID; -/** - * @author meanmail - */ public class EduStepikRestService extends RestService { private static final Logger LOG = Logger.getInstance(EduStepikRestService.class.getName()); - private static final Pattern OPEN_COURSE = Pattern.compile("/" + EDU_STEPIK_SERVICE_NAME + "/course/(\\d+)"); + private static final Pattern OPEN_COURSE_PATTERN = Pattern.compile("/" + EDU_STEPIK_SERVICE_NAME + "/course/(\\d+)"); @NotNull @Override @@ -64,7 +61,7 @@ public class EduStepikRestService extends RestService { LOG.info("Request: " + urlDecoder.uri()); String path = urlDecoder.path(); - Matcher matcher = OPEN_COURSE.matcher(path); + Matcher matcher = OPEN_COURSE_PATTERN.matcher(path); if (matcher.matches()) { int courseId = Integer.parseInt(matcher.group(1)); List stepIds = urlDecoder.parameters().get(STEP_ID); 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 index 2fb6524fa7ce..704c6c3cf3f8 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/newproject/EduCourseProjectGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/newproject/EduCourseProjectGenerator.java @@ -22,9 +22,6 @@ 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(); 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 index 2e8b6389e4ca..0a70c41d750a 100644 --- 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 @@ -47,21 +47,18 @@ 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; + private Project myProject; + private Course myCourse; + private final List myListeners = new ArrayList<>(); + private final EduCreateNewProjectPanel myPanel; public EduCreateNewProjectDialog() { super(false); - setTitle("New project"); + setTitle("New Project"); Project defaultProject = ProjectManager.getInstance().getDefaultProject(); - panel = new EduCreateNewProjectPanel(defaultProject, this); + myPanel = new EduCreateNewProjectPanel(defaultProject, this); setOKButtonText("Create"); init(); } @@ -69,45 +66,45 @@ public class EduCreateNewProjectDialog extends DialogWrapper { @Nullable @Override protected JComponent createCenterPanel() { - return panel; + return myPanel; } public void addListener(@NotNull EduCreateNewProjectListener listener) { - listeners.add(listener); + myListeners.add(listener); } public void setCourse(@Nullable Course course) { - this.course = course; + this.myCourse = course; } @Override protected void doOKAction() { - if (course == null) { - panel.setError("Selected course is null"); + if (myCourse == null) { + myPanel.setError("Selected course is null"); return; } - Language language = course.getLanguageById(); + Language language = myCourse.getLanguageById(); if (language == null) { String message = "Selected course don't have language"; - panel.setError(message); + myPanel.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); + myPanel.setError(message); LOG.warn(message + ": " + language); return; } EduCourseProjectGenerator projectGenerator = configurator.getEduCourseProjectGenerator(); String errorMessage = createProject(projectGenerator); if (errorMessage != null) { - panel.setError(errorMessage); + myPanel.setError(errorMessage); return; } - if (project == null) { - panel.setError("Project did't created"); + if (myProject == null) { + myPanel.setError("Project did't created"); return; } notifyCreated(); @@ -121,8 +118,8 @@ public class EduCreateNewProjectDialog extends DialogWrapper { */ @Nullable private String createProject(@NotNull final EduCourseProjectGenerator projectGenerator) { - String name = panel.getName(); - String path = FileUtil.join(FileUtil.toSystemDependentName(panel.getLocationPath()), name); + String name = myPanel.getName(); + String path = FileUtil.join(FileUtil.toSystemDependentName(myPanel.getLocationPath()), name); ValidationResult result = projectGenerator.validate(path); if (!result.isOk()) { @@ -136,7 +133,7 @@ public class EduCreateNewProjectDialog extends DialogWrapper { return message; } - projectGenerator.setCourse(course); + projectGenerator.setCourse(myCourse); final VirtualFile baseDir = ApplicationManager.getApplication() .runWriteAction((Computable)() -> @@ -152,9 +149,9 @@ public class EduCreateNewProjectDialog extends DialogWrapper { 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()); + int rc = Messages.showYesNoDialog((Project)null, message, "New Project", Messages.getQuestionIcon()); if (rc != Messages.YES) { - panel.resetError(); + myPanel.resetError(); return "Canceled by user"; } } @@ -179,11 +176,11 @@ public class EduCreateNewProjectDialog extends DialogWrapper { }; } EnumSet options = EnumSet.noneOf(PlatformProjectOpenProcessor.Option.class); - project = PlatformProjectOpenProcessor.doOpenProject(baseDir, null, -1, callback, options); + myProject = PlatformProjectOpenProcessor.doOpenProject(baseDir, null, -1, callback, options); return null; } private void notifyCreated() { - listeners.forEach(listener -> listener.created(project)); + myListeners.forEach(listener -> listener.created(myProject)); } } 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 index 8c034be752e6..3c7b8e122f0c 100644 --- 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 @@ -18,9 +18,6 @@ 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 index 5365323d6369..087a3863e63e 100644 --- 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 @@ -1,6 +1,6 @@
- + @@ -17,7 +17,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -41,7 +41,7 @@ - + @@ -61,7 +61,7 @@ - + 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 index dbc7ed938a32..be758ac5dd39 100644 --- 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 @@ -31,31 +31,28 @@ 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; + private JPanel myPanel; + private JTextField myNameField; + private TextFieldWithBrowseButton myLocationField; + private JLabel myErrorIcon; + private JLabel myErrorLabel; public EduCreateNewProjectPanel(@NotNull final Project project, @NotNull EduCreateNewProjectDialog dialog) { setLayout(new BorderLayout()); - add(panel, BorderLayout.CENTER); - errorIcon.setIcon(AllIcons.Actions.Lightning); + add(myPanel, BorderLayout.CENTER); + myErrorIcon.setIcon(AllIcons.Actions.Lightning); resetError(); String location = RecentProjectsManager.getInstance().getLastProjectCreationLocation(); - locationField.setText(location); + myLocationField.setText(location); String name = ProjectWizardUtil.findNonExistingFileName(location, "course", ""); - nameField.setText(name); + myNameField.setText(name); FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); - locationField.addBrowseFolderListener("Choose Location Folder", null, project, descriptor); - locationField.addFocusListener(new FocusAdapter() { + myLocationField.addBrowseFolderListener("Choose Location Folder", null, project, descriptor); + myLocationField.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { - String location = FileUtil.toSystemDependentName(locationField.getText()); + String location = FileUtil.toSystemDependentName(myLocationField.getText()); File file = new File(location); if (!FileUtil.ensureCanCreateFile(file)) { dialog.setOKActionEnabled(false); @@ -69,21 +66,21 @@ public class EduCreateNewProjectPanel extends JPanel { } private void setState(boolean isVisible) { - errorIcon.setVisible(isVisible); - errorLabel.setVisible(isVisible); + myErrorIcon.setVisible(isVisible); + myErrorLabel.setVisible(isVisible); } void setError(@NotNull String message) { - errorLabel.setText(message); + myErrorLabel.setText(message); setState(true); } public String getName() { - return nameField.getText(); + return myNameField.getText(); } public String getLocationPath() { - return locationField.getText(); + return myLocationField.getText(); } public void resetError() { 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 index f7bebcfd42c7..0c7079b58281 100644 --- 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 @@ -29,9 +29,6 @@ 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); @@ -52,7 +49,7 @@ public class EduCreateNewStepikProjectDialog extends EduCreateNewProjectDialog { try { Course course = EduStepicConnector.getCourseFromStepik(user, courseId); if (course != null) { - setTitle("New project - " + course.getName()); + setTitle("New Project - " + course.getName()); } setCourse(course); 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 index 44a79b44979d..ad0d6f4469c4 100644 --- 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 @@ -34,53 +34,50 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -/** - * @author meanmail - */ public class PyEduCourseProjectGenerator implements EduCourseProjectGenerator { - private final PyStudyDirectoryProjectGenerator generator; + private final PyStudyDirectoryProjectGenerator myGenerator; public PyEduCourseProjectGenerator() { - this.generator = new PyStudyDirectoryProjectGenerator(); + this.myGenerator = new PyStudyDirectoryProjectGenerator(); } @NotNull @Override public DirectoryProjectGenerator getDirectoryProjectGenerator() { - return generator; + return myGenerator; } @Nullable @Override public Object getProjectSettings() { - return generator.getProjectSettings(); + return myGenerator.getProjectSettings(); } @Override public void setCourse(@NotNull Course course) { - generator.setSelectedCourse(course); + myGenerator.setSelectedCourse(course); } @Override public ValidationResult validate(@NotNull String path) { - generator.setValidationResult(ValidationResult.OK); - return generator.validate(path); + myGenerator.setValidationResult(ValidationResult.OK); + return myGenerator.validate(path); } @Override public boolean beforeProjectGenerated() { BooleanFunction function = - generator.beforeProjectGenerated(null); - return function != null && function.fun(generator); + myGenerator.beforeProjectGenerated(null); + return function != null && function.fun(myGenerator); } @Override public void afterProjectGenerated(@NotNull Project project) { - PyNewProjectSettings settings = (PyNewProjectSettings)generator.getProjectSettings(); + PyNewProjectSettings settings = (PyNewProjectSettings)myGenerator.getProjectSettings(); Sdk sdk = settings.getSdk(); if (sdk == null) { - generator.createAndAddVirtualEnv(project, settings); + myGenerator.createAndAddVirtualEnv(project, settings); sdk = settings.getSdk(); } 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 dc5055e6d477..ddccfdce44cc 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 @@ -51,7 +51,6 @@ import java.awt.event.MouseEvent; import java.util.Collection; import java.util.List; - public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator { private static final Logger LOG = Logger.getInstance(PyStudyDirectoryProjectGenerator.class.getName()); private final StudyProjectGenerator myGenerator; From a359e3b72fa6c2319e4c15e0ba1bcf5a91c27734 Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 14 Apr 2017 15:46:38 +0700 Subject: [PATCH 21/24] Removed PyEduCourseProjectGenerator --- .../newproject/EduCourseProjectGenerator.java | 2 +- .../ui/EduCreateNewProjectDialog.java | 3 +- .../PyEduCourseProjectGenerator.java | 93 ------------------- .../edu/learning/PyEduPluginConfigurator.java | 5 +- .../PyStudyDirectoryProjectGenerator.java | 58 +++++++++++- .../PyStudyIntroductionCourseAction.java | 2 +- 6 files changed, 60 insertions(+), 103 deletions(-) delete mode 100644 python/educational-python/Edu-Python/src/com/jetbrains/edu/common/newproject/PyEduCourseProjectGenerator.java 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 index 704c6c3cf3f8..0a0b04f5c09d 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/newproject/EduCourseProjectGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/newproject/EduCourseProjectGenerator.java @@ -31,7 +31,7 @@ public interface EduCourseProjectGenerator { void setCourse(@NotNull Course course); - ValidationResult validate(@NotNull String path); + ValidationResult validate(); boolean beforeProjectGenerated(); 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 index 0a70c41d750a..a80f170f5ebf 100644 --- 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 @@ -121,7 +121,7 @@ public class EduCreateNewProjectDialog extends DialogWrapper { String name = myPanel.getName(); String path = FileUtil.join(FileUtil.toSystemDependentName(myPanel.getLocationPath()), name); - ValidationResult result = projectGenerator.validate(path); + ValidationResult result = projectGenerator.validate(); if (!result.isOk()) { return result.getErrorMessage(); } @@ -170,6 +170,7 @@ public class EduCreateNewProjectDialog extends DialogWrapper { callback = (project, module) -> { if (projectGenerator.beforeProjectGenerated()) { Object settings = projectGenerator.getProjectSettings(); + //noinspection unchecked generator.generateProject(project, baseDir, settings, module); projectGenerator.afterProjectGenerated(project); } 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 deleted file mode 100644 index ad0d6f4469c4..000000000000 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/common/newproject/PyEduCourseProjectGenerator.java +++ /dev/null @@ -1,93 +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.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; - -public class PyEduCourseProjectGenerator implements EduCourseProjectGenerator { - private final PyStudyDirectoryProjectGenerator myGenerator; - - public PyEduCourseProjectGenerator() { - this.myGenerator = new PyStudyDirectoryProjectGenerator(); - } - - @NotNull - @Override - public DirectoryProjectGenerator getDirectoryProjectGenerator() { - return myGenerator; - } - - @Nullable - @Override - public Object getProjectSettings() { - return myGenerator.getProjectSettings(); - } - - @Override - public void setCourse(@NotNull Course course) { - myGenerator.setSelectedCourse(course); - } - - @Override - public ValidationResult validate(@NotNull String path) { - myGenerator.setValidationResult(ValidationResult.OK); - return myGenerator.validate(path); - } - - @Override - public boolean beforeProjectGenerated() { - BooleanFunction function = - myGenerator.beforeProjectGenerated(null); - return function != null && function.fun(myGenerator); - } - - @Override - public void afterProjectGenerated(@NotNull Project project) { - PyNewProjectSettings settings = (PyNewProjectSettings)myGenerator.getProjectSettings(); - Sdk sdk = settings.getSdk(); - - if (sdk == null) { - myGenerator.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 2d5be4f68221..8bb2f43c9af3 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 @@ -15,14 +15,13 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; 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.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; +import com.jetbrains.edu.learning.newproject.EduCourseProjectGenerator; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -149,6 +148,6 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator { @Override public EduCourseProjectGenerator getEduCourseProjectGenerator() { - return new PyEduCourseProjectGenerator(); + return new PyStudyDirectoryProjectGenerator(); } } 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 ddccfdce44cc..ce401b6100eb 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 @@ -18,8 +18,10 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkAdditionalData; import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; import com.intellij.util.BooleanFunction; @@ -27,6 +29,7 @@ import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.RemoteCourse; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; +import com.jetbrains.edu.learning.newproject.EduCourseProjectGenerator; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import com.jetbrains.edu.learning.ui.StudyNewProjectPanel; import com.jetbrains.python.configuration.PyConfigurableInterpreterList; @@ -38,6 +41,7 @@ import com.jetbrains.python.remote.PyProjectSynchronizer; import com.jetbrains.python.sdk.AbstractCreateVirtualEnvDialog; import com.jetbrains.python.sdk.PyDetectedSdk; import com.jetbrains.python.sdk.PythonSdkAdditionalData; +import com.jetbrains.python.sdk.PythonSdkType; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; import icons.InteractiveLearningPythonIcons; import org.jetbrains.annotations.Nls; @@ -51,7 +55,8 @@ import java.awt.event.MouseEvent; import java.util.Collection; import java.util.List; -public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator { +public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator + implements EduCourseProjectGenerator { private static final Logger LOG = Logger.getInstance(PyStudyDirectoryProjectGenerator.class.getName()); private final StudyProjectGenerator myGenerator; private static final String NO_PYTHON_INTERPRETER = "Add python interpreter."; @@ -113,16 +118,61 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks(); + + ValidationResult validationResult; if (sdks.isEmpty()) { - myValidationResult = new ValidationResult(NO_PYTHON_INTERPRETER); + validationResult = new ValidationResult(NO_PYTHON_INTERPRETER); + } else { + validationResult = ValidationResult.OK; + } + + return validationResult; + } + + @NotNull + @Override + public ValidationResult validate(@NotNull String s) { + ValidationResult validationResult = validate(); + if (!validationResult.isOk()) { + myValidationResult = validationResult; } return myValidationResult; } + @Override + public boolean beforeProjectGenerated() { + BooleanFunction function = beforeProjectGenerated(null); + return function != null && function.fun(this); + } + + @Override + public void afterProjectGenerated(@NotNull Project project) { + PyNewProjectSettings settings = (PyNewProjectSettings)getProjectSettings(); + Sdk sdk = settings.getSdk(); + + if (sdk == null) { + 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); + } + } + } + public void setValidationResult(ValidationResult validationResult) { myValidationResult = validationResult; } @@ -172,7 +222,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator Date: Fri, 14 Apr 2017 20:40:44 +0700 Subject: [PATCH 22/24] Removed EduCreateNewProjectListener --- .../edu/learning/StudyProjectComponent.java | 30 +++++++++-- .../edu/learning/StudyTaskManager.java | 9 ++++ .../jetbrains/edu/learning/StudyUtils.java | 23 ++++++++ .../builtInServer/EduBuiltInServerUtils.java | 54 ++++--------------- .../ui/EduCreateNewProjectDialog.java | 18 ++----- .../ui/EduCreateNewProjectListener.java | 23 -------- .../ui/EduCreateNewStepikProjectDialog.java | 9 ++++ 7 files changed, 80 insertions(+), 86 deletions(-) delete mode 100644 python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectListener.java diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java index e2e84ae3ea67..16aa194a1ba4 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -21,6 +21,7 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; @@ -56,13 +57,14 @@ import java.util.List; import java.util.Map; import static com.jetbrains.edu.learning.StudyUtils.execCancelable; +import static com.jetbrains.edu.learning.StudyUtils.navigateToStep; public class StudyProjectComponent implements ProjectComponent { private static final Logger LOG = Logger.getInstance(StudyProjectComponent.class.getName()); private final Project myProject; private FileCreatedByUserListener myListener; - private Map>> myDeletedShortcuts = new HashMap<>(); + private final Map>> myDeletedShortcuts = new HashMap<>(); private MessageBusConnection myBusConnection; private StudyProjectComponent(@NotNull final Project project) { @@ -103,6 +105,29 @@ public class StudyProjectComponent implements ProjectComponent { } } }); + + selectStep(); + } + + private void selectStep() { + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> { + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + if (myProject == defaultProject) { + return; + } + StudyTaskManager defaultTaskManager = StudyTaskManager.getInstance(defaultProject); + int stepId = defaultTaskManager.getStepId(); + + if (stepId != 0) { + StudyTaskManager taskManager = StudyTaskManager.getInstance(myProject); + Course course = taskManager.getCourse(); + if (course != null) { + + navigateToStep(myProject, course, stepId); + defaultTaskManager.setStepId(0); + } + } + }); } private void updateAvailable(Course course) { @@ -122,7 +147,6 @@ public class StudyProjectComponent implements ProjectComponent { }, "Updating Course", true, myProject); EduUtils.synchronize(); course.setUpdated(); - } }); notification.notify(myProject); @@ -272,7 +296,7 @@ public class StudyProjectComponent implements ProjectComponent { AnAction[] newGroupActions = ((ActionGroup)ActionManager.getInstance().getAction("NewGroup")).getChildren(null); for (AnAction newAction : newGroupActions) { if (newAction == action) { - myListener = new FileCreatedByUserListener(); + myListener = new FileCreatedByUserListener(); VirtualFileManager.getInstance().addVirtualFileListener(myListener); break; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java index 519a56396258..210dd11c6a87 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -55,6 +55,7 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb private boolean myTurnEditingMode = false; @Transient private final Project myProject; + @Transient private int myStepId; public StudyTaskManager(Project project) { myProject = project; @@ -255,4 +256,12 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public void setTurnEditingMode(boolean turnEditingMode) { myTurnEditingMode = turnEditingMode; } + + public void setStepId(int stepId) { + myStepId = stepId; + } + + public int getStepId() { + return myStepId; + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java index 46d3ca1b1fc2..0b1b6bcf557e 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java @@ -84,6 +84,8 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import static com.jetbrains.edu.learning.navigation.StudyNavigator.navigateToTask; + public class StudyUtils { private StudyUtils() { } @@ -821,4 +823,25 @@ public class StudyUtils { } } } + + public static void navigateToStep(@NotNull Project project, @NotNull Course course, int stepId) { + if (stepId == 0 || course.isAdaptive()) { + return; + } + Task task = getTask(course, stepId); + if (task != null) { + navigateToTask(project, task); + } + } + + @Nullable + private static Task getTask(@NotNull Course course, int stepId) { + for (Lesson lesson : course.getLessons()) { + Task task = lesson.getTask(stepId); + if (task != null) { + return task; + } + } + return null; + } } 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 d3b912b6e6ee..32408c5262ae 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 @@ -24,11 +24,8 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.util.xmlb.XmlSerializationException; 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.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; @@ -41,8 +38,8 @@ import java.io.File; import java.io.IOException; import java.util.List; +import static com.jetbrains.edu.learning.StudyUtils.navigateToStep; import static com.jetbrains.edu.learning.core.EduNames.STUDY_PROJECT_XML_PATH; -import static com.jetbrains.edu.learning.navigation.StudyNavigator.navigateToTask; public class EduBuiltInServerUtils { public static boolean focusOpenProject(int courseId, int stepId) { @@ -96,8 +93,6 @@ public class EduBuiltInServerUtils { List recentPaths = state.recentPaths; - Project defaultProject = ProjectManager.getInstance().getDefaultProject(); - StudyTaskManager taskManager = new StudyTaskManager(defaultProject); SAXBuilder parser = new SAXBuilder(); for (String projectPath : recentPaths) { @@ -105,17 +100,13 @@ public class EduBuiltInServerUtils { if (component == null) { continue; } + StudyTaskManager taskManager = getDefaultTaskManager(); int courseId = getCourseId(taskManager, component); if (courseId == targetCourseId) { + taskManager.setStepId(stepId); Project project = openProject(projectPath); if (project != null) { - Course course = taskManager.getCourse(); - if (course != null) { - ApplicationManager.getApplication().invokeLater(() -> - navigateToStep(project, course, stepId) - ); - } return true; } } @@ -123,6 +114,7 @@ public class EduBuiltInServerUtils { return false; } + @Nullable private static Element readComponent(@NotNull SAXBuilder parser, @NotNull String projectPath) { Element component = null; @@ -154,44 +146,16 @@ public class EduBuiltInServerUtils { public static boolean createProject(int courseId, int stepId) { ApplicationManager.getApplication().invokeLater(() -> { + getDefaultTaskManager().setStepId(stepId); 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(); - if (targetCourse != null) { - navigateToStep(project, targetCourse, stepId); - } - }); - } - }; - createNewProjectDlg.addListener(listener); createNewProjectDlg.show(); }); return true; } - - private static void navigateToStep(@NotNull Project project, @NotNull Course course, int stepId) { - if (stepId == 0 || course.isAdaptive()) { - return; - } - Task task = getTask(course, stepId); - if (task != null) { - navigateToTask(project, task); - } - } - - @Nullable - private static Task getTask(@NotNull Course course, int stepId) { - for (Lesson lesson : course.getLessons()) { - Task task = lesson.getTask(stepId); - if (task != null) { - return task; - } - } - return null; + @NotNull + private static StudyTaskManager getDefaultTaskManager() { + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + return StudyTaskManager.getInstance(defaultProject); } } 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 index a80f170f5ebf..97e6937dae2a 100644 --- 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 @@ -43,15 +43,12 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.File; -import java.util.ArrayList; import java.util.EnumSet; -import java.util.List; public class EduCreateNewProjectDialog extends DialogWrapper { private static final Logger LOG = Logger.getInstance(EduCreateNewProjectDialog.class); - private Project myProject; - private Course myCourse; - private final List myListeners = new ArrayList<>(); + protected Project myProject; + protected Course myCourse; private final EduCreateNewProjectPanel myPanel; public EduCreateNewProjectDialog() { @@ -69,12 +66,8 @@ public class EduCreateNewProjectDialog extends DialogWrapper { return myPanel; } - public void addListener(@NotNull EduCreateNewProjectListener listener) { - myListeners.add(listener); - } - public void setCourse(@Nullable Course course) { - this.myCourse = course; + myCourse = course; } @Override @@ -107,7 +100,6 @@ public class EduCreateNewProjectDialog extends DialogWrapper { myPanel.setError("Project did't created"); return; } - notifyCreated(); super.doOKAction(); } @@ -180,8 +172,4 @@ public class EduCreateNewProjectDialog extends DialogWrapper { myProject = PlatformProjectOpenProcessor.doOpenProject(baseDir, null, -1, callback, options); return null; } - - private void notifyCreated() { - myListeners.forEach(listener -> listener.created(myProject)); - } } 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 deleted file mode 100644 index 3c7b8e122f0c..000000000000 --- a/python/educational-core/src/com/jetbrains/edu/learning/newproject/ui/EduCreateNewProjectListener.java +++ /dev/null @@ -1,23 +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.newproject.ui; - -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; - -public interface EduCreateNewProjectListener { - void created(@NotNull Project project); -} 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 index 0c7079b58281..7db74ffde68d 100644 --- 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 @@ -62,4 +62,13 @@ public class EduCreateNewStepikProjectDialog extends EduCreateNewProjectDialog { }, "Getting Available Courses", true, defaultProject) ); } + + @Override + public void show() { + if (myCourse != null) { + super.show(); + } else { + doCancelAction(); + } + } } From f4a46d304fdf6953c11a4075d50aae87524f9990 Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 14 Apr 2017 23:12:48 +0700 Subject: [PATCH 23/24] Merged location and name. Added description. (New Project dialog) --- .../ui/EduCreateNewProjectDialog.java | 23 +++++----- .../ui/EduCreateNewProjectPanel.form | 43 +++++++++---------- .../ui/EduCreateNewProjectPanel.java | 38 ++++++++++------ 3 files changed, 59 insertions(+), 45 deletions(-) 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 index 97e6937dae2a..9d7c42ca66fe 100644 --- 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 @@ -68,6 +68,8 @@ public class EduCreateNewProjectDialog extends DialogWrapper { public void setCourse(@Nullable Course course) { myCourse = course; + String description = course != null ? course.getDescription() : ""; + myPanel.setDescription(description); } @Override @@ -103,25 +105,23 @@ public class EduCreateNewProjectDialog extends DialogWrapper { 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 = myPanel.getName(); - String path = FileUtil.join(FileUtil.toSystemDependentName(myPanel.getLocationPath()), name); + String location = FileUtil.toSystemDependentName(myPanel.getLocationPath()); ValidationResult result = projectGenerator.validate(); if (!result.isOk()) { return result.getErrorMessage(); } - final File location = new File(path); - if (!location.exists() && !location.mkdirs()) { + final File directory = new File(location); + if (!FileUtil.createDirectory(directory)) { String message = "Can't create a project directory"; - LOG.error(message + ": " + path); + LOG.error(message + ": " + location); return message; } @@ -129,18 +129,19 @@ public class EduCreateNewProjectDialog extends DialogWrapper { final VirtualFile baseDir = ApplicationManager.getApplication() .runWriteAction((Computable)() -> - LocalFileSystem.getInstance().refreshAndFindFileByIoFile(location) + LocalFileSystem.getInstance().refreshAndFindFileByIoFile(directory) ); if (baseDir == null) { - LOG.error("Couldn't find '" + location + "' in VFS"); + LOG.error("Couldn't find '" + directory + "' 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()); + String.format("Directory '%s' is not empty.\nFiles and directories will remove.\nDo you want continue?", + directory.getAbsolutePath()); int rc = Messages.showYesNoDialog((Project)null, message, "New Project", Messages.getQuestionIcon()); if (rc != Messages.YES) { myPanel.resetError(); @@ -153,11 +154,11 @@ public class EduCreateNewProjectDialog extends DialogWrapper { String generatorName = ConvertUsagesUtil.ensureProperKey(generator.getName()); UsageTrigger.trigger("AbstractNewProjectStep." + generatorName); - RecentProjectsManager.getInstance().setLastProjectCreationLocation(location.getParent()); + RecentProjectsManager.getInstance().setLastProjectCreationLocation(directory.getParent()); ProjectOpenedCallback callback = null; if(generator instanceof TemplateProjectDirectoryGenerator){ - ((TemplateProjectDirectoryGenerator)generator).generateProject(baseDir.getName(), path); + ((TemplateProjectDirectoryGenerator)generator).generateProject(baseDir.getName(), location); } else { callback = (project, module) -> { if (projectGenerator.beforeProjectGenerated()) { 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 index 087a3863e63e..9cc641b0f21a 100644 --- 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 @@ -3,28 +3,11 @@ - + - - - - - - - - - - - - - - - - - @@ -53,8 +36,8 @@ - - + + @@ -63,12 +46,28 @@ - - + + + + + + + + + + + + + + + + + + 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 index be758ac5dd39..d9e71daa53d3 100644 --- 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 @@ -16,11 +16,11 @@ package com.jetbrains.edu.learning.newproject.ui; import com.intellij.icons.AllIcons; -import com.intellij.ide.RecentProjectsManager; -import com.intellij.ide.util.projectWizard.ProjectWizardUtil; +import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapperPeer; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; @@ -33,22 +33,31 @@ import java.io.File; public class EduCreateNewProjectPanel extends JPanel { private JPanel myPanel; - private JTextField myNameField; private TextFieldWithBrowseButton myLocationField; private JLabel myErrorIcon; private JLabel myErrorLabel; + private JTextPane myDescription; public EduCreateNewProjectPanel(@NotNull final Project project, @NotNull EduCreateNewProjectDialog dialog) { setLayout(new BorderLayout()); add(myPanel, BorderLayout.CENTER); myErrorIcon.setIcon(AllIcons.Actions.Lightning); resetError(); - String location = RecentProjectsManager.getInstance().getLastProjectCreationLocation(); + String location = findSequentNonExistingUntitled().toString(); myLocationField.setText(location); - String name = ProjectWizardUtil.findNonExistingFileName(location, "course", ""); - myNameField.setText(name); - FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); - myLocationField.addBrowseFolderListener("Choose Location Folder", null, project, descriptor); + final int index = location.lastIndexOf(File.separator); + if (index > 0) { + JTextField textField = myLocationField.getTextField(); + textField.select(index + 1, location.length()); + textField.putClientProperty(DialogWrapperPeer.HAVE_INITIAL_SELECTION, true); + } + + final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); + myLocationField.addBrowseFolderListener("Select Base Directory", + "Select base directory for the project", + project, + descriptor); + myLocationField.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { @@ -65,6 +74,11 @@ public class EduCreateNewProjectPanel extends JPanel { }); } + @NotNull + protected File findSequentNonExistingUntitled() { + return FileUtil.findSequentNonexistentFile(new File(ProjectUtil.getBaseDir()), "course", ""); + } + private void setState(boolean isVisible) { myErrorIcon.setVisible(isVisible); myErrorLabel.setVisible(isVisible); @@ -75,10 +89,6 @@ public class EduCreateNewProjectPanel extends JPanel { setState(true); } - public String getName() { - return myNameField.getText(); - } - public String getLocationPath() { return myLocationField.getText(); } @@ -86,4 +96,8 @@ public class EduCreateNewProjectPanel extends JPanel { public void resetError() { setState(false); } + + public void setDescription(@NotNull String description) { + myDescription.setText(description); + } } From 0f396676301634119c81ab43b77b565c55a38874 Mon Sep 17 00:00:00 2001 From: meanmail Date: Fri, 14 Apr 2017 23:20:59 +0700 Subject: [PATCH 24/24] Set a correct size for the location field (New Project dialog) --- .../learning/newproject/ui/EduCreateNewProjectPanel.form | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 index 9cc641b0f21a..7a771f682f0f 100644 --- 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 @@ -36,9 +36,7 @@ - - - + @@ -48,7 +46,7 @@ - +