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/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/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;
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/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) {
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 @@
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);
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 e425e5c25885..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(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 d3bceb0ba549..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(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..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
@@ -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;
@@ -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,11 +81,11 @@ public class CCPushTask extends DumbAwareAction {
@Override
public void run(@NotNull ProgressIndicator indicator) {
indicator.setText("Uploading task to " + EduStepicNames.STEPIC_URL);
- if (task.getStepicId() <= 0) {
- EduStepicConnector.postTask(project, task, lesson.getId());
+ if (task.getStepId() <= 0) {
+ 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/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java
index 9c8b39331e72..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
@@ -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() {
@@ -197,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/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/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
new file mode 100644
index 000000000000..47e4d59cad77
--- /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.getStepId()));
+ 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.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 1ab1531cb311..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,12 +50,10 @@ 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.*;
+import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getStep;
public class EduAdaptiveStepicConnector {
public static final String PYTHON2 = "python2";
@@ -67,12 +65,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 +86,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 +110,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 +156,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 +168,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;
}
@@ -208,7 +198,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);
@@ -318,7 +308,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) {
@@ -398,7 +388,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 +419,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 +452,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();
@@ -506,13 +494,12 @@ 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)));
- 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 +511,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;
}
@@ -540,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
new file mode 100644
index 000000000000..15623b798f8c
--- /dev/null
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicAuthorizedClient.java
@@ -0,0 +1,256 @@
+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.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;
+ }
+ final StepicUser stepicUser = StudyTaskManager.getInstance(project).getUser();
+ ourClient = initializeClient(stepicUser);
+ if (ourClient == null) {
+ final StepicUser user = login(stepicUser);
+ if (user != null) {
+ StudyTaskManager.getInstance(project).setUser(user);
+ ourClient = initializeClient(stepicUser);
+ }
+ }
+ return ourClient;
+ }
+
+ public static T getFromStepic(String link, final Class container, @NotNull final Project project) throws IOException {
+ 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 StepicUser stepicUser) {
+ final List headers = new ArrayList<>();
+ 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));
+ 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 StepicUser login(@NotNull final StepicUser user) {
+ 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);
+ if (tokenInfo != null) {
+ user.setupTokenInfo(tokenInfo);
+ }
+ }
+ else {
+ final StepicUser stepicUser = login(login, user.getPassword());
+ if (stepicUser == null) {
+ return showLoginDialog();
+ }
+ return stepicUser;
+ }
+ }
+ return null;
+ }
+
+ private static StepicUser showLoginDialog() {
+ final StepicUser[] stepicUser = new StepicUser[1];
+ ApplicationManager.getApplication().invokeAndWait(() -> {
+ final LoginDialog dialog = new LoginDialog();
+ dialog.show();
+ stepicUser[0] = dialog.getStepicUser();
+ });
+ return stepicUser[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());
+ }
+ if (tokenInfo != null) {
+ 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));
+
+ return postCredentials(parameters);
+ }
+
+ @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..3ed86017f1ee
--- /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, getHttpClient());
+ }
+
+ 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 7b4b345258a5..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
@@ -1,280 +1,49 @@
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;
-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.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 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.HttpPost;
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) {
+ 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)));
- setHeaders(post, CONTENT_TYPE_APPL_JSON);
- if (ourClient == null) {
- initializeClient();
- }
- CloseableHttpResponse response = ourClient.execute(post);
+ final CloseableHttpClient client = EduStepicAuthorizedClient.getHttpClient(stepicUser);
+ CloseableHttpResponse response = client.execute(post);
StatusLine line = response.getStatusLine();
return line.getStatusCode() == HttpStatus.SC_CREATED;
}
@@ -300,23 +69,10 @@ public class EduStepicConnector {
return Collections.singletonList(CourseInfo.INVALID_COURSE);
}
- 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);
- 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 {
- 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 +87,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 +102,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 +124,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 +132,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 +189,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) {
@@ -459,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);
@@ -477,53 +233,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) {
- if (task.getStepicId() <= 0) {
+ public static void postAttempt(@NotNull final Task task, boolean passed, @NotNull final Project project) {
+ if (task.getStepId() <= 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()));
+ String attemptRequestBody = new Gson().toJson(new StepicWrappers.AttemptWrapper(task.getStepId()));
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 +262,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();
@@ -556,383 +285,4 @@ public class EduStepicConnector {
LOG.error("Failed to make submission " + responseString);
}
}
-
- 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);
- }
- });
- }
-
- private static void postCourse(final Project project, @NotNull Course course, boolean relogin, @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 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());
- }
- }
-
- course.setAuthors(authors.users);
- }
-
- 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 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()));
- int position = 1;
- for (Lesson lesson : course.getLessons()) {
- indicator.checkCanceled();
- final int lessonId = postLesson(project, lesson, indicator);
- postUnit(lessonId, position, sectionId);
- position += 1;
- }
- ApplicationManager.getApplication().runReadAction(() -> postAdditionalFiles(project, postedCourse.id, indicator));
- }
- catch (IOException e) {
- LOG.error(e.getMessage());
- }
- }
-
- 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() {
- @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(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(lessonId, 1, sectionId);
- }
- }
-
- public static void postUnit(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;
- unitWrapper.unit.position = position;
- unitWrapper.unit.section = sectionId;
-
- String requestBody = new Gson().toJson(unitWrapper);
- request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
-
- try {
- final CloseableHttpResponse response = ourClient.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(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;
- 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 CloseableHttpResponse response = ourClient.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();
-
- 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(() -> {
- final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
- request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
-
- try {
- final CloseableHttpResponse response = ourClient.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);
- }
- }
- 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()));
- 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 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);
- }
-
- 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");
- 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 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;
- }
- 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) {
- 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 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) {
- 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();
- ApplicationManager.getApplication().invokeLater(() -> {
- final String requestBody = gson.toJson(new StepicWrappers.StepSourceWrapper(project, task, lessonId));
- request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
-
- try {
- final CloseableHttpResponse response = ourClient.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;
- }
-
- 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());
- }
- });
- }
-
- 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..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,13 +42,9 @@ public class LoginDialog extends DialogWrapper {
@Override
protected void doOKAction() {
if (!validateLoginAndPasswordFields()) return;
- final StepicUser user = EduStepicConnector.login(myLoginPanel.getLogin(), myLoginPanel.getPassword());
- if (user != null) {
+ myStepicUser = EduStepicAuthorizedClient.login(myLoginPanel.getLogin(), myLoginPanel.getPassword());
+ if (myStepicUser != null) {
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");
@@ -76,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/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/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..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
@@ -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(@NotNull final 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..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;
}
@@ -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/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;
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..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.EduStepicConnector;
-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;
@@ -344,14 +341,13 @@ 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());
-
+ 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-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 {
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..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
@@ -183,7 +183,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator {
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());
}
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");
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