From 23bdbddbb69e73a7bc7f14c6b5d813283a68adce Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 19 Sep 2016 12:53:00 +0200 Subject: [PATCH 01/14] update "add @NotNull assertions" option to reflect the new behavior (IDEA-76782) --- .../options/CompilerOptionsPanel.form | 39 +++++++++++++++---- .../options/CompilerUIConfigurable.java | 16 +++++++- .../src/messages/CompilerBundle.properties | 2 +- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form b/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form index 0d94987cec94..3942ee8b03a8 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form +++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form @@ -60,14 +60,6 @@ - - - - - - - - @@ -172,6 +164,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java index 711b48767af4..74f8e1328660 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java +++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java @@ -15,20 +15,23 @@ */ package com.intellij.compiler.options; +import com.intellij.codeInsight.NullableNotNullDialog; import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.CompilerConfigurationImpl; import com.intellij.compiler.CompilerWorkspaceConfiguration; import com.intellij.compiler.MalformedPatternException; import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration; import com.intellij.compiler.server.BuildManager; +import com.intellij.ide.DataManager; import com.intellij.ide.PowerSaveMode; +import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.compiler.CompilerBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; +import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.Gray; @@ -44,6 +47,8 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.event.DocumentEvent; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.*; import java.util.List; @@ -80,6 +85,7 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura private JLabel myResourcePatternsLabel; private JLabel myEnableAutomakeLegendLabel; private JLabel myParallelCompilationLegendLabel; + private JButton myConfigureAnnotations; public CompilerUIConfigurable(@NotNull final Project project) { myProject = project; @@ -99,6 +105,14 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura s -> StringUtil.startsWithIgnoreCase(s, "-Xmx")) == null); } }); + myConfigureAnnotations.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(myPanel)); + if (project == null) project = ProjectManager.getInstance().getDefaultProject(); + new NullableNotNullDialog(project).show(); + } + }); } private void tweakControls(@NotNull Project project) { diff --git a/resources-en/src/messages/CompilerBundle.properties b/resources-en/src/messages/CompilerBundle.properties index 28f6d7e45342..e7069c2f0481 100644 --- a/resources-en/src/messages/CompilerBundle.properties +++ b/resources-en/src/messages/CompilerBundle.properties @@ -103,7 +103,7 @@ statistics.warnings.count={0} {0,choice, 0#warnings|1#warning|2#warnings} compiler.running.dialog.title=Compiler Running warning.compiler.running.on.project.close=The compiler is running. Proceed with project closing? warning.compiler.running.on.toolwindow.close=The compiler is running. Terminate it? -add.notnull.assertions=Add @NotNull &assertions +add.notnull.assertions=Add runtime &assertions for not-null-annotated methods and parameters compiler.eclipse.name=Eclipse eclipse.options.group.title=Eclipse Options From 6e58942b2ed0ee476c4f9b8baf521b9ecfd84b70 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Wed, 10 Aug 2016 17:44:27 +0300 Subject: [PATCH 02/14] set tests before push task to stepik --- .../com/jetbrains/edu/learning/stepic/EduStepicConnector.java | 1 + 1 file changed, 1 insertion(+) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 7b4b345258a5..ccd792882995 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -757,6 +757,7 @@ public class EduStepicConnector { final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); ApplicationManager.getApplication().invokeLater(() -> { + task.addTestsTexts("tests.py", task.getTestsText(project)); final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId)); request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); From 565912c269c8a8b85952b38494c93e990575e6c8 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Wed, 7 Sep 2016 18:25:16 +0300 Subject: [PATCH 03/14] missing dot in condition --- .../com/jetbrains/edu/learning/stepic/EduStepicConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index ccd792882995..a738887576a7 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -647,7 +647,7 @@ public class EduStepicConnector { public boolean accept(VirtualFile file) { final String name = file.getName(); return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS) && - !"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith(""); + !"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith("."); } }); From 738c9eaa7da209061a5d06609b05392f013373b8 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Sep 2016 19:19:18 +0300 Subject: [PATCH 04/14] OAuth stepic authorization, connector refactoring extracted and separated unauthorized and authorized http client for stepic so one can be confident about user login state removed enrolled course ids (todo: discuss with Valentina) added refresh/access tokens to the user information (todo: should be updated properly because it expires in 10 hours) --- .../coursecreator/actions/CCPushCourse.java | 2 +- .../coursecreator/actions/CCPushLesson.java | 2 +- .../edu/learning/checker/StudyCheckTask.java | 14 +- .../StudyProjectGenerator.java | 10 - .../stepic/EduAdaptiveStepicConnector.java | 30 +- .../stepic/EduStepicAuthorizedClient.java | 228 +++++++++ .../edu/learning/stepic/EduStepicClient.java | 124 +++++ .../learning/stepic/EduStepicConnector.java | 443 +++--------------- .../edu/learning/stepic/EduStepicNames.java | 4 +- .../edu/learning/stepic/LoginDialog.java | 10 +- .../learning/stepic/StepicStudyOptions.java | 5 +- .../edu/learning/stepic/StepicUser.java | 23 + .../edu/learning/stepic/StepicWrappers.java | 21 + .../edu/learning/ui/StudyNewProjectPanel.java | 7 +- .../PyStudyDirectoryProjectGenerator.java | 3 +- 15 files changed, 482 insertions(+), 444 deletions(-) create mode 100644 python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java create mode 100644 python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java index e425e5c25885..bdf55716c5e0 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java @@ -66,7 +66,7 @@ public class CCPushCourse extends DumbAwareAction { final int lessonId = EduStepicConnector.postLesson(project, lesson, indicator); final List sections = info.getSections(); final Integer sectionId = sections.get(sections.size() - 1); - EduStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId); + EduStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); } } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java index d3bceb0ba549..3424ab3a1159 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java @@ -84,7 +84,7 @@ public class CCPushLesson extends DumbAwareAction { final int lessonId = EduStepicConnector.postLesson(project, lesson, indicator); final List sections = info.getSections(); final Integer sectionId = sections.get(sections.size()-1); - EduStepicConnector.postUnit(lessonId, lesson.getIndex(), sectionId); + EduStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); } }}); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java index 056032b361ad..36b81208eb80 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java @@ -10,7 +10,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.edu.learning.StudyPluginConfigurator; import com.jetbrains.edu.learning.StudyState; @@ -24,7 +23,6 @@ import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector; import com.jetbrains.edu.learning.stepic.EduStepicConnector; -import com.jetbrains.edu.learning.stepic.StepicUser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,7 +38,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun private final Ref myCheckInProcess; private final Process myTestProcess; private final String myCommandLine; - private final String FAILED_CHECK_LAUNCH = "Failed to launch checking"; + private static final String FAILED_CHECK_LAUNCH = "Failed to launch checking"; public StudyCheckTask(Project project, StudyState studyState, Ref checkInProcess, Process testProcess, String commandLine) { super(project, "Checking Task"); @@ -100,7 +98,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun runAfterTaskCheckedActions(); final Course course = StudyTaskManager.getInstance(myProject).getCourse(); if (course != null && EduNames.STUDY.equals(course.getCourseMode())) { - postAttemptToStepic(testsOutput); + EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), myProject); } } } @@ -211,12 +209,4 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun LOG.warn("No configurator is provided for the plugin"); } } - - protected void postAttemptToStepic(@NotNull StudyTestsOutputParser.TestsOutput testsOutput) { - final StudyTaskManager studySettings = StudyTaskManager.getInstance(myProject); - final StepicUser user = studySettings.getUser(); - final String login = user.getEmail(); - final String password = StringUtil.isEmptyOrSpaces(login) ? "" : user.getPassword(); - EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), login, password); - } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 5df925d276f8..2037f317613c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -59,7 +59,6 @@ public class StudyProjectGenerator { private final List myListeners = ContainerUtil.newArrayList(); @Nullable public StepicUser myUser; private List myCourses = new ArrayList<>(); - private List myEnrolledCoursesIds = new ArrayList<>(); protected CourseInfo mySelectedCourseInfo; public void setCourses(List courses) { @@ -70,15 +69,6 @@ public class StudyProjectGenerator { return myUser != null && !StringUtil.isEmptyOrSpaces(myUser.getPassword()) && !StringUtil.isEmptyOrSpaces(myUser.getEmail()); } - public void setEnrolledCoursesIds(@NotNull final List coursesIds) { - myEnrolledCoursesIds = coursesIds; - } - - @NotNull - public List getEnrolledCoursesIds() { - return myEnrolledCoursesIds; - } - public void setSelectedCourse(@NotNull final CourseInfo courseName) { mySelectedCourseInfo = courseName; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index 1ab1531cb311..c8e529f27c2a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -55,7 +55,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.jetbrains.edu.learning.stepic.EduStepicConnector.*; +import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getStep; public class EduAdaptiveStepicConnector { public static final String PYTHON2 = "python2"; @@ -67,12 +67,11 @@ public class EduAdaptiveStepicConnector { @Nullable public static Task getNextRecommendation(@NotNull final Project project, @NotNull Course course) { try { - final CloseableHttpClient client = getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); final URI uri = new URIBuilder(EduStepicNames.STEPIC_API_URL + EduStepicNames.RECOMMENDATIONS_URL) .addParameter(EduNames.COURSE, String.valueOf(course.getId())) .build(); final HttpGet request = new HttpGet(uri); - setHeaders(request, EduStepicNames.CONTENT_TYPE_APPL_JSON); setTimeout(request); final CloseableHttpResponse response = client.execute(request); @@ -89,7 +88,8 @@ public class EduAdaptiveStepicConnector { final StepicWrappers.Recommendation recommendation = recomWrapper.recommendations[0]; final String lessonId = recommendation.lesson; final StepicWrappers.LessonContainer - lessonContainer = getFromStepic(EduStepicNames.LESSONS + lessonId, StepicWrappers.LessonContainer.class); + lessonContainer = EduStepicAuthorizedClient + .getFromStepic(EduStepicNames.LESSONS + lessonId, StepicWrappers.LessonContainer.class, project); if (lessonContainer.lessons.size() == 1) { final Lesson realLesson = lessonContainer.lessons.get(0); course.getLessons().get(0).setId(Integer.parseInt(lessonId)); @@ -112,9 +112,6 @@ public class EduAdaptiveStepicConnector { } } else { - if ((statusCode == HttpStatus.SC_BAD_REQUEST || statusCode == HttpStatus.SC_UNAUTHORIZED) && login(project)) { - return getNextRecommendation(project, course); - } throw new IOException("Stepic returned non 200 status code: " + responseString); } } @@ -161,8 +158,7 @@ public class EduAdaptiveStepicConnector { final String json = new Gson() .toJson(new StepicWrappers.RecommendationReactionWrapper(new StepicWrappers.RecommendationReaction(reaction, user, lessonId))); post.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); - final CloseableHttpClient client = getHttpClient(project); - setHeaders(post, EduStepicNames.CONTENT_TYPE_APPL_JSON); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); setTimeout(post); try { final CloseableHttpResponse execute = client.execute(post); @@ -174,10 +170,6 @@ public class EduAdaptiveStepicConnector { return true; } else { - if ((statusCode == HttpStatus.SC_BAD_REQUEST || statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) - && login(project)) { - return postRecommendationReaction(project, lessonId, user, reaction); - } LOG.warn("Stepic returned non-201 status code: " + statusCode + " " + entityString); return false; } @@ -398,7 +390,7 @@ public class EduAdaptiveStepicConnector { final Editor editor = StudyUtils.getSelectedEditor(project); String language = getLanguageString(task, project); if (editor != null && language != null) { - final CloseableHttpClient client = getHttpClient(project); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); StepicWrappers.ResultSubmissionWrapper wrapper = postResultsForCheck(client, attemptId, language, editor.getDocument().getText()); final StepicUser user = StudyTaskManager.getInstance(project).getUser(); @@ -429,7 +421,6 @@ public class EduAdaptiveStepicConnector { final StepicWrappers.SubmissionToPostWrapper submissionToPostWrapper = new StepicWrappers.SubmissionToPostWrapper(String.valueOf(attemptId), language, PYCHARM_COMMENT + text); final HttpPost httpPost = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.SUBMISSIONS); - setHeaders(httpPost, EduStepicNames.CONTENT_TYPE_APPL_JSON); setTimeout(httpPost); try { httpPost.setEntity(new StringEntity(new Gson().toJson(submissionToPostWrapper))); @@ -463,7 +454,6 @@ public class EduAdaptiveStepicConnector { .addParameter("user", String.valueOf(id)) .build(); final HttpGet httpGet = new HttpGet(submissionURI); - setHeaders(httpGet, EduStepicNames.CONTENT_TYPE_APPL_JSON); setTimeout(httpGet); final CloseableHttpResponse httpResponse = client.execute(httpGet); final HttpEntity entity = httpResponse.getEntity(); @@ -511,8 +501,7 @@ public class EduAdaptiveStepicConnector { final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); post.setEntity(new StringEntity(new Gson().toJson(attemptWrapper))); - final CloseableHttpClient client = getHttpClient(project); - setHeaders(post, EduStepicNames.CONTENT_TYPE_APPL_JSON); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); setTimeout(post); final CloseableHttpResponse httpResponse = client.execute(post); final int statusCode = httpResponse.getStatusLine().getStatusCode(); @@ -524,11 +513,6 @@ public class EduAdaptiveStepicConnector { new Gson().fromJson(entityString, StepicWrappers.AttemptContainer.class); return (container.attempts != null && !container.attempts.isEmpty()) ? container.attempts.get(0).id : -1; } - else { - if ((statusCode == HttpStatus.SC_BAD_REQUEST || statusCode == HttpStatus.SC_UNAUTHORIZED) && login(project)) { - return getAttemptId(project, task); - } - } return -1; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java new file mode 100644 index 000000000000..6a3338ec228a --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java @@ -0,0 +1,228 @@ +package com.jetbrains.edu.learning.stepic; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.net.HttpConfigurable; +import com.intellij.util.net.ssl.CertificateManager; +import com.jetbrains.edu.learning.StudyTaskManager; +import org.apache.http.*; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.DefaultConnectionReuseStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URI; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; + +public class EduStepicAuthorizedClient { + private static final Logger LOG = Logger.getInstance(EduStepicAuthorizedClient.class.getName()); + private static final String ourClientId = "hUCWcq3hZHCmz0DKrDtwOWITLcYutzot7p4n59vU"; + private static CloseableHttpClient ourClient; + + private EduStepicAuthorizedClient() { + } + + @NotNull + public static CloseableHttpClient getHttpClient(@NotNull final Project project) { + if (ourClient != null) { + return ourClient; + } + ourClient = initializeClient(project); + if (ourClient == null) { + if (login(project)) { + ourClient = initializeClient(project); + } + } + return ourClient; + } + + public static T getFromStepic(String link, final Class container, @NotNull final Project project) throws IOException { + return EduStepicClient.getFromStepic(link, container, getHttpClient(project)); + } + + @Nullable + private static CloseableHttpClient initializeClient(@NotNull final Project project) { + final List headers = new ArrayList<>(); + final StepicUser currentUser = StudyTaskManager.getInstance(project).getUser(); + final String accessToken = currentUser.getAccessToken(); + if (accessToken != null && !accessToken.isEmpty()) { + headers.add(new BasicHeader("Authorization", "Bearer " + accessToken)); + headers.add(new BasicHeader("Content-type", EduStepicNames.CONTENT_TYPE_APP_JSON)); + return getBuilder().setDefaultHeaders(headers).build(); + } + return null; + } + + @NotNull + private static TrustManager[] trustAllCerts() { + // Create a trust manager that does not validate certificate for this connection + return new TrustManager[]{new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + }}; + } + + @NotNull + private static HttpClientBuilder getBuilder() { + final HttpClientBuilder builder = HttpClients.custom().setSslcontext(CertificateManager.getInstance().getSslContext()). + setMaxConnPerRoute(100000).setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE); + + final HttpConfigurable proxyConfigurable = HttpConfigurable.getInstance(); + final List proxies = proxyConfigurable.getOnlyBySettingsSelector().select(URI.create(EduStepicNames.STEPIC_URL)); + final InetSocketAddress address = proxies.size() > 0 ? (InetSocketAddress)proxies.get(0).address() : null; + if (address != null) { + builder.setProxy(new HttpHost(address.getHostName(), address.getPort())); + } + final TrustManager[] trustAllCerts = trustAllCerts(); + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new SecureRandom()); + builder.setSslcontext(sslContext); + } + catch (NoSuchAlgorithmException | KeyManagementException e) { + LOG.error(e.getMessage()); + } + return builder; + } + + private static boolean login(@NotNull final Project project) { + final StepicUser user = StudyTaskManager.getInstance(project).getUser(); + final String login = user.getEmail(); + final String refreshToken = user.getRefreshToken(); + if (StringUtil.isEmptyOrSpaces(login)) { + return showLoginDialog(); + } + else { + if (StringUtil.isNotEmpty(refreshToken)) { + final StepicWrappers.TokenInfo tokenInfo = login(refreshToken); + user.setupTokenInfo(tokenInfo); + } + else { + final StepicUser stepicUser = login(login, user.getPassword()); + if (stepicUser == null) { + return showLoginDialog(); + } + else { + StudyTaskManager.getInstance(project).setUser(stepicUser); + } + } + } + return true; + } + + private static boolean showLoginDialog() { + final boolean[] logged = {false}; + ApplicationManager.getApplication().invokeAndWait(() -> { + final LoginDialog dialog = new LoginDialog(); + dialog.show(); + logged[0] = dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE; + }); + return logged[0]; + } + + public static StepicUser login(@NotNull final String email, @NotNull final String password) { + final List parameters = new ArrayList<>(); + if (password.isEmpty()) return null; + parameters.add(new BasicNameValuePair("client_id", ourClientId)); + parameters.add(new BasicNameValuePair("grant_type", "password")); + parameters.add(new BasicNameValuePair("username", email)); + parameters.add(new BasicNameValuePair("password", password)); + + final StepicWrappers.TokenInfo tokenInfo = postCredentials(parameters); + + final StepicUser user = new StepicUser(email, password); + final StepicUser currentUser = getCurrentUser(); + if (currentUser != null) { + user.setId(currentUser.getId()); + } + user.setupTokenInfo(tokenInfo); + return user; + } + + private static StepicWrappers.TokenInfo login(@NotNull final String refreshToken) { + final List parameters = new ArrayList<>(); + + if (refreshToken.isEmpty()) return null; + parameters.add(new BasicNameValuePair("client_id", ourClientId)); + parameters.add(new BasicNameValuePair("content-type", "application/json")); + parameters.add(new BasicNameValuePair("grant_type", "refresh_token")); + parameters.add(new BasicNameValuePair("refresh_token", refreshToken)); + + final StepicWrappers.TokenInfo tokenInfo = postCredentials(parameters); + return tokenInfo; + } + + @Nullable + static StepicUser getCurrentUser() { + try { + final StepicWrappers.AuthorWrapper wrapper = EduStepicClient.getFromStepic(EduStepicNames.CURRENT_USER, + StepicWrappers.AuthorWrapper.class); + if (wrapper != null && !wrapper.users.isEmpty()) { + return wrapper.users.get(0); + } + } + catch (IOException e) { + LOG.warn("Couldn't get author info"); + } + return null; + } + + @Nullable + private static StepicWrappers.TokenInfo postCredentials(@NotNull final List parameters) { + final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + + final HttpPost request = new HttpPost(EduStepicNames.TOKEN_URL); + request.setEntity(new UrlEncodedFormEntity(parameters, Consts.UTF_8)); + + try { + final CloseableHttpClient client = EduStepicClient.getHttpClient(); + final CloseableHttpResponse response = client.execute(request); + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + EntityUtils.consume(responseEntity); + if (statusLine.getStatusCode() == HttpStatus.SC_OK) { + return gson.fromJson(responseString, StepicWrappers.TokenInfo.class); + } + else { + LOG.warn("Failed to Login: " + statusLine.getStatusCode() + statusLine.getReasonPhrase()); + } + } + catch (IOException e) { + LOG.warn(e.getMessage()); + } + return null; + } +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java new file mode 100644 index 000000000000..dd770b8ffc01 --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java @@ -0,0 +1,124 @@ +package com.jetbrains.edu.learning.stepic; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.net.HttpConfigurable; +import com.intellij.util.net.ssl.CertificateManager; +import com.jetbrains.edu.learning.StudySerializationUtils; +import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; +import com.jetbrains.edu.learning.courseFormat.TaskFile; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.DefaultConnectionReuseStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.jetbrains.annotations.NotNull; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URI; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.List; + +public class EduStepicClient { + private static final Logger LOG = Logger.getInstance(EduStepicClient.class.getName()); + private static CloseableHttpClient ourClient; + + private EduStepicClient() { + } + + @NotNull + public static CloseableHttpClient getHttpClient() { + if (ourClient == null) { + initializeClient(); + } + return ourClient; + } + + public static T getFromStepic(String link, final Class container) throws IOException { + return getFromStepic(link, container, ourClient); + } + + static T getFromStepic(String link, final Class container, @NotNull final CloseableHttpClient client) throws IOException { + if (!link.startsWith("/")) link = "/" + link; + final HttpGet request = new HttpGet(EduStepicNames.STEPIC_API_URL + link); + if (ourClient == null) { + initializeClient(); + } + + final CloseableHttpResponse response = client.execute(request); + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + EntityUtils.consume(responseEntity); + if (statusLine.getStatusCode() != HttpStatus.SC_OK) { + throw new IOException("Stepic returned non 200 status code " + responseString); + } + Gson gson = new GsonBuilder().registerTypeAdapter(TaskFile.class, new StudySerializationUtils.Json.StepicTaskFileAdapter()). + registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()). + setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"). + setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + return gson.fromJson(responseString, container); + } + + private static void initializeClient() { + if (ourClient == null) { + final HttpClientBuilder builder = getBuilder(); + ourClient = builder.build(); + } + } + + @NotNull + private static TrustManager[] trustAllCerts() { + // Create a trust manager that does not validate certificate for this connection + return new TrustManager[]{new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + }}; + } + + @NotNull + private static HttpClientBuilder getBuilder() { + final HttpClientBuilder builder = HttpClients.custom().setSslcontext(CertificateManager.getInstance().getSslContext()). + setMaxConnPerRoute(100000).setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE); + + final HttpConfigurable proxyConfigurable = HttpConfigurable.getInstance(); + final List proxies = proxyConfigurable.getOnlyBySettingsSelector().select(URI.create(EduStepicNames.STEPIC_URL)); + final InetSocketAddress address = proxies.size() > 0 ? (InetSocketAddress)proxies.get(0).address() : null; + if (address != null) { + builder.setProxy(new HttpHost(address.getHostName(), address.getPort())); + } + final TrustManager[] trustAllCerts = trustAllCerts(); + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new SecureRandom()); + builder.setSslcontext(sslContext); + } + catch (NoSuchAlgorithmException | KeyManagementException e) { + LOG.error(e.getMessage()); + } + return builder; + } +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index a738887576a7..e743ea13f3bc 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -1,6 +1,5 @@ package com.jetbrains.edu.learning.stepic; -import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; @@ -9,272 +8,53 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileFilter; -import com.intellij.util.net.HttpConfigurable; -import com.intellij.util.net.ssl.CertificateManager; import com.jetbrains.edu.learning.StudySerializationUtils; -import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import org.apache.commons.codec.binary.Base64; -import org.apache.http.*; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; import org.apache.http.client.utils.URIBuilder; -import org.apache.http.cookie.Cookie; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.DefaultConnectionReuseStrategy; -import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Proxy; import java.net.URI; import java.net.URISyntaxException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; import java.util.*; -import static com.jetbrains.edu.learning.stepic.EduStepicNames.CONTENT_TYPE_APPL_JSON; - public class EduStepicConnector { private static final Logger LOG = Logger.getInstance(EduStepicConnector.class.getName()); - private static String ourCSRFToken = ""; - private static CloseableHttpClient ourClient; //this prefix indicates that course can be opened by educational plugin public static final String PYCHARM_PREFIX = "pycharm"; - private static BasicCookieStore ourCookieStore; - private static final String ADAPTIVE_NOTE = "\n\nInitially, the adaptive system may behave somewhat randomly, but the more problems you solve, the smarter it become!"; private EduStepicConnector() { } - public static StepicUser login(@NotNull final String username, @NotNull final String password) { - initializeClient(); - if (postCredentials(username, password)) { - final StepicWrappers.AuthorWrapper stepicUserWrapper = getCurrentUser(); - if (stepicUserWrapper != null && stepicUserWrapper.users.size() == 1) { - return stepicUserWrapper.users.get(0); - } - } - return null; - } - - @NotNull - public static List getEnrolledCoursesIds() { - try { - final URI enrolledCoursesUri = new URIBuilder(EduStepicNames.COURSES).addParameter("enrolled", "true").build(); - final List courses = getFromStepic(enrolledCoursesUri.toString(), StepicWrappers.CoursesContainer.class).courses; - final ArrayList ids = new ArrayList<>(); - for (CourseInfo course : courses) { - ids.add(course.getId()); - } - return ids; - } - catch (IOException e) { - LOG.warn(e.getMessage()); - } - catch (URISyntaxException e) { - LOG.warn(e.getMessage()); - } - return Collections.emptyList(); - } - - @Nullable - public static StepicWrappers.AuthorWrapper getCurrentUser() { - try { - return getFromStepic(EduStepicNames.CURRENT_USER, StepicWrappers.AuthorWrapper.class); - } - catch (IOException e) { - LOG.warn("Couldn't get author info"); - } - return null; - } - - public static boolean createUser(@NotNull final String user, @NotNull final String password) { - final HttpPost userRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.USERS); - initializeClient(); - setHeaders(userRequest, CONTENT_TYPE_APPL_JSON); - String requestBody = new Gson().toJson(new StepicWrappers.UserWrapper(user, password)); - userRequest.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - - try { - final CloseableHttpResponse response = ourClient.execute(userRequest); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - final StatusLine statusLine = response.getStatusLine(); - EntityUtils.consume(responseEntity); - if (statusLine.getStatusCode() != HttpStatus.SC_CREATED) { - LOG.error("Failed to create user " + responseString); - return false; - } - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - return true; - } - - public static void initializeClient() { - if (ourClient == null) { - final HttpGet request = new HttpGet(EduStepicNames.STEPIC_URL); - setHeaders(request, CONTENT_TYPE_APPL_JSON); - - HttpClientBuilder builder = - HttpClients.custom().setSslcontext(CertificateManager.getInstance().getSslContext()).setMaxConnPerRoute(100000). - setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE); - - final HttpConfigurable proxyConfigurable = HttpConfigurable.getInstance(); - final List proxies = proxyConfigurable.getOnlyBySettingsSelector().select(URI.create(EduStepicNames.STEPIC_URL)); - final InetSocketAddress address = proxies.size() > 0 ? (InetSocketAddress)proxies.get(0).address() : null; - if (address != null) { - builder.setProxy(new HttpHost(address.getHostName(), address.getPort())); - } - ourCookieStore = new BasicCookieStore(); - - try { - // Create a trust manager that does not validate certificate for this connection - TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - }}; - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustAllCerts, new SecureRandom()); - ourClient = builder.setDefaultCookieStore(ourCookieStore).setSslcontext(sslContext).build(); - - ourClient.execute(request); - saveCSRFToken(); - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - catch (NoSuchAlgorithmException e) { - LOG.error(e.getMessage()); - } - catch (KeyManagementException e) { - LOG.error(e.getMessage()); - } - } - } - - private static void saveCSRFToken() { - if (ourCookieStore == null) return; - final List cookies = ourCookieStore.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("csrftoken")) { - ourCSRFToken = cookie.getValue(); - } - } - } - - private static boolean postCredentials(String user, String password) { - String url = EduStepicNames.STEPIC_URL + EduStepicNames.LOGIN; - final HttpPost request = new HttpPost(url); - List nvps = new ArrayList<>(); - nvps.add(new BasicNameValuePair("csrfmiddlewaretoken", ourCSRFToken)); - nvps.add(new BasicNameValuePair("login", user)); - nvps.add(new BasicNameValuePair("next", "/")); - nvps.add(new BasicNameValuePair("password", password)); - nvps.add(new BasicNameValuePair("remember", "on")); - - request.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); - - setHeaders(request, "application/x-www-form-urlencoded"); - - try { - final CloseableHttpResponse response = ourClient.execute(request); - saveCSRFToken(); - final StatusLine line = response.getStatusLine(); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - EntityUtils.consume(responseEntity); - if (line.getStatusCode() != HttpStatus.SC_MOVED_TEMPORARILY) { - LOG.warn("Failed to login: " + line.getStatusCode() + line.getReasonPhrase()); - LOG.debug("Failed to login " + responseString); - - ourClient = null; - return false; - } - } - catch (IOException e) { - LOG.warn(e.getMessage()); - ourClient = null; - return false; - } - return true; - } - - static T getFromStepic(String link, final Class container) throws IOException { - if (!link.startsWith("/")) link = "/" + link; - final HttpGet request = new HttpGet(EduStepicNames.STEPIC_API_URL + link); - if (ourClient == null) { - initializeClient(); - } - setHeaders(request, CONTENT_TYPE_APPL_JSON); - - final CloseableHttpResponse response = ourClient.execute(request); - final StatusLine statusLine = response.getStatusLine(); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - EntityUtils.consume(responseEntity); - if (statusLine.getStatusCode() != HttpStatus.SC_OK) { - throw new IOException("Stepic returned non 200 status code " + responseString); - } - Gson gson = new GsonBuilder().registerTypeAdapter(TaskFile.class, new StudySerializationUtils.Json.StepicTaskFileAdapter()). - registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()). - setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"). - setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); - return gson.fromJson(responseString, container); - } - - @NotNull - public static CloseableHttpClient getHttpClient(@NotNull final Project project) { - if (ourClient == null) { - login(project); - } - return ourClient; - } - public static boolean enrollToCourse(final int courseId) { HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ENROLLMENTS); try { final StepicWrappers.EnrollmentWrapper enrollment = new StepicWrappers.EnrollmentWrapper(String.valueOf(courseId)); post.setEntity(new StringEntity(new GsonBuilder().create().toJson(enrollment))); - setHeaders(post, CONTENT_TYPE_APPL_JSON); - if (ourClient == null) { - initializeClient(); - } - CloseableHttpResponse response = ourClient.execute(post); + final CloseableHttpClient client = EduStepicClient.getHttpClient(); + CloseableHttpResponse response = client.execute(post); StatusLine line = response.getStatusLine(); return line.getStatusCode() == HttpStatus.SC_CREATED; } @@ -303,8 +83,8 @@ public class EduStepicConnector { public static CourseInfo getCourseInfo(Project project, String courseId) { final String url = EduStepicNames.COURSES + "/" + courseId; try { - login(project); - final StepicWrappers.CoursesContainer coursesContainer = getFromStepic(url, StepicWrappers.CoursesContainer.class); + final StepicWrappers.CoursesContainer coursesContainer = + EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class, project); return coursesContainer.courses.get(0); } catch (IOException e) { @@ -316,7 +96,7 @@ public class EduStepicConnector { public static Date getCourseUpdateDate(final int courseId) { final String url = EduStepicNames.COURSES + "/" + courseId; try { - final List courses = getFromStepic(url, StepicWrappers.CoursesContainer.class).courses; + final List courses = EduStepicClient.getFromStepic(url, StepicWrappers.CoursesContainer.class).courses; if (!courses.isEmpty()) { return courses.get(0).getUpdateDate(); } @@ -331,7 +111,7 @@ public class EduStepicConnector { public static Date getLessonUpdateDate(final int lessonId) { final String url = EduStepicNames.LESSONS + "/" + lessonId; try { - List lessons = getFromStepic(url, StepicWrappers.LessonContainer.class).lessons; + List lessons = EduStepicClient.getFromStepic(url, StepicWrappers.LessonContainer.class).lessons; if (!lessons.isEmpty()) { return lessons.get(0).getUpdateDate(); } @@ -346,7 +126,7 @@ public class EduStepicConnector { public static Date getTaskUpdateDate(final int taskId) { final String url = EduStepicNames.STEPS + "/" + String.valueOf(taskId); try { - List steps = getFromStepic(url, StepicWrappers.StepContainer.class).steps; + List steps = EduStepicClient.getFromStepic(url, StepicWrappers.StepContainer.class).steps; if (!steps.isEmpty()) { return steps.get(0).update_date; } @@ -368,7 +148,7 @@ public class EduStepicConnector { LOG.error(e.getMessage()); return false; } - final StepicWrappers.CoursesContainer coursesContainer = getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class); + final StepicWrappers.CoursesContainer coursesContainer = EduStepicClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class); final List courseInfos = coursesContainer.courses; for (CourseInfo info : courseInfos) { final String courseType = info.getType(); @@ -376,7 +156,7 @@ public class EduStepicConnector { final List typeLanguage = StringUtil.split(courseType, " "); if (info.isAdaptive() || (typeLanguage.size() == 2 && PYCHARM_PREFIX.equals(typeLanguage.get(0)))) { for (Integer instructor : info.instructors) { - final StepicUser author = getFromStepic(EduStepicNames.USERS + "/" + String.valueOf(instructor), + final StepicUser author = EduStepicClient.getFromStepic(EduStepicNames.USERS + "/" + String.valueOf(instructor), StepicWrappers.AuthorWrapper.class).users.get(0); info.addAuthor(author); } @@ -433,15 +213,15 @@ public class EduStepicConnector { public static List getLessons(int sectionId) throws IOException { final StepicWrappers.SectionContainer - sectionContainer = getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), StepicWrappers.SectionContainer.class); + sectionContainer = EduStepicClient.getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), StepicWrappers.SectionContainer.class); List unitIds = sectionContainer.sections.get(0).units; final List lessons = new ArrayList<>(); for (Integer unitId : unitIds) { StepicWrappers.UnitContainer - unit = getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class); + unit = EduStepicClient.getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class); int lessonID = unit.units.get(0).lesson; StepicWrappers.LessonContainer - lessonContainer = getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), StepicWrappers.LessonContainer.class); + lessonContainer = EduStepicClient.getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), StepicWrappers.LessonContainer.class); Lesson lesson = lessonContainer.lessons.get(0); lesson.taskList = new ArrayList<>(); for (Integer s : lesson.steps) { @@ -477,53 +257,25 @@ public class EduStepicConnector { } public static StepicWrappers.StepSource getStep(Integer step) throws IOException { - return getFromStepic(EduStepicNames.STEPS + "/" + String.valueOf(step), StepicWrappers.StepContainer.class).steps.get(0); + return EduStepicClient.getFromStepic(EduStepicNames.STEPS + "/" + String.valueOf(step), StepicWrappers.StepContainer.class).steps.get(0); } - - public static boolean showLoginDialog() { - final boolean[] logged = {false}; - ApplicationManager.getApplication().invokeAndWait(() -> { - final LoginDialog dialog = new LoginDialog(); - dialog.show(); - logged[0] = dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE; - }); - return logged[0]; - } - - public static void postAttempt(@NotNull final Task task, boolean passed, @Nullable String login, @Nullable String password) { + public static void postAttempt(@NotNull final Task task, boolean passed, @NotNull final Project project) { if (task.getStepicId() <= 0) { return; } - if (ourClient == null) { - if (StringUtil.isEmptyOrSpaces(login) || StringUtil.isEmptyOrSpaces(password)) { - return; - } - else { - if (login(login, password) == null) return; - } - } final HttpPost attemptRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); - setHeaders(attemptRequest, CONTENT_TYPE_APPL_JSON); String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(task.getStepicId())); attemptRequest.setEntity(new StringEntity(attemptRequestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpResponse attemptResponse = ourClient.execute(attemptRequest); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse attemptResponse = client.execute(attemptRequest); final HttpEntity responseEntity = attemptResponse.getEntity(); final String attemptResponseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; final StatusLine statusLine = attemptResponse.getStatusLine(); EntityUtils.consume(responseEntity); - if (statusLine.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { - if (StringUtil.isEmptyOrSpaces(login) || StringUtil.isEmptyOrSpaces(password)) { - return; - } - else { - login(login, password); - postAttempt(task, passed, login, password); - } - } if (statusLine.getStatusCode() != HttpStatus.SC_CREATED) { LOG.warn("Failed to make attempt " + attemptResponseString); } @@ -534,20 +286,21 @@ public class EduStepicConnector { for (TaskFile fileEntry : taskFiles.values()) { files.add(new StepicWrappers.SolutionFile(fileEntry.name, fileEntry.text)); } - postSubmission(passed, attempt, files); + postSubmission(passed, attempt, project, files); } catch (IOException e) { LOG.error(e.getMessage()); } } - private static void postSubmission(boolean passed, StepicWrappers.AttemptWrapper.Attempt attempt, ArrayList files) throws IOException { + private static void postSubmission(boolean passed, StepicWrappers.AttemptWrapper.Attempt attempt, + Project project, ArrayList files) throws IOException { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.SUBMISSIONS); - setHeaders(request, CONTENT_TYPE_APPL_JSON); String requestBody = new Gson().toJson(new StepicWrappers.SubmissionWrapper(attempt.id, passed ? "1" : "0", files)); request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; final StatusLine line = response.getStatusLine(); @@ -558,65 +311,53 @@ public class EduStepicConnector { } public static void postCourseWithProgress(final Project project, @NotNull final Course course) { - postCourseWithProgress(project, course, false); - } - - public static void postCourseWithProgress(final Project project, @NotNull final Course course, final boolean relogin) { ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Uploading Course", true) { @Override public void run(@NotNull final ProgressIndicator indicator) { - postCourse(project, course, relogin, indicator); + postCourse(project, course, indicator); } }); } - private static void postCourse(final Project project, @NotNull Course course, boolean relogin, @NotNull final ProgressIndicator indicator) { + private static void postCourse(final Project project, @NotNull Course course, @NotNull final ProgressIndicator indicator) { indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL); final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses"); - if (ourClient == null || !relogin) { - if (!login(project)) return; - } - final StepicWrappers.AuthorWrapper authors = getCurrentUser(); - if (authors != null) { + + final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser(); + if (currentUser != null) { final List courseAuthors = course.getAuthors(); for (int i = 0; i < courseAuthors.size(); i++) { - final StepicUser user = authors.users.get(i); if (courseAuthors.size() > i) { final StepicUser courseAuthor = courseAuthors.get(i); - user.setFirstName(courseAuthor.getFirstName()); - user.setLastName(courseAuthor.getLastName()); + currentUser.setFirstName(courseAuthor.getFirstName()); + currentUser.setLastName(courseAuthor.getLastName()); } } - - course.setAuthors(authors.users); + course.setAuthors(Collections.singletonList(currentUser)); } - setHeaders(request, CONTENT_TYPE_APPL_JSON); String requestBody = new Gson().toJson(new StepicWrappers.CourseWrapper(course)); request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; final StatusLine line = response.getStatusLine(); EntityUtils.consume(responseEntity); if (line.getStatusCode() != HttpStatus.SC_CREATED) { - if (!relogin) { - login(project); - postCourse(project, course, true, indicator); - } LOG.error("Failed to push " + responseString); return; } final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0); course.setId(postedCourse.id); - final int sectionId = postModule(postedCourse.id, 1, String.valueOf(postedCourse.getName())); + final int sectionId = postModule(project, postedCourse.id, 1, String.valueOf(postedCourse.getName())); int position = 1; for (Lesson lesson : course.getLessons()) { indicator.checkCanceled(); final int lessonId = postLesson(project, lesson, indicator); - postUnit(lessonId, position, sectionId); + postUnit(project, lessonId, position, sectionId); position += 1; } ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator)); @@ -626,20 +367,6 @@ public class EduStepicConnector { } } - static boolean login(@NotNull final Project project) { - final StepicUser user = StudyTaskManager.getInstance(project).getUser(); - final String login = user.getEmail(); - if (StringUtil.isEmptyOrSpaces(login)) { - return showLoginDialog(); - } - else { - if (login(login, user.getPassword()) == null) { - return showLoginDialog(); - } - } - return true; - } - private static void postAdditionalFiles(@NotNull final Project project, int id, ProgressIndicator indicator) { final VirtualFile baseDir = project.getBaseDir(); final List files = VfsUtil.getChildren(baseDir, new VirtualFileFilter() { @@ -652,7 +379,7 @@ public class EduStepicConnector { }); if (!files.isEmpty()) { - final int sectionId = postModule(id, 2, EduNames.PYCHARM_ADDITIONAL); + final int sectionId = postModule(project, id, 2, EduNames.PYCHARM_ADDITIONAL); final Lesson lesson = new Lesson(); lesson.setName(EduNames.PYCHARM_ADDITIONAL); final Task task = new Task(); @@ -678,13 +405,12 @@ public class EduStepicConnector { lesson.addTask(task); lesson.setIndex(1); final int lessonId = postLesson(project, lesson, indicator); - postUnit(lessonId, 1, sectionId); + postUnit(project, lessonId, 1, sectionId); } } - public static void postUnit(int lessonId, int position, int sectionId) { + public static void postUnit(@NotNull final Project project, int lessonId, int position, int sectionId) { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.UNITS); - setHeaders(request, CONTENT_TYPE_APPL_JSON); final StepicWrappers.UnitWrapper unitWrapper = new StepicWrappers.UnitWrapper(); unitWrapper.unit = new StepicWrappers.Unit(); unitWrapper.unit.lesson = lessonId; @@ -695,7 +421,8 @@ public class EduStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; final StatusLine line = response.getStatusLine(); @@ -709,9 +436,8 @@ public class EduStepicConnector { } } - private static int postModule(int courseId, int position, @NotNull final String title) { + private static int postModule(@NotNull final Project project, int courseId, int position, @NotNull final String title) { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/sections"); - setHeaders(request, CONTENT_TYPE_APPL_JSON); final StepicWrappers.Section section = new StepicWrappers.Section(); section.course = courseId; section.title = title; @@ -722,7 +448,8 @@ public class EduStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; final StatusLine line = response.getStatusLine(); @@ -745,15 +472,7 @@ public class EduStepicConnector { final Lesson lesson = task.getLesson(); final int lessonId = lesson.getId(); - if (ourClient == null) { - if (!login(project)) { - LOG.error("Failed to update task"); - return -1; - } - } - final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + "/step-sources/" + String.valueOf(task.getStepicId())); - setHeaders(request, CONTENT_TYPE_APPL_JSON); final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); ApplicationManager.getApplication().invokeLater(() -> { @@ -762,17 +481,12 @@ public class EduStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; EntityUtils.consume(responseEntity); final StatusLine line = response.getStatusLine(); - if (line.getStatusCode() == HttpStatus.SC_FORBIDDEN) { - if (login(project)) { - updateTask(project, task); - return; - } - } if (line.getStatusCode() != HttpStatus.SC_OK) { LOG.error("Failed to push " + responseString); } @@ -786,35 +500,24 @@ public class EduStepicConnector { public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) { final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + EduStepicNames.LESSONS + String.valueOf(lesson.getId())); - if (ourClient == null) { - if (!login(project)) { - LOG.error("Failed to push lesson"); - return -1; - } - } - setHeaders(request, CONTENT_TYPE_APPL_JSON); String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson)); request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; final StatusLine line = response.getStatusLine(); EntityUtils.consume(responseEntity); - if (line.getStatusCode() == HttpStatus.SC_FORBIDDEN) { - if (login(project)) { - return updateLesson(project, lesson, indicator); - } - } if (line.getStatusCode() != HttpStatus.SC_OK) { LOG.error("Failed to push " + responseString); return -1; } final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); for (Integer step : postedLesson.steps) { - deleteTask(step); + deleteTask(step, project); } for (Task task : lesson.getTaskList()) { @@ -831,25 +534,17 @@ public class EduStepicConnector { public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) { final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/lessons"); - if (ourClient == null) { - login(project); - } - setHeaders(request, CONTENT_TYPE_APPL_JSON); String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson)); request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; final StatusLine line = response.getStatusLine(); EntityUtils.consume(responseEntity); - if (line.getStatusCode() == HttpStatus.SC_FORBIDDEN) { - if (login(project)) { - return postLesson(project, lesson, indicator); - } - } if (line.getStatusCode() != HttpStatus.SC_CREATED) { LOG.error("Failed to push " + responseString); return 0; @@ -868,12 +563,12 @@ public class EduStepicConnector { return -1; } - public static void deleteTask(@NotNull final Integer task) { + public static void deleteTask(@NotNull final Integer task, Project project) { final HttpDelete request = new HttpDelete(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES + task); - setHeaders(request, CONTENT_TYPE_APPL_JSON); ApplicationManager.getApplication().invokeLater(() -> { try { - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; EntityUtils.consume(responseEntity); @@ -889,14 +584,7 @@ public class EduStepicConnector { } public static void postTask(final Project project, @NotNull final Task task, final int lessonId) { - if (ourClient == null) { - if (!login(project)) { - LOG.error("Failed to update task"); - } - } - final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/step-sources"); - setHeaders(request, CONTENT_TYPE_APPL_JSON); //TODO: register type adapter for task files here? final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); @@ -905,17 +593,12 @@ public class EduStepicConnector { request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try { - final CloseableHttpResponse response = ourClient.execute(request); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); final StatusLine line = response.getStatusLine(); final HttpEntity responseEntity = response.getEntity(); final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; EntityUtils.consume(responseEntity); - if (line.getStatusCode() == HttpStatus.SC_FORBIDDEN) { - if (login(project)) { - postTask(project, task, lessonId); - return; - } - } if (line.getStatusCode() != HttpStatus.SC_CREATED) { LOG.error("Failed to push " + responseString); return; @@ -930,10 +613,4 @@ public class EduStepicConnector { } }); } - - static void setHeaders(@NotNull final HttpRequestBase request, String contentType) { - request.addHeader(new BasicHeader("referer", EduStepicNames.STEPIC_URL)); - request.addHeader(new BasicHeader("X-CSRFToken", ourCSRFToken)); - request.addHeader(new BasicHeader("content-type", contentType)); - } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java index 4e0e14e45672..a007a114afcf 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java @@ -2,9 +2,11 @@ package com.jetbrains.edu.learning.stepic; public class EduStepicNames { public static final String STEPIC_URL = "https://stepic.org"; + public static final String TOKEN_URL = STEPIC_URL + "/oauth2/token/"; public static final String STEPIC_API_URL = STEPIC_URL + "/api"; + public static final String RECOMMENDATIONS_URL = "/recommendations"; - public static final String CONTENT_TYPE_APPL_JSON = "application/json"; + public static final String CONTENT_TYPE_APP_JSON = "application/json"; public static final String LESSONS = "/lessons/"; public static final String RECOMMENDATION_REACTIONS_URL = "/recommendation-reactions"; public static final String ATTEMPTS = "/attempts"; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java index 9f0a2779f936..c66f150688b4 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java @@ -44,13 +44,11 @@ public class LoginDialog extends DialogWrapper { @Override protected void doOKAction() { if (!validateLoginAndPasswordFields()) return; - final StepicUser user = EduStepicConnector.login(myLoginPanel.getLogin(), myLoginPanel.getPassword()); - if (user != null) { + final Project project = ProjectUtil.guessCurrentProject(myLoginPanel.getContentPanel()); + final StepicUser stepicUser = EduStepicAuthorizedClient.login(myLoginPanel.getLogin(), myLoginPanel.getPassword()); + if (stepicUser != null) { + StudyTaskManager.getInstance(project).setUser(stepicUser); doJustOkAction(); - final Project project = ProjectUtil.guessCurrentProject(myLoginPanel.getContentPanel()); - user.setEmail(myLoginPanel.getLogin()); - user.setPassword(myLoginPanel.getPassword()); - StudyTaskManager.getInstance(project).setUser(user); } else { setErrorText("Login failed"); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java index fabfd285d130..2e2ac1d8d221 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java @@ -151,7 +151,10 @@ public class StepicStudyOptions implements StudyOptionsProvider { user.setPassword(password); // login to post credentials - EduStepicConnector.login(login, password); + final StepicUser stepicUser = EduStepicAuthorizedClient.login(login, password); + if (stepicUser != null) { + taskManager.setUser(stepicUser); + } } } else { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUser.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUser.java index b501886b80c5..d07c526f1f09 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUser.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUser.java @@ -12,6 +12,8 @@ public class StepicUser { private String myFirstName; private String myLastName; private String myEmail; + private String myAccessToken; + private String myRefreshToken; public StepicUser() { myFirstName = ""; @@ -74,4 +76,25 @@ public class StepicUser { public String getName() { return StringUtil.join(new String[]{myFirstName, myLastName}, " "); } + + public String getAccessToken() { + return myAccessToken; + } + + public void setAccessToken(String accessToken) { + this.myAccessToken = accessToken; + } + + public String getRefreshToken() { + return myRefreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.myRefreshToken = refreshToken; + } + + public void setupTokenInfo(StepicWrappers.TokenInfo tokenInfo) { + myAccessToken = tokenInfo.getAccessToken(); + myRefreshToken = tokenInfo.getRefreshToken(); + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java index 0661580f5509..95c86af42025 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java @@ -435,4 +435,25 @@ public class StepicWrappers { enrollment = new Enrollment(courseId); } } + + static class TokenInfo { + @Expose String accessToken; + @Expose String refreshToken; + @Expose String tokenType; + @Expose String scope; + @Expose int expiresIn; + + public TokenInfo() { + accessToken = ""; + refreshToken = ""; + } + + public String getAccessToken() { + return accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java index 9438b30ee520..9abff2c91767 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java @@ -30,7 +30,7 @@ import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.stepic.CourseInfo; -import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.EduStepicAuthorizedClient; import com.jetbrains.edu.learning.stepic.LoginDialog; import com.jetbrains.edu.learning.stepic.StepicUser; import icons.InteractiveLearningIcons; @@ -344,13 +344,12 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - final StepicUser stepicUser = StudyUtils.execCancelable(() -> EduStepicConnector.login(myLoginPanel.getLogin(), - myLoginPanel.getPassword())); + final StepicUser stepicUser = StudyUtils.execCancelable(() -> EduStepicAuthorizedClient.login(myLoginPanel.getLogin(), + myLoginPanel.getPassword())); if (stepicUser != null) { stepicUser.setEmail(myLoginPanel.getLogin()); stepicUser.setPassword(myLoginPanel.getPassword()); myGenerator.myUser = stepicUser; - myGenerator.setEnrolledCoursesIds(EduStepicConnector.getEnrolledCoursesIds()); final List courses = myGenerator.getCourses(true); if (courses != null && myRefreshCourseList) { diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index 4a89f56aa645..f5f593991de6 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -177,10 +177,9 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator beforeProjectGenerated(@Nullable Sdk sdk) { return generator -> { - final List enrolledCoursesIds = myGenerator.getEnrolledCoursesIds(); final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem(); if (course == null) return true; - if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) { + if (course.isAdaptive()) { ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId())); From 9a5f47622f6d77c8cb3b2d9c43577a72046c69b6 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Sep 2016 19:26:02 +0300 Subject: [PATCH 05/14] separate course-creator stepic connector from student one --- .../actions/CCGetCourseFromStepic.java | 3 +- .../coursecreator/actions/CCPushCourse.java | 10 +- .../coursecreator/actions/CCPushLesson.java | 8 +- .../edu/coursecreator/actions/CCPushTask.java | 6 +- .../learning/stepic/CCStepicConnector.java | 362 ++++++++++++++++++ .../learning/stepic/EduStepicConnector.java | 336 +--------------- 6 files changed, 380 insertions(+), 345 deletions(-) create mode 100644 python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java index 60223fcfde66..73683ee54e38 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCGetCourseFromStepic.java @@ -24,6 +24,7 @@ import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.courseGeneration.StudyGenerator; +import com.jetbrains.edu.learning.stepic.CCStepicConnector; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import org.jetbrains.annotations.NotNull; @@ -61,7 +62,7 @@ public class CCGetCourseFromStepic extends DumbAwareAction { private static void createCourse(Project project, String courseId) { final VirtualFile baseDir = project.getBaseDir(); - final CourseInfo info = EduStepicConnector.getCourseInfo(project, courseId); + final CourseInfo info = CCStepicConnector.getCourseInfo(project, courseId); if (info == null) return; final Course course = EduStepicConnector.getCourse(project, info); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java index bdf55716c5e0..87a4e51f3af0 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java @@ -15,8 +15,8 @@ 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.statistics.EduUsagesCollector; +import com.jetbrains.edu.learning.stepic.CCStepicConnector; import com.jetbrains.edu.learning.stepic.CourseInfo; -import com.jetbrains.edu.learning.stepic.EduStepicConnector; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -59,21 +59,21 @@ public class CCPushCourse extends DumbAwareAction { public void run(@NotNull ProgressIndicator indicator) { for (Lesson lesson : course.getLessons()) { if (lesson.getId() > 0) { - EduStepicConnector.updateLesson(project, lesson, indicator); + CCStepicConnector.updateLesson(project, lesson, indicator); } else { final CourseInfo info = CourseInfo.fromCourse(course); - final int lessonId = EduStepicConnector.postLesson(project, lesson, indicator); + final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator); final List sections = info.getSections(); final Integer sectionId = sections.get(sections.size() - 1); - EduStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); + CCStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); } } } }); } else { - EduStepicConnector.postCourseWithProgress(project, course); + CCStepicConnector.postCourseWithProgress(project, course); } EduUsagesCollector.courseUploaded(); } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java index 3424ab3a1159..2122709f977e 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java @@ -15,8 +15,8 @@ import com.jetbrains.edu.coursecreator.CCUtils; 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.stepic.CCStepicConnector; import com.jetbrains.edu.learning.stepic.CourseInfo; -import com.jetbrains.edu.learning.stepic.EduStepicConnector; import com.jetbrains.edu.learning.stepic.EduStepicNames; import org.jetbrains.annotations.NotNull; @@ -77,14 +77,14 @@ public class CCPushLesson extends DumbAwareAction { public void run(@NotNull ProgressIndicator indicator) { indicator.setText("Uploading lesson to " + EduStepicNames.STEPIC_URL); if (lesson.getId() > 0) { - EduStepicConnector.updateLesson(project, lesson, indicator); + CCStepicConnector.updateLesson(project, lesson, indicator); } else { final CourseInfo info = CourseInfo.fromCourse(course); - final int lessonId = EduStepicConnector.postLesson(project, lesson, indicator); + final int lessonId = CCStepicConnector.postLesson(project, lesson, indicator); final List sections = info.getSections(); final Integer sectionId = sections.get(sections.size()-1); - EduStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); + CCStepicConnector.postUnit(project, lessonId, lesson.getIndex(), sectionId); } }}); } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java index 260ea0ad2cec..6445c2715c0d 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java @@ -16,7 +16,7 @@ import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.CCStepicConnector; import com.jetbrains.edu.learning.stepic.EduStepicNames; import org.jetbrains.annotations.NotNull; @@ -82,10 +82,10 @@ public class CCPushTask extends DumbAwareAction { public void run(@NotNull ProgressIndicator indicator) { indicator.setText("Uploading task to " + EduStepicNames.STEPIC_URL); if (task.getStepicId() <= 0) { - EduStepicConnector.postTask(project, task, lesson.getId()); + CCStepicConnector.postTask(project, task, lesson.getId()); } else { - EduStepicConnector.updateTask(project, task); + CCStepicConnector.updateTask(project, task); } } }); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java new file mode 100644 index 000000000000..ccaaa981b002 --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java @@ -0,0 +1,362 @@ +package com.jetbrains.edu.learning.stepic; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileFilter; +import com.jetbrains.edu.learning.StudySerializationUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.core.EduUtils; +import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Lesson; +import com.jetbrains.edu.learning.courseFormat.Task; +import org.apache.commons.codec.binary.Base64; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +public class CCStepicConnector { + private static final Logger LOG = Logger.getInstance(CCStepicConnector.class.getName()); + + private CCStepicConnector() { + } + + public static CourseInfo getCourseInfo(Project project, String courseId) { + final String url = EduStepicNames.COURSES + "/" + courseId; + try { + final StepicWrappers.CoursesContainer coursesContainer = + EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class, project); + return coursesContainer.courses.get(0); + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + return null; + } + + public static void postCourseWithProgress(final Project project, @NotNull final Course course) { + ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Uploading Course", true) { + @Override + public void run(@NotNull final ProgressIndicator indicator) { + postCourse(project, course, indicator); + } + }); + } + + private static void postCourse(final Project project, @NotNull Course course, @NotNull final ProgressIndicator indicator) { + indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL); + final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses"); + + final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser(); + if (currentUser != null) { + final List courseAuthors = course.getAuthors(); + for (int i = 0; i < courseAuthors.size(); i++) { + if (courseAuthors.size() > i) { + final StepicUser courseAuthor = courseAuthors.get(i); + currentUser.setFirstName(courseAuthor.getFirstName()); + currentUser.setLastName(courseAuthor.getLastName()); + } + } + course.setAuthors(Collections.singletonList(currentUser)); + } + + String requestBody = new Gson().toJson(new StepicWrappers.CourseWrapper(course)); + request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + + try { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + final StatusLine line = response.getStatusLine(); + EntityUtils.consume(responseEntity); + if (line.getStatusCode() != HttpStatus.SC_CREATED) { + LOG.error("Failed to push " + responseString); + return; + } + final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0); + course.setId(postedCourse.id); + final int sectionId = postModule(project, postedCourse.id, 1, String.valueOf(postedCourse.getName())); + int position = 1; + for (Lesson lesson : course.getLessons()) { + indicator.checkCanceled(); + final int lessonId = postLesson(project, lesson, indicator); + postUnit(project, lessonId, position, sectionId); + position += 1; + } + ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator)); + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + } + + private static void postAdditionalFiles(@NotNull final Project project, int id, ProgressIndicator indicator) { + final VirtualFile baseDir = project.getBaseDir(); + final List files = VfsUtil.getChildren(baseDir, new VirtualFileFilter() { + @Override + public boolean accept(VirtualFile file) { + final String name = file.getName(); + return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS) && + !"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith("."); + } + }); + + if (!files.isEmpty()) { + final int sectionId = postModule(project, id, 2, EduNames.PYCHARM_ADDITIONAL); + final Lesson lesson = new Lesson(); + lesson.setName(EduNames.PYCHARM_ADDITIONAL); + final Task task = new Task(); + task.setLesson(lesson); + task.setName(EduNames.PYCHARM_ADDITIONAL); + task.setIndex(1); + task.setText(EduNames.PYCHARM_ADDITIONAL); + for (VirtualFile file : files) { + try { + if (file != null) { + if (EduUtils.isImage(file.getName())) { + task.addTestsTexts(file.getName(), Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream()))); + } + else { + task.addTestsTexts(file.getName(), FileUtil.loadTextAndClose(file.getInputStream())); + } + } + } + catch (IOException e) { + LOG.error("Can't find file " + file.getPath()); + } + } + lesson.addTask(task); + lesson.setIndex(1); + final int lessonId = postLesson(project, lesson, indicator); + postUnit(project, lessonId, 1, sectionId); + } + } + + public static void postUnit(@NotNull final Project project, int lessonId, int position, int sectionId) { + final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.UNITS); + final StepicWrappers.UnitWrapper unitWrapper = new StepicWrappers.UnitWrapper(); + unitWrapper.unit = new StepicWrappers.Unit(); + unitWrapper.unit.lesson = lessonId; + unitWrapper.unit.position = position; + unitWrapper.unit.section = sectionId; + + String requestBody = new Gson().toJson(unitWrapper); + request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + + try { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + final StatusLine line = response.getStatusLine(); + EntityUtils.consume(responseEntity); + if (line.getStatusCode() != HttpStatus.SC_CREATED) { + LOG.error("Failed to push " + responseString); + } + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + } + + private static int postModule(@NotNull final Project project, int courseId, int position, @NotNull final String title) { + final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/sections"); + final StepicWrappers.Section section = new StepicWrappers.Section(); + section.course = courseId; + section.title = title; + section.position = position; + final StepicWrappers.SectionWrapper sectionContainer = new StepicWrappers.SectionWrapper(); + sectionContainer.section = section; + String requestBody = new Gson().toJson(sectionContainer); + request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + + try { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + final StatusLine line = response.getStatusLine(); + EntityUtils.consume(responseEntity); + if (line.getStatusCode() != HttpStatus.SC_CREATED) { + LOG.error("Failed to push " + responseString); + return -1; + } + final StepicWrappers.Section + postedSection = new Gson().fromJson(responseString, StepicWrappers.SectionContainer.class).sections.get(0); + return postedSection.id; + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + return -1; + } + + public static int updateTask(@NotNull final Project project, @NotNull final Task task) { + final Lesson lesson = task.getLesson(); + final int lessonId = lesson.getId(); + + final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + "/step-sources/" + String.valueOf(task.getStepicId())); + final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). + registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); + ApplicationManager.getApplication().invokeLater(() -> { + task.addTestsTexts("tests.py", task.getTestsText(project)); + final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId)); + request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + + try { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + EntityUtils.consume(responseEntity); + final StatusLine line = response.getStatusLine(); + if (line.getStatusCode() != HttpStatus.SC_OK) { + LOG.error("Failed to push " + responseString); + } + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + }); + return -1; + } + + public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) { + final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + EduStepicNames.LESSONS + String.valueOf(lesson.getId())); + + String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson)); + request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + + try { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + final StatusLine line = response.getStatusLine(); + EntityUtils.consume(responseEntity); + if (line.getStatusCode() != HttpStatus.SC_OK) { + LOG.error("Failed to push " + responseString); + return -1; + } + final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); + for (Integer step : postedLesson.steps) { + deleteTask(step, project); + } + + for (Task task : lesson.getTaskList()) { + indicator.checkCanceled(); + postTask(project, task, lesson.getId()); + } + return lesson.getId(); + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + return -1; + } + + public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) { + final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/lessons"); + + String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson)); + request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + + try { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + final StatusLine line = response.getStatusLine(); + EntityUtils.consume(responseEntity); + if (line.getStatusCode() != HttpStatus.SC_CREATED) { + LOG.error("Failed to push " + responseString); + return 0; + } + final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); + lesson.setId(postedLesson.getId()); + for (Task task : lesson.getTaskList()) { + indicator.checkCanceled(); + postTask(project, task, postedLesson.getId()); + } + return postedLesson.getId(); + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + return -1; + } + + public static void deleteTask(@NotNull final Integer task, Project project) { + final HttpDelete request = new HttpDelete(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES + task); + ApplicationManager.getApplication().invokeLater(() -> { + try { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + EntityUtils.consume(responseEntity); + final StatusLine line = response.getStatusLine(); + if (line.getStatusCode() != HttpStatus.SC_NO_CONTENT) { + LOG.error("Failed to delete task " + responseString); + } + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + }); + } + + public static void postTask(final Project project, @NotNull final Task task, final int lessonId) { + final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/step-sources"); + //TODO: register type adapter for task files here? + final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). + registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); + ApplicationManager.getApplication().invokeLater(() -> { + final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId)); + request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + + try { + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); + final CloseableHttpResponse response = client.execute(request); + final StatusLine line = response.getStatusLine(); + final HttpEntity responseEntity = response.getEntity(); + final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; + EntityUtils.consume(responseEntity); + if (line.getStatusCode() != HttpStatus.SC_CREATED) { + LOG.error("Failed to push " + responseString); + return; + } + + final JsonObject postedTask = new Gson().fromJson(responseString, JsonObject.class); + final JsonObject stepSource = postedTask.getAsJsonArray("step-sources").get(0).getAsJsonObject(); + task.setStepicId(stepSource.getAsJsonPrimitive("id").getAsInt()); + } + catch (IOException e) { + LOG.error(e.getMessage()); + } + }); + } +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index e743ea13f3bc..c89ec9aa3a5d 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -2,29 +2,18 @@ package com.jetbrains.edu.learning.stepic; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileFilter; -import com.jetbrains.edu.learning.StudySerializationUtils; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.*; -import org.apache.commons.codec.binary.Base64; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Lesson; +import com.jetbrains.edu.learning.courseFormat.Task; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; @@ -80,19 +69,6 @@ public class EduStepicConnector { return Collections.singletonList(CourseInfo.INVALID_COURSE); } - public static CourseInfo getCourseInfo(Project project, String courseId) { - final String url = EduStepicNames.COURSES + "/" + courseId; - try { - final StepicWrappers.CoursesContainer coursesContainer = - EduStepicAuthorizedClient.getFromStepic(url, StepicWrappers.CoursesContainer.class, project); - return coursesContainer.courses.get(0); - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - return null; - } - public static Date getCourseUpdateDate(final int courseId) { final String url = EduStepicNames.COURSES + "/" + courseId; try { @@ -309,308 +285,4 @@ public class EduStepicConnector { LOG.error("Failed to make submission " + responseString); } } - - public static void postCourseWithProgress(final Project project, @NotNull final Course course) { - ProgressManager.getInstance().run(new com.intellij.openapi.progress.Task.Modal(project, "Uploading Course", true) { - @Override - public void run(@NotNull final ProgressIndicator indicator) { - postCourse(project, course, indicator); - } - }); - } - - private static void postCourse(final Project project, @NotNull Course course, @NotNull final ProgressIndicator indicator) { - indicator.setText("Uploading course to " + EduStepicNames.STEPIC_URL); - final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/courses"); - - final StepicUser currentUser = EduStepicAuthorizedClient.getCurrentUser(); - if (currentUser != null) { - final List courseAuthors = course.getAuthors(); - for (int i = 0; i < courseAuthors.size(); i++) { - if (courseAuthors.size() > i) { - final StepicUser courseAuthor = courseAuthors.get(i); - currentUser.setFirstName(courseAuthor.getFirstName()); - currentUser.setLastName(courseAuthor.getLastName()); - } - } - course.setAuthors(Collections.singletonList(currentUser)); - } - - String requestBody = new Gson().toJson(new StepicWrappers.CourseWrapper(course)); - request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - - try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final CloseableHttpResponse response = client.execute(request); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - final StatusLine line = response.getStatusLine(); - EntityUtils.consume(responseEntity); - if (line.getStatusCode() != HttpStatus.SC_CREATED) { - LOG.error("Failed to push " + responseString); - return; - } - final CourseInfo postedCourse = new Gson().fromJson(responseString, StepicWrappers.CoursesContainer.class).courses.get(0); - course.setId(postedCourse.id); - final int sectionId = postModule(project, postedCourse.id, 1, String.valueOf(postedCourse.getName())); - int position = 1; - for (Lesson lesson : course.getLessons()) { - indicator.checkCanceled(); - final int lessonId = postLesson(project, lesson, indicator); - postUnit(project, lessonId, position, sectionId); - position += 1; - } - ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator)); - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - } - - private static void postAdditionalFiles(@NotNull final Project project, int id, ProgressIndicator indicator) { - final VirtualFile baseDir = project.getBaseDir(); - final List files = VfsUtil.getChildren(baseDir, new VirtualFileFilter() { - @Override - public boolean accept(VirtualFile file) { - final String name = file.getName(); - return !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS) && - !"pyc".equals(file.getExtension()) && !file.isDirectory() && !name.equals(EduNames.TEST_HELPER) && !name.startsWith("."); - } - }); - - if (!files.isEmpty()) { - final int sectionId = postModule(project, id, 2, EduNames.PYCHARM_ADDITIONAL); - final Lesson lesson = new Lesson(); - lesson.setName(EduNames.PYCHARM_ADDITIONAL); - final Task task = new Task(); - task.setLesson(lesson); - task.setName(EduNames.PYCHARM_ADDITIONAL); - task.setIndex(1); - task.setText(EduNames.PYCHARM_ADDITIONAL); - for (VirtualFile file : files) { - try { - if (file != null) { - if (EduUtils.isImage(file.getName())) { - task.addTestsTexts(file.getName(), Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream()))); - } - else { - task.addTestsTexts(file.getName(), FileUtil.loadTextAndClose(file.getInputStream())); - } - } - } - catch (IOException e) { - LOG.error("Can't find file " + file.getPath()); - } - } - lesson.addTask(task); - lesson.setIndex(1); - final int lessonId = postLesson(project, lesson, indicator); - postUnit(project, lessonId, 1, sectionId); - } - } - - public static void postUnit(@NotNull final Project project, int lessonId, int position, int sectionId) { - final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.UNITS); - final StepicWrappers.UnitWrapper unitWrapper = new StepicWrappers.UnitWrapper(); - unitWrapper.unit = new StepicWrappers.Unit(); - unitWrapper.unit.lesson = lessonId; - unitWrapper.unit.position = position; - unitWrapper.unit.section = sectionId; - - String requestBody = new Gson().toJson(unitWrapper); - request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - - try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final CloseableHttpResponse response = client.execute(request); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - final StatusLine line = response.getStatusLine(); - EntityUtils.consume(responseEntity); - if (line.getStatusCode() != HttpStatus.SC_CREATED) { - LOG.error("Failed to push " + responseString); - } - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - } - - private static int postModule(@NotNull final Project project, int courseId, int position, @NotNull final String title) { - final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/sections"); - final StepicWrappers.Section section = new StepicWrappers.Section(); - section.course = courseId; - section.title = title; - section.position = position; - final StepicWrappers.SectionWrapper sectionContainer = new StepicWrappers.SectionWrapper(); - sectionContainer.section = section; - String requestBody = new Gson().toJson(sectionContainer); - request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - - try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final CloseableHttpResponse response = client.execute(request); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - final StatusLine line = response.getStatusLine(); - EntityUtils.consume(responseEntity); - if (line.getStatusCode() != HttpStatus.SC_CREATED) { - LOG.error("Failed to push " + responseString); - return -1; - } - final StepicWrappers.Section - postedSection = new Gson().fromJson(responseString, StepicWrappers.SectionContainer.class).sections.get(0); - return postedSection.id; - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - return -1; - } - - public static int updateTask(@NotNull final Project project, @NotNull final Task task) { - final Lesson lesson = task.getLesson(); - final int lessonId = lesson.getId(); - - final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + "/step-sources/" + String.valueOf(task.getStepicId())); - final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). - registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); - ApplicationManager.getApplication().invokeLater(() -> { - task.addTestsTexts("tests.py", task.getTestsText(project)); - final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId)); - request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - - try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final CloseableHttpResponse response = client.execute(request); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - EntityUtils.consume(responseEntity); - final StatusLine line = response.getStatusLine(); - if (line.getStatusCode() != HttpStatus.SC_OK) { - LOG.error("Failed to push " + responseString); - } - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - }); - return -1; - } - - public static int updateLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) { - final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + EduStepicNames.LESSONS + String.valueOf(lesson.getId())); - - String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson)); - request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - - try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final CloseableHttpResponse response = client.execute(request); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - final StatusLine line = response.getStatusLine(); - EntityUtils.consume(responseEntity); - if (line.getStatusCode() != HttpStatus.SC_OK) { - LOG.error("Failed to push " + responseString); - return -1; - } - final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); - for (Integer step : postedLesson.steps) { - deleteTask(step, project); - } - - for (Task task : lesson.getTaskList()) { - indicator.checkCanceled(); - postTask(project, task, lesson.getId()); - } - return lesson.getId(); - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - return -1; - } - - public static int postLesson(@NotNull final Project project, @NotNull final Lesson lesson, ProgressIndicator indicator) { - final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/lessons"); - - String requestBody = new Gson().toJson(new StepicWrappers.LessonWrapper(lesson)); - request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - - try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final CloseableHttpResponse response = client.execute(request); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - final StatusLine line = response.getStatusLine(); - EntityUtils.consume(responseEntity); - if (line.getStatusCode() != HttpStatus.SC_CREATED) { - LOG.error("Failed to push " + responseString); - return 0; - } - final Lesson postedLesson = new Gson().fromJson(responseString, Course.class).getLessons().get(0); - lesson.setId(postedLesson.getId()); - for (Task task : lesson.getTaskList()) { - indicator.checkCanceled(); - postTask(project, task, postedLesson.getId()); - } - return postedLesson.getId(); - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - return -1; - } - - public static void deleteTask(@NotNull final Integer task, Project project) { - final HttpDelete request = new HttpDelete(EduStepicNames.STEPIC_API_URL + EduStepicNames.STEP_SOURCES + task); - ApplicationManager.getApplication().invokeLater(() -> { - try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final CloseableHttpResponse response = client.execute(request); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - EntityUtils.consume(responseEntity); - final StatusLine line = response.getStatusLine(); - if (line.getStatusCode() != HttpStatus.SC_NO_CONTENT) { - LOG.error("Failed to delete task " + responseString); - } - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - }); - } - - public static void postTask(final Project project, @NotNull final Task task, final int lessonId) { - final HttpPost request = new HttpPost(EduStepicNames.STEPIC_API_URL + "/step-sources"); - //TODO: register type adapter for task files here? - final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). - registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); - ApplicationManager.getApplication().invokeLater(() -> { - final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId)); - request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); - - try { - final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(project); - final CloseableHttpResponse response = client.execute(request); - final StatusLine line = response.getStatusLine(); - final HttpEntity responseEntity = response.getEntity(); - final String responseString = responseEntity != null ? EntityUtils.toString(responseEntity) : ""; - EntityUtils.consume(responseEntity); - if (line.getStatusCode() != HttpStatus.SC_CREATED) { - LOG.error("Failed to push " + responseString); - return; - } - - final JsonObject postedTask = new Gson().fromJson(responseString, JsonObject.class); - final JsonObject stepSource = postedTask.getAsJsonArray("step-sources").get(0).getAsJsonObject(); - task.setStepicId(stepSource.getAsJsonPrimitive("id").getAsInt()); - } - catch (IOException e) { - LOG.error(e.getMessage()); - } - }); - } } From a8b86610cbd2ad9fbd35f606d877470cc1886a8a Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Sep 2016 20:02:08 +0300 Subject: [PATCH 06/14] cleanup --- .../edu/learning/StudyProjectComponent.java | 31 +++++++------------ .../stepic/EduStepicAuthorizedClient.java | 3 +- .../stepic/StepicAdaptiveReactionsPanel.java | 2 +- .../ui/CCCreateAnswerPlaceholderPanel.java | 2 +- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index 9c8b39331e72..e400b13e150e 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -97,26 +97,19 @@ public class StudyProjectComponent implements ProjectComponent { } StudyUtils.registerStudyToolWindow(course, myProject); - StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new DumbAwareRunnable() { - @Override - public void run() { - Course course = StudyTaskManager.getInstance(myProject).getCourse(); - if (course != null) { - final UISettings instance = UISettings.getInstance(); - if (instance != null) { - instance.HIDE_TOOL_STRIPES = false; - instance.fireUISettingsChanged(); - } - registerShortcuts(); - EduUsagesCollector.projectTypeOpened(course.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY); - } + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(() -> ApplicationManager.getApplication().invokeLater( + (DumbAwareRunnable)() -> ApplicationManager.getApplication().runWriteAction((DumbAwareRunnable)() -> { + Course course1 = StudyTaskManager.getInstance(myProject).getCourse(); + if (course1 != null) { + final UISettings instance = UISettings.getInstance(); + if (instance != null) { + instance.HIDE_TOOL_STRIPES = false; + instance.fireUISettingsChanged(); } - }); - } - })); + registerShortcuts(); + EduUsagesCollector.projectTypeOpened(course1.isAdaptive() ? EduNames.ADAPTIVE : EduNames.STUDY); + } + }))); } private void registerShortcuts() { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java index 6a3338ec228a..d1e0c85fe985 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java @@ -180,8 +180,7 @@ public class EduStepicAuthorizedClient { parameters.add(new BasicNameValuePair("grant_type", "refresh_token")); parameters.add(new BasicNameValuePair("refresh_token", refreshToken)); - final StepicWrappers.TokenInfo tokenInfo = postCredentials(parameters); - return tokenInfo; + return postCredentials(parameters); } @Nullable diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java index c9e625ea2370..065b9e152621 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java @@ -158,7 +158,7 @@ public class StepicAdaptiveReactionsPanel extends JPanel { public void run(@NotNull ProgressIndicator indicator) { StepicAdaptiveReactionsPanel.this.setEnabledRecursive(false); ApplicationManager.getApplication().invokeLater(()->setBackground(UIUtil.getLabelBackground())); - EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, myReaction, indicator); + EduAdaptiveStepicConnector.addNextRecommendedTask(StepicAdaptiveReactionsPanel.this.myProject, myReaction, indicator); StepicAdaptiveReactionsPanel.this.setEnabledRecursive(true); } }); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java index 9dd1757670c6..9013b7a4d709 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/CCCreateAnswerPlaceholderPanel.java @@ -21,7 +21,7 @@ import java.util.ArrayList; import java.util.List; public class CCCreateAnswerPlaceholderPanel { - private static String ourFirstHintText = "Type here to add hint"; + private static final String ourFirstHintText = "Type here to add hint"; private JPanel myPanel; private JTextArea myHintTextArea; From 8fd51da0e607c46e7d001c074a40a22177d9a453 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Sep 2016 20:06:20 +0300 Subject: [PATCH 07/14] proper name for step id in task --- .../edu/coursecreator/actions/CCPushTask.java | 4 ++-- .../edu/learning/StudyProjectComponent.java | 2 +- .../edu/learning/courseFormat/Course.java | 2 +- .../edu/learning/courseFormat/Lesson.java | 2 +- .../jetbrains/edu/learning/courseFormat/Task.java | 14 +++++++------- .../edu/learning/stepic/CCStepicConnector.java | 4 ++-- .../stepic/EduAdaptiveStepicConnector.java | 6 +++--- .../edu/learning/stepic/EduStepicConnector.java | 6 +++--- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java index 6445c2715c0d..b2895586af22 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushTask.java @@ -48,7 +48,7 @@ public class CCPushTask extends DumbAwareAction { if (lesson != null && lesson.getId() > 0 && course.getId() > 0) { e.getPresentation().setEnabledAndVisible(true); final com.jetbrains.edu.learning.courseFormat.Task task = lesson.getTask(taskDir.getName()); - if (task.getStepicId() <= 0) { + if (task.getStepId() <= 0) { e.getPresentation().setText("Upload Task to Stepik"); } } @@ -81,7 +81,7 @@ public class CCPushTask extends DumbAwareAction { @Override public void run(@NotNull ProgressIndicator indicator) { indicator.setText("Uploading task to " + EduStepicNames.STEPIC_URL); - if (task.getStepicId() <= 0) { + if (task.getStepId() <= 0) { CCStepicConnector.postTask(project, task, lesson.getId()); } else { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index e400b13e150e..941ae6e34c8a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -190,7 +190,7 @@ public class StudyProjectComponent implements ProjectComponent { final ArrayList tasks = new ArrayList<>(); for (Task task : lesson.getTaskList()) { index += 1; - final Task studentTask = studentLesson.getTask(task.getStepicId()); + final Task studentTask = studentLesson.getTask(task.getStepId()); if (studentTask != null && StudyStatus.Solved.equals(studentTask.getStatus())) { studentTask.setIndex(index); tasks.add(studentTask); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java index 7c0772b56fd1..9f14e104d415 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java @@ -151,7 +151,7 @@ public class Course { for (Lesson lesson : lessons) { lesson.setUpdateDate(EduStepicConnector.getLessonUpdateDate(lesson.getId())); for (Task task : lesson.getTaskList()) { - task.setUpdateDate(EduStepicConnector.getTaskUpdateDate(task.getStepicId())); + task.setUpdateDate(EduStepicConnector.getTaskUpdateDate(task.getStepId())); } } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Lesson.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Lesson.java index 020796196167..1713576e1a16 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Lesson.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Lesson.java @@ -93,7 +93,7 @@ public class Lesson implements StudyItem { public Task getTask(int id) { for (Task task : taskList) { - if (task.getStepicId() == id) { + if (task.getStepId() == id) { return task; } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java index db3daa38fdb1..378b68e24afc 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java @@ -31,7 +31,7 @@ public class Task implements StudyItem { private StudyStatus myStatus = StudyStatus.Unchecked; @SerializedName("stepic_id") - @Expose private int myStepicId; + @Expose private int myStepId; @SerializedName("task_files") @Expose public Map taskFiles = new HashMap<>(); @@ -204,12 +204,12 @@ public class Task implements StudyItem { return result; } - public void setStepicId(int stepicId) { - myStepicId = stepicId; + public void setStepId(int stepId) { + myStepId = stepId; } - public int getStepicId() { - return myStepicId; + public int getStepId() { + return myStepId; } public StudyStatus getStatus() { @@ -244,8 +244,8 @@ public class Task implements StudyItem { } public boolean isUpToDate() { - if (getStepicId() == 0) return true; - final Date date = EduStepicConnector.getTaskUpdateDate(getStepicId()); + if (getStepId() == 0) return true; + final Date date = EduStepicConnector.getTaskUpdateDate(getStepId()); if (date == null) return true; if (myUpdateDate == null) return false; return !date.after(myUpdateDate); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java index ccaaa981b002..47e4d59cad77 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CCStepicConnector.java @@ -218,7 +218,7 @@ public class CCStepicConnector { final Lesson lesson = task.getLesson(); final int lessonId = lesson.getId(); - final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + "/step-sources/" + String.valueOf(task.getStepicId())); + final HttpPut request = new HttpPut(EduStepicNames.STEPIC_API_URL + "/step-sources/" + String.valueOf(task.getStepId())); final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation(). registerTypeAdapter(AnswerPlaceholder.class, new StudySerializationUtils.Json.StepicAnswerPlaceholderAdapter()).create(); ApplicationManager.getApplication().invokeLater(() -> { @@ -352,7 +352,7 @@ public class CCStepicConnector { final JsonObject postedTask = new Gson().fromJson(responseString, JsonObject.class); final JsonObject stepSource = postedTask.getAsJsonArray("step-sources").get(0).getAsJsonObject(); - task.setStepicId(stepSource.getAsJsonPrimitive("id").getAsInt()); + task.setStepId(stepSource.getAsJsonPrimitive("id").getAsInt()); } catch (IOException e) { LOG.error(e.getMessage()); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index c8e529f27c2a..7a1f75776f49 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -200,7 +200,7 @@ public class EduAdaptiveStepicConnector { final String lessonName = EduNames.LESSON + String.valueOf(adaptive.getIndex()); if (reaction == 0 || reaction == -1) { unsolvedTask.setName(task.getName()); - unsolvedTask.setStepicId(task.getStepicId()); + unsolvedTask.setStepId(task.getStepId()); unsolvedTask.setText(task.getText()); unsolvedTask.getTestsText().clear(); unsolvedTask.setStatus(StudyStatus.Unchecked); @@ -310,7 +310,7 @@ public class EduAdaptiveStepicConnector { private static Task getTaskFromStep(Project project, int lessonID, @NotNull final StepicWrappers.Step step, @NotNull String name) { final Task task = new Task(); task.setName(name); - task.setStepicId(lessonID); + task.setStepId(lessonID); task.setText(step.text); task.setStatus(StudyStatus.Unchecked); if (step.options.samples != null) { @@ -496,7 +496,7 @@ public class EduAdaptiveStepicConnector { } private static int getAttemptId(@NotNull final Project project, @NotNull Task task) throws IOException { - final StepicWrappers.AttemptToPostWrapper attemptWrapper = new StepicWrappers.AttemptToPostWrapper(task.getStepicId()); + final StepicWrappers.AttemptToPostWrapper attemptWrapper = new StepicWrappers.AttemptToPostWrapper(task.getStepId()); final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); post.setEntity(new StringEntity(new Gson().toJson(attemptWrapper))); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index c89ec9aa3a5d..6a44929520fc 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -215,7 +215,7 @@ public class EduStepicConnector { final StepicWrappers.Step block = step.block; if (!block.name.equals(PYCHARM_PREFIX)) return; final Task task = new Task(); - task.setStepicId(stepicId); + task.setStepId(stepicId); task.setUpdateDate(step.update_date); task.setName(block.options != null ? block.options.title : PYCHARM_PREFIX); task.setText(block.text); @@ -237,12 +237,12 @@ public class EduStepicConnector { } public static void postAttempt(@NotNull final Task task, boolean passed, @NotNull final Project project) { - if (task.getStepicId() <= 0) { + if (task.getStepId() <= 0) { return; } final HttpPost attemptRequest = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); - String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(task.getStepicId())); + String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(task.getStepId())); attemptRequest.setEntity(new StringEntity(attemptRequestBody, ContentType.APPLICATION_JSON)); try { From 139fe5f6fcef2b992aadab9275a23a50e8fd8218 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Sep 2016 20:11:50 +0300 Subject: [PATCH 08/14] cleanup --- .../com/jetbrains/edu/learning/stepic/StepicWrappers.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java index 95c86af42025..01336b30eb05 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java @@ -119,12 +119,12 @@ public class StepicWrappers { String python27; @Nullable - public String getTemplateForLanguage(@NotNull final String langauge) { - if (langauge.equals(EduAdaptiveStepicConnector.PYTHON2)) { + public String getTemplateForLanguage(@NotNull final String language) { + if (language.equals(EduAdaptiveStepicConnector.PYTHON2)) { return python27; } - if (langauge.equals(EduAdaptiveStepicConnector.PYTHON3)) { + if (language.equals(EduAdaptiveStepicConnector.PYTHON3)) { return python3; } From 906139b1ec870d0a08d2fc1956e41f4575f21972 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 15 Sep 2016 20:15:17 +0300 Subject: [PATCH 09/14] cleanup --- .../com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java index 597c143f5697..3e96dfe834f8 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java @@ -24,7 +24,7 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware { final Course course = taskManager.getCourse(); if (course != null) { final StudyToolWindow studyToolWindow; - if (StudyUtils.hasJavaFx() && StudyTaskManager.getInstance(project).shouldUseJavaFx()) { + if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) { studyToolWindow = new StudyJavaFxToolWindow(); } else { From 6ef0630856b3d362d71d458883560fee5f443aea Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 16 Sep 2016 19:40:17 +0300 Subject: [PATCH 10/14] brought back get enrolled courses ids. tested with adaptive python course --- .../StudyProjectGenerator.java | 10 +++ .../stepic/EduAdaptiveStepicConnector.java | 27 +++++++- .../stepic/EduStepicAuthorizedClient.java | 67 +++++++++++++------ .../edu/learning/stepic/EduStepicClient.java | 2 +- .../learning/stepic/EduStepicConnector.java | 4 +- .../edu/learning/stepic/LoginDialog.java | 14 ++-- .../edu/learning/stepic/StepicUser.java | 2 +- .../edu/learning/ui/StudyNewProjectPanel.java | 7 +- .../PyStudyDirectoryProjectGenerator.java | 5 +- 9 files changed, 98 insertions(+), 40 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 2037f317613c..5df925d276f8 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -59,6 +59,7 @@ public class StudyProjectGenerator { private final List myListeners = ContainerUtil.newArrayList(); @Nullable public StepicUser myUser; private List myCourses = new ArrayList<>(); + private List myEnrolledCoursesIds = new ArrayList<>(); protected CourseInfo mySelectedCourseInfo; public void setCourses(List courses) { @@ -69,6 +70,15 @@ public class StudyProjectGenerator { return myUser != null && !StringUtil.isEmptyOrSpaces(myUser.getPassword()) && !StringUtil.isEmptyOrSpaces(myUser.getEmail()); } + public void setEnrolledCoursesIds(@NotNull final List coursesIds) { + myEnrolledCoursesIds = coursesIds; + } + + @NotNull + public List getEnrolledCoursesIds() { + return myEnrolledCoursesIds; + } + public void setSelectedCourse(@NotNull final CourseInfo courseName) { mySelectedCourseInfo = courseName; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index 7a1f75776f49..14c772db34c0 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -50,9 +50,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.TimeUnit; import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getStep; @@ -524,4 +522,27 @@ public class EduAdaptiveStepicConnector { " check_samples(samples=" + new GsonBuilder().create().toJson(samples) + ")"; task.addTestsTexts("tests.py", testText); } + + @NotNull + public static List getEnrolledCoursesIds(StepicUser stepicUser) { + try { + final URI enrolledCoursesUri = new URIBuilder(EduStepicNames.COURSES).addParameter("enrolled", "true").build(); + final List courses = EduStepicAuthorizedClient.getFromStepic(enrolledCoursesUri.toString(), + StepicWrappers.CoursesContainer.class, + stepicUser).courses; + final ArrayList ids = new ArrayList<>(); + for (CourseInfo course : courses) { + ids.add(course.getId()); + } + return ids; + } + catch (IOException e) { + LOG.warn(e.getMessage()); + } + catch (URISyntaxException e) { + LOG.warn(e.getMessage()); + } + return Collections.emptyList(); + } + } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java index d1e0c85fe985..15623b798f8c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java @@ -6,7 +6,6 @@ import com.google.gson.GsonBuilder; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.net.HttpConfigurable; import com.intellij.util.net.ssl.CertificateManager; @@ -52,10 +51,13 @@ public class EduStepicAuthorizedClient { if (ourClient != null) { return ourClient; } - ourClient = initializeClient(project); + final StepicUser stepicUser = StudyTaskManager.getInstance(project).getUser(); + ourClient = initializeClient(stepicUser); if (ourClient == null) { - if (login(project)) { - ourClient = initializeClient(project); + final StepicUser user = login(stepicUser); + if (user != null) { + StudyTaskManager.getInstance(project).setUser(user); + ourClient = initializeClient(stepicUser); } } return ourClient; @@ -65,11 +67,37 @@ public class EduStepicAuthorizedClient { return EduStepicClient.getFromStepic(link, container, getHttpClient(project)); } + /* + * This method should be used only in project generation while project is not available. + * Make sure you saved stepic user in task manager after using this method. + */ + @NotNull + public static CloseableHttpClient getHttpClient(@NotNull final StepicUser stepicUser) { + if (ourClient != null) { + return ourClient; + } + ourClient = initializeClient(stepicUser); + if (ourClient == null) { + final StepicUser user = login(stepicUser); + if (user != null) { + ourClient = initializeClient(stepicUser); + } + } + return ourClient; + } + + /* + * This method should be used only in project generation while project is not available. + * Make sure you saved stepic user in task manager after using this method. + */ + public static T getFromStepic(String link, final Class container, @NotNull final StepicUser stepicUser) throws IOException { + return EduStepicClient.getFromStepic(link, container, getHttpClient(stepicUser)); + } + @Nullable - private static CloseableHttpClient initializeClient(@NotNull final Project project) { + private static CloseableHttpClient initializeClient(@NotNull final StepicUser stepicUser) { final List headers = new ArrayList<>(); - final StepicUser currentUser = StudyTaskManager.getInstance(project).getUser(); - final String accessToken = currentUser.getAccessToken(); + final String accessToken = stepicUser.getAccessToken(); if (accessToken != null && !accessToken.isEmpty()) { headers.add(new BasicHeader("Authorization", "Bearer " + accessToken)); headers.add(new BasicHeader("Content-type", EduStepicNames.CONTENT_TYPE_APP_JSON)); @@ -117,8 +145,7 @@ public class EduStepicAuthorizedClient { return builder; } - private static boolean login(@NotNull final Project project) { - final StepicUser user = StudyTaskManager.getInstance(project).getUser(); + private static StepicUser login(@NotNull final StepicUser user) { final String login = user.getEmail(); final String refreshToken = user.getRefreshToken(); if (StringUtil.isEmptyOrSpaces(login)) { @@ -127,29 +154,29 @@ public class EduStepicAuthorizedClient { else { if (StringUtil.isNotEmpty(refreshToken)) { final StepicWrappers.TokenInfo tokenInfo = login(refreshToken); - user.setupTokenInfo(tokenInfo); + if (tokenInfo != null) { + user.setupTokenInfo(tokenInfo); + } } else { final StepicUser stepicUser = login(login, user.getPassword()); if (stepicUser == null) { return showLoginDialog(); } - else { - StudyTaskManager.getInstance(project).setUser(stepicUser); - } + return stepicUser; } } - return true; + return null; } - private static boolean showLoginDialog() { - final boolean[] logged = {false}; + private static StepicUser showLoginDialog() { + final StepicUser[] stepicUser = new StepicUser[1]; ApplicationManager.getApplication().invokeAndWait(() -> { final LoginDialog dialog = new LoginDialog(); dialog.show(); - logged[0] = dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE; + stepicUser[0] = dialog.getStepicUser(); }); - return logged[0]; + return stepicUser[0]; } public static StepicUser login(@NotNull final String email, @NotNull final String password) { @@ -167,7 +194,9 @@ public class EduStepicAuthorizedClient { if (currentUser != null) { user.setId(currentUser.getId()); } - user.setupTokenInfo(tokenInfo); + if (tokenInfo != null) { + user.setupTokenInfo(tokenInfo); + } return user; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java index dd770b8ffc01..3ed86017f1ee 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicClient.java @@ -51,7 +51,7 @@ public class EduStepicClient { } public static T getFromStepic(String link, final Class container) throws IOException { - return getFromStepic(link, container, ourClient); + return getFromStepic(link, container, getHttpClient()); } static T getFromStepic(String link, final Class container, @NotNull final CloseableHttpClient client) throws IOException { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 6a44929520fc..243e30213000 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -37,12 +37,12 @@ public class EduStepicConnector { private EduStepicConnector() { } - public static boolean enrollToCourse(final int courseId) { + public static boolean enrollToCourse(final int courseId, final StepicUser stepicUser) { HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ENROLLMENTS); try { final StepicWrappers.EnrollmentWrapper enrollment = new StepicWrappers.EnrollmentWrapper(String.valueOf(courseId)); post.setEntity(new StringEntity(new GsonBuilder().create().toJson(enrollment))); - final CloseableHttpClient client = EduStepicClient.getHttpClient(); + final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(stepicUser); CloseableHttpResponse response = client.execute(post); StatusLine line = response.getStatusLine(); return line.getStatusCode() == HttpStatus.SC_CREATED; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java index c66f150688b4..15a3c65e07bb 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/LoginDialog.java @@ -1,10 +1,7 @@ package com.jetbrains.edu.learning.stepic; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.text.StringUtil; -import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.ui.LoginPanel; import org.jetbrains.annotations.NotNull; @@ -12,6 +9,7 @@ import javax.swing.*; public class LoginDialog extends DialogWrapper { protected final LoginPanel myLoginPanel; + private StepicUser myStepicUser; public LoginDialog() { super(false); @@ -44,10 +42,8 @@ public class LoginDialog extends DialogWrapper { @Override protected void doOKAction() { if (!validateLoginAndPasswordFields()) return; - final Project project = ProjectUtil.guessCurrentProject(myLoginPanel.getContentPanel()); - final StepicUser stepicUser = EduStepicAuthorizedClient.login(myLoginPanel.getLogin(), myLoginPanel.getPassword()); - if (stepicUser != null) { - StudyTaskManager.getInstance(project).setUser(stepicUser); + myStepicUser = EduStepicAuthorizedClient.login(myLoginPanel.getLogin(), myLoginPanel.getPassword()); + if (myStepicUser != null) { doJustOkAction(); } else { @@ -74,4 +70,8 @@ public class LoginDialog extends DialogWrapper { public void clearErrors() { setErrorText(null); } + + public StepicUser getStepicUser() { + return myStepicUser; + } } \ No newline at end of file diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUser.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUser.java index d07c526f1f09..e40ae9428f60 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUser.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicUser.java @@ -93,7 +93,7 @@ public class StepicUser { this.myRefreshToken = refreshToken; } - public void setupTokenInfo(StepicWrappers.TokenInfo tokenInfo) { + public void setupTokenInfo(@NotNull final StepicWrappers.TokenInfo tokenInfo) { myAccessToken = tokenInfo.getAccessToken(); myRefreshToken = tokenInfo.getRefreshToken(); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java index 9abff2c91767..7029f9eac4f9 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java @@ -29,10 +29,7 @@ import com.intellij.util.ui.UIUtil; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; -import com.jetbrains.edu.learning.stepic.CourseInfo; -import com.jetbrains.edu.learning.stepic.EduStepicAuthorizedClient; -import com.jetbrains.edu.learning.stepic.LoginDialog; -import com.jetbrains.edu.learning.stepic.StepicUser; +import com.jetbrains.edu.learning.stepic.*; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -350,7 +347,7 @@ public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { stepicUser.setEmail(myLoginPanel.getLogin()); stepicUser.setPassword(myLoginPanel.getPassword()); myGenerator.myUser = stepicUser; - + myGenerator.setEnrolledCoursesIds(EduAdaptiveStepicConnector.getEnrolledCoursesIds(stepicUser)); final List courses = myGenerator.getCourses(true); if (courses != null && myRefreshCourseList) { ApplicationManager.getApplication().invokeLater(() -> refreshCoursesList(courses)); diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index f5f593991de6..e474f3e29a29 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -177,12 +177,13 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator beforeProjectGenerated(@Nullable Sdk sdk) { return generator -> { + final List enrolledCoursesIds = myGenerator.getEnrolledCoursesIds(); final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem(); if (course == null) return true; - if (course.isAdaptive()) { + if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) { ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId())); + return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId(), myGenerator.myUser)); }, "Creating Course", true, ProjectManager.getInstance().getDefaultProject()); } From 020e0e7dbb5e9ffe2210987839e6ed2ec5a71690 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 19 Sep 2016 13:14:14 +0200 Subject: [PATCH 11/14] IDEA-153233 Default base URL for invoking help from products has changed: use only major.minor version to build help url --- .../src/com/intellij/help/impl/HelpManagerImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/help/impl/HelpManagerImpl.java b/platform/platform-impl/src/com/intellij/help/impl/HelpManagerImpl.java index 63222252c3cd..60510164b2a1 100644 --- a/platform/platform-impl/src/com/intellij/help/impl/HelpManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/help/impl/HelpManagerImpl.java @@ -63,7 +63,12 @@ public class HelpManagerImpl extends HelpManager { if (broker == null) { ApplicationInfoEx info = ApplicationInfoEx.getInstanceEx(); - String productVersion = info.getMajorVersion() + "." + info.getMinorVersion(); + String minorVersion = info.getMinorVersion(); + int dot = minorVersion.indexOf('.'); + if (dot != -1) { + minorVersion = minorVersion.substring(0, dot); + } + String productVersion = info.getMajorVersion() + "." + minorVersion; String productCode = info.getPackageCode(); String url = info.getWebHelpUrl() + "/" + productVersion + "/?" + id; From fd969b56aca7fb811f560e23ee652db18107a6c6 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 19 Sep 2016 13:21:20 +0200 Subject: [PATCH 12/14] data context from focus should be returned in a write-safe context for actions to proceed (IDEA-161318, IDEA-161366) --- .../intellij/ide/impl/DataManagerImpl.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/impl/DataManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/impl/DataManagerImpl.java index 941d278d87e1..68c5b3188841 100644 --- a/platform/platform-impl/src/com/intellij/ide/impl/DataManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/impl/DataManagerImpl.java @@ -19,6 +19,7 @@ import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.impl.dataRules.*; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; @@ -51,6 +52,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; public class DataManagerImpl extends DataManager { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.impl.DataManagerImpl"); @@ -209,13 +211,24 @@ public class DataManagerImpl extends DataManager { @Override public AsyncResult getDataContextFromFocus() { - final AsyncResult context = new AsyncResult<>(); - - IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> context.setDone(getDataContext())); - + AsyncResult context = new AsyncResult<>(); + doWhenFocusSettlesDownInModality(context, ModalityState.current()); return context; } + private void doWhenFocusSettlesDownInModality(AsyncResult context, ModalityState modality) { + AtomicBoolean immediate = new AtomicBoolean(true); + IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> { + if (immediate.get()) { + context.setDone(getDataContext()); + return; + } + + ApplicationManager.getApplication().invokeLater(() -> doWhenFocusSettlesDownInModality(context, modality), modality); + }); + immediate.set(false); + } + public DataContext getDataContextTest(Component component) { DataContext dataContext = getDataContext(component); if (myWindowManager == null) { From 7366aac212e57d64d717586a7bd321d3966081ee Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Mon, 19 Sep 2016 14:26:32 +0300 Subject: [PATCH 13/14] IDEA-157763 Settings repository for IDE: init wizard + plugin support --- .../ide/plugins/PluginManagerCore.java | 27 ++++++++- .../ide/cloudConfig/CloudConfigProvider.java | 60 +++++++++++++++++++ .../CustomizeFeaturedPluginsStepPanel.java | 24 ++++++-- .../customize/CustomizeIDEWizardDialog.java | 7 +++ .../customize/CustomizeUIThemeStepPanel.java | 20 ++++++- .../intellij/ide/customize/PluginGroups.java | 28 ++++++++- .../plugins/InstalledPluginsManagerMain.java | 3 +- .../intellij/ide/plugins/PluginInstaller.java | 41 ++++++++++++- .../ide/plugins/PluginStateListener.java | 27 +++++++++ .../src/com/intellij/idea/StartupUtil.java | 14 +++++ .../application/ConfigImportHelper.java | 9 ++- .../openapi/application/ImportOldConfigs.form | 18 ++++-- .../application/ImportOldConfigsPanel.java | 7 +++ .../updateSettings/impl/PluginDownloader.java | 2 +- 14 files changed, 268 insertions(+), 19 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/ide/cloudConfig/CloudConfigProvider.java create mode 100644 platform/platform-impl/src/com/intellij/ide/plugins/PluginStateListener.java diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index 625c9ecb8854..c8c570c801c0 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -65,7 +65,7 @@ import java.util.zip.ZipFile; public class PluginManagerCore { private static final Logger LOG = Logger.getInstance(PluginManagerCore.class); - private static final String DISABLED_PLUGINS_FILENAME = "disabled_plugins.txt"; + public static final String DISABLED_PLUGINS_FILENAME = "disabled_plugins.txt"; public static final String CORE_PLUGIN_ID = "com.intellij"; private static final String META_INF = "META-INF"; public static final String PLUGIN_XML = "plugin.xml"; @@ -101,6 +101,8 @@ public class PluginManagerCore { } } + private static List myDisablePluginListeners; + /** * do not call this method during bootstrap, should be called in a copy of PluginManager, loaded by IdeaClassLoader */ @@ -140,6 +142,7 @@ public class PluginManagerCore { reader.close(); if (!requiredPlugins.isEmpty()) { savePluginsList(disabledPlugins, false, new File(PathManager.getConfigPath(), DISABLED_PLUGINS_FILENAME)); + fireEditDisablePlugins(); } } } @@ -204,6 +207,27 @@ public class PluginManagerCore { return app != null && app.isUnitTestMode(); } + public static void addDisablePluginListener(@NotNull Runnable listener) { + if (myDisablePluginListeners == null) { + myDisablePluginListeners = new ArrayList(); + } + myDisablePluginListeners.add(listener); + } + + public static void removeDisablePluginListener(@NotNull Runnable listener) { + if (myDisablePluginListeners != null) { + myDisablePluginListeners.remove(listener); + } + } + + private static void fireEditDisablePlugins() { + if (myDisablePluginListeners != null) { + for (Runnable listener : myDisablePluginListeners) { + listener.run(); + } + } + } + public static void savePluginsList(@NotNull Collection ids, boolean append, @NotNull File plugins) throws IOException { if (!plugins.isFile()) { FileUtil.ensureCanCreateFile(plugins); @@ -256,6 +280,7 @@ public class PluginManagerCore { File plugins = new File(PathManager.getConfigPath(), DISABLED_PLUGINS_FILENAME); savePluginsList(ids, append, plugins); ourDisabledPlugins = null; + fireEditDisablePlugins(); } public static Logger getLogger() { diff --git a/platform/platform-impl/src/com/intellij/ide/cloudConfig/CloudConfigProvider.java b/platform/platform-impl/src/com/intellij/ide/cloudConfig/CloudConfigProvider.java new file mode 100644 index 000000000000..f15b2d21cd0b --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/cloudConfig/CloudConfigProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2016 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.intellij.ide.cloudConfig; + +import com.intellij.ide.customize.AbstractCustomizeWizardStep; +import com.intellij.openapi.application.ConfigImportSettings; +import com.intellij.openapi.application.ImportOldConfigsPanel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.io.File; +import java.util.List; + +/** + * @author Alexander Lobas + */ +public abstract class CloudConfigProvider { + private static CloudConfigProvider myProvider; + + @Nullable + public static CloudConfigProvider getProvider() { + return myProvider; + } + + public static void setProvider(@Nullable CloudConfigProvider provider) { + myProvider = provider; + } + + public abstract void initConfigsPanel(@NotNull ImportOldConfigsPanel dialog, + @NotNull JPanel parentPanel, + @NotNull ConfigImportSettings settings); + + public abstract void importFinished(@NotNull File newConfigDir); + + public abstract void beforeStartupWizard(); + + @Nullable + public abstract String getLafClassName(); + + @NotNull + public abstract List getInstalledPlugins(); + + public abstract int initSteps(@NotNull List steps); + + public abstract void startupWizardFinished(); +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java index 545bd4380704..1231e6b6c1ed 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java @@ -23,6 +23,7 @@ import com.intellij.openapi.progress.util.AbstractProgressIndicatorExBase; import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.updateSettings.impl.PluginDownloader; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.ex.ProgressIndicatorEx; import com.intellij.ui.ColorUtil; import com.intellij.ui.JBColor; @@ -84,7 +85,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt int i = s.indexOf(':'); String topic = s.substring(0, i); int j = s.indexOf(':', i + 1); - final String description = s.substring(i + 1, j); + String description = s.substring(i + 1, j); final String pluginId = s.substring(j + 1); IdeaPluginDescriptor foundDescriptor = null; for (IdeaPluginDescriptor descriptor : pluginsFromRepository) { @@ -96,18 +97,29 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt if (foundDescriptor == null) continue; final IdeaPluginDescriptor descriptor = foundDescriptor; - - final boolean isVIM = PluginGroups.IDEA_VIM_PLUGIN_ID.equals(descriptor.getPluginId().getIdString()); + boolean isCloud = "#Cloud".equals(topic); + + if (isCloud) { + title = descriptor.getName(); + description = StringUtil.defaultIfEmpty(descriptor.getDescription(), "No description available"); + topic = StringUtil.defaultIfEmpty(descriptor.getCategory(), "Unknown"); + } JLabel titleLabel = new JLabel("

" + title + "

"); JLabel topicLabel = new JLabel("

" + topic + "

"); JLabel descriptionLabel = createHTMLLabel(description); JLabel warningLabel = null; - if (isVIM) { - warningLabel = createHTMLLabel("Recommended only if you are
familiar with Vim."); - warningLabel.setIcon(AllIcons.General.BalloonWarning); + if (isVIM || isCloud) { + if (isCloud) { + warningLabel = createHTMLLabel("JBA account"); + warningLabel.setIcon(AllIcons.General.BalloonInformation); + } + else { + warningLabel = createHTMLLabel("Recommended only if you are
familiar with Vim."); + warningLabel.setIcon(AllIcons.General.BalloonWarning); + } if (!SystemInfo.isWindows) UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, warningLabel); } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java index 8db6dc10a720..27769cc3624e 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java @@ -15,6 +15,7 @@ */ package com.intellij.ide.customize; +import com.intellij.ide.cloudConfig.CloudConfigProvider; import com.intellij.ide.startup.StartupActionScriptManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.ui.DialogWrapper; @@ -58,6 +59,12 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis getPeer().setAppIcons(); stepsProvider.initSteps(this, mySteps); + + CloudConfigProvider configProvider = CloudConfigProvider.getProvider(); + if (configProvider != null) { + myIndex = configProvider.initSteps(mySteps); + } + if (mySteps.isEmpty()) { throw new IllegalArgumentException(stepsProvider + " provided no steps"); } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java index 329e23906cb9..a9db87bfe678 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java @@ -17,6 +17,7 @@ package com.intellij.ide.customize; import com.intellij.CommonBundle; import com.intellij.ide.WelcomeWizardUtil; +import com.intellij.ide.cloudConfig.CloudConfigProvider; import com.intellij.ide.ui.LafManager; import com.intellij.ide.ui.laf.IntelliJLaf; import com.intellij.ide.ui.laf.LafManagerImpl; @@ -26,7 +27,9 @@ import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.util.IconUtil; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -89,7 +92,7 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { myColumnMode = myThemes.size() > 2; JPanel buttonsPanel = new JPanel(new GridLayout(myColumnMode ? myThemes.size() : 1, myColumnMode ? 1 : myThemes.size(), 5, 5)); ButtonGroup group = new ButtonGroup(); - final ThemeInfo myDefaultTheme = myThemes.iterator().next(); + final ThemeInfo myDefaultTheme = getDefaultTheme(); for (final ThemeInfo theme : myThemes) { final JRadioButton radioButton = new JRadioButton(theme.name, myDefaultTheme == theme); @@ -146,6 +149,21 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { } } + @NotNull + private ThemeInfo getDefaultTheme() { + CloudConfigProvider provider = CloudConfigProvider.getProvider(); + if (provider != null) { + String lafClassName = provider.getLafClassName(); + if (lafClassName != null) { + ThemeInfo result = ContainerUtil.find(myThemes, theme -> lafClassName.equals(theme.laf)); + if (result != null) { + return result; + } + } + } + return myThemes.iterator().next(); + } + @Override public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); diff --git a/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java b/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java index 58d8e4314adc..8ded8152c09e 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java @@ -16,6 +16,7 @@ package com.intellij.ide.customize; import com.intellij.ide.WelcomeWizardUtil; +import com.intellij.ide.cloudConfig.CloudConfigProvider; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.plugins.RepositoryHelper; @@ -32,6 +33,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.File; import java.util.*; +import java.util.Map.Entry; import java.util.concurrent.ExecutionException; public class PluginGroups { @@ -84,6 +86,7 @@ public class PluginGroups { PluginManagerCore.loadDisabledPlugins(new File(PathManager.getConfigPath()).getPath(), myDisabledPluginIds); initGroups(myTree, myFeaturedPlugins); + initCloudPlugins(); } public void setLoadingCallback(Runnable loadingCallback) { @@ -93,6 +96,29 @@ public class PluginGroups { } } + private void initCloudPlugins() { + CloudConfigProvider provider = CloudConfigProvider.getProvider(); + if (provider == null) { + return; + } + + List plugins = provider.getInstalledPlugins(); + if (plugins.isEmpty()) { + return; + } + + for (Iterator> I = myFeaturedPlugins.entrySet().iterator(); I.hasNext(); ) { + String value = I.next().getValue(); + if (ContainerUtil.find(plugins, plugin -> value.endsWith(":" + plugin)) != null) { + I.remove(); + } + } + + for (String plugin : plugins) { + myFeaturedPlugins.put(plugin, "#Cloud:#Cloud:" + plugin); + } + } + protected void initGroups(Map>> tree, Map featuredPlugins) { tree.put(CORE, Pair.create((Icon)null, Arrays.asList( @@ -263,7 +289,7 @@ public class PluginGroups { private void initIfNeed() { if (myInitialized) return; myInitialized = true; - for (Map.Entry>> entry : myTree.entrySet()) { + for (Entry>> entry : myTree.entrySet()) { final String group = entry.getKey(); if (CORE.equals(group)) continue; diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsManagerMain.java b/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsManagerMain.java index 3335dd646b15..426eb532c832 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsManagerMain.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsManagerMain.java @@ -45,7 +45,6 @@ import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.ArrayUtilRt; import com.intellij.util.Consumer; -import com.intellij.util.Function; import com.intellij.util.ui.StatusText; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -151,7 +150,7 @@ public class InstalledPluginsManagerMain extends PluginManagerMain { } } - PluginInstaller.install(file, file.getName(), false); + PluginInstaller.install(file, file.getName(), false, pluginDescriptor); ourState.onPluginInstall(pluginDescriptor); checkInstalledPluginDependencies(model, pluginDescriptor, parent); callback.consume(pair(file, (IdeaPluginDescriptor)pluginDescriptor)); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java index cc8117797fe6..4f6d75a9d47c 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java @@ -30,10 +30,10 @@ import com.intellij.openapi.updateSettings.impl.UpdateSettings; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.GuiUtils; import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.ZipUtil; +import com.intellij.util.ui.UIUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -232,6 +232,8 @@ public class PluginInstaller { if (pluginDescriptor != null) { StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(pluginDescriptor.getPath()); StartupActionScriptManager.addActionCommand(deleteOld); + + fireState(pluginDescriptor, false); } else { PluginManagerMain.LOG.error("Plugin not found: " + pluginId); @@ -240,7 +242,10 @@ public class PluginInstaller { } } - public static void install(final File fromFile, final String pluginName, boolean deleteFromFile) throws IOException { + public static void install(@NotNull File fromFile, + @NotNull String pluginName, + boolean deleteFromFile, + @NotNull IdeaPluginDescriptor descriptor) throws IOException { //noinspection HardCodedStringLiteral if (fromFile.getName().endsWith(".jar")) { // add command to copy file to the IDEA/plugins path @@ -267,5 +272,37 @@ public class PluginInstaller { StartupActionScriptManager.ActionCommand deleteTemp = new StartupActionScriptManager.DeleteCommand(fromFile); StartupActionScriptManager.addActionCommand(deleteTemp); } + + fireState(descriptor, true); + } + + private static List myStateListeners; + + public static void addStateListener(@NotNull PluginStateListener listener) { + if (myStateListeners == null) { + myStateListeners = new ArrayList<>(); + } + myStateListeners.add(listener); + } + + public static void removeStateListener(@NotNull PluginStateListener listener) { + if (myStateListeners != null) { + myStateListeners.remove(listener); + } + } + + private static void fireState(@NotNull IdeaPluginDescriptor descriptor, boolean install) { + if (myStateListeners != null) { + UIUtil.invokeLaterIfNeeded(() -> { + for (PluginStateListener listener : myStateListeners) { + if (install) { + listener.install(descriptor); + } + else { + listener.uninstall(descriptor); + } + } + }); + } } } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginStateListener.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginStateListener.java new file mode 100644 index 000000000000..d3eb50b1f0c4 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginStateListener.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2016 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.intellij.ide.plugins; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Alexander Lobas + */ +public interface PluginStateListener { + void install(@NotNull IdeaPluginDescriptor descriptor); + + void uninstall(@NotNull IdeaPluginDescriptor descriptor); +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java index 8a65f70151c6..1a255b55442b 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.idea; +import com.intellij.ide.cloudConfig.CloudConfigProvider; import com.intellij.ide.customize.CustomizeIDEWizardDialog; import com.intellij.ide.customize.CustomizeIDEWizardStepsProvider; import com.intellij.ide.plugins.PluginManagerCore; @@ -83,6 +84,8 @@ public class StartupUtil { interface AppStarter { void start(boolean newConfigFolder); + + default void beforeImportConfigs() {} } static void prepareAndStart(String[] args, AppStarter appStarter) { @@ -125,6 +128,7 @@ public class StartupUtil { } if (newConfigFolder) { + appStarter.beforeImportConfigs(); ConfigImportHelper.importConfigsTo(PathManager.getConfigPath()); } @@ -422,8 +426,18 @@ public class StartupUtil { return; } + CloudConfigProvider configProvider = CloudConfigProvider.getProvider(); + if (configProvider != null) { + configProvider.beforeStartupWizard(); + } + new CustomizeIDEWizardDialog(provider).show(); + PluginManagerCore.invalidatePlugins(); + if (configProvider != null) { + configProvider.startupWizardFinished(); + } + return; } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java index b5f514c7e0e2..6d21bc8916cb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.application; +import com.intellij.ide.cloudConfig.CloudConfigProvider; import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.PluginManagerCore; @@ -79,6 +80,12 @@ public class ConfigImportHelper { doImport(newConfigDir, oldConfigDir, settings, installationHome); settings.importFinished(newConfigPath); System.setProperty(CONFIG_IMPORTED_IN_CURRENT_SESSION_KEY, Boolean.TRUE.toString()); + + CloudConfigProvider provider = CloudConfigProvider.getProvider(); + if (provider != null) { + provider.importFinished(newConfigDir); + } + break; } } @@ -230,7 +237,7 @@ public class ConfigImportHelper { } @Nullable - private static File getOldConfigDir(@Nullable File oldInstallHome, ConfigImportSettings settings) { + public static File getOldConfigDir(@Nullable File oldInstallHome, ConfigImportSettings settings) { if (oldInstallHome == null) { return null; } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigs.form b/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigs.form index 849abd2222d5..18257e1ee507 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigs.form +++ b/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigs.form @@ -1,9 +1,9 @@
- + - + @@ -11,7 +11,7 @@ - + @@ -38,7 +38,7 @@ - + @@ -123,6 +123,16 @@ + + + + + + + + + + diff --git a/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigsPanel.java b/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigsPanel.java index 1097409baaee..94fc8f51c524 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigsPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/ImportOldConfigsPanel.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.application; +import com.intellij.ide.cloudConfig.CloudConfigProvider; import com.intellij.openapi.MnemonicHelper; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.SystemInfo; @@ -45,6 +46,7 @@ public class ImportOldConfigsPanel extends JDialog { private JLabel mySuggestLabel; private JLabel myHomeLabel; private JRadioButton myRbImportAuto; + private JPanel myCustomPanel; private final File myGuessedOldConfig; private final ConfigImportSettings mySettings; @@ -120,6 +122,11 @@ public class ImportOldConfigsPanel extends JDialog { } }); + CloudConfigProvider configProvider = CloudConfigProvider.getProvider(); + if (configProvider != null) { + configProvider.initConfigsPanel(this, myCustomPanel, mySettings); + } + getContentPane().setLayout(new BorderLayout()); getContentPane().add(myRootPanel); getRootPane().setDefaultButton(myOkButton); diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java index 87186b86fb1f..0b9df19978cf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java @@ -218,7 +218,7 @@ public class PluginDownloader { StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(myOldFile); StartupActionScriptManager.addActionCommand(deleteOld); } - PluginInstaller.install(myFile, getPluginName(), true); + PluginInstaller.install(myFile, getPluginName(), true, myDescriptor); InstalledPluginsState state = InstalledPluginsState.getInstanceIfLoaded(); if (state != null) { state.onPluginInstall(myDescriptor); From 72432db1cffc41fef9ba3653244a9e0b511149b5 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Tue, 6 Sep 2016 18:46:50 +0300 Subject: [PATCH 14/14] Fix PY-16473 Inspection naming error for namedtuple declared into a function --- .../inspections/PyPep8NamingInspection.java | 17 ++++++++++++----- .../namedTupleInsideFunction.py | 5 +++++ .../inspections/PyPep8NamingInspectionTest.java | 4 ++++ 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 python/testData/inspections/PyPep8NamingInspection/namedTupleInsideFunction.py diff --git a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java index 6bb42692560c..67d4f4d684da 100644 --- a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java @@ -25,6 +25,7 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.PopupChooserBuilder; +import com.intellij.openapi.util.Pair; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; @@ -115,18 +116,24 @@ public class PyPep8NamingInspection extends PyInspection { final PyFunction function = PsiTreeUtil.getParentOfType(node, PyFunction.class, true, PyClass.class); if (function == null) return; final Scope scope = ControlFlowCache.getScope(function); - for (PyExpression expression : node.getTargets()) { - final String name = expression.getName(); + for (Pair pair : node.getTargetsToValuesMapping()) { + final String name = pair.getFirst().getName(); if (name == null || scope.isGlobal(name)) continue; - if (expression instanceof PyTargetExpression) { - final PyExpression qualifier = ((PyTargetExpression)expression).getQualifier(); + if (pair.getFirst() instanceof PyTargetExpression) { + final PyExpression qualifier = ((PyTargetExpression)pair.getFirst()).getQualifier(); if (qualifier != null) { return; } } + + final PyCallExpression assignedValue = PyUtil.as(pair.getSecond(), PyCallExpression.class); + if (assignedValue != null + && assignedValue.getCallee() != null && PyNames.NAMEDTUPLE.equals(assignedValue.getCallee().getName())) { + return; + } final String errorCode = "N806"; if (!LOWERCASE_REGEX.matcher(name).matches() && !name.startsWith("_") && !ignoredErrors.contains(errorCode)) { - registerAndAddRenameAndIgnoreErrorQuickFixes(expression, errorCode); + registerAndAddRenameAndIgnoreErrorQuickFixes(pair.getFirst(), errorCode); } } } diff --git a/python/testData/inspections/PyPep8NamingInspection/namedTupleInsideFunction.py b/python/testData/inspections/PyPep8NamingInspection/namedTupleInsideFunction.py new file mode 100644 index 000000000000..c9d27db5808b --- /dev/null +++ b/python/testData/inspections/PyPep8NamingInspection/namedTupleInsideFunction.py @@ -0,0 +1,5 @@ +from collections import namedtuple + +def f(): + Point = namedtuple("Point", ["x1", "x2"], verbose=True) + Test = "foo" \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyPep8NamingInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyPep8NamingInspectionTest.java index a72e25f39eac..e774c9a3a6f2 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyPep8NamingInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyPep8NamingInspectionTest.java @@ -112,6 +112,10 @@ public class PyPep8NamingInspectionTest extends PyTestCase { doTest(); } + public void testNamedTupleInsideFunction() { + doTest(); + } + public void testOverrideFromModule() { myFixture.configureByFiles("inspections/PyPep8NamingInspection/" + getTestName(true) + ".py", "inspections/PyPep8NamingInspection/tmp1.py");