mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'meanmail/EDU-839'
This commit is contained in:
@@ -33,5 +33,6 @@
|
||||
<orderEntry type="module" module-name="idea-ui" />
|
||||
<orderEntry type="module" module-name="openapi" />
|
||||
<orderEntry type="library" name="Guava" level="project" />
|
||||
<orderEntry type="module" module-name="built-in-server" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -173,6 +173,7 @@
|
||||
<applicationService serviceImplementation="com.jetbrains.edu.learning.StudySettings"/>
|
||||
<editorTabTitleProvider implementation="com.jetbrains.edu.learning.StudyTabTitleProvider"/>
|
||||
<typedHandler implementation="com.jetbrains.edu.learning.StudyTypeHandlerDelegate" order="first, before completionAutoPopup"/>
|
||||
<httpRequestHandler implementation="com.jetbrains.edu.learning.builtInServer.EduStepikRestService"/>
|
||||
|
||||
<!--course creator-->
|
||||
<projectService serviceImplementation="com.jetbrains.edu.coursecreator.CCProjectService"/>
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.module.StdModuleTypes;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.jetbrains.edu.learning.newproject.EduCourseProjectGenerator;
|
||||
import com.jetbrains.edu.learning.actions.*;
|
||||
import com.jetbrains.edu.learning.checker.StudyTaskChecker;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
@@ -138,5 +139,7 @@ public interface EduPluginConfigurator {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
EduCourseProjectGenerator getEduCourseProjectGenerator();
|
||||
|
||||
default ModuleType getModuleType() {return StdModuleTypes.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<Keymap, List<Pair<String, String>>> myDeletedShortcuts = new HashMap<>();
|
||||
private final Map<Keymap, List<Pair<String, String>>> 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;
|
||||
}
|
||||
|
||||
@@ -46,15 +46,16 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, 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<Task, List<UserTest>> myUserTests = new HashMap<>();
|
||||
public final Map<Task, List<UserTest>> myUserTests = new HashMap<>();
|
||||
|
||||
public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx();
|
||||
private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT;
|
||||
private boolean myTurnEditingMode = false;
|
||||
|
||||
@Transient private final Project myProject;
|
||||
@Transient private int myStepId;
|
||||
|
||||
public StudyTaskManager(Project project) {
|
||||
myProject = project;
|
||||
@@ -191,14 +192,18 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, 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 +216,9 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, 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);
|
||||
@@ -251,4 +257,11 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, Dumb
|
||||
myTurnEditingMode = turnEditingMode;
|
||||
}
|
||||
|
||||
public void setStepId(int stepId) {
|
||||
myStepId = stepId;
|
||||
}
|
||||
|
||||
public int getStepId() {
|
||||
return myStepId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.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.util.xmlb.XmlSerializationException;
|
||||
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.newproject.ui.EduCreateNewProjectDialog;
|
||||
import com.jetbrains.edu.learning.newproject.ui.EduCreateNewStepikProjectDialog;
|
||||
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.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;
|
||||
|
||||
public class EduBuiltInServerUtils {
|
||||
public static boolean focusOpenProject(int courseId, int stepId) {
|
||||
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
|
||||
for (Project project : openProjects) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Project openProject(@NotNull String projectPath) {
|
||||
final Project[] project = {null};
|
||||
ApplicationManager.getApplication().invokeAndWait(() -> {
|
||||
TransactionGuard.getInstance().submitTransactionAndWait(() ->
|
||||
project[0] = ProjectUtil.openProject(projectPath, null, true));
|
||||
requestFocus(project[0]);
|
||||
});
|
||||
return project[0];
|
||||
}
|
||||
|
||||
private static void requestFocus(@NotNull Project project) {
|
||||
ProjectUtil.focusProjectWindow(project, false);
|
||||
}
|
||||
|
||||
public static boolean openRecentProject(int targetCourseId, int stepId) {
|
||||
RecentProjectsManagerBase recentProjectsManager = RecentProjectsManagerBase.getInstanceEx();
|
||||
|
||||
if (recentProjectsManager == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RecentProjectsManagerBase.State state = recentProjectsManager.getState();
|
||||
|
||||
if (state == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> recentPaths = state.recentPaths;
|
||||
|
||||
SAXBuilder parser = new SAXBuilder();
|
||||
|
||||
for (String projectPath : recentPaths) {
|
||||
Element component = readComponent(parser, projectPath);
|
||||
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) {
|
||||
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 | XmlSerializationException ignored) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static boolean createProject(int courseId, int stepId) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
getDefaultTaskManager().setStepId(stepId);
|
||||
EduCreateNewProjectDialog createNewProjectDlg = new EduCreateNewStepikProjectDialog(courseId);
|
||||
createNewProjectDlg.show();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
@NotNull
|
||||
private static StudyTaskManager getDefaultTaskManager() {
|
||||
Project defaultProject = ProjectManager.getInstance().getDefaultProject();
|
||||
return StudyTaskManager.getInstance(defaultProject);
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.HttpResponseStatus;
|
||||
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;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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;
|
||||
|
||||
public class EduStepikRestService extends RestService {
|
||||
private static final Logger LOG = Logger.getInstance(EduStepikRestService.class.getName());
|
||||
private static final Pattern OPEN_COURSE_PATTERN = Pattern.compile("/" + EDU_STEPIK_SERVICE_NAME + "/course/(\\d+)");
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getServiceName() {
|
||||
return EDU_STEPIK_SERVICE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMethodSupported(@NotNull HttpMethod method) {
|
||||
return method == HttpMethod.GET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isPrefixlessAllowed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String execute(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context)
|
||||
throws IOException {
|
||||
LOG.info("Request: " + urlDecoder.uri());
|
||||
|
||||
String path = urlDecoder.path();
|
||||
Matcher matcher = OPEN_COURSE_PATTERN.matcher(path);
|
||||
if (matcher.matches()) {
|
||||
int courseId = Integer.parseInt(matcher.group(1));
|
||||
List<String> stepIds = urlDecoder.parameters().get(STEP_ID);
|
||||
int stepId = 0;
|
||||
if (stepIds != null && !stepIds.isEmpty()) {
|
||||
String firstStepId = "";
|
||||
try {
|
||||
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));
|
||||
|
||||
if (focusOpenProject(courseId, stepId) || openRecentProject(courseId, stepId) || createProject(courseId, stepId)) {
|
||||
RestService.sendOk(request, context);
|
||||
LOG.info("Course opened: " + courseId);
|
||||
return null;
|
||||
}
|
||||
|
||||
RestService.sendStatus(HttpResponseStatus.NOT_FOUND, false, context.channel());
|
||||
String message = "A project didn't found or created";
|
||||
LOG.info(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
RestService.sendStatus(HttpResponseStatus.BAD_REQUEST, false, context.channel());
|
||||
String message = "Unknown command: " + path;
|
||||
LOG.info(message);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
|
||||
+24
-18
@@ -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<Task> 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<String, TaskFile> 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<String, String> texts = task.getTaskTexts();
|
||||
createFiles(taskDir, texts);
|
||||
}
|
||||
|
||||
private static void createTestFiles(@NotNull VirtualFile taskDir, @NotNull Task task) throws IOException {
|
||||
final Map<String, String> tests = task.getTestsText();
|
||||
createFiles(taskDir, tests);
|
||||
}
|
||||
|
||||
private static void createFiles(@NotNull VirtualFile taskDir, @NotNull Map<String, String> texts) throws IOException {
|
||||
for (Map.Entry<String, String> 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<String, String> tests = task.getTestsText();
|
||||
for (Map.Entry<String, String> 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<Task> 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));
|
||||
}
|
||||
|
||||
+6
-1
@@ -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<TreePath> paths = TreeUtil.collectExpandedPaths(tree);
|
||||
List<TreePath> toCollapse = new ArrayList<>();
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface EduCourseProjectGenerator {
|
||||
@NotNull
|
||||
DirectoryProjectGenerator getDirectoryProjectGenerator();
|
||||
|
||||
@Nullable
|
||||
Object getProjectSettings();
|
||||
|
||||
void setCourse(@NotNull Course course);
|
||||
|
||||
ValidationResult validate();
|
||||
|
||||
boolean beforeProjectGenerated();
|
||||
|
||||
void afterProjectGenerated(@NotNull Project project);
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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.EnumSet;
|
||||
|
||||
public class EduCreateNewProjectDialog extends DialogWrapper {
|
||||
private static final Logger LOG = Logger.getInstance(EduCreateNewProjectDialog.class);
|
||||
protected Project myProject;
|
||||
protected Course myCourse;
|
||||
private final EduCreateNewProjectPanel myPanel;
|
||||
|
||||
public EduCreateNewProjectDialog() {
|
||||
super(false);
|
||||
setTitle("New Project");
|
||||
Project defaultProject = ProjectManager.getInstance().getDefaultProject();
|
||||
myPanel = new EduCreateNewProjectPanel(defaultProject, this);
|
||||
setOKButtonText("Create");
|
||||
init();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
public void setCourse(@Nullable Course course) {
|
||||
myCourse = course;
|
||||
String description = course != null ? course.getDescription() : "";
|
||||
myPanel.setDescription(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
if (myCourse == null) {
|
||||
myPanel.setError("Selected course is null");
|
||||
return;
|
||||
}
|
||||
Language language = myCourse.getLanguageById();
|
||||
if (language == null) {
|
||||
String message = "Selected course don't have language";
|
||||
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";
|
||||
myPanel.setError(message);
|
||||
LOG.warn(message + ": " + language);
|
||||
return;
|
||||
}
|
||||
EduCourseProjectGenerator projectGenerator = configurator.getEduCourseProjectGenerator();
|
||||
String errorMessage = createProject(projectGenerator);
|
||||
if (errorMessage != null) {
|
||||
myPanel.setError(errorMessage);
|
||||
return;
|
||||
}
|
||||
if (myProject == null) {
|
||||
myPanel.setError("Project did't created");
|
||||
return;
|
||||
}
|
||||
super.doOKAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param projectGenerator
|
||||
* @return error message if didn't create project else return null
|
||||
*/
|
||||
@Nullable
|
||||
private String createProject(@NotNull final EduCourseProjectGenerator projectGenerator) {
|
||||
String location = FileUtil.toSystemDependentName(myPanel.getLocationPath());
|
||||
|
||||
ValidationResult result = projectGenerator.validate();
|
||||
if (!result.isOk()) {
|
||||
return result.getErrorMessage();
|
||||
}
|
||||
|
||||
final File directory = new File(location);
|
||||
if (!FileUtil.createDirectory(directory)) {
|
||||
String message = "Can't create a project directory";
|
||||
LOG.error(message + ": " + location);
|
||||
return message;
|
||||
}
|
||||
|
||||
projectGenerator.setCourse(myCourse);
|
||||
|
||||
final VirtualFile baseDir = ApplicationManager.getApplication()
|
||||
.runWriteAction((Computable<VirtualFile>)() ->
|
||||
LocalFileSystem.getInstance().refreshAndFindFileByIoFile(directory)
|
||||
);
|
||||
|
||||
if (baseDir == null) {
|
||||
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?",
|
||||
directory.getAbsolutePath());
|
||||
int rc = Messages.showYesNoDialog((Project)null, message, "New Project", Messages.getQuestionIcon());
|
||||
if (rc != Messages.YES) {
|
||||
myPanel.resetError();
|
||||
return "Canceled by user";
|
||||
}
|
||||
}
|
||||
|
||||
DirectoryProjectGenerator generator = projectGenerator.getDirectoryProjectGenerator();
|
||||
|
||||
String generatorName = ConvertUsagesUtil.ensureProperKey(generator.getName());
|
||||
UsageTrigger.trigger("AbstractNewProjectStep." + generatorName);
|
||||
|
||||
RecentProjectsManager.getInstance().setLastProjectCreationLocation(directory.getParent());
|
||||
|
||||
ProjectOpenedCallback callback = null;
|
||||
if(generator instanceof TemplateProjectDirectoryGenerator){
|
||||
((TemplateProjectDirectoryGenerator)generator).generateProject(baseDir.getName(), location);
|
||||
} else {
|
||||
callback = (project, module) -> {
|
||||
if (projectGenerator.beforeProjectGenerated()) {
|
||||
Object settings = projectGenerator.getProjectSettings();
|
||||
//noinspection unchecked
|
||||
generator.generateProject(project, baseDir, settings, module);
|
||||
projectGenerator.afterProjectGenerated(project);
|
||||
}
|
||||
};
|
||||
}
|
||||
EnumSet<PlatformProjectOpenProcessor.Option> options = EnumSet.noneOf(PlatformProjectOpenProcessor.Option.class);
|
||||
myProject = PlatformProjectOpenProcessor.doOpenProject(baseDir, null, -1, callback, options);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.edu.learning.newproject.ui.EduCreateNewProjectPanel">
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="507" height="325"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="29be7" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="4fa15" class="javax.swing.JLabel" binding="myErrorIcon">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value=""/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="4bdcf" class="javax.swing.JLabel" binding="myErrorLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value=""/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<component id="628ab" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Location:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="aab8" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myLocationField">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
|
||||
<minimum-size width="400" height="-1"/>
|
||||
<preferred-size width="400" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<scrollpane id="69178" class="com.intellij.ui.components.JBScrollPane">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
|
||||
<minimum-size width="-1" height="150"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="bdfa2" class="javax.swing.JTextPane" binding="myDescription">
|
||||
<constraints/>
|
||||
<properties/>
|
||||
</component>
|
||||
</children>
|
||||
</scrollpane>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.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;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.io.File;
|
||||
|
||||
public class EduCreateNewProjectPanel extends JPanel {
|
||||
private JPanel myPanel;
|
||||
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 = findSequentNonExistingUntitled().toString();
|
||||
myLocationField.setText(location);
|
||||
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) {
|
||||
String location = FileUtil.toSystemDependentName(myLocationField.getText());
|
||||
File file = new File(location);
|
||||
if (!FileUtil.ensureCanCreateFile(file)) {
|
||||
dialog.setOKActionEnabled(false);
|
||||
setError("Invalid location");
|
||||
} else {
|
||||
dialog.setOKActionEnabled(true);
|
||||
resetError();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected File findSequentNonExistingUntitled() {
|
||||
return FileUtil.findSequentNonexistentFile(new File(ProjectUtil.getBaseDir()), "course", "");
|
||||
}
|
||||
|
||||
private void setState(boolean isVisible) {
|
||||
myErrorIcon.setVisible(isVisible);
|
||||
myErrorLabel.setVisible(isVisible);
|
||||
}
|
||||
|
||||
void setError(@NotNull String message) {
|
||||
myErrorLabel.setText(message);
|
||||
setState(true);
|
||||
}
|
||||
|
||||
public String getLocationPath() {
|
||||
return myLocationField.getText();
|
||||
}
|
||||
|
||||
public void resetError() {
|
||||
setState(false);
|
||||
}
|
||||
|
||||
public void setDescription(@NotNull String description) {
|
||||
myDescription.setText(description);
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
if (myCourse != null) {
|
||||
super.show();
|
||||
} else {
|
||||
doCancelAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
-7
@@ -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<Course> 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<Course> result, StepicWrappers.CoursesContainer coursesContainer) throws IOException {
|
||||
final List<RemoteCourse> courses = coursesContainer.courses;
|
||||
for (RemoteCourse info : courses) {
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -333,10 +333,6 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
|
||||
myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : "");
|
||||
}
|
||||
|
||||
public JComboBox<Course> getCoursesComboBox() {
|
||||
return myCoursesComboBox;
|
||||
}
|
||||
|
||||
public JPanel getInfoPanel() {
|
||||
return myInfoPanel;
|
||||
}
|
||||
|
||||
+6
@@ -25,6 +25,7 @@ import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
|
||||
import com.jetbrains.edu.learning.newproject.EduCourseProjectGenerator;
|
||||
import com.jetbrains.python.PythonModuleTypeBase;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -151,6 +152,11 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator {
|
||||
return Collections.singletonList(new File(new File(jarPath, "courses"), COURSE_NAME).getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EduCourseProjectGenerator getEduCourseProjectGenerator() {
|
||||
return new PyStudyDirectoryProjectGenerator();
|
||||
}
|
||||
|
||||
public ModuleType getModuleType() {
|
||||
return PythonModuleTypeBase.getInstance();
|
||||
}
|
||||
|
||||
+100
-45
@@ -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,13 +55,13 @@ import java.awt.event.MouseEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyNewProjectSettings> {
|
||||
public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyNewProjectSettings>
|
||||
implements EduCourseProjectGenerator {
|
||||
private static final Logger LOG = Logger.getInstance(PyStudyDirectoryProjectGenerator.class.getName());
|
||||
private final StudyProjectGenerator myGenerator;
|
||||
private static final String NO_PYTHON_INTERPRETER = "<html><u>Add</u> python interpreter.</html>";
|
||||
private final boolean isLocal;
|
||||
public ValidationResult myValidationResult = new ValidationResult("selected course is not valid");
|
||||
private StudyNewProjectPanel mySettingsPanel;
|
||||
|
||||
@SuppressWarnings("unused") // used on startup
|
||||
public PyStudyDirectoryProjectGenerator() {
|
||||
@@ -65,6 +69,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
}
|
||||
|
||||
public PyStudyDirectoryProjectGenerator(boolean isLocal) {
|
||||
this.isLocal = isLocal;
|
||||
myGenerator = new StudyProjectGenerator();
|
||||
myGenerator.addSettingsStateListener(new StudyProjectGenerator.SettingsListener() {
|
||||
@Override
|
||||
@@ -72,43 +77,6 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
setValidationResult(result);
|
||||
}
|
||||
});
|
||||
|
||||
mySettingsPanel = new StudyNewProjectPanel(myGenerator, isLocal);
|
||||
mySettingsPanel.registerValidators(new FacetValidatorsManager() {
|
||||
public void registerValidator(FacetEditorValidator validator, JComponent... componentsToWatch) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
ApplicationManager.getApplication().invokeLater(() -> 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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nls
|
||||
@@ -131,6 +99,10 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
@NotNull Module module,
|
||||
@Nullable PyProjectSynchronizer synchronizer) {
|
||||
myGenerator.generateProject(project, baseDir);
|
||||
createTestHelper(project, baseDir);
|
||||
}
|
||||
|
||||
private static void createTestHelper(@NotNull Project project, @NotNull VirtualFile baseDir) {
|
||||
final String testHelper = EduNames.TEST_HELPER;
|
||||
if (baseDir.findChild(testHelper) != null) return;
|
||||
final FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate("test_helper");
|
||||
@@ -146,16 +118,61 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ValidationResult validate(@NotNull String s) {
|
||||
public DirectoryProjectGenerator getDirectoryProjectGenerator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValidationResult validate() {
|
||||
final Project project = ProjectManager.getInstance().getDefaultProject();
|
||||
final List<Sdk> 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<PythonProjectGenerator> 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<Sdk> 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;
|
||||
}
|
||||
@@ -163,10 +180,49 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
@Nullable
|
||||
@Override
|
||||
public JPanel extendBasePanel() throws ProcessCanceledException {
|
||||
StudyNewProjectPanel mySettingsPanel = new StudyNewProjectPanel(myGenerator, isLocal);
|
||||
mySettingsPanel.registerValidators(new FacetValidatorsManager() {
|
||||
public void registerValidator(FacetEditorValidator validator, JComponent... componentsToWatch) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
ApplicationManager.getApplication().invokeLater(() -> 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;
|
||||
}
|
||||
|
||||
public void setSelectedCourse(Course course) {
|
||||
public void setCourse(@NotNull Course course) {
|
||||
myGenerator.setSelectedCourse(course);
|
||||
}
|
||||
|
||||
@@ -184,7 +240,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
public BooleanFunction<PythonProjectGenerator> beforeProjectGenerated(@Nullable Sdk sdk) {
|
||||
return generator -> {
|
||||
final List<Integer> 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(() -> {
|
||||
@@ -192,7 +248,6 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(((RemoteCourse)course).getId(),
|
||||
StudySettings.getInstance().getUser()));
|
||||
}, "Creating Course", true, ProjectManager.getInstance().getDefaultProject());
|
||||
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ public class PyStudyIntroductionCourseAction extends AnAction {
|
||||
final ProjectSpecificSettingsStep step = new ProjectSpecificSettingsStep(generator, callback);
|
||||
step.createPanel(); // initialize panel to set location
|
||||
step.setLocation(projectDir.toString());
|
||||
generator.setSelectedCourse(introCourse);
|
||||
generator.setCourse(introCourse);
|
||||
|
||||
callback.consume(step);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user